diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml new file mode 100644 index 0000000000000..5340b4bf578cd --- /dev/null +++ b/.buildkite/ftr_configs.yml @@ -0,0 +1,241 @@ +disabled: + # TODO: Enable once RBAC timeline search strategy test updated + - x-pack/test/timeline/security_and_spaces/config_basic.ts + + # Base config files, only necessary to inform config finding script + - test/functional/config.base.js + - x-pack/test/functional/config.base.js + - x-pack/test/detection_engine_api_integration/security_and_spaces/config.base.ts + - x-pack/test/functional_enterprise_search/base_config.ts + - test/server_integration/config.base.js + + # QA suites that are run out-of-band + - x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js + - x-pack/test/upgrade/config.ts + - test/functional/config.edge.js + - x-pack/test/functional/config.edge.js + + # Cypress configs, for now these are still run manually + - x-pack/test/fleet_cypress/cli_config.ts + - x-pack/test/fleet_cypress/config.ts + - x-pack/test/fleet_cypress/visual_config.ts + - x-pack/test/functional_enterprise_search/cypress.config.ts + - x-pack/test/osquery_cypress/cli_config.ts + - x-pack/test/osquery_cypress/config.ts + - x-pack/test/osquery_cypress/visual_config.ts + - x-pack/test/security_solution_cypress/cases_cli_config.ts + - x-pack/test/security_solution_cypress/ccs_config.ts + - x-pack/test/security_solution_cypress/cli_config.ts + - x-pack/test/security_solution_cypress/config.firefox.ts + - x-pack/test/security_solution_cypress/config.ts + - x-pack/test/security_solution_cypress/response_ops_cli_config.ts + - x-pack/test/security_solution_cypress/upgrade_config.ts + - x-pack/test/security_solution_cypress/visual_config.ts + - x-pack/test/functional_enterprise_search/with_host_configured.config.ts + - x-pack/plugins/apm/ftr_e2e/ftr_config_run.ts + - x-pack/plugins/apm/ftr_e2e/ftr_config.ts + + # Elastic Synthetics configs + - x-pack/plugins/synthetics/e2e/config.ts + - x-pack/plugins/synthetics/e2e/playwright_run.ts + + # Configs that exist but weren't running in CI when this file was introduced + - test/visual_regression/config.ts + - x-pack/test/visual_regression/config.ts + - x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/config.ts + - x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/config.ts + - x-pack/test/alerting_api_integration/spaces_only_legacy/config.ts + - x-pack/test/banners_functional/config.ts + - x-pack/test/cloud_integration/config.ts + - x-pack/test/performance/config.playwright.ts + - x-pack/test/load/config.ts + - x-pack/test/plugin_api_perf/config.js + - x-pack/test/screenshot_creation/config.ts + +enabled: + - test/accessibility/config.ts + - test/analytics/config.ts + - test/api_integration/config.js + - test/examples/config.js + - test/functional/apps/bundles/config.ts + - test/functional/apps/console/config.ts + - test/functional/apps/context/config.ts + - test/functional/apps/dashboard_elements/config.ts + - test/functional/apps/dashboard/group1/config.ts + - test/functional/apps/dashboard/group2/config.ts + - test/functional/apps/dashboard/group3/config.ts + - test/functional/apps/dashboard/group4/config.ts + - test/functional/apps/dashboard/group5/config.ts + - test/functional/apps/discover/config.ts + - test/functional/apps/getting_started/config.ts + - test/functional/apps/home/config.ts + - test/functional/apps/management/config.ts + - test/functional/apps/saved_objects_management/config.ts + - test/functional/apps/status_page/config.ts + - test/functional/apps/visualize/group1/config.ts + - test/functional/apps/visualize/group2/config.ts + - test/functional/apps/visualize/group3/config.ts + - test/functional/apps/visualize/group4/config.ts + - test/functional/apps/visualize/group5/config.ts + - test/functional/apps/visualize/group6/config.ts + - test/functional/apps/visualize/replaced_vislib_chart_types/config.ts + - test/functional/config.ccs.ts + - test/functional/config.firefox.js + - test/interactive_setup_api_integration/enrollment_flow.config.ts + - test/interactive_setup_api_integration/manual_configuration_flow_without_tls.config.ts + - test/interactive_setup_api_integration/manual_configuration_flow.config.ts + - test/interactive_setup_functional/enrollment_token.config.ts + - test/interactive_setup_functional/manual_configuration_without_security.config.ts + - test/interactive_setup_functional/manual_configuration_without_tls.config.ts + - test/interactive_setup_functional/manual_configuration.config.ts + - test/interpreter_functional/config.ts + - test/new_visualize_flow/config.ts + - test/plugin_functional/config.ts + - test/server_integration/http/platform/config.status.ts + - test/server_integration/http/platform/config.ts + - test/server_integration/http/ssl_redirect/config.js + - test/server_integration/http/ssl_with_p12_intermediate/config.js + - test/server_integration/http/ssl_with_p12/config.js + - test/server_integration/http/ssl/config.js + - test/ui_capabilities/newsfeed_err/config.ts + - x-pack/test/accessibility/config.ts + - x-pack/test/alerting_api_integration/basic/config.ts + - x-pack/test/alerting_api_integration/security_and_spaces/group1/config.ts + - x-pack/test/alerting_api_integration/security_and_spaces/group2/config.ts + - x-pack/test/alerting_api_integration/spaces_only/config.ts + - x-pack/test/api_integration_basic/config.ts + - x-pack/test/api_integration/config_security_basic.ts + - x-pack/test/api_integration/config_security_trial.ts + - x-pack/test/api_integration/config.ts + - x-pack/test/apm_api_integration/basic/config.ts + - x-pack/test/apm_api_integration/rules/config.ts + - x-pack/test/apm_api_integration/trial/config.ts + - x-pack/test/cases_api_integration/security_and_spaces/config_basic.ts + - x-pack/test/cases_api_integration/security_and_spaces/config_trial.ts + - x-pack/test/cases_api_integration/spaces_only/config.ts + - x-pack/test/detection_engine_api_integration/basic/config.ts + - x-pack/test/detection_engine_api_integration/security_and_spaces/group1/config.ts + - x-pack/test/detection_engine_api_integration/security_and_spaces/group2/config.ts + - x-pack/test/detection_engine_api_integration/security_and_spaces/group3/config.ts + - x-pack/test/detection_engine_api_integration/security_and_spaces/group4/config.ts + - x-pack/test/detection_engine_api_integration/security_and_spaces/group5/config.ts + - x-pack/test/detection_engine_api_integration/security_and_spaces/group6/config.ts + - x-pack/test/detection_engine_api_integration/security_and_spaces/group7/config.ts + - x-pack/test/detection_engine_api_integration/security_and_spaces/group8/config.ts + - x-pack/test/detection_engine_api_integration/security_and_spaces/group9/config.ts + - x-pack/test/encrypted_saved_objects_api_integration/config.ts + - x-pack/test/endpoint_api_integration_no_ingest/config.ts + - x-pack/test/examples/config.ts + - x-pack/test/fleet_api_integration/config.ts + - x-pack/test/fleet_functional/config.ts + - x-pack/test/functional_basic/config.ts + - x-pack/test/functional_cors/config.ts + - x-pack/test/functional_embedded/config.ts + - x-pack/test/functional_enterprise_search/without_host_configured.config.ts + - x-pack/test/functional_execution_context/config.ts + - x-pack/test/functional_synthetics/config.js + - x-pack/test/functional_with_es_ssl/config.ts + - x-pack/test/functional/apps/advanced_settings/config.ts + - x-pack/test/functional/apps/api_keys/config.ts + - x-pack/test/functional/apps/apm/config.ts + - x-pack/test/functional/apps/canvas/config.ts + - x-pack/test/functional/apps/cross_cluster_replication/config.ts + - x-pack/test/functional/apps/dashboard/group1/config.ts + - x-pack/test/functional/apps/dashboard/group2/config.ts + - x-pack/test/functional/apps/data_views/config.ts + - x-pack/test/functional/apps/dev_tools/config.ts + - x-pack/test/functional/apps/discover/config.ts + - x-pack/test/functional/apps/graph/config.ts + - x-pack/test/functional/apps/grok_debugger/config.ts + - x-pack/test/functional/apps/home/config.ts + - x-pack/test/functional/apps/index_lifecycle_management/config.ts + - x-pack/test/functional/apps/index_management/config.ts + - x-pack/test/functional/apps/infra/config.ts + - x-pack/test/functional/apps/ingest_pipelines/config.ts + - x-pack/test/functional/apps/lens/group1/config.ts + - x-pack/test/functional/apps/lens/group2/config.ts + - x-pack/test/functional/apps/lens/group3/config.ts + - x-pack/test/functional/apps/license_management/config.ts + - x-pack/test/functional/apps/logstash/config.ts + - x-pack/test/functional/apps/management/config.ts + - x-pack/test/functional/apps/maps/group1/config.ts + - x-pack/test/functional/apps/maps/group2/config.ts + - x-pack/test/functional/apps/maps/group3/config.ts + - x-pack/test/functional/apps/maps/group4/config.ts + - x-pack/test/functional/apps/ml/anomaly_detection/config.ts + - x-pack/test/functional/apps/ml/data_frame_analytics/config.ts + - x-pack/test/functional/apps/ml/data_visualizer/config.ts + - x-pack/test/functional/apps/ml/permissions/config.ts + - x-pack/test/functional/apps/ml/short_tests/config.ts + - x-pack/test/functional/apps/ml/stack_management_jobs/config.ts + - x-pack/test/functional/apps/monitoring/config.ts + - x-pack/test/functional/apps/remote_clusters/config.ts + - x-pack/test/functional/apps/reporting_management/config.ts + - x-pack/test/functional/apps/rollup_job/config.ts + - x-pack/test/functional/apps/saved_objects_management/config.ts + - x-pack/test/functional/apps/security/config.ts + - x-pack/test/functional/apps/snapshot_restore/config.ts + - x-pack/test/functional/apps/spaces/config.ts + - x-pack/test/functional/apps/status_page/config.ts + - x-pack/test/functional/apps/transform/config.ts + - x-pack/test/functional/apps/upgrade_assistant/config.ts + - x-pack/test/functional/apps/uptime/config.ts + - x-pack/test/functional/apps/visualize/config.ts + - x-pack/test/functional/apps/watcher/config.ts + - x-pack/test/functional/config_security_basic.ts + - x-pack/test/functional/config.ccs.ts + - x-pack/test/functional/config.firefox.js + - x-pack/test/licensing_plugin/config.public.ts + - x-pack/test/licensing_plugin/config.ts + - x-pack/test/lists_api_integration/security_and_spaces/config.ts + - x-pack/test/observability_api_integration/basic/config.ts + - x-pack/test/observability_api_integration/trial/config.ts + - x-pack/test/observability_functional/with_rac_write.config.ts + - x-pack/test/plugin_api_integration/config.ts + - x-pack/test/plugin_functional/config.ts + - x-pack/test/reporting_api_integration/reporting_and_security.config.ts + - x-pack/test/reporting_api_integration/reporting_without_security.config.ts + - x-pack/test/reporting_functional/reporting_and_deprecated_security.config.ts + - x-pack/test/reporting_functional/reporting_and_security.config.ts + - x-pack/test/reporting_functional/reporting_without_security.config.ts + - x-pack/test/rule_registry/security_and_spaces/config_basic.ts + - x-pack/test/rule_registry/security_and_spaces/config_trial.ts + - x-pack/test/rule_registry/spaces_only/config_basic.ts + - x-pack/test/rule_registry/spaces_only/config_trial.ts + - x-pack/test/saved_object_api_integration/security_and_spaces/config_basic.ts + - x-pack/test/saved_object_api_integration/security_and_spaces/config_trial.ts + - x-pack/test/saved_object_api_integration/spaces_only/config.ts + - x-pack/test/saved_object_tagging/api_integration/security_and_spaces/config.ts + - x-pack/test/saved_object_tagging/api_integration/tagging_api/config.ts + - x-pack/test/saved_object_tagging/functional/config.ts + - x-pack/test/saved_objects_field_count/config.ts + - x-pack/test/search_sessions_integration/config.ts + - x-pack/test/security_api_integration/anonymous_es_anonymous.config.ts + - x-pack/test/security_api_integration/anonymous.config.ts + - x-pack/test/security_api_integration/audit.config.ts + - x-pack/test/security_api_integration/http_bearer.config.ts + - x-pack/test/security_api_integration/http_no_auth_providers.config.ts + - x-pack/test/security_api_integration/kerberos_anonymous_access.config.ts + - x-pack/test/security_api_integration/kerberos.config.ts + - x-pack/test/security_api_integration/login_selector.config.ts + - x-pack/test/security_api_integration/oidc_implicit_flow.config.ts + - x-pack/test/security_api_integration/oidc.config.ts + - x-pack/test/security_api_integration/pki.config.ts + - x-pack/test/security_api_integration/saml.config.ts + - x-pack/test/security_api_integration/session_idle.config.ts + - x-pack/test/security_api_integration/session_invalidate.config.ts + - x-pack/test/security_api_integration/session_lifespan.config.ts + - x-pack/test/security_api_integration/token.config.ts + - x-pack/test/security_functional/login_selector.config.ts + - x-pack/test/security_functional/oidc.config.ts + - x-pack/test/security_functional/saml.config.ts + - x-pack/test/security_solution_endpoint_api_int/config.ts + - x-pack/test/security_solution_endpoint/config.ts + - x-pack/test/spaces_api_integration/security_and_spaces/config_basic.ts + - x-pack/test/spaces_api_integration/security_and_spaces/config_trial.ts + - x-pack/test/spaces_api_integration/spaces_only/config.ts + - x-pack/test/timeline/security_and_spaces/config_trial.ts + - x-pack/test/ui_capabilities/security_and_spaces/config.ts + - x-pack/test/ui_capabilities/spaces_only/config.ts + - x-pack/test/upgrade_assistant_integration/config.js + - x-pack/test/usage_collection/config.ts diff --git a/.buildkite/package-lock.json b/.buildkite/package-lock.json new file mode 100644 index 0000000000000..e8509e8126adb --- /dev/null +++ b/.buildkite/package-lock.json @@ -0,0 +1,962 @@ +{ + "name": "kibana-buildkite", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "kibana-buildkite", + "version": "1.0.0", + "dependencies": { + "kibana-buildkite-library": "git+https://git@github.com/elastic/kibana-buildkite-library#a0037514b7650296a23dbad99b165601d4eab1be" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "dependencies": { + "@octokit/types": "^6.0.3" + } + }, + "node_modules/@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "dependencies": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", + "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", + "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", + "dependencies": { + "@octokit/types": "^6.34.0" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", + "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", + "dependencies": { + "@octokit/types": "^6.34.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/rest": { + "version": "18.12.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", + "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", + "dependencies": { + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.8", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + } + }, + "node_modules/@octokit/types": { + "version": "6.34.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", + "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", + "dependencies": { + "@octokit/openapi-types": "^11.2.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/kibana-buildkite-library": { + "version": "1.0.0", + "resolved": "git+https://git@github.com/elastic/kibana-buildkite-library.git#a0037514b7650296a23dbad99b165601d4eab1be", + "integrity": "sha512-W9oH2c0q21IbO3sKJR2BkebhDlXVuWfqKO1r6T/E8/RRxCXJg/Wf073k8aDdpl1Enk8Pq47F+lG7/IVT+kAcFA==", + "license": "MIT", + "dependencies": { + "@octokit/rest": "^18.10.0", + "axios": "^0.21.4", + "globby": "^11.1.0", + "js-yaml": "^4.1.0", + "minimatch": "^5.0.1" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + }, + "dependencies": { + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "requires": { + "@octokit/types": "^6.0.3" + } + }, + "@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "requires": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "requires": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", + "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==" + }, + "@octokit/plugin-paginate-rest": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", + "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", + "requires": { + "@octokit/types": "^6.34.0" + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "requires": {} + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", + "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", + "requires": { + "@octokit/types": "^6.34.0", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "requires": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "18.12.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", + "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", + "requires": { + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.8", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + } + }, + "@octokit/types": { + "version": "6.34.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", + "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", + "requires": { + "@octokit/openapi-types": "^11.2.0" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "requires": { + "follow-redirects": "^1.14.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "requires": { + "path-type": "^4.0.0" + } + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "requires": { + "reusify": "^1.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "kibana-buildkite-library": { + "version": "git+https://git@github.com/elastic/kibana-buildkite-library.git#a0037514b7650296a23dbad99b165601d4eab1be", + "integrity": "sha512-W9oH2c0q21IbO3sKJR2BkebhDlXVuWfqKO1r6T/E8/RRxCXJg/Wf073k8aDdpl1Enk8Pq47F+lG7/IVT+kAcFA==", + "from": "kibana-buildkite-library@git+https://git@github.com/elastic/kibana-buildkite-library#a0037514b7650296a23dbad99b165601d4eab1be", + "requires": { + "@octokit/rest": "^18.10.0", + "axios": "^0.21.4", + "globby": "^11.1.0", + "js-yaml": "^4.1.0", + "minimatch": "^5.0.1" + } + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } +} diff --git a/.buildkite/package.json b/.buildkite/package.json index 385c9f2429f79..7f15a2fdf75bc 100644 --- a/.buildkite/package.json +++ b/.buildkite/package.json @@ -3,6 +3,6 @@ "version": "1.0.0", "private": true, "dependencies": { - "kibana-buildkite-library": "elastic/kibana-buildkite-library" + "kibana-buildkite-library": "git+https://git@github.com/elastic/kibana-buildkite-library#a0037514b7650296a23dbad99b165601d4eab1be" } } diff --git a/.buildkite/pipelines/es_snapshots/verify.yml b/.buildkite/pipelines/es_snapshots/verify.yml index a4a6b1303f70d..bdbce0745c1f8 100755 --- a/.buildkite/pipelines/es_snapshots/verify.yml +++ b/.buildkite/pipelines/es_snapshots/verify.yml @@ -27,61 +27,28 @@ steps: if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''" timeout_in_minutes: 60 - - command: .buildkite/scripts/steps/functional/xpack_cigroup.sh - label: 'Default CI Group' - parallelism: 31 + - command: .buildkite/scripts/steps/test/pick_test_group_run_order.sh + label: 'Pick Test Group Run Order' agents: - queue: n2-4 - depends_on: build - timeout_in_minutes: 150 - key: default-cigroup - retry: - automatic: - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/oss_cigroup.sh - label: 'OSS CI Group' - parallelism: 12 - agents: - queue: ci-group-4d - depends_on: build - timeout_in_minutes: 120 - key: oss-cigroup - retry: - automatic: - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/test/jest_integration.sh - label: 'Jest Integration Tests' - parallelism: 3 - agents: - queue: n2-4 - timeout_in_minutes: 120 - key: jest-integration + queue: kibana-default + env: + JEST_UNIT_SCRIPT: '.buildkite/scripts/steps/test/jest.sh' + JEST_INTEGRATION_SCRIPT: '.buildkite/scripts/steps/test/jest_integration.sh' + FTR_CONFIGS_SCRIPT: '.buildkite/scripts/steps/test/ftr_configs.sh' + LIMIT_CONFIG_TYPE: integration,functional retry: automatic: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/test/api_integration.sh - label: 'API Integration Tests' - agents: - queue: n2-2 - timeout_in_minutes: 120 - key: api-integration - - command: .buildkite/scripts/steps/es_snapshots/trigger_promote.sh label: Trigger promotion timeout_in_minutes: 10 agents: queue: kibana-default depends_on: - - default-cigroup - - oss-cigroup + - ftr-configs - jest-integration - - api-integration - wait: ~ continue_on_failure: true diff --git a/.buildkite/pipelines/flaky_tests/groups.json b/.buildkite/pipelines/flaky_tests/groups.json index e471d5c6a8679..78f8e6e56b904 100644 --- a/.buildkite/pipelines/flaky_tests/groups.json +++ b/.buildkite/pipelines/flaky_tests/groups.json @@ -1,46 +1,20 @@ { "groups": [ { - "key": "oss/cigroup", - "name": "OSS CI Group", - "ciGroups": 12 - }, - { - "key": "oss/firefox", - "name": "OSS Firefox" - }, - { - "key": "oss/accessibility", - "name": "OSS Accessibility" - }, - { - "key": "xpack/cypress/security_solution", + "key": "cypress/security_solution", "name": "Security Solution - Cypress" }, { - "key": "xpack/cypress/osquery_cypress", + "key": "cypress/osquery_cypress", "name": "Osquery - Cypress" }, { - "key": "xpack/cypress/fleet_cypress", + "key": "cypress/fleet_cypress", "name": "Fleet - Cypress" }, { - "key": "xpack/cypress/apm_cypress", + "key": "cypress/apm_cypress", "name": "APM - Cypress" - }, - { - "key": "xpack/cigroup", - "name": "Default CI Group", - "ciGroups": 30 - }, - { - "key": "xpack/firefox", - "name": "Default Firefox" - }, - { - "key": "xpack/accessibility", - "name": "Default Accessibility" } ] } diff --git a/.buildkite/pipelines/flaky_tests/pipeline.js b/.buildkite/pipelines/flaky_tests/pipeline.js index b7f93412edb37..d62156a08c55a 100644 --- a/.buildkite/pipelines/flaky_tests/pipeline.js +++ b/.buildkite/pipelines/flaky_tests/pipeline.js @@ -6,56 +6,179 @@ * Side Public License, v 1. */ +const configJson = process.env.KIBANA_FLAKY_TEST_RUNNER_CONFIG; +if (!configJson) { + console.error('+++ Triggering directly is not supported anymore'); + console.error( + `Please use the "Trigger Flaky Test Runner" UI to run the Flaky Test Runner. You can find the UI at the URL below:` + ); + console.error('\n https://ci-stats.kibana.dev/trigger_flaky_test_runner\n'); + process.exit(1); +} + const groups = /** @type {Array<{key: string, name: string, ciGroups: number }>} */ ( require('./groups.json').groups ); -const stepInput = (key, nameOfSuite) => { - return { - key: `ftsr-suite/${key}`, - text: nameOfSuite, - required: false, - default: '0', +const concurrency = process.env.KIBANA_FLAKY_TEST_CONCURRENCY + ? parseInt(process.env.KIBANA_FLAKY_TEST_CONCURRENCY, 10) + : 25; + +if (Number.isNaN(concurrency)) { + throw new Error( + `invalid KIBANA_FLAKY_TEST_CONCURRENCY: ${process.env.KIBANA_FLAKY_TEST_CONCURRENCY}` + ); +} + +const BASE_JOBS = 1; +const MAX_JOBS = 500; + +function getTestSuitesFromJson(json) { + const fail = (errorMsg) => { + console.error('+++ Invalid test config provided'); + console.error(`${errorMsg}: ${json}`); + process.exit(1); }; -}; -const inputs = [ - { - key: 'ftsr-override-count', - text: 'Override for all suites', - default: '0', - required: true, - }, -]; - -for (const group of groups) { - if (!group.ciGroups) { - inputs.push(stepInput(group.key, group.name)); - } else { - for (let i = 1; i <= group.ciGroups; i++) { - inputs.push(stepInput(`${group.key}/${i}`, `${group.name} ${i}`)); + let parsed; + try { + parsed = JSON.parse(json); + } catch (error) { + fail(`JSON test config did not parse correctly`); + } + + if (!Array.isArray(parsed)) { + fail(`JSON test config must be an array`); + } + + /** @type {Array<{ type: 'group', key: string; count: number } | { type: 'ftrConfig', ftrConfig: string; count: number }>} */ + const testSuites = []; + for (const item of parsed) { + if (typeof item !== 'object' || item === null) { + fail(`testSuites must be objects`); + } + + const count = item.count; + if (typeof count !== 'number') { + fail(`testSuite.count must be a number`); + } + + const type = item.type; + if (type !== 'ftrConfig' && type !== 'group') { + fail(`testSuite.type must be either "ftrConfig" or "group"`); } + + if (item.type === 'ftrConfig') { + const ftrConfig = item.ftrConfig; + if (typeof ftrConfig !== 'string') { + fail(`testSuite.ftrConfig must be a string`); + } + + testSuites.push({ + ftrConfig, + count, + }); + continue; + } + + const key = item.key; + if (typeof key !== 'string') { + fail(`testSuite.key must be a string`); + } + testSuites.push({ + key, + count, + }); } + + return testSuites; +} + +const testSuites = getTestSuitesFromJson(configJson); + +const totalJobs = testSuites.reduce((acc, t) => acc + t.count, BASE_JOBS); + +if (totalJobs > MAX_JOBS) { + console.error('+++ Too many tests'); + console.error( + `Buildkite builds can only contain ${MAX_JOBS} jobs in total. Found ${totalJobs} based on this config. Make sure your test runs are less than ${ + MAX_JOBS - BASE_JOBS + }` + ); + process.exit(1); } +const steps = []; const pipeline = { - steps: [ - { - input: 'Number of Runs - Click Me', - fields: inputs, - if: `build.env('KIBANA_FLAKY_TEST_RUNNER_CONFIG') == null`, - }, - { - wait: '~', - }, - { - command: '.buildkite/pipelines/flaky_tests/runner.sh', - label: 'Create pipeline', + env: { + IGNORE_SHIP_CI_STATS_ERROR: 'true', + }, + steps, +}; + +steps.push({ + command: '.buildkite/scripts/steps/build_kibana.sh', + label: 'Build Kibana Distribution and Plugins', + agents: { queue: 'c2-8' }, + key: 'build', + if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''", +}); + +for (const testSuite of testSuites) { + if (testSuite.count <= 0) { + continue; + } + + if (testSuite.ftrConfig) { + steps.push({ + command: `.buildkite/scripts/steps/test/ftr_configs.sh`, + env: { + FTR_CONFIG: testSuite.ftrConfig, + }, + label: `FTR Config: ${testSuite.ftrConfig}`, + parallelism: testSuite.count, + concurrency: concurrency, + concurrency_group: process.env.UUID, + concurrency_method: 'eager', agents: { - queue: 'kibana-default', + queue: 'n2-4-spot-2', }, - }, - ], -}; + depends_on: 'build', + timeout_in_minutes: 150, + retry: { + automatic: [ + { exit_status: '-1', limit: 3 }, + // { exit_status: '*', limit: 1 }, + ], + }, + }); + continue; + } + + const keyParts = testSuite.key.split('/'); + switch (keyParts[0]) { + case 'cypress': + const CYPRESS_SUITE = keyParts[1]; + const group = groups.find((group) => group.key.includes(CYPRESS_SUITE)); + if (!group) { + throw new Error( + `Group configuration was not found in groups.json for the following cypress suite: {${CYPRESS_SUITE}}.` + ); + } + steps.push({ + command: `.buildkite/scripts/steps/functional/${CYPRESS_SUITE}.sh`, + label: group.name, + agents: { queue: 'ci-group-6' }, + depends_on: 'build', + parallelism: testSuite.count, + concurrency: concurrency, + concurrency_group: process.env.UUID, + concurrency_method: 'eager', + }); + break; + default: + throw new Error(`unknown test suite: ${testSuite.key}`); + } +} console.log(JSON.stringify(pipeline, null, 2)); diff --git a/.buildkite/pipelines/flaky_tests/pipeline.sh b/.buildkite/pipelines/flaky_tests/pipeline.sh index 6335cd5490af0..17fa0152e8b34 100755 --- a/.buildkite/pipelines/flaky_tests/pipeline.sh +++ b/.buildkite/pipelines/flaky_tests/pipeline.sh @@ -2,4 +2,7 @@ set -euo pipefail +UUID="$(cat /proc/sys/kernel/random/uuid)" +export UUID + node .buildkite/pipelines/flaky_tests/pipeline.js | buildkite-agent pipeline upload diff --git a/.buildkite/pipelines/flaky_tests/runner.js b/.buildkite/pipelines/flaky_tests/runner.js deleted file mode 100644 index a5cc237a1814a..0000000000000 --- a/.buildkite/pipelines/flaky_tests/runner.js +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -const { execSync } = require('child_process'); -const groups = /** @type {Array<{key: string, name: string, ciGroups: number }>} */ ( - require('./groups.json').groups -); - -const concurrency = 25; -const defaultCount = concurrency * 2; -const initialJobs = 3; - -function getTestSuitesFromMetadata() { - const keys = execSync('buildkite-agent meta-data keys') - .toString() - .split('\n') - .filter((k) => k.startsWith('ftsr-suite/')); - - const overrideCount = execSync(`buildkite-agent meta-data get 'ftsr-override-count'`) - .toString() - .trim(); - - const testSuites = []; - for (const key of keys) { - if (!key) { - continue; - } - - const value = - overrideCount && overrideCount !== '0' - ? overrideCount - : execSync(`buildkite-agent meta-data get '${key}'`).toString().trim(); - - const count = value === '' ? defaultCount : parseInt(value); - testSuites.push({ - key: key.replace('ftsr-suite/', ''), - count: count, - }); - } - - return testSuites; -} - -function getTestSuitesFromJson(json) { - const fail = (errorMsg) => { - console.error('+++ Invalid test config provided'); - console.error(`${errorMsg}: ${json}`); - process.exit(1); - }; - - let parsed; - try { - parsed = JSON.parse(json); - } catch (error) { - fail(`JSON test config did not parse correctly`); - } - - if (!Array.isArray(parsed)) { - fail(`JSON test config must be an array`); - } - - /** @type {Array<{ key: string, count: number }>} */ - const testSuites = []; - for (const item of parsed) { - if (typeof item !== 'object' || item === null) { - fail(`testSuites must be objects`); - } - const key = item.key; - if (typeof key !== 'string') { - fail(`testSuite.key must be a string`); - } - const count = item.count; - if (typeof count !== 'number') { - fail(`testSuite.count must be a number`); - } - testSuites.push({ - key, - count, - }); - } - - return testSuites; -} - -const testSuites = process.env.KIBANA_FLAKY_TEST_RUNNER_CONFIG - ? getTestSuitesFromJson(process.env.KIBANA_FLAKY_TEST_RUNNER_CONFIG) - : getTestSuitesFromMetadata(); - -const totalJobs = testSuites.reduce((acc, t) => acc + t.count, initialJobs); - -if (totalJobs > 500) { - console.error('+++ Too many tests'); - console.error( - `Buildkite builds can only contain 500 steps in total. Found ${totalJobs} in total. Make sure your test runs are less than ${ - 500 - initialJobs - }` - ); - process.exit(1); -} - -const steps = []; -const pipeline = { - env: { - IGNORE_SHIP_CI_STATS_ERROR: 'true', - }, - steps: steps, -}; - -steps.push({ - command: '.buildkite/scripts/steps/build_kibana.sh', - label: 'Build Kibana Distribution and Plugins', - agents: { queue: 'c2-8' }, - key: 'build', - if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''", -}); - -for (const testSuite of testSuites) { - const TEST_SUITE = testSuite.key; - const RUN_COUNT = testSuite.count; - const UUID = process.env.UUID; - - const JOB_PARTS = TEST_SUITE.split('/'); - const IS_XPACK = JOB_PARTS[0] === 'xpack'; - const TASK = JOB_PARTS[1]; - const CI_GROUP = JOB_PARTS.length > 2 ? JOB_PARTS[2] : ''; - - if (RUN_COUNT < 1) { - continue; - } - - switch (TASK) { - case 'cigroup': - if (IS_XPACK) { - steps.push({ - command: `CI_GROUP=${CI_GROUP} .buildkite/scripts/steps/functional/xpack_cigroup.sh`, - label: `Default CI Group ${CI_GROUP}`, - agents: { queue: 'n2-4' }, - depends_on: 'build', - parallelism: RUN_COUNT, - concurrency: concurrency, - concurrency_group: UUID, - concurrency_method: 'eager', - }); - } else { - steps.push({ - command: `CI_GROUP=${CI_GROUP} .buildkite/scripts/steps/functional/oss_cigroup.sh`, - label: `OSS CI Group ${CI_GROUP}`, - agents: { queue: 'ci-group-4d' }, - depends_on: 'build', - parallelism: RUN_COUNT, - concurrency: concurrency, - concurrency_group: UUID, - concurrency_method: 'eager', - }); - } - break; - - case 'firefox': - steps.push({ - command: `.buildkite/scripts/steps/functional/${IS_XPACK ? 'xpack' : 'oss'}_firefox.sh`, - label: `${IS_XPACK ? 'Default' : 'OSS'} Firefox`, - agents: { queue: IS_XPACK ? 'n2-4' : 'ci-group-4d' }, - depends_on: 'build', - parallelism: RUN_COUNT, - concurrency: concurrency, - concurrency_group: UUID, - concurrency_method: 'eager', - }); - break; - - case 'accessibility': - steps.push({ - command: `.buildkite/scripts/steps/functional/${ - IS_XPACK ? 'xpack' : 'oss' - }_accessibility.sh`, - label: `${IS_XPACK ? 'Default' : 'OSS'} Accessibility`, - agents: { queue: IS_XPACK ? 'n2-4' : 'ci-group-4d' }, - depends_on: 'build', - parallelism: RUN_COUNT, - concurrency: concurrency, - concurrency_group: UUID, - concurrency_method: 'eager', - }); - break; - - case 'cypress': - const CYPRESS_SUITE = CI_GROUP; - const group = groups.find((group) => group.key.includes(CYPRESS_SUITE)); - if (!group) { - throw new Error( - `Group configuration was not found in groups.json for the following cypress suite: {${CYPRESS_SUITE}}.` - ); - } - steps.push({ - command: `.buildkite/scripts/steps/functional/${CYPRESS_SUITE}.sh`, - label: group.name, - agents: { queue: 'ci-group-6' }, - depends_on: 'build', - parallelism: RUN_COUNT, - concurrency: concurrency, - concurrency_group: UUID, - concurrency_method: 'eager', - }); - break; - } -} - -console.log(JSON.stringify(pipeline, null, 2)); diff --git a/.buildkite/pipelines/flaky_tests/runner.sh b/.buildkite/pipelines/flaky_tests/runner.sh deleted file mode 100755 index b541af88a408a..0000000000000 --- a/.buildkite/pipelines/flaky_tests/runner.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -UUID="$(cat /proc/sys/kernel/random/uuid)" -export UUID - -node .buildkite/pipelines/flaky_tests/runner.js | buildkite-agent pipeline upload diff --git a/.buildkite/pipelines/on_merge.yml b/.buildkite/pipelines/on_merge.yml index 8b94f4d7c22c7..586199f082925 100644 --- a/.buildkite/pipelines/on_merge.yml +++ b/.buildkite/pipelines/on_merge.yml @@ -49,149 +49,19 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/xpack_cigroup.sh - label: 'Default CI Group' - parallelism: 31 + - command: .buildkite/scripts/steps/test/pick_test_group_run_order.sh + label: 'Pick Test Group Run Order' agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 250 - key: default-cigroup - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/oss_cigroup.sh - label: 'OSS CI Group' - parallelism: 12 - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - key: oss-cigroup - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/oss_accessibility.sh - label: 'OSS Accessibility Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/xpack_accessibility.sh - label: 'Default Accessibility Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/oss_firefox.sh - label: 'OSS Firefox Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/xpack_firefox.sh - label: 'Default Firefox Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/oss_misc.sh - label: 'OSS Misc Functional Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/xpack_saved_object_field_metrics.sh - label: 'Saved Object Field Metrics' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 + queue: kibana-default + env: + JEST_UNIT_SCRIPT: '.buildkite/scripts/steps/test/jest.sh' + JEST_INTEGRATION_SCRIPT: '.buildkite/scripts/steps/test/jest_integration.sh' + FTR_CONFIGS_SCRIPT: '.buildkite/scripts/steps/test/ftr_configs.sh' retry: automatic: - - exit_status: '-1' - limit: 3 - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/test/jest.sh - label: 'Jest Tests' - parallelism: 8 - agents: - queue: n2-4-spot - timeout_in_minutes: 90 - key: jest - retry: - automatic: - - exit_status: '-1' - limit: 3 - - - command: .buildkite/scripts/steps/test/jest_integration.sh - label: 'Jest Integration Tests' - parallelism: 3 - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - key: jest-integration - retry: - automatic: - - exit_status: '-1' - limit: 3 - - - command: .buildkite/scripts/steps/test/api_integration.sh - label: 'API Integration Tests' - agents: - queue: n2-2-spot - timeout_in_minutes: 120 - key: api-integration - retry: - automatic: - - exit_status: '-1' - limit: 3 - - command: .buildkite/scripts/steps/lint.sh label: 'Linting' agents: @@ -212,10 +82,19 @@ steps: - command: .buildkite/scripts/steps/checks.sh label: 'Checks' + agents: + queue: n2-2-spot + timeout_in_minutes: 60 + retry: + automatic: + - exit_status: '-1' + limit: 3 + + - command: .buildkite/scripts/steps/check_types.sh + label: 'Check Types' agents: queue: c2-8 - key: checks - timeout_in_minutes: 120 + timeout_in_minutes: 60 - command: .buildkite/scripts/steps/storybooks/build_and_upload.sh label: 'Build Storybooks' diff --git a/.buildkite/pipelines/performance/daily.yml b/.buildkite/pipelines/performance/daily.yml index 658ab3a72f186..07e73c27508a6 100644 --- a/.buildkite/pipelines/performance/daily.yml +++ b/.buildkite/pipelines/performance/daily.yml @@ -17,6 +17,13 @@ steps: agents: queue: kb-static-ubuntu depends_on: build + key: tests + + - label: ':shipit: Performance Tests dataset extraction for scalability benchmarking' + command: .buildkite/scripts/steps/functional/scalability_dataset_extraction.sh + agents: + queue: n2-2 + depends_on: tests - wait: ~ continue_on_failure: true diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml index fc12a96964fb2..73c03e0382a0f 100644 --- a/.buildkite/pipelines/pull_request/base.yml +++ b/.buildkite/pipelines/pull_request/base.yml @@ -15,149 +15,19 @@ steps: if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''" timeout_in_minutes: 60 - - command: .buildkite/scripts/steps/functional/xpack_cigroup.sh - label: 'Default CI Group' - parallelism: 31 + - command: .buildkite/scripts/steps/test/pick_test_group_run_order.sh + label: 'Pick Test Group Run Order' agents: - queue: n2-4-spot-2 - depends_on: build - timeout_in_minutes: 150 - key: default-cigroup - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/oss_cigroup.sh - label: 'OSS CI Group' - parallelism: 12 - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - key: oss-cigroup - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/oss_accessibility.sh - label: 'OSS Accessibility Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/xpack_accessibility.sh - label: 'Default Accessibility Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/oss_firefox.sh - label: 'OSS Firefox Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/xpack_firefox.sh - label: 'Default Firefox Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/oss_misc.sh - label: 'OSS Misc Functional Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/xpack_saved_object_field_metrics.sh - label: 'Saved Object Field Metrics' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 + queue: kibana-default + env: + JEST_UNIT_SCRIPT: '.buildkite/scripts/steps/test/jest.sh' + JEST_INTEGRATION_SCRIPT: '.buildkite/scripts/steps/test/jest_integration.sh' + FTR_CONFIGS_SCRIPT: '.buildkite/scripts/steps/test/ftr_configs.sh' retry: automatic: - - exit_status: '-1' - limit: 3 - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/test/jest.sh - label: 'Jest Tests' - parallelism: 8 - agents: - queue: n2-4-spot - timeout_in_minutes: 90 - key: jest - retry: - automatic: - - exit_status: '-1' - limit: 3 - - - command: .buildkite/scripts/steps/test/jest_integration.sh - label: 'Jest Integration Tests' - parallelism: 3 - agents: - queue: n2-4-spot - timeout_in_minutes: 120 - key: jest-integration - retry: - automatic: - - exit_status: '-1' - limit: 3 - - - command: .buildkite/scripts/steps/test/api_integration.sh - label: 'API Integration Tests' - agents: - queue: n2-2-spot - timeout_in_minutes: 120 - key: api-integration - retry: - automatic: - - exit_status: '-1' - limit: 3 - - command: .buildkite/scripts/steps/lint.sh label: 'Linting' agents: @@ -174,10 +44,19 @@ steps: - command: .buildkite/scripts/steps/checks.sh label: 'Checks' + agents: + queue: n2-2-spot + timeout_in_minutes: 60 + retry: + automatic: + - exit_status: '-1' + limit: 3 + + - command: .buildkite/scripts/steps/check_types.sh + label: 'Check Types' agents: queue: c2-8 - key: checks - timeout_in_minutes: 120 + timeout_in_minutes: 60 - command: .buildkite/scripts/steps/storybooks/build_and_upload.sh label: 'Build Storybooks' diff --git a/.buildkite/pipelines/pull_request/response_ops.yml b/.buildkite/pipelines/pull_request/response_ops.yml index 846477170409b..87f6a60c85c5a 100644 --- a/.buildkite/pipelines/pull_request/response_ops.yml +++ b/.buildkite/pipelines/pull_request/response_ops.yml @@ -1,6 +1,6 @@ steps: - - command: .buildkite/scripts/steps/functional/response_ops_cases.sh - label: 'Cases Cypress Tests on Security Solution' + - command: .buildkite/scripts/steps/functional/response_ops.sh + label: 'Rules, Alerts and Exceptions ResponseOps Cypress Tests on Security Solution' agents: queue: ci-group-6 depends_on: build diff --git a/.buildkite/pipelines/pull_request/response_ops_cases.yml b/.buildkite/pipelines/pull_request/response_ops_cases.yml new file mode 100644 index 0000000000000..846477170409b --- /dev/null +++ b/.buildkite/pipelines/pull_request/response_ops_cases.yml @@ -0,0 +1,11 @@ +steps: + - command: .buildkite/scripts/steps/functional/response_ops_cases.sh + label: 'Cases Cypress Tests on Security Solution' + agents: + queue: ci-group-6 + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '*' + limit: 1 diff --git a/.buildkite/scripts/bootstrap.sh b/.buildkite/scripts/bootstrap.sh index 34461ca6db194..b4ec748b863e2 100755 --- a/.buildkite/scripts/bootstrap.sh +++ b/.buildkite/scripts/bootstrap.sh @@ -6,6 +6,15 @@ source .buildkite/scripts/common/util.sh source .buildkite/scripts/common/setup_bazel.sh echo "--- yarn install and bootstrap" + +# Use the node_modules that is baked into the agent image, if it exists, as a cache +# But only for agents not mounting the workspace on a local ssd or in memory +# It actually ends up being slower to move all of the tiny files between the disks vs extracting archives from the yarn cache +if [[ -d ~/.kibana/node_modules && "$(pwd)" != *"/local-ssd/"* && "$(pwd)" != "/dev/shm"* ]]; then + echo "Using ~/.kibana/node_modules as a starting point" + mv ~/.kibana/node_modules ./ +fi + if ! yarn kbn bootstrap; then echo "bootstrap failed, trying again in 15 seconds" sleep 15 diff --git a/.buildkite/scripts/common/env.sh b/.buildkite/scripts/common/env.sh index 5d2ccda66285b..82c42af67f226 100755 --- a/.buildkite/scripts/common/env.sh +++ b/.buildkite/scripts/common/env.sh @@ -98,3 +98,11 @@ fi export BUILD_TS_REFS_DISABLE=true export DISABLE_BOOTSTRAP_VALIDATION=true + +# Prevent Browserlist from logging on CI about outdated database versions +export BROWSERSLIST_IGNORE_OLD_DATA=true + +# keys used to associate test group data in ci-stats with Jest execution order +export TEST_GROUP_TYPE_UNIT="Jest Unit Tests" +export TEST_GROUP_TYPE_INTEGRATION="Jest Integration Tests" +export TEST_GROUP_TYPE_FUNCTIONAL="Functional Tests" diff --git a/.buildkite/scripts/common/setup_bazel.sh b/.buildkite/scripts/common/setup_bazel.sh index 96cd04fa612fd..e3791dfa393c7 100755 --- a/.buildkite/scripts/common/setup_bazel.sh +++ b/.buildkite/scripts/common/setup_bazel.sh @@ -11,8 +11,29 @@ cat < $KIBANA_DIR/.bazelrc build --build_metadata=ROLE=CI EOF -if [[ "${BAZEL_CACHE_MODE:-none}" == read* ]]; then - echo "[bazel] enabling caching" +BAZEL_CACHE_MODE=${BAZEL_CACHE_MODE:-gcs} + +if [[ "$BAZEL_CACHE_MODE" == "gcs" ]]; then + echo "[bazel] enabling caching with GCS buckets" + + BAZEL_REGION="us-central1" + if [[ "$(curl -is metadata.google.internal || true)" ]]; then + # projects/1003139005402/zones/us-central1-a -> us-central1-a -> us-central1 + BAZEL_REGION=$(curl -sH Metadata-Flavor:Google http://metadata.google.internal/computeMetadata/v1/instance/zone | rev | cut -d'/' -f1 | cut -c3- | rev) + fi + + BAZEL_BUCKET="kibana-ci-bazel_$BAZEL_REGION" + + echo "[bazel] using GCS bucket: $BAZEL_BUCKET" + +cat <> $KIBANA_DIR/.bazelrc + build --remote_cache=https://storage.googleapis.com/$BAZEL_BUCKET + build --google_default_credentials +EOF +fi + +if [[ "$BAZEL_CACHE_MODE" == "buildbuddy" ]]; then + echo "[bazel] enabling caching with Buildbuddy" cat <> $KIBANA_DIR/.bazelrc build --bes_results_url=https://app.buildbuddy.io/invocation/ build --bes_backend=grpcs://remote.buildbuddy.io @@ -22,14 +43,7 @@ cat <> $KIBANA_DIR/.bazelrc EOF fi -if [[ "${BAZEL_CACHE_MODE:-none}" == "read" ]]; then - echo "[bazel] cache set to read-only" -cat <> $KIBANA_DIR/.bazelrc - build --noremote_upload_local_results -EOF -fi - -if [[ "${BAZEL_CACHE_MODE:-none}" != @(read|read-write|none|) ]]; then - echo "invalid value for BAZEL_CACHE_MODE received ($BAZEL_CACHE_MODE), expected one of [read,read-write,none]" +if [[ "$BAZEL_CACHE_MODE" != @(gcs|buildbuddy|none|) ]]; then + echo "invalid value for BAZEL_CACHE_MODE received ($BAZEL_CACHE_MODE), expected one of [gcs,buildbuddy,none]" exit 1 fi diff --git a/.buildkite/scripts/lifecycle/pre_command.sh b/.buildkite/scripts/lifecycle/pre_command.sh index e7a176a5c2666..8f3776db3ca6b 100755 --- a/.buildkite/scripts/lifecycle/pre_command.sh +++ b/.buildkite/scripts/lifecycle/pre_command.sh @@ -9,21 +9,7 @@ export BUILDKITE_TOKEN echo '--- Install buildkite dependencies' cd '.buildkite' - -# If this yarn install is terminated early, e.g. if the build is cancelled in buildkite, -# A node module could end up in a bad state that can cause all future builds to fail -# So, let's cache clean and try again to make sure that's not what caused the error -install_deps() { - yarn install --production --pure-lockfile - EXIT=$? - if [[ "$EXIT" != "0" ]]; then - yarn cache clean - fi - return $EXIT -} - -retry 5 15 install_deps - +retry 5 15 npm ci cd .. echo '--- Agent Debug/SSH Info' diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.js b/.buildkite/scripts/pipelines/pull_request/pipeline.js index fa167d9f324b4..6a4610284e400 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.js +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.js @@ -9,18 +9,7 @@ const execSync = require('child_process').execSync; const fs = require('fs'); const { areChangesSkippable, doAnyChangesMatch } = require('kibana-buildkite-library'); - -const SKIPPABLE_PATHS = [ - /^docs\//, - /^rfcs\//, - /^.ci\/.+\.yml$/, - /^.ci\/es-snapshots\//, - /^.ci\/pipeline-library\//, - /^.ci\/Jenkinsfile_[^\/]+$/, - /^\.github\//, - /\.md$/, - /^\.backportrc\.json$/, -]; +const { SKIPPABLE_PR_MATCHERS } = require('./skippable_pr_matchers'); const REQUIRED_PATHS = [ // this file is auto-generated and changes to it need to be validated with CI @@ -46,7 +35,7 @@ const uploadPipeline = (pipelineContent) => { (async () => { try { - const skippable = await areChangesSkippable(SKIPPABLE_PATHS, REQUIRED_PATHS); + const skippable = await areChangesSkippable(SKIPPABLE_PR_MATCHERS, REQUIRED_PATHS); if (skippable) { console.log('All changes in PR are skippable. Skipping CI.'); @@ -64,12 +53,12 @@ const uploadPipeline = (pipelineContent) => { if ( (await doAnyChangesMatch([ - /^x-pack\/plugins\/security_solution/, /^x-pack\/plugins\/lists/, + /^x-pack\/plugins\/security_solution/, /^x-pack\/plugins\/timelines/, - /^x-pack\/test\/security_solution_cypress/, /^x-pack\/plugins\/triggers_actions_ui\/public\/application\/sections\/action_connector_form/, /^x-pack\/plugins\/triggers_actions_ui\/public\/application\/context\/actions_connectors_context\.tsx/, + /^x-pack\/test\/security_solution_cypress/, ])) || process.env.GITHUB_PR_LABELS.includes('ci:all-cypress-suites') ) { @@ -77,12 +66,26 @@ const uploadPipeline = (pipelineContent) => { } if ( - (await doAnyChangesMatch([/^x-pack\/plugins\/cases/])) || + (await doAnyChangesMatch([ + /^src\/plugins\/data/, + /^x-pack\/plugins\/actions/, + /^x-pack\/plugins\/alerting/, + /^x-pack\/plugins\/event_log/, + /^x-pack\/plugins\/rule_registry/, + /^x-pack\/plugins\/task_manager/, + ])) || process.env.GITHUB_PR_LABELS.includes('ci:all-cypress-suites') ) { pipeline.push(getPipeline('.buildkite/pipelines/pull_request/response_ops.yml')); } + if ( + (await doAnyChangesMatch([/^x-pack\/plugins\/cases/])) || + process.env.GITHUB_PR_LABELS.includes('ci:all-cypress-suites') + ) { + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/response_ops_cases.yml')); + } + if ( (await doAnyChangesMatch([/^x-pack\/plugins\/apm/])) || process.env.GITHUB_PR_LABELS.includes('ci:all-cypress-suites') diff --git a/.buildkite/scripts/pipelines/pull_request/skippable_pr_matchers.js b/.buildkite/scripts/pipelines/pull_request/skippable_pr_matchers.js new file mode 100644 index 0000000000000..2a36e66e11cd6 --- /dev/null +++ b/.buildkite/scripts/pipelines/pull_request/skippable_pr_matchers.js @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + SKIPPABLE_PR_MATCHERS: [ + /^docs\//, + /^rfcs\//, + /^.ci\/.+\.yml$/, + /^.ci\/es-snapshots\//, + /^.ci\/pipeline-library\//, + /^.ci\/Jenkinsfile_[^\/]+$/, + /^\.github\//, + /\.md$/, + /^\.backportrc\.json$/, + /^nav-kibana-dev\.docnav\.json$/, + /^src\/dev\/prs\/kibana_qa_pr_list\.json$/, + /^\.buildkite\/scripts\/pipelines\/pull_request\/skippable_pr_matchers\.js$/, + ], +}; diff --git a/.buildkite/scripts/saved_object_field_metrics.sh b/.buildkite/scripts/saved_object_field_metrics.sh index 3b6c63eeff3b0..4cc249db20edc 100755 --- a/.buildkite/scripts/saved_object_field_metrics.sh +++ b/.buildkite/scripts/saved_object_field_metrics.sh @@ -5,9 +5,8 @@ set -euo pipefail source .buildkite/scripts/common/util.sh echo '--- Default Saved Object Field Metrics' -cd "$XPACK_DIR" checks-reporter-with-killswitch "Capture Kibana Saved Objects field count metrics" \ node scripts/functional_tests \ --debug --bail \ --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ - --config test/saved_objects_field_count/config.ts + --config x-pack/test/saved_objects_field_count/config.ts diff --git a/.buildkite/scripts/steps/bazel_cache/bootstrap_mac.sh b/.buildkite/scripts/steps/bazel_cache/bootstrap_mac.sh index 1417137f0b6f0..62aabf496fd8a 100755 --- a/.buildkite/scripts/steps/bazel_cache/bootstrap_mac.sh +++ b/.buildkite/scripts/steps/bazel_cache/bootstrap_mac.sh @@ -2,7 +2,7 @@ set -euo pipefail -export BAZEL_CACHE_MODE=read-write +export BAZEL_CACHE_MODE=buildbuddy export DISABLE_BOOTSTRAP_VALIDATION=true # Since our Mac agents are currently static, diff --git a/.buildkite/scripts/steps/checks/check_types.sh b/.buildkite/scripts/steps/check_types.sh similarity index 84% rename from .buildkite/scripts/steps/checks/check_types.sh rename to .buildkite/scripts/steps/check_types.sh index 3b649a73e8060..eb7a41d74e8d8 100755 --- a/.buildkite/scripts/steps/checks/check_types.sh +++ b/.buildkite/scripts/steps/check_types.sh @@ -4,6 +4,8 @@ set -euo pipefail source .buildkite/scripts/common/util.sh +.buildkite/scripts/bootstrap.sh + echo --- Check Types checks-reporter-with-killswitch "Check Types" \ node scripts/type_check diff --git a/.buildkite/scripts/steps/checks.sh b/.buildkite/scripts/steps/checks.sh index cae019150b626..024037a8a4bb9 100755 --- a/.buildkite/scripts/steps/checks.sh +++ b/.buildkite/scripts/steps/checks.sh @@ -13,7 +13,6 @@ export DISABLE_BOOTSTRAP_VALIDATION=false .buildkite/scripts/steps/checks/doc_api_changes.sh .buildkite/scripts/steps/checks/kbn_pm_dist.sh .buildkite/scripts/steps/checks/plugin_list_docs.sh -.buildkite/scripts/steps/checks/check_types.sh .buildkite/scripts/steps/checks/bundle_limits.sh .buildkite/scripts/steps/checks/i18n.sh .buildkite/scripts/steps/checks/file_casing.sh diff --git a/.buildkite/scripts/steps/functional/fleet_cypress.sh b/.buildkite/scripts/steps/functional/fleet_cypress.sh index 3847ffda08822..7207a2adf454a 100755 --- a/.buildkite/scripts/steps/functional/fleet_cypress.sh +++ b/.buildkite/scripts/steps/functional/fleet_cypress.sh @@ -11,10 +11,8 @@ export JOB=kibana-fleet-cypress echo "--- Fleet Cypress tests" -cd "$XPACK_DIR" - checks-reporter-with-killswitch "Fleet Cypress Tests" \ node scripts/functional_tests \ --debug --bail \ --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ - --config test/fleet_cypress/cli_config.ts + --config x-pack/test/fleet_cypress/cli_config.ts diff --git a/.buildkite/scripts/steps/functional/osquery_cypress.sh b/.buildkite/scripts/steps/functional/osquery_cypress.sh index a0d98713aa379..02766e0530bfb 100755 --- a/.buildkite/scripts/steps/functional/osquery_cypress.sh +++ b/.buildkite/scripts/steps/functional/osquery_cypress.sh @@ -12,10 +12,8 @@ export JOB=kibana-osquery-cypress echo "--- Osquery Cypress tests" -cd "$XPACK_DIR" - checks-reporter-with-killswitch "Osquery Cypress Tests" \ node scripts/functional_tests \ --debug --bail \ - --config test/osquery_cypress/cli_config.ts + --config x-pack/test/osquery_cypress/cli_config.ts diff --git a/.buildkite/scripts/steps/functional/oss_accessibility.sh b/.buildkite/scripts/steps/functional/oss_accessibility.sh deleted file mode 100755 index e8c65cfa760b2..0000000000000 --- a/.buildkite/scripts/steps/functional/oss_accessibility.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh - -echo --- OSS Accessibility Tests - -checks-reporter-with-killswitch "Kibana accessibility tests" \ - node scripts/functional_tests \ - --debug --bail \ - --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ - --config test/accessibility/config.ts diff --git a/.buildkite/scripts/steps/functional/oss_cigroup.sh b/.buildkite/scripts/steps/functional/oss_cigroup.sh deleted file mode 100755 index a7a5960a41afe..0000000000000 --- a/.buildkite/scripts/steps/functional/oss_cigroup.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh - -export CI_GROUP=${CI_GROUP:-$((BUILDKITE_PARALLEL_JOB+1))} -export JOB=kibana-oss-ciGroup${CI_GROUP} - -echo "--- OSS CI Group $CI_GROUP" - -checks-reporter-with-killswitch "Functional tests / Group ${CI_GROUP}" \ - node scripts/functional_tests \ - --bail \ - --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ - --include-tag "ciGroup$CI_GROUP" diff --git a/.buildkite/scripts/steps/functional/oss_firefox.sh b/.buildkite/scripts/steps/functional/oss_firefox.sh deleted file mode 100755 index e953973da62d6..0000000000000 --- a/.buildkite/scripts/steps/functional/oss_firefox.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh - -echo --- OSS Firefox Smoke Tests - -checks-reporter-with-killswitch "Firefox smoke test" \ - node scripts/functional_tests \ - --bail --debug \ - --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ - --include-tag "includeFirefox" \ - --config test/functional/config.firefox.js diff --git a/.buildkite/scripts/steps/functional/oss_misc.sh b/.buildkite/scripts/steps/functional/oss_misc.sh deleted file mode 100755 index 22d4eda608cc2..0000000000000 --- a/.buildkite/scripts/steps/functional/oss_misc.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh - -# Required, at least for plugin_functional tests -.buildkite/scripts/build_kibana_plugins.sh - -echo --- Plugin Functional Tests -checks-reporter-with-killswitch "Plugin Functional Tests" \ - node scripts/functional_tests \ - --config test/plugin_functional/config.ts \ - --bail \ - --debug - -echo --- Interpreter Functional Tests -checks-reporter-with-killswitch "Interpreter Functional Tests" \ - node scripts/functional_tests \ - --config test/interpreter_functional/config.ts \ - --bail \ - --debug \ - --kibana-install-dir "$KIBANA_BUILD_LOCATION" - -echo --- Server Integration Tests -checks-reporter-with-killswitch "Server Integration Tests" \ - node scripts/functional_tests \ - --config test/server_integration/http/ssl/config.js \ - --config test/server_integration/http/ssl_redirect/config.js \ - --config test/server_integration/http/platform/config.ts \ - --config test/server_integration/http/ssl_with_p12/config.js \ - --config test/server_integration/http/ssl_with_p12_intermediate/config.js \ - --bail \ - --debug \ - --kibana-install-dir "$KIBANA_BUILD_LOCATION" - -# Tests that must be run against source in order to build test plugins -echo --- Status Integration Tests -checks-reporter-with-killswitch "Status Integration Tests" \ - node scripts/functional_tests \ - --config test/server_integration/http/platform/config.status.ts \ - --bail \ - --debug - -# Tests that must be run against source in order to build test plugins -echo --- Analytics Integration Tests -checks-reporter-with-killswitch "Analytics Integration Tests" \ - node scripts/functional_tests \ - --config test/analytics/config.ts \ - --bail \ - --debug diff --git a/.buildkite/scripts/steps/functional/performance_playwright.sh b/.buildkite/scripts/steps/functional/performance_playwright.sh index dad75c9f66a98..a1c3f23ced51e 100644 --- a/.buildkite/scripts/steps/functional/performance_playwright.sh +++ b/.buildkite/scripts/steps/functional/performance_playwright.sh @@ -18,8 +18,6 @@ export TEST_ES_DISABLE_STARTUP=true sleep 120 -cd "$XPACK_DIR" - journeys=("login" "ecommerce_dashboard" "flight_dashboard" "web_logs_dashboard" "promotion_tracking_dashboard" "many_fields_discover") for i in "${journeys[@]}"; do @@ -31,8 +29,8 @@ for i in "${journeys[@]}"; do checks-reporter-with-killswitch "Run Performance Tests with Playwright Config (Journey:${i},Phase: WARMUP)" \ node scripts/functional_tests \ - --config test/performance/config.playwright.ts \ - --include "test/performance/tests/playwright/${i}.ts" \ + --config x-pack/test/performance/config.playwright.ts \ + --include "x-pack/test/performance/tests/playwright/${i}.ts" \ --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ --debug \ --bail @@ -42,8 +40,8 @@ for i in "${journeys[@]}"; do checks-reporter-with-killswitch "Run Performance Tests with Playwright Config (Journey:${i},Phase: TEST)" \ node scripts/functional_tests \ - --config test/performance/config.playwright.ts \ - --include "test/performance/tests/playwright/${i}.ts" \ + --config x-pack/test/performance/config.playwright.ts \ + --include "x-pack/test/performance/tests/playwright/${i}.ts" \ --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ --debug \ --bail diff --git a/.buildkite/scripts/steps/functional/response_ops.sh b/.buildkite/scripts/steps/functional/response_ops.sh new file mode 100755 index 0000000000000..9828884e6d6a2 --- /dev/null +++ b/.buildkite/scripts/steps/functional/response_ops.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/steps/functional/common.sh + +export JOB=kibana-security-solution-chrome + +echo "--- Response Ops Cypress Tests on Security Solution" + +checks-reporter-with-killswitch "Response Ops Cypress Tests on Security Solution" \ + node scripts/functional_tests \ + --debug --bail \ + --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ + --config x-pack/test/security_solution_cypress/response_ops_cli_config.ts diff --git a/.buildkite/scripts/steps/functional/response_ops_cases.sh b/.buildkite/scripts/steps/functional/response_ops_cases.sh index 13d0ef52130a3..2485e025833ed 100755 --- a/.buildkite/scripts/steps/functional/response_ops_cases.sh +++ b/.buildkite/scripts/steps/functional/response_ops_cases.sh @@ -8,10 +8,8 @@ export JOB=kibana-security-solution-chrome echo "--- Response Ops Cases Cypress Tests on Security Solution" -cd "$XPACK_DIR" - checks-reporter-with-killswitch "Response Ops Cases Cypress Tests on Security Solution" \ node scripts/functional_tests \ --debug --bail \ --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ - --config test/security_solution_cypress/cases_cli_config.ts + --config x-pack/test/security_solution_cypress/cases_cli_config.ts diff --git a/.buildkite/scripts/steps/functional/scalability_dataset_extraction.sh b/.buildkite/scripts/steps/functional/scalability_dataset_extraction.sh new file mode 100755 index 0000000000000..1a8bd77bd2893 --- /dev/null +++ b/.buildkite/scripts/steps/functional/scalability_dataset_extraction.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/common/util.sh + +USER_FROM_VAULT="$(retry 5 5 vault read -field=username secret/kibana-issues/dev/apm_parser_performance)" +PASS_FROM_VAULT="$(retry 5 5 vault read -field=password secret/kibana-issues/dev/apm_parser_performance)" +ES_SERVER_URL="https://kibana-ops-e2e-perf.es.us-central1.gcp.cloud.es.io:9243" +BUILD_ID=${BUILDKITE_BUILD_ID} +GCS_BUCKET="gs://kibana-performance/scalability-tests" + +.buildkite/scripts/bootstrap.sh + +echo "--- Extract APM metrics" +journeys=("login" "ecommerce_dashboard" "flight_dashboard" "web_logs_dashboard" "promotion_tracking_dashboard" "many_fields_discover") + +for i in "${journeys[@]}"; do + JOURNEY_NAME="${i}" + echo "Looking for JOURNEY=${JOURNEY_NAME} and BUILD_ID=${BUILD_ID} in APM traces" + + node scripts/extract_performance_testing_dataset \ + --journeyName "${JOURNEY_NAME}" \ + --buildId "${BUILD_ID}" \ + --es-url "${ES_SERVER_URL}" \ + --es-username "${USER_FROM_VAULT}" \ + --es-password "${PASS_FROM_VAULT}" +done + +echo "--- Upload Kibana build, plugins and scalability traces to the public bucket" +mkdir "${BUILD_ID}" +# Archive json files with traces and upload as build artifacts +tar -czf "${BUILD_ID}/scalability_traces.tar.gz" -C target scalability_traces +buildkite-agent artifact upload "${BUILD_ID}/scalability_traces.tar.gz" +# Upload Kibana build, plugins, commit sha and traces to the bucket +buildkite-agent artifact download kibana-default.tar.gz ./"${BUILD_ID}" +buildkite-agent artifact download kibana-default-plugins.tar.gz ./"${BUILD_ID}" +echo "${BUILDKITE_COMMIT}" > "${BUILD_ID}/KIBANA_COMMIT_HASH" +gsutil -m cp -r "${BUILD_ID}" "${GCS_BUCKET}" +echo "--- Update reference to the latest CI build" +echo "${BUILD_ID}" > LATEST +gsutil cp LATEST "${GCS_BUCKET}" \ No newline at end of file diff --git a/.buildkite/scripts/steps/functional/security_solution.sh b/.buildkite/scripts/steps/functional/security_solution.sh index 9b2bfc7207a95..ae81eaa4f48e2 100755 --- a/.buildkite/scripts/steps/functional/security_solution.sh +++ b/.buildkite/scripts/steps/functional/security_solution.sh @@ -8,10 +8,8 @@ export JOB=kibana-security-solution-chrome echo "--- Security Solution tests (Chrome)" -cd "$XPACK_DIR" - checks-reporter-with-killswitch "Security Solution Cypress Tests (Chrome)" \ node scripts/functional_tests \ --debug --bail \ --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ - --config test/security_solution_cypress/cli_config.ts + --config x-pack/test/security_solution_cypress/cli_config.ts diff --git a/.buildkite/scripts/steps/functional/xpack_accessibility.sh b/.buildkite/scripts/steps/functional/xpack_accessibility.sh deleted file mode 100755 index 5b098da858c96..0000000000000 --- a/.buildkite/scripts/steps/functional/xpack_accessibility.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh - -cd "$XPACK_DIR" - -echo --- Default Accessibility Tests - -checks-reporter-with-killswitch "X-Pack accessibility tests" \ - node scripts/functional_tests \ - --debug --bail \ - --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ - --config test/accessibility/config.ts diff --git a/.buildkite/scripts/steps/functional/xpack_cigroup.sh b/.buildkite/scripts/steps/functional/xpack_cigroup.sh deleted file mode 100755 index 6877e88cff8b3..0000000000000 --- a/.buildkite/scripts/steps/functional/xpack_cigroup.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh - -export CI_GROUP=${CI_GROUP:-$((BUILDKITE_PARALLEL_JOB+1))} -export JOB=kibana-default-ciGroup${CI_GROUP} - -echo "--- Default CI Group $CI_GROUP" - -cd "$XPACK_DIR" - -checks-reporter-with-killswitch "X-Pack Chrome Functional tests / Group ${CI_GROUP}" \ - node scripts/functional_tests \ - --bail \ - --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ - --include-tag "ciGroup$CI_GROUP" - -cd "$KIBANA_DIR" diff --git a/.buildkite/scripts/steps/functional/xpack_firefox.sh b/.buildkite/scripts/steps/functional/xpack_firefox.sh deleted file mode 100755 index a9a9704ea78de..0000000000000 --- a/.buildkite/scripts/steps/functional/xpack_firefox.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh - -cd "$XPACK_DIR" - -echo --- Default Firefox Smoke Tests - -checks-reporter-with-killswitch "X-Pack firefox smoke test" \ - node scripts/functional_tests \ - --debug --bail \ - --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ - --include-tag "includeFirefox" \ - --config test/functional/config.firefox.js \ - --config test/functional_embedded/config.firefox.ts diff --git a/.buildkite/scripts/steps/functional/xpack_saved_object_field_metrics.sh b/.buildkite/scripts/steps/functional/xpack_saved_object_field_metrics.sh deleted file mode 100755 index 5e00d2446bf7a..0000000000000 --- a/.buildkite/scripts/steps/functional/xpack_saved_object_field_metrics.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/common/util.sh - -.buildkite/scripts/bootstrap.sh -.buildkite/scripts/download_build_artifacts.sh - -cd "$XPACK_DIR" - -echo --- Capture Kibana Saved Objects field count metrics -checks-reporter-with-killswitch "Capture Kibana Saved Objects field count metrics" \ - node scripts/functional_tests \ - --debug --bail \ - --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ - --config test/saved_objects_field_count/config.ts; diff --git a/.buildkite/scripts/steps/on_merge_ts_refs_api_docs.sh b/.buildkite/scripts/steps/on_merge_ts_refs_api_docs.sh index 6d08fbb2c6835..5659db50e1404 100755 --- a/.buildkite/scripts/steps/on_merge_ts_refs_api_docs.sh +++ b/.buildkite/scripts/steps/on_merge_ts_refs_api_docs.sh @@ -2,7 +2,7 @@ set -euo pipefail -export BAZEL_CACHE_MODE=read-write # Populate bazel remote cache for linux +export BAZEL_CACHE_MODE=buildbuddy # Populate Buildbuddy bazel remote cache for linux export BUILD_TS_REFS_CACHE_ENABLE=true export BUILD_TS_REFS_CACHE_CAPTURE=true export DISABLE_BOOTSTRAP_VALIDATION=true diff --git a/.buildkite/scripts/steps/package_testing/test.sh b/.buildkite/scripts/steps/package_testing/test.sh index e5ed00f760864..390adc2dbacee 100755 --- a/.buildkite/scripts/steps/package_testing/test.sh +++ b/.buildkite/scripts/steps/package_testing/test.sh @@ -41,9 +41,9 @@ trap "echoKibanaLogs" EXIT vagrant provision "$TEST_PACKAGE" -export TEST_BROWSER_HEADLESS=1 -export TEST_KIBANA_URL="http://elastic:changeme@$KIBANA_IP_ADDRESS:5601" -export TEST_ES_URL=http://elastic:changeme@192.168.56.1:9200 +# export TEST_BROWSER_HEADLESS=1 +# export TEST_KIBANA_URL="http://elastic:changeme@$KIBANA_IP_ADDRESS:5601" +# export TEST_ES_URL=http://elastic:changeme@192.168.56.1:9200 -cd x-pack -node scripts/functional_test_runner.js --include-tag=smoke +# cd x-pack +# node scripts/functional_test_runner.js --include-tag=smoke diff --git a/.buildkite/scripts/steps/storybooks/build_and_upload.js b/.buildkite/scripts/steps/storybooks/build_and_upload.js index 482640b8d9cc0..c541f59548753 100644 --- a/.buildkite/scripts/steps/storybooks/build_and_upload.js +++ b/.buildkite/scripts/steps/storybooks/build_and_upload.js @@ -20,7 +20,7 @@ const STORYBOOKS = [ 'custom_integrations', 'dashboard_enhanced', 'dashboard', - 'data_enhanced', + 'data', 'embeddable', 'expression_error', 'expression_image', @@ -38,6 +38,7 @@ const STORYBOOKS = [ 'security_solution', 'shared_ux', 'ui_actions_enhanced', + 'unified_search', ]; const GITHUB_CONTEXT = 'Build and Publish Storybooks'; diff --git a/.buildkite/scripts/steps/test/api_integration.sh b/.buildkite/scripts/steps/test/api_integration.sh deleted file mode 100755 index f56e98903d226..0000000000000 --- a/.buildkite/scripts/steps/test/api_integration.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/common/util.sh - -is_test_execution_step - -.buildkite/scripts/bootstrap.sh - -echo --- API Integration Tests -checks-reporter-with-killswitch "API Integration Tests" \ - node scripts/functional_tests \ - --config test/api_integration/config.js \ - --bail \ - --debug diff --git a/.buildkite/scripts/steps/test/ftr_configs.sh b/.buildkite/scripts/steps/test/ftr_configs.sh new file mode 100755 index 0000000000000..244b108a269f8 --- /dev/null +++ b/.buildkite/scripts/steps/test/ftr_configs.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/steps/functional/common.sh + +export JOB_NUM=$BUILDKITE_PARALLEL_JOB +export JOB=ftr-configs-${JOB_NUM} + +FAILED_CONFIGS_KEY="${BUILDKITE_STEP_ID}${BUILDKITE_PARALLEL_JOB:-0}" + +# a FTR failure will result in the script returning an exit code of 10 +exitCode=0 + +configs="${FTR_CONFIG:-}" + +# The first retry should only run the configs that failed in the previous attempt +# Any subsequent retries, which would generally only happen by someone clicking the button in the UI, will run everything +if [[ ! "$configs" && "${BUILDKITE_RETRY_COUNT:-0}" == "1" ]]; then + configs=$(buildkite-agent meta-data get "$FAILED_CONFIGS_KEY" --default '') + if [[ "$configs" ]]; then + echo "--- Retrying only failed configs" + echo "$configs" + fi +fi + +if [[ "$configs" == "" ]]; then + echo "--- downloading ftr test run order" + buildkite-agent artifact download ftr_run_order.json . + configs=$(jq -r '.groups[env.JOB_NUM | tonumber].names | .[]' ftr_run_order.json) +fi + +failedConfigs="" +results=() + +while read -r config; do + if [[ ! "$config" ]]; then + continue; + fi + + echo "--- $ node scripts/functional_tests --bail --config $config" + start=$(date +%s) + + # prevent non-zero exit code from breaking the loop + set +e; + node ./scripts/functional_tests \ + --bail \ + --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ + --config="$config" + lastCode=$? + set -e; + + timeSec=$(($(date +%s)-start)) + if [[ $timeSec -gt 60 ]]; then + min=$((timeSec/60)) + sec=$((timeSec-(min*60))) + duration="${min}m ${sec}s" + else + duration="${timeSec}s" + fi + + results+=("- $config + duration: ${duration} + result: ${lastCode}") + + if [ $lastCode -ne 0 ]; then + exitCode=10 + echo "FTR exited with code $lastCode" + echo "^^^ +++" + + if [[ "$failedConfigs" ]]; then + failedConfigs="${failedConfigs}"$'\n'"$config" + else + failedConfigs="$config" + fi + fi +done <<< "$configs" + +if [[ "$failedConfigs" ]]; then + buildkite-agent meta-data set "$FAILED_CONFIGS_KEY" "$failedConfigs" +fi + +echo "--- FTR configs complete" +printf "%s\n" "${results[@]}" +echo "" + +exit $exitCode diff --git a/.buildkite/scripts/steps/test/jest_parallel.sh b/.buildkite/scripts/steps/test/jest_parallel.sh index 27eb9e9216365..71ecf7a853d4a 100755 --- a/.buildkite/scripts/steps/test/jest_parallel.sh +++ b/.buildkite/scripts/steps/test/jest_parallel.sh @@ -1,37 +1,60 @@ #!/bin/bash -set -uo pipefail +set -euo pipefail -JOB=$BUILDKITE_PARALLEL_JOB -JOB_COUNT=$BUILDKITE_PARALLEL_JOB_COUNT +export JOB=$BUILDKITE_PARALLEL_JOB # a jest failure will result in the script returning an exit code of 10 - -i=0 exitCode=0 +results=() -# run unit tests in parallel if [[ "$1" == 'jest.config.js' ]]; then + # run unit tests in parallel parallelism="-w2" + TEST_TYPE="unit" else + # run integration tests in-band parallelism="--runInBand" + TEST_TYPE="integration" fi +export TEST_TYPE +echo "--- downloading jest test run order" +buildkite-agent artifact download jest_run_order.json . +configs=$(jq -r 'getpath([env.TEST_TYPE]) | .groups[env.JOB | tonumber].names | .[]' jest_run_order.json) + while read -r config; do - if [ "$((i % JOB_COUNT))" -eq "$JOB" ]; then - echo "--- $ node scripts/jest --config $config" - node --max-old-space-size=14336 ./scripts/jest --config="$config" "$parallelism" --coverage=false --passWithNoTests - lastCode=$? - - if [ $lastCode -ne 0 ]; then - exitCode=10 - echo "Jest exited with code $lastCode" - echo "^^^ +++" - fi + echo "--- $ node scripts/jest --config $config" + start=$(date +%s) + + # prevent non-zero exit code from breaking the loop + set +e; + NODE_OPTIONS="--max-old-space-size=14336" node ./scripts/jest --config="$config" "$parallelism" --coverage=false --passWithNoTests + lastCode=$? + set -e; + + timeSec=$(($(date +%s)-start)) + if [[ $timeSec -gt 60 ]]; then + min=$((timeSec/60)) + sec=$((timeSec-(min*60))) + duration="${min}m ${sec}s" + else + duration="${timeSec}s" + fi + + results+=("- $config + duration: ${duration} + result: ${lastCode}") + + if [ $lastCode -ne 0 ]; then + exitCode=10 + echo "Jest exited with code $lastCode" + echo "^^^ +++" fi +done <<< "$configs" - ((i=i+1)) -# uses heredoc to avoid the while loop being in a sub-shell thus unable to overwrite exitCode -done <<< "$(find src x-pack packages -name "$1" -not -path "*/__fixtures__/*" | sort)" +echo "--- Jest configs complete" +printf "%s\n" "${results[@]}" +echo "" exit $exitCode diff --git a/.buildkite/scripts/steps/test/pick_test_group_run_order.js b/.buildkite/scripts/steps/test/pick_test_group_run_order.js new file mode 100644 index 0000000000000..a5fa499419ab5 --- /dev/null +++ b/.buildkite/scripts/steps/test/pick_test_group_run_order.js @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +const { CiStats } = require('kibana-buildkite-library'); + +(async () => { + try { + await CiStats.pickTestGroupRunOrder(); + } catch (ex) { + console.error('CI Stats Error', ex.message); + if (ex.response) { + console.error('HTTP Error Response Status', ex.response.status); + console.error('HTTP Error Response Body', ex.response.data); + } + process.exit(1); + } +})(); diff --git a/.buildkite/scripts/steps/test/pick_test_group_run_order.sh b/.buildkite/scripts/steps/test/pick_test_group_run_order.sh new file mode 100644 index 0000000000000..56308b73c6fd7 --- /dev/null +++ b/.buildkite/scripts/steps/test/pick_test_group_run_order.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/common/util.sh + +echo '--- Pick Test Group Run Order' +node "$(dirname "${0}")/pick_test_group_run_order.js" diff --git a/.buildkite/yarn.lock b/.buildkite/yarn.lock deleted file mode 100644 index c5a4e404ba970..0000000000000 --- a/.buildkite/yarn.lock +++ /dev/null @@ -1,180 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@octokit/auth-token@^2.4.4": - version "2.5.0" - resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz" - integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== - dependencies: - "@octokit/types" "^6.0.3" - -"@octokit/core@^3.5.1": - version "3.5.1" - resolved "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz" - integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw== - dependencies: - "@octokit/auth-token" "^2.4.4" - "@octokit/graphql" "^4.5.8" - "@octokit/request" "^5.6.0" - "@octokit/request-error" "^2.0.5" - "@octokit/types" "^6.0.3" - before-after-hook "^2.2.0" - universal-user-agent "^6.0.0" - -"@octokit/endpoint@^6.0.1": - version "6.0.12" - resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz" - integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== - dependencies: - "@octokit/types" "^6.0.3" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - -"@octokit/graphql@^4.5.8": - version "4.8.0" - resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz" - integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== - dependencies: - "@octokit/request" "^5.6.0" - "@octokit/types" "^6.0.3" - universal-user-agent "^6.0.0" - -"@octokit/openapi-types@^11.2.0": - version "11.2.0" - resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz" - integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== - -"@octokit/plugin-paginate-rest@^2.16.8": - version "2.17.0" - resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz" - integrity sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw== - dependencies: - "@octokit/types" "^6.34.0" - -"@octokit/plugin-request-log@^1.0.4": - version "1.0.4" - resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz" - integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== - -"@octokit/plugin-rest-endpoint-methods@^5.12.0": - version "5.13.0" - resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz" - integrity sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA== - dependencies: - "@octokit/types" "^6.34.0" - deprecation "^2.3.1" - -"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": - version "2.1.0" - resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz" - integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== - dependencies: - "@octokit/types" "^6.0.3" - deprecation "^2.0.0" - once "^1.4.0" - -"@octokit/request@^5.6.0": - version "5.6.2" - resolved "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz" - integrity sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA== - dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.1.0" - "@octokit/types" "^6.16.1" - is-plain-object "^5.0.0" - node-fetch "^2.6.1" - universal-user-agent "^6.0.0" - -"@octokit/rest@^18.10.0": - version "18.12.0" - resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz" - integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== - dependencies: - "@octokit/core" "^3.5.1" - "@octokit/plugin-paginate-rest" "^2.16.8" - "@octokit/plugin-request-log" "^1.0.4" - "@octokit/plugin-rest-endpoint-methods" "^5.12.0" - -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.34.0": - version "6.34.0" - resolved "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz" - integrity sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw== - dependencies: - "@octokit/openapi-types" "^11.2.0" - -axios@^0.21.4: - version "0.21.4" - resolved "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== - dependencies: - follow-redirects "^1.14.0" - -before-after-hook@^2.2.0: - version "2.2.2" - resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz" - integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== - -deprecation@^2.0.0, deprecation@^2.3.1: - version "2.3.1" - resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" - integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== - -follow-redirects@^1.14.0: - version "1.14.5" - resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz" - integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA== - -is-plain-object@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - -kibana-buildkite-library@elastic/kibana-buildkite-library: - version "1.0.0" - resolved "https://codeload.github.com/elastic/kibana-buildkite-library/tar.gz/bd0bec4c7af5f64a12c781d03cedb9fb2386bfbd" - dependencies: - "@octokit/rest" "^18.10.0" - axios "^0.21.4" - -node-fetch@^2.6.1: - version "2.6.6" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz" - integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA== - dependencies: - whatwg-url "^5.0.0" - -once@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - -universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 215514d58f601..e98ba1d451ff3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -79,11 +79,11 @@ /src/plugins/inspector/ @elastic/kibana-app-services /src/plugins/unified_search/ @elastic/kibana-app-services /x-pack/examples/ui_actions_enhanced_examples/ @elastic/kibana-app-services -/x-pack/plugins/data_enhanced/ @elastic/kibana-app-services /x-pack/plugins/embeddable_enhanced/ @elastic/kibana-app-services /x-pack/plugins/ui_actions_enhanced/ @elastic/kibana-app-services /x-pack/plugins/runtime_fields @elastic/kibana-app-services /x-pack/test/search_sessions_integration/ @elastic/kibana-app-services +/src/plugins/dashboard/public/application/embeddable/viewport/print_media @elastic/kibana-app-services ### Observability Plugins @@ -129,6 +129,7 @@ /src/apm.js @elastic/kibana-core @vigneshshanmugam /packages/kbn-apm-config-loader/ @elastic/kibana-core @vigneshshanmugam /src/core/types/elasticsearch @elastic/apm-ui +/packages/elastic-apm-synthtrace/ @elastic/apm-ui #CC# /src/plugins/apm_oss/ @elastic/apm-ui #CC# /x-pack/plugins/observability/ @elastic/apm-ui diff --git a/.gitignore b/.gitignore index 7c20367dfe6de..fac261b2a97ea 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ selenium *.swo *.out package-lock.json +!/.buildkite/package-lock.json .yo-rc.json .vscode *.sublime-* diff --git a/api_docs/actions.devdocs.json b/api_docs/actions.devdocs.json index 227bba7d93599..b5fd8147b8a2e 100644 --- a/api_docs/actions.devdocs.json +++ b/api_docs/actions.devdocs.json @@ -1,12 +1,209 @@ { "id": "actions", "client": { - "classes": [], + "classes": [ + { + "parentPluginId": "actions", + "id": "def-public.Plugin", + "type": "Class", + "tags": [], + "label": "Plugin", + "description": [], + "signature": [ + { + "pluginId": "actions", + "scope": "public", + "docId": "kibActionsPluginApi", + "section": "def-public.Plugin", + "text": "Plugin" + }, + " implements ", + { + "pluginId": "core", + "scope": "public", + "docId": "kibCorePluginApi", + "section": "def-public.Plugin", + "text": "Plugin" + }, + "<", + { + "pluginId": "actions", + "scope": "public", + "docId": "kibActionsPluginApi", + "section": "def-public.ActionsPublicPluginSetup", + "text": "ActionsPublicPluginSetup" + }, + ", void, object, object>" + ], + "path": "x-pack/plugins/actions/public/plugin.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-public.Plugin.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "x-pack/plugins/actions/public/plugin.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-public.Plugin.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "ctx", + "description": [], + "signature": [ + { + "pluginId": "core", + "scope": "public", + "docId": "kibCorePluginApi", + "section": "def-public.PluginInitializerContext", + "text": "PluginInitializerContext" + }, + "<", + "Config", + ">" + ], + "path": "x-pack/plugins/actions/public/plugin.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "actions", + "id": "def-public.Plugin.setup", + "type": "Function", + "tags": [], + "label": "setup", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "actions", + "scope": "public", + "docId": "kibActionsPluginApi", + "section": "def-public.ActionsPublicPluginSetup", + "text": "ActionsPublicPluginSetup" + } + ], + "path": "x-pack/plugins/actions/public/plugin.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "actions", + "id": "def-public.Plugin.start", + "type": "Function", + "tags": [], + "label": "start", + "description": [], + "signature": [ + "() => void" + ], + "path": "x-pack/plugins/actions/public/plugin.ts", + "deprecated": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], "functions": [], "interfaces": [], "enums": [], "misc": [], - "objects": [] + "objects": [], + "setup": { + "parentPluginId": "actions", + "id": "def-public.ActionsPublicPluginSetup", + "type": "Interface", + "tags": [], + "label": "ActionsPublicPluginSetup", + "description": [], + "path": "x-pack/plugins/actions/public/plugin.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-public.ActionsPublicPluginSetup.validateEmailAddresses", + "type": "Function", + "tags": [], + "label": "validateEmailAddresses", + "description": [], + "signature": [ + "(emails: string[], options?: ", + { + "pluginId": "actions", + "scope": "common", + "docId": "kibActionsPluginApi", + "section": "def-common.ValidateEmailAddressesOptions", + "text": "ValidateEmailAddressesOptions" + }, + " | undefined) => ", + { + "pluginId": "actions", + "scope": "common", + "docId": "kibActionsPluginApi", + "section": "def-common.ValidatedEmail", + "text": "ValidatedEmail" + }, + "[]" + ], + "path": "x-pack/plugins/actions/public/plugin.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-public.ActionsPublicPluginSetup.validateEmailAddresses.$1", + "type": "Array", + "tags": [], + "label": "emails", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/plugins/actions/public/plugin.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "actions", + "id": "def-public.ActionsPublicPluginSetup.validateEmailAddresses.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "actions", + "scope": "common", + "docId": "kibActionsPluginApi", + "section": "def-common.ValidateEmailAddressesOptions", + "text": "ValidateEmailAddressesOptions" + }, + " | undefined" + ], + "path": "x-pack/plugins/actions/public/plugin.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + } + ], + "lifecycle": "setup", + "initialIsOpen": true + } }, "server": { "classes": [], @@ -181,6 +378,16 @@ "description": [], "path": "x-pack/plugins/actions/server/types.ts", "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-server.ActionResult.isDeprecated", + "type": "boolean", + "tags": [], + "label": "isDeprecated", + "description": [], + "path": "x-pack/plugins/actions/server/types.ts", + "deprecated": false } ], "initialIsOpen": false @@ -738,7 +945,7 @@ "label": "ActionParamsType", "description": [], "signature": [ - "{ readonly source?: string | undefined; readonly summary?: string | undefined; readonly group?: string | undefined; readonly timestamp?: string | undefined; readonly eventAction?: \"resolve\" | \"trigger\" | \"acknowledge\" | undefined; readonly dedupKey?: string | undefined; readonly severity?: \"error\" | \"info\" | \"warning\" | \"critical\" | undefined; readonly component?: string | undefined; readonly class?: string | undefined; }" + "{ readonly source?: string | undefined; readonly summary?: string | undefined; readonly group?: string | undefined; readonly timestamp?: string | undefined; readonly eventAction?: \"resolve\" | \"trigger\" | \"acknowledge\" | undefined; readonly dedupKey?: string | undefined; readonly severity?: \"error\" | \"warning\" | \"info\" | \"critical\" | undefined; readonly component?: string | undefined; readonly class?: string | undefined; }" ], "path": "x-pack/plugins/actions/server/builtin_action_types/pagerduty.ts", "deprecated": false, @@ -1586,6 +1793,85 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "actions", + "id": "def-common.hasMustacheTemplate", + "type": "Function", + "tags": [], + "label": "hasMustacheTemplate", + "description": [ + "does the string contain `{{.*}}`?" + ], + "signature": [ + "(string: string) => boolean" + ], + "path": "x-pack/plugins/actions/common/mustache_template.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-common.hasMustacheTemplate.$1", + "type": "string", + "tags": [], + "label": "string", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/plugins/actions/common/mustache_template.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-common.invalidEmailsAsMessage", + "type": "Function", + "tags": [], + "label": "invalidEmailsAsMessage", + "description": [], + "signature": [ + "(validatedEmails: ", + { + "pluginId": "actions", + "scope": "common", + "docId": "kibActionsPluginApi", + "section": "def-common.ValidatedEmail", + "text": "ValidatedEmail" + }, + "[]) => string | undefined" + ], + "path": "x-pack/plugins/actions/common/validate_email_addresses.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-common.invalidEmailsAsMessage.$1", + "type": "Array", + "tags": [], + "label": "validatedEmails", + "description": [], + "signature": [ + { + "pluginId": "actions", + "scope": "common", + "docId": "kibActionsPluginApi", + "section": "def-common.ValidatedEmail", + "text": "ValidatedEmail" + }, + "[]" + ], + "path": "x-pack/plugins/actions/common/validate_email_addresses.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "actions", "id": "def-common.isActionTypeExecutorResult", @@ -1616,70 +1902,249 @@ ], "returnComment": [], "initialIsOpen": false - } - ], - "interfaces": [ + }, { "parentPluginId": "actions", - "id": "def-common.ActionResult", - "type": "Interface", + "id": "def-common.validateEmailAddresses", + "type": "Function", "tags": [], - "label": "ActionResult", + "label": "validateEmailAddresses", "description": [], - "path": "x-pack/plugins/actions/common/types.ts", - "deprecated": false, - "children": [ + "signature": [ + "(allowedDomains: string[] | null, addresses: string[], options: ", { - "parentPluginId": "actions", - "id": "def-common.ActionResult.id", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "path": "x-pack/plugins/actions/common/types.ts", - "deprecated": false + "pluginId": "actions", + "scope": "common", + "docId": "kibActionsPluginApi", + "section": "def-common.ValidateEmailAddressesOptions", + "text": "ValidateEmailAddressesOptions" }, + ") => ", { - "parentPluginId": "actions", - "id": "def-common.ActionResult.actionTypeId", - "type": "string", - "tags": [], - "label": "actionTypeId", - "description": [], - "path": "x-pack/plugins/actions/common/types.ts", - "deprecated": false + "pluginId": "actions", + "scope": "common", + "docId": "kibActionsPluginApi", + "section": "def-common.ValidatedEmail", + "text": "ValidatedEmail" }, + "[]" + ], + "path": "x-pack/plugins/actions/common/validate_email_addresses.ts", + "deprecated": false, + "children": [ { "parentPluginId": "actions", - "id": "def-common.ActionResult.name", - "type": "string", + "id": "def-common.validateEmailAddresses.$1", + "type": "CompoundType", "tags": [], - "label": "name", + "label": "allowedDomains", "description": [], - "path": "x-pack/plugins/actions/common/types.ts", - "deprecated": false + "signature": [ + "string[] | null" + ], + "path": "x-pack/plugins/actions/common/validate_email_addresses.ts", + "deprecated": false, + "isRequired": false }, { "parentPluginId": "actions", - "id": "def-common.ActionResult.config", - "type": "Object", + "id": "def-common.validateEmailAddresses.$2", + "type": "Array", "tags": [], - "label": "config", + "label": "addresses", "description": [], "signature": [ - "{ [x: string]: any; }" + "string[]" ], - "path": "x-pack/plugins/actions/common/types.ts", - "deprecated": false + "path": "x-pack/plugins/actions/common/validate_email_addresses.ts", + "deprecated": false, + "isRequired": true }, { "parentPluginId": "actions", - "id": "def-common.ActionResult.isPreconfigured", - "type": "boolean", + "id": "def-common.validateEmailAddresses.$3", + "type": "Object", "tags": [], - "label": "isPreconfigured", + "label": "options", "description": [], - "path": "x-pack/plugins/actions/common/types.ts", + "signature": [ + { + "pluginId": "actions", + "scope": "common", + "docId": "kibActionsPluginApi", + "section": "def-common.ValidateEmailAddressesOptions", + "text": "ValidateEmailAddressesOptions" + } + ], + "path": "x-pack/plugins/actions/common/validate_email_addresses.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-common.validateEmailAddressesAsAlwaysValid", + "type": "Function", + "tags": [], + "label": "validateEmailAddressesAsAlwaysValid", + "description": [], + "signature": [ + "(addresses: string[]) => ", + { + "pluginId": "actions", + "scope": "common", + "docId": "kibActionsPluginApi", + "section": "def-common.ValidatedEmail", + "text": "ValidatedEmail" + }, + "[]" + ], + "path": "x-pack/plugins/actions/common/validate_email_addresses.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-common.validateEmailAddressesAsAlwaysValid.$1", + "type": "Array", + "tags": [], + "label": "addresses", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/plugins/actions/common/validate_email_addresses.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-common.withoutMustacheTemplate", + "type": "Function", + "tags": [], + "label": "withoutMustacheTemplate", + "description": [ + "filter strings that do not contain `{{.*}}`" + ], + "signature": [ + "(strings: string[]) => string[]" + ], + "path": "x-pack/plugins/actions/common/mustache_template.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-common.withoutMustacheTemplate.$1", + "type": "Array", + "tags": [], + "label": "strings", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/plugins/actions/common/mustache_template.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "actions", + "id": "def-common.ActionResult", + "type": "Interface", + "tags": [], + "label": "ActionResult", + "description": [], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-common.ActionResult.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.ActionResult.actionTypeId", + "type": "string", + "tags": [], + "label": "actionTypeId", + "description": [], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.ActionResult.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.ActionResult.config", + "type": "Object", + "tags": [], + "label": "config", + "description": [], + "signature": [ + "{ [x: string]: any; }" + ], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.ActionResult.isPreconfigured", + "type": "boolean", + "tags": [], + "label": "isPreconfigured", + "description": [], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-common.ActionsPublicConfigType", + "type": "Interface", + "tags": [], + "label": "ActionsPublicConfigType", + "description": [], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-common.ActionsPublicConfigType.allowedEmailDomains", + "type": "Array", + "tags": [], + "label": "allowedEmailDomains", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/plugins/actions/common/types.ts", "deprecated": false } ], @@ -1858,6 +2323,165 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-common.SNProductsConfigValue", + "type": "Interface", + "tags": [], + "label": "SNProductsConfigValue", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-common.SNProductsConfigValue.table", + "type": "string", + "tags": [], + "label": "table", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.SNProductsConfigValue.appScope", + "type": "string", + "tags": [], + "label": "appScope", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.SNProductsConfigValue.useImportAPI", + "type": "boolean", + "tags": [], + "label": "useImportAPI", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.SNProductsConfigValue.importSetTable", + "type": "string", + "tags": [], + "label": "importSetTable", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.SNProductsConfigValue.commentFieldKey", + "type": "string", + "tags": [], + "label": "commentFieldKey", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.SNProductsConfigValue.appId", + "type": "string", + "tags": [], + "label": "appId", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-common.ValidatedEmail", + "type": "Interface", + "tags": [], + "label": "ValidatedEmail", + "description": [], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-common.ValidatedEmail.address", + "type": "string", + "tags": [], + "label": "address", + "description": [], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.ValidatedEmail.valid", + "type": "boolean", + "tags": [], + "label": "valid", + "description": [], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.ValidatedEmail.reason", + "type": "CompoundType", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + { + "pluginId": "actions", + "scope": "common", + "docId": "kibActionsPluginApi", + "section": "def-common.InvalidEmailReason", + "text": "InvalidEmailReason" + }, + " | undefined" + ], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-common.ValidateEmailAddressesOptions", + "type": "Interface", + "tags": [], + "label": "ValidateEmailAddressesOptions", + "description": [ + "Options that can be used when validating email addresses" + ], + "path": "x-pack/plugins/actions/common/validate_email_addresses.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-common.ValidateEmailAddressesOptions.treatMustacheTemplatesAsValid", + "type": "CompoundType", + "tags": [], + "label": "treatMustacheTemplatesAsValid", + "description": [ + "treat any address which contains a mustache template as valid" + ], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/actions/common/validate_email_addresses.ts", + "deprecated": false + } + ], + "initialIsOpen": false } ], "enums": [ @@ -1871,6 +2495,17 @@ "path": "x-pack/plugins/actions/common/index.ts", "deprecated": false, "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-common.InvalidEmailReason", + "type": "Enum", + "tags": [], + "label": "InvalidEmailReason", + "description": [], + "path": "x-pack/plugins/actions/common/types.ts", + "deprecated": false, + "initialIsOpen": false } ], "misc": [ @@ -1969,6 +2604,34 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "actions", + "id": "def-common.DEFAULT_ALERTS_GROUPING_KEY", + "type": "string", + "tags": [], + "label": "DEFAULT_ALERTS_GROUPING_KEY", + "description": [], + "signature": [ + "\"{{rule.id}}:{{alert.id}}\"" + ], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-common.FIELD_PREFIX", + "type": "string", + "tags": [], + "label": "FIELD_PREFIX", + "description": [], + "signature": [ + "\"u_\"" + ], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "actions", "id": "def-common.INTERNAL_BASE_ACTION_API_PATH", @@ -2066,8 +2729,343 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-common.ServiceNowITOMActionTypeId", + "type": "string", + "tags": [], + "label": "ServiceNowITOMActionTypeId", + "description": [], + "signature": [ + "\".servicenow-itom\"" + ], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-common.ServiceNowITSMActionTypeId", + "type": "string", + "tags": [], + "label": "ServiceNowITSMActionTypeId", + "description": [], + "signature": [ + "\".servicenow\"" + ], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-common.serviceNowITSMTable", + "type": "string", + "tags": [], + "label": "serviceNowITSMTable", + "description": [], + "signature": [ + "\"incident\"" + ], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-common.ServiceNowSIRActionTypeId", + "type": "string", + "tags": [], + "label": "ServiceNowSIRActionTypeId", + "description": [], + "signature": [ + "\".servicenow-sir\"" + ], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-common.serviceNowSIRTable", + "type": "string", + "tags": [], + "label": "serviceNowSIRTable", + "description": [], + "signature": [ + "\"sn_si_incident\"" + ], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-common.SNProductsConfig", + "type": "Type", + "tags": [], + "label": "SNProductsConfig", + "description": [], + "signature": [ + "{ [x: string]: ", + { + "pluginId": "actions", + "scope": "common", + "docId": "kibActionsPluginApi", + "section": "def-common.SNProductsConfigValue", + "text": "SNProductsConfigValue" + }, + "; }" + ], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false, + "initialIsOpen": false } ], - "objects": [] + "objects": [ + { + "parentPluginId": "actions", + "id": "def-common.MustacheInEmailRegExp", + "type": "Object", + "tags": [], + "label": "MustacheInEmailRegExp", + "description": [], + "signature": [ + "RegExp" + ], + "path": "x-pack/plugins/actions/common/mustache_template.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig", + "type": "Object", + "tags": [], + "label": "snExternalServiceConfig", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenow", + "type": "Object", + "tags": [], + "label": "'.servicenow'", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenow.importSetTable", + "type": "string", + "tags": [], + "label": "importSetTable", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenow.appScope", + "type": "string", + "tags": [], + "label": "appScope", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenow.table", + "type": "string", + "tags": [], + "label": "table", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenow.useImportAPI", + "type": "boolean", + "tags": [], + "label": "useImportAPI", + "description": [], + "signature": [ + "true" + ], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenow.commentFieldKey", + "type": "string", + "tags": [], + "label": "commentFieldKey", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenow.appId", + "type": "string", + "tags": [], + "label": "appId", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenowsir", + "type": "Object", + "tags": [], + "label": "'.servicenow-sir'", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenowsir.importSetTable", + "type": "string", + "tags": [], + "label": "importSetTable", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenowsir.appScope", + "type": "string", + "tags": [], + "label": "appScope", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenowsir.table", + "type": "string", + "tags": [], + "label": "table", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenowsir.useImportAPI", + "type": "boolean", + "tags": [], + "label": "useImportAPI", + "description": [], + "signature": [ + "true" + ], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenowsir.commentFieldKey", + "type": "string", + "tags": [], + "label": "commentFieldKey", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenowsir.appId", + "type": "string", + "tags": [], + "label": "appId", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenowitom", + "type": "Object", + "tags": [], + "label": "'.servicenow-itom'", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenowitom.importSetTable", + "type": "string", + "tags": [], + "label": "importSetTable", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenowitom.appScope", + "type": "string", + "tags": [], + "label": "appScope", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenowitom.table", + "type": "string", + "tags": [], + "label": "table", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenowitom.useImportAPI", + "type": "boolean", + "tags": [], + "label": "useImportAPI", + "description": [], + "signature": [ + "false" + ], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + }, + { + "parentPluginId": "actions", + "id": "def-common.snExternalServiceConfig..servicenowitom.commentFieldKey", + "type": "string", + "tags": [], + "label": "commentFieldKey", + "description": [], + "path": "x-pack/plugins/actions/common/servicenow_config.ts", + "deprecated": false + } + ] + } + ], + "initialIsOpen": false + } + ] } } \ No newline at end of file diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 72bb9168e149d..267f24e44374c 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github summary: API docs for the actions plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] warning: 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. --- @@ -18,7 +18,15 @@ Contact [Response Ops](https://github.com/orgs/elastic/teams/response-ops) for q | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 127 | 0 | 127 | 10 | +| 195 | 0 | 191 | 11 | + +## Client + +### Setup + + +### Classes + ## Server @@ -39,6 +47,9 @@ Contact [Response Ops](https://github.com/orgs/elastic/teams/response-ops) for q ## Common +### Objects + + ### Functions diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index ae965957eacb7..4403e9604c11a 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github summary: API docs for the advancedSettings plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] warning: 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. --- diff --git a/api_docs/alerting.devdocs.json b/api_docs/alerting.devdocs.json index dcaed350c4882..a7c437fda9dbf 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -2456,20 +2456,6 @@ ], "path": "x-pack/plugins/alerting/server/types.ts", "deprecated": false - }, - { - "parentPluginId": "alerting", - "id": "def-server.RuleType.config", - "type": "Object", - "tags": [], - "label": "config", - "description": [], - "signature": [ - "RuleTypeConfig", - " | undefined" - ], - "path": "x-pack/plugins/alerting/server/types.ts", - "deprecated": false } ], "initialIsOpen": false @@ -3889,10 +3875,10 @@ }, { "parentPluginId": "alerting", - "id": "def-common.IExecutionLog.num_scheduled_actions", + "id": "def-common.IExecutionLog.num_generated_actions", "type": "number", "tags": [], - "label": "num_scheduled_actions", + "label": "num_generated_actions", "description": [], "path": "x-pack/plugins/alerting/common/execution_log_types.ts", "deprecated": false @@ -4547,72 +4533,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "alerting", - "id": "def-common.RuleExecutionRunResult", - "type": "Interface", - "tags": [], - "label": "RuleExecutionRunResult", - "description": [], - "path": "x-pack/plugins/alerting/common/rule_task_instance.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "alerting", - "id": "def-common.RuleExecutionRunResult.state", - "type": "CompoundType", - "tags": [], - "label": "state", - "description": [], - "signature": [ - "{ alertTypeState?: { [x: string]: unknown; } | undefined; alertInstances?: { [x: string]: { state?: { [x: string]: unknown; } | undefined; meta?: { lastScheduledActions?: ({ subgroup?: string | undefined; } & { group: string; date: Date; }) | undefined; } | undefined; }; } | undefined; previousStartedAt?: Date | null | undefined; } & { metrics: { numSearches?: number | undefined; totalSearchDurationMs?: number | undefined; esSearchDurationMs?: number | undefined; }; alertExecutionStore: { numberOfTriggeredActions?: number | undefined; numberOfScheduledActions?: number | undefined; triggeredActionsStatus?: string | undefined; }; }" - ], - "path": "x-pack/plugins/alerting/common/rule_task_instance.ts", - "deprecated": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RuleExecutionRunResult.monitoring", - "type": "Object", - "tags": [], - "label": "monitoring", - "description": [], - "signature": [ - { - "pluginId": "alerting", - "scope": "common", - "docId": "kibAlertingPluginApi", - "section": "def-common.RuleMonitoring", - "text": "RuleMonitoring" - }, - " | undefined" - ], - "path": "x-pack/plugins/alerting/common/rule_task_instance.ts", - "deprecated": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RuleExecutionRunResult.schedule", - "type": "Object", - "tags": [], - "label": "schedule", - "description": [], - "signature": [ - { - "pluginId": "alerting", - "scope": "common", - "docId": "kibAlertingPluginApi", - "section": "def-common.IntervalSchedule", - "text": "IntervalSchedule" - }, - " | undefined" - ], - "path": "x-pack/plugins/alerting/common/rule_task_instance.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "alerting", "id": "def-common.RuleExecutionStatus", @@ -4631,46 +4551,7 @@ "label": "status", "description": [], "signature": [ - "\"error\" | \"unknown\" | \"warning\" | \"pending\" | \"ok\" | \"active\"" - ], - "path": "x-pack/plugins/alerting/common/rule.ts", - "deprecated": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RuleExecutionStatus.numberOfTriggeredActions", - "type": "number", - "tags": [], - "label": "numberOfTriggeredActions", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "x-pack/plugins/alerting/common/rule.ts", - "deprecated": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RuleExecutionStatus.numberOfScheduledActions", - "type": "number", - "tags": [], - "label": "numberOfScheduledActions", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "x-pack/plugins/alerting/common/rule.ts", - "deprecated": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RuleExecutionStatus.metrics", - "type": "Object", - "tags": [], - "label": "metrics", - "description": [], - "signature": [ - "{ numSearches?: number | undefined; totalSearchDurationMs?: number | undefined; esSearchDurationMs?: number | undefined; } | undefined" + "\"error\" | \"warning\" | \"unknown\" | \"pending\" | \"ok\" | \"active\"" ], "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false @@ -5103,6 +4984,17 @@ } ], "enums": [ + { + "parentPluginId": "alerting", + "id": "def-common.ActionsCompletion", + "type": "Enum", + "tags": [], + "label": "ActionsCompletion", + "description": [], + "path": "x-pack/plugins/alerting/common/rule_task_instance.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.HealthStatus", @@ -5263,7 +5155,7 @@ "label": "ExecutionLogSortFields", "description": [], "signature": [ - "\"timestamp\" | \"execution_duration\" | \"total_search_duration\" | \"es_search_duration\" | \"schedule_delay\" | \"num_triggered_actions\" | \"num_scheduled_actions\"" + "\"timestamp\" | \"execution_duration\" | \"total_search_duration\" | \"es_search_duration\" | \"schedule_delay\" | \"num_triggered_actions\" | \"num_generated_actions\"" ], "path": "x-pack/plugins/alerting/common/execution_log_types.ts", "deprecated": false, @@ -5463,34 +5355,6 @@ "deprecated": false, "initialIsOpen": false }, - { - "parentPluginId": "alerting", - "id": "def-common.RuleExecutionMetrics", - "type": "Type", - "tags": [], - "label": "RuleExecutionMetrics", - "description": [], - "signature": [ - "{ numSearches?: number | undefined; totalSearchDurationMs?: number | undefined; esSearchDurationMs?: number | undefined; }" - ], - "path": "x-pack/plugins/alerting/common/rule_task_instance.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RuleExecutionState", - "type": "Type", - "tags": [], - "label": "RuleExecutionState", - "description": [], - "signature": [ - "{ alertTypeState?: { [x: string]: unknown; } | undefined; alertInstances?: { [x: string]: { state?: { [x: string]: unknown; } | undefined; meta?: { lastScheduledActions?: ({ subgroup?: string | undefined; } & { group: string; date: Date; }) | undefined; } | undefined; }; } | undefined; previousStartedAt?: Date | null | undefined; } & { metrics: { numSearches?: number | undefined; totalSearchDurationMs?: number | undefined; esSearchDurationMs?: number | undefined; }; alertExecutionStore: { numberOfTriggeredActions?: number | undefined; numberOfScheduledActions?: number | undefined; triggeredActionsStatus?: string | undefined; }; }" - ], - "path": "x-pack/plugins/alerting/common/rule_task_instance.ts", - "deprecated": false, - "initialIsOpen": false - }, { "parentPluginId": "alerting", "id": "def-common.RuleExecutionStatuses", @@ -5499,7 +5363,7 @@ "label": "RuleExecutionStatuses", "description": [], "signature": [ - "\"error\" | \"unknown\" | \"warning\" | \"pending\" | \"ok\" | \"active\"" + "\"error\" | \"warning\" | \"unknown\" | \"pending\" | \"ok\" | \"active\"" ], "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false, @@ -5750,7 +5614,7 @@ "label": "executionLogSortableColumns", "description": [], "signature": [ - "readonly [\"timestamp\", \"execution_duration\", \"total_search_duration\", \"es_search_duration\", \"schedule_delay\", \"num_triggered_actions\", \"num_scheduled_actions\"]" + "readonly [\"timestamp\", \"execution_duration\", \"total_search_duration\", \"es_search_duration\", \"schedule_delay\", \"num_triggered_actions\", \"num_generated_actions\"]" ], "path": "x-pack/plugins/alerting/common/execution_log_types.ts", "deprecated": false, diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index 8ed04bc845f1a..cc9c60a87987e 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github summary: API docs for the alerting plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Response Ops](https://github.com/orgs/elastic/teams/response-ops) for q | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 334 | 0 | 325 | 20 | +| 325 | 0 | 316 | 19 | ## Client diff --git a/api_docs/apm.devdocs.json b/api_docs/apm.devdocs.json index bd88217f34fc3..302f8c93bdea9 100644 --- a/api_docs/apm.devdocs.json +++ b/api_docs/apm.devdocs.json @@ -345,12 +345,43 @@ { "parentPluginId": "apm", "id": "def-server.APMRouteHandlerResources.context", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "context", "description": [], "signature": [ - "ApmPluginRequestHandlerContext" + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " & { licensing: Promise<", + { + "pluginId": "licensing", + "scope": "server", + "docId": "kibLicensingPluginApi", + "section": "def-server.LicensingApiRequestHandlerContext", + "text": "LicensingApiRequestHandlerContext" + }, + ">; alerting: Promise<", + { + "pluginId": "alerting", + "scope": "server", + "docId": "kibAlertingPluginApi", + "section": "def-server.AlertingApiRequestHandlerContext", + "text": "AlertingApiRequestHandlerContext" + }, + ">; rac: Promise<", + { + "pluginId": "ruleRegistry", + "scope": "server", + "docId": "kibRuleRegistryPluginApi", + "section": "def-server.RacApiRequestHandlerContext", + "text": "RacApiRequestHandlerContext" + }, + ">; }" ], "path": "x-pack/plugins/apm/server/routes/typings.ts", "deprecated": false @@ -491,7 +522,7 @@ "section": "def-server.RequestHandlerContext", "text": "RequestHandlerContext" }, - " & { licensing: ", + " & { licensing: Promise<", { "pluginId": "licensing", "scope": "server", @@ -499,7 +530,7 @@ "section": "def-server.LicensingApiRequestHandlerContext", "text": "LicensingApiRequestHandlerContext" }, - "; }, request: ", + ">; }, request: ", { "pluginId": "core", "scope": "server", @@ -2979,7 +3010,11 @@ "Type", "; end: ", "Type", - "; }>]>; }>, ", + "; }>, ", + "TypeC", + "<{ probability: ", + "Type", + "; }>]>; }>, ", { "pluginId": "apm", "scope": "server", @@ -4010,6 +4045,8 @@ "<{ query: ", "IntersectionC", "<[", + "IntersectionC", + "<[", "TypeC", "<{ environment: ", "UnionC", @@ -4039,6 +4076,10 @@ "StringC", "; }>, ", "TypeC", + "<{ probability: ", + "Type", + "; }>]>, ", + "TypeC", "<{ serviceNames: ", "Type", "; }>]>; }>, ", @@ -4085,7 +4126,11 @@ "PartialC", "<{ serviceGroup: ", "StringC", - "; }>]>; }>, ", + "; }>, ", + "TypeC", + "<{ probability: ", + "Type", + "; }>]>; }>, ", { "pluginId": "apm", "scope": "server", @@ -5219,12 +5264,43 @@ { "parentPluginId": "apm", "id": "def-server.APMPluginSetup.createApmEventClient.$1.context", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "context", "description": [], "signature": [ - "ApmPluginRequestHandlerContext" + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " & { licensing: Promise<", + { + "pluginId": "licensing", + "scope": "server", + "docId": "kibLicensingPluginApi", + "section": "def-server.LicensingApiRequestHandlerContext", + "text": "LicensingApiRequestHandlerContext" + }, + ">; alerting: Promise<", + { + "pluginId": "alerting", + "scope": "server", + "docId": "kibAlertingPluginApi", + "section": "def-server.AlertingApiRequestHandlerContext", + "text": "AlertingApiRequestHandlerContext" + }, + ">; rac: Promise<", + { + "pluginId": "ruleRegistry", + "scope": "server", + "docId": "kibRuleRegistryPluginApi", + "section": "def-server.RacApiRequestHandlerContext", + "text": "RacApiRequestHandlerContext" + }, + ">; }" ], "path": "x-pack/plugins/apm/server/types.ts", "deprecated": false diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 2337384644551..9dd3c0c89fdf4 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github summary: API docs for the apm plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] warning: 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. --- diff --git a/api_docs/banners.devdocs.json b/api_docs/banners.devdocs.json index eae41491a43d2..d370967767d93 100644 --- a/api_docs/banners.devdocs.json +++ b/api_docs/banners.devdocs.json @@ -38,7 +38,7 @@ "label": "placement", "description": [], "signature": [ - "\"top\" | \"disabled\"" + "\"disabled\" | \"top\"" ], "path": "x-pack/plugins/banners/common/types.ts", "deprecated": false @@ -129,7 +129,7 @@ "label": "BannerPlacement", "description": [], "signature": [ - "\"top\" | \"disabled\"" + "\"disabled\" | \"top\"" ], "path": "x-pack/plugins/banners/common/types.ts", "deprecated": false, diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 732a3f841f0c3..e22924610e9ae 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github summary: API docs for the banners plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] warning: 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. --- diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index c19e0fecea759..2b2f9e84817e5 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github summary: API docs for the bfetch plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] warning: 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. --- diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 623e6529ac3f4..cc1fdd45c597c 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github summary: API docs for the canvas plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] warning: 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. --- diff --git a/api_docs/cases.devdocs.json b/api_docs/cases.devdocs.json index 76d0d7ae642cc..a78b058751b23 100644 --- a/api_docs/cases.devdocs.json +++ b/api_docs/cases.devdocs.json @@ -1348,7 +1348,9 @@ "type": "string", "tags": [], "label": "SECURITY_SOLUTION_OWNER", - "description": [], + "description": [ + "\nOwner" + ], "signature": [ "\"securitySolution\"" ], diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index 9ec7594d975cd..3b1c2a5e78c50 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github summary: API docs for the cases plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [ResponseOps](https://github.com/orgs/elastic/teams/response-ops) for qu | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 71 | 0 | 58 | 19 | +| 71 | 0 | 57 | 19 | ## Client diff --git a/api_docs/charts.devdocs.json b/api_docs/charts.devdocs.json index d65c1a3de92dc..584443f5b46dc 100644 --- a/api_docs/charts.devdocs.json +++ b/api_docs/charts.devdocs.json @@ -9,29 +9,68 @@ "type": "Function", "tags": [], "label": "ColorPicker", - "description": [], + "description": [ + "\nA `ColorPicker` component that is wrapped by the `withSuspense` HOC. This component can\nbe used directly by consumers and will load the `ColorPickerLazy` component lazily with\na predefined fallback and error boundary." + ], "signature": [ - "({ onChange, color: selectedColor, label, useLegacyColors, colorIsOverwritten, onKeyDown, maxDepth, layerIndex, }: ColorPickerProps) => JSX.Element" + "React.ForwardRefExoticComponent<", + "ColorPickerProps", + " & React.RefAttributes<{}>>" ], - "path": "src/plugins/charts/public/static/components/color_picker.tsx", + "path": "src/plugins/charts/public/static/components/index.ts", "deprecated": false, + "returnComment": [], "children": [ { "parentPluginId": "charts", "id": "def-public.ColorPicker.$1", - "type": "Object", + "type": "Uncategorized", "tags": [], - "label": "{\n onChange,\n color: selectedColor,\n label,\n useLegacyColors = true,\n colorIsOverwritten = true,\n onKeyDown,\n maxDepth,\n layerIndex,\n}", + "label": "props", "description": [], "signature": [ - "ColorPickerProps" + "P" ], - "path": "src/plugins/charts/public/static/components/color_picker.tsx", - "deprecated": false, - "isRequired": true + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false } ], + "initialIsOpen": false + }, + { + "parentPluginId": "charts", + "id": "def-public.ColorPickerLazy", + "type": "Function", + "tags": [], + "label": "ColorPickerLazy", + "description": [ + "\nThe Lazily-loaded `ColorPicker` component. Consumers should use `React.Suspense` or\nthe withSuspense` HOC to load this component." + ], + "signature": [ + "React.ExoticComponent<", + "ColorPickerProps", + "> & { readonly _result: ({ onChange, color: selectedColor, label, useLegacyColors, colorIsOverwritten, onKeyDown, maxDepth, layerIndex, }: ", + "ColorPickerProps", + ") => JSX.Element; }" + ], + "path": "src/plugins/charts/public/static/components/index.ts", + "deprecated": false, "returnComment": [], + "children": [ + { + "parentPluginId": "charts", + "id": "def-public.ColorPickerLazy.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false + } + ], "initialIsOpen": false }, { @@ -108,9 +147,9 @@ "label": "EmptyPlaceholder", "description": [], "signature": [ - "({ icon, iconColor, message, dataTestSubj, className, }: { icon: ", - "IconType", - "; iconColor?: string | undefined; message?: JSX.Element | undefined; dataTestSubj?: string | undefined; className?: string | undefined; }) => JSX.Element" + "({ icon, iconColor, message, dataTestSubj, className, }: ", + "EmptyPlaceholderProps", + ") => JSX.Element" ], "path": "src/plugins/charts/public/static/components/empty_placeholder.tsx", "deprecated": false, @@ -122,75 +161,12 @@ "tags": [], "label": "{\n icon,\n iconColor = 'subdued',\n message = ,\n dataTestSubj = 'emptyPlaceholder',\n className,\n}", "description": [], + "signature": [ + "EmptyPlaceholderProps" + ], "path": "src/plugins/charts/public/static/components/empty_placeholder.tsx", "deprecated": false, - "children": [ - { - "parentPluginId": "charts", - "id": "def-public.EmptyPlaceholder.$1.icon", - "type": "CompoundType", - "tags": [], - "label": "icon", - "description": [], - "signature": [ - "string | React.ComponentType<{}>" - ], - "path": "src/plugins/charts/public/static/components/empty_placeholder.tsx", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-public.EmptyPlaceholder.$1.iconColor", - "type": "string", - "tags": [], - "label": "iconColor", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/charts/public/static/components/empty_placeholder.tsx", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-public.EmptyPlaceholder.$1.message", - "type": "Object", - "tags": [], - "label": "message", - "description": [], - "signature": [ - "JSX.Element | undefined" - ], - "path": "src/plugins/charts/public/static/components/empty_placeholder.tsx", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-public.EmptyPlaceholder.$1.dataTestSubj", - "type": "string", - "tags": [], - "label": "dataTestSubj", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/charts/public/static/components/empty_placeholder.tsx", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-public.EmptyPlaceholder.$1.className", - "type": "string", - "tags": [], - "label": "className", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/charts/public/static/components/empty_placeholder.tsx", - "deprecated": false - } - ] + "isRequired": true } ], "returnComment": [], @@ -628,11 +604,15 @@ "type": "Function", "tags": [], "label": "LegendToggle", - "description": [], + "description": [ + "\nA `LegendToggle` component that is wrapped by the `withSuspense` HOC. This component can\nbe used directly by consumers and will load the `LegendToggleLazy` component lazily with\na predefined fallback and error boundary." + ], "signature": [ - "React.NamedExoticComponent & { readonly type: ({ onClick, showLegend, legendPosition }: LegendToggleProps) => JSX.Element; }" + "React.ForwardRefExoticComponent<", + "LegendToggleProps", + " & React.RefAttributes<{}>>" ], - "path": "src/plugins/charts/public/static/components/legend_toggle.tsx", + "path": "src/plugins/charts/public/static/components/index.ts", "deprecated": false, "returnComment": [], "children": [ @@ -652,6 +632,42 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "charts", + "id": "def-public.LegendToggleLazy", + "type": "Function", + "tags": [], + "label": "LegendToggleLazy", + "description": [ + "\nThe Lazily-loaded `LegendToggle` component. Consumers should use `React.Suspense` or\nthe withSuspense` HOC to load this component." + ], + "signature": [ + "React.ExoticComponent<", + "LegendToggleProps", + "> & { readonly _result: React.MemoExoticComponent<({ onClick, showLegend, legendPosition }: ", + "LegendToggleProps", + ") => JSX.Element>; }" + ], + "path": "src/plugins/charts/public/static/components/index.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "charts", + "id": "def-public.LegendToggleLazy.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "charts", "id": "def-public.lightenColor", @@ -866,6 +882,26 @@ ], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "charts", + "id": "def-public.useCommonChartStyles", + "type": "Function", + "tags": [], + "label": "useCommonChartStyles", + "description": [], + "signature": [ + "() => { chartIcon: { subdued: ", + "SerializedStyles", + "; accent: ", + "SerializedStyles", + "; }; }" + ], + "path": "src/plugins/charts/public/static/components/common_chart_styles.ts", + "deprecated": false, + "children": [], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [ @@ -916,81 +952,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "charts", - "id": "def-public.ChartColorConfiguration", - "type": "Interface", - "tags": [], - "label": "ChartColorConfiguration", - "description": [ - "\nInformation about the structure of a chart to determine the color of a series within it." - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "charts", - "id": "def-public.ChartColorConfiguration.totalSeries", - "type": "number", - "tags": [], - "label": "totalSeries", - "description": [ - "\nOverall number of series in the current chart" - ], - "signature": [ - "number | undefined" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-public.ChartColorConfiguration.maxDepth", - "type": "number", - "tags": [], - "label": "maxDepth", - "description": [ - "\nMax nesting depth of the series tree" - ], - "signature": [ - "number | undefined" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-public.ChartColorConfiguration.behindText", - "type": "CompoundType", - "tags": [], - "label": "behindText", - "description": [ - "\nFlag whether the color will be used behind text. The palette can use this information to\nadjust colors for better a11y. Might be ignored depending on the palette." - ], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-public.ChartColorConfiguration.syncColors", - "type": "CompoundType", - "tags": [], - "label": "syncColors", - "description": [ - "\nFlag whether a color assignment to a given key should be remembered and re-used the next time the key shows up.\nThis setting might be ignored based on the palette." - ], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "charts", "id": "def-public.ClickTriggerEvent", @@ -1162,7 +1123,7 @@ "tags": [], "label": "CustomPaletteArguments", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false, "children": [ { @@ -1175,7 +1136,7 @@ "signature": [ "string[] | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -1185,7 +1146,7 @@ "tags": [], "label": "gradient", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -1198,7 +1159,7 @@ "signature": [ "boolean | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -1211,7 +1172,7 @@ "signature": [ "number[] | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -1224,7 +1185,7 @@ "signature": [ "\"number\" | \"percent\" | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -1237,7 +1198,7 @@ "signature": [ "number | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -1250,7 +1211,7 @@ "signature": [ "number | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -1261,16 +1222,10 @@ "label": "continuity", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteContinuity", - "text": "PaletteContinuity" - }, + "PaletteContinuity", " | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false } ], @@ -1283,7 +1238,7 @@ "tags": [], "label": "CustomPaletteState", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false, "children": [ { @@ -1296,7 +1251,7 @@ "signature": [ "string[]" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -1306,7 +1261,7 @@ "tags": [], "label": "gradient", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -1319,7 +1274,7 @@ "signature": [ "number[]" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -1332,7 +1287,7 @@ "signature": [ "\"number\" | \"percent\"" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -1342,7 +1297,7 @@ "tags": [], "label": "rangeMin", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -1352,7 +1307,7 @@ "tags": [], "label": "rangeMax", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -1363,16 +1318,10 @@ "label": "continuity", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteContinuity", - "text": "PaletteContinuity" - }, + "PaletteContinuity", " | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false } ], @@ -1471,576 +1420,54 @@ }, { "parentPluginId": "charts", - "id": "def-public.PaletteDefinition", + "id": "def-public.RawColorSchema", "type": "Interface", "tags": [], - "label": "PaletteDefinition", - "description": [ - "\nDefinition of a global palette.\n\nA palette controls the appearance of Lens charts on an editor level.\nThe palette wont get reset when switching charts.\n\nA palette can hold internal state (e.g. for customizations) and also includes\nan editor component to edit the internal state." - ], - "signature": [ - { - "pluginId": "charts", - "scope": "public", - "docId": "kibChartsPluginApi", - "section": "def-public.PaletteDefinition", - "text": "PaletteDefinition" - }, - "" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", + "label": "RawColorSchema", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false, "children": [ { "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.id", - "type": "string", + "id": "def-public.RawColorSchema.id", + "type": "Enum", "tags": [], "label": "id", - "description": [ - "\nUnique id of the palette (this will be persisted along with the visualization state)" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.title", - "type": "string", - "tags": [], - "label": "title", - "description": [ - "\nUser facing title (should be i18n-ized)" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.internal", - "type": "CompoundType", - "tags": [], - "label": "internal", - "description": [ - "\nFlag indicating whether users should be able to pick this palette manually." - ], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.toExpression", - "type": "Function", - "tags": [], - "label": "toExpression", - "description": [ - "\nSerialize the internal state of the palette into an expression function.\nThis function should be used to pass the palette to the expression function applying color and other styles" - ], + "description": [], "signature": [ - "(state?: T | undefined) => ", { - "pluginId": "expressions", + "pluginId": "charts", "scope": "common", - "docId": "kibExpressionsPluginApi", - "section": "def-common.ExpressionAstExpression", - "text": "ExpressionAstExpression" - } - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.toExpression.$1", - "type": "Uncategorized", - "tags": [], - "label": "state", - "description": [ - "The internal state of the palette" - ], - "signature": [ - "T | undefined" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.getCategoricalColor", - "type": "Function", - "tags": [], - "label": "getCategoricalColor", - "description": [ - "\nColor a series according to the internal rules of the palette." - ], - "signature": [ - "(series: ", - { - "pluginId": "charts", - "scope": "public", - "docId": "kibChartsPluginApi", - "section": "def-public.SeriesLayer", - "text": "SeriesLayer" - }, - "[], chartConfiguration?: ", - { - "pluginId": "charts", - "scope": "public", - "docId": "kibChartsPluginApi", - "section": "def-public.ChartColorConfiguration", - "text": "ChartColorConfiguration" - }, - " | undefined, state?: T | undefined) => string | null" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.getCategoricalColor.$1", - "type": "Array", - "tags": [], - "label": "series", - "description": [ - "The current series along with its ancestors." - ], - "signature": [ - { - "pluginId": "charts", - "scope": "public", - "docId": "kibChartsPluginApi", - "section": "def-public.SeriesLayer", - "text": "SeriesLayer" - }, - "[]" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.getCategoricalColor.$2", - "type": "Object", - "tags": [], - "label": "chartConfiguration", - "description": [], - "signature": [ - { - "pluginId": "charts", - "scope": "public", - "docId": "kibChartsPluginApi", - "section": "def-public.ChartColorConfiguration", - "text": "ChartColorConfiguration" - }, - " | undefined" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.getCategoricalColor.$3", - "type": "Uncategorized", - "tags": [], - "label": "state", - "description": [ - "The internal state of the palette" - ], - "signature": [ - "T | undefined" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.getCategoricalColors", - "type": "Function", - "tags": [], - "label": "getCategoricalColors", - "description": [ - "\nGet a spectrum of colors of the current palette.\nThis can be used if the chart wants to control color assignment locally." - ], - "signature": [ - "(size: number, state?: T | undefined) => string[]" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.getCategoricalColors.$1", - "type": "number", - "tags": [], - "label": "size", - "description": [], - "signature": [ - "number" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.getCategoricalColors.$2", - "type": "Uncategorized", - "tags": [], - "label": "state", - "description": [], - "signature": [ - "T | undefined" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.canDynamicColoring", - "type": "CompoundType", - "tags": [], - "label": "canDynamicColoring", - "description": [ - "\nDefine whether a palette supports dynamic coloring (i.e. gradient colors mapped to number values)" - ], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.getColorForValue", - "type": "Function", - "tags": [], - "label": "getColorForValue", - "description": [ - "\nGet the assigned color for the given value based on its data domain and state settings.\nThis can be used for dynamic coloring based on uniform color distribution or custom stops." - ], - "signature": [ - "((value: number | undefined, state: T, { min, max }: { min: number; max: number; }) => string | undefined) | undefined" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.getColorForValue.$1", - "type": "number", - "tags": [], - "label": "value", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.getColorForValue.$2", - "type": "Uncategorized", - "tags": [], - "label": "state", - "description": [], - "signature": [ - "T" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.getColorForValue.$3", - "type": "Object", - "tags": [], - "label": "{ min, max }", - "description": [], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.getColorForValue.$3.min", - "type": "number", - "tags": [], - "label": "min", - "description": [], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteDefinition.getColorForValue.$3.max", - "type": "number", - "tags": [], - "label": "max", - "description": [], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false - } - ] - } - ], - "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteOutput", - "type": "Interface", - "tags": [], - "label": "PaletteOutput", - "description": [], - "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, - "" - ], - "path": "src/plugins/charts/common/palette.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "charts", - "id": "def-public.PaletteOutput.type", - "type": "CompoundType", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"palette\" | \"system_palette\"" - ], - "path": "src/plugins/charts/common/palette.ts", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteOutput.name", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "path": "src/plugins/charts/common/palette.ts", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteOutput.params", - "type": "Uncategorized", - "tags": [], - "label": "params", - "description": [], - "signature": [ - "T | undefined" - ], - "path": "src/plugins/charts/common/palette.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteRegistry", - "type": "Interface", - "tags": [], - "label": "PaletteRegistry", - "description": [], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "charts", - "id": "def-public.PaletteRegistry.get", - "type": "Function", - "tags": [], - "label": "get", - "description": [], - "signature": [ - "(name: string) => ", - { - "pluginId": "charts", - "scope": "public", - "docId": "kibChartsPluginApi", - "section": "def-public.PaletteDefinition", - "text": "PaletteDefinition" - }, - "" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "charts", - "id": "def-public.PaletteRegistry.get.$1", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "signature": [ - "string" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "charts", - "id": "def-public.PaletteRegistry.getAll", - "type": "Function", - "tags": [], - "label": "getAll", - "description": [], - "signature": [ - "() => ", - { - "pluginId": "charts", - "scope": "public", "docId": "kibChartsPluginApi", - "section": "def-public.PaletteDefinition", - "text": "PaletteDefinition" - }, - "[]" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "children": [], - "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "charts", - "id": "def-public.RawColorSchema", - "type": "Interface", - "tags": [], - "label": "RawColorSchema", - "description": [], - "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "charts", - "id": "def-public.RawColorSchema.id", - "type": "Enum", - "tags": [], - "label": "id", - "description": [], - "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.ColorSchemas", - "text": "ColorSchemas" - } - ], - "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-public.RawColorSchema.label", - "type": "string", - "tags": [], - "label": "label", - "description": [], - "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-public.RawColorSchema.value", - "type": "Array", - "tags": [], - "label": "value", - "description": [], - "signature": [ - "[number, number[]][]" - ], - "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "charts", - "id": "def-public.SeriesLayer", - "type": "Interface", - "tags": [], - "label": "SeriesLayer", - "description": [ - "\nInformation about a series in a chart used to determine its color.\nSeries layers can be nested, this means each series layer can have an ancestor." - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "charts", - "id": "def-public.SeriesLayer.name", - "type": "string", - "tags": [], - "label": "name", - "description": [ - "\nName of the series (can be used for lookup-based coloring)" + "section": "def-common.ColorSchemas", + "text": "ColorSchemas" + } ], - "path": "src/plugins/charts/public/services/palettes/types.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { "parentPluginId": "charts", - "id": "def-public.SeriesLayer.rankAtDepth", - "type": "number", + "id": "def-public.RawColorSchema.label", + "type": "string", "tags": [], - "label": "rankAtDepth", - "description": [ - "\nRank of the series compared to siblings with the same ancestor" - ], - "path": "src/plugins/charts/public/services/palettes/types.ts", + "label": "label", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { "parentPluginId": "charts", - "id": "def-public.SeriesLayer.totalSeriesAtDepth", - "type": "number", + "id": "def-public.RawColorSchema.value", + "type": "Array", "tags": [], - "label": "totalSeriesAtDepth", - "description": [ - "\nTotal number of series with the same ancestor" + "label": "value", + "description": [], + "signature": [ + "[number, number[]][]" ], - "path": "src/plugins/charts/public/services/palettes/types.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false } ], @@ -2116,7 +1543,7 @@ "tags": [], "label": "SystemPaletteArguments", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/system_palette.ts", "deprecated": false, "children": [ { @@ -2126,7 +1553,7 @@ "tags": [], "label": "name", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/system_palette.ts", "deprecated": false } ], @@ -2858,13 +2285,7 @@ "description": [], "signature": [ "{ getPalettes: () => Promise<", - { - "pluginId": "charts", - "scope": "public", - "docId": "kibChartsPluginApi", - "section": "def-public.PaletteRegistry", - "text": "PaletteRegistry" - }, + "PaletteRegistry", ">; }" ], "path": "src/plugins/charts/public/plugin.ts", @@ -2910,7 +2331,7 @@ "tags": [], "label": "CustomPaletteArguments", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false, "children": [ { @@ -2923,7 +2344,7 @@ "signature": [ "string[] | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -2933,7 +2354,7 @@ "tags": [], "label": "gradient", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -2946,7 +2367,7 @@ "signature": [ "boolean | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -2959,7 +2380,7 @@ "signature": [ "number[] | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -2972,7 +2393,7 @@ "signature": [ "\"number\" | \"percent\" | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -2985,7 +2406,7 @@ "signature": [ "number | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -2998,7 +2419,7 @@ "signature": [ "number | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3009,16 +2430,10 @@ "label": "continuity", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteContinuity", - "text": "PaletteContinuity" - }, + "PaletteContinuity", " | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false } ], @@ -3031,7 +2446,7 @@ "tags": [], "label": "CustomPaletteState", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false, "children": [ { @@ -3044,7 +2459,7 @@ "signature": [ "string[]" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3054,7 +2469,7 @@ "tags": [], "label": "gradient", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3067,7 +2482,7 @@ "signature": [ "number[]" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3080,7 +2495,7 @@ "signature": [ "\"number\" | \"percent\"" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3090,7 +2505,7 @@ "tags": [], "label": "rangeMin", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3100,7 +2515,7 @@ "tags": [], "label": "rangeMax", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3111,75 +2526,10 @@ "label": "continuity", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteContinuity", - "text": "PaletteContinuity" - }, + "PaletteContinuity", " | undefined" ], - "path": "src/plugins/charts/common/palette.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "charts", - "id": "def-server.PaletteOutput", - "type": "Interface", - "tags": [], - "label": "PaletteOutput", - "description": [], - "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, - "" - ], - "path": "src/plugins/charts/common/palette.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "charts", - "id": "def-server.PaletteOutput.type", - "type": "CompoundType", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"palette\" | \"system_palette\"" - ], - "path": "src/plugins/charts/common/palette.ts", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-server.PaletteOutput.name", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "path": "src/plugins/charts/common/palette.ts", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-server.PaletteOutput.params", - "type": "Uncategorized", - "tags": [], - "label": "params", - "description": [], - "signature": [ - "T | undefined" - ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false } ], @@ -3192,7 +2542,7 @@ "tags": [], "label": "SystemPaletteArguments", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/system_palette.ts", "deprecated": false, "children": [ { @@ -3202,7 +2552,7 @@ "tags": [], "label": "name", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/system_palette.ts", "deprecated": false } ], @@ -3231,98 +2581,6 @@ "common": { "classes": [], "functions": [ - { - "parentPluginId": "charts", - "id": "def-common.checkIsMaxContinuity", - "type": "Function", - "tags": [], - "label": "checkIsMaxContinuity", - "description": [], - "signature": [ - "(continuity: ", - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteContinuity", - "text": "PaletteContinuity" - }, - " | undefined) => boolean" - ], - "path": "src/plugins/charts/common/static/palette/index.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "charts", - "id": "def-common.checkIsMaxContinuity.$1", - "type": "CompoundType", - "tags": [], - "label": "continuity", - "description": [], - "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteContinuity", - "text": "PaletteContinuity" - }, - " | undefined" - ], - "path": "src/plugins/charts/common/static/palette/index.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "charts", - "id": "def-common.checkIsMinContinuity", - "type": "Function", - "tags": [], - "label": "checkIsMinContinuity", - "description": [], - "signature": [ - "(continuity: ", - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteContinuity", - "text": "PaletteContinuity" - }, - " | undefined) => boolean" - ], - "path": "src/plugins/charts/common/static/palette/index.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "charts", - "id": "def-common.checkIsMinContinuity.$1", - "type": "CompoundType", - "tags": [], - "label": "continuity", - "description": [], - "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteContinuity", - "text": "PaletteContinuity" - }, - " | undefined" - ], - "path": "src/plugins/charts/common/static/palette/index.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "charts", "id": "def-common.getHeatmapColors", @@ -3377,58 +2635,9 @@ "description": [], "signature": [ "() => ", - { - "pluginId": "expressions", - "scope": "common", - "docId": "kibExpressionsPluginApi", - "section": "def-common.ExpressionFunctionDefinition", - "text": "ExpressionFunctionDefinition" - }, - "<\"palette\", null, ", - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.CustomPaletteArguments", - "text": "CustomPaletteArguments" - }, - ", ", - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, - "<", - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.CustomPaletteState", - "text": "CustomPaletteState" - }, - ">, ", - { - "pluginId": "expressions", - "scope": "common", - "docId": "kibExpressionsPluginApi", - "section": "def-common.ExecutionContext", - "text": "ExecutionContext" - }, - "<", - { - "pluginId": "inspector", - "scope": "common", - "docId": "kibInspectorPluginApi", - "section": "def-common.Adapters", - "text": "Adapters" - }, - ", ", - "SerializableRecord", - ">>" + "PaletteExpressionFunctionDefinition" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/palette.ts", "deprecated": false, "children": [], "returnComment": [], @@ -3459,13 +2668,7 @@ "text": "SystemPaletteArguments" }, ", ", - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<{ [key: string]: unknown; }>, ", { "pluginId": "expressions", @@ -3486,7 +2689,7 @@ "SerializableRecord", ">>" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/system_palette.ts", "deprecated": false, "children": [], "returnComment": [], @@ -3618,7 +2821,7 @@ "tags": [], "label": "CustomPaletteArguments", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false, "children": [ { @@ -3631,7 +2834,7 @@ "signature": [ "string[] | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3641,7 +2844,7 @@ "tags": [], "label": "gradient", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3654,7 +2857,7 @@ "signature": [ "boolean | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3667,7 +2870,7 @@ "signature": [ "number[] | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3680,7 +2883,7 @@ "signature": [ "\"number\" | \"percent\" | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3693,7 +2896,7 @@ "signature": [ "number | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3706,7 +2909,7 @@ "signature": [ "number | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3717,16 +2920,10 @@ "label": "continuity", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteContinuity", - "text": "PaletteContinuity" - }, + "PaletteContinuity", " | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false } ], @@ -3739,7 +2936,7 @@ "tags": [], "label": "CustomPaletteState", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false, "children": [ { @@ -3752,7 +2949,7 @@ "signature": [ "string[]" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3762,7 +2959,7 @@ "tags": [], "label": "gradient", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3775,7 +2972,7 @@ "signature": [ "number[]" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3788,7 +2985,7 @@ "signature": [ "\"number\" | \"percent\"" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3798,7 +2995,7 @@ "tags": [], "label": "rangeMin", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3808,7 +3005,7 @@ "tags": [], "label": "rangeMax", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false }, { @@ -3819,16 +3016,10 @@ "label": "continuity", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteContinuity", - "text": "PaletteContinuity" - }, + "PaletteContinuity", " | undefined" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/types.ts", "deprecated": false } ], @@ -3925,65 +3116,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "charts", - "id": "def-common.PaletteOutput", - "type": "Interface", - "tags": [], - "label": "PaletteOutput", - "description": [], - "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, - "" - ], - "path": "src/plugins/charts/common/palette.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "charts", - "id": "def-common.PaletteOutput.type", - "type": "CompoundType", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"palette\" | \"system_palette\"" - ], - "path": "src/plugins/charts/common/palette.ts", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-common.PaletteOutput.name", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "path": "src/plugins/charts/common/palette.ts", - "deprecated": false - }, - { - "parentPluginId": "charts", - "id": "def-common.PaletteOutput.params", - "type": "Uncategorized", - "tags": [], - "label": "params", - "description": [], - "signature": [ - "T | undefined" - ], - "path": "src/plugins/charts/common/palette.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "charts", "id": "def-common.RawColorSchema", @@ -4109,7 +3241,7 @@ "tags": [], "label": "SystemPaletteArguments", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/system_palette.ts", "deprecated": false, "children": [ { @@ -4119,7 +3251,7 @@ "tags": [], "label": "name", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/expressions/palette/system_palette.ts", "deprecated": false } ], @@ -4210,7 +3342,7 @@ "signature": [ "string[]" ], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/constants.ts", "deprecated": false, "initialIsOpen": false }, @@ -4242,20 +3374,6 @@ "deprecated": false, "initialIsOpen": false }, - { - "parentPluginId": "charts", - "id": "def-common.PaletteContinuity", - "type": "Type", - "tags": [], - "label": "PaletteContinuity", - "description": [], - "signature": [ - "\"above\" | \"below\" | \"none\" | \"all\"" - ], - "path": "src/plugins/charts/common/types.ts", - "deprecated": false, - "initialIsOpen": false - }, { "parentPluginId": "charts", "id": "def-common.paletteIds", diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index dc22287d352d0..0fe3642f18398 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github summary: API docs for the charts plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 322 | 2 | 289 | 4 | +| 272 | 2 | 253 | 9 | ## Client diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index 378879307a9d0..ccf2076033b67 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github summary: API docs for the cloud plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] warning: 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. --- diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index d9a6b1d20909c..d487aab1a4754 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github summary: API docs for the cloudSecurityPosture plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] warning: 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. --- diff --git a/api_docs/console.mdx b/api_docs/console.mdx index 343ada2ae4109..4d91e53febaf7 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github summary: API docs for the console plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] warning: 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. --- diff --git a/api_docs/controls.devdocs.json b/api_docs/controls.devdocs.json index 7cd89434ad64f..fe22caa955878 100644 --- a/api_docs/controls.devdocs.json +++ b/api_docs/controls.devdocs.json @@ -3154,31 +3154,11 @@ "text": "OptionsListEmbeddableInput" }, " extends ", - "ControlInput" + "DataControlInput" ], "path": "src/plugins/controls/common/control_types/options_list/types.ts", "deprecated": false, "children": [ - { - "parentPluginId": "controls", - "id": "def-public.OptionsListEmbeddableInput.fieldName", - "type": "string", - "tags": [], - "label": "fieldName", - "description": [], - "path": "src/plugins/controls/common/control_types/options_list/types.ts", - "deprecated": false - }, - { - "parentPluginId": "controls", - "id": "def-public.OptionsListEmbeddableInput.dataViewId", - "type": "string", - "tags": [], - "label": "dataViewId", - "description": [], - "path": "src/plugins/controls/common/control_types/options_list/types.ts", - "deprecated": false - }, { "parentPluginId": "controls", "id": "def-public.OptionsListEmbeddableInput.selectedOptions", @@ -3302,31 +3282,11 @@ "text": "RangeSliderEmbeddableInput" }, " extends ", - "ControlInput" + "DataControlInput" ], "path": "src/plugins/controls/common/control_types/range_slider/types.ts", "deprecated": false, "children": [ - { - "parentPluginId": "controls", - "id": "def-public.RangeSliderEmbeddableInput.fieldName", - "type": "string", - "tags": [], - "label": "fieldName", - "description": [], - "path": "src/plugins/controls/common/control_types/range_slider/types.ts", - "deprecated": false - }, - { - "parentPluginId": "controls", - "id": "def-public.RangeSliderEmbeddableInput.dataViewId", - "type": "string", - "tags": [], - "label": "dataViewId", - "description": [], - "path": "src/plugins/controls/common/control_types/range_slider/types.ts", - "deprecated": false - }, { "parentPluginId": "controls", "id": "def-public.RangeSliderEmbeddableInput.value", @@ -3600,7 +3560,46 @@ }, "server": { "classes": [], - "functions": [], + "functions": [ + { + "parentPluginId": "controls", + "id": "def-server.initializeControlGroupTelemetry", + "type": "Function", + "tags": [], + "label": "initializeControlGroupTelemetry", + "description": [], + "signature": [ + "(statsSoFar: Record) => ", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.ControlGroupTelemetry", + "text": "ControlGroupTelemetry" + } + ], + "path": "src/plugins/controls/server/control_group/control_group_telemetry.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "controls", + "id": "def-server.initializeControlGroupTelemetry.$1", + "type": "Object", + "tags": [], + "label": "statsSoFar", + "description": [], + "signature": [ + "Record" + ], + "path": "src/plugins/controls/server/control_group/control_group_telemetry.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], "interfaces": [], "enums": [], "misc": [], @@ -3609,6 +3608,60 @@ "common": { "classes": [], "functions": [ + { + "parentPluginId": "controls", + "id": "def-common.controlGroupInputToRawControlGroupAttributes", + "type": "Function", + "tags": [], + "label": "controlGroupInputToRawControlGroupAttributes", + "description": [], + "signature": [ + "(controlGroupInput: Omit<", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.ControlGroupInput", + "text": "ControlGroupInput" + }, + ", \"id\">) => ", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.RawControlGroupAttributes", + "text": "RawControlGroupAttributes" + } + ], + "path": "src/plugins/controls/common/control_group/control_group_persistence.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "controls", + "id": "def-common.controlGroupInputToRawControlGroupAttributes.$1", + "type": "Object", + "tags": [], + "label": "controlGroupInput", + "description": [], + "signature": [ + "Omit<", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.ControlGroupInput", + "text": "ControlGroupInput" + }, + ", \"id\">" + ], + "path": "src/plugins/controls/common/control_group/control_group_persistence.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "controls", "id": "def-common.getDefaultControlGroupInput", @@ -3627,11 +3680,228 @@ }, ", \"id\">" ], - "path": "src/plugins/controls/common/control_group/control_group_constants.ts", + "path": "src/plugins/controls/common/control_group/control_group_persistence.ts", "deprecated": false, "children": [], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "controls", + "id": "def-common.persistableControlGroupInputIsEqual", + "type": "Function", + "tags": [], + "label": "persistableControlGroupInputIsEqual", + "description": [], + "signature": [ + "(a: ", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.PersistableControlGroupInput", + "text": "PersistableControlGroupInput" + }, + " | undefined, b: ", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.PersistableControlGroupInput", + "text": "PersistableControlGroupInput" + }, + " | undefined) => boolean" + ], + "path": "src/plugins/controls/common/control_group/control_group_persistence.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "controls", + "id": "def-common.persistableControlGroupInputIsEqual.$1", + "type": "Object", + "tags": [], + "label": "a", + "description": [], + "signature": [ + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.PersistableControlGroupInput", + "text": "PersistableControlGroupInput" + }, + " | undefined" + ], + "path": "src/plugins/controls/common/control_group/control_group_persistence.ts", + "deprecated": false, + "isRequired": false + }, + { + "parentPluginId": "controls", + "id": "def-common.persistableControlGroupInputIsEqual.$2", + "type": "Object", + "tags": [], + "label": "b", + "description": [], + "signature": [ + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.PersistableControlGroupInput", + "text": "PersistableControlGroupInput" + }, + " | undefined" + ], + "path": "src/plugins/controls/common/control_group/control_group_persistence.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "controls", + "id": "def-common.rawControlGroupAttributesToControlGroupInput", + "type": "Function", + "tags": [], + "label": "rawControlGroupAttributesToControlGroupInput", + "description": [], + "signature": [ + "(rawControlGroupAttributes: ", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.RawControlGroupAttributes", + "text": "RawControlGroupAttributes" + }, + ") => Omit<", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.ControlGroupInput", + "text": "ControlGroupInput" + }, + ", \"id\"> | undefined" + ], + "path": "src/plugins/controls/common/control_group/control_group_persistence.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "controls", + "id": "def-common.rawControlGroupAttributesToControlGroupInput.$1", + "type": "CompoundType", + "tags": [], + "label": "rawControlGroupAttributes", + "description": [], + "signature": [ + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.RawControlGroupAttributes", + "text": "RawControlGroupAttributes" + } + ], + "path": "src/plugins/controls/common/control_group/control_group_persistence.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "controls", + "id": "def-common.rawControlGroupAttributesToSerializable", + "type": "Function", + "tags": [], + "label": "rawControlGroupAttributesToSerializable", + "description": [], + "signature": [ + "(rawControlGroupAttributes: Omit<", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.RawControlGroupAttributes", + "text": "RawControlGroupAttributes" + }, + ", \"id\">) => ", + "SerializableRecord" + ], + "path": "src/plugins/controls/common/control_group/control_group_persistence.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "controls", + "id": "def-common.rawControlGroupAttributesToSerializable.$1", + "type": "Object", + "tags": [], + "label": "rawControlGroupAttributes", + "description": [], + "signature": [ + "Omit<", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.RawControlGroupAttributes", + "text": "RawControlGroupAttributes" + }, + ", \"id\">" + ], + "path": "src/plugins/controls/common/control_group/control_group_persistence.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "controls", + "id": "def-common.serializableToRawControlGroupAttributes", + "type": "Function", + "tags": [], + "label": "serializableToRawControlGroupAttributes", + "description": [], + "signature": [ + "(serializable: ", + "SerializableRecord", + ") => Omit<", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.RawControlGroupAttributes", + "text": "RawControlGroupAttributes" + }, + ", \"id\" | \"type\">" + ], + "path": "src/plugins/controls/common/control_group/control_group_persistence.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "controls", + "id": "def-common.serializableToRawControlGroupAttributes.$1", + "type": "Object", + "tags": [], + "label": "serializable", + "description": [], + "signature": [ + "SerializableRecord" + ], + "path": "src/plugins/controls/common/control_group/control_group_persistence.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [ @@ -3732,6 +4002,81 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "controls", + "id": "def-common.ControlGroupTelemetry", + "type": "Interface", + "tags": [], + "label": "ControlGroupTelemetry", + "description": [], + "path": "src/plugins/controls/common/control_group/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "controls", + "id": "def-common.ControlGroupTelemetry.total", + "type": "number", + "tags": [], + "label": "total", + "description": [], + "path": "src/plugins/controls/common/control_group/types.ts", + "deprecated": false + }, + { + "parentPluginId": "controls", + "id": "def-common.ControlGroupTelemetry.chaining_system", + "type": "Object", + "tags": [], + "label": "chaining_system", + "description": [], + "signature": [ + "{ [key: string]: number; }" + ], + "path": "src/plugins/controls/common/control_group/types.ts", + "deprecated": false + }, + { + "parentPluginId": "controls", + "id": "def-common.ControlGroupTelemetry.label_position", + "type": "Object", + "tags": [], + "label": "label_position", + "description": [], + "signature": [ + "{ [key: string]: number; }" + ], + "path": "src/plugins/controls/common/control_group/types.ts", + "deprecated": false + }, + { + "parentPluginId": "controls", + "id": "def-common.ControlGroupTelemetry.ignore_settings", + "type": "Object", + "tags": [], + "label": "ignore_settings", + "description": [], + "signature": [ + "{ [key: string]: number; }" + ], + "path": "src/plugins/controls/common/control_group/types.ts", + "deprecated": false + }, + { + "parentPluginId": "controls", + "id": "def-common.ControlGroupTelemetry.by_type", + "type": "Object", + "tags": [], + "label": "by_type", + "description": [], + "signature": [ + "{ [key: string]: { total: number; details: { [key: string]: number; }; }; }" + ], + "path": "src/plugins/controls/common/control_group/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "controls", "id": "def-common.ControlPanelState", @@ -3838,31 +4183,11 @@ "text": "OptionsListEmbeddableInput" }, " extends ", - "ControlInput" + "DataControlInput" ], "path": "src/plugins/controls/common/control_types/options_list/types.ts", "deprecated": false, "children": [ - { - "parentPluginId": "controls", - "id": "def-common.OptionsListEmbeddableInput.fieldName", - "type": "string", - "tags": [], - "label": "fieldName", - "description": [], - "path": "src/plugins/controls/common/control_types/options_list/types.ts", - "deprecated": false - }, - { - "parentPluginId": "controls", - "id": "def-common.OptionsListEmbeddableInput.dataViewId", - "type": "string", - "tags": [], - "label": "dataViewId", - "description": [], - "path": "src/plugins/controls/common/control_types/options_list/types.ts", - "deprecated": false - }, { "parentPluginId": "controls", "id": "def-common.OptionsListEmbeddableInput.selectedOptions", @@ -3921,31 +4246,11 @@ "text": "RangeSliderEmbeddableInput" }, " extends ", - "ControlInput" + "DataControlInput" ], "path": "src/plugins/controls/common/control_types/range_slider/types.ts", "deprecated": false, "children": [ - { - "parentPluginId": "controls", - "id": "def-common.RangeSliderEmbeddableInput.fieldName", - "type": "string", - "tags": [], - "label": "fieldName", - "description": [], - "path": "src/plugins/controls/common/control_types/range_slider/types.ts", - "deprecated": false - }, - { - "parentPluginId": "controls", - "id": "def-common.RangeSliderEmbeddableInput.dataViewId", - "type": "string", - "tags": [], - "label": "dataViewId", - "description": [], - "path": "src/plugins/controls/common/control_types/range_slider/types.ts", - "deprecated": false - }, { "parentPluginId": "controls", "id": "def-common.RangeSliderEmbeddableInput.value", @@ -3993,6 +4298,34 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "controls", + "id": "def-common.DEFAULT_CONTROL_STYLE", + "type": "CompoundType", + "tags": [], + "label": "DEFAULT_CONTROL_STYLE", + "description": [], + "signature": [ + "\"twoLine\" | \"oneLine\"" + ], + "path": "src/plugins/controls/common/control_group/control_group_constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "controls", + "id": "def-common.DEFAULT_CONTROL_WIDTH", + "type": "CompoundType", + "tags": [], + "label": "DEFAULT_CONTROL_WIDTH", + "description": [], + "signature": [ + "\"auto\" | \"small\" | \"medium\" | \"large\"" + ], + "path": "src/plugins/controls/common/control_group/control_group_constants.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "controls", "id": "def-common.OPTIONS_LIST_CONTROL", @@ -4007,6 +4340,34 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "controls", + "id": "def-common.PersistableControlGroupInput", + "type": "Type", + "tags": [], + "label": "PersistableControlGroupInput", + "description": [], + "signature": [ + "{ controlStyle: ", + "ControlStyle", + "; ignoreParentSettings?: ", + "ParentIgnoreSettings", + " | undefined; chainingSystem: ", + "ControlGroupChainingSystem", + "; panels: ", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.ControlsPanels", + "text": "ControlsPanels" + }, + "; }" + ], + "path": "src/plugins/controls/common/control_group/types.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "controls", "id": "def-common.RANGE_SLIDER_CONTROL", @@ -4021,6 +4382,28 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "controls", + "id": "def-common.RawControlGroupAttributes", + "type": "Type", + "tags": [], + "label": "RawControlGroupAttributes", + "description": [], + "signature": [ + "Omit<", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.PersistableControlGroupInput", + "text": "PersistableControlGroupInput" + }, + ", \"ignoreParentSettings\" | \"panels\"> & { ignoreParentSettingsJSON: string; panelsJSON: string; }" + ], + "path": "src/plugins/controls/common/control_group/types.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "controls", "id": "def-common.TIME_SLIDER_CONTROL", diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 7a59f3a0a5120..870bc6876f2c6 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github summary: API docs for the controls plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-prese | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 188 | 0 | 182 | 4 | +| 203 | 0 | 197 | 6 | ## Client @@ -34,6 +34,11 @@ Contact [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-prese ### Consts, variables and types +## Server + +### Functions + + ## Common ### Functions diff --git a/api_docs/core.devdocs.json b/api_docs/core.devdocs.json index 7568da42bdc95..ae6443972aefb 100644 --- a/api_docs/core.devdocs.json +++ b/api_docs/core.devdocs.json @@ -651,7 +651,7 @@ "description": [ "\r\nAnalytics client's public APIs" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -666,7 +666,7 @@ "signature": [ ">(eventType: string, eventData: EventTypeData) => void" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -681,7 +681,7 @@ "signature": [ "string" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true }, @@ -697,7 +697,7 @@ "signature": [ "EventTypeData" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true } @@ -718,7 +718,7 @@ "EventTypeOpts", ") => void" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -732,7 +732,7 @@ "EventTypeOpts", "" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true } @@ -757,7 +757,7 @@ "RegisterShipperOpts", " | undefined) => void" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -773,7 +773,7 @@ "ShipperClassConstructor", "" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true }, @@ -789,7 +789,7 @@ "signature": [ "ShipperConfig" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true }, @@ -806,7 +806,7 @@ "RegisterShipperOpts", " | undefined" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": false } @@ -827,7 +827,7 @@ "OptInConfig", ") => void" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -842,7 +842,7 @@ "signature": [ "OptInConfig" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true } @@ -863,7 +863,7 @@ "ContextProviderOpts", ") => void" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -879,7 +879,7 @@ "ContextProviderOpts", "" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true } @@ -898,7 +898,7 @@ "signature": [ "(contextProviderName: string) => void" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -913,7 +913,7 @@ "signature": [ "string" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true } @@ -935,7 +935,7 @@ "TelemetryCounter", ">" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -1113,7 +1113,7 @@ "ContextProviderOpts", "" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -1125,7 +1125,7 @@ "description": [ "\r\nThe name of the provider." ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -1141,7 +1141,7 @@ "Observable", "" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -1158,7 +1158,7 @@ "SchemaValue", "; }" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -2075,7 +2075,7 @@ "signature": [ "Event" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -2087,7 +2087,7 @@ "description": [ "\r\nThe time the event was generated in ISO format." ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -2099,7 +2099,7 @@ "description": [ "\r\nThe event type." ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -2114,7 +2114,7 @@ "signature": [ "{ [x: string]: unknown; }" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -2129,7 +2129,7 @@ "signature": [ "EventContext" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -2141,10 +2141,102 @@ "type": "Interface", "tags": [], "label": "EventContext", - "description": [], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "description": [ + "\r\nDefinition of the context that can be appended to the events through the {@link IAnalyticsClient.registerContextProvider}." + ], + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ + { + "parentPluginId": "core", + "id": "def-public.EventContext.userId", + "type": "string", + "tags": [], + "label": "userId", + "description": [ + "\r\nThe unique user ID." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-public.EventContext.cloudId", + "type": "string", + "tags": [], + "label": "cloudId", + "description": [ + "\r\nThe Cloud ID." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-public.EventContext.version", + "type": "string", + "tags": [], + "label": "version", + "description": [ + "\r\nThe product's version." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-public.EventContext.pageName", + "type": "string", + "tags": [], + "label": "pageName", + "description": [ + "\r\nThe name of the current page." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-public.EventContext.applicationId", + "type": "string", + "tags": [], + "label": "applicationId", + "description": [ + "\r\nThe current application ID." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-public.EventContext.entityId", + "type": "string", + "tags": [], + "label": "entityId", + "description": [ + "\r\nThe current entity ID (dashboard ID, visualization ID, etc.)." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", + "deprecated": false + }, { "parentPluginId": "core", "id": "def-public.EventContext.Unnamed", @@ -2155,7 +2247,7 @@ "signature": [ "[key: string]: unknown" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -2174,7 +2266,7 @@ "EventTypeOpts", "" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -2186,7 +2278,7 @@ "description": [ "\r\nThe event type's unique name." ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -2203,7 +2295,7 @@ "SchemaValue", "; }" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -2619,7 +2711,7 @@ "description": [ "\r\nBasic structure of a Shipper" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -2636,7 +2728,7 @@ "Event", "[]) => void" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -2652,7 +2744,7 @@ "Event", "[]" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true } @@ -2671,7 +2763,7 @@ "signature": [ "(isOptedIn: boolean) => void" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -2686,7 +2778,7 @@ "signature": [ "boolean" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true } @@ -2707,7 +2799,7 @@ "EventContext", ") => void) | undefined" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -2720,7 +2812,7 @@ "signature": [ "EventContext" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true } @@ -2742,7 +2834,7 @@ "TelemetryCounter", "> | undefined" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -3479,7 +3571,7 @@ "description": [ "\r\n" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -3494,7 +3586,7 @@ "signature": [ "OptInConfigPerType" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -3511,7 +3603,7 @@ "OptInConfigPerType", " | undefined> | undefined" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -5391,6 +5483,21 @@ "path": "src/core/server/saved_objects/service/lib/collect_multi_namespace_references.ts", "deprecated": false }, + { + "parentPluginId": "core", + "id": "def-public.SavedObjectReferenceWithContext.originId", + "type": "string", + "tags": [], + "label": "originId", + "description": [ + "The origin ID of the referenced object (if it has one)" + ], + "signature": [ + "string | undefined" + ], + "path": "src/core/server/saved_objects/service/lib/collect_multi_namespace_references.ts", + "deprecated": false + }, { "parentPluginId": "core", "id": "def-public.SavedObjectReferenceWithContext.spaces", @@ -5450,6 +5557,21 @@ ], "path": "src/core/server/saved_objects/service/lib/collect_multi_namespace_references.ts", "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-public.SavedObjectReferenceWithContext.spacesWithMatchingOrigins", + "type": "Array", + "tags": [], + "label": "spacesWithMatchingOrigins", + "description": [ + "The space(s) that objects matching this origin exist in (including this one)" + ], + "signature": [ + "string[] | undefined" + ], + "path": "src/core/server/saved_objects/service/lib/collect_multi_namespace_references.ts", + "deprecated": false } ], "initialIsOpen": false @@ -6793,7 +6915,7 @@ "ShipperClassConstructor", "" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -6805,7 +6927,7 @@ "description": [ "\r\nThe shipper's unique name" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -6820,7 +6942,7 @@ "signature": [ "any" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -6835,7 +6957,7 @@ "description": [ "\r\nShape of the events emitted by the telemetryCounter$ observable" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -6850,7 +6972,7 @@ "signature": [ "TelemetryCounterType" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -6862,7 +6984,7 @@ "description": [ "\r\nWho emitted the event? It can be \"client\" or the name of the shipper." ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -6874,7 +6996,7 @@ "description": [ "\r\nThe event type the success/failure/drop event refers to." ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -6886,7 +7008,7 @@ "description": [ "\r\nCode to provide additional information about the success or failure. Examples are 200/400/504/ValidationError/UnknownError" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -6898,7 +7020,7 @@ "description": [ "\r\nThe number of events that this counter refers to." ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -7340,7 +7462,7 @@ "description": [ "\r\nTypes of the Telemetry Counter: It allows to differentiate what happened to the events" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "initialIsOpen": false } @@ -7412,7 +7534,7 @@ "signature": [ "string" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "initialIsOpen": false }, @@ -7903,7 +8025,7 @@ "signature": [ "Pick<", "Toast", - ", \"children\" | \"onClick\" | \"onChange\" | \"color\" | \"onKeyDown\" | \"className\" | \"security\" | \"defaultValue\" | \"hidden\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"style\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"role\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-label\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"toastLifeTimeMs\" | \"iconType\" | \"onClose\" | \"data-test-subj\"> & { title?: string | ", + ", \"children\" | \"color\" | \"className\" | \"onChange\" | \"onKeyDown\" | \"onClick\" | \"security\" | \"defaultValue\" | \"hidden\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"style\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"role\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-label\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"toastLifeTimeMs\" | \"iconType\" | \"onClose\" | \"data-test-subj\"> & { title?: string | ", { "pluginId": "core", "scope": "public", @@ -7960,7 +8082,7 @@ "signature": [ "Pick<", "Toast", - ", \"children\" | \"onClick\" | \"onChange\" | \"color\" | \"onKeyDown\" | \"className\" | \"security\" | \"defaultValue\" | \"hidden\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"style\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"role\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-label\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"toastLifeTimeMs\" | \"iconType\" | \"onClose\" | \"data-test-subj\"> & { title?: string | ", + ", \"children\" | \"color\" | \"className\" | \"onChange\" | \"onKeyDown\" | \"onClick\" | \"security\" | \"defaultValue\" | \"hidden\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"style\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"role\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-label\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"toastLifeTimeMs\" | \"iconType\" | \"onClose\" | \"data-test-subj\"> & { title?: string | ", { "pluginId": "core", "scope": "public", @@ -8750,7 +8872,7 @@ "label": "rawConfig", "description": [], "signature": [ - "Readonly<{ username?: string | undefined; password?: string | undefined; serviceAccountToken?: string | undefined; } & { ssl: Readonly<{ key?: string | undefined; certificateAuthorities?: string | string[] | undefined; certificate?: string | undefined; keyPassphrase?: string | undefined; } & { verificationMode: \"none\" | \"full\" | \"certificate\"; keystore: Readonly<{ path?: string | undefined; password?: string | undefined; } & {}>; truststore: Readonly<{ path?: string | undefined; password?: string | undefined; } & {}>; alwaysPresentCertificate: boolean; }>; healthCheck: Readonly<{} & { delay: moment.Duration; }>; hosts: string | string[]; sniffOnStart: boolean; sniffInterval: false | moment.Duration; sniffOnConnectionFault: boolean; maxSockets: number; compression: boolean; requestHeadersWhitelist: string | string[]; customHeaders: Record; shardTimeout: moment.Duration; requestTimeout: moment.Duration; pingTimeout: moment.Duration; logQueries: boolean; apiVersion: string; ignoreVersionMismatch: boolean; skipStartupConnectionCheck: boolean; }>" + "Readonly<{ username?: string | undefined; password?: string | undefined; serviceAccountToken?: string | undefined; } & { ssl: Readonly<{ key?: string | undefined; certificateAuthorities?: string | string[] | undefined; certificate?: string | undefined; keyPassphrase?: string | undefined; } & { verificationMode: \"none\" | \"full\" | \"certificate\"; keystore: Readonly<{ path?: string | undefined; password?: string | undefined; } & {}>; truststore: Readonly<{ path?: string | undefined; password?: string | undefined; } & {}>; alwaysPresentCertificate: boolean; }>; healthCheck: Readonly<{} & { delay: moment.Duration; }>; customHeaders: Record; hosts: string | string[]; sniffOnStart: boolean; sniffInterval: false | moment.Duration; sniffOnConnectionFault: boolean; maxSockets: number; compression: boolean; requestHeadersWhitelist: string | string[]; shardTimeout: moment.Duration; requestTimeout: moment.Duration; pingTimeout: moment.Duration; logQueries: boolean; apiVersion: string; ignoreVersionMismatch: boolean; skipStartupConnectionCheck: boolean; }>" ], "path": "src/core/server/elasticsearch/elasticsearch_config.ts", "deprecated": false, @@ -8918,7 +9040,7 @@ "description": [ "\r\nAnalytics client's public APIs" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -8933,7 +9055,7 @@ "signature": [ ">(eventType: string, eventData: EventTypeData) => void" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -8948,7 +9070,7 @@ "signature": [ "string" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true }, @@ -8964,7 +9086,7 @@ "signature": [ "EventTypeData" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true } @@ -8985,7 +9107,7 @@ "EventTypeOpts", ") => void" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -8999,7 +9121,7 @@ "EventTypeOpts", "" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true } @@ -9024,7 +9146,7 @@ "RegisterShipperOpts", " | undefined) => void" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -9040,7 +9162,7 @@ "ShipperClassConstructor", "" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true }, @@ -9056,7 +9178,7 @@ "signature": [ "ShipperConfig" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true }, @@ -9073,7 +9195,7 @@ "RegisterShipperOpts", " | undefined" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": false } @@ -9094,7 +9216,7 @@ "OptInConfig", ") => void" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -9109,7 +9231,7 @@ "signature": [ "OptInConfig" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true } @@ -9130,7 +9252,7 @@ "ContextProviderOpts", ") => void" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -9146,7 +9268,7 @@ "ContextProviderOpts", "" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true } @@ -9165,7 +9287,7 @@ "signature": [ "(contextProviderName: string) => void" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -9180,7 +9302,7 @@ "signature": [ "string" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true } @@ -9202,7 +9324,7 @@ "TelemetryCounter", ">" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -10300,7 +10422,7 @@ "ContextProviderOpts", "" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -10312,7 +10434,7 @@ "description": [ "\r\nThe name of the provider." ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -10328,7 +10450,7 @@ "Observable", "" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -10345,7 +10467,7 @@ "SchemaValue", "; }" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -10483,6 +10605,161 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "core", + "id": "def-server.CoreRequestHandlerContext", + "type": "Interface", + "tags": [], + "label": "CoreRequestHandlerContext", + "description": [ + "\nThe `core` context provided to route handler.\n\nProvides the following clients and services:\n - {@link SavedObjectsClient | savedObjects.client} - Saved Objects client\n which uses the credentials of the incoming request\n - {@link ISavedObjectTypeRegistry | savedObjects.typeRegistry} - Type registry containing\n all the registered types.\n - {@link IScopedClusterClient | elasticsearch.client} - Elasticsearch\n data client which uses the credentials of the incoming request\n - {@link IUiSettingsClient | uiSettings.client} - uiSettings client\n which uses the credentials of the incoming request" + ], + "path": "src/core/server/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.CoreRequestHandlerContext.savedObjects", + "type": "Object", + "tags": [], + "label": "savedObjects", + "description": [], + "signature": [ + "{ client: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + }, + "; typeRegistry: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.ISavedObjectTypeRegistry", + "text": "ISavedObjectTypeRegistry" + }, + "; getClient: (options?: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsClientProviderOptions", + "text": "SavedObjectsClientProviderOptions" + }, + " | undefined) => ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + }, + "; getExporter: (client: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + }, + ") => ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.ISavedObjectsExporter", + "text": "ISavedObjectsExporter" + }, + "; getImporter: (client: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + }, + ") => ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.ISavedObjectsImporter", + "text": "ISavedObjectsImporter" + }, + "; }" + ], + "path": "src/core/server/index.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-server.CoreRequestHandlerContext.elasticsearch", + "type": "Object", + "tags": [], + "label": "elasticsearch", + "description": [], + "signature": [ + "{ client: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.IScopedClusterClient", + "text": "IScopedClusterClient" + }, + "; }" + ], + "path": "src/core/server/index.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-server.CoreRequestHandlerContext.uiSettings", + "type": "Object", + "tags": [], + "label": "uiSettings", + "description": [], + "signature": [ + "{ client: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.IUiSettingsClient", + "text": "IUiSettingsClient" + }, + "; }" + ], + "path": "src/core/server/index.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-server.CoreRequestHandlerContext.deprecations", + "type": "Object", + "tags": [], + "label": "deprecations", + "description": [], + "signature": [ + "{ client: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.DeprecationsClient", + "text": "DeprecationsClient" + }, + "; }" + ], + "path": "src/core/server/index.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "core", "id": "def-server.CoreSetup", @@ -11470,6 +11747,21 @@ ], "path": "src/core/server/plugins/types.ts", "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-server.DiscoveredPlugin.enabledOnAnonymousPages", + "type": "CompoundType", + "tags": [], + "label": "enabledOnAnonymousPages", + "description": [ + "\nSpecifies whether this plugin - and its required dependencies - will be enabled for anonymous pages (login page, status page when\nconfigured, etc.) Default is false." + ], + "signature": [ + "boolean | undefined" + ], + "path": "src/core/server/plugins/types.ts", + "deprecated": false } ], "initialIsOpen": false @@ -12002,7 +12294,7 @@ "signature": [ "Event" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -12014,7 +12306,7 @@ "description": [ "\r\nThe time the event was generated in ISO format." ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -12026,7 +12318,7 @@ "description": [ "\r\nThe event type." ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -12041,7 +12333,7 @@ "signature": [ "{ [x: string]: unknown; }" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -12056,7 +12348,7 @@ "signature": [ "EventContext" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -12068,10 +12360,102 @@ "type": "Interface", "tags": [], "label": "EventContext", - "description": [], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "description": [ + "\r\nDefinition of the context that can be appended to the events through the {@link IAnalyticsClient.registerContextProvider}." + ], + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ + { + "parentPluginId": "core", + "id": "def-server.EventContext.userId", + "type": "string", + "tags": [], + "label": "userId", + "description": [ + "\r\nThe unique user ID." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-server.EventContext.cloudId", + "type": "string", + "tags": [], + "label": "cloudId", + "description": [ + "\r\nThe Cloud ID." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-server.EventContext.version", + "type": "string", + "tags": [], + "label": "version", + "description": [ + "\r\nThe product's version." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-server.EventContext.pageName", + "type": "string", + "tags": [], + "label": "pageName", + "description": [ + "\r\nThe name of the current page." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-server.EventContext.applicationId", + "type": "string", + "tags": [], + "label": "applicationId", + "description": [ + "\r\nThe current application ID." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-server.EventContext.entityId", + "type": "string", + "tags": [], + "label": "entityId", + "description": [ + "\r\nThe current entity ID (dashboard ID, visualization ID, etc.)." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", + "deprecated": false + }, { "parentPluginId": "core", "id": "def-server.EventContext.Unnamed", @@ -12082,7 +12466,7 @@ "signature": [ "[key: string]: unknown" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -12101,7 +12485,7 @@ "EventTypeOpts", "" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -12113,7 +12497,7 @@ "description": [ "\r\nThe event type's unique name." ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -12130,7 +12514,7 @@ "SchemaValue", "; }" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -12236,7 +12620,7 @@ "Headers used for authentication against Elasticsearch" ], "signature": [ - "{ from?: string | string[] | undefined; date?: string | string[] | undefined; range?: string | string[] | undefined; warning?: string | string[] | undefined; location?: string | string[] | undefined; origin?: string | string[] | undefined; allow?: string | string[] | undefined; accept?: string | string[] | undefined; host?: string | string[] | undefined; \"accept-language\"?: string | string[] | undefined; \"accept-patch\"?: string | string[] | undefined; \"accept-ranges\"?: string | string[] | undefined; \"access-control-allow-credentials\"?: string | string[] | undefined; \"access-control-allow-headers\"?: string | string[] | undefined; \"access-control-allow-methods\"?: string | string[] | undefined; \"access-control-allow-origin\"?: string | string[] | undefined; \"access-control-expose-headers\"?: string | string[] | undefined; \"access-control-max-age\"?: string | string[] | undefined; \"access-control-request-headers\"?: string | string[] | undefined; \"access-control-request-method\"?: string | string[] | undefined; age?: string | string[] | undefined; \"alt-svc\"?: string | string[] | undefined; authorization?: string | string[] | undefined; \"cache-control\"?: string | string[] | undefined; connection?: string | string[] | undefined; \"content-disposition\"?: string | string[] | undefined; \"content-encoding\"?: string | string[] | undefined; \"content-language\"?: string | string[] | undefined; \"content-length\"?: string | string[] | undefined; \"content-location\"?: string | string[] | undefined; \"content-range\"?: string | string[] | undefined; \"content-type\"?: string | string[] | undefined; cookie?: string | string[] | undefined; etag?: string | string[] | undefined; expect?: string | string[] | undefined; expires?: string | string[] | undefined; forwarded?: string | string[] | undefined; \"if-match\"?: string | string[] | undefined; \"if-modified-since\"?: string | string[] | undefined; \"if-none-match\"?: string | string[] | undefined; \"if-unmodified-since\"?: string | string[] | undefined; \"last-modified\"?: string | string[] | undefined; pragma?: string | string[] | undefined; \"proxy-authenticate\"?: string | string[] | undefined; \"proxy-authorization\"?: string | string[] | undefined; \"public-key-pins\"?: string | string[] | undefined; referer?: string | string[] | undefined; \"retry-after\"?: string | string[] | undefined; \"sec-websocket-accept\"?: string | string[] | undefined; \"sec-websocket-extensions\"?: string | string[] | undefined; \"sec-websocket-key\"?: string | string[] | undefined; \"sec-websocket-protocol\"?: string | string[] | undefined; \"sec-websocket-version\"?: string | string[] | undefined; \"set-cookie\"?: string | string[] | undefined; \"strict-transport-security\"?: string | string[] | undefined; tk?: string | string[] | undefined; trailer?: string | string[] | undefined; \"transfer-encoding\"?: string | string[] | undefined; upgrade?: string | string[] | undefined; \"user-agent\"?: string | string[] | undefined; vary?: string | string[] | undefined; via?: string | string[] | undefined; \"www-authenticate\"?: string | string[] | undefined; } & { [header: string]: string | string[] | undefined; }" + "{ warning?: string | string[] | undefined; from?: string | string[] | undefined; date?: string | string[] | undefined; range?: string | string[] | undefined; location?: string | string[] | undefined; origin?: string | string[] | undefined; allow?: string | string[] | undefined; accept?: string | string[] | undefined; host?: string | string[] | undefined; \"accept-language\"?: string | string[] | undefined; \"accept-patch\"?: string | string[] | undefined; \"accept-ranges\"?: string | string[] | undefined; \"access-control-allow-credentials\"?: string | string[] | undefined; \"access-control-allow-headers\"?: string | string[] | undefined; \"access-control-allow-methods\"?: string | string[] | undefined; \"access-control-allow-origin\"?: string | string[] | undefined; \"access-control-expose-headers\"?: string | string[] | undefined; \"access-control-max-age\"?: string | string[] | undefined; \"access-control-request-headers\"?: string | string[] | undefined; \"access-control-request-method\"?: string | string[] | undefined; age?: string | string[] | undefined; \"alt-svc\"?: string | string[] | undefined; authorization?: string | string[] | undefined; \"cache-control\"?: string | string[] | undefined; connection?: string | string[] | undefined; \"content-disposition\"?: string | string[] | undefined; \"content-encoding\"?: string | string[] | undefined; \"content-language\"?: string | string[] | undefined; \"content-length\"?: string | string[] | undefined; \"content-location\"?: string | string[] | undefined; \"content-range\"?: string | string[] | undefined; \"content-type\"?: string | string[] | undefined; cookie?: string | string[] | undefined; etag?: string | string[] | undefined; expect?: string | string[] | undefined; expires?: string | string[] | undefined; forwarded?: string | string[] | undefined; \"if-match\"?: string | string[] | undefined; \"if-modified-since\"?: string | string[] | undefined; \"if-none-match\"?: string | string[] | undefined; \"if-unmodified-since\"?: string | string[] | undefined; \"last-modified\"?: string | string[] | undefined; pragma?: string | string[] | undefined; \"proxy-authenticate\"?: string | string[] | undefined; \"proxy-authorization\"?: string | string[] | undefined; \"public-key-pins\"?: string | string[] | undefined; referer?: string | string[] | undefined; \"retry-after\"?: string | string[] | undefined; \"sec-websocket-accept\"?: string | string[] | undefined; \"sec-websocket-extensions\"?: string | string[] | undefined; \"sec-websocket-key\"?: string | string[] | undefined; \"sec-websocket-protocol\"?: string | string[] | undefined; \"sec-websocket-version\"?: string | string[] | undefined; \"set-cookie\"?: string | string[] | undefined; \"strict-transport-security\"?: string | string[] | undefined; tk?: string | string[] | undefined; trailer?: string | string[] | undefined; \"transfer-encoding\"?: string | string[] | undefined; upgrade?: string | string[] | undefined; \"user-agent\"?: string | string[] | undefined; vary?: string | string[] | undefined; via?: string | string[] | undefined; \"www-authenticate\"?: string | string[] | undefined; } & { [header: string]: string | string[] | undefined; }" ], "path": "src/core/server/elasticsearch/types.ts", "deprecated": false @@ -14725,6 +15109,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -14915,6 +15303,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -15151,6 +15543,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -15582,12 +15978,12 @@ "children": [ { "parentPluginId": "core", - "id": "def-server.IRenderOptions.includeUserSettings", + "id": "def-server.IRenderOptions.isAnonymousPage", "type": "CompoundType", "tags": [], - "label": "includeUserSettings", + "label": "isAnonymousPage", "description": [ - "\nSet whether to output user settings in the page metadata.\n`true` by default." + "\nSet whether the page is anonymous, which determines what plugins are enabled and whether to output user settings in the page metadata.\n`false` by default." ], "signature": [ "boolean | undefined" @@ -18028,7 +18424,7 @@ "description": [ "\r\nBasic structure of a Shipper" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -18045,7 +18441,7 @@ "Event", "[]) => void" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -18061,7 +18457,7 @@ "Event", "[]" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true } @@ -18080,7 +18476,7 @@ "signature": [ "(isOptedIn: boolean) => void" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -18095,7 +18491,7 @@ "signature": [ "boolean" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true } @@ -18116,7 +18512,7 @@ "EventContext", ") => void) | undefined" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -18129,7 +18525,7 @@ "signature": [ "EventContext" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "isRequired": true } @@ -18151,7 +18547,7 @@ "TelemetryCounter", "> | undefined" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -18884,7 +19280,7 @@ "label": "loggers", "description": [], "signature": [ - "Readonly<{} & { name: string; level: \"all\" | \"error\" | \"info\" | \"off\" | \"debug\" | \"trace\" | \"warn\" | \"fatal\"; appenders: string[]; }>[] | undefined" + "Readonly<{} & { name: string; level: \"error\" | \"all\" | \"info\" | \"off\" | \"debug\" | \"trace\" | \"warn\" | \"fatal\"; appenders: string[]; }>[] | undefined" ], "path": "src/core/server/logging/logging_config.ts", "deprecated": false @@ -19593,7 +19989,7 @@ "description": [ "\r\n" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -19608,7 +20004,7 @@ "signature": [ "OptInConfigPerType" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -19625,7 +20021,7 @@ "OptInConfigPerType", " | undefined> | undefined" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -20266,6 +20662,21 @@ ], "path": "src/core/server/plugins/types.ts", "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-server.PluginManifest.enabledOnAnonymousPages", + "type": "CompoundType", + "tags": [], + "label": "enabledOnAnonymousPages", + "description": [ + "\nSpecifies whether this plugin - and its required dependencies - will be enabled for anonymous pages (login page, status page when\nconfigured, etc.) Default is false." + ], + "signature": [ + "boolean | undefined" + ], + "path": "src/core/server/plugins/types.ts", + "deprecated": false } ], "initialIsOpen": false @@ -21781,7 +22192,24 @@ "tags": [], "label": "RequestHandlerContext", "description": [ - "\nPlugin specific context passed to a route handler.\n\nProvides the following clients and services:\n - {@link SavedObjectsClient | savedObjects.client} - Saved Objects client\n which uses the credentials of the incoming request\n - {@link ISavedObjectTypeRegistry | savedObjects.typeRegistry} - Type registry containing\n all the registered types.\n - {@link IScopedClusterClient | elasticsearch.client} - Elasticsearch\n data client which uses the credentials of the incoming request\n - {@link IUiSettingsClient | uiSettings.client} - uiSettings client\n which uses the credentials of the incoming request\n" + "\nBase context passed to a route handler.\n" + ], + "signature": [ + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " extends ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.RequestHandlerContextBase", + "text": "RequestHandlerContextBase" + } ], "path": "src/core/server/index.ts", "deprecated": false, @@ -21794,95 +22222,15 @@ "label": "core", "description": [], "signature": [ - "{ savedObjects: { client: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCoreSavedObjectsPluginApi", - "section": "def-server.SavedObjectsClientContract", - "text": "SavedObjectsClientContract" - }, - "; typeRegistry: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCoreSavedObjectsPluginApi", - "section": "def-server.ISavedObjectTypeRegistry", - "text": "ISavedObjectTypeRegistry" - }, - "; getClient: (options?: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCoreSavedObjectsPluginApi", - "section": "def-server.SavedObjectsClientProviderOptions", - "text": "SavedObjectsClientProviderOptions" - }, - " | undefined) => ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCoreSavedObjectsPluginApi", - "section": "def-server.SavedObjectsClientContract", - "text": "SavedObjectsClientContract" - }, - "; getExporter: (client: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCoreSavedObjectsPluginApi", - "section": "def-server.SavedObjectsClientContract", - "text": "SavedObjectsClientContract" - }, - ") => ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCoreSavedObjectsPluginApi", - "section": "def-server.ISavedObjectsExporter", - "text": "ISavedObjectsExporter" - }, - "; getImporter: (client: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCoreSavedObjectsPluginApi", - "section": "def-server.SavedObjectsClientContract", - "text": "SavedObjectsClientContract" - }, - ") => ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCoreSavedObjectsPluginApi", - "section": "def-server.ISavedObjectsImporter", - "text": "ISavedObjectsImporter" - }, - "; }; elasticsearch: { client: ", + "Promise<", { "pluginId": "core", "scope": "server", "docId": "kibCorePluginApi", - "section": "def-server.IScopedClusterClient", - "text": "IScopedClusterClient" - }, - "; }; uiSettings: { client: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.IUiSettingsClient", - "text": "IUiSettingsClient" - }, - "; }; deprecations: { client: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.DeprecationsClient", - "text": "DeprecationsClient" + "section": "def-server.CoreRequestHandlerContext", + "text": "CoreRequestHandlerContext" }, - "; }; }" + ">" ], "path": "src/core/server/index.ts", "deprecated": false @@ -21890,6 +22238,53 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "core", + "id": "def-server.RequestHandlerContextBase", + "type": "Interface", + "tags": [], + "label": "RequestHandlerContextBase", + "description": [], + "path": "src/core/server/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.RequestHandlerContextBase.resolve", + "type": "Function", + "tags": [], + "label": "resolve", + "description": [ + "\nAwait all the specified context parts and return them.\n" + ], + "signature": [ + ">(parts: T[]) => Promise<", + "AwaitedProperties", + ">>" + ], + "path": "src/core/server/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.RequestHandlerContextBase.resolve.$1", + "type": "Array", + "tags": [], + "label": "parts", + "description": [], + "signature": [ + "T[]" + ], + "path": "src/core/server/index.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "core", "id": "def-server.ResolveCapabilitiesOptions", @@ -22530,7 +22925,7 @@ "ShipperClassConstructor", "" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -22542,7 +22937,7 @@ "description": [ "\r\nThe shipper's unique name" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -22557,7 +22952,7 @@ "signature": [ "any" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -22751,7 +23146,7 @@ "description": [ "\r\nShape of the events emitted by the telemetryCounter$ observable" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "children": [ { @@ -22766,7 +23161,7 @@ "signature": [ "TelemetryCounterType" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -22778,7 +23173,7 @@ "description": [ "\r\nWho emitted the event? It can be \"client\" or the name of the shipper." ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -22790,7 +23185,7 @@ "description": [ "\r\nThe event type the success/failure/drop event refers to." ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -22802,7 +23197,7 @@ "description": [ "\r\nCode to provide additional information about the success or failure. Examples are 200/400/504/ValidationError/UnknownError" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false }, { @@ -22814,7 +23209,7 @@ "description": [ "\r\nThe number of events that this counter refers to." ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false } ], @@ -23486,7 +23881,7 @@ "description": [ "\r\nTypes of the Telemetry Counter: It allows to differentiate what happened to the events" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "initialIsOpen": false } @@ -23851,6 +24246,27 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "core", + "id": "def-server.CustomRequestHandlerContext", + "type": "Type", + "tags": [], + "label": "CustomRequestHandlerContext", + "description": [], + "signature": [ + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " & { [Key in keyof T]: T[Key] extends Promise ? T[Key] : Promise; }" + ], + "path": "src/core/server/index.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "core", "id": "def-server.DeprecationsDetails", @@ -23951,7 +24367,7 @@ "label": "EcsEventOutcome", "description": [], "signature": [ - "\"unknown\" | \"success\" | \"failure\"" + "\"success\" | \"unknown\" | \"failure\"" ], "path": "node_modules/@types/kbn__logging/index.d.ts", "deprecated": false, @@ -23965,7 +24381,7 @@ "label": "EcsEventType", "description": [], "signature": [ - "\"start\" | \"user\" | \"error\" | \"info\" | \"group\" | \"end\" | \"protocol\" | \"connection\" | \"access\" | \"admin\" | \"allowed\" | \"change\" | \"creation\" | \"deletion\" | \"denied\" | \"installation\"" + "\"start\" | \"error\" | \"user\" | \"info\" | \"group\" | \"end\" | \"admin\" | \"protocol\" | \"connection\" | \"access\" | \"allowed\" | \"change\" | \"creation\" | \"deletion\" | \"denied\" | \"installation\"" ], "path": "node_modules/@types/kbn__logging/index.d.ts", "deprecated": false, @@ -25193,7 +25609,7 @@ "section": "def-server.ElasticsearchConfig", "text": "ElasticsearchConfig" }, - ", \"username\" | \"hosts\" | \"password\" | \"sniffOnStart\" | \"sniffInterval\" | \"sniffOnConnectionFault\" | \"maxSockets\" | \"compression\" | \"serviceAccountToken\" | \"requestHeadersWhitelist\" | \"customHeaders\"> & { pingTimeout?: number | moment.Duration | undefined; requestTimeout?: number | moment.Duration | undefined; ssl?: Partial; truststore: Readonly<{ path?: string | undefined; password?: string | undefined; } & {}>; alwaysPresentCertificate: boolean; }>, \"key\" | \"verificationMode\" | \"certificate\" | \"keyPassphrase\" | \"alwaysPresentCertificate\"> & { certificateAuthorities?: string[] | undefined; }> | undefined; keepAlive?: boolean | undefined; caFingerprint?: string | undefined; }" + ", \"username\" | \"customHeaders\" | \"hosts\" | \"password\" | \"sniffOnStart\" | \"sniffInterval\" | \"sniffOnConnectionFault\" | \"maxSockets\" | \"compression\" | \"serviceAccountToken\" | \"requestHeadersWhitelist\"> & { pingTimeout?: number | moment.Duration | undefined; requestTimeout?: number | moment.Duration | undefined; ssl?: Partial; truststore: Readonly<{ path?: string | undefined; password?: string | undefined; } & {}>; alwaysPresentCertificate: boolean; }>, \"key\" | \"verificationMode\" | \"certificate\" | \"keyPassphrase\" | \"alwaysPresentCertificate\"> & { certificateAuthorities?: string[] | undefined; }> | undefined; keepAlive?: boolean | undefined; caFingerprint?: string | undefined; }" ], "path": "src/core/server/elasticsearch/client/client_config.ts", "deprecated": false, @@ -25211,7 +25627,7 @@ "signature": [ "string" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "initialIsOpen": false }, @@ -25488,6 +25904,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -25795,6 +26215,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -25851,7 +26275,9 @@ }, ") => ", "KibanaResponse", - "; }) => Context[ContextName] | Promise" + "; }) => ", + "MaybePromise", + ">" ], "path": "src/core/server/context/container/context.ts", "deprecated": false, @@ -26018,6 +26444,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -26106,7 +26536,7 @@ "label": "LoggerConfigType", "description": [], "signature": [ - "{ readonly name: string; readonly level: \"all\" | \"error\" | \"info\" | \"off\" | \"debug\" | \"trace\" | \"warn\" | \"fatal\"; readonly appenders: string[]; }" + "{ readonly name: string; readonly level: \"error\" | \"all\" | \"info\" | \"off\" | \"debug\" | \"trace\" | \"warn\" | \"fatal\"; readonly appenders: string[]; }" ], "path": "src/core/server/logging/logging_config.ts", "deprecated": false, diff --git a/api_docs/core.mdx b/api_docs/core.mdx index 0290b6009ea42..765f93b5bb6f9 100644 --- a/api_docs/core.mdx +++ b/api_docs/core.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core title: "core" image: https://source.unsplash.com/400x175/?github summary: API docs for the core plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2497 | 15 | 971 | 33 | +| 2524 | 15 | 977 | 33 | ## Client diff --git a/api_docs/core_application.devdocs.json b/api_docs/core_application.devdocs.json index cc3cb0a2d1dd2..1f59b41949a3a 100644 --- a/api_docs/core_application.devdocs.json +++ b/api_docs/core_application.devdocs.json @@ -510,7 +510,7 @@ "tags": [], "label": "id", "description": [ - "\nThe unique identifier of the application" + "\nThe unique identifier of the application.\n\nCan only be composed of alphanumeric characters, `-`, `:` and `_`" ], "path": "src/core/public/application/types.ts", "deprecated": false @@ -1210,7 +1210,9 @@ "type": "Object", "tags": [], "label": "options", - "description": [], + "description": [ + "- navigation options" + ], "signature": [ { "pluginId": "core", diff --git a/api_docs/core_application.mdx b/api_docs/core_application.mdx index ad976a37aa5db..680c5de7566c6 100644 --- a/api_docs/core_application.mdx +++ b/api_docs/core_application.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core-application title: "core.application" image: https://source.unsplash.com/400x175/?github summary: API docs for the core.application plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.application'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2497 | 15 | 971 | 33 | +| 2524 | 15 | 977 | 33 | ## Client diff --git a/api_docs/core_chrome.mdx b/api_docs/core_chrome.mdx index 09e098a7bf1ba..43eda7a95eaf8 100644 --- a/api_docs/core_chrome.mdx +++ b/api_docs/core_chrome.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core-chrome title: "core.chrome" image: https://source.unsplash.com/400x175/?github summary: API docs for the core.chrome plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.chrome'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2497 | 15 | 971 | 33 | +| 2524 | 15 | 977 | 33 | ## Client diff --git a/api_docs/core_http.devdocs.json b/api_docs/core_http.devdocs.json index 1f99d8f6e9bc1..ea42549b15bf9 100644 --- a/api_docs/core_http.devdocs.json +++ b/api_docs/core_http.devdocs.json @@ -2345,7 +2345,7 @@ "\nReadonly copy of incoming request headers." ], "signature": [ - "{ from?: string | string[] | undefined; date?: string | string[] | undefined; range?: string | string[] | undefined; warning?: string | string[] | undefined; location?: string | string[] | undefined; origin?: string | string[] | undefined; allow?: string | string[] | undefined; accept?: string | string[] | undefined; host?: string | string[] | undefined; \"accept-language\"?: string | string[] | undefined; \"accept-patch\"?: string | string[] | undefined; \"accept-ranges\"?: string | string[] | undefined; \"access-control-allow-credentials\"?: string | string[] | undefined; \"access-control-allow-headers\"?: string | string[] | undefined; \"access-control-allow-methods\"?: string | string[] | undefined; \"access-control-allow-origin\"?: string | string[] | undefined; \"access-control-expose-headers\"?: string | string[] | undefined; \"access-control-max-age\"?: string | string[] | undefined; \"access-control-request-headers\"?: string | string[] | undefined; \"access-control-request-method\"?: string | string[] | undefined; age?: string | string[] | undefined; \"alt-svc\"?: string | string[] | undefined; authorization?: string | string[] | undefined; \"cache-control\"?: string | string[] | undefined; connection?: string | string[] | undefined; \"content-disposition\"?: string | string[] | undefined; \"content-encoding\"?: string | string[] | undefined; \"content-language\"?: string | string[] | undefined; \"content-length\"?: string | string[] | undefined; \"content-location\"?: string | string[] | undefined; \"content-range\"?: string | string[] | undefined; \"content-type\"?: string | string[] | undefined; cookie?: string | string[] | undefined; etag?: string | string[] | undefined; expect?: string | string[] | undefined; expires?: string | string[] | undefined; forwarded?: string | string[] | undefined; \"if-match\"?: string | string[] | undefined; \"if-modified-since\"?: string | string[] | undefined; \"if-none-match\"?: string | string[] | undefined; \"if-unmodified-since\"?: string | string[] | undefined; \"last-modified\"?: string | string[] | undefined; pragma?: string | string[] | undefined; \"proxy-authenticate\"?: string | string[] | undefined; \"proxy-authorization\"?: string | string[] | undefined; \"public-key-pins\"?: string | string[] | undefined; referer?: string | string[] | undefined; \"retry-after\"?: string | string[] | undefined; \"sec-websocket-accept\"?: string | string[] | undefined; \"sec-websocket-extensions\"?: string | string[] | undefined; \"sec-websocket-key\"?: string | string[] | undefined; \"sec-websocket-protocol\"?: string | string[] | undefined; \"sec-websocket-version\"?: string | string[] | undefined; \"set-cookie\"?: string | string[] | undefined; \"strict-transport-security\"?: string | string[] | undefined; tk?: string | string[] | undefined; trailer?: string | string[] | undefined; \"transfer-encoding\"?: string | string[] | undefined; upgrade?: string | string[] | undefined; \"user-agent\"?: string | string[] | undefined; vary?: string | string[] | undefined; via?: string | string[] | undefined; \"www-authenticate\"?: string | string[] | undefined; } & { [header: string]: string | string[] | undefined; }" + "{ warning?: string | string[] | undefined; from?: string | string[] | undefined; date?: string | string[] | undefined; range?: string | string[] | undefined; location?: string | string[] | undefined; origin?: string | string[] | undefined; allow?: string | string[] | undefined; accept?: string | string[] | undefined; host?: string | string[] | undefined; \"accept-language\"?: string | string[] | undefined; \"accept-patch\"?: string | string[] | undefined; \"accept-ranges\"?: string | string[] | undefined; \"access-control-allow-credentials\"?: string | string[] | undefined; \"access-control-allow-headers\"?: string | string[] | undefined; \"access-control-allow-methods\"?: string | string[] | undefined; \"access-control-allow-origin\"?: string | string[] | undefined; \"access-control-expose-headers\"?: string | string[] | undefined; \"access-control-max-age\"?: string | string[] | undefined; \"access-control-request-headers\"?: string | string[] | undefined; \"access-control-request-method\"?: string | string[] | undefined; age?: string | string[] | undefined; \"alt-svc\"?: string | string[] | undefined; authorization?: string | string[] | undefined; \"cache-control\"?: string | string[] | undefined; connection?: string | string[] | undefined; \"content-disposition\"?: string | string[] | undefined; \"content-encoding\"?: string | string[] | undefined; \"content-language\"?: string | string[] | undefined; \"content-length\"?: string | string[] | undefined; \"content-location\"?: string | string[] | undefined; \"content-range\"?: string | string[] | undefined; \"content-type\"?: string | string[] | undefined; cookie?: string | string[] | undefined; etag?: string | string[] | undefined; expect?: string | string[] | undefined; expires?: string | string[] | undefined; forwarded?: string | string[] | undefined; \"if-match\"?: string | string[] | undefined; \"if-modified-since\"?: string | string[] | undefined; \"if-none-match\"?: string | string[] | undefined; \"if-unmodified-since\"?: string | string[] | undefined; \"last-modified\"?: string | string[] | undefined; pragma?: string | string[] | undefined; \"proxy-authenticate\"?: string | string[] | undefined; \"proxy-authorization\"?: string | string[] | undefined; \"public-key-pins\"?: string | string[] | undefined; referer?: string | string[] | undefined; \"retry-after\"?: string | string[] | undefined; \"sec-websocket-accept\"?: string | string[] | undefined; \"sec-websocket-extensions\"?: string | string[] | undefined; \"sec-websocket-key\"?: string | string[] | undefined; \"sec-websocket-protocol\"?: string | string[] | undefined; \"sec-websocket-version\"?: string | string[] | undefined; \"set-cookie\"?: string | string[] | undefined; \"strict-transport-security\"?: string | string[] | undefined; tk?: string | string[] | undefined; trailer?: string | string[] | undefined; \"transfer-encoding\"?: string | string[] | undefined; upgrade?: string | string[] | undefined; \"user-agent\"?: string | string[] | undefined; vary?: string | string[] | undefined; via?: string | string[] | undefined; \"www-authenticate\"?: string | string[] | undefined; } & { [header: string]: string | string[] | undefined; }" ], "path": "src/core/server/http/router/request.ts", "deprecated": false @@ -3946,7 +3946,7 @@ "section": "def-server.RequestHandlerContext", "text": "RequestHandlerContext" }, - ", ContextName extends keyof Context>(contextName: ContextName, provider: ", + ", ContextName extends Exclude>(contextName: ContextName, provider: ", { "pluginId": "core", "scope": "server", @@ -4613,6 +4613,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -4841,6 +4845,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -5121,6 +5129,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -5349,6 +5361,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -5629,6 +5645,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -5857,6 +5877,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -6137,6 +6161,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -6365,6 +6393,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -6645,6 +6677,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -6873,6 +6909,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -7161,6 +7201,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -7343,6 +7387,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -8950,6 +8998,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -9152,6 +9204,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -9397,7 +9453,7 @@ "\nHttp request headers to read." ], "signature": [ - "{ from?: string | string[] | undefined; date?: string | string[] | undefined; range?: string | string[] | undefined; warning?: string | string[] | undefined; location?: string | string[] | undefined; origin?: string | string[] | undefined; allow?: string | string[] | undefined; accept?: string | string[] | undefined; host?: string | string[] | undefined; \"accept-language\"?: string | string[] | undefined; \"accept-patch\"?: string | string[] | undefined; \"accept-ranges\"?: string | string[] | undefined; \"access-control-allow-credentials\"?: string | string[] | undefined; \"access-control-allow-headers\"?: string | string[] | undefined; \"access-control-allow-methods\"?: string | string[] | undefined; \"access-control-allow-origin\"?: string | string[] | undefined; \"access-control-expose-headers\"?: string | string[] | undefined; \"access-control-max-age\"?: string | string[] | undefined; \"access-control-request-headers\"?: string | string[] | undefined; \"access-control-request-method\"?: string | string[] | undefined; age?: string | string[] | undefined; \"alt-svc\"?: string | string[] | undefined; authorization?: string | string[] | undefined; \"cache-control\"?: string | string[] | undefined; connection?: string | string[] | undefined; \"content-disposition\"?: string | string[] | undefined; \"content-encoding\"?: string | string[] | undefined; \"content-language\"?: string | string[] | undefined; \"content-length\"?: string | string[] | undefined; \"content-location\"?: string | string[] | undefined; \"content-range\"?: string | string[] | undefined; \"content-type\"?: string | string[] | undefined; cookie?: string | string[] | undefined; etag?: string | string[] | undefined; expect?: string | string[] | undefined; expires?: string | string[] | undefined; forwarded?: string | string[] | undefined; \"if-match\"?: string | string[] | undefined; \"if-modified-since\"?: string | string[] | undefined; \"if-none-match\"?: string | string[] | undefined; \"if-unmodified-since\"?: string | string[] | undefined; \"last-modified\"?: string | string[] | undefined; pragma?: string | string[] | undefined; \"proxy-authenticate\"?: string | string[] | undefined; \"proxy-authorization\"?: string | string[] | undefined; \"public-key-pins\"?: string | string[] | undefined; referer?: string | string[] | undefined; \"retry-after\"?: string | string[] | undefined; \"sec-websocket-accept\"?: string | string[] | undefined; \"sec-websocket-extensions\"?: string | string[] | undefined; \"sec-websocket-key\"?: string | string[] | undefined; \"sec-websocket-protocol\"?: string | string[] | undefined; \"sec-websocket-version\"?: string | string[] | undefined; \"set-cookie\"?: string | string[] | undefined; \"strict-transport-security\"?: string | string[] | undefined; tk?: string | string[] | undefined; trailer?: string | string[] | undefined; \"transfer-encoding\"?: string | string[] | undefined; upgrade?: string | string[] | undefined; \"user-agent\"?: string | string[] | undefined; vary?: string | string[] | undefined; via?: string | string[] | undefined; \"www-authenticate\"?: string | string[] | undefined; } & { [header: string]: string | string[] | undefined; }" + "{ warning?: string | string[] | undefined; from?: string | string[] | undefined; date?: string | string[] | undefined; range?: string | string[] | undefined; location?: string | string[] | undefined; origin?: string | string[] | undefined; allow?: string | string[] | undefined; accept?: string | string[] | undefined; host?: string | string[] | undefined; \"accept-language\"?: string | string[] | undefined; \"accept-patch\"?: string | string[] | undefined; \"accept-ranges\"?: string | string[] | undefined; \"access-control-allow-credentials\"?: string | string[] | undefined; \"access-control-allow-headers\"?: string | string[] | undefined; \"access-control-allow-methods\"?: string | string[] | undefined; \"access-control-allow-origin\"?: string | string[] | undefined; \"access-control-expose-headers\"?: string | string[] | undefined; \"access-control-max-age\"?: string | string[] | undefined; \"access-control-request-headers\"?: string | string[] | undefined; \"access-control-request-method\"?: string | string[] | undefined; age?: string | string[] | undefined; \"alt-svc\"?: string | string[] | undefined; authorization?: string | string[] | undefined; \"cache-control\"?: string | string[] | undefined; connection?: string | string[] | undefined; \"content-disposition\"?: string | string[] | undefined; \"content-encoding\"?: string | string[] | undefined; \"content-language\"?: string | string[] | undefined; \"content-length\"?: string | string[] | undefined; \"content-location\"?: string | string[] | undefined; \"content-range\"?: string | string[] | undefined; \"content-type\"?: string | string[] | undefined; cookie?: string | string[] | undefined; etag?: string | string[] | undefined; expect?: string | string[] | undefined; expires?: string | string[] | undefined; forwarded?: string | string[] | undefined; \"if-match\"?: string | string[] | undefined; \"if-modified-since\"?: string | string[] | undefined; \"if-none-match\"?: string | string[] | undefined; \"if-unmodified-since\"?: string | string[] | undefined; \"last-modified\"?: string | string[] | undefined; pragma?: string | string[] | undefined; \"proxy-authenticate\"?: string | string[] | undefined; \"proxy-authorization\"?: string | string[] | undefined; \"public-key-pins\"?: string | string[] | undefined; referer?: string | string[] | undefined; \"retry-after\"?: string | string[] | undefined; \"sec-websocket-accept\"?: string | string[] | undefined; \"sec-websocket-extensions\"?: string | string[] | undefined; \"sec-websocket-key\"?: string | string[] | undefined; \"sec-websocket-protocol\"?: string | string[] | undefined; \"sec-websocket-version\"?: string | string[] | undefined; \"set-cookie\"?: string | string[] | undefined; \"strict-transport-security\"?: string | string[] | undefined; tk?: string | string[] | undefined; trailer?: string | string[] | undefined; \"transfer-encoding\"?: string | string[] | undefined; upgrade?: string | string[] | undefined; \"user-agent\"?: string | string[] | undefined; vary?: string | string[] | undefined; via?: string | string[] | undefined; \"www-authenticate\"?: string | string[] | undefined; } & { [header: string]: string | string[] | undefined; }" ], "path": "src/core/server/http/router/headers.ts", "deprecated": false, @@ -9670,6 +9726,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -9742,7 +9802,7 @@ "\nSet of well-known HTTP headers." ], "signature": [ - "\"from\" | \"date\" | \"range\" | \"warning\" | \"location\" | \"origin\" | \"allow\" | \"accept\" | \"host\" | \"accept-language\" | \"accept-patch\" | \"accept-ranges\" | \"access-control-allow-credentials\" | \"access-control-allow-headers\" | \"access-control-allow-methods\" | \"access-control-allow-origin\" | \"access-control-expose-headers\" | \"access-control-max-age\" | \"access-control-request-headers\" | \"access-control-request-method\" | \"age\" | \"alt-svc\" | \"authorization\" | \"cache-control\" | \"connection\" | \"content-disposition\" | \"content-encoding\" | \"content-language\" | \"content-length\" | \"content-location\" | \"content-range\" | \"content-type\" | \"cookie\" | \"etag\" | \"expect\" | \"expires\" | \"forwarded\" | \"if-match\" | \"if-modified-since\" | \"if-none-match\" | \"if-unmodified-since\" | \"last-modified\" | \"pragma\" | \"proxy-authenticate\" | \"proxy-authorization\" | \"public-key-pins\" | \"referer\" | \"retry-after\" | \"sec-websocket-accept\" | \"sec-websocket-extensions\" | \"sec-websocket-key\" | \"sec-websocket-protocol\" | \"sec-websocket-version\" | \"set-cookie\" | \"strict-transport-security\" | \"tk\" | \"trailer\" | \"transfer-encoding\" | \"upgrade\" | \"user-agent\" | \"vary\" | \"via\" | \"www-authenticate\"" + "\"warning\" | \"from\" | \"date\" | \"range\" | \"location\" | \"origin\" | \"allow\" | \"accept\" | \"host\" | \"accept-language\" | \"accept-patch\" | \"accept-ranges\" | \"access-control-allow-credentials\" | \"access-control-allow-headers\" | \"access-control-allow-methods\" | \"access-control-allow-origin\" | \"access-control-expose-headers\" | \"access-control-max-age\" | \"access-control-request-headers\" | \"access-control-request-method\" | \"age\" | \"alt-svc\" | \"authorization\" | \"cache-control\" | \"connection\" | \"content-disposition\" | \"content-encoding\" | \"content-language\" | \"content-length\" | \"content-location\" | \"content-range\" | \"content-type\" | \"cookie\" | \"etag\" | \"expect\" | \"expires\" | \"forwarded\" | \"if-match\" | \"if-modified-since\" | \"if-none-match\" | \"if-unmodified-since\" | \"last-modified\" | \"pragma\" | \"proxy-authenticate\" | \"proxy-authorization\" | \"public-key-pins\" | \"referer\" | \"retry-after\" | \"sec-websocket-accept\" | \"sec-websocket-extensions\" | \"sec-websocket-key\" | \"sec-websocket-protocol\" | \"sec-websocket-version\" | \"set-cookie\" | \"strict-transport-security\" | \"tk\" | \"trailer\" | \"transfer-encoding\" | \"upgrade\" | \"user-agent\" | \"vary\" | \"via\" | \"www-authenticate\"" ], "path": "src/core/server/http/router/headers.ts", "deprecated": false, @@ -9864,6 +9924,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -10016,6 +10080,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -10190,6 +10258,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -10363,6 +10435,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -10537,6 +10613,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -10811,6 +10891,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -10985,6 +11069,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -11318,6 +11406,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -11374,7 +11466,9 @@ }, ") => ", "KibanaResponse", - "; }) => Context[ContextName] | Promise" + "; }) => ", + "MaybePromise", + ">" ], "path": "src/core/server/http/types.ts", "deprecated": false, @@ -11535,6 +11629,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -11759,6 +11857,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -11941,6 +12043,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -12158,7 +12264,7 @@ "\nHttp response headers to set." ], "signature": [ - "Record<\"from\" | \"date\" | \"range\" | \"warning\" | \"location\" | \"origin\" | \"allow\" | \"accept\" | \"host\" | \"accept-language\" | \"accept-patch\" | \"accept-ranges\" | \"access-control-allow-credentials\" | \"access-control-allow-headers\" | \"access-control-allow-methods\" | \"access-control-allow-origin\" | \"access-control-expose-headers\" | \"access-control-max-age\" | \"access-control-request-headers\" | \"access-control-request-method\" | \"age\" | \"alt-svc\" | \"authorization\" | \"cache-control\" | \"connection\" | \"content-disposition\" | \"content-encoding\" | \"content-language\" | \"content-length\" | \"content-location\" | \"content-range\" | \"content-type\" | \"cookie\" | \"etag\" | \"expect\" | \"expires\" | \"forwarded\" | \"if-match\" | \"if-modified-since\" | \"if-none-match\" | \"if-unmodified-since\" | \"last-modified\" | \"pragma\" | \"proxy-authenticate\" | \"proxy-authorization\" | \"public-key-pins\" | \"referer\" | \"retry-after\" | \"sec-websocket-accept\" | \"sec-websocket-extensions\" | \"sec-websocket-key\" | \"sec-websocket-protocol\" | \"sec-websocket-version\" | \"set-cookie\" | \"strict-transport-security\" | \"tk\" | \"trailer\" | \"transfer-encoding\" | \"upgrade\" | \"user-agent\" | \"vary\" | \"via\" | \"www-authenticate\", string | string[]> | Record" + "Record<\"warning\" | \"from\" | \"date\" | \"range\" | \"location\" | \"origin\" | \"allow\" | \"accept\" | \"host\" | \"accept-language\" | \"accept-patch\" | \"accept-ranges\" | \"access-control-allow-credentials\" | \"access-control-allow-headers\" | \"access-control-allow-methods\" | \"access-control-allow-origin\" | \"access-control-expose-headers\" | \"access-control-max-age\" | \"access-control-request-headers\" | \"access-control-request-method\" | \"age\" | \"alt-svc\" | \"authorization\" | \"cache-control\" | \"connection\" | \"content-disposition\" | \"content-encoding\" | \"content-language\" | \"content-length\" | \"content-location\" | \"content-range\" | \"content-type\" | \"cookie\" | \"etag\" | \"expect\" | \"expires\" | \"forwarded\" | \"if-match\" | \"if-modified-since\" | \"if-none-match\" | \"if-unmodified-since\" | \"last-modified\" | \"pragma\" | \"proxy-authenticate\" | \"proxy-authorization\" | \"public-key-pins\" | \"referer\" | \"retry-after\" | \"sec-websocket-accept\" | \"sec-websocket-extensions\" | \"sec-websocket-key\" | \"sec-websocket-protocol\" | \"sec-websocket-version\" | \"set-cookie\" | \"strict-transport-security\" | \"tk\" | \"trailer\" | \"transfer-encoding\" | \"upgrade\" | \"user-agent\" | \"vary\" | \"via\" | \"www-authenticate\", string | string[]> | Record" ], "path": "src/core/server/http/router/headers.ts", "deprecated": false, @@ -12362,6 +12468,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -12586,6 +12696,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", diff --git a/api_docs/core_http.mdx b/api_docs/core_http.mdx index 394a7c613118c..954a5b1421f89 100644 --- a/api_docs/core_http.mdx +++ b/api_docs/core_http.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core-http title: "core.http" image: https://source.unsplash.com/400x175/?github summary: API docs for the core.http plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.http'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2497 | 15 | 971 | 33 | +| 2524 | 15 | 977 | 33 | ## Client diff --git a/api_docs/core_saved_objects.devdocs.json b/api_docs/core_saved_objects.devdocs.json index 08b910c7268f8..ba6118416a1e1 100644 --- a/api_docs/core_saved_objects.devdocs.json +++ b/api_docs/core_saved_objects.devdocs.json @@ -9895,6 +9895,21 @@ "path": "src/core/server/saved_objects/service/lib/collect_multi_namespace_references.ts", "deprecated": false }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectReferenceWithContext.originId", + "type": "string", + "tags": [], + "label": "originId", + "description": [ + "The origin ID of the referenced object (if it has one)" + ], + "signature": [ + "string | undefined" + ], + "path": "src/core/server/saved_objects/service/lib/collect_multi_namespace_references.ts", + "deprecated": false + }, { "parentPluginId": "core", "id": "def-server.SavedObjectReferenceWithContext.spaces", @@ -9954,6 +9969,21 @@ ], "path": "src/core/server/saved_objects/service/lib/collect_multi_namespace_references.ts", "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectReferenceWithContext.spacesWithMatchingOrigins", + "type": "Array", + "tags": [], + "label": "spacesWithMatchingOrigins", + "description": [ + "The space(s) that objects matching this origin exist in (including this one)" + ], + "signature": [ + "string[] | undefined" + ], + "path": "src/core/server/saved_objects/service/lib/collect_multi_namespace_references.ts", + "deprecated": false } ], "initialIsOpen": false diff --git a/api_docs/core_saved_objects.mdx b/api_docs/core_saved_objects.mdx index 43c89a9e03e2f..3d2ddcc47ed8a 100644 --- a/api_docs/core_saved_objects.mdx +++ b/api_docs/core_saved_objects.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core-savedObjects title: "core.savedObjects" image: https://source.unsplash.com/400x175/?github summary: API docs for the core.savedObjects plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.savedObjects'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2497 | 15 | 971 | 33 | +| 2524 | 15 | 977 | 33 | ## Client diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index d100081f7b272..3d1c022b25501 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github summary: API docs for the customIntegrations plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] warning: 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. --- diff --git a/api_docs/dashboard.devdocs.json b/api_docs/dashboard.devdocs.json index a7a90e41d4b05..840d4d1eee1e8 100644 --- a/api_docs/dashboard.devdocs.json +++ b/api_docs/dashboard.devdocs.json @@ -1219,7 +1219,13 @@ "label": "controlGroupInput", "description": [], "signature": [ - "DashboardContainerControlGroupInput", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.PersistableControlGroupInput", + "text": "PersistableControlGroupInput" + }, " | undefined" ], "path": "src/plugins/dashboard/public/types.ts", @@ -1952,7 +1958,13 @@ "description": [], "signature": [ "Omit<", - "RawControlGroupAttributes", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.RawControlGroupAttributes", + "text": "RawControlGroupAttributes" + }, ", \"id\"> | undefined" ], "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts", @@ -2079,7 +2091,7 @@ "section": "def-common.RawSavedDashboardPanel730ToLatest", "text": "RawSavedDashboardPanel730ToLatest" }, - ", \"type\" | \"title\" | \"panelIndex\" | \"gridData\" | \"version\" | \"embeddableConfig\" | \"panelRefName\"> & { readonly id?: string | undefined; readonly type: string; }" + ", \"title\" | \"type\" | \"panelIndex\" | \"gridData\" | \"version\" | \"embeddableConfig\" | \"panelRefName\"> & { readonly id?: string | undefined; readonly type: string; }" ], "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, @@ -2431,78 +2443,6 @@ "common": { "classes": [], "functions": [ - { - "parentPluginId": "dashboard", - "id": "def-common.controlGroupInputToRawAttributes", - "type": "Function", - "tags": [], - "label": "controlGroupInputToRawAttributes", - "description": [], - "signature": [ - "(controlGroupInput: Omit<", - { - "pluginId": "controls", - "scope": "common", - "docId": "kibControlsPluginApi", - "section": "def-common.ControlGroupInput", - "text": "ControlGroupInput" - }, - ", \"id\">) => ", - "RawControlGroupAttributes" - ], - "path": "src/plugins/dashboard/common/embeddable/dashboard_control_group.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-common.controlGroupInputToRawAttributes.$1", - "type": "Object", - "tags": [], - "label": "controlGroupInput", - "description": [], - "signature": [ - "Omit<", - { - "pluginId": "controls", - "scope": "common", - "docId": "kibControlsPluginApi", - "section": "def-common.ControlGroupInput", - "text": "ControlGroupInput" - }, - ", \"id\">" - ], - "path": "src/plugins/dashboard/common/embeddable/dashboard_control_group.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-common.getDefaultDashboardControlGroupInput", - "type": "Function", - "tags": [], - "label": "getDefaultDashboardControlGroupInput", - "description": [], - "signature": [ - "() => Omit<", - { - "pluginId": "controls", - "scope": "common", - "docId": "kibControlsPluginApi", - "section": "def-common.ControlGroupInput", - "text": "ControlGroupInput" - }, - ", \"id\">" - ], - "path": "src/plugins/dashboard/common/embeddable/dashboard_control_group.ts", - "deprecated": false, - "returnComment": [], - "children": [], - "initialIsOpen": false - }, { "parentPluginId": "dashboard", "id": "def-common.migratePanelsTo730", @@ -2677,118 +2617,6 @@ ], "returnComment": [], "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-common.rawAttributesToControlGroupInput", - "type": "Function", - "tags": [], - "label": "rawAttributesToControlGroupInput", - "description": [], - "signature": [ - "(rawControlGroupAttributes: ", - "RawControlGroupAttributes", - ") => Omit<", - { - "pluginId": "controls", - "scope": "common", - "docId": "kibControlsPluginApi", - "section": "def-common.ControlGroupInput", - "text": "ControlGroupInput" - }, - ", \"id\"> | undefined" - ], - "path": "src/plugins/dashboard/common/embeddable/dashboard_control_group.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-common.rawAttributesToControlGroupInput.$1", - "type": "CompoundType", - "tags": [], - "label": "rawControlGroupAttributes", - "description": [], - "signature": [ - "RawControlGroupAttributes" - ], - "path": "src/plugins/dashboard/common/embeddable/dashboard_control_group.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-common.rawAttributesToSerializable", - "type": "Function", - "tags": [], - "label": "rawAttributesToSerializable", - "description": [], - "signature": [ - "(rawControlGroupAttributes: Omit<", - "RawControlGroupAttributes", - ", \"id\">) => ", - "SerializableRecord" - ], - "path": "src/plugins/dashboard/common/embeddable/dashboard_control_group.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-common.rawAttributesToSerializable.$1", - "type": "Object", - "tags": [], - "label": "rawControlGroupAttributes", - "description": [], - "signature": [ - "Omit<", - "RawControlGroupAttributes", - ", \"id\">" - ], - "path": "src/plugins/dashboard/common/embeddable/dashboard_control_group.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "dashboard", - "id": "def-common.serializableToRawAttributes", - "type": "Function", - "tags": [], - "label": "serializableToRawAttributes", - "description": [], - "signature": [ - "(serializable: ", - "SerializableRecord", - ") => Omit<", - "RawControlGroupAttributes", - ", \"type\" | \"id\">" - ], - "path": "src/plugins/dashboard/common/embeddable/dashboard_control_group.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "dashboard", - "id": "def-common.serializableToRawAttributes.$1", - "type": "Object", - "tags": [], - "label": "serializable", - "description": [], - "signature": [ - "SerializableRecord" - ], - "path": "src/plugins/dashboard/common/embeddable/dashboard_control_group.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false } ], "interfaces": [ @@ -2850,7 +2678,13 @@ "label": "controlGroupInput", "description": [], "signature": [ - "DashboardContainerControlGroupInput", + { + "pluginId": "controls", + "scope": "common", + "docId": "kibControlsPluginApi", + "section": "def-common.PersistableControlGroupInput", + "text": "PersistableControlGroupInput" + }, " | undefined" ], "path": "src/plugins/dashboard/common/types.ts", @@ -2944,7 +2778,7 @@ "signature": [ "Pick<", "RawSavedDashboardPanel610", - ", \"columns\" | \"title\" | \"sort\" | \"panelIndex\" | \"gridData\" | \"version\"> & { readonly id: string; readonly type: string; }" + ", \"title\" | \"columns\" | \"sort\" | \"panelIndex\" | \"gridData\" | \"version\"> & { readonly id: string; readonly type: string; }" ], "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, @@ -2960,7 +2794,7 @@ "signature": [ "Pick<", "RawSavedDashboardPanel620", - ", \"columns\" | \"title\" | \"sort\" | \"panelIndex\" | \"gridData\" | \"version\" | \"embeddableConfig\"> & { readonly id: string; readonly type: string; }" + ", \"title\" | \"columns\" | \"sort\" | \"panelIndex\" | \"gridData\" | \"version\" | \"embeddableConfig\"> & { readonly id: string; readonly type: string; }" ], "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, @@ -2976,7 +2810,7 @@ "signature": [ "Pick<", "RawSavedDashboardPanel620", - ", \"columns\" | \"title\" | \"sort\" | \"panelIndex\" | \"gridData\" | \"version\" | \"embeddableConfig\"> & { readonly id: string; readonly type: string; }" + ", \"title\" | \"columns\" | \"sort\" | \"panelIndex\" | \"gridData\" | \"version\" | \"embeddableConfig\"> & { readonly id: string; readonly type: string; }" ], "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, @@ -3014,7 +2848,7 @@ "section": "def-common.RawSavedDashboardPanel730ToLatest", "text": "RawSavedDashboardPanel730ToLatest" }, - ", \"type\" | \"title\" | \"panelIndex\" | \"gridData\" | \"version\" | \"embeddableConfig\" | \"panelRefName\"> & { readonly id?: string | undefined; readonly type: string; }" + ", \"title\" | \"type\" | \"panelIndex\" | \"gridData\" | \"version\" | \"embeddableConfig\" | \"panelRefName\"> & { readonly id?: string | undefined; readonly type: string; }" ], "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, @@ -3030,7 +2864,7 @@ "signature": [ "Pick<", "RawSavedDashboardPanelTo60", - ", \"columns\" | \"title\" | \"sort\" | \"size_x\" | \"size_y\" | \"row\" | \"col\" | \"panelIndex\"> & { readonly id: string; readonly type: string; }" + ", \"title\" | \"columns\" | \"sort\" | \"size_x\" | \"size_y\" | \"row\" | \"col\" | \"panelIndex\"> & { readonly id: string; readonly type: string; }" ], "path": "src/plugins/dashboard/common/types.ts", "deprecated": false, diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index d0f5a00bab677..b7d6084864327 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github summary: API docs for the dashboard plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-prese | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 151 | 0 | 149 | 14 | +| 142 | 0 | 140 | 12 | ## Client diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index 532dc65f407b6..458cdf96fef07 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github summary: API docs for the dashboardEnhanced plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] warning: 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. --- diff --git a/api_docs/data.devdocs.json b/api_docs/data.devdocs.json index 75239e090a139..62c04ae8d5d64 100644 --- a/api_docs/data.devdocs.json +++ b/api_docs/data.devdocs.json @@ -2395,7 +2395,7 @@ "section": "def-public.PluginInitializerContext", "text": "PluginInitializerContext" }, - "; }>; }>; autocomplete: Readonly<{} & { querySuggestions: Readonly<{} & { enabled: boolean; }>; valueSuggestions: Readonly<{} & { timeout: moment.Duration; enabled: boolean; tiers: string[]; terminateAfter: moment.Duration; }>; }>; }>>" + "; }>; }>; }>>" ], "path": "src/plugins/data/public/plugin.ts", "deprecated": false, @@ -2731,26 +2731,6 @@ "plugin": "dataViewEditor", "path": "src/plugins/data_view_editor/public/open_editor.tsx" }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/hooks/update_kuery_string.ts" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/hooks/update_kuery_string.ts" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx" - }, { "plugin": "maps", "path": "x-pack/plugins/maps/public/classes/tooltips/es_tooltip_property.test.ts" @@ -3265,6 +3245,96 @@ "children": [], "initialIsOpen": false }, + { + "parentPluginId": "data", + "id": "def-public.NowProvider", + "type": "Class", + "tags": [], + "label": "NowProvider", + "description": [ + "\nUsed to synchronize time between parallel searches with relative time range that rely on `now`." + ], + "path": "src/plugins/data/public/now_provider/now_provider.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-public.NowProvider.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "src/plugins/data/public/now_provider/now_provider.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-public.NowProvider.get", + "type": "Function", + "tags": [], + "label": "get", + "description": [], + "signature": [ + "() => Date" + ], + "path": "src/plugins/data/public/now_provider/now_provider.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-public.NowProvider.set", + "type": "Function", + "tags": [], + "label": "set", + "description": [], + "signature": [ + "(now: Date) => void" + ], + "path": "src/plugins/data/public/now_provider/now_provider.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-public.NowProvider.set.$1", + "type": "Object", + "tags": [], + "label": "now", + "description": [], + "signature": [ + "Date" + ], + "path": "src/plugins/data/public/now_provider/now_provider.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-public.NowProvider.reset", + "type": "Function", + "tags": [], + "label": "reset", + "description": [], + "signature": [ + "() => void" + ], + "path": "src/plugins/data/public/now_provider/now_provider.ts", + "deprecated": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "data", "id": "def-public.OptionedParamType", @@ -7626,6 +7696,50 @@ "plugin": "dataViews", "path": "src/plugins/data_views/common/data_views/data_view.ts" }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" + }, { "plugin": "unifiedSearch", "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" @@ -7660,31 +7774,27 @@ }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" + "path": "src/plugins/unified_search/server/autocomplete/terms_enum.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" + "path": "src/plugins/unified_search/server/autocomplete/terms_enum.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" + "path": "src/plugins/unified_search/server/autocomplete/terms_agg.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/server/utils.ts" + "path": "src/plugins/unified_search/server/autocomplete/terms_agg.ts" }, { - "plugin": "dataViews", - "path": "src/plugins/data_views/server/utils.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/server/autocomplete/terms_agg.ts" }, { - "plugin": "dataViews", - "path": "src/plugins/data_views/server/utils.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/server/autocomplete/terms_agg.ts" }, { "plugin": "unifiedSearch", @@ -7711,24 +7821,28 @@ "path": "src/plugins/unified_search/public/filter_bar/filter_editor/value_input_type.tsx" }, { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/utils.test.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" }, { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/utils.test.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" }, { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/fields.mocks.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" }, { "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/fields.mocks.ts" + "path": "src/plugins/data_views/common/utils.test.ts" }, { "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/fields.mocks.ts" + "path": "src/plugins/data_views/common/utils.test.ts" } ], "children": [ @@ -8028,155 +8142,31 @@ }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/alerts/components/param_details_form/use_derived_index_pattern.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/alerts/components/param_details_form/use_derived_index_pattern.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/components/kuery_bar/with_kuery_autocompletion.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/components/kuery_bar/with_kuery_autocompletion.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/components/kuery_bar/index.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/components/kuery_bar/index.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/lib/kuery.ts" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/lib/kuery.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_item.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_item.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_bar.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_bar.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/search_bar/search_bar.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/search_bar/search_bar.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/apply_filters/apply_filter_popover_content.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/apply_filters/apply_filter_popover_content.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/apply_filters/apply_filters_popover.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/apply_filters/apply_filters_popover.tsx" - }, - { - "plugin": "stackAlerts", - "path": "x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx" - }, - { - "plugin": "stackAlerts", - "path": "x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" } ], "children": [ @@ -9581,7 +9571,7 @@ "label": "AggConfigOptions", "description": [], "signature": [ - "{ type: ", + "{ id?: string | undefined; type: ", { "pluginId": "data", "scope": "common", @@ -9589,7 +9579,7 @@ "section": "def-common.IAggType", "text": "IAggType" }, - "; id?: string | undefined; enabled?: boolean | undefined; params?: {} | ", + "; enabled?: boolean | undefined; params?: {} | ", "SerializableRecord", " | undefined; schema?: string | undefined; }" ], @@ -10727,14 +10717,6 @@ "plugin": "dataViews", "path": "src/plugins/data_views/public/index.ts" }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/fetch_index_patterns.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/fetch_index_patterns.ts" - }, { "plugin": "unifiedSearch", "path": "src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx" @@ -11119,12 +11101,60 @@ "removeBy": "8.1", "references": [ { - "plugin": "dataEnhanced", - "path": "x-pack/plugins/data_enhanced/server/search/session/types.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts" }, { - "plugin": "dataEnhanced", - "path": "x-pack/plugins/data_enhanced/server/search/session/types.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts" + }, + { + "plugin": "dataEnhanced", + "path": "x-pack/plugins/data_enhanced/server/search/session/types.ts" + }, + { + "plugin": "dataEnhanced", + "path": "x-pack/plugins/data_enhanced/server/search/session/types.ts" }, { "plugin": "dataEnhanced", @@ -11182,6 +11212,20 @@ "references": [], "initialIsOpen": false }, + { + "parentPluginId": "data", + "id": "def-public.NowProviderInternalContract", + "type": "Type", + "tags": [], + "label": "NowProviderInternalContract", + "description": [], + "signature": [ + "{ get: () => Date; set: (now: Date) => void; reset: () => void; }" + ], + "path": "src/plugins/data/public/now_provider/now_provider.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "data", "id": "def-public.ParsedInterval", @@ -12237,8 +12281,8 @@ "pluginId": "dataViews", "scope": "common", "docId": "kibDataViewsPluginApi", - "section": "def-common.IIndexPattern", - "text": "IIndexPattern" + "section": "def-common.DataView", + "text": "DataView" }, "[]) => string" ], @@ -12275,8 +12319,8 @@ "pluginId": "dataViews", "scope": "common", "docId": "kibDataViewsPluginApi", - "section": "def-common.IIndexPattern", - "text": "IIndexPattern" + "section": "def-common.DataView", + "text": "DataView" }, "[]" ], @@ -12560,9 +12604,9 @@ "Observable", "; getTimeUpdate$: () => ", "Observable", - "; getRefreshIntervalUpdate$: () => ", + "; getRefreshIntervalUpdate$: () => ", "Observable", - "; getAutoRefreshFetch$: () => ", + "; getAutoRefreshFetch$: () => ", "Observable", "<", { @@ -12574,7 +12618,7 @@ }, ">; getFetch$: () => ", "Observable", - "; getTime: () => ", + "; getTime: () => ", { "pluginId": "data", "scope": "common", @@ -14725,27 +14769,6 @@ "path": "src/plugins/data/public/types.ts", "deprecated": false, "children": [ - { - "parentPluginId": "data", - "id": "def-public.DataPublicPluginSetup.autocomplete", - "type": "Object", - "tags": [], - "label": "autocomplete", - "description": [], - "signature": [ - "{ getQuerySuggestions: ", - { - "pluginId": "data", - "scope": "public", - "docId": "kibDataAutocompletePluginApi", - "section": "def-public.QuerySuggestionGetFn", - "text": "QuerySuggestionGetFn" - }, - "; getAutocompleteSettings: () => { terminateAfter: number; timeout: number; }; }" - ], - "path": "src/plugins/data/public/types.ts", - "deprecated": false - }, { "parentPluginId": "data", "id": "def-public.DataPublicPluginSetup.search", @@ -14782,7 +14805,13 @@ "text": "FilterManager" }, "; timefilter: ", - "TimefilterSetup", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.TimefilterSetup", + "text": "TimefilterSetup" + }, "; queryString: ", { "pluginId": "data", @@ -14851,31 +14880,6 @@ "path": "src/plugins/data/public/types.ts", "deprecated": false }, - { - "parentPluginId": "data", - "id": "def-public.DataPublicPluginStart.autocomplete", - "type": "Object", - "tags": [], - "label": "autocomplete", - "description": [ - "\nautocomplete service\n{@link AutocompleteStart}" - ], - "signature": [ - "{ getQuerySuggestions: ", - { - "pluginId": "data", - "scope": "public", - "docId": "kibDataAutocompletePluginApi", - "section": "def-public.QuerySuggestionGetFn", - "text": "QuerySuggestionGetFn" - }, - "; hasQuerySuggestions: (language: string) => boolean; getValueSuggestions: ", - "ValueSuggestionsGetFn", - "; }" - ], - "path": "src/plugins/data/public/types.ts", - "deprecated": false - }, { "parentPluginId": "data", "id": "def-public.DataPublicPluginStart.dataViews", @@ -14949,14 +14953,6 @@ "plugin": "graph", "path": "x-pack/plugins/graph/public/plugin.ts" }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/alerts/components/param_details_form/use_derived_index_pattern.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/alerts/components/param_details_form/use_derived_index_pattern.tsx" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts" @@ -14981,10 +14977,6 @@ "plugin": "stackAlerts", "path": "x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/index.tsx" }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx" - }, { "plugin": "inputControlVis", "path": "src/plugins/input_control_vis/public/control/list_control_factory.ts" @@ -15232,7 +15224,13 @@ "text": "QueryState" }, "; }>; timefilter: ", - "TimefilterSetup", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.TimefilterSetup", + "text": "TimefilterSetup" + }, "; getEsQuery: (indexPattern: ", { "pluginId": "dataViews", @@ -15352,7 +15350,7 @@ "section": "def-server.PluginInitializerContext", "text": "PluginInitializerContext" }, - "; }>; }>; autocomplete: Readonly<{} & { querySuggestions: Readonly<{} & { enabled: boolean; }>; valueSuggestions: Readonly<{} & { timeout: moment.Duration; enabled: boolean; tiers: string[]; terminateAfter: moment.Duration; }>; }>; }>>" + "; }>; }>; }>>" ], "path": "src/plugins/data/server/plugin.ts", "deprecated": false, @@ -15389,7 +15387,7 @@ }, ">, { bfetch, expressions, usageCollection, fieldFormats }: ", "DataPluginSetupDependencies", - ") => { autocomplete: { getAutocompleteSettings: () => { terminateAfter: number; timeout: number; }; }; __enhance: (enhancements: DataEnhancements) => void; search: ", + ") => { __enhance: (enhancements: DataEnhancements) => void; search: ", "ISearchSetup", "; query: { filterManager: { extract: (filters: ", "Filter", @@ -17157,26 +17155,6 @@ "plugin": "dataViewEditor", "path": "src/plugins/data_view_editor/public/open_editor.tsx" }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/hooks/update_kuery_string.ts" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/hooks/update_kuery_string.ts" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx" - }, { "plugin": "maps", "path": "x-pack/plugins/maps/public/classes/tooltips/es_tooltip_property.test.ts" @@ -18113,6 +18091,44 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "data", + "id": "def-server.getRequestAbortedSignal", + "type": "Function", + "tags": [], + "label": "getRequestAbortedSignal", + "description": [ + "\nA simple utility function that returns an `AbortSignal` corresponding to an `AbortController`\nwhich aborts when the given request is aborted." + ], + "signature": [ + "(aborted$: ", + "Observable", + ") => AbortSignal" + ], + "path": "src/plugins/data/server/lib/get_request_aborted_signal.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-server.getRequestAbortedSignal.$1", + "type": "Object", + "tags": [], + "label": "aborted$", + "description": [ + "The observable of abort events (usually `request.events.aborted$`)" + ], + "signature": [ + "Observable", + "" + ], + "path": "src/plugins/data/server/lib/get_request_aborted_signal.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "data", "id": "def-server.getTime", @@ -18463,552 +18479,103 @@ }, { "parentPluginId": "data", - "id": "def-server.IFieldType", + "id": "def-server.ISearchOptions", "type": "Interface", - "tags": [ - "deprecated" - ], - "label": "IFieldType", + "tags": [], + "label": "ISearchOptions", "description": [], - "signature": [ - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.IFieldType", - "text": "IFieldType" - }, - " extends ", - "DataViewFieldBase" - ], - "path": "src/plugins/data_views/common/fields/types.ts", - "deprecated": true, - "removeBy": "8.2", - "references": [ + "path": "src/plugins/data/common/search/types.ts", + "deprecated": false, + "children": [ { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/utils.ts" + "parentPluginId": "data", + "id": "def-server.ISearchOptions.abortSignal", + "type": "Object", + "tags": [], + "label": "abortSignal", + "description": [ + "\nAn `AbortSignal` that allows the caller of `search` to abort a search request." + ], + "signature": [ + "AbortSignal | undefined" + ], + "path": "src/plugins/data/common/search/types.ts", + "deprecated": false }, { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/utils.ts" + "parentPluginId": "data", + "id": "def-server.ISearchOptions.strategy", + "type": "string", + "tags": [], + "label": "strategy", + "description": [ + "\nUse this option to force using a specific server side search strategy. Leave empty to use the default strategy." + ], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/data/common/search/types.ts", + "deprecated": false }, { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/data_view_field.ts" + "parentPluginId": "data", + "id": "def-server.ISearchOptions.legacyHitsTotal", + "type": "CompoundType", + "tags": [], + "label": "legacyHitsTotal", + "description": [ + "\nRequest the legacy format for the total number of hits. If sending `rest_total_hits_as_int` to\nsomething other than `true`, this should be set to `false`." + ], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/data/common/search/types.ts", + "deprecated": false }, { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/data_view_field.ts" + "parentPluginId": "data", + "id": "def-server.ISearchOptions.sessionId", + "type": "string", + "tags": [], + "label": "sessionId", + "description": [ + "\nA session ID, grouping multiple search requests into a single session." + ], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/data/common/search/types.ts", + "deprecated": false }, { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/field_list.ts" + "parentPluginId": "data", + "id": "def-server.ISearchOptions.isStored", + "type": "CompoundType", + "tags": [], + "label": "isStored", + "description": [ + "\nWhether the session is already saved (i.e. sent to background)" + ], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/data/common/search/types.ts", + "deprecated": false }, { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/field_list.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/field_list.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/field_list.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/types.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/types.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/types.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/types.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/index.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/data_views/data_view.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/data_views/data_view.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/range_value_input.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/range_value_input.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/server/utils.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/server/utils.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/server/utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_operators.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_operators.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_operators.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_operators.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/value_input_type.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/value_input_type.tsx" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/utils.test.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/utils.test.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/fields.mocks.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/fields.mocks.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/fields.mocks.ts" - } - ], - "children": [ - { - "parentPluginId": "data", - "id": "def-server.IFieldType.count", - "type": "number", - "tags": [], - "label": "count", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "src/plugins/data_views/common/fields/types.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-server.IFieldType.esTypes", - "type": "Array", - "tags": [], - "label": "esTypes", - "description": [], - "signature": [ - "string[] | undefined" - ], - "path": "src/plugins/data_views/common/fields/types.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-server.IFieldType.aggregatable", - "type": "CompoundType", - "tags": [], - "label": "aggregatable", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/data_views/common/fields/types.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-server.IFieldType.filterable", - "type": "CompoundType", - "tags": [], - "label": "filterable", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/data_views/common/fields/types.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-server.IFieldType.searchable", - "type": "CompoundType", - "tags": [], - "label": "searchable", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/data_views/common/fields/types.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-server.IFieldType.sortable", - "type": "CompoundType", - "tags": [], - "label": "sortable", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/data_views/common/fields/types.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-server.IFieldType.visualizable", - "type": "CompoundType", - "tags": [], - "label": "visualizable", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/data_views/common/fields/types.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-server.IFieldType.readFromDocValues", - "type": "CompoundType", - "tags": [], - "label": "readFromDocValues", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/data_views/common/fields/types.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-server.IFieldType.displayName", - "type": "string", - "tags": [], - "label": "displayName", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/data_views/common/fields/types.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-server.IFieldType.customLabel", - "type": "string", - "tags": [], - "label": "customLabel", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/data_views/common/fields/types.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-server.IFieldType.format", - "type": "Any", - "tags": [], - "label": "format", - "description": [], - "signature": [ - "any" - ], - "path": "src/plugins/data_views/common/fields/types.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-server.IFieldType.toSpec", - "type": "Function", - "tags": [], - "label": "toSpec", - "description": [], - "signature": [ - "((options?: { getFormatterForField?: ((field: ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.IFieldType", - "text": "IFieldType" - }, - " | ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.FieldSpec", - "text": "FieldSpec" - }, - " | ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewField", - "text": "DataViewField" - }, - ") => ", - { - "pluginId": "fieldFormats", - "scope": "common", - "docId": "kibFieldFormatsPluginApi", - "section": "def-common.FieldFormat", - "text": "FieldFormat" - }, - ") | undefined; } | undefined) => ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.FieldSpec", - "text": "FieldSpec" - }, - ") | undefined" - ], - "path": "src/plugins/data_views/common/fields/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "data", - "id": "def-server.IFieldType.toSpec.$1", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "path": "src/plugins/data_views/common/fields/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "data", - "id": "def-server.IFieldType.toSpec.$1.getFormatterForField", - "type": "Function", - "tags": [], - "label": "getFormatterForField", - "description": [], - "signature": [ - "((field: ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.IFieldType", - "text": "IFieldType" - }, - " | ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.FieldSpec", - "text": "FieldSpec" - }, - " | ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewField", - "text": "DataViewField" - }, - ") => ", - { - "pluginId": "fieldFormats", - "scope": "common", - "docId": "kibFieldFormatsPluginApi", - "section": "def-common.FieldFormat", - "text": "FieldFormat" - }, - ") | undefined" - ], - "path": "src/plugins/data_views/common/fields/types.ts", - "deprecated": false - } - ] - } - ], - "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "data", - "id": "def-server.ISearchOptions", - "type": "Interface", - "tags": [], - "label": "ISearchOptions", - "description": [], - "path": "src/plugins/data/common/search/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "data", - "id": "def-server.ISearchOptions.abortSignal", - "type": "Object", - "tags": [], - "label": "abortSignal", - "description": [ - "\nAn `AbortSignal` that allows the caller of `search` to abort a search request." - ], - "signature": [ - "AbortSignal | undefined" - ], - "path": "src/plugins/data/common/search/types.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-server.ISearchOptions.strategy", - "type": "string", - "tags": [], - "label": "strategy", - "description": [ - "\nUse this option to force using a specific server side search strategy. Leave empty to use the default strategy." - ], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/data/common/search/types.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-server.ISearchOptions.legacyHitsTotal", - "type": "CompoundType", - "tags": [], - "label": "legacyHitsTotal", - "description": [ - "\nRequest the legacy format for the total number of hits. If sending `rest_total_hits_as_int` to\nsomething other than `true`, this should be set to `false`." - ], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/data/common/search/types.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-server.ISearchOptions.sessionId", - "type": "string", - "tags": [], - "label": "sessionId", - "description": [ - "\nA session ID, grouping multiple search requests into a single session." - ], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/data/common/search/types.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-server.ISearchOptions.isStored", - "type": "CompoundType", - "tags": [], - "label": "isStored", - "description": [ - "\nWhether the session is already saved (i.e. sent to background)" - ], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/data/common/search/types.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-server.ISearchOptions.isRestore", - "type": "CompoundType", - "tags": [], - "label": "isRestore", - "description": [ - "\nWhether the session is restored (i.e. search requests should re-use the stored search IDs,\nrather than starting from scratch)" - ], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/data/common/search/types.ts", - "deprecated": false + "parentPluginId": "data", + "id": "def-server.ISearchOptions.isRestore", + "type": "CompoundType", + "tags": [], + "label": "isRestore", + "description": [ + "\nWhether the session is restored (i.e. search requests should re-use the stored search IDs,\nrather than starting from scratch)" + ], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/data/common/search/types.ts", + "deprecated": false }, { "parentPluginId": "data", @@ -19456,30 +19023,78 @@ "description": [], "signature": [ { - "pluginId": "dataViews", - "scope": "server", - "docId": "kibDataViewsPluginApi", - "section": "def-server.DataViewsServerPluginStart", - "text": "DataViewsServerPluginStart" - } - ], - "path": "src/plugins/data_views/server/types.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "data", - "id": "def-server.KueryNode", - "type": "Type", - "tags": [ - "deprecated" - ], - "label": "KueryNode", - "description": [], - "path": "src/plugins/data/common/es_query/index.ts", - "deprecated": true, - "removeBy": "8.1", - "references": [ + "pluginId": "dataViews", + "scope": "server", + "docId": "kibDataViewsPluginApi", + "section": "def-server.DataViewsServerPluginStart", + "text": "DataViewsServerPluginStart" + } + ], + "path": "src/plugins/data_views/server/types.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "data", + "id": "def-server.KueryNode", + "type": "Type", + "tags": [ + "deprecated" + ], + "label": "KueryNode", + "description": [], + "path": "src/plugins/data/common/es_query/index.ts", + "deprecated": true, + "removeBy": "8.1", + "references": [ + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts" + }, { "plugin": "dataEnhanced", "path": "x-pack/plugins/data_enhanced/server/search/session/types.ts" @@ -20795,19 +20410,6 @@ "path": "src/plugins/data/server/plugin.ts", "deprecated": false, "children": [ - { - "parentPluginId": "data", - "id": "def-server.DataPluginSetup.autocomplete", - "type": "Object", - "tags": [], - "label": "autocomplete", - "description": [], - "signature": [ - "{ getAutocompleteSettings: () => { terminateAfter: number; timeout: number; }; }" - ], - "path": "src/plugins/data/server/plugin.ts", - "deprecated": false - }, { "parentPluginId": "data", "id": "def-server.DataPluginSetup.search", @@ -25044,26 +24646,6 @@ "plugin": "dataViewEditor", "path": "src/plugins/data_view_editor/public/open_editor.tsx" }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/hooks/update_kuery_string.ts" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/hooks/update_kuery_string.ts" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx" - }, { "plugin": "maps", "path": "x-pack/plugins/maps/public/classes/tooltips/es_tooltip_property.test.ts" @@ -29492,6 +29074,50 @@ "plugin": "dataViews", "path": "src/plugins/data_views/common/data_views/data_view.ts" }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" + }, { "plugin": "unifiedSearch", "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" @@ -29526,31 +29152,27 @@ }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" + "path": "src/plugins/unified_search/server/autocomplete/terms_enum.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" + "path": "src/plugins/unified_search/server/autocomplete/terms_enum.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" + "path": "src/plugins/unified_search/server/autocomplete/terms_agg.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" + "path": "src/plugins/unified_search/server/autocomplete/terms_agg.ts" }, { - "plugin": "dataViews", - "path": "src/plugins/data_views/server/utils.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/server/utils.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/server/autocomplete/terms_agg.ts" }, { - "plugin": "dataViews", - "path": "src/plugins/data_views/server/utils.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/server/autocomplete/terms_agg.ts" }, { "plugin": "unifiedSearch", @@ -29577,24 +29199,28 @@ "path": "src/plugins/unified_search/public/filter_bar/filter_editor/value_input_type.tsx" }, { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/utils.test.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" }, { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/utils.test.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" }, { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/fields.mocks.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" }, { "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/fields.mocks.ts" + "path": "src/plugins/data_views/common/utils.test.ts" }, { "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/fields.mocks.ts" + "path": "src/plugins/data_views/common/utils.test.ts" } ], "children": [ @@ -29894,155 +29520,31 @@ }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/alerts/components/param_details_form/use_derived_index_pattern.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/alerts/components/param_details_form/use_derived_index_pattern.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/components/kuery_bar/with_kuery_autocompletion.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/components/kuery_bar/with_kuery_autocompletion.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/components/kuery_bar/index.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/components/kuery_bar/index.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/lib/kuery.ts" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/lib/kuery.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_item.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_item.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_bar.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_bar.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/search_bar/search_bar.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/search_bar/search_bar.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/apply_filters/apply_filter_popover_content.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/apply_filters/apply_filter_popover_content.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/apply_filters/apply_filters_popover.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/apply_filters/apply_filters_popover.tsx" - }, - { - "plugin": "stackAlerts", - "path": "x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx" - }, - { - "plugin": "stackAlerts", - "path": "x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" } ], "children": [ @@ -32586,14 +32088,6 @@ "plugin": "dataViews", "path": "src/plugins/data_views/public/index.ts" }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/fetch_index_patterns.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/fetch_index_patterns.ts" - }, { "plugin": "unifiedSearch", "path": "src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx" @@ -32648,6 +32142,54 @@ "deprecated": true, "removeBy": "8.1", "references": [ + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts" + }, { "plugin": "dataEnhanced", "path": "x-pack/plugins/data_enhanced/server/search/session/types.ts" @@ -32815,7 +32357,7 @@ "signature": [ "Pick<", "Toast", - ", \"children\" | \"onClick\" | \"onChange\" | \"color\" | \"onKeyDown\" | \"className\" | \"security\" | \"defaultValue\" | \"hidden\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"style\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"role\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-label\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"toastLifeTimeMs\" | \"iconType\" | \"onClose\" | \"data-test-subj\"> & { title?: string | ", + ", \"children\" | \"color\" | \"className\" | \"onChange\" | \"onKeyDown\" | \"onClick\" | \"security\" | \"defaultValue\" | \"hidden\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"style\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"role\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-label\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"toastLifeTimeMs\" | \"iconType\" | \"onClose\" | \"data-test-subj\"> & { title?: string | ", { "pluginId": "core", "scope": "public", diff --git a/api_docs/data.mdx b/api_docs/data.mdx index 354f58d2afd80..a876abb895181 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github summary: API docs for the data plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3426 | 40 | 2816 | 20 | +| 3414 | 38 | 2802 | 18 | ## Client diff --git a/api_docs/data_autocomplete.devdocs.json b/api_docs/data_autocomplete.devdocs.json deleted file mode 100644 index 63229979a6082..0000000000000 --- a/api_docs/data_autocomplete.devdocs.json +++ /dev/null @@ -1,434 +0,0 @@ -{ - "id": "data.autocomplete", - "client": { - "classes": [], - "functions": [], - "interfaces": [ - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionBasic", - "type": "Interface", - "tags": [], - "label": "QuerySuggestionBasic", - "description": [], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionBasic.type", - "type": "Enum", - "tags": [], - "label": "type", - "description": [], - "signature": [ - { - "pluginId": "data", - "scope": "public", - "docId": "kibDataAutocompletePluginApi", - "section": "def-public.QuerySuggestionTypes", - "text": "QuerySuggestionTypes" - } - ], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionBasic.description", - "type": "CompoundType", - "tags": [], - "label": "description", - "description": [], - "signature": [ - "string | JSX.Element | undefined" - ], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionBasic.end", - "type": "number", - "tags": [], - "label": "end", - "description": [], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionBasic.start", - "type": "number", - "tags": [], - "label": "start", - "description": [], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionBasic.text", - "type": "string", - "tags": [], - "label": "text", - "description": [], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionBasic.cursorIndex", - "type": "number", - "tags": [], - "label": "cursorIndex", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionField", - "type": "Interface", - "tags": [], - "label": "QuerySuggestionField", - "description": [], - "signature": [ - { - "pluginId": "data", - "scope": "public", - "docId": "kibDataAutocompletePluginApi", - "section": "def-public.QuerySuggestionField", - "text": "QuerySuggestionField" - }, - " extends ", - { - "pluginId": "data", - "scope": "public", - "docId": "kibDataAutocompletePluginApi", - "section": "def-public.QuerySuggestionBasic", - "text": "QuerySuggestionBasic" - } - ], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionField.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - { - "pluginId": "data", - "scope": "public", - "docId": "kibDataAutocompletePluginApi", - "section": "def-public.QuerySuggestionTypes", - "text": "QuerySuggestionTypes" - }, - ".Field" - ], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionField.field", - "type": "Object", - "tags": [], - "label": "field", - "description": [], - "signature": [ - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.IFieldType", - "text": "IFieldType" - } - ], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionGetFnArgs", - "type": "Interface", - "tags": [], - "label": "QuerySuggestionGetFnArgs", - "description": [], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionGetFnArgs.language", - "type": "string", - "tags": [], - "label": "language", - "description": [], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionGetFnArgs.indexPatterns", - "type": "Array", - "tags": [], - "label": "indexPatterns", - "description": [], - "signature": [ - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.IIndexPattern", - "text": "IIndexPattern" - }, - "[]" - ], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionGetFnArgs.query", - "type": "string", - "tags": [], - "label": "query", - "description": [], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionGetFnArgs.selectionStart", - "type": "number", - "tags": [], - "label": "selectionStart", - "description": [], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionGetFnArgs.selectionEnd", - "type": "number", - "tags": [], - "label": "selectionEnd", - "description": [], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionGetFnArgs.signal", - "type": "Object", - "tags": [], - "label": "signal", - "description": [], - "signature": [ - "AbortSignal | undefined" - ], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionGetFnArgs.useTimeRange", - "type": "CompoundType", - "tags": [], - "label": "useTimeRange", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionGetFnArgs.boolFilter", - "type": "Any", - "tags": [], - "label": "boolFilter", - "description": [], - "signature": [ - "any" - ], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionGetFnArgs.method", - "type": "CompoundType", - "tags": [], - "label": "method", - "description": [], - "signature": [ - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataPluginApi", - "section": "def-common.ValueSuggestionsMethod", - "text": "ValueSuggestionsMethod" - }, - " | undefined" - ], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - } - ], - "initialIsOpen": false - } - ], - "enums": [ - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionTypes", - "type": "Enum", - "tags": [], - "label": "QuerySuggestionTypes", - "description": [], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false, - "initialIsOpen": false - } - ], - "misc": [ - { - "parentPluginId": "data", - "id": "def-public.AutocompleteStart", - "type": "Type", - "tags": [], - "label": "AutocompleteStart", - "description": [], - "signature": [ - "{ getQuerySuggestions: ", - { - "pluginId": "data", - "scope": "public", - "docId": "kibDataAutocompletePluginApi", - "section": "def-public.QuerySuggestionGetFn", - "text": "QuerySuggestionGetFn" - }, - "; hasQuerySuggestions: (language: string) => boolean; getValueSuggestions: ", - "ValueSuggestionsGetFn", - "; }" - ], - "path": "src/plugins/data/public/autocomplete/autocomplete_service.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestion", - "type": "Type", - "tags": [], - "label": "QuerySuggestion", - "description": [], - "signature": [ - { - "pluginId": "data", - "scope": "public", - "docId": "kibDataAutocompletePluginApi", - "section": "def-public.QuerySuggestionBasic", - "text": "QuerySuggestionBasic" - }, - " | ", - { - "pluginId": "data", - "scope": "public", - "docId": "kibDataAutocompletePluginApi", - "section": "def-public.QuerySuggestionField", - "text": "QuerySuggestionField" - } - ], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionGetFn", - "type": "Type", - "tags": [], - "label": "QuerySuggestionGetFn", - "description": [], - "signature": [ - "(args: ", - { - "pluginId": "data", - "scope": "public", - "docId": "kibDataAutocompletePluginApi", - "section": "def-public.QuerySuggestionGetFnArgs", - "text": "QuerySuggestionGetFnArgs" - }, - ") => Promise<", - { - "pluginId": "data", - "scope": "public", - "docId": "kibDataAutocompletePluginApi", - "section": "def-public.QuerySuggestion", - "text": "QuerySuggestion" - }, - "[]> | undefined" - ], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "data", - "id": "def-public.QuerySuggestionGetFn.$1", - "type": "Object", - "tags": [], - "label": "args", - "description": [], - "signature": [ - { - "pluginId": "data", - "scope": "public", - "docId": "kibDataAutocompletePluginApi", - "section": "def-public.QuerySuggestionGetFnArgs", - "text": "QuerySuggestionGetFnArgs" - } - ], - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts", - "deprecated": false - } - ], - "initialIsOpen": false - } - ], - "objects": [] - }, - "server": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "common": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - } -} \ No newline at end of file diff --git a/api_docs/data_autocomplete.mdx b/api_docs/data_autocomplete.mdx deleted file mode 100644 index 731ed5bb655e7..0000000000000 --- a/api_docs/data_autocomplete.mdx +++ /dev/null @@ -1,33 +0,0 @@ ---- -id: kibDataAutocompletePluginApi -slug: /kibana-dev-docs/api/data-autocomplete -title: "data.autocomplete" -image: https://source.unsplash.com/400x175/?github -summary: API docs for the data.autocomplete plugin -date: 2022-04-05 -tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.autocomplete'] -warning: 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. ---- -import dataAutocompleteObj from './data_autocomplete.devdocs.json'; - -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. - -Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) for questions regarding this plugin. - -**Code health stats** - -| Public API count | Any count | Items lacking comments | Missing exports | -|-------------------|-----------|------------------------|-----------------| -| 3426 | 40 | 2816 | 20 | - -## Client - -### Interfaces - - -### Enums - - -### Consts, variables and types - - diff --git a/api_docs/data_enhanced.mdx b/api_docs/data_enhanced.mdx index 03a5d056398c2..6eb3ac5dc589d 100644 --- a/api_docs/data_enhanced.mdx +++ b/api_docs/data_enhanced.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataEnhanced title: "dataEnhanced" image: https://source.unsplash.com/400x175/?github summary: API docs for the dataEnhanced plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataEnhanced'] warning: 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. --- diff --git a/api_docs/data_query.devdocs.json b/api_docs/data_query.devdocs.json index 4cccb61a48547..db5f5e436135d 100644 --- a/api_docs/data_query.devdocs.json +++ b/api_docs/data_query.devdocs.json @@ -863,6 +863,347 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "data", + "id": "def-public.QueryService", + "type": "Class", + "tags": [], + "label": "QueryService", + "description": [], + "path": "src/plugins/data/public/query/query_service.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-public.QueryService.filterManager", + "type": "Object", + "tags": [], + "label": "filterManager", + "description": [], + "signature": [ + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.FilterManager", + "text": "FilterManager" + } + ], + "path": "src/plugins/data/public/query/query_service.ts", + "deprecated": false + }, + { + "parentPluginId": "data", + "id": "def-public.QueryService.timefilter", + "type": "Object", + "tags": [], + "label": "timefilter", + "description": [], + "signature": [ + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.TimefilterSetup", + "text": "TimefilterSetup" + } + ], + "path": "src/plugins/data/public/query/query_service.ts", + "deprecated": false + }, + { + "parentPluginId": "data", + "id": "def-public.QueryService.queryStringManager", + "type": "Object", + "tags": [], + "label": "queryStringManager", + "description": [], + "signature": [ + "{ getDefaultQuery: () => { query: string; language: any; }; formatQuery: (query: string | ", + "Query", + " | undefined) => ", + "Query", + "; getUpdates$: () => ", + "Observable", + "<", + "Query", + ">; getQuery: () => ", + "Query", + "; setQuery: (query: ", + "Query", + ") => void; clearQuery: () => void; }" + ], + "path": "src/plugins/data/public/query/query_service.ts", + "deprecated": false + }, + { + "parentPluginId": "data", + "id": "def-public.QueryService.state$", + "type": "Object", + "tags": [], + "label": "state$", + "description": [], + "signature": [ + "Observable", + "<{ changes: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.QueryStateChange", + "text": "QueryStateChange" + }, + "; state: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.QueryState", + "text": "QueryState" + }, + "; }>" + ], + "path": "src/plugins/data/public/query/query_service.ts", + "deprecated": false + }, + { + "parentPluginId": "data", + "id": "def-public.QueryService.setup", + "type": "Function", + "tags": [], + "label": "setup", + "description": [], + "signature": [ + "({ storage, uiSettings, nowProvider }: QueryServiceSetupDependencies) => { filterManager: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.FilterManager", + "text": "FilterManager" + }, + "; timefilter: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.TimefilterSetup", + "text": "TimefilterSetup" + }, + "; queryString: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.QueryStringContract", + "text": "QueryStringContract" + }, + "; state$: ", + "Observable", + "<{ changes: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.QueryStateChange", + "text": "QueryStateChange" + }, + "; state: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.QueryState", + "text": "QueryState" + }, + "; }>; }" + ], + "path": "src/plugins/data/public/query/query_service.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-public.QueryService.setup.$1", + "type": "Object", + "tags": [], + "label": "{ storage, uiSettings, nowProvider }", + "description": [], + "signature": [ + "QueryServiceSetupDependencies" + ], + "path": "src/plugins/data/public/query/query_service.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-public.QueryService.start", + "type": "Function", + "tags": [], + "label": "start", + "description": [], + "signature": [ + "({ storage, uiSettings, http }: QueryServiceStartDependencies) => { addToQueryLog: (appName: string, { language, query }: ", + "Query", + ") => void; filterManager: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.FilterManager", + "text": "FilterManager" + }, + "; queryString: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.QueryStringContract", + "text": "QueryStringContract" + }, + "; savedQueries: { createQuery: (attributes: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.SavedQueryAttributes", + "text": "SavedQueryAttributes" + }, + ", { overwrite }?: { overwrite?: boolean | undefined; }) => Promise<", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.SavedQuery", + "text": "SavedQuery" + }, + ">; updateQuery: (id: string, attributes: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.SavedQueryAttributes", + "text": "SavedQueryAttributes" + }, + ") => Promise<", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.SavedQuery", + "text": "SavedQuery" + }, + ">; getAllSavedQueries: () => Promise<", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.SavedQuery", + "text": "SavedQuery" + }, + "[]>; findSavedQueries: (search?: string, perPage?: number, page?: number) => Promise<{ total: number; queries: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.SavedQuery", + "text": "SavedQuery" + }, + "[]; }>; getSavedQuery: (id: string) => Promise<", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.SavedQuery", + "text": "SavedQuery" + }, + ">; deleteSavedQuery: (id: string) => Promise<{}>; getSavedQueryCount: () => Promise; }; state$: ", + "Observable", + "<{ changes: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.QueryStateChange", + "text": "QueryStateChange" + }, + "; state: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.QueryState", + "text": "QueryState" + }, + "; }>; timefilter: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.TimefilterSetup", + "text": "TimefilterSetup" + }, + "; getEsQuery: (indexPattern: ", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.IndexPattern", + "text": "IndexPattern" + }, + ", timeRange?: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRange", + "text": "TimeRange" + }, + " | undefined) => { bool: ", + "BoolQuery", + "; }; }" + ], + "path": "src/plugins/data/public/query/query_service.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-public.QueryService.start.$1", + "type": "Object", + "tags": [], + "label": "{ storage, uiSettings, http }", + "description": [], + "signature": [ + "QueryServiceStartDependencies" + ], + "path": "src/plugins/data/public/query/query_service.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-public.QueryService.stop", + "type": "Function", + "tags": [], + "label": "stop", + "description": [], + "signature": [ + "() => void" + ], + "path": "src/plugins/data/public/query/query_service.ts", + "deprecated": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "data", "id": "def-public.TimeHistory", @@ -1118,7 +1459,13 @@ "text": "QueryState" }, "; }>; timefilter: ", - "TimefilterSetup", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.TimefilterSetup", + "text": "TimefilterSetup" + }, "; getEsQuery: (indexPattern: ", { "pluginId": "dataViews", @@ -1146,7 +1493,13 @@ "text": "FilterManager" }, "; timefilter: ", - "TimefilterSetup", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.TimefilterSetup", + "text": "TimefilterSetup" + }, "; queryString: ", { "pluginId": "data", @@ -1289,7 +1642,13 @@ "text": "QueryState" }, "; }>; timefilter: ", - "TimefilterSetup", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.TimefilterSetup", + "text": "TimefilterSetup" + }, "; getEsQuery: (indexPattern: ", { "pluginId": "dataViews", @@ -1317,7 +1676,13 @@ "text": "FilterManager" }, "; timefilter: ", - "TimefilterSetup", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.TimefilterSetup", + "text": "TimefilterSetup" + }, "; queryString: ", { "pluginId": "data", @@ -1780,8 +2145,8 @@ "pluginId": "dataViews", "scope": "common", "docId": "kibDataViewsPluginApi", - "section": "def-common.IIndexPattern", - "text": "IIndexPattern" + "section": "def-common.DataView", + "text": "DataView" }, "[]) => string" ], @@ -1814,8 +2179,8 @@ "pluginId": "dataViews", "scope": "common", "docId": "kibDataViewsPluginApi", - "section": "def-common.IIndexPattern", - "text": "IIndexPattern" + "section": "def-common.DataView", + "text": "DataView" }, "[]" ], @@ -1842,16 +2207,16 @@ "pluginId": "dataViews", "scope": "common", "docId": "kibDataViewsPluginApi", - "section": "def-common.IIndexPattern", - "text": "IIndexPattern" + "section": "def-common.DataView", + "text": "DataView" }, "[]) => ", { "pluginId": "dataViews", "scope": "common", "docId": "kibDataViewsPluginApi", - "section": "def-common.IIndexPattern", - "text": "IIndexPattern" + "section": "def-common.DataView", + "text": "DataView" }, " | undefined" ], @@ -1884,8 +2249,8 @@ "pluginId": "dataViews", "scope": "common", "docId": "kibDataViewsPluginApi", - "section": "def-common.IIndexPattern", - "text": "IIndexPattern" + "section": "def-common.DataView", + "text": "DataView" }, "[]" ], @@ -2036,7 +2401,13 @@ "text": "QueryState" }, "; }>; timefilter: ", - "TimefilterSetup", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.TimefilterSetup", + "text": "TimefilterSetup" + }, "; getEsQuery: (indexPattern: ", { "pluginId": "dataViews", @@ -2064,7 +2435,13 @@ "text": "FilterManager" }, "; timefilter: ", - "TimefilterSetup", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.TimefilterSetup", + "text": "TimefilterSetup" + }, "; queryString: ", { "pluginId": "data", @@ -2205,7 +2582,13 @@ "text": "QueryState" }, "; }>; timefilter: ", - "TimefilterSetup", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.TimefilterSetup", + "text": "TimefilterSetup" + }, "; getEsQuery: (indexPattern: ", { "pluginId": "dataViews", @@ -2233,7 +2616,13 @@ "text": "FilterManager" }, "; timefilter: ", - "TimefilterSetup", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.TimefilterSetup", + "text": "TimefilterSetup" + }, "; queryString: ", { "pluginId": "data", @@ -2740,6 +3129,211 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "data", + "id": "def-public.TimefilterSetup", + "type": "Interface", + "tags": [], + "label": "TimefilterSetup", + "description": [], + "path": "src/plugins/data/public/query/timefilter/timefilter_service.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-public.TimefilterSetup.timefilter", + "type": "Object", + "tags": [], + "label": "timefilter", + "description": [], + "signature": [ + "{ isTimeRangeSelectorEnabled: () => boolean; isAutoRefreshSelectorEnabled: () => boolean; isTimeTouched: () => boolean; isRefreshIntervalTouched: () => boolean; getEnabledUpdated$: () => ", + "Observable", + "; getTimeUpdate$: () => ", + "Observable", + "; getRefreshIntervalUpdate$: () => ", + "Observable", + "; getAutoRefreshFetch$: () => ", + "Observable", + "<", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.AutoRefreshDoneFn", + "text": "AutoRefreshDoneFn" + }, + ">; getFetch$: () => ", + "Observable", + "; getTime: () => ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRange", + "text": "TimeRange" + }, + "; getAbsoluteTime: () => ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRange", + "text": "TimeRange" + }, + "; setTime: (time: ", + "InputTimeRange", + ") => void; getRefreshInterval: () => ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.RefreshInterval", + "text": "RefreshInterval" + }, + "; setRefreshInterval: (refreshInterval: Partial<", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.RefreshInterval", + "text": "RefreshInterval" + }, + ">) => void; createFilter: (indexPattern: ", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.IIndexPattern", + "text": "IIndexPattern" + }, + ", timeRange?: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRange", + "text": "TimeRange" + }, + " | undefined) => ", + "RangeFilter", + " | ", + "ScriptedRangeFilter", + " | MatchAllRangeFilter | undefined; createRelativeFilter: (indexPattern: ", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.IIndexPattern", + "text": "IIndexPattern" + }, + ", timeRange?: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRange", + "text": "TimeRange" + }, + " | undefined) => ", + "RangeFilter", + " | ", + "ScriptedRangeFilter", + " | MatchAllRangeFilter | undefined; getBounds: () => ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRangeBounds", + "text": "TimeRangeBounds" + }, + "; calculateBounds: (timeRange: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRange", + "text": "TimeRange" + }, + ") => ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRangeBounds", + "text": "TimeRangeBounds" + }, + "; getActiveBounds: () => ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRangeBounds", + "text": "TimeRangeBounds" + }, + " | undefined; enableTimeRangeSelector: () => void; disableTimeRangeSelector: () => void; enableAutoRefreshSelector: () => void; disableAutoRefreshSelector: () => void; getTimeDefaults: () => ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRange", + "text": "TimeRange" + }, + "; getRefreshIntervalDefaults: () => ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.RefreshInterval", + "text": "RefreshInterval" + }, + "; }" + ], + "path": "src/plugins/data/public/query/timefilter/timefilter_service.ts", + "deprecated": false + }, + { + "parentPluginId": "data", + "id": "def-public.TimefilterSetup.history", + "type": "Object", + "tags": [], + "label": "history", + "description": [], + "signature": [ + "{ get$: () => ", + "Observable", + "<", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRange", + "text": "TimeRange" + }, + "[]>; add: (time: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRange", + "text": "TimeRange" + }, + ") => void; get: () => ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRange", + "text": "TimeRange" + }, + "[]; }" + ], + "path": "src/plugins/data/public/query/timefilter/timefilter_service.ts", + "deprecated": false + } + ], + "initialIsOpen": false } ], "enums": [], @@ -2777,7 +3371,13 @@ "text": "FilterManager" }, "; timefilter: ", - "TimefilterSetup", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.TimefilterSetup", + "text": "TimefilterSetup" + }, "; queryString: ", { "pluginId": "data", @@ -2911,7 +3511,13 @@ "text": "QueryState" }, "; }>; timefilter: ", - "TimefilterSetup", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.TimefilterSetup", + "text": "TimefilterSetup" + }, "; getEsQuery: (indexPattern: ", { "pluginId": "dataViews", @@ -3003,9 +3609,9 @@ "Observable", "; getTimeUpdate$: () => ", "Observable", - "; getRefreshIntervalUpdate$: () => ", + "; getRefreshIntervalUpdate$: () => ", "Observable", - "; getAutoRefreshFetch$: () => ", + "; getAutoRefreshFetch$: () => ", "Observable", "<", { @@ -3017,7 +3623,7 @@ }, ">; getFetch$: () => ", "Observable", - "; getTime: () => ", + "; getTime: () => ", { "pluginId": "data", "scope": "common", diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index c4ef7a0a5710b..76b3bbc03583e 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github summary: API docs for the data.query plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3426 | 40 | 2816 | 20 | +| 3414 | 38 | 2802 | 18 | ## Client diff --git a/api_docs/data_search.devdocs.json b/api_docs/data_search.devdocs.json index 3519424309902..733c32b8df845 100644 --- a/api_docs/data_search.devdocs.json +++ b/api_docs/data_search.devdocs.json @@ -1474,55 +1474,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "data", - "id": "def-server.DataRequestHandlerContext", - "type": "Interface", - "tags": [], - "label": "DataRequestHandlerContext", - "description": [], - "signature": [ - { - "pluginId": "data", - "scope": "server", - "docId": "kibDataSearchPluginApi", - "section": "def-server.DataRequestHandlerContext", - "text": "DataRequestHandlerContext" - }, - " extends ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - } - ], - "path": "src/plugins/data/server/search/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "data", - "id": "def-server.DataRequestHandlerContext.search", - "type": "Object", - "tags": [], - "label": "search", - "description": [], - "signature": [ - { - "pluginId": "data", - "scope": "server", - "docId": "kibDataSearchPluginApi", - "section": "def-server.IScopedSearchClient", - "text": "IScopedSearchClient" - } - ], - "path": "src/plugins/data/server/search/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "data", "id": "def-server.IScopedSearchClient", @@ -2636,6 +2587,35 @@ ], "enums": [], "misc": [ + { + "parentPluginId": "data", + "id": "def-server.DataRequestHandlerContext", + "type": "Type", + "tags": [], + "label": "DataRequestHandlerContext", + "description": [], + "signature": [ + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " & { search: Promise<", + { + "pluginId": "data", + "scope": "server", + "docId": "kibDataSearchPluginApi", + "section": "def-server.IScopedSearchClient", + "text": "IScopedSearchClient" + }, + ">; }" + ], + "path": "src/plugins/data/server/search/types.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "data", "id": "def-server.SearchRequestHandlerContext", @@ -7599,6 +7579,117 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "data", + "id": "def-common.MultiFieldKey", + "type": "Class", + "tags": [], + "label": "MultiFieldKey", + "description": [], + "path": "src/plugins/data/common/search/aggs/buckets/multi_field_key.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-common.MultiFieldKey.id", + "type": "string", + "tags": [], + "label": "[id]", + "description": [], + "path": "src/plugins/data/common/search/aggs/buckets/multi_field_key.ts", + "deprecated": false + }, + { + "parentPluginId": "data", + "id": "def-common.MultiFieldKey.keys", + "type": "Array", + "tags": [], + "label": "keys", + "description": [], + "signature": [ + "string[]" + ], + "path": "src/plugins/data/common/search/aggs/buckets/multi_field_key.ts", + "deprecated": false + }, + { + "parentPluginId": "data", + "id": "def-common.MultiFieldKey.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "src/plugins/data/common/search/aggs/buckets/multi_field_key.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-common.MultiFieldKey.Unnamed.$1", + "type": "Unknown", + "tags": [], + "label": "bucket", + "description": [], + "signature": [ + "unknown" + ], + "path": "src/plugins/data/common/search/aggs/buckets/multi_field_key.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-common.MultiFieldKey.idBucket", + "type": "Function", + "tags": [], + "label": "idBucket", + "description": [], + "signature": [ + "(bucket: unknown) => string" + ], + "path": "src/plugins/data/common/search/aggs/buckets/multi_field_key.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-common.MultiFieldKey.idBucket.$1", + "type": "Unknown", + "tags": [], + "label": "bucket", + "description": [], + "signature": [ + "unknown" + ], + "path": "src/plugins/data/common/search/aggs/buckets/multi_field_key.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-common.MultiFieldKey.toString", + "type": "Function", + "tags": [], + "label": "toString", + "description": [], + "signature": [ + "() => string" + ], + "path": "src/plugins/data/common/search/aggs/buckets/multi_field_key.ts", + "deprecated": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "data", "id": "def-common.OptionedParamType", @@ -17986,6 +18077,19 @@ "path": "src/plugins/data/common/search/aggs/buckets/multi_terms.ts", "deprecated": false }, + { + "parentPluginId": "data", + "id": "def-common.AggParamsMultiTerms.shardSize", + "type": "number", + "tags": [], + "label": "shardSize", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "src/plugins/data/common/search/aggs/buckets/multi_terms.ts", + "deprecated": false + }, { "parentPluginId": "data", "id": "def-common.AggParamsMultiTerms.otherBucket", @@ -18778,6 +18882,19 @@ "path": "src/plugins/data/common/search/aggs/buckets/terms.ts", "deprecated": false }, + { + "parentPluginId": "data", + "id": "def-common.AggParamsTerms.shardSize", + "type": "number", + "tags": [], + "label": "shardSize", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "src/plugins/data/common/search/aggs/buckets/terms.ts", + "deprecated": false + }, { "parentPluginId": "data", "id": "def-common.AggParamsTerms.missingBucket", @@ -24348,7 +24465,7 @@ "label": "AggConfigOptions", "description": [], "signature": [ - "{ type: ", + "{ id?: string | undefined; type: ", { "pluginId": "data", "scope": "common", @@ -24356,7 +24473,7 @@ "section": "def-common.IAggType", "text": "IAggType" }, - "; id?: string | undefined; enabled?: boolean | undefined; params?: {} | ", + "; enabled?: boolean | undefined; params?: {} | ", "SerializableRecord", " | undefined; schema?: string | undefined; }" ], @@ -25058,7 +25175,7 @@ "label": "CreateAggConfigParams", "description": [], "signature": [ - "{ type: string | ", + "{ id?: string | undefined; type: string | ", { "pluginId": "data", "scope": "common", @@ -25066,7 +25183,7 @@ "section": "def-common.IAggType", "text": "IAggType" }, - "; id?: string | undefined; enabled?: boolean | undefined; params?: {} | ", + "; enabled?: boolean | undefined; params?: {} | ", "SerializableRecord", " | undefined; schema?: string | undefined; }" ], diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 608be3044f659..ae24c35139513 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github summary: API docs for the data.search plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3426 | 40 | 2816 | 20 | +| 3414 | 38 | 2802 | 18 | ## Client diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 8a11bfeccdeab..01bc3bf40b471 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github summary: API docs for the dataViewEditor plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] warning: 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. --- diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index 7a72bfa2e79d6..27b12a115195c 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github summary: API docs for the dataViewFieldEditor plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor'] warning: 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. --- diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx index 1d26cc3a65daa..b1bfe293c7c92 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github summary: API docs for the dataViewManagement plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] warning: 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. --- diff --git a/api_docs/data_views.devdocs.json b/api_docs/data_views.devdocs.json index 5ad402626c258..327d6e36ba3f4 100644 --- a/api_docs/data_views.devdocs.json +++ b/api_docs/data_views.devdocs.json @@ -4107,26 +4107,6 @@ "plugin": "dataViewEditor", "path": "src/plugins/data_view_editor/public/open_editor.tsx" }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/hooks/update_kuery_string.ts" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/hooks/update_kuery_string.ts" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx" - }, { "plugin": "maps", "path": "x-pack/plugins/maps/public/classes/tooltips/es_tooltip_property.test.ts" @@ -7104,14 +7084,6 @@ "plugin": "data", "path": "src/plugins/data/public/search/search_service.ts" }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/fetch_index_patterns.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/fetch_index_patterns.ts" - }, { "plugin": "unifiedSearch", "path": "src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx" @@ -8116,8 +8088,8 @@ "pluginId": "dataViews", "scope": "common", "docId": "kibDataViewsPluginApi", - "section": "def-common.IFieldType", - "text": "IFieldType" + "section": "def-common.FieldSpec", + "text": "FieldSpec" }, " | undefined" ], @@ -15771,26 +15743,6 @@ "plugin": "dataViewEditor", "path": "src/plugins/data_view_editor/public/open_editor.tsx" }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/hooks/update_kuery_string.ts" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/hooks/update_kuery_string.ts" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx" - }, - { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx" - }, { "plugin": "maps", "path": "x-pack/plugins/maps/public/classes/tooltips/es_tooltip_property.test.ts" @@ -18995,89 +18947,61 @@ "plugin": "data", "path": "src/plugins/data/public/query/filter_manager/lib/generate_filters.ts" }, - { - "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts" - }, - { - "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts" - }, - { - "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts" - }, - { - "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts" - }, - { - "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts" - }, - { - "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts" - }, { "plugin": "data", "path": "src/plugins/data/public/index.ts" }, { "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/kql_query_suggestion/field.tsx" - }, - { - "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/kql_query_suggestion/field.tsx" + "path": "src/plugins/data/public/actions/filters/create_filters_from_range_select.ts" }, { "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/kql_query_suggestion/field.tsx" + "path": "src/plugins/data/public/actions/filters/create_filters_from_range_select.ts" }, { - "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/kql_query_suggestion/value.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx" }, { - "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/kql_query_suggestion/value.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx" }, { - "plugin": "data", - "path": "src/plugins/data/public/actions/filters/create_filters_from_range_select.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx" }, { - "plugin": "data", - "path": "src/plugins/data/public/actions/filters/create_filters_from_range_select.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts" }, { - "plugin": "data", - "path": "src/plugins/data/server/autocomplete/terms_enum.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts" }, { - "plugin": "data", - "path": "src/plugins/data/server/autocomplete/terms_enum.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts" }, { - "plugin": "data", - "path": "src/plugins/data/server/autocomplete/terms_agg.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts" }, { - "plugin": "data", - "path": "src/plugins/data/server/autocomplete/terms_agg.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" }, { - "plugin": "data", - "path": "src/plugins/data/server/autocomplete/terms_agg.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" }, { - "plugin": "data", - "path": "src/plugins/data/server/autocomplete/terms_agg.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" }, { - "plugin": "data", - "path": "src/plugins/data/server/index.ts" + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" }, { "plugin": "unifiedSearch", @@ -19113,19 +19037,27 @@ }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" + "path": "src/plugins/unified_search/server/autocomplete/terms_enum.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" + "path": "src/plugins/unified_search/server/autocomplete/terms_enum.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" + "path": "src/plugins/unified_search/server/autocomplete/terms_agg.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" + "path": "src/plugins/unified_search/server/autocomplete/terms_agg.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/server/autocomplete/terms_agg.ts" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/server/autocomplete/terms_agg.ts" }, { "plugin": "data", @@ -19174,6 +19106,22 @@ { "plugin": "unifiedSearch", "path": "src/plugins/unified_search/public/filter_bar/filter_editor/value_input_type.tsx" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" + }, + { + "plugin": "unifiedSearch", + "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" } ], "children": [ @@ -19511,30 +19459,6 @@ "plugin": "data", "path": "src/plugins/data/public/query/filter_manager/lib/generate_filters.ts" }, - { - "plugin": "data", - "path": "src/plugins/data/public/query/filter_manager/lib/get_index_pattern_from_filter.ts" - }, - { - "plugin": "data", - "path": "src/plugins/data/public/query/filter_manager/lib/get_index_pattern_from_filter.ts" - }, - { - "plugin": "data", - "path": "src/plugins/data/public/query/filter_manager/lib/get_index_pattern_from_filter.ts" - }, - { - "plugin": "data", - "path": "src/plugins/data/public/query/filter_manager/lib/get_display_value.ts" - }, - { - "plugin": "data", - "path": "src/plugins/data/public/query/filter_manager/lib/get_display_value.ts" - }, - { - "plugin": "data", - "path": "src/plugins/data/public/query/filter_manager/lib/get_display_value.ts" - }, { "plugin": "data", "path": "src/plugins/data/public/query/timefilter/timefilter.ts" @@ -19547,189 +19471,37 @@ "plugin": "data", "path": "src/plugins/data/public/query/timefilter/timefilter.ts" }, - { - "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts" - }, - { - "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts" - }, - { - "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts" - }, - { - "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts" - }, - { - "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts" - }, { "plugin": "data", "path": "src/plugins/data/public/index.ts" }, - { - "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/kql_query_suggestion/value.ts" - }, - { - "plugin": "data", - "path": "src/plugins/data/public/autocomplete/providers/kql_query_suggestion/value.ts" - }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" }, { "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/alerts/components/param_details_form/use_derived_index_pattern.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/alerts/components/param_details_form/use_derived_index_pattern.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/components/kuery_bar/with_kuery_autocompletion.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/components/kuery_bar/with_kuery_autocompletion.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/components/kuery_bar/index.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/components/kuery_bar/index.tsx" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/lib/kuery.ts" - }, - { - "plugin": "monitoring", - "path": "x-pack/plugins/monitoring/public/lib/kuery.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_item.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_item.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_bar.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/filter_bar/filter_bar.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/search_bar/search_bar.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/search_bar/search_bar.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/apply_filters/apply_filter_popover_content.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/apply_filters/apply_filter_popover_content.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/apply_filters/apply_filters_popover.tsx" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/apply_filters/apply_filters_popover.tsx" - }, - { - "plugin": "stackAlerts", - "path": "x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx" - }, - { - "plugin": "stackAlerts", - "path": "x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx" + "path": "src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts" }, { "plugin": "data", @@ -22132,14 +21904,6 @@ "plugin": "data", "path": "src/plugins/data/public/search/search_service.ts" }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/fetch_index_patterns.ts" - }, - { - "plugin": "unifiedSearch", - "path": "src/plugins/unified_search/public/query_string_input/fetch_index_patterns.ts" - }, { "plugin": "unifiedSearch", "path": "src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx" @@ -22286,7 +22050,7 @@ "signature": [ "Pick<", "Toast", - ", \"children\" | \"onClick\" | \"onChange\" | \"color\" | \"onKeyDown\" | \"className\" | \"security\" | \"defaultValue\" | \"hidden\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"style\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"role\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-label\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"toastLifeTimeMs\" | \"iconType\" | \"onClose\" | \"data-test-subj\"> & { title?: string | ", + ", \"children\" | \"color\" | \"className\" | \"onChange\" | \"onKeyDown\" | \"onClick\" | \"security\" | \"defaultValue\" | \"hidden\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"style\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"role\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-label\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"toastLifeTimeMs\" | \"iconType\" | \"onClose\" | \"data-test-subj\"> & { title?: string | ", { "pluginId": "core", "scope": "public", diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index b595999839c2c..24669a558c24f 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github summary: API docs for the dataViews plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] warning: 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. --- diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index 7bc87154c95ff..dba30154fefa5 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github summary: API docs for the dataVisualizer plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] warning: 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. --- diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index 8db17bbe8bee3..a9ce0254cea52 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -3,7 +3,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API summary: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. --- @@ -15,40 +15,39 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | ---------------|-----------|-----------| | | dataViews, maps, data | - | | | dataViews, unifiedSearch, maps, data | - | -| | dataViews, discover, ux, savedObjects, dataViewEditor, uptime, maps, visDefaultEditor, data | - | +| | dataViews, discover, ux, savedObjects, dataViewEditor, maps, visDefaultEditor, data | - | | | dataViews, dataViewEditor, maps, visDefaultEditor, data | - | -| | dataViews, unifiedSearch, monitoring, stackAlerts | - | +| | dataViews, unifiedSearch | - | | | dataViews, canvas | - | -| | dataViews, unifiedSearch, monitoring, stackAlerts, data | - | +| | dataViews, unifiedSearch, data | - | | | dataViews, canvas, data | - | | | dataViews, unifiedSearch, maps, data | - | | | dataViews, dataViewEditor, maps, visDefaultEditor, data | - | | | dataViews, maps, data | - | | | dataViews | - | -| | dataViews, discover, ux, savedObjects, dataViewEditor, uptime, maps, visDefaultEditor, data | - | +| | dataViews, discover, ux, savedObjects, dataViewEditor, maps, visDefaultEditor, data | - | | | dataViews, maps | - | | | dataViewManagement, dataViews | - | | | visTypeTimeseries, graph, dataViewManagement, dataViews | - | | | dataViews, dataViewManagement | - | | | dataViews, canvas | - | | | dataViews, dataViewEditor, maps, visDefaultEditor | - | -| | dataViews, discover, ux, savedObjects, dataViewEditor, uptime, maps, visDefaultEditor | - | +| | dataViews, discover, ux, savedObjects, dataViewEditor, maps, visDefaultEditor | - | | | dataViews, maps | - | | | dataViews, maps | - | | | dataViewManagement, dataViews | - | | | visTypeTimeseries, graph, dataViewManagement, dataViews | - | | | dataViews, dataViewManagement | - | -| | unifiedSearch, discover, maps, infra, graph, monitoring, securitySolution, stackAlerts, uptime, inputControlVis, savedObjects | - | +| | unifiedSearch, discover, maps, infra, graph, securitySolution, stackAlerts, inputControlVis, savedObjects | - | | | maps | - | | | data, infra, maps | - | -| | dashboard, maps | - | | | discover | - | | | discover | - | | | data, discover, embeddable | - | | | advancedSettings, discover | - | | | advancedSettings, discover | - | -| | esUiShared, home, spaces, fleet, visualizations, lens, observability, dataEnhanced, ml, apm, cloudSecurityPosture, indexLifecycleManagement, upgradeAssistant, uptime, ux, kibanaOverview, savedObjectsManagement | - | -| | dashboard, lens, stackAlerts, visTypeTable, visTypeTimeseries, visTypeXy, visTypeVislib, expressionPartitionVis | - | +| | management, observability, infra, apm, cloudSecurityPosture, enterpriseSearch, securitySolution, synthetics, ux, kibanaOverview | - | +| | esUiShared, home, spaces, fleet, visualizations, lens, observability, dataEnhanced, ml, apm, cloudSecurityPosture, indexLifecycleManagement, synthetics, upgradeAssistant, ux, kibanaOverview, savedObjectsManagement | - | | | canvas, visTypeXy | - | | | canvas | - | | | canvas | - | @@ -59,41 +58,40 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | canvas | - | | | canvas | - | | | canvas, visTypeXy | - | +| | management, spaces, observability, ml, canvas, cloudSecurityPosture, enterpriseSearch, osquery, securitySolution, kibanaOverview | - | +| | dashboard, lens, stackAlerts, visTypeTable, visTypeTimeseries, visTypeXy, visTypeVislib, expressionPartitionVis | - | | | visTypeTimeseries, graph, dataViewManagement | - | | | encryptedSavedObjects, actions, cloud, ml, dataEnhanced, logstash, securitySolution | - | +| | dashboard | - | | | visTypeTimeseries | - | | | dataViewManagement | - | | | dataViewManagement | - | +| | actions, ml, enterpriseSearch, savedObjectsTagging | - | | | spaces, savedObjectsManagement | - | | | spaces, savedObjectsManagement | - | -| | actions, ml, enterpriseSearch, savedObjectsTagging | - | | | visTypeGauge | - | | | visTypePie | - | | | visTypePie | - | | | actions, alerting | - | | | console | - | | | unifiedSearch | 8.1 | +| | unifiedSearch, dataEnhanced | 8.1 | | | unifiedSearch, discover, dashboard, urlDrilldown, stackAlerts | 8.1 | | | unifiedSearch | 8.1 | | | unifiedSearch | 8.1 | | | unifiedSearch, discover, dashboard, urlDrilldown, stackAlerts | 8.1 | +| | unifiedSearch, dataEnhanced | 8.1 | | | unifiedSearch, discover, dashboard, urlDrilldown, stackAlerts | 8.1 | +| | unifiedSearch, dataEnhanced | 8.1 | | | discover, stackAlerts, inputControlVis | 8.1 | | | discover, stackAlerts, inputControlVis | 8.1 | -| | dataEnhanced | 8.1 | | | dataEnhanced | 8.1 | -| | dataEnhanced | 8.1 | -| | dataEnhanced | 8.1 | | | apm | 8.1 | | | dataViews, unifiedSearch | 8.2 | | | dataViews, unifiedSearch, data | 8.2 | -| | dataViews, unifiedSearch | 8.2 | -| | visualizations, dashboard, maps, graph | 8.8.0 | | | visualizations, dashboard, lens, maps, ml, securitySolution, security | 8.8.0 | | | lens, dashboard, maps | 8.8.0 | -| | embeddable, presentationUtil, discover, dashboard, graph | 8.8.0 | -| | monitoring, visTypeVega | 8.8.0 | -| | monitoring, kibanaUsageCollection | 8.8.0 | +| | embeddable, discover, presentationUtil, dashboard, graph | 8.8.0 | | | spaces, security, actions, alerting, ml, remoteClusters, graph, indexLifecycleManagement, mapsEms, painlessLab, rollup, searchprofiler, snapshotRestore, transform, upgradeAssistant | 8.8.0 | | | apm, security, securitySolution | 8.8.0 | | | apm, security, securitySolution | 8.8.0 | @@ -102,14 +100,16 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | dashboard | 8.8.0 | | | cloud, apm | 8.8.0 | | | security, licenseManagement, ml, apm, crossClusterReplication, logstash, painlessLab, searchprofiler, watcher | 8.8.0 | +| | management, fleet, security, kibanaOverview | 8.8.0 | | | spaces, security, alerting | 8.8.0 | | | security, fleet | 8.8.0 | | | security, fleet | 8.8.0 | | | security, fleet | 8.8.0 | -| | management, fleet, security, kibanaOverview | 8.8.0 | | | security | 8.8.0 | | | mapsEms | 8.8.0 | | | visTypeVega | 8.8.0 | +| | monitoring, visTypeVega | 8.8.0 | +| | monitoring, kibanaUsageCollection | 8.8.0 | | | ml | 8.8.0 Note to maintainers: when looking at usages, mind that typical use could be inside a `catch` block, @@ -201,6 +201,7 @@ Safe to remove. | | expressions | | | expressions | | | expressions | +| | savedObjects | | | licensing | | | licensing | | | licensing | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 0005cd3853b72..bc9d1dac3a021 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -3,7 +3,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin summary: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. --- @@ -45,6 +45,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | ---------------|-----------|-----------| | | [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx#:~:text=esKuery), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx#:~:text=esKuery), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx#:~:text=esKuery) | 8.1 | | | [plugin.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/public/plugin.ts#:~:text=environment) | 8.8.0 | +| | [no_data_config.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/public/components/routing/templates/no_data_config.ts#:~:text=KibanaPageTemplateProps), [no_data_config.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/public/components/routing/templates/no_data_config.ts#:~:text=KibanaPageTemplateProps), [apm_main_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/public/components/routing/templates/apm_main_template.tsx#:~:text=KibanaPageTemplateProps), [apm_main_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/public/components/routing/templates/apm_main_template.tsx#:~:text=KibanaPageTemplateProps), [service_group_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/public/components/routing/templates/service_group_template.tsx#:~:text=KibanaPageTemplateProps), [service_group_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/public/components/routing/templates/service_group_template.tsx#:~:text=KibanaPageTemplateProps) | - | | | [app_root.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/public/components/routing/app_root.tsx#:~:text=RedirectAppLinks), [app_root.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/public/components/routing/app_root.tsx#:~:text=RedirectAppLinks), [app_root.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/public/components/routing/app_root.tsx#:~:text=RedirectAppLinks) | - | | | [license_check.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/common/license_check.test.ts#:~:text=mode), [license_check.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/common/license_check.test.ts#:~:text=mode), [license_check.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/common/license_check.test.ts#:~:text=mode), [license_check.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/common/license_check.test.ts#:~:text=mode), [license_check.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/common/license_check.test.ts#:~:text=mode), [license_check.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/common/license_check.test.ts#:~:text=mode), [license_check.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/common/license_check.test.ts#:~:text=mode), [license_check.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/common/license_check.test.ts#:~:text=mode), [license_check.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/common/license_check.test.ts#:~:text=mode), [license_check.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/common/license_check.test.ts#:~:text=mode)+ 2 more | 8.8.0 | | | [license_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/apm/public/context/license/license_context.tsx#:~:text=license%24) | 8.8.0 | @@ -59,16 +60,17 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [es_service.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/lib/es_service.ts#:~:text=IndexPatternAttributes), [es_service.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/lib/es_service.ts#:~:text=IndexPatternAttributes), [es_service.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/lib/es_service.ts#:~:text=IndexPatternAttributes) | - | | | [es_service.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/lib/es_service.ts#:~:text=IndexPatternAttributes), [es_service.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/lib/es_service.ts#:~:text=IndexPatternAttributes), [es_service.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/lib/es_service.ts#:~:text=IndexPatternAttributes), [es_service.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/lib/es_service.ts#:~:text=IndexPatternAttributes), [es_service.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/lib/es_service.ts#:~:text=IndexPatternAttributes), [es_service.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/lib/es_service.ts#:~:text=IndexPatternAttributes) | - | | | [es_service.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/lib/es_service.ts#:~:text=IndexPatternAttributes), [es_service.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/lib/es_service.ts#:~:text=IndexPatternAttributes), [es_service.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/lib/es_service.ts#:~:text=IndexPatternAttributes) | - | -| | [embeddable.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts#:~:text=context), [filters.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/common/functions/filters.ts#:~:text=context), [escount.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/escount.ts#:~:text=context), [esdocs.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/esdocs.ts#:~:text=context), [essql.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/essql.ts#:~:text=context), [neq.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/common/neq.ts#:~:text=context), [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts#:~:text=context), [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts#:~:text=context) | - | +| | [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts#:~:text=context), [embeddable.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts#:~:text=context), [esdocs.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/esdocs.ts#:~:text=context), [essql.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/essql.ts#:~:text=context), [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts#:~:text=context), [filters.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/common/functions/filters.ts#:~:text=context), [escount.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/escount.ts#:~:text=context), [neq.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/common/neq.ts#:~:text=context) | - | | | [setup_expressions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/setup_expressions.ts#:~:text=getFunction) | - | | | [application.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/application.tsx#:~:text=getFunctions), [functions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/server/routes/functions/functions.ts#:~:text=getFunctions), [functions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/server/routes/functions/functions.ts#:~:text=getFunctions), [functions.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/server/routes/functions/functions.test.ts#:~:text=getFunctions) | - | | | [setup_expressions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/setup_expressions.ts#:~:text=getTypes), [application.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/application.tsx#:~:text=getTypes), [functions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/server/routes/functions/functions.ts#:~:text=getTypes) | - | | | [state.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/types/state.ts#:~:text=Render), [state.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/types/state.ts#:~:text=Render), [markdown.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts#:~:text=Render), [markdown.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts#:~:text=Render), [timefilterControl.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/common/timefilterControl.ts#:~:text=Render), [timefilterControl.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/common/timefilterControl.ts#:~:text=Render), [pie.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/functions/pie.ts#:~:text=Render), [pie.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/functions/pie.ts#:~:text=Render), [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/functions/plot/index.ts#:~:text=Render), [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/functions/plot/index.ts#:~:text=Render)+ 2 more | - | -| | [embeddable.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts#:~:text=context), [filters.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/common/functions/filters.ts#:~:text=context), [escount.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/escount.ts#:~:text=context), [esdocs.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/esdocs.ts#:~:text=context), [essql.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/essql.ts#:~:text=context), [neq.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/common/neq.ts#:~:text=context), [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts#:~:text=context), [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts#:~:text=context) | - | +| | [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts#:~:text=context), [embeddable.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts#:~:text=context), [esdocs.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/esdocs.ts#:~:text=context), [essql.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/essql.ts#:~:text=context), [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts#:~:text=context), [filters.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/common/functions/filters.ts#:~:text=context), [escount.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/escount.ts#:~:text=context), [neq.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/common/neq.ts#:~:text=context) | - | | | [setup_expressions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/setup_expressions.ts#:~:text=getFunction) | - | | | [application.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/application.tsx#:~:text=getFunctions), [functions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/server/routes/functions/functions.ts#:~:text=getFunctions), [functions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/server/routes/functions/functions.ts#:~:text=getFunctions), [functions.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/server/routes/functions/functions.test.ts#:~:text=getFunctions) | - | | | [setup_expressions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/setup_expressions.ts#:~:text=getTypes), [application.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/application.tsx#:~:text=getTypes), [functions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/server/routes/functions/functions.ts#:~:text=getTypes) | - | -| | [embeddable.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts#:~:text=context), [filters.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/common/functions/filters.ts#:~:text=context), [escount.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/escount.ts#:~:text=context), [esdocs.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/esdocs.ts#:~:text=context), [essql.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/essql.ts#:~:text=context), [neq.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/common/neq.ts#:~:text=context), [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts#:~:text=context), [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts#:~:text=context) | - | +| | [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts#:~:text=context), [embeddable.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts#:~:text=context), [esdocs.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/esdocs.ts#:~:text=context), [essql.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/essql.ts#:~:text=context), [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts#:~:text=context), [filters.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/common/functions/filters.ts#:~:text=context), [escount.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/escount.ts#:~:text=context), [neq.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/canvas_plugin_src/functions/common/neq.ts#:~:text=context) | - | +| | [home.component.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/components/home/home.component.tsx#:~:text=KibanaPageTemplate), [home.component.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/components/home/home.component.tsx#:~:text=KibanaPageTemplate), [home.component.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/canvas/public/components/home/home.component.tsx#:~:text=KibanaPageTemplate) | - | @@ -85,6 +87,8 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| +| | [csp_page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx#:~:text=KibanaPageTemplateProps), [csp_page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx#:~:text=KibanaPageTemplateProps), [csp_page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx#:~:text=KibanaPageTemplateProps), [csp_page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx#:~:text=KibanaPageTemplateProps), [csp_page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx#:~:text=KibanaPageTemplateProps), [csp_page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx#:~:text=KibanaPageTemplateProps), [csp_page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx#:~:text=KibanaPageTemplateProps), [csp_page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx#:~:text=KibanaPageTemplateProps), [compliance_dashboard.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx#:~:text=KibanaPageTemplateProps), [compliance_dashboard.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx#:~:text=KibanaPageTemplateProps)+ 2 more | - | +| | [csp_page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx#:~:text=KibanaPageTemplate), [csp_page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx#:~:text=KibanaPageTemplate), [csp_page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx#:~:text=KibanaPageTemplate) | - | | | [app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/cloud_security_posture/public/application/app.tsx#:~:text=RedirectAppLinks), [app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/cloud_security_posture/public/application/app.tsx#:~:text=RedirectAppLinks), [app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/cloud_security_posture/public/application/app.tsx#:~:text=RedirectAppLinks) | - | @@ -117,7 +121,6 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [saved_objects.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/services/saved_objects.ts#:~:text=SavedObjectSaveModal), [save_modal.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/top_nav/save_modal.tsx#:~:text=SavedObjectSaveModal), [save_modal.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/top_nav/save_modal.tsx#:~:text=SavedObjectSaveModal) | 8.8.0 | | | [saved_object_loader.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/services/saved_object_loader.ts#:~:text=SavedObject), [saved_object_loader.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/services/saved_object_loader.ts#:~:text=SavedObject), [saved_object_loader.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/services/saved_object_loader.ts#:~:text=SavedObject), [saved_objects.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/services/saved_objects.ts#:~:text=SavedObject), [saved_dashboard.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts#:~:text=SavedObject), [saved_dashboard.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts#:~:text=SavedObject), [dashboard_tagging.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/lib/dashboard_tagging.ts#:~:text=SavedObject), [dashboard_tagging.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/lib/dashboard_tagging.ts#:~:text=SavedObject), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx#:~:text=SavedObject), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx#:~:text=SavedObject)+ 1 more | 8.8.0 | | | [saved_dashboard.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts#:~:text=SavedObjectClass) | 8.8.0 | -| | [dashboard_listing.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx#:~:text=settings), [dashboard_listing.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx#:~:text=settings) | 8.8.0 | | | [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/types.ts#:~:text=onAppLeave), [dashboard_router.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/dashboard_router.tsx#:~:text=onAppLeave), [plugin.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/plugin.tsx#:~:text=onAppLeave) | 8.8.0 | | | [migrations_730.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/server/saved_objects/migrations_730.ts#:~:text=warning), [migrations_730.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/server/saved_objects/migrations_730.ts#:~:text=warning) | 8.8.0 | @@ -131,9 +134,9 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/index.ts#:~:text=IndexPatternsContract), [create_search_source.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/create_search_source.ts#:~:text=IndexPatternsContract), [create_search_source.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/create_search_source.ts#:~:text=IndexPatternsContract), [search_source_service.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/search_source_service.ts#:~:text=IndexPatternsContract), [search_source_service.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/search_source_service.ts#:~:text=IndexPatternsContract), [esaggs_fn.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts#:~:text=IndexPatternsContract), [esaggs_fn.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts#:~:text=IndexPatternsContract), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/search/types.ts#:~:text=IndexPatternsContract), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/search/types.ts#:~:text=IndexPatternsContract), [create_search_source.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/create_search_source.test.ts#:~:text=IndexPatternsContract)+ 19 more | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/index.ts#:~:text=IndexPatternsService), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/server/index.ts#:~:text=IndexPatternsService), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/server/index.ts#:~:text=IndexPatternsService), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/server/index.ts#:~:text=IndexPatternsService), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/index.ts#:~:text=IndexPatternsService) | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/index.ts#:~:text=IndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/types.ts#:~:text=IndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/types.ts#:~:text=IndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/types.ts#:~:text=IndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/types.ts#:~:text=IndexPattern), [search_source.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/search_source.ts#:~:text=IndexPattern), [search_source.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/search_source.ts#:~:text=IndexPattern), [search_source.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/search_source.ts#:~:text=IndexPattern), [tabify_docs.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/tabify/tabify_docs.ts#:~:text=IndexPattern), [tabify_docs.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/tabify/tabify_docs.ts#:~:text=IndexPattern)+ 89 more | - | -| | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/index.ts#:~:text=IFieldType), [date_histogram.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/aggs/buckets/date_histogram.ts#:~:text=IFieldType), [date_histogram.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/aggs/buckets/date_histogram.ts#:~:text=IFieldType), [generate_filters.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts#:~:text=IFieldType), [generate_filters.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts#:~:text=IFieldType), [generate_filters.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts#:~:text=IFieldType), [generate_filters.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts#:~:text=IFieldType), [query_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts#:~:text=IFieldType), [query_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts#:~:text=IFieldType), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IFieldType)+ 24 more | 8.2 | +| | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/index.ts#:~:text=IFieldType), [date_histogram.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/aggs/buckets/date_histogram.ts#:~:text=IFieldType), [date_histogram.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/aggs/buckets/date_histogram.ts#:~:text=IFieldType), [generate_filters.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts#:~:text=IFieldType), [generate_filters.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts#:~:text=IFieldType), [generate_filters.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts#:~:text=IFieldType), [generate_filters.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts#:~:text=IFieldType), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/index.ts#:~:text=IFieldType), [create_filters_from_range_select.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts#:~:text=IFieldType), [create_filters_from_range_select.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts#:~:text=IFieldType)+ 6 more | 8.2 | | | [field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/aggs/param_types/field.ts#:~:text=IndexPatternField), [field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/aggs/param_types/field.ts#:~:text=IndexPatternField), [field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/aggs/param_types/field.ts#:~:text=IndexPatternField), [field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/aggs/param_types/field.ts#:~:text=IndexPatternField), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/index.ts#:~:text=IndexPatternField), [kibana_context_type.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/expressions/kibana_context_type.ts#:~:text=IndexPatternField), [kibana_context_type.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/expressions/kibana_context_type.ts#:~:text=IndexPatternField), [search_source.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/search_source.ts#:~:text=IndexPatternField), [search_source.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/search_source.ts#:~:text=IndexPatternField), [search_source.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/search_source.ts#:~:text=IndexPatternField)+ 16 more | - | -| | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/index.ts#:~:text=IIndexPattern), [get_time.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/query/timefilter/get_time.ts#:~:text=IIndexPattern), [get_time.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/query/timefilter/get_time.ts#:~:text=IIndexPattern), [get_time.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/query/timefilter/get_time.ts#:~:text=IIndexPattern), [get_time.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/query/timefilter/get_time.ts#:~:text=IIndexPattern), [normalize_sort_request.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/normalize_sort_request.ts#:~:text=IIndexPattern), [normalize_sort_request.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/normalize_sort_request.ts#:~:text=IIndexPattern), [normalize_sort_request.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/normalize_sort_request.ts#:~:text=IIndexPattern), [search_source.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/search_source.ts#:~:text=IIndexPattern), [search_source.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/search_source.ts#:~:text=IIndexPattern)+ 36 more | - | +| | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/index.ts#:~:text=IIndexPattern), [get_time.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/query/timefilter/get_time.ts#:~:text=IIndexPattern), [get_time.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/query/timefilter/get_time.ts#:~:text=IIndexPattern), [get_time.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/query/timefilter/get_time.ts#:~:text=IIndexPattern), [get_time.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/query/timefilter/get_time.ts#:~:text=IIndexPattern), [normalize_sort_request.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/normalize_sort_request.ts#:~:text=IIndexPattern), [normalize_sort_request.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/normalize_sort_request.ts#:~:text=IIndexPattern), [normalize_sort_request.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/normalize_sort_request.ts#:~:text=IIndexPattern), [search_source.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/search_source.ts#:~:text=IIndexPattern), [search_source.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/search_source.ts#:~:text=IIndexPattern)+ 23 more | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/index.ts#:~:text=IndexPatternAttributes), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/index.ts#:~:text=IndexPatternAttributes), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/server/index.ts#:~:text=IndexPatternAttributes) | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/index.ts#:~:text=IndexPatternsContract), [create_search_source.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/create_search_source.ts#:~:text=IndexPatternsContract), [create_search_source.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/create_search_source.ts#:~:text=IndexPatternsContract), [search_source_service.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/search_source_service.ts#:~:text=IndexPatternsContract), [search_source_service.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/search_source_service.ts#:~:text=IndexPatternsContract), [esaggs_fn.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts#:~:text=IndexPatternsContract), [esaggs_fn.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts#:~:text=IndexPatternsContract), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/search/types.ts#:~:text=IndexPatternsContract), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/search/types.ts#:~:text=IndexPatternsContract), [create_search_source.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/search/search_source/create_search_source.test.ts#:~:text=IndexPatternsContract)+ 19 more | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/common/index.ts#:~:text=IndexPatternsService), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/server/index.ts#:~:text=IndexPatternsService), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/server/index.ts#:~:text=IndexPatternsService), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/server/index.ts#:~:text=IndexPatternsService), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data/public/index.ts#:~:text=IndexPatternsService) | - | @@ -194,9 +197,9 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPattern), [data_view_field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.test.ts#:~:text=IndexPattern), [data_view_field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.test.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/public/index.ts#:~:text=IndexPattern), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPattern), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPattern), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPattern), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPattern) | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPatternField), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/public/index.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view_field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.test.ts#:~:text=IndexPatternField), [data_view_field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.test.ts#:~:text=IndexPatternField)+ 2 more | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IIndexPattern), [data_view.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.ts#:~:text=IIndexPattern), [data_view.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.ts#:~:text=IIndexPattern) | - | -| | [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType)+ 13 more | 8.2 | +| | [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType)+ 7 more | 8.2 | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPatternAttributes) | - | -| | [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType)+ 13 more | 8.2 | +| | [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType)+ 7 more | 8.2 | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IIndexPattern), [data_view.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.ts#:~:text=IIndexPattern), [data_view.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.ts#:~:text=IIndexPattern) | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPatternAttributes) | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPatternsContract), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/public/index.ts#:~:text=IndexPatternsContract) | - | @@ -208,7 +211,6 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=removeScriptedField) | - | | | [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=getNonScriptedFields) | - | | | [data_view.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.ts#:~:text=getScriptedFields), [data_view.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.ts#:~:text=getScriptedFields), [data_views.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_views.ts#:~:text=getScriptedFields), [register_index_pattern_usage_collection.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/register_index_pattern_usage_collection.ts#:~:text=getScriptedFields), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=getScriptedFields), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=getScriptedFields), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=getScriptedFields), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=getScriptedFields), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=getScriptedFields) | - | -| | [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType)+ 13 more | 8.2 | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPatternAttributes) | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPatternField), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/public/index.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view_field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.test.ts#:~:text=IndexPatternField), [data_view_field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.test.ts#:~:text=IndexPatternField)+ 2 more | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPattern), [data_view_field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.test.ts#:~:text=IndexPattern), [data_view_field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.test.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/public/index.ts#:~:text=IndexPattern), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPattern), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPattern), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPattern), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPattern) | - | @@ -263,6 +265,8 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| +| | [page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/enterprise_search/public/applications/shared/layout/page_template.tsx#:~:text=KibanaPageTemplateProps), [page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/enterprise_search/public/applications/shared/layout/page_template.tsx#:~:text=KibanaPageTemplateProps) | - | +| | [version_mismatch_page.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/enterprise_search/public/applications/shared/version_mismatch/version_mismatch_page.tsx#:~:text=KibanaPageTemplate), [version_mismatch_page.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/enterprise_search/public/applications/shared/version_mismatch/version_mismatch_page.tsx#:~:text=KibanaPageTemplate), [version_mismatch_page.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/enterprise_search/public/applications/shared/version_mismatch/version_mismatch_page.tsx#:~:text=KibanaPageTemplate), [error_connecting.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/error_connecting/error_connecting.tsx#:~:text=KibanaPageTemplate), [error_connecting.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/error_connecting/error_connecting.tsx#:~:text=KibanaPageTemplate), [error_connecting.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/error_connecting/error_connecting.tsx#:~:text=KibanaPageTemplate), [product_selector.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx#:~:text=KibanaPageTemplate), [product_selector.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx#:~:text=KibanaPageTemplate), [product_selector.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx#:~:text=KibanaPageTemplate), [page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/enterprise_search/public/applications/shared/layout/page_template.tsx#:~:text=KibanaPageTemplate)+ 11 more | - | | | [check_access.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/enterprise_search/server/lib/check_access.ts#:~:text=authz), [check_access.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/enterprise_search/server/lib/check_access.ts#:~:text=authz), [check_access.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/enterprise_search/server/lib/check_access.ts#:~:text=authz) | - | @@ -304,7 +308,6 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [deserialize.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/public/services/persistence/deserialize.ts#:~:text=getNonScriptedFields), [datasource.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/public/state_management/datasource.test.ts#:~:text=getNonScriptedFields), [deserialize.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/public/services/persistence/deserialize.test.ts#:~:text=getNonScriptedFields), [deserialize.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/public/services/persistence/deserialize.test.ts#:~:text=getNonScriptedFields) | - | | | [deserialize.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/public/services/persistence/deserialize.ts#:~:text=getNonScriptedFields), [datasource.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/public/state_management/datasource.test.ts#:~:text=getNonScriptedFields), [deserialize.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/public/services/persistence/deserialize.test.ts#:~:text=getNonScriptedFields), [deserialize.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/public/services/persistence/deserialize.test.ts#:~:text=getNonScriptedFields) | - | | | [save_modal.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/public/components/save_modal.tsx#:~:text=SavedObjectSaveModal), [save_modal.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/public/components/save_modal.tsx#:~:text=SavedObjectSaveModal) | 8.8.0 | -| | [listing_route.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/public/apps/listing_route.tsx#:~:text=settings), [listing_route.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/public/apps/listing_route.tsx#:~:text=settings) | 8.8.0 | | | [plugin.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/server/plugin.ts#:~:text=license%24) | 8.8.0 | @@ -332,6 +335,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | ---------------|-----------|-----------| | | [use_kibana_index_patterns.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/infra/public/hooks/use_kibana_index_patterns.ts#:~:text=indexPatterns) | - | | | [kibana_framework_adapter.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts#:~:text=indexPatternsServiceFactory) | - | +| | [page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/infra/public/pages/logs/page_template.tsx#:~:text=KibanaPageTemplateProps), [page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/infra/public/pages/logs/page_template.tsx#:~:text=KibanaPageTemplateProps), [page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/infra/public/pages/metrics/page_template.tsx#:~:text=KibanaPageTemplateProps), [page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/infra/public/pages/metrics/page_template.tsx#:~:text=KibanaPageTemplateProps) | - | @@ -349,6 +353,8 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| +| | [overview.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/kibana_overview/public/components/overview/overview.tsx#:~:text=KibanaPageTemplateProps), [overview.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/kibana_overview/public/components/overview/overview.tsx#:~:text=KibanaPageTemplateProps) | - | +| | [overview.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/kibana_overview/public/components/overview/overview.tsx#:~:text=KibanaPageTemplate), [overview.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/kibana_overview/public/components/overview/overview.tsx#:~:text=KibanaPageTemplate), [overview.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/kibana_overview/public/components/overview/overview.tsx#:~:text=KibanaPageTemplate) | - | | | [add_data.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/kibana_overview/public/components/add_data/add_data.tsx#:~:text=RedirectAppLinks), [add_data.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/kibana_overview/public/components/add_data/add_data.tsx#:~:text=RedirectAppLinks), [add_data.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/kibana_overview/public/components/add_data/add_data.tsx#:~:text=RedirectAppLinks), [manage_data.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/kibana_overview/public/components/manage_data/manage_data.tsx#:~:text=RedirectAppLinks), [manage_data.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/kibana_overview/public/components/manage_data/manage_data.tsx#:~:text=RedirectAppLinks), [manage_data.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/kibana_overview/public/components/manage_data/manage_data.tsx#:~:text=RedirectAppLinks), [overview.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/kibana_overview/public/components/overview/overview.tsx#:~:text=RedirectAppLinks), [overview.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/kibana_overview/public/components/overview/overview.tsx#:~:text=RedirectAppLinks), [overview.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/kibana_overview/public/components/overview/overview.tsx#:~:text=RedirectAppLinks), [overview.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/kibana_overview/public/components/overview/overview.tsx#:~:text=RedirectAppLinks)+ 1 more | - | | | [application.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/kibana_overview/public/application.tsx#:~:text=appBasePath) | 8.8.0 | @@ -395,6 +401,8 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| +| | [management_app.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/management/public/components/management_app/management_app.tsx#:~:text=KibanaPageTemplateProps), [management_app.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/management/public/components/management_app/management_app.tsx#:~:text=KibanaPageTemplateProps) | - | +| | [management_app.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/management/public/components/management_app/management_app.tsx#:~:text=KibanaPageTemplate), [management_app.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/management/public/components/management_app/management_app.tsx#:~:text=KibanaPageTemplate), [management_app.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/management/public/components/management_app/management_app.tsx#:~:text=KibanaPageTemplate) | - | | | [application.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/management/public/application.tsx#:~:text=appBasePath) | 8.8.0 | @@ -419,8 +427,6 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [es_search_source.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx#:~:text=flattenHit), [es_search_source.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx#:~:text=flattenHit) | - | | | [es_search_source.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx#:~:text=flattenHit), [es_search_source.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx#:~:text=flattenHit) | - | | | [kibana_server_services.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/server/kibana_server_services.ts#:~:text=indexPatternsServiceFactory), [indexing_routes.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/server/data_indexing/indexing_routes.ts#:~:text=indexPatternsServiceFactory) | - | -| | [map_container.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx#:~:text=ExitFullScreenButton), [map_container.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx#:~:text=ExitFullScreenButton) | - | -| | [maps_list_view.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx#:~:text=settings), [maps_list_view.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx#:~:text=settings), [maps_list_view.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx#:~:text=settings) | 8.8.0 | | | [render_app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/render_app.tsx#:~:text=onAppLeave), [map_app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/routes/map_page/map_app/map_app.tsx#:~:text=onAppLeave), [map_page.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/routes/map_page/map_page.tsx#:~:text=onAppLeave) | 8.8.0 | | | [saved_object_migrations.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.ts#:~:text=warning) | 8.8.0 | @@ -439,6 +445,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| +| | [ml_page.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx#:~:text=KibanaPageTemplate), [ml_page.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx#:~:text=KibanaPageTemplate), [ml_page.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx#:~:text=KibanaPageTemplate) | - | | | [ml_page.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx#:~:text=RedirectAppLinks), [ml_page.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx#:~:text=RedirectAppLinks), [ml_page.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx#:~:text=RedirectAppLinks), [jobs_list_page.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx#:~:text=RedirectAppLinks), [jobs_list_page.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx#:~:text=RedirectAppLinks), [jobs_list_page.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx#:~:text=RedirectAppLinks) | - | | | [check_license.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ml/public/application/license/check_license.tsx#:~:text=license%24), [plugin.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ml/public/plugin.ts#:~:text=license%24) | 8.8.0 | | | [plugin.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ml/server/plugin.ts#:~:text=license%24), [plugin.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ml/server/plugin.ts#:~:text=license%24) | 8.8.0 | @@ -456,9 +463,6 @@ so TS and code-reference navigation might not highlight them. | | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [use_derived_index_pattern.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/alerts/components/param_details_form/use_derived_index_pattern.tsx#:~:text=IIndexPattern), [use_derived_index_pattern.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/alerts/components/param_details_form/use_derived_index_pattern.tsx#:~:text=IIndexPattern), [with_kuery_autocompletion.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/components/kuery_bar/with_kuery_autocompletion.tsx#:~:text=IIndexPattern), [with_kuery_autocompletion.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/components/kuery_bar/with_kuery_autocompletion.tsx#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/components/kuery_bar/index.tsx#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/components/kuery_bar/index.tsx#:~:text=IIndexPattern), [kuery.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/lib/kuery.ts#:~:text=IIndexPattern), [kuery.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/lib/kuery.ts#:~:text=IIndexPattern) | - | -| | [use_derived_index_pattern.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/alerts/components/param_details_form/use_derived_index_pattern.tsx#:~:text=indexPatterns), [use_derived_index_pattern.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/alerts/components/param_details_form/use_derived_index_pattern.tsx#:~:text=indexPatterns) | - | -| | [use_derived_index_pattern.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/alerts/components/param_details_form/use_derived_index_pattern.tsx#:~:text=IIndexPattern), [use_derived_index_pattern.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/alerts/components/param_details_form/use_derived_index_pattern.tsx#:~:text=IIndexPattern), [with_kuery_autocompletion.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/components/kuery_bar/with_kuery_autocompletion.tsx#:~:text=IIndexPattern), [with_kuery_autocompletion.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/components/kuery_bar/with_kuery_autocompletion.tsx#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/components/kuery_bar/index.tsx#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/components/kuery_bar/index.tsx#:~:text=IIndexPattern), [kuery.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/lib/kuery.ts#:~:text=IIndexPattern), [kuery.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/lib/kuery.ts#:~:text=IIndexPattern), [use_derived_index_pattern.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/alerts/components/param_details_form/use_derived_index_pattern.tsx#:~:text=IIndexPattern), [use_derived_index_pattern.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/alerts/components/param_details_form/use_derived_index_pattern.tsx#:~:text=IIndexPattern)+ 6 more | - | | | [legacy_shims.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/public/legacy_shims.ts#:~:text=injectedMetadata) | 8.8.0 | | | [bulk_uploader.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/monitoring/server/kibana_monitoring/bulk_uploader.ts#:~:text=process) | 8.8.0 | @@ -468,10 +472,20 @@ so TS and code-reference navigation might not highlight them. | | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| +| | [page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx#:~:text=KibanaPageTemplateProps), [page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx#:~:text=KibanaPageTemplateProps), [no_data_config.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/utils/no_data_config.ts#:~:text=KibanaPageTemplateProps), [no_data_config.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/utils/no_data_config.ts#:~:text=KibanaPageTemplateProps) | - | +| | [page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx#:~:text=KibanaPageTemplate), [page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx#:~:text=KibanaPageTemplate), [page_template.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx#:~:text=KibanaPageTemplate) | - | | | [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/application/index.tsx#:~:text=RedirectAppLinks), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/application/index.tsx#:~:text=RedirectAppLinks), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/application/index.tsx#:~:text=RedirectAppLinks) | - | +## osquery + +| Deprecated API | Reference location(s) | Remove By | +| ---------------|-----------|-----------| +| | [empty_state.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/osquery/public/components/empty_state.tsx#:~:text=KibanaPageTemplate), [empty_state.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/osquery/public/components/empty_state.tsx#:~:text=KibanaPageTemplate) | - | + + + ## painlessLab | Deprecated API | Reference location(s) | Remove By | @@ -582,6 +596,8 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| | | [middleware.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts#:~:text=indexPatterns), [dependencies_start_mock.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/mock/endpoint/dependencies_start_mock.ts#:~:text=indexPatterns) | - | +| | [use_primary_navigation.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_primary_navigation.tsx#:~:text=KibanaPageTemplateProps), [use_primary_navigation.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_primary_navigation.tsx#:~:text=KibanaPageTemplateProps), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/app/home/template_wrapper/bottom_bar/index.tsx#:~:text=KibanaPageTemplateProps), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/app/home/template_wrapper/bottom_bar/index.tsx#:~:text=KibanaPageTemplateProps) | - | +| | [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx#:~:text=KibanaPageTemplate), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx#:~:text=KibanaPageTemplate) | - | | | [policy_config.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [isolation.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts#:~:text=mode), [isolation.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts#:~:text=mode)+ 2 more | 8.8.0 | | | [policy_config.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [isolation.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts#:~:text=mode), [isolation.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts#:~:text=mode)+ 2 more | 8.8.0 | | | [request_context_factory.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/request_context_factory.ts#:~:text=authc), [request_context_factory.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/request_context_factory.ts#:~:text=authc), [create_signals_migration_route.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts#:~:text=authc), [delete_signals_migration_route.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts#:~:text=authc), [finalize_signals_migration_route.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts#:~:text=authc), [open_close_signals_route.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts#:~:text=authc), [preview_rules_route.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts#:~:text=authc), [common.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts#:~:text=authc) | - | @@ -602,6 +618,7 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| +| | [space_selector.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/spaces/public/space_selector/space_selector.tsx#:~:text=KibanaPageTemplate), [space_selector.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/spaces/public/space_selector/space_selector.tsx#:~:text=KibanaPageTemplate), [space_selector.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/spaces/public/space_selector/space_selector.tsx#:~:text=KibanaPageTemplate) | - | | | [spaces_management_app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/spaces/public/management/spaces_management_app.tsx#:~:text=RedirectAppLinks), [spaces_management_app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/spaces/public/management/spaces_management_app.tsx#:~:text=RedirectAppLinks), [spaces_management_app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/spaces/public/management/spaces_management_app.tsx#:~:text=RedirectAppLinks) | - | | | [on_post_auth_interceptor.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.ts#:~:text=getKibanaFeatures), [spaces_usage_collector.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts#:~:text=getKibanaFeatures), [on_post_auth_interceptor.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.test.ts#:~:text=getKibanaFeatures) | 8.8.0 | | | [spaces_usage_collector.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts#:~:text=license%24), [plugin.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/spaces/server/plugin.ts#:~:text=license%24), [plugin.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/spaces/server/plugin.ts#:~:text=license%24), [spaces_usage_collector.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts#:~:text=license%24) | 8.8.0 | @@ -614,18 +631,25 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [read_only_filter_items.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx#:~:text=IIndexPattern), [read_only_filter_items.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx#:~:text=IIndexPattern) | - | | | [fetch_search_source_query.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/server/alert_types/es_query/lib/fetch_search_source_query.ts#:~:text=fetch), [alert_type.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts#:~:text=fetch), [alert_type.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts#:~:text=fetch) | 8.1 | | | [entity_index_expression.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_index_expression.tsx#:~:text=indexPatterns), [boundary_index_expression.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/boundary_index_expression.tsx#:~:text=indexPatterns), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/index.tsx#:~:text=indexPatterns), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/index.tsx#:~:text=indexPatterns), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/index.tsx#:~:text=indexPatterns) | - | | | [expression.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx#:~:text=fieldFormats) | - | | | [read_only_filter_items.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx#:~:text=Filter), [read_only_filter_items.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx#:~:text=Filter), [search_source_expression.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/search_source_expression.tsx#:~:text=Filter), [search_source_expression.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/search_source_expression.tsx#:~:text=Filter) | 8.1 | | | [read_only_filter_items.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx#:~:text=Filter), [read_only_filter_items.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx#:~:text=Filter), [search_source_expression.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/search_source_expression.tsx#:~:text=Filter), [search_source_expression.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/search_source_expression.tsx#:~:text=Filter) | 8.1 | -| | [read_only_filter_items.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx#:~:text=IIndexPattern), [read_only_filter_items.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx#:~:text=IIndexPattern), [read_only_filter_items.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx#:~:text=IIndexPattern), [read_only_filter_items.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx#:~:text=IIndexPattern) | - | | | [fetch_search_source_query.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/server/alert_types/es_query/lib/fetch_search_source_query.ts#:~:text=fetch), [alert_type.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts#:~:text=fetch), [alert_type.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts#:~:text=fetch) | 8.1 | | | [read_only_filter_items.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx#:~:text=Filter), [read_only_filter_items.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx#:~:text=Filter), [search_source_expression.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/search_source_expression.tsx#:~:text=Filter), [search_source_expression.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/search_source_expression.tsx#:~:text=Filter) | 8.1 | +## synthetics + +| Deprecated API | Reference location(s) | Remove By | +| ---------------|-----------|-----------| +| | [use_no_data_config.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/synthetics/public/apps/use_no_data_config.ts#:~:text=KibanaPageTemplateProps), [use_no_data_config.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/synthetics/public/apps/use_no_data_config.ts#:~:text=KibanaPageTemplateProps) | - | +| | [alert_messages.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/synthetics/public/lib/alert_types/alert_messages.tsx#:~:text=RedirectAppLinks), [alert_messages.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/synthetics/public/lib/alert_types/alert_messages.tsx#:~:text=RedirectAppLinks), [alert_messages.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/synthetics/public/lib/alert_types/alert_messages.tsx#:~:text=RedirectAppLinks), [uptime_app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/synthetics/public/apps/uptime_app.tsx#:~:text=RedirectAppLinks), [uptime_app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/synthetics/public/apps/uptime_app.tsx#:~:text=RedirectAppLinks), [uptime_app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/synthetics/public/apps/uptime_app.tsx#:~:text=RedirectAppLinks) | - | + + + ## transform | Deprecated API | Reference location(s) | Remove By | @@ -638,20 +662,22 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [fetch_index_patterns.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/fetch_index_patterns.ts#:~:text=IndexPatternsContract), [fetch_index_patterns.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/fetch_index_patterns.ts#:~:text=IndexPatternsContract), [index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx#:~:text=IndexPatternsContract), [index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx#:~:text=IndexPatternsContract), [create_index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/create_index_pattern_select.tsx#:~:text=IndexPatternsContract), [create_index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/create_index_pattern_select.tsx#:~:text=IndexPatternsContract), [fetch_index_patterns.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/fetch_index_patterns.ts#:~:text=IndexPatternsContract), [fetch_index_patterns.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/fetch_index_patterns.ts#:~:text=IndexPatternsContract), [index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx#:~:text=IndexPatternsContract), [index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx#:~:text=IndexPatternsContract)+ 2 more | - | -| | [query_string_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/query_string_input.tsx#:~:text=IIndexPattern), [query_string_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/query_string_input.tsx#:~:text=IIndexPattern), [query_string_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/query_string_input.tsx#:~:text=IIndexPattern), [query_string_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/query_string_input.tsx#:~:text=IIndexPattern), [query_string_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/query_string_input.tsx#:~:text=IIndexPattern), [query_bar_top_row.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx#:~:text=IIndexPattern), [query_bar_top_row.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx#:~:text=IIndexPattern), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IIndexPattern), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IIndexPattern), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IIndexPattern)+ 18 more | - | -| | [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [phrase_suggestor.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx#:~:text=IFieldType), [phrase_suggestor.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx#:~:text=IFieldType), [range_value_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/range_value_input.tsx#:~:text=IFieldType), [range_value_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/range_value_input.tsx#:~:text=IFieldType), [index.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx#:~:text=IFieldType), [index.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx#:~:text=IFieldType)+ 8 more | 8.2 | +| | [index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx#:~:text=IndexPatternsContract), [index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx#:~:text=IndexPatternsContract), [create_index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/create_index_pattern_select.tsx#:~:text=IndexPatternsContract), [create_index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/create_index_pattern_select.tsx#:~:text=IndexPatternsContract), [index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx#:~:text=IndexPatternsContract), [index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx#:~:text=IndexPatternsContract), [create_index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/create_index_pattern_select.tsx#:~:text=IndexPatternsContract), [create_index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/create_index_pattern_select.tsx#:~:text=IndexPatternsContract) | - | +| | [value.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts#:~:text=IIndexPattern), [value.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts#:~:text=IIndexPattern), [query_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts#:~:text=IIndexPattern), [query_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts#:~:text=IIndexPattern), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IIndexPattern), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IIndexPattern), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IIndexPattern) | - | +| | [field.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx#:~:text=IFieldType), [field.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx#:~:text=IFieldType), [field.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx#:~:text=IFieldType), [value.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts#:~:text=IFieldType), [value.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts#:~:text=IFieldType), [query_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts#:~:text=IFieldType), [query_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts#:~:text=IFieldType), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IFieldType), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IFieldType), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IFieldType)+ 25 more | 8.2 | | | [query_string_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/query_string_input.tsx#:~:text=indexPatterns) | - | | | [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=esFilters), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=esFilters), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=esFilters) | 8.1 | +| | [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [value.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts#:~:text=KueryNode)+ 2 more | 8.1 | | | [use_filter_manager.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/search_bar/lib/use_filter_manager.ts#:~:text=Filter), [use_filter_manager.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/search_bar/lib/use_filter_manager.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [get_stub_filter.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/test_helpers/get_stub_filter.ts#:~:text=Filter), [get_stub_filter.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/test_helpers/get_stub_filter.ts#:~:text=Filter), [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=Filter)+ 10 more | 8.1 | | | [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=FILTERS), [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=FILTERS), [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=FILTERS), [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=FILTERS), [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=FILTERS), [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=FILTERS) | 8.1 | | | [filter_editor_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.test.ts#:~:text=toggleFilterNegated), [filter_editor_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.test.ts#:~:text=toggleFilterNegated), [filter_editor_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.test.ts#:~:text=toggleFilterNegated), [filter_editor_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.test.ts#:~:text=toggleFilterNegated), [filter_editor_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.test.ts#:~:text=toggleFilterNegated) | 8.1 | | | [use_filter_manager.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/search_bar/lib/use_filter_manager.ts#:~:text=Filter), [use_filter_manager.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/search_bar/lib/use_filter_manager.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [get_stub_filter.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/test_helpers/get_stub_filter.ts#:~:text=Filter), [get_stub_filter.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/test_helpers/get_stub_filter.ts#:~:text=Filter), [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=Filter)+ 10 more | 8.1 | -| | [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [phrase_suggestor.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx#:~:text=IFieldType), [phrase_suggestor.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx#:~:text=IFieldType), [range_value_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/range_value_input.tsx#:~:text=IFieldType), [range_value_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/range_value_input.tsx#:~:text=IFieldType), [index.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx#:~:text=IFieldType), [index.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx#:~:text=IFieldType)+ 26 more | 8.2 | -| | [query_string_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/query_string_input.tsx#:~:text=IIndexPattern), [query_string_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/query_string_input.tsx#:~:text=IIndexPattern), [query_string_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/query_string_input.tsx#:~:text=IIndexPattern), [query_string_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/query_string_input.tsx#:~:text=IIndexPattern), [query_string_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/query_string_input.tsx#:~:text=IIndexPattern), [query_bar_top_row.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx#:~:text=IIndexPattern), [query_bar_top_row.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx#:~:text=IIndexPattern), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IIndexPattern), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IIndexPattern), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IIndexPattern)+ 46 more | - | -| | [fetch_index_patterns.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/fetch_index_patterns.ts#:~:text=IndexPatternsContract), [fetch_index_patterns.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/fetch_index_patterns.ts#:~:text=IndexPatternsContract), [index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx#:~:text=IndexPatternsContract), [index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx#:~:text=IndexPatternsContract), [create_index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/create_index_pattern_select.tsx#:~:text=IndexPatternsContract), [create_index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/create_index_pattern_select.tsx#:~:text=IndexPatternsContract), [fetch_index_patterns.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/fetch_index_patterns.ts#:~:text=IndexPatternsContract), [fetch_index_patterns.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/query_string_input/fetch_index_patterns.ts#:~:text=IndexPatternsContract), [index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx#:~:text=IndexPatternsContract), [index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx#:~:text=IndexPatternsContract)+ 2 more | - | -| | [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [phrase_suggestor.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx#:~:text=IFieldType), [phrase_suggestor.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx#:~:text=IFieldType), [range_value_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/range_value_input.tsx#:~:text=IFieldType), [range_value_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/range_value_input.tsx#:~:text=IFieldType), [index.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx#:~:text=IFieldType), [index.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx#:~:text=IFieldType)+ 8 more | 8.2 | +| | [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [value.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts#:~:text=KueryNode)+ 2 more | 8.1 | +| | [field.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx#:~:text=IFieldType), [field.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx#:~:text=IFieldType), [field.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx#:~:text=IFieldType), [value.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts#:~:text=IFieldType), [value.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts#:~:text=IFieldType), [query_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts#:~:text=IFieldType), [query_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts#:~:text=IFieldType), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IFieldType), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IFieldType), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IFieldType)+ 60 more | 8.2 | +| | [value.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts#:~:text=IIndexPattern), [value.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts#:~:text=IIndexPattern), [query_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts#:~:text=IIndexPattern), [query_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts#:~:text=IIndexPattern), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IIndexPattern), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IIndexPattern), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IIndexPattern), [value.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts#:~:text=IIndexPattern), [value.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts#:~:text=IIndexPattern), [query_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts#:~:text=IIndexPattern)+ 4 more | - | +| | [index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx#:~:text=IndexPatternsContract), [index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx#:~:text=IndexPatternsContract), [create_index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/create_index_pattern_select.tsx#:~:text=IndexPatternsContract), [create_index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/create_index_pattern_select.tsx#:~:text=IndexPatternsContract), [index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx#:~:text=IndexPatternsContract), [index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx#:~:text=IndexPatternsContract), [create_index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/create_index_pattern_select.tsx#:~:text=IndexPatternsContract), [create_index_pattern_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/index_pattern_select/create_index_pattern_select.tsx#:~:text=IndexPatternsContract) | - | | | [use_filter_manager.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/search_bar/lib/use_filter_manager.ts#:~:text=Filter), [use_filter_manager.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/search_bar/lib/use_filter_manager.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [get_stub_filter.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/test_helpers/get_stub_filter.ts#:~:text=Filter), [get_stub_filter.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/test_helpers/get_stub_filter.ts#:~:text=Filter), [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=Filter)+ 10 more | 8.1 | +| | [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [value.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts#:~:text=KueryNode)+ 2 more | 8.1 | @@ -664,18 +690,6 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ -## uptime - -| Deprecated API | Reference location(s) | Remove By | -| ---------------|-----------|-----------| -| | [update_kuery_string.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/hooks/update_kuery_string.ts#:~:text=IndexPattern), [update_kuery_string.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/hooks/update_kuery_string.ts#:~:text=IndexPattern), [uptime_index_pattern_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx#:~:text=IndexPattern), [uptime_index_pattern_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx#:~:text=IndexPattern), [uptime_index_pattern_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx#:~:text=IndexPattern), [update_kuery_string.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/hooks/update_kuery_string.ts#:~:text=IndexPattern), [update_kuery_string.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/hooks/update_kuery_string.ts#:~:text=IndexPattern), [uptime_index_pattern_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx#:~:text=IndexPattern), [uptime_index_pattern_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx#:~:text=IndexPattern), [uptime_index_pattern_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx#:~:text=IndexPattern) | - | -| | [uptime_index_pattern_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx#:~:text=indexPatterns) | - | -| | [update_kuery_string.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/hooks/update_kuery_string.ts#:~:text=IndexPattern), [update_kuery_string.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/hooks/update_kuery_string.ts#:~:text=IndexPattern), [uptime_index_pattern_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx#:~:text=IndexPattern), [uptime_index_pattern_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx#:~:text=IndexPattern), [uptime_index_pattern_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx#:~:text=IndexPattern), [update_kuery_string.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/hooks/update_kuery_string.ts#:~:text=IndexPattern), [update_kuery_string.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/hooks/update_kuery_string.ts#:~:text=IndexPattern), [uptime_index_pattern_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx#:~:text=IndexPattern), [uptime_index_pattern_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx#:~:text=IndexPattern), [uptime_index_pattern_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx#:~:text=IndexPattern) | - | -| | [update_kuery_string.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/hooks/update_kuery_string.ts#:~:text=IndexPattern), [update_kuery_string.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/hooks/update_kuery_string.ts#:~:text=IndexPattern), [uptime_index_pattern_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx#:~:text=IndexPattern), [uptime_index_pattern_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx#:~:text=IndexPattern), [uptime_index_pattern_context.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/contexts/uptime_index_pattern_context.tsx#:~:text=IndexPattern) | - | -| | [alert_messages.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/lib/alert_types/alert_messages.tsx#:~:text=RedirectAppLinks), [alert_messages.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/lib/alert_types/alert_messages.tsx#:~:text=RedirectAppLinks), [alert_messages.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/lib/alert_types/alert_messages.tsx#:~:text=RedirectAppLinks), [uptime_app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/apps/uptime_app.tsx#:~:text=RedirectAppLinks), [uptime_app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/apps/uptime_app.tsx#:~:text=RedirectAppLinks), [uptime_app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/uptime/public/apps/uptime_app.tsx#:~:text=RedirectAppLinks) | - | - - - ## urlDrilldown | Deprecated API | Reference location(s) | Remove By | @@ -693,6 +707,7 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | | [selected_filters.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_filters.tsx#:~:text=IndexPattern), [selected_filters.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_filters.tsx#:~:text=IndexPattern), [selected_wildcards.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_wildcards.tsx#:~:text=IndexPattern), [selected_wildcards.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_wildcards.tsx#:~:text=IndexPattern), [selected_filters.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_filters.tsx#:~:text=IndexPattern), [selected_filters.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_filters.tsx#:~:text=IndexPattern), [selected_wildcards.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_wildcards.tsx#:~:text=IndexPattern), [selected_wildcards.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_wildcards.tsx#:~:text=IndexPattern) | - | | | [selected_filters.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_filters.tsx#:~:text=IndexPattern), [selected_filters.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_filters.tsx#:~:text=IndexPattern), [selected_wildcards.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_wildcards.tsx#:~:text=IndexPattern), [selected_wildcards.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_wildcards.tsx#:~:text=IndexPattern), [selected_filters.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_filters.tsx#:~:text=IndexPattern), [selected_filters.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_filters.tsx#:~:text=IndexPattern), [selected_wildcards.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_wildcards.tsx#:~:text=IndexPattern), [selected_wildcards.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_wildcards.tsx#:~:text=IndexPattern) | - | | | [selected_filters.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_filters.tsx#:~:text=IndexPattern), [selected_filters.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_filters.tsx#:~:text=IndexPattern), [selected_wildcards.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_wildcards.tsx#:~:text=IndexPattern), [selected_wildcards.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_wildcards.tsx#:~:text=IndexPattern) | - | +| | [rum_home.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/rum_home.tsx#:~:text=KibanaPageTemplateProps), [rum_home.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/components/app/rum_dashboard/rum_home.tsx#:~:text=KibanaPageTemplateProps) | - | | | [ux_app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/application/ux_app.tsx#:~:text=RedirectAppLinks), [ux_app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/application/ux_app.tsx#:~:text=RedirectAppLinks), [ux_app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/ux/public/application/ux_app.tsx#:~:text=RedirectAppLinks) | - | @@ -782,7 +797,6 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | ---------------|-----------|-----------| | | [get_table_columns.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/visualize_app/utils/get_table_columns.tsx#:~:text=RedirectAppLinks), [get_table_columns.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/visualize_app/utils/get_table_columns.tsx#:~:text=RedirectAppLinks), [get_table_columns.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/visualize_app/utils/get_table_columns.tsx#:~:text=RedirectAppLinks) | - | | | [display_duplicate_title_confirm_modal.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/utils/saved_objects_utils/display_duplicate_title_confirm_modal.ts#:~:text=SavedObject), [display_duplicate_title_confirm_modal.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/utils/saved_objects_utils/display_duplicate_title_confirm_modal.ts#:~:text=SavedObject), [check_for_duplicate_title.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/utils/saved_objects_utils/check_for_duplicate_title.ts#:~:text=SavedObject), [check_for_duplicate_title.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/utils/saved_objects_utils/check_for_duplicate_title.ts#:~:text=SavedObject) | 8.8.0 | -| | [visualize_listing.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx#:~:text=settings), [visualize_listing.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx#:~:text=settings) | 8.8.0 | | | [visualize_top_nav.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx#:~:text=onAppLeave), [visualize_editor_common.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/visualize_app/components/visualize_editor_common.tsx#:~:text=onAppLeave), [app.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/visualize_app/app.tsx#:~:text=onAppLeave), [index.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/visualize_app/index.tsx#:~:text=onAppLeave) | 8.8.0 | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 43a36e5f1cf9d..aab61180e9dc8 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -3,7 +3,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team summary: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. --- @@ -25,9 +25,8 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | Plugin | Deprecated API | Reference location(s) | Remove By | | --------|-------|-----------|-----------| -| dataViews | | [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType)+ 13 more | 8.2 | -| dataViews | | [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType)+ 47 more | 8.2 | -| dataViews | | [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType)+ 13 more | 8.2 | +| dataViews | | [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType)+ 7 more | 8.2 | +| dataViews | | [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType)+ 23 more | 8.2 | | dataEnhanced | | [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/types.ts#:~:text=KueryNode), [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/types.ts#:~:text=KueryNode), [get_search_session_page.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/get_search_session_page.ts#:~:text=KueryNode), [get_search_session_page.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/get_search_session_page.ts#:~:text=KueryNode), [check_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/check_persisted_sessions.ts#:~:text=KueryNode), [check_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/check_persisted_sessions.ts#:~:text=KueryNode), [check_non_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/check_non_persisted_sessions.ts#:~:text=KueryNode), [check_non_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/check_non_persisted_sessions.ts#:~:text=KueryNode), [expire_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/expire_persisted_sessions.ts#:~:text=KueryNode), [expire_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/expire_persisted_sessions.ts#:~:text=KueryNode) | 8.1 | | dataEnhanced | | [check_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/check_persisted_sessions.ts#:~:text=nodeBuilder), [check_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/check_persisted_sessions.ts#:~:text=nodeBuilder), [check_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/check_persisted_sessions.ts#:~:text=nodeBuilder), [check_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/check_persisted_sessions.ts#:~:text=nodeBuilder), [check_non_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/check_non_persisted_sessions.ts#:~:text=nodeBuilder), [check_non_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/check_non_persisted_sessions.ts#:~:text=nodeBuilder), [expire_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/expire_persisted_sessions.ts#:~:text=nodeBuilder), [expire_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/expire_persisted_sessions.ts#:~:text=nodeBuilder), [expire_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/expire_persisted_sessions.ts#:~:text=nodeBuilder), [expire_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/expire_persisted_sessions.ts#:~:text=nodeBuilder)+ 2 more | 8.1 | | dataEnhanced | | [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/types.ts#:~:text=KueryNode), [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/types.ts#:~:text=KueryNode), [get_search_session_page.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/get_search_session_page.ts#:~:text=KueryNode), [get_search_session_page.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/get_search_session_page.ts#:~:text=KueryNode), [check_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/check_persisted_sessions.ts#:~:text=KueryNode), [check_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/check_persisted_sessions.ts#:~:text=KueryNode), [check_non_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/check_non_persisted_sessions.ts#:~:text=KueryNode), [check_non_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/check_non_persisted_sessions.ts#:~:text=KueryNode), [expire_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/expire_persisted_sessions.ts#:~:text=KueryNode), [expire_persisted_sessions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/data_enhanced/server/search/session/expire_persisted_sessions.ts#:~:text=KueryNode) | 8.1 | @@ -49,7 +48,6 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | discover | | [anchor.ts](https://github.com/elastic/kibana/tree/master/src/plugins/discover/public/application/context/services/anchor.ts#:~:text=fetch), [fetch_hits_in_interval.ts](https://github.com/elastic/kibana/tree/master/src/plugins/discover/public/application/context/utils/fetch_hits_in_interval.ts#:~:text=fetch) | 8.1 | | discover | | [view_alert_route.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/discover/public/application/view_alert/view_alert_route.tsx#:~:text=Filter), [view_alert_route.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/discover/public/application/view_alert/view_alert_route.tsx#:~:text=Filter) | 8.1 | | discover | | [on_save_search.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx#:~:text=SavedObjectSaveModal), [on_save_search.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx#:~:text=SavedObjectSaveModal), [save_modal.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/public/components/save_modal.tsx#:~:text=SavedObjectSaveModal), [save_modal.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/public/components/save_modal.tsx#:~:text=SavedObjectSaveModal) | 8.8.0 | -| graph | | [listing_route.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/public/apps/listing_route.tsx#:~:text=settings), [listing_route.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/public/apps/listing_route.tsx#:~:text=settings) | 8.8.0 | | graph | | [plugin.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/graph/server/plugin.ts#:~:text=license%24) | 8.8.0 | @@ -69,7 +67,6 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | Plugin | Deprecated API | Reference location(s) | Remove By | | --------|-------|-----------|-----------| -| maps | | [maps_list_view.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx#:~:text=settings), [maps_list_view.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx#:~:text=settings), [maps_list_view.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx#:~:text=settings) | 8.8.0 | | maps | | [render_app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/render_app.tsx#:~:text=onAppLeave), [map_app.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/routes/map_page/map_app/map_app.tsx#:~:text=onAppLeave), [map_page.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/public/routes/map_page/map_page.tsx#:~:text=onAppLeave) | 8.8.0 | | maps | | [saved_object_migrations.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.ts#:~:text=warning) | 8.8.0 | | mapsEms | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/maps_ems/server/index.ts#:~:text=license%24) | 8.8.0 | @@ -99,7 +96,6 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | dashboard | | [saved_objects.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/services/saved_objects.ts#:~:text=SavedObjectSaveModal), [save_modal.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/top_nav/save_modal.tsx#:~:text=SavedObjectSaveModal), [save_modal.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/top_nav/save_modal.tsx#:~:text=SavedObjectSaveModal), [saved_object_save_modal_dashboard.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx#:~:text=SavedObjectSaveModal), [saved_object_save_modal_dashboard.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx#:~:text=SavedObjectSaveModal) | 8.8.0 | | dashboard | | [saved_object_loader.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/services/saved_object_loader.ts#:~:text=SavedObject), [saved_object_loader.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/services/saved_object_loader.ts#:~:text=SavedObject), [saved_object_loader.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/services/saved_object_loader.ts#:~:text=SavedObject), [saved_objects.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/services/saved_objects.ts#:~:text=SavedObject), [saved_dashboard.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts#:~:text=SavedObject), [saved_dashboard.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts#:~:text=SavedObject), [dashboard_tagging.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/lib/dashboard_tagging.ts#:~:text=SavedObject), [dashboard_tagging.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/lib/dashboard_tagging.ts#:~:text=SavedObject), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx#:~:text=SavedObject), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx#:~:text=SavedObject)+ 1 more | 8.8.0 | | dashboard | | [saved_dashboard.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts#:~:text=SavedObjectClass) | 8.8.0 | -| dashboard | | [dashboard_listing.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx#:~:text=settings), [dashboard_listing.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx#:~:text=settings) | 8.8.0 | | dashboard | | [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/types.ts#:~:text=onAppLeave), [dashboard_router.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/application/dashboard_router.tsx#:~:text=onAppLeave), [plugin.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/public/plugin.tsx#:~:text=onAppLeave) | 8.8.0 | | dashboard | | [migrations_730.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/server/saved_objects/migrations_730.ts#:~:text=warning), [migrations_730.ts](https://github.com/elastic/kibana/tree/master/src/plugins/dashboard/server/saved_objects/migrations_730.ts#:~:text=warning) | 8.8.0 | @@ -208,15 +204,17 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | Plugin | Deprecated API | Reference location(s) | Remove By | | --------|-------|-----------|-----------| -| unifiedSearch | | [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [phrase_suggestor.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx#:~:text=IFieldType), [phrase_suggestor.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx#:~:text=IFieldType), [range_value_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/range_value_input.tsx#:~:text=IFieldType), [range_value_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/range_value_input.tsx#:~:text=IFieldType), [index.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx#:~:text=IFieldType), [index.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx#:~:text=IFieldType)+ 8 more | 8.2 | +| unifiedSearch | | [field.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx#:~:text=IFieldType), [field.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx#:~:text=IFieldType), [field.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx#:~:text=IFieldType), [value.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts#:~:text=IFieldType), [value.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts#:~:text=IFieldType), [query_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts#:~:text=IFieldType), [query_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts#:~:text=IFieldType), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IFieldType), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IFieldType), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IFieldType)+ 25 more | 8.2 | | unifiedSearch | | [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=esFilters), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=esFilters), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=esFilters) | 8.1 | +| unifiedSearch | | [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [value.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts#:~:text=KueryNode)+ 2 more | 8.1 | | unifiedSearch | | [use_filter_manager.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/search_bar/lib/use_filter_manager.ts#:~:text=Filter), [use_filter_manager.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/search_bar/lib/use_filter_manager.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [get_stub_filter.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/test_helpers/get_stub_filter.ts#:~:text=Filter), [get_stub_filter.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/test_helpers/get_stub_filter.ts#:~:text=Filter), [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=Filter)+ 10 more | 8.1 | | unifiedSearch | | [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=FILTERS), [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=FILTERS), [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=FILTERS), [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=FILTERS), [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=FILTERS), [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=FILTERS) | 8.1 | | unifiedSearch | | [filter_editor_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.test.ts#:~:text=toggleFilterNegated), [filter_editor_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.test.ts#:~:text=toggleFilterNegated), [filter_editor_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.test.ts#:~:text=toggleFilterNegated), [filter_editor_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.test.ts#:~:text=toggleFilterNegated), [filter_editor_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.test.ts#:~:text=toggleFilterNegated) | 8.1 | | unifiedSearch | | [use_filter_manager.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/search_bar/lib/use_filter_manager.ts#:~:text=Filter), [use_filter_manager.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/search_bar/lib/use_filter_manager.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [get_stub_filter.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/test_helpers/get_stub_filter.ts#:~:text=Filter), [get_stub_filter.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/test_helpers/get_stub_filter.ts#:~:text=Filter), [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=Filter)+ 10 more | 8.1 | -| unifiedSearch | | [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [phrase_suggestor.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx#:~:text=IFieldType), [phrase_suggestor.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx#:~:text=IFieldType), [range_value_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/range_value_input.tsx#:~:text=IFieldType), [range_value_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/range_value_input.tsx#:~:text=IFieldType), [index.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx#:~:text=IFieldType), [index.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx#:~:text=IFieldType)+ 26 more | 8.2 | -| unifiedSearch | | [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [filter_editor_utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_editor_utils.ts#:~:text=IFieldType), [phrase_suggestor.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx#:~:text=IFieldType), [phrase_suggestor.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx#:~:text=IFieldType), [range_value_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/range_value_input.tsx#:~:text=IFieldType), [range_value_input.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/range_value_input.tsx#:~:text=IFieldType), [index.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx#:~:text=IFieldType), [index.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx#:~:text=IFieldType)+ 8 more | 8.2 | +| unifiedSearch | | [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [value.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts#:~:text=KueryNode)+ 2 more | 8.1 | +| unifiedSearch | | [field.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx#:~:text=IFieldType), [field.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx#:~:text=IFieldType), [field.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx#:~:text=IFieldType), [value.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts#:~:text=IFieldType), [value.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts#:~:text=IFieldType), [query_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts#:~:text=IFieldType), [query_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts#:~:text=IFieldType), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IFieldType), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IFieldType), [value_suggestion_provider.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts#:~:text=IFieldType)+ 60 more | 8.2 | | unifiedSearch | | [use_filter_manager.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/search_bar/lib/use_filter_manager.ts#:~:text=Filter), [use_filter_manager.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/search_bar/lib/use_filter_manager.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [apply_filter_action.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/actions/apply_filter_action.ts#:~:text=Filter), [get_stub_filter.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/test_helpers/get_stub_filter.ts#:~:text=Filter), [get_stub_filter.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/test_helpers/get_stub_filter.ts#:~:text=Filter), [filter_label.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx#:~:text=Filter)+ 10 more | 8.1 | +| unifiedSearch | | [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [conjunction.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [operator.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts#:~:text=KueryNode), [value.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts#:~:text=KueryNode)+ 2 more | 8.1 | @@ -227,7 +225,6 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | lens | | [display_duplicate_title_confirm_modal.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/lens/public/persistence/saved_objects_utils/display_duplicate_title_confirm_modal.ts#:~:text=SavedObject), [display_duplicate_title_confirm_modal.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/lens/public/persistence/saved_objects_utils/display_duplicate_title_confirm_modal.ts#:~:text=SavedObject), [check_for_duplicate_title.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/lens/public/persistence/saved_objects_utils/check_for_duplicate_title.ts#:~:text=SavedObject), [check_for_duplicate_title.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/lens/public/persistence/saved_objects_utils/check_for_duplicate_title.ts#:~:text=SavedObject), [display_duplicate_title_confirm_modal.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/utils/saved_objects_utils/display_duplicate_title_confirm_modal.ts#:~:text=SavedObject), [display_duplicate_title_confirm_modal.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/utils/saved_objects_utils/display_duplicate_title_confirm_modal.ts#:~:text=SavedObject), [check_for_duplicate_title.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/utils/saved_objects_utils/check_for_duplicate_title.ts#:~:text=SavedObject), [check_for_duplicate_title.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/utils/saved_objects_utils/check_for_duplicate_title.ts#:~:text=SavedObject) | 8.8.0 | | lens | | [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/lens/public/app_plugin/types.ts#:~:text=onAppLeave), [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/lens/public/app_plugin/types.ts#:~:text=onAppLeave), [mounter.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/lens/public/app_plugin/mounter.tsx#:~:text=onAppLeave), [visualize_top_nav.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx#:~:text=onAppLeave), [visualize_editor_common.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/visualize_app/components/visualize_editor_common.tsx#:~:text=onAppLeave), [app.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/visualize_app/app.tsx#:~:text=onAppLeave), [index.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/visualize_app/index.tsx#:~:text=onAppLeave) | 8.8.0 | | lens | | [saved_object_migrations.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts#:~:text=warning), [saved_object_migrations.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts#:~:text=warning) | 8.8.0 | -| visualizations | | [visualize_listing.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx#:~:text=settings), [visualize_listing.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx#:~:text=settings) | 8.8.0 | | management | | [application.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/management/public/application.tsx#:~:text=appBasePath) | 8.8.0 | | visTypeVega | | [plugin.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/vega/public/plugin.ts#:~:text=injectedMetadata) | 8.8.0 | | visTypeVega | | [search_api.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/vega/public/data_model/search_api.ts#:~:text=injectedMetadata), [plugin.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/vega/public/plugin.ts#:~:text=injectedMetadata) | 8.8.0 | \ No newline at end of file diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index fefa3a0c3391c..21a175173b3f6 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github summary: API docs for the devTools plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] warning: 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. --- diff --git a/api_docs/discover.devdocs.json b/api_docs/discover.devdocs.json index 4f5f940b63275..e4d6e9e749105 100644 --- a/api_docs/discover.devdocs.json +++ b/api_docs/discover.devdocs.json @@ -1222,20 +1222,6 @@ "interfaces": [], "enums": [], "misc": [ - { - "parentPluginId": "discover", - "id": "def-common.APP_ID", - "type": "string", - "tags": [], - "label": "APP_ID", - "description": [], - "signature": [ - "\"discover\"" - ], - "path": "src/plugins/discover/common/index.ts", - "deprecated": false, - "initialIsOpen": false - }, { "parentPluginId": "discover", "id": "def-common.CONTEXT_DEFAULT_SIZE_SETTING", diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 43970283258bf..960d60c26777d 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github summary: API docs for the discover plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-disco | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 77 | 0 | 61 | 7 | +| 76 | 0 | 60 | 7 | ## Client diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index 76d0262f5cfe8..e8c18b01c1cfc 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github summary: API docs for the discoverEnhanced plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] warning: 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. --- diff --git a/api_docs/elastic_analytics.devdocs.json b/api_docs/elastic_analytics.devdocs.json deleted file mode 100644 index 0d1df6975ed2f..0000000000000 --- a/api_docs/elastic_analytics.devdocs.json +++ /dev/null @@ -1,1726 +0,0 @@ -{ - "id": "@elastic/analytics", - "client": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "server": { - "classes": [], - "functions": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.createAnalytics", - "type": "Function", - "tags": [], - "label": "createAnalytics", - "description": [ - "\nCreates an {@link AnalyticsClient}." - ], - "signature": [ - "(initContext: ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.AnalyticsClientInitContext", - "text": "AnalyticsClientInitContext" - }, - ") => ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.IAnalyticsClient", - "text": "IAnalyticsClient" - } - ], - "path": "packages/elastic-analytics/src/index.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.createAnalytics.$1", - "type": "Object", - "tags": [], - "label": "initContext", - "description": [ - "The initial context to create the client {@link AnalyticsClientInitContext }" - ], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.AnalyticsClientInitContext", - "text": "AnalyticsClientInitContext" - } - ], - "path": "packages/elastic-analytics/src/index.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - } - ], - "interfaces": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.AnalyticsClientInitContext", - "type": "Interface", - "tags": [], - "label": "AnalyticsClientInitContext", - "description": [ - "\nGeneral settings of the analytics client" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.AnalyticsClientInitContext.isDev", - "type": "boolean", - "tags": [], - "label": "isDev", - "description": [ - "\nBoolean indicating if it's running in developer mode." - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.AnalyticsClientInitContext.sendTo", - "type": "CompoundType", - "tags": [], - "label": "sendTo", - "description": [ - "\nSpecify if the shippers should send their data to the production or staging environments." - ], - "signature": [ - "\"staging\" | \"production\"" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.AnalyticsClientInitContext.logger", - "type": "Object", - "tags": [], - "label": "logger", - "description": [ - "\nApplication-provided logger." - ], - "signature": [ - "Logger" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.ContextProviderOpts", - "type": "Interface", - "tags": [], - "label": "ContextProviderOpts", - "description": [ - "\nDefinition of a context provider" - ], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.ContextProviderOpts", - "text": "ContextProviderOpts" - }, - "" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.ContextProviderOpts.name", - "type": "string", - "tags": [], - "label": "name", - "description": [ - "\nThe name of the provider." - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.ContextProviderOpts.context$", - "type": "Object", - "tags": [], - "label": "context$", - "description": [ - "\nObservable that emits the custom context." - ], - "signature": [ - "Observable", - "" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.ContextProviderOpts.schema", - "type": "Object", - "tags": [], - "label": "schema", - "description": [ - "\nSchema declaring and documenting the expected output in the context$\n" - ], - "signature": [ - "{ [Key in keyof Required]: ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaValue", - "text": "SchemaValue" - }, - "; }" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.Event", - "type": "Interface", - "tags": [], - "label": "Event", - "description": [ - "\nDefinition of the full event structure" - ], - "path": "packages/elastic-analytics/src/events/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.Event.timestamp", - "type": "string", - "tags": [], - "label": "timestamp", - "description": [ - "\nThe time the event was generated in ISO format." - ], - "path": "packages/elastic-analytics/src/events/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.Event.event_type", - "type": "string", - "tags": [], - "label": "event_type", - "description": [ - "\nThe event type." - ], - "path": "packages/elastic-analytics/src/events/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.Event.properties", - "type": "Object", - "tags": [], - "label": "properties", - "description": [ - "\nThe specific properties of the event type." - ], - "signature": [ - "{ [x: string]: unknown; }" - ], - "path": "packages/elastic-analytics/src/events/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.Event.context", - "type": "Object", - "tags": [], - "label": "context", - "description": [ - "\nThe {@link EventContext} enriched during the processing pipeline." - ], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.EventContext", - "text": "EventContext" - } - ], - "path": "packages/elastic-analytics/src/events/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.EventContext", - "type": "Interface", - "tags": [], - "label": "EventContext", - "description": [], - "path": "packages/elastic-analytics/src/events/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.EventContext.Unnamed", - "type": "IndexSignature", - "tags": [], - "label": "[key: string]: unknown", - "description": [], - "signature": [ - "[key: string]: unknown" - ], - "path": "packages/elastic-analytics/src/events/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.EventTypeOpts", - "type": "Interface", - "tags": [], - "label": "EventTypeOpts", - "description": [ - "\nDefinition of an Event Type." - ], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.EventTypeOpts", - "text": "EventTypeOpts" - }, - "" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.EventTypeOpts.eventType", - "type": "string", - "tags": [], - "label": "eventType", - "description": [ - "\nThe event type's unique name." - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.EventTypeOpts.schema", - "type": "Object", - "tags": [], - "label": "schema", - "description": [ - "\nSchema declaring and documenting the expected structure of this event type.\n" - ], - "signature": [ - "{ [Key in keyof Required]: ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaValue", - "text": "SchemaValue" - }, - "; }" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient", - "type": "Interface", - "tags": [], - "label": "IAnalyticsClient", - "description": [ - "\nAnalytics client's public APIs" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient.reportEvent", - "type": "Function", - "tags": [], - "label": "reportEvent", - "description": [ - "\nReports a telemetry event." - ], - "signature": [ - ">(eventType: string, eventData: EventTypeData) => void" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient.reportEvent.$1", - "type": "string", - "tags": [], - "label": "eventType", - "description": [ - "The event type registered via the `registerEventType` API." - ], - "signature": [ - "string" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient.reportEvent.$2", - "type": "Uncategorized", - "tags": [], - "label": "eventData", - "description": [ - "The properties matching the schema declared in the `registerEventType` API." - ], - "signature": [ - "EventTypeData" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient.registerEventType", - "type": "Function", - "tags": [], - "label": "registerEventType", - "description": [ - "\nRegisters the event type that will be emitted via the reportEvent API." - ], - "signature": [ - "(eventTypeOps: ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.EventTypeOpts", - "text": "EventTypeOpts" - }, - ") => void" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient.registerEventType.$1", - "type": "Object", - "tags": [], - "label": "eventTypeOps", - "description": [], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.EventTypeOpts", - "text": "EventTypeOpts" - }, - "" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient.registerShipper", - "type": "Function", - "tags": [], - "label": "registerShipper", - "description": [ - "\nSet up the shipper that will be used to report the telemetry events." - ], - "signature": [ - "(Shipper: ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.ShipperClassConstructor", - "text": "ShipperClassConstructor" - }, - ", shipperConfig: ShipperConfig, opts?: ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.RegisterShipperOpts", - "text": "RegisterShipperOpts" - }, - " | undefined) => void" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient.registerShipper.$1", - "type": "Object", - "tags": [], - "label": "Shipper", - "description": [ - "The {@link IShipper } class to instantiate the shipper." - ], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.ShipperClassConstructor", - "text": "ShipperClassConstructor" - }, - "" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient.registerShipper.$2", - "type": "Uncategorized", - "tags": [], - "label": "shipperConfig", - "description": [ - "The config specific to the Shipper to instantiate." - ], - "signature": [ - "ShipperConfig" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient.registerShipper.$3", - "type": "Object", - "tags": [], - "label": "opts", - "description": [ - "Additional options to register the shipper {@link RegisterShipperOpts }." - ], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.RegisterShipperOpts", - "text": "RegisterShipperOpts" - }, - " | undefined" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient.optIn", - "type": "Function", - "tags": [], - "label": "optIn", - "description": [ - "\nUsed to control the user's consent to report the data.\nIn the advanced mode, it allows to \"cherry-pick\" which events and shippers are enabled/disabled." - ], - "signature": [ - "(optInConfig: ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.OptInConfig", - "text": "OptInConfig" - }, - ") => void" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient.optIn.$1", - "type": "Object", - "tags": [], - "label": "optInConfig", - "description": [ - "{@link OptInConfig }" - ], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.OptInConfig", - "text": "OptInConfig" - } - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient.registerContextProvider", - "type": "Function", - "tags": [], - "label": "registerContextProvider", - "description": [ - "\nRegisters the context provider to enrich the any reported events." - ], - "signature": [ - "(contextProviderOpts: ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.ContextProviderOpts", - "text": "ContextProviderOpts" - }, - ") => void" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient.registerContextProvider.$1", - "type": "Object", - "tags": [], - "label": "contextProviderOpts", - "description": [ - "{@link ContextProviderOpts }" - ], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.ContextProviderOpts", - "text": "ContextProviderOpts" - }, - "" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient.removeContextProvider", - "type": "Function", - "tags": [], - "label": "removeContextProvider", - "description": [ - "\nRemoves the context provider and stop enriching the events from its context." - ], - "signature": [ - "(contextProviderName: string) => void" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient.removeContextProvider.$1", - "type": "string", - "tags": [], - "label": "contextProviderName", - "description": [ - "The name of the context provider to remove." - ], - "signature": [ - "string" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IAnalyticsClient.telemetryCounter$", - "type": "Object", - "tags": [], - "label": "telemetryCounter$", - "description": [ - "\nObservable to emit the stats of the processed events." - ], - "signature": [ - "Observable", - "<", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.TelemetryCounter", - "text": "TelemetryCounter" - }, - ">" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IShipper", - "type": "Interface", - "tags": [], - "label": "IShipper", - "description": [ - "\nBasic structure of a Shipper" - ], - "path": "packages/elastic-analytics/src/shippers/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IShipper.reportEvents", - "type": "Function", - "tags": [], - "label": "reportEvents", - "description": [ - "\nAdapts and ships the event to the persisting/analytics solution." - ], - "signature": [ - "(events: ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.Event", - "text": "Event" - }, - "[]) => void" - ], - "path": "packages/elastic-analytics/src/shippers/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IShipper.reportEvents.$1", - "type": "Array", - "tags": [], - "label": "events", - "description": [ - "batched events {@link Event }" - ], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.Event", - "text": "Event" - }, - "[]" - ], - "path": "packages/elastic-analytics/src/shippers/types.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IShipper.optIn", - "type": "Function", - "tags": [], - "label": "optIn", - "description": [ - "\nStops/restarts the shipping mechanism based on the value of isOptedIn" - ], - "signature": [ - "(isOptedIn: boolean) => void" - ], - "path": "packages/elastic-analytics/src/shippers/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IShipper.optIn.$1", - "type": "boolean", - "tags": [], - "label": "isOptedIn", - "description": [ - "`true` for resume sending events. `false` to stop." - ], - "signature": [ - "boolean" - ], - "path": "packages/elastic-analytics/src/shippers/types.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IShipper.extendContext", - "type": "Function", - "tags": [], - "label": "extendContext", - "description": [ - "\nPerform any necessary calls to the persisting/analytics solution to set the event's context." - ], - "signature": [ - "((newContext: ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.EventContext", - "text": "EventContext" - }, - ") => void) | undefined" - ], - "path": "packages/elastic-analytics/src/shippers/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IShipper.extendContext.$1", - "type": "Object", - "tags": [], - "label": "newContext", - "description": [], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.EventContext", - "text": "EventContext" - } - ], - "path": "packages/elastic-analytics/src/shippers/types.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.IShipper.telemetryCounter$", - "type": "Object", - "tags": [], - "label": "telemetryCounter$", - "description": [ - "\nObservable to emit the stats of the processed events." - ], - "signature": [ - "Observable", - "<", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.TelemetryCounter", - "text": "TelemetryCounter" - }, - "> | undefined" - ], - "path": "packages/elastic-analytics/src/shippers/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.OptInConfig", - "type": "Interface", - "tags": [], - "label": "OptInConfig", - "description": [ - "\n" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.OptInConfig.global", - "type": "Object", - "tags": [], - "label": "global", - "description": [ - "\nControls the global enabled/disabled behaviour of the client and shippers." - ], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.OptInConfigPerType", - "text": "OptInConfigPerType" - } - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.OptInConfig.event_types", - "type": "Object", - "tags": [], - "label": "event_types", - "description": [ - "\nControls if an event type should be disabled for a specific type of shipper." - ], - "signature": [ - "Record | undefined" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.OptInConfigPerType", - "type": "Interface", - "tags": [], - "label": "OptInConfigPerType", - "description": [ - "\nSets whether a type of event is enabled/disabled globally or per shipper." - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.OptInConfigPerType.enabled", - "type": "boolean", - "tags": [], - "label": "enabled", - "description": [ - "\nThe event type is globally enabled." - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.OptInConfigPerType.shippers", - "type": "Object", - "tags": [], - "label": "shippers", - "description": [ - "\nControls if an event type should be disabled for a specific type of shipper." - ], - "signature": [ - "Record | undefined" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.RegisterShipperOpts", - "type": "Interface", - "tags": [], - "label": "RegisterShipperOpts", - "description": [ - "\nOptional options to register a shipper" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "children": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.SchemaArray", - "type": "Interface", - "tags": [], - "label": "SchemaArray", - "description": [ - "\nSchema to represent an array" - ], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaArray", - "text": "SchemaArray" - }, - " extends ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaMeta", - "text": "SchemaMeta" - }, - "" - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.SchemaArray.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"array\"" - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.SchemaArray.items", - "type": "CompoundType", - "tags": [], - "label": "items", - "description": [], - "signature": [ - "{ type: \"pass_through\"; _meta: { description: string; } & ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaMetaOptional", - "text": "SchemaMetaOptional" - }, - "; } | (unknown extends Value ? ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaArray", - "text": "SchemaArray" - }, - " | ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaObject", - "text": "SchemaObject" - }, - " | ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaChildValue", - "text": "SchemaChildValue" - }, - " : NonNullable extends (infer U)[] ? ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaArray", - "text": "SchemaArray" - }, - " : NonNullable extends object ? ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaObject", - "text": "SchemaObject" - }, - " : ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaChildValue", - "text": "SchemaChildValue" - }, - ")" - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.SchemaChildValue", - "type": "Interface", - "tags": [], - "label": "SchemaChildValue", - "description": [], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaChildValue", - "text": "SchemaChildValue" - }, - "" - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.SchemaChildValue.type", - "type": "Uncategorized", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "NonNullable extends string ? ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.AllowedSchemaStringTypes", - "text": "AllowedSchemaStringTypes" - }, - " : NonNullable extends number ? ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.AllowedSchemaNumberTypes", - "text": "AllowedSchemaNumberTypes" - }, - " : NonNullable extends boolean ? \"boolean\" : ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.AllowedSchemaTypes", - "text": "AllowedSchemaTypes" - } - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.SchemaChildValue._meta", - "type": "CompoundType", - "tags": [], - "label": "_meta", - "description": [], - "signature": [ - "{ description: string; } & ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaMetaOptional", - "text": "SchemaMetaOptional" - }, - "" - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.SchemaMeta", - "type": "Interface", - "tags": [], - "label": "SchemaMeta", - "description": [ - "\nSchema meta with optional description" - ], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaMeta", - "text": "SchemaMeta" - }, - "" - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.SchemaMeta._meta", - "type": "CompoundType", - "tags": [], - "label": "_meta", - "description": [], - "signature": [ - "({ description?: string | undefined; } & ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaMetaOptional", - "text": "SchemaMetaOptional" - }, - ") | undefined" - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.SchemaObject", - "type": "Interface", - "tags": [], - "label": "SchemaObject", - "description": [ - "\nSchema to represent an object" - ], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaObject", - "text": "SchemaObject" - }, - " extends ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaMeta", - "text": "SchemaMeta" - }, - "" - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.SchemaObject.properties", - "type": "Object", - "tags": [], - "label": "properties", - "description": [], - "signature": [ - "{ [Key in keyof Required]: ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaValue", - "text": "SchemaValue" - }, - "; }" - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.ShipperClassConstructor", - "type": "Interface", - "tags": [], - "label": "ShipperClassConstructor", - "description": [ - "\nConstructor of a {@link IShipper}" - ], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.ShipperClassConstructor", - "text": "ShipperClassConstructor" - }, - "" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.ShipperClassConstructor.shipperName", - "type": "string", - "tags": [], - "label": "shipperName", - "description": [ - "\nThe shipper's unique name" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.ShipperClassConstructor.Unnamed", - "type": "Any", - "tags": [], - "label": "Unnamed", - "description": [ - "\nThe constructor" - ], - "signature": [ - "any" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.TelemetryCounter", - "type": "Interface", - "tags": [], - "label": "TelemetryCounter", - "description": [ - "\nShape of the events emitted by the telemetryCounter$ observable" - ], - "path": "packages/elastic-analytics/src/events/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.TelemetryCounter.type", - "type": "Enum", - "tags": [], - "label": "type", - "description": [ - "\nIndicates if the event contains data about succeeded, failed or dropped events." - ], - "signature": [ - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.TelemetryCounterType", - "text": "TelemetryCounterType" - } - ], - "path": "packages/elastic-analytics/src/events/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.TelemetryCounter.source", - "type": "string", - "tags": [], - "label": "source", - "description": [ - "\nWho emitted the event? It can be \"client\" or the name of the shipper." - ], - "path": "packages/elastic-analytics/src/events/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.TelemetryCounter.event_type", - "type": "string", - "tags": [], - "label": "event_type", - "description": [ - "\nThe event type the success/failure/drop event refers to." - ], - "path": "packages/elastic-analytics/src/events/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.TelemetryCounter.code", - "type": "string", - "tags": [], - "label": "code", - "description": [ - "\nCode to provide additional information about the success or failure. Examples are 200/400/504/ValidationError/UnknownError" - ], - "path": "packages/elastic-analytics/src/events/types.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.TelemetryCounter.count", - "type": "number", - "tags": [], - "label": "count", - "description": [ - "\nThe number of events that this counter refers to." - ], - "path": "packages/elastic-analytics/src/events/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - } - ], - "enums": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.TelemetryCounterType", - "type": "Enum", - "tags": [], - "label": "TelemetryCounterType", - "description": [ - "\nTypes of the Telemetry Counter: It allows to differentiate what happened to the events" - ], - "path": "packages/elastic-analytics/src/events/types.ts", - "deprecated": false, - "initialIsOpen": false - } - ], - "misc": [ - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.AllowedSchemaBooleanTypes", - "type": "Type", - "tags": [], - "label": "AllowedSchemaBooleanTypes", - "description": [ - "Types matching boolean values" - ], - "signature": [ - "\"boolean\"" - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.AllowedSchemaNumberTypes", - "type": "Type", - "tags": [], - "label": "AllowedSchemaNumberTypes", - "description": [ - "Types matching number values" - ], - "signature": [ - "\"date\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"double\"" - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.AllowedSchemaStringTypes", - "type": "Type", - "tags": [], - "label": "AllowedSchemaStringTypes", - "description": [ - "Types matching string values" - ], - "signature": [ - "\"keyword\" | \"date\" | \"text\"" - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.AllowedSchemaTypes", - "type": "Type", - "tags": [], - "label": "AllowedSchemaTypes", - "description": [ - "\nPossible type values in the schema" - ], - "signature": [ - "\"boolean\" | \"keyword\" | \"date\" | \"text\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"double\"" - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.EventType", - "type": "Type", - "tags": [], - "label": "EventType", - "description": [ - "\nEvent Type used for indexed structures. Only used to improve the readability of the types" - ], - "signature": [ - "string" - ], - "path": "packages/elastic-analytics/src/events/types.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.PossibleSchemaTypes", - "type": "Type", - "tags": [], - "label": "PossibleSchemaTypes", - "description": [ - "\nHelper to ensure the declared types match the schema types" - ], - "signature": [ - "Value extends string ? ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.AllowedSchemaStringTypes", - "text": "AllowedSchemaStringTypes" - }, - " : Value extends number ? ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.AllowedSchemaNumberTypes", - "text": "AllowedSchemaNumberTypes" - }, - " : Value extends boolean ? \"boolean\" : ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.AllowedSchemaTypes", - "text": "AllowedSchemaTypes" - } - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.RootSchema", - "type": "Type", - "tags": [], - "label": "RootSchema", - "description": [ - "\nSchema definition to match the structure of the properties provided.\n" - ], - "signature": [ - "{ [Key in keyof Required]: ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaValue", - "text": "SchemaValue" - }, - "; }" - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.SchemaMetaOptional", - "type": "Type", - "tags": [], - "label": "SchemaMetaOptional", - "description": [ - "\nEnforces { optional: true } if the value can be undefined" - ], - "signature": [ - "unknown extends Value ? { optional?: boolean | undefined; } : undefined extends Value ? { optional: true; } : { optional?: false | undefined; }" - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.SchemaValue", - "type": "Type", - "tags": [], - "label": "SchemaValue", - "description": [ - "\nType that defines all the possible values that the Schema accepts.\nThese types definitions are helping to identify earlier the possible missing `properties` nesting when\nmanually defining the schemas." - ], - "signature": [ - "{ type: \"pass_through\"; _meta: { description: string; } & ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaMetaOptional", - "text": "SchemaMetaOptional" - }, - "; } | (unknown extends Value ? ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaArray", - "text": "SchemaArray" - }, - " | ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaObject", - "text": "SchemaObject" - }, - " | ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaChildValue", - "text": "SchemaChildValue" - }, - " : NonNullable extends (infer U)[] ? ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaArray", - "text": "SchemaArray" - }, - " : NonNullable extends object ? ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaObject", - "text": "SchemaObject" - }, - " : ", - { - "pluginId": "@elastic/analytics", - "scope": "server", - "docId": "kibElasticAnalyticsPluginApi", - "section": "def-server.SchemaChildValue", - "text": "SchemaChildValue" - }, - ")" - ], - "path": "packages/elastic-analytics/src/schema/types.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/analytics", - "id": "def-server.ShipperName", - "type": "Type", - "tags": [], - "label": "ShipperName", - "description": [ - "\nShipper Name used for indexed structures. Only used to improve the readability of the types" - ], - "signature": [ - "string" - ], - "path": "packages/elastic-analytics/src/analytics_client/types.ts", - "deprecated": false, - "initialIsOpen": false - } - ], - "objects": [] - }, - "common": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - } -} \ No newline at end of file diff --git a/api_docs/elastic_analytics.mdx b/api_docs/elastic_analytics.mdx deleted file mode 100644 index 2d00c52f5e6a0..0000000000000 --- a/api_docs/elastic_analytics.mdx +++ /dev/null @@ -1,36 +0,0 @@ ---- -id: kibElasticAnalyticsPluginApi -slug: /kibana-dev-docs/api/elastic-analytics -title: "@elastic/analytics" -image: https://source.unsplash.com/400x175/?github -summary: API docs for the @elastic/analytics plugin -date: 2022-04-05 -tags: ['contributor', 'dev', 'apidocs', 'kibana', '@elastic/analytics'] -warning: 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. ---- -import elasticAnalyticsObj from './elastic_analytics.devdocs.json'; - - - -Contact [Owner missing] for questions regarding this plugin. - -**Code health stats** - -| Public API count | Any count | Items lacking comments | Missing exports | -|-------------------|-----------|------------------------|-----------------| -| 82 | 1 | 11 | 0 | - -## Server - -### Functions - - -### Interfaces - - -### Enums - - -### Consts, variables and types - - diff --git a/api_docs/elastic_apm_synthtrace.devdocs.json b/api_docs/elastic_apm_synthtrace.devdocs.json index 0e802d55936c3..aa59d75c2508a 100644 --- a/api_docs/elastic_apm_synthtrace.devdocs.json +++ b/api_docs/elastic_apm_synthtrace.devdocs.json @@ -64,20 +64,36 @@ { "parentPluginId": "@elastic/apm-synthtrace", "id": "def-server.ApmSynthtraceEsClient.Unnamed.$3", - "type": "boolean", + "type": "Object", "tags": [], - "label": "forceDataStreams", + "label": "options", "description": [], "signature": [ - "boolean" + "ApmSynthtraceEsClientOptions", + " | undefined" ], "path": "packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts", "deprecated": false, - "isRequired": true + "isRequired": false } ], "returnComment": [] }, + { + "parentPluginId": "@elastic/apm-synthtrace", + "id": "def-server.ApmSynthtraceEsClient.runningVersion", + "type": "Function", + "tags": [], + "label": "runningVersion", + "description": [], + "signature": [ + "() => Promise" + ], + "path": "packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "@elastic/apm-synthtrace", "id": "def-server.ApmSynthtraceEsClient.clean", @@ -123,6 +139,53 @@ ], "returnComment": [] }, + { + "parentPluginId": "@elastic/apm-synthtrace", + "id": "def-server.ApmSynthtraceEsClient.registerGcpRepository", + "type": "Function", + "tags": [], + "label": "registerGcpRepository", + "description": [], + "signature": [ + "(connectionString: string) => Promise" + ], + "path": "packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@elastic/apm-synthtrace", + "id": "def-server.ApmSynthtraceEsClient.registerGcpRepository.$1", + "type": "string", + "tags": [], + "label": "connectionString", + "description": [], + "signature": [ + "string" + ], + "path": "packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@elastic/apm-synthtrace", + "id": "def-server.ApmSynthtraceEsClient.refresh", + "type": "Function", + "tags": [], + "label": "refresh", + "description": [], + "signature": [ + "() => Promise<", + "ShardsOperationResponseBase", + ">" + ], + "path": "packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "@elastic/apm-synthtrace", "id": "def-server.ApmSynthtraceEsClient.index", @@ -131,27 +194,43 @@ "label": "index", "description": [], "signature": [ - "(events: ", + "(events: ", { "pluginId": "@elastic/apm-synthtrace", "scope": "server", "docId": "kibElasticApmSynthtracePluginApi", - "section": "def-server.SpanIterable", - "text": "SpanIterable" + "section": "def-server.EntityIterable", + "text": "EntityIterable" }, - " | ", + " | ", { "pluginId": "@elastic/apm-synthtrace", "scope": "server", "docId": "kibElasticApmSynthtracePluginApi", - "section": "def-server.SpanIterable", - "text": "SpanIterable" + "section": "def-server.EntityIterable", + "text": "EntityIterable" }, - "[], options?: ", + "[], options?: ", "StreamToBulkOptions", - " | undefined) => Promise<", - "ShardsOperationResponseBase", - ">" + "<", + { + "pluginId": "@elastic/apm-synthtrace", + "scope": "server", + "docId": "kibElasticApmSynthtracePluginApi", + "section": "def-server.ApmFields", + "text": "ApmFields" + }, + "> | undefined, streamProcessor?: ", + "StreamProcessor", + "<", + { + "pluginId": "@elastic/apm-synthtrace", + "scope": "server", + "docId": "kibElasticApmSynthtracePluginApi", + "section": "def-server.ApmFields", + "text": "ApmFields" + }, + "> | undefined) => Promise" ], "path": "packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts", "deprecated": false, @@ -168,18 +247,18 @@ "pluginId": "@elastic/apm-synthtrace", "scope": "server", "docId": "kibElasticApmSynthtracePluginApi", - "section": "def-server.SpanIterable", - "text": "SpanIterable" + "section": "def-server.EntityIterable", + "text": "EntityIterable" }, - " | ", + " | ", { "pluginId": "@elastic/apm-synthtrace", "scope": "server", "docId": "kibElasticApmSynthtracePluginApi", - "section": "def-server.SpanIterable", - "text": "SpanIterable" + "section": "def-server.EntityIterable", + "text": "EntityIterable" }, - "[]" + "[]" ], "path": "packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts", "deprecated": false, @@ -194,7 +273,38 @@ "description": [], "signature": [ "StreamToBulkOptions", - " | undefined" + "<", + { + "pluginId": "@elastic/apm-synthtrace", + "scope": "server", + "docId": "kibElasticApmSynthtracePluginApi", + "section": "def-server.ApmFields", + "text": "ApmFields" + }, + "> | undefined" + ], + "path": "packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts", + "deprecated": false, + "isRequired": false + }, + { + "parentPluginId": "@elastic/apm-synthtrace", + "id": "def-server.ApmSynthtraceEsClient.index.$3", + "type": "Object", + "tags": [], + "label": "streamProcessor", + "description": [], + "signature": [ + "StreamProcessor", + "<", + { + "pluginId": "@elastic/apm-synthtrace", + "scope": "server", + "docId": "kibElasticApmSynthtracePluginApi", + "section": "def-server.ApmFields", + "text": "ApmFields" + }, + "> | undefined" ], "path": "packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts", "deprecated": false, @@ -208,34 +318,35 @@ }, { "parentPluginId": "@elastic/apm-synthtrace", - "id": "def-server.SpanArrayIterable", + "id": "def-server.EntityArrayIterable", "type": "Class", "tags": [], - "label": "SpanArrayIterable", + "label": "EntityArrayIterable", "description": [], "signature": [ { "pluginId": "@elastic/apm-synthtrace", "scope": "server", "docId": "kibElasticApmSynthtracePluginApi", - "section": "def-server.SpanArrayIterable", - "text": "SpanArrayIterable" + "section": "def-server.EntityArrayIterable", + "text": "EntityArrayIterable" }, - " implements ", + " implements ", { "pluginId": "@elastic/apm-synthtrace", "scope": "server", "docId": "kibElasticApmSynthtracePluginApi", - "section": "def-server.SpanIterable", - "text": "SpanIterable" - } + "section": "def-server.EntityIterable", + "text": "EntityIterable" + }, + "" ], - "path": "packages/elastic-apm-synthtrace/src/lib/span_iterable.ts", + "path": "packages/elastic-apm-synthtrace/src/lib/entity_iterable.ts", "deprecated": false, "children": [ { "parentPluginId": "@elastic/apm-synthtrace", - "id": "def-server.SpanArrayIterable.Unnamed", + "id": "def-server.EntityArrayIterable.Unnamed", "type": "Function", "tags": [], "label": "Constructor", @@ -243,21 +354,20 @@ "signature": [ "any" ], - "path": "packages/elastic-apm-synthtrace/src/lib/span_iterable.ts", + "path": "packages/elastic-apm-synthtrace/src/lib/entity_iterable.ts", "deprecated": false, "children": [ { "parentPluginId": "@elastic/apm-synthtrace", - "id": "def-server.SpanArrayIterable.Unnamed.$1", + "id": "def-server.EntityArrayIterable.Unnamed.$1", "type": "Array", "tags": [], "label": "fields", "description": [], "signature": [ - "ApmFields", - "[]" + "TFields[]" ], - "path": "packages/elastic-apm-synthtrace/src/lib/span_iterable.ts", + "path": "packages/elastic-apm-synthtrace/src/lib/entity_iterable.ts", "deprecated": false, "isRequired": true } @@ -266,7 +376,7 @@ }, { "parentPluginId": "@elastic/apm-synthtrace", - "id": "def-server.SpanArrayIterable.order", + "id": "def-server.EntityArrayIterable.order", "type": "Function", "tags": [], "label": "order", @@ -274,51 +384,62 @@ "signature": [ "() => \"asc\" | \"desc\"" ], - "path": "packages/elastic-apm-synthtrace/src/lib/span_iterable.ts", + "path": "packages/elastic-apm-synthtrace/src/lib/entity_iterable.ts", "deprecated": false, "children": [], "returnComment": [] }, { "parentPluginId": "@elastic/apm-synthtrace", - "id": "def-server.SpanArrayIterable.Symbol.asyncIterator", + "id": "def-server.EntityArrayIterable.ratePerMinute", + "type": "Function", + "tags": [], + "label": "ratePerMinute", + "description": [], + "signature": [ + "() => number" + ], + "path": "packages/elastic-apm-synthtrace/src/lib/entity_iterable.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@elastic/apm-synthtrace", + "id": "def-server.EntityArrayIterable.Symbol.asyncIterator", "type": "Function", "tags": [], "label": "[Symbol.asyncIterator]", "description": [], "signature": [ - "() => AsyncIterator<", - "ApmFields", - ", any, undefined>" + "() => AsyncIterator" ], - "path": "packages/elastic-apm-synthtrace/src/lib/span_iterable.ts", + "path": "packages/elastic-apm-synthtrace/src/lib/entity_iterable.ts", "deprecated": false, "children": [], "returnComment": [] }, { "parentPluginId": "@elastic/apm-synthtrace", - "id": "def-server.SpanArrayIterable.Symbol.iterator", + "id": "def-server.EntityArrayIterable.Symbol.iterator", "type": "Function", "tags": [], "label": "[Symbol.iterator]", "description": [], "signature": [ - "() => Iterator<", - "ApmFields", - ", any, undefined>" + "() => Iterator" ], - "path": "packages/elastic-apm-synthtrace/src/lib/span_iterable.ts", + "path": "packages/elastic-apm-synthtrace/src/lib/entity_iterable.ts", "deprecated": false, "children": [], "returnComment": [] }, { "parentPluginId": "@elastic/apm-synthtrace", - "id": "def-server.SpanArrayIterable.concat", + "id": "def-server.EntityArrayIterable.merge", "type": "Function", "tags": [], - "label": "concat", + "label": "merge", "description": [], "signature": [ "(...iterables: ", @@ -326,18 +447,19 @@ "pluginId": "@elastic/apm-synthtrace", "scope": "server", "docId": "kibElasticApmSynthtracePluginApi", - "section": "def-server.SpanIterable", - "text": "SpanIterable" + "section": "def-server.EntityIterable", + "text": "EntityIterable" }, - "[]) => ", - "SpanGeneratorsUnion" + "[]) => ", + "EntityStreams", + "" ], - "path": "packages/elastic-apm-synthtrace/src/lib/span_iterable.ts", + "path": "packages/elastic-apm-synthtrace/src/lib/entity_iterable.ts", "deprecated": false, "children": [ { "parentPluginId": "@elastic/apm-synthtrace", - "id": "def-server.SpanArrayIterable.concat.$1", + "id": "def-server.EntityArrayIterable.merge.$1", "type": "Array", "tags": [], "label": "iterables", @@ -347,12 +469,12 @@ "pluginId": "@elastic/apm-synthtrace", "scope": "server", "docId": "kibElasticApmSynthtracePluginApi", - "section": "def-server.SpanIterable", - "text": "SpanIterable" + "section": "def-server.EntityIterable", + "text": "EntityIterable" }, - "[]" + "[]" ], - "path": "packages/elastic-apm-synthtrace/src/lib/span_iterable.ts", + "path": "packages/elastic-apm-synthtrace/src/lib/entity_iterable.ts", "deprecated": false, "isRequired": true } @@ -361,17 +483,15 @@ }, { "parentPluginId": "@elastic/apm-synthtrace", - "id": "def-server.SpanArrayIterable.toArray", + "id": "def-server.EntityArrayIterable.toArray", "type": "Function", "tags": [], "label": "toArray", "description": [], "signature": [ - "() => ", - "ApmFields", - "[]" + "() => TFields[]" ], - "path": "packages/elastic-apm-synthtrace/src/lib/span_iterable.ts", + "path": "packages/elastic-apm-synthtrace/src/lib/entity_iterable.ts", "deprecated": false, "children": [], "returnComment": [] @@ -569,57 +689,27 @@ }, { "parentPluginId": "@elastic/apm-synthtrace", - "id": "def-server.Fields", - "type": "Interface", - "tags": [], - "label": "Fields", - "description": [], - "path": "packages/elastic-apm-synthtrace/src/lib/entity.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/apm-synthtrace", - "id": "def-server.Fields.timestamp", - "type": "number", - "tags": [], - "label": "'@timestamp'", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "packages/elastic-apm-synthtrace/src/lib/entity.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/apm-synthtrace", - "id": "def-server.SpanIterable", + "id": "def-server.EntityIterable", "type": "Interface", "tags": [], - "label": "SpanIterable", + "label": "EntityIterable", "description": [], "signature": [ { "pluginId": "@elastic/apm-synthtrace", "scope": "server", "docId": "kibElasticApmSynthtracePluginApi", - "section": "def-server.SpanIterable", - "text": "SpanIterable" + "section": "def-server.EntityIterable", + "text": "EntityIterable" }, - " extends Iterable<", - "ApmFields", - ">,AsyncIterable<", - "ApmFields", - ">" + " extends Iterable,AsyncIterable" ], - "path": "packages/elastic-apm-synthtrace/src/lib/span_iterable.ts", + "path": "packages/elastic-apm-synthtrace/src/lib/entity_iterable.ts", "deprecated": false, "children": [ { "parentPluginId": "@elastic/apm-synthtrace", - "id": "def-server.SpanIterable.order", + "id": "def-server.EntityIterable.order", "type": "Function", "tags": [], "label": "order", @@ -627,34 +717,55 @@ "signature": [ "() => \"asc\" | \"desc\"" ], - "path": "packages/elastic-apm-synthtrace/src/lib/span_iterable.ts", + "path": "packages/elastic-apm-synthtrace/src/lib/entity_iterable.ts", "deprecated": false, "children": [], "returnComment": [] }, { "parentPluginId": "@elastic/apm-synthtrace", - "id": "def-server.SpanIterable.toArray", + "id": "def-server.EntityIterable.ratePerMinute", + "type": "Function", + "tags": [], + "label": "ratePerMinute", + "description": [], + "signature": [ + "() => number" + ], + "path": "packages/elastic-apm-synthtrace/src/lib/entity_iterable.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@elastic/apm-synthtrace", + "id": "def-server.EntityIterable.toArray", "type": "Function", "tags": [], "label": "toArray", "description": [], "signature": [ "() => ", - "ApmFields", + { + "pluginId": "@elastic/apm-synthtrace", + "scope": "server", + "docId": "kibElasticApmSynthtracePluginApi", + "section": "def-server.ApmFields", + "text": "ApmFields" + }, "[]" ], - "path": "packages/elastic-apm-synthtrace/src/lib/span_iterable.ts", + "path": "packages/elastic-apm-synthtrace/src/lib/entity_iterable.ts", "deprecated": false, "children": [], "returnComment": [] }, { "parentPluginId": "@elastic/apm-synthtrace", - "id": "def-server.SpanIterable.concat", + "id": "def-server.EntityIterable.merge", "type": "Function", "tags": [], - "label": "concat", + "label": "merge", "description": [], "signature": [ "(...iterables: ", @@ -662,18 +773,19 @@ "pluginId": "@elastic/apm-synthtrace", "scope": "server", "docId": "kibElasticApmSynthtracePluginApi", - "section": "def-server.SpanIterable", - "text": "SpanIterable" + "section": "def-server.EntityIterable", + "text": "EntityIterable" }, - "[]) => ", - "SpanGeneratorsUnion" + "[]) => ", + "EntityStreams", + "" ], - "path": "packages/elastic-apm-synthtrace/src/lib/span_iterable.ts", + "path": "packages/elastic-apm-synthtrace/src/lib/entity_iterable.ts", "deprecated": false, "children": [ { "parentPluginId": "@elastic/apm-synthtrace", - "id": "def-server.SpanIterable.concat.$1", + "id": "def-server.EntityIterable.merge.$1", "type": "Array", "tags": [], "label": "iterables", @@ -683,12 +795,12 @@ "pluginId": "@elastic/apm-synthtrace", "scope": "server", "docId": "kibElasticApmSynthtracePluginApi", - "section": "def-server.SpanIterable", - "text": "SpanIterable" + "section": "def-server.EntityIterable", + "text": "EntityIterable" }, - "[]" + "[]" ], - "path": "packages/elastic-apm-synthtrace/src/lib/span_iterable.ts", + "path": "packages/elastic-apm-synthtrace/src/lib/entity_iterable.ts", "deprecated": false, "isRequired": true } @@ -697,6 +809,32 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "@elastic/apm-synthtrace", + "id": "def-server.Fields", + "type": "Interface", + "tags": [], + "label": "Fields", + "description": [], + "path": "packages/elastic-apm-synthtrace/src/lib/entity.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@elastic/apm-synthtrace", + "id": "def-server.Fields.timestamp", + "type": "number", + "tags": [], + "label": "'@timestamp'", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/elastic-apm-synthtrace/src/lib/entity.ts", + "deprecated": false + } + ], + "initialIsOpen": false } ], "enums": [ @@ -712,7 +850,39 @@ "initialIsOpen": false } ], - "misc": [], + "misc": [ + { + "parentPluginId": "@elastic/apm-synthtrace", + "id": "def-server.ApmFields", + "type": "Type", + "tags": [], + "label": "ApmFields", + "description": [], + "signature": [ + { + "pluginId": "@elastic/apm-synthtrace", + "scope": "server", + "docId": "kibElasticApmSynthtracePluginApi", + "section": "def-server.Fields", + "text": "Fields" + }, + " & Partial<{ 'timestamp.us'?: number | undefined; 'agent.name': string; 'agent.version': string; 'container.id': string; 'ecs.version': string; 'event.outcome': string; 'event.ingested': number; 'error.id': string; 'error.exception': ", + { + "pluginId": "@elastic/apm-synthtrace", + "scope": "server", + "docId": "kibElasticApmSynthtracePluginApi", + "section": "def-server.ApmException", + "text": "ApmException" + }, + "[]; 'error.grouping_name': string; 'error.grouping_key': string; 'host.name': string; 'kubernetes.pod.uid': string; 'metricset.name': string; observer: ", + "Observer", + "; 'parent.id': string; 'processor.event': string; 'processor.name': string; 'trace.id': string; 'transaction.name': string; 'transaction.type': string; 'transaction.id': string; 'transaction.duration.us': number; 'transaction.duration.histogram': { values: number[]; counts: number[]; }; 'transaction.sampled': true; 'service.name': string; 'service.version': string; 'service.environment': string; 'service.node.name': string; 'service.runtime.name': string; 'service.runtime.version': string; 'service.framework.name': string; 'span.id': string; 'span.name': string; 'span.type': string; 'span.subtype': string; 'span.duration.us': number; 'span.destination.service.name': string; 'span.destination.service.resource': string; 'span.destination.service.type': string; 'span.destination.service.response_time.sum.us': number; 'span.destination.service.response_time.count': number; 'span.self_time.count': number; 'span.self_time.sum.us': number; 'cloud.provider': string; 'cloud.project.name': string; 'cloud.service.name': string; 'cloud.availability_zone': string; 'cloud.machine.type': string; 'cloud.region': string; 'host.os.platform': string; 'faas.id': string; 'faas.coldstart': boolean; 'faas.execution': string; 'faas.trigger.type': string; 'faas.trigger.request_id': string; }> & Partial<{ 'system.process.memory.size': number; 'system.memory.actual.free': number; 'system.memory.total': number; 'system.cpu.total.norm.pct': number; 'system.process.memory.rss.bytes': number; 'system.process.cpu.total.norm.pct': number; 'jvm.memory.heap.used': number; 'jvm.memory.non_heap.used': number; 'jvm.thread.count': number; }>" + ], + "path": "packages/elastic-apm-synthtrace/src/lib/apm/apm_fields.ts", + "deprecated": false, + "initialIsOpen": false + } + ], "objects": [ { "parentPluginId": "@elastic/apm-synthtrace", @@ -830,7 +1000,13 @@ "description": [], "signature": [ "(events: ", - "ApmFields", + { + "pluginId": "@elastic/apm-synthtrace", + "scope": "server", + "docId": "kibElasticApmSynthtracePluginApi", + "section": "def-server.ApmFields", + "text": "ApmFields" + }, "[]) => { 'metricset.name': string; 'transaction.duration.histogram': { values: number[]; counts: number[]; }; _doc_count: number; '@timestamp'?: number | undefined; 'timestamp.us'?: number | undefined; 'agent.name'?: string | undefined; 'agent.version'?: string | undefined; 'container.id'?: string | undefined; 'ecs.version'?: string | undefined; 'event.outcome'?: string | undefined; 'event.ingested'?: number | undefined; 'error.id'?: string | undefined; 'error.exception'?: ", { "pluginId": "@elastic/apm-synthtrace", @@ -855,7 +1031,13 @@ "label": "events", "description": [], "signature": [ - "ApmFields", + { + "pluginId": "@elastic/apm-synthtrace", + "scope": "server", + "docId": "kibElasticApmSynthtracePluginApi", + "section": "def-server.ApmFields", + "text": "ApmFields" + }, "[]" ], "path": "packages/elastic-apm-synthtrace/src/lib/apm/processors/get_transaction_metrics.ts", @@ -872,7 +1054,13 @@ "description": [], "signature": [ "(events: ", - "ApmFields", + { + "pluginId": "@elastic/apm-synthtrace", + "scope": "server", + "docId": "kibElasticApmSynthtracePluginApi", + "section": "def-server.ApmFields", + "text": "ApmFields" + }, "[]) => { \"metricset.name\": string; 'span.destination.service.response_time.sum.us': number; 'span.destination.service.response_time.count': number; '@timestamp'?: number | undefined; 'timestamp.us'?: number | undefined; 'agent.name'?: string | undefined; 'agent.version'?: string | undefined; 'container.id'?: string | undefined; 'ecs.version'?: string | undefined; 'event.outcome'?: string | undefined; 'event.ingested'?: number | undefined; 'error.id'?: string | undefined; 'error.exception'?: ", { "pluginId": "@elastic/apm-synthtrace", @@ -897,7 +1085,13 @@ "label": "events", "description": [], "signature": [ - "ApmFields", + { + "pluginId": "@elastic/apm-synthtrace", + "scope": "server", + "docId": "kibElasticApmSynthtracePluginApi", + "section": "def-server.ApmFields", + "text": "ApmFields" + }, "[]" ], "path": "packages/elastic-apm-synthtrace/src/lib/apm/processors/get_span_destination_metrics.ts", @@ -929,9 +1123,21 @@ "description": [], "signature": [ "(events: ", - "ApmFields", + { + "pluginId": "@elastic/apm-synthtrace", + "scope": "server", + "docId": "kibElasticApmSynthtracePluginApi", + "section": "def-server.ApmFields", + "text": "ApmFields" + }, "[]) => ", - "ApmFields", + { + "pluginId": "@elastic/apm-synthtrace", + "scope": "server", + "docId": "kibElasticApmSynthtracePluginApi", + "section": "def-server.ApmFields", + "text": "ApmFields" + }, "[]" ], "path": "packages/elastic-apm-synthtrace/src/lib/apm/index.ts", @@ -946,7 +1152,13 @@ "label": "events", "description": [], "signature": [ - "ApmFields", + { + "pluginId": "@elastic/apm-synthtrace", + "scope": "server", + "docId": "kibElasticApmSynthtracePluginApi", + "section": "def-server.ApmFields", + "text": "ApmFields" + }, "[]" ], "path": "packages/elastic-apm-synthtrace/src/lib/apm/processors/get_breakdown_metrics.ts", @@ -962,9 +1174,9 @@ "label": "getApmWriteTargets", "description": [], "signature": [ - "({ client, forceDataStreams, }: { client: ", + "({ client, forceLegacyIndices, }: { client: ", "default", - "; forceDataStreams?: boolean | undefined; }) => Promise<", + "; forceLegacyIndices?: boolean | undefined; }) => Promise<", "ApmElasticsearchOutputWriteTargets", ">" ], @@ -982,7 +1194,7 @@ "signature": [ "{ client: ", "default", - "; forceDataStreams?: boolean | undefined; }" + "; forceLegacyIndices?: boolean | undefined; }" ], "path": "packages/elastic-apm-synthtrace/src/lib/apm/utils/get_apm_write_targets.ts", "deprecated": false diff --git a/api_docs/elastic_apm_synthtrace.mdx b/api_docs/elastic_apm_synthtrace.mdx index a1ce9ad2020bd..727d8b367a3a2 100644 --- a/api_docs/elastic_apm_synthtrace.mdx +++ b/api_docs/elastic_apm_synthtrace.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/elastic-apm-synthtrace title: "@elastic/apm-synthtrace" image: https://source.unsplash.com/400x175/?github summary: API docs for the @elastic/apm-synthtrace plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@elastic/apm-synthtrace'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 62 | 0 | 62 | 9 | +| 70 | 0 | 70 | 10 | ## Server @@ -37,3 +37,6 @@ Contact [Owner missing] for questions regarding this plugin. ### Enums +### Consts, variables and types + + diff --git a/api_docs/elastic_datemath.devdocs.json b/api_docs/elastic_datemath.devdocs.json deleted file mode 100644 index 166f816481b33..0000000000000 --- a/api_docs/elastic_datemath.devdocs.json +++ /dev/null @@ -1,578 +0,0 @@ -{ - "id": "@elastic/datemath", - "client": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "server": { - "classes": [], - "functions": [ - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.parse", - "type": "Function", - "tags": [], - "label": "parse", - "description": [], - "signature": [ - "(input: string, options: { roundUp?: boolean | undefined; momentInstance?: typeof moment | undefined; forceNow?: Date | undefined; }) => moment.Moment | undefined" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.parse.$1", - "type": "string", - "tags": [], - "label": "input", - "description": [], - "signature": [ - "string" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.parse.$2", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.parse.$2.roundUp", - "type": "CompoundType", - "tags": [], - "label": "roundUp", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.parse.$2.momentInstance", - "type": "Function", - "tags": [], - "label": "momentInstance", - "description": [], - "signature": [ - "typeof moment | undefined" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.parse.$2.forceNow", - "type": "Object", - "tags": [], - "label": "forceNow", - "description": [], - "signature": [ - "Date | undefined" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - } - ] - } - ], - "returnComment": [], - "initialIsOpen": false - } - ], - "interfaces": [], - "enums": [], - "misc": [ - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.Unit", - "type": "Type", - "tags": [], - "label": "Unit", - "description": [], - "signature": [ - "\"y\" | \"M\" | \"w\" | \"d\" | \"h\" | \"m\" | \"s\" | \"ms\"" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.units", - "type": "Array", - "tags": [], - "label": "units", - "description": [], - "signature": [ - { - "pluginId": "@elastic/datemath", - "scope": "server", - "docId": "kibElasticDatemathPluginApi", - "section": "def-server.Unit", - "text": "Unit" - }, - "[]" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsAsc", - "type": "Array", - "tags": [], - "label": "unitsAsc", - "description": [], - "signature": [ - { - "pluginId": "@elastic/datemath", - "scope": "server", - "docId": "kibElasticDatemathPluginApi", - "section": "def-server.Unit", - "text": "Unit" - }, - "[]" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsDesc", - "type": "Array", - "tags": [], - "label": "unitsDesc", - "description": [], - "signature": [ - { - "pluginId": "@elastic/datemath", - "scope": "server", - "docId": "kibElasticDatemathPluginApi", - "section": "def-server.Unit", - "text": "Unit" - }, - "[]" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.UnitsMap", - "type": "Type", - "tags": [], - "label": "UnitsMap", - "description": [], - "signature": [ - "{ y: { weight: number; type: \"calendar\" | \"fixed\" | \"mixed\"; base: number; }; M: { weight: number; type: \"calendar\" | \"fixed\" | \"mixed\"; base: number; }; w: { weight: number; type: \"calendar\" | \"fixed\" | \"mixed\"; base: number; }; d: { weight: number; type: \"calendar\" | \"fixed\" | \"mixed\"; base: number; }; h: { weight: number; type: \"calendar\" | \"fixed\" | \"mixed\"; base: number; }; m: { weight: number; type: \"calendar\" | \"fixed\" | \"mixed\"; base: number; }; s: { weight: number; type: \"calendar\" | \"fixed\" | \"mixed\"; base: number; }; ms: { weight: number; type: \"calendar\" | \"fixed\" | \"mixed\"; base: number; }; }" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "initialIsOpen": false - } - ], - "objects": [ - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap", - "type": "Object", - "tags": [], - "label": "unitsMap", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.ms", - "type": "Object", - "tags": [], - "label": "ms", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.ms.weight", - "type": "number", - "tags": [], - "label": "weight", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.ms.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"fixed\"" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.ms.base", - "type": "number", - "tags": [], - "label": "base", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.s", - "type": "Object", - "tags": [], - "label": "s", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.s.weight", - "type": "number", - "tags": [], - "label": "weight", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.s.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"fixed\"" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.s.base", - "type": "number", - "tags": [], - "label": "base", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.m", - "type": "Object", - "tags": [], - "label": "m", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.m.weight", - "type": "number", - "tags": [], - "label": "weight", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.m.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"mixed\"" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.m.base", - "type": "number", - "tags": [], - "label": "base", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.h", - "type": "Object", - "tags": [], - "label": "h", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.h.weight", - "type": "number", - "tags": [], - "label": "weight", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.h.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"mixed\"" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.h.base", - "type": "number", - "tags": [], - "label": "base", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.d", - "type": "Object", - "tags": [], - "label": "d", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.d.weight", - "type": "number", - "tags": [], - "label": "weight", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.d.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"mixed\"" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.d.base", - "type": "number", - "tags": [], - "label": "base", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.w", - "type": "Object", - "tags": [], - "label": "w", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.w.weight", - "type": "number", - "tags": [], - "label": "weight", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.w.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"calendar\"" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.w.base", - "type": "number", - "tags": [], - "label": "base", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.M", - "type": "Object", - "tags": [], - "label": "M", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.M.weight", - "type": "number", - "tags": [], - "label": "weight", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.M.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"calendar\"" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.M.base", - "type": "number", - "tags": [], - "label": "base", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - } - ] - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.y", - "type": "Object", - "tags": [], - "label": "y", - "description": [ - "// q: { weight: 8, type: 'calendar' }, // TODO: moment duration does not support quarter" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.y.weight", - "type": "number", - "tags": [], - "label": "weight", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.y.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"calendar\"" - ], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - }, - { - "parentPluginId": "@elastic/datemath", - "id": "def-server.unitsMap.y.base", - "type": "number", - "tags": [], - "label": "base", - "description": [], - "path": "packages/elastic-datemath/src/index.ts", - "deprecated": false - } - ] - } - ], - "initialIsOpen": false - } - ] - }, - "common": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - } -} \ No newline at end of file diff --git a/api_docs/elastic_datemath.mdx b/api_docs/elastic_datemath.mdx deleted file mode 100644 index 14fd7fef84034..0000000000000 --- a/api_docs/elastic_datemath.mdx +++ /dev/null @@ -1,33 +0,0 @@ ---- -id: kibElasticDatemathPluginApi -slug: /kibana-dev-docs/api/elastic-datemath -title: "@elastic/datemath" -image: https://source.unsplash.com/400x175/?github -summary: API docs for the @elastic/datemath plugin -date: 2022-04-05 -tags: ['contributor', 'dev', 'apidocs', 'kibana', '@elastic/datemath'] -warning: 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. ---- -import elasticDatemathObj from './elastic_datemath.devdocs.json'; - -elasticsearch datemath parser, used in kibana - -Contact [Owner missing] for questions regarding this plugin. - -**Code health stats** - -| Public API count | Any count | Items lacking comments | Missing exports | -|-------------------|-----------|------------------------|-----------------| -| 44 | 0 | 43 | 0 | - -## Server - -### Objects - - -### Functions - - -### Consts, variables and types - - diff --git a/api_docs/embeddable.devdocs.json b/api_docs/embeddable.devdocs.json index 4ff19a79a26b4..d8d5fd3006360 100644 --- a/api_docs/embeddable.devdocs.json +++ b/api_docs/embeddable.devdocs.json @@ -8749,7 +8749,7 @@ "section": "def-public.EmbeddableFactory", "text": "EmbeddableFactory" }, - ", \"type\" | \"create\" | \"isEditable\" | \"getDisplayName\"> & Partial, \"create\" | \"type\" | \"isEditable\" | \"getDisplayName\"> & Partial; readonly accessCheckTimeout: number; readonly accessCheckTimeoutWarning: number; }" + "{ readonly host?: string | undefined; readonly customHeaders?: Readonly<{} & {}> | undefined; readonly ssl: Readonly<{ certificateAuthorities?: string | string[] | undefined; } & { verificationMode: \"none\" | \"full\" | \"certificate\"; }>; readonly accessCheckTimeout: number; readonly accessCheckTimeoutWarning: number; }" ], "path": "x-pack/plugins/enterprise_search/server/index.ts", "deprecated": false, @@ -51,7 +51,9 @@ "Type", "; verificationMode: ", "Type", - "<\"none\" | \"full\" | \"certificate\">; }>; }>" + "<\"none\" | \"full\" | \"certificate\">; }>; customHeaders: ", + "Type", + " | undefined>; }>" ], "path": "x-pack/plugins/enterprise_search/server/index.ts", "deprecated": false, diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index 8ea1f6d5c47b3..4e4c43e2977c1 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github summary: API docs for the enterpriseSearch plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] warning: 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. --- diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index 3f8b8a4e25633..937c0be2c8ba3 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github summary: API docs for the esUiShared plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] warning: 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. --- diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index db8d13ecbffad..5e3d34fcae086 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github summary: API docs for the eventAnnotation plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] warning: 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. --- diff --git a/api_docs/event_log.devdocs.json b/api_docs/event_log.devdocs.json index cf94115cf3b25..1a4840efc4729 100644 --- a/api_docs/event_log.devdocs.json +++ b/api_docs/event_log.devdocs.json @@ -1047,7 +1047,7 @@ "label": "data", "description": [], "signature": [ - "(Readonly<{ user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; error?: Readonly<{ message?: string | undefined; type?: string | undefined; id?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; tags?: string[] | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: number | undefined; number_of_scheduled_actions?: number | undefined; number_of_searches?: number | undefined; total_indexing_duration_ms?: number | undefined; es_search_duration_ms?: number | undefined; total_search_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: number | undefined; scheduled?: string | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; outcome?: string | undefined; category?: string[] | undefined; url?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: number | undefined; code?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: number | undefined; created?: string | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}> | undefined)[]" + "(Readonly<{ error?: Readonly<{ message?: string | undefined; id?: string | undefined; type?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; tags?: string[] | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: number | undefined; number_of_generated_actions?: number | undefined; number_of_new_alerts?: number | undefined; number_of_active_alerts?: number | undefined; number_of_recovered_alerts?: number | undefined; total_number_of_alerts?: number | undefined; number_of_searches?: number | undefined; total_indexing_duration_ms?: number | undefined; es_search_duration_ms?: number | undefined; total_search_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; saved_objects?: Readonly<{ id?: string | undefined; type?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: number | undefined; scheduled?: string | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; id?: string | undefined; type?: string[] | undefined; outcome?: string | undefined; category?: string[] | undefined; url?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: number | undefined; code?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: number | undefined; created?: string | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}> | undefined)[]" ], "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", "deprecated": false @@ -1066,7 +1066,7 @@ "label": "IEvent", "description": [], "signature": [ - "DeepPartial | undefined; message?: string | undefined; error?: Readonly<{ message?: string | undefined; type?: string | undefined; id?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; tags?: string[] | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: number | undefined; number_of_scheduled_actions?: number | undefined; number_of_searches?: number | undefined; total_indexing_duration_ms?: number | undefined; es_search_duration_ms?: number | undefined; total_search_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: number | undefined; scheduled?: string | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; outcome?: string | undefined; category?: string[] | undefined; url?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: number | undefined; code?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: number | undefined; created?: string | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}>>> | undefined" + "DeepPartial | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; tags?: string[] | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: number | undefined; number_of_generated_actions?: number | undefined; number_of_new_alerts?: number | undefined; number_of_active_alerts?: number | undefined; number_of_recovered_alerts?: number | undefined; total_number_of_alerts?: number | undefined; number_of_searches?: number | undefined; total_indexing_duration_ms?: number | undefined; es_search_duration_ms?: number | undefined; total_search_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; saved_objects?: Readonly<{ id?: string | undefined; type?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: number | undefined; scheduled?: string | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; id?: string | undefined; type?: string[] | undefined; outcome?: string | undefined; category?: string[] | undefined; url?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: number | undefined; code?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: number | undefined; created?: string | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}>>> | undefined" ], "path": "x-pack/plugins/event_log/generated/schemas.ts", "deprecated": false, @@ -1080,7 +1080,7 @@ "label": "IValidatedEvent", "description": [], "signature": [ - "Readonly<{ user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; error?: Readonly<{ message?: string | undefined; type?: string | undefined; id?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; tags?: string[] | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: number | undefined; number_of_scheduled_actions?: number | undefined; number_of_searches?: number | undefined; total_indexing_duration_ms?: number | undefined; es_search_duration_ms?: number | undefined; total_search_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: number | undefined; scheduled?: string | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; outcome?: string | undefined; category?: string[] | undefined; url?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: number | undefined; code?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: number | undefined; created?: string | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}> | undefined" + "Readonly<{ error?: Readonly<{ message?: string | undefined; id?: string | undefined; type?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; message?: string | undefined; tags?: string[] | undefined; kibana?: Readonly<{ alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: number | undefined; number_of_generated_actions?: number | undefined; number_of_new_alerts?: number | undefined; number_of_active_alerts?: number | undefined; number_of_recovered_alerts?: number | undefined; total_number_of_alerts?: number | undefined; number_of_searches?: number | undefined; total_indexing_duration_ms?: number | undefined; es_search_duration_ms?: number | undefined; total_search_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; } & {}> | undefined; version?: string | undefined; saved_objects?: Readonly<{ id?: string | undefined; type?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: number | undefined; scheduled?: string | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; id?: string | undefined; type?: string[] | undefined; outcome?: string | undefined; category?: string[] | undefined; url?: string | undefined; end?: string | undefined; original?: string | undefined; duration?: number | undefined; code?: string | undefined; action?: string | undefined; kind?: string | undefined; hash?: string | undefined; severity?: number | undefined; created?: string | undefined; dataset?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; } & {}> | undefined" ], "path": "x-pack/plugins/event_log/generated/schemas.ts", "deprecated": false, diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index 40049f0fbe11d..562f7fee9dbbb 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github summary: API docs for the eventLog plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] warning: 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. --- diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index 47da67003080d..22245b38031d1 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionError plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] warning: 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. --- diff --git a/api_docs/expression_gauge.devdocs.json b/api_docs/expression_gauge.devdocs.json index 63e409a1a6c3a..111082f04d28a 100644 --- a/api_docs/expression_gauge.devdocs.json +++ b/api_docs/expression_gauge.devdocs.json @@ -103,13 +103,7 @@ "text": "Accessors" }, " | undefined, paletteParams?: ", - { - "pluginId": "expressionGauge", - "scope": "common", - "docId": "kibExpressionGaugePluginApi", - "section": "def-common.CustomPaletteParams", - "text": "CustomPaletteParams" - }, + "CustomPaletteParams", " | undefined, isRespectRanges?: boolean | undefined) => number" ], "path": "src/plugins/chart_expressions/expression_gauge/public/components/utils/accessors.ts", @@ -165,13 +159,7 @@ "label": "paletteParams", "description": [], "signature": [ - { - "pluginId": "expressionGauge", - "scope": "common", - "docId": "kibExpressionGaugePluginApi", - "section": "def-common.CustomPaletteParams", - "text": "CustomPaletteParams" - }, + "CustomPaletteParams", " | undefined" ], "path": "src/plugins/chart_expressions/expression_gauge/public/components/utils/accessors.ts", @@ -221,13 +209,7 @@ "text": "Accessors" }, " | undefined, paletteParams?: ", - { - "pluginId": "expressionGauge", - "scope": "common", - "docId": "kibExpressionGaugePluginApi", - "section": "def-common.CustomPaletteParams", - "text": "CustomPaletteParams" - }, + "CustomPaletteParams", " | undefined, isRespectRanges?: boolean | undefined) => any" ], "path": "src/plugins/chart_expressions/expression_gauge/public/components/utils/accessors.ts", @@ -283,13 +265,7 @@ "label": "paletteParams", "description": [], "signature": [ - { - "pluginId": "expressionGauge", - "scope": "common", - "docId": "kibExpressionGaugePluginApi", - "section": "def-common.CustomPaletteParams", - "text": "CustomPaletteParams" - }, + "CustomPaletteParams", " | undefined" ], "path": "src/plugins/chart_expressions/expression_gauge/public/components/utils/accessors.ts", @@ -551,196 +527,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "expressionGauge", - "id": "def-common.ColorStop", - "type": "Interface", - "tags": [], - "label": "ColorStop", - "description": [], - "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "expressionGauge", - "id": "def-common.ColorStop.color", - "type": "string", - "tags": [], - "label": "color", - "description": [], - "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionGauge", - "id": "def-common.ColorStop.stop", - "type": "number", - "tags": [], - "label": "stop", - "description": [], - "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "expressionGauge", - "id": "def-common.CustomPaletteParams", - "type": "Interface", - "tags": [], - "label": "CustomPaletteParams", - "description": [], - "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "expressionGauge", - "id": "def-common.CustomPaletteParams.name", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionGauge", - "id": "def-common.CustomPaletteParams.reverse", - "type": "CompoundType", - "tags": [], - "label": "reverse", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionGauge", - "id": "def-common.CustomPaletteParams.rangeType", - "type": "CompoundType", - "tags": [], - "label": "rangeType", - "description": [], - "signature": [ - "\"number\" | \"percent\" | undefined" - ], - "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionGauge", - "id": "def-common.CustomPaletteParams.continuity", - "type": "CompoundType", - "tags": [], - "label": "continuity", - "description": [], - "signature": [ - "\"above\" | \"below\" | \"none\" | \"all\" | undefined" - ], - "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionGauge", - "id": "def-common.CustomPaletteParams.progression", - "type": "string", - "tags": [], - "label": "progression", - "description": [], - "signature": [ - "\"fixed\" | undefined" - ], - "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionGauge", - "id": "def-common.CustomPaletteParams.rangeMin", - "type": "number", - "tags": [], - "label": "rangeMin", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionGauge", - "id": "def-common.CustomPaletteParams.rangeMax", - "type": "number", - "tags": [], - "label": "rangeMax", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionGauge", - "id": "def-common.CustomPaletteParams.stops", - "type": "Array", - "tags": [], - "label": "stops", - "description": [], - "signature": [ - { - "pluginId": "expressionGauge", - "scope": "common", - "docId": "kibExpressionGaugePluginApi", - "section": "def-common.ColorStop", - "text": "ColorStop" - }, - "[] | undefined" - ], - "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionGauge", - "id": "def-common.CustomPaletteParams.colorStops", - "type": "Array", - "tags": [], - "label": "colorStops", - "description": [], - "signature": [ - { - "pluginId": "expressionGauge", - "scope": "common", - "docId": "kibExpressionGaugePluginApi", - "section": "def-common.ColorStop", - "text": "ColorStop" - }, - "[] | undefined" - ], - "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionGauge", - "id": "def-common.CustomPaletteParams.steps", - "type": "number", - "tags": [], - "label": "steps", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "expressionGauge", "id": "def-common.GaugeExpressionProps", @@ -796,13 +582,7 @@ "; colorMode: ", "GaugeColorMode", "; palette?: ", - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<", { "pluginId": "charts", @@ -1014,21 +794,9 @@ "label": "palette", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<", - { - "pluginId": "expressionGauge", - "scope": "common", - "docId": "kibExpressionGaugePluginApi", - "section": "def-common.CustomPaletteParams", - "text": "CustomPaletteParams" - }, + "CustomPaletteParams", "> | undefined" ], "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_functions.ts", @@ -1208,13 +976,7 @@ "; colorMode: ", "GaugeColorMode", "; palette?: ", - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<", { "pluginId": "charts", @@ -1340,13 +1102,7 @@ "; chartsThemeService: ", "Theme", "; paletteService: ", - { - "pluginId": "charts", - "scope": "public", - "docId": "kibChartsPluginApi", - "section": "def-public.PaletteRegistry", - "text": "PaletteRegistry" - }, + "PaletteRegistry", "; uiState: ", { "pluginId": "visualizations", @@ -1416,36 +1172,6 @@ "path": "src/plugins/chart_expressions/expression_gauge/common/index.ts", "deprecated": false, "initialIsOpen": false - }, - { - "parentPluginId": "expressionGauge", - "id": "def-common.RequiredPaletteParamTypes", - "type": "Type", - "tags": [], - "label": "RequiredPaletteParamTypes", - "description": [], - "signature": [ - "{ name: string; reverse: boolean; rangeType: \"number\" | \"percent\"; continuity: \"above\" | \"below\" | \"none\" | \"all\"; progression: \"fixed\"; rangeMin: number; rangeMax: number; stops: ", - { - "pluginId": "expressionGauge", - "scope": "common", - "docId": "kibExpressionGaugePluginApi", - "section": "def-common.ColorStop", - "text": "ColorStop" - }, - "[]; colorStops: ", - { - "pluginId": "expressionGauge", - "scope": "common", - "docId": "kibExpressionGaugePluginApi", - "section": "def-common.ColorStop", - "text": "ColorStop" - }, - "[]; steps: number; }" - ], - "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", - "deprecated": false, - "initialIsOpen": false } ], "objects": [ diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index b99d793cc0063..f83a1b80f6f29 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionGauge plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 76 | 0 | 76 | 2 | +| 61 | 0 | 61 | 2 | ## Client diff --git a/api_docs/expression_heatmap.devdocs.json b/api_docs/expression_heatmap.devdocs.json index fa4e095495ed6..e686e8a1e7b76 100644 --- a/api_docs/expression_heatmap.devdocs.json +++ b/api_docs/expression_heatmap.devdocs.json @@ -121,196 +121,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "expressionHeatmap", - "id": "def-common.ColorStop", - "type": "Interface", - "tags": [], - "label": "ColorStop", - "description": [], - "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "expressionHeatmap", - "id": "def-common.ColorStop.color", - "type": "string", - "tags": [], - "label": "color", - "description": [], - "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionHeatmap", - "id": "def-common.ColorStop.stop", - "type": "number", - "tags": [], - "label": "stop", - "description": [], - "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "expressionHeatmap", - "id": "def-common.CustomPaletteParams", - "type": "Interface", - "tags": [], - "label": "CustomPaletteParams", - "description": [], - "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "expressionHeatmap", - "id": "def-common.CustomPaletteParams.name", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionHeatmap", - "id": "def-common.CustomPaletteParams.reverse", - "type": "CompoundType", - "tags": [], - "label": "reverse", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionHeatmap", - "id": "def-common.CustomPaletteParams.rangeType", - "type": "CompoundType", - "tags": [], - "label": "rangeType", - "description": [], - "signature": [ - "\"number\" | \"percent\" | undefined" - ], - "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionHeatmap", - "id": "def-common.CustomPaletteParams.continuity", - "type": "CompoundType", - "tags": [], - "label": "continuity", - "description": [], - "signature": [ - "\"above\" | \"below\" | \"none\" | \"all\" | undefined" - ], - "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionHeatmap", - "id": "def-common.CustomPaletteParams.progression", - "type": "string", - "tags": [], - "label": "progression", - "description": [], - "signature": [ - "\"fixed\" | undefined" - ], - "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionHeatmap", - "id": "def-common.CustomPaletteParams.rangeMin", - "type": "number", - "tags": [], - "label": "rangeMin", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionHeatmap", - "id": "def-common.CustomPaletteParams.rangeMax", - "type": "number", - "tags": [], - "label": "rangeMax", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionHeatmap", - "id": "def-common.CustomPaletteParams.stops", - "type": "Array", - "tags": [], - "label": "stops", - "description": [], - "signature": [ - { - "pluginId": "expressionHeatmap", - "scope": "common", - "docId": "kibExpressionHeatmapPluginApi", - "section": "def-common.ColorStop", - "text": "ColorStop" - }, - "[] | undefined" - ], - "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionHeatmap", - "id": "def-common.CustomPaletteParams.colorStops", - "type": "Array", - "tags": [], - "label": "colorStops", - "description": [], - "signature": [ - { - "pluginId": "expressionHeatmap", - "scope": "common", - "docId": "kibExpressionHeatmapPluginApi", - "section": "def-common.ColorStop", - "text": "ColorStop" - }, - "[] | undefined" - ], - "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts", - "deprecated": false - }, - { - "parentPluginId": "expressionHeatmap", - "id": "def-common.CustomPaletteParams.steps", - "type": "number", - "tags": [], - "label": "steps", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "expressionHeatmap", "id": "def-common.FilterEvent", @@ -428,13 +238,7 @@ "label": "palette", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<", { "pluginId": "charts", @@ -805,13 +609,7 @@ "text": "Datatable" }, "; column: number; range: number[]; timeFieldName?: string | undefined; }) => void; paletteService: ", - { - "pluginId": "charts", - "scope": "public", - "docId": "kibChartsPluginApi", - "section": "def-public.PaletteRegistry", - "text": "PaletteRegistry" - }, + "PaletteRegistry", "; uiState: ", { "pluginId": "visualizations", @@ -853,36 +651,6 @@ "path": "src/plugins/chart_expressions/expression_heatmap/common/index.ts", "deprecated": false, "initialIsOpen": false - }, - { - "parentPluginId": "expressionHeatmap", - "id": "def-common.RequiredPaletteParamTypes", - "type": "Type", - "tags": [], - "label": "RequiredPaletteParamTypes", - "description": [], - "signature": [ - "{ name: string; reverse: boolean; rangeType: \"number\" | \"percent\"; continuity: \"above\" | \"below\" | \"none\" | \"all\"; progression: \"fixed\"; rangeMin: number; rangeMax: number; stops: ", - { - "pluginId": "expressionHeatmap", - "scope": "common", - "docId": "kibExpressionHeatmapPluginApi", - "section": "def-common.ColorStop", - "text": "ColorStop" - }, - "[]; colorStops: ", - { - "pluginId": "expressionHeatmap", - "scope": "common", - "docId": "kibExpressionHeatmapPluginApi", - "section": "def-common.ColorStop", - "text": "ColorStop" - }, - "[]; steps: number; }" - ], - "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_renderers.ts", - "deprecated": false, - "initialIsOpen": false } ], "objects": [ diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index aad7b2efe25d8..d828747b22637 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionHeatmap plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 119 | 0 | 115 | 3 | +| 104 | 0 | 100 | 3 | ## Client diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index feea2fe660d34..093aecd94cd99 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionImage plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] warning: 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. --- diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index 8affa769b01a2..527d9a85c187c 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionMetric plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] warning: 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. --- diff --git a/api_docs/expression_metric_vis.devdocs.json b/api_docs/expression_metric_vis.devdocs.json index 43cb1a3470887..de4956164a459 100644 --- a/api_docs/expression_metric_vis.devdocs.json +++ b/api_docs/expression_metric_vis.devdocs.json @@ -150,13 +150,7 @@ "label": "palette", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<", { "pluginId": "charts", diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index a428ff8611edf..e4d8c09eb36cd 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionMetricVis plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] warning: 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. --- diff --git a/api_docs/expression_partition_vis.devdocs.json b/api_docs/expression_partition_vis.devdocs.json index 6511db71b8625..4953f5401fb20 100644 --- a/api_docs/expression_partition_vis.devdocs.json +++ b/api_docs/expression_partition_vis.devdocs.json @@ -609,13 +609,7 @@ "label": "palette", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<{ [key: string]: unknown; }>" ], "path": "src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts", diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index 2ed4287363bf1..97c89c561e56c 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionPartitionVis plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] warning: 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. --- diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index 30b19d8b19abb..5b0af3d5efbc8 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionRepeatImage plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage'] warning: 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. --- diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx index 8e270f3640f04..c7eca19a624cb 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionRevealImage plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] warning: 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. --- diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index 54a9aa6e92925..f3ee5306578c3 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionShape plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] warning: 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. --- diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index e3f34e10096c9..8331a345d07cc 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionTagcloud plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] warning: 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. --- diff --git a/api_docs/expression_x_y.devdocs.json b/api_docs/expression_x_y.devdocs.json index c8e2d0f579833..7064fd2035207 100644 --- a/api_docs/expression_x_y.devdocs.json +++ b/api_docs/expression_x_y.devdocs.json @@ -452,13 +452,7 @@ "label": "palette", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<{ [key: string]: unknown; }>" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", @@ -3086,7 +3080,7 @@ "label": "types", "description": [], "signature": [ - "(\"palette\" | \"system_palette\")[]" + "(\"system_palette\" | \"palette\")[]" ], "path": "src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts", "deprecated": false @@ -3156,13 +3150,7 @@ "text": "XScaleType" }, "; isHistogram: boolean; palette: ", - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<{ [key: string]: unknown; }>; yConfig?: ", { "pluginId": "expressionXY", diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index 1bb894548102d..90fa5545dffd1 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionXY plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] warning: 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. --- diff --git a/api_docs/expressions.devdocs.json b/api_docs/expressions.devdocs.json index 5da80bd20a369..193a9d90ba337 100644 --- a/api_docs/expressions.devdocs.json +++ b/api_docs/expressions.devdocs.json @@ -6361,7 +6361,13 @@ "description": [], "signature": [ "(nodeRef: React.RefObject, {\n debounce,\n expression,\n hasCustomErrorRenderer,\n onData$,\n onEvent,\n onRender$,\n reload$,\n ...loaderParams\n }: ", - "ExpressionRendererParams", + { + "pluginId": "expressions", + "scope": "public", + "docId": "kibExpressionsPluginApi", + "section": "def-public.ExpressionRendererParams", + "text": "ExpressionRendererParams" + }, ") => ExpressionRendererState" ], "path": "src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts", @@ -6389,7 +6395,13 @@ "label": "{\n debounce,\n expression,\n hasCustomErrorRenderer,\n onData$,\n onEvent,\n onRender$,\n reload$,\n ...loaderParams\n }", "description": [], "signature": [ - "ExpressionRendererParams" + { + "pluginId": "expressions", + "scope": "public", + "docId": "kibExpressionsPluginApi", + "section": "def-public.ExpressionRendererParams", + "text": "ExpressionRendererParams" + } ], "path": "src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts", "deprecated": false, @@ -7877,35 +7889,35 @@ "references": [ { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/common/functions/filters.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/escount.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/esdocs.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/esdocs.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/essql.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/essql.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/common/neq.ts" + "path": "x-pack/plugins/canvas/common/functions/filters.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/escount.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/common/neq.ts" }, { "plugin": "visTypeXy", @@ -8612,6 +8624,230 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "expressions", + "id": "def-public.ExpressionRendererParams", + "type": "Interface", + "tags": [], + "label": "ExpressionRendererParams", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "public", + "docId": "kibExpressionsPluginApi", + "section": "def-public.ExpressionRendererParams", + "text": "ExpressionRendererParams" + }, + " extends ", + { + "pluginId": "expressions", + "scope": "public", + "docId": "kibExpressionsPluginApi", + "section": "def-public.IExpressionLoaderParams", + "text": "IExpressionLoaderParams" + } + ], + "path": "src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "expressions", + "id": "def-public.ExpressionRendererParams.debounce", + "type": "number", + "tags": [], + "label": "debounce", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts", + "deprecated": false + }, + { + "parentPluginId": "expressions", + "id": "def-public.ExpressionRendererParams.expression", + "type": "CompoundType", + "tags": [], + "label": "expression", + "description": [], + "signature": [ + "string | ", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExpressionAstExpression", + "text": "ExpressionAstExpression" + } + ], + "path": "src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts", + "deprecated": false + }, + { + "parentPluginId": "expressions", + "id": "def-public.ExpressionRendererParams.hasCustomErrorRenderer", + "type": "CompoundType", + "tags": [], + "label": "hasCustomErrorRenderer", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts", + "deprecated": false + }, + { + "parentPluginId": "expressions", + "id": "def-public.ExpressionRendererParams.onData$", + "type": "Function", + "tags": [], + "label": "onData$", + "description": [], + "signature": [ + "((data: TData, adapters?: TInspectorAdapters | undefined, partial?: boolean | undefined) => void) | undefined" + ], + "path": "src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "expressions", + "id": "def-public.ExpressionRendererParams.onData$.$1", + "type": "Uncategorized", + "tags": [], + "label": "data", + "description": [], + "signature": [ + "TData" + ], + "path": "src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "expressions", + "id": "def-public.ExpressionRendererParams.onData$.$2", + "type": "Uncategorized", + "tags": [], + "label": "adapters", + "description": [], + "signature": [ + "TInspectorAdapters | undefined" + ], + "path": "src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts", + "deprecated": false, + "isRequired": false + }, + { + "parentPluginId": "expressions", + "id": "def-public.ExpressionRendererParams.onData$.$3", + "type": "CompoundType", + "tags": [], + "label": "partial", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "expressions", + "id": "def-public.ExpressionRendererParams.onEvent", + "type": "Function", + "tags": [], + "label": "onEvent", + "description": [], + "signature": [ + "((event: ", + { + "pluginId": "expressions", + "scope": "public", + "docId": "kibExpressionsPluginApi", + "section": "def-public.ExpressionRendererEvent", + "text": "ExpressionRendererEvent" + }, + ") => void) | undefined" + ], + "path": "src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "expressions", + "id": "def-public.ExpressionRendererParams.onEvent.$1", + "type": "Object", + "tags": [], + "label": "event", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "public", + "docId": "kibExpressionsPluginApi", + "section": "def-public.ExpressionRendererEvent", + "text": "ExpressionRendererEvent" + } + ], + "path": "src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "expressions", + "id": "def-public.ExpressionRendererParams.onRender$", + "type": "Function", + "tags": [], + "label": "onRender$", + "description": [], + "signature": [ + "((item: number) => void) | undefined" + ], + "path": "src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "expressions", + "id": "def-public.ExpressionRendererParams.onRender$.$1", + "type": "number", + "tags": [], + "label": "item", + "description": [], + "signature": [ + "number" + ], + "path": "src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "expressions", + "id": "def-public.ExpressionRendererParams.reload$", + "type": "Object", + "tags": [], + "label": "reload$", + "description": [ + "\nAn observable which can be used to re-run the expression without destroying the component" + ], + "signature": [ + "Observable", + " | undefined" + ], + "path": "src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "expressions", "id": "def-public.ExpressionRenderError", @@ -10366,7 +10602,13 @@ "text": "ReactExpressionRendererProps" }, " extends Omit<", - "ExpressionRendererParams", + { + "pluginId": "expressions", + "scope": "public", + "docId": "kibExpressionsPluginApi", + "section": "def-public.ExpressionRendererParams", + "text": "ExpressionRendererParams" + }, ", \"hasCustomErrorRenderer\">" ], "path": "src/plugins/expressions/public/react_expression_renderer/react_expression_renderer.tsx", @@ -11266,7 +11508,7 @@ "\nAllowed column names in a PointSeries" ], "signature": [ - "\"color\" | \"y\" | \"x\" | \"size\" | \"text\"" + "\"color\" | \"size\" | \"text\" | \"y\" | \"x\"" ], "path": "src/plugins/expressions/common/expression_types/specs/pointseries.ts", "deprecated": false, @@ -17996,35 +18238,35 @@ "references": [ { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/common/functions/filters.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/escount.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/esdocs.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/esdocs.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/essql.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/essql.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/common/neq.ts" + "path": "x-pack/plugins/canvas/common/functions/filters.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/escount.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/common/neq.ts" }, { "plugin": "visTypeXy", @@ -20218,7 +20460,7 @@ "\nAllowed column names in a PointSeries" ], "signature": [ - "\"color\" | \"y\" | \"x\" | \"size\" | \"text\"" + "\"color\" | \"size\" | \"text\" | \"y\" | \"x\"" ], "path": "src/plugins/expressions/common/expression_types/specs/pointseries.ts", "deprecated": false, @@ -29735,35 +29977,35 @@ "references": [ { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/common/functions/filters.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/escount.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/esdocs.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/esdocs.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/essql.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/essql.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/common/neq.ts" + "path": "x-pack/plugins/canvas/common/functions/filters.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/browser/escount.ts" }, { "plugin": "canvas", - "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts" + "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/common/neq.ts" }, { "plugin": "visTypeXy", @@ -34299,7 +34541,7 @@ "\nAllowed column names in a PointSeries" ], "signature": [ - "\"color\" | \"y\" | \"x\" | \"size\" | \"text\"" + "\"color\" | \"size\" | \"text\" | \"y\" | \"x\"" ], "path": "src/plugins/expressions/common/expression_types/specs/pointseries.ts", "deprecated": false, @@ -38006,7 +38248,15 @@ "section": "def-common.DatatableRow", "text": "DatatableRow" }, - "[]; type: \"datatable\"; meta?: ", + "[] | (", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.DatatableRow", + "text": "DatatableRow" + }, + " | { [x: string]: any; })[]; type: \"datatable\"; meta?: ", "DatatableMeta", " | undefined; }>" ], diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 8470b4f8a9643..48f344a1fc219 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressions plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2145 | 17 | 1701 | 6 | +| 2158 | 17 | 1713 | 5 | ## Client diff --git a/api_docs/features.mdx b/api_docs/features.mdx index 19443c000875b..27dc3a6e8652a 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github summary: API docs for the features plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features'] warning: 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. --- diff --git a/api_docs/field_formats.devdocs.json b/api_docs/field_formats.devdocs.json index 3128c6077b0c5..fbe312ee376af 100644 --- a/api_docs/field_formats.devdocs.json +++ b/api_docs/field_formats.devdocs.json @@ -4807,7 +4807,7 @@ "label": "FieldFormatsContentType", "description": [], "signature": [ - "\"html\" | \"text\"" + "\"text\" | \"html\"" ], "path": "src/plugins/field_formats/common/types.ts", "deprecated": false, @@ -5083,7 +5083,7 @@ "label": "HTML_CONTEXT_TYPE", "description": [], "signature": [ - "\"html\" | \"text\"" + "\"text\" | \"html\"" ], "path": "src/plugins/field_formats/common/content_types/html_content_type.ts", "deprecated": false, @@ -5361,7 +5361,7 @@ "label": "TEXT_CONTEXT_TYPE", "description": [], "signature": [ - "\"html\" | \"text\"" + "\"text\" | \"html\"" ], "path": "src/plugins/field_formats/common/content_types/text_content_type.ts", "deprecated": false, diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx index 41c16cfcde6a2..c425ca2c5924c 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github summary: API docs for the fieldFormats plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] warning: 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. --- diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index 5361e280670e0..670175e6463d6 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github summary: API docs for the fileUpload plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] warning: 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. --- diff --git a/api_docs/fleet.devdocs.json b/api_docs/fleet.devdocs.json index 71cb3650bbbce..530b55d549f63 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -6882,7 +6882,7 @@ "label": "runExternalCallbacks", "description": [], "signature": [ - "(externalCallbackType: A, packagePolicy: A extends \"postPackagePolicyDelete\" ? ", + "(externalCallbackType: A, packagePolicy: A extends \"postPackagePolicyDelete\" ? ", { "pluginId": "fleet", "scope": "common", @@ -6890,6 +6890,14 @@ "section": "def-common.DeletePackagePoliciesResponse", "text": "DeletePackagePoliciesResponse" }, + " : A extends \"packagePolicyPostCreate\" ? ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.PackagePolicy", + "text": "PackagePolicy" + }, " : ", { "pluginId": "fleet", @@ -6914,7 +6922,15 @@ "section": "def-server.KibanaRequest", "text": "KibanaRequest" }, - ") => Promise) => Promise) => Promise<", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.PackagePolicy", + "text": "PackagePolicy" + }, + ">" + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyPostCreateCallback.$1", + "type": "Object", + "tags": [], + "label": "packagePolicy", + "description": [], + "signature": [ + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.PackagePolicy", + "text": "PackagePolicy" + } + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyPostCreateCallback.$2", + "type": "Object", + "tags": [], + "label": "context", + "description": [], + "signature": [ + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.RequestHandlerContext", + "text": "RequestHandlerContext" + } + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PostPackagePolicyPostCreateCallback.$3", + "type": "Object", + "tags": [], + "label": "request", + "description": [], + "signature": [ + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreHttpPluginApi", + "section": "def-server.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "x-pack/plugins/fleet/server/types/extensions.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "fleet", "id": "def-server.PutPackagePolicyUpdateCallback", @@ -11410,7 +11543,7 @@ "\nLst checkin status" ], "signature": [ - "\"online\" | \"error\" | \"updating\" | \"degraded\" | undefined" + "\"error\" | \"online\" | \"updating\" | \"degraded\" | undefined" ], "path": "x-pack/plugins/fleet/common/types/models/agent.ts", "deprecated": false @@ -12800,7 +12933,20 @@ "label": "missing_requirements", "description": [], "signature": [ - "(\"fleet_server\" | \"security_required\" | \"tls_required\" | \"api_keys\" | \"fleet_admin_user\" | \"encrypted_saved_object_encryption_key_required\")[]" + "(\"fleet_server\" | \"security_required\" | \"tls_required\" | \"api_keys\" | \"fleet_admin_user\")[]" + ], + "path": "x-pack/plugins/fleet/common/types/rest_spec/fleet_setup.ts", + "deprecated": false + }, + { + "parentPluginId": "fleet", + "id": "def-common.GetFleetStatusResponse.missing_optional_features", + "type": "Array", + "tags": [], + "label": "missing_optional_features", + "description": [], + "signature": [ + "\"encrypted_saved_object_encryption_key_required\"[]" ], "path": "x-pack/plugins/fleet/common/types/rest_spec/fleet_setup.ts", "deprecated": false @@ -13411,7 +13557,7 @@ "label": "query", "description": [], "signature": [ - "{ category?: string | undefined; experimental?: boolean | undefined; }" + "{ category?: string | undefined; experimental?: boolean | undefined; excludeInstallStatus?: boolean | undefined; }" ], "path": "x-pack/plugins/fleet/common/types/rest_spec/epm.ts", "deprecated": false @@ -18630,7 +18776,7 @@ "label": "AgentStatus", "description": [], "signature": [ - "\"offline\" | \"online\" | \"error\" | \"warning\" | \"inactive\" | \"enrolling\" | \"unenrolling\" | \"updating\" | \"degraded\"" + "\"error\" | \"offline\" | \"online\" | \"warning\" | \"inactive\" | \"enrolling\" | \"unenrolling\" | \"updating\" | \"degraded\"" ], "path": "x-pack/plugins/fleet/common/types/models/agent.ts", "deprecated": false, @@ -19661,6 +19807,14 @@ "label": "Installable", "description": [], "signature": [ + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.InstallStatusExcluded", + "text": "InstallStatusExcluded" + }, + " | ", { "pluginId": "fleet", "scope": "common", @@ -19838,6 +19992,20 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "fleet", + "id": "def-common.InstallStatusExcluded", + "type": "Type", + "tags": [], + "label": "InstallStatusExcluded", + "description": [], + "signature": [ + "T & { status: undefined; }" + ], + "path": "x-pack/plugins/fleet/common/types/models/epm.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "fleet", "id": "def-common.InstallType", @@ -20194,7 +20362,7 @@ "section": "def-common.NewOutput", "text": "NewOutput" }, - " & { output_id?: string | undefined; }" + " & { output_id?: string | undefined; ssl?: string | undefined; }" ], "path": "x-pack/plugins/fleet/common/types/models/output.ts", "deprecated": false, @@ -20749,7 +20917,7 @@ "label": "RegistrySearchResult", "description": [], "signature": [ - "{ download: string; type?: \"integration\" | undefined; title: string; description: string; icons?: (", + "{ download: string; title: string; type?: \"integration\" | undefined; description: string; icons?: (", { "pluginId": "fleet", "scope": "common", diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 35b1cf07adef1..221ed6ae0e6e0 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github summary: API docs for the fleet plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Fleet](https://github.com/orgs/elastic/teams/fleet) for questions regar | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 1379 | 8 | 1262 | 9 | +| 1385 | 8 | 1268 | 10 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 9f756367f808c..31f9aa31263a9 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github summary: API docs for the globalSearch plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] warning: 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. --- diff --git a/api_docs/home.devdocs.json b/api_docs/home.devdocs.json index 56a7ef14f9710..516a1e6965511 100644 --- a/api_docs/home.devdocs.json +++ b/api_docs/home.devdocs.json @@ -368,20 +368,14 @@ { "parentPluginId": "home", "id": "def-public.FeatureCatalogueEntry.category", - "type": "Enum", + "type": "CompoundType", "tags": [], "label": "category", "description": [ "{@link FeatureCatalogueCategory} to display this feature in." ], "signature": [ - { - "pluginId": "home", - "scope": "public", - "docId": "kibHomePluginApi", - "section": "def-public.FeatureCatalogueCategory", - "text": "FeatureCatalogueCategory" - } + "\"data\" | \"other\" | \"admin\"" ], "path": "src/plugins/home/public/services/feature_catalogue/feature_catalogue_registry.ts", "deprecated": false @@ -682,19 +676,7 @@ "initialIsOpen": false } ], - "enums": [ - { - "parentPluginId": "home", - "id": "def-public.FeatureCatalogueCategory", - "type": "Enum", - "tags": [], - "label": "FeatureCatalogueCategory", - "description": [], - "path": "src/plugins/home/public/services/feature_catalogue/feature_catalogue_registry.ts", - "deprecated": false, - "initialIsOpen": false - } - ], + "enums": [], "misc": [ { "parentPluginId": "home", @@ -718,6 +700,20 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "home", + "id": "def-public.FeatureCatalogueCategory", + "type": "Type", + "tags": [], + "label": "FeatureCatalogueCategory", + "description": [], + "signature": [ + "\"data\" | \"other\" | \"admin\"" + ], + "path": "src/plugins/home/public/services/feature_catalogue/feature_catalogue_registry.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "home", "id": "def-public.FeatureCatalogueSetup", @@ -1577,7 +1573,7 @@ "label": "InstructionSetSchema", "description": [], "signature": [ - "{ readonly title?: string | undefined; readonly callOut?: Readonly<{ message?: string | undefined; iconType?: string | undefined; } & { title: string; }> | undefined; readonly statusCheck?: Readonly<{ title?: string | undefined; error?: string | undefined; text?: string | undefined; success?: string | undefined; btnLabel?: string | undefined; } & { esHitsCheck: Readonly<{} & { query: Record; index: string | string[]; }>; }> | undefined; readonly instructionVariants: Readonly<{ initialSelected?: boolean | undefined; } & { id: string; instructions: Readonly<{ title?: string | undefined; commands?: string[] | undefined; textPre?: string | undefined; textPost?: string | undefined; customComponentName?: string | undefined; } & {}>[]; }>[]; }" + "{ readonly title?: string | undefined; readonly callOut?: Readonly<{ message?: string | undefined; iconType?: string | undefined; } & { title: string; }> | undefined; readonly statusCheck?: Readonly<{ error?: string | undefined; success?: string | undefined; text?: string | undefined; title?: string | undefined; btnLabel?: string | undefined; } & { esHitsCheck: Readonly<{} & { query: Record; index: string | string[]; }>; }> | undefined; readonly instructionVariants: Readonly<{ initialSelected?: boolean | undefined; } & { id: string; instructions: Readonly<{ title?: string | undefined; commands?: string[] | undefined; textPre?: string | undefined; textPost?: string | undefined; customComponentName?: string | undefined; } & {}>[]; }>[]; }" ], "path": "src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts", "deprecated": false, @@ -1591,7 +1587,7 @@ "label": "InstructionsSchema", "description": [], "signature": [ - "{ readonly params?: Readonly<{ defaultValue?: any; } & { label: string; type: \"string\" | \"number\"; id: string; }>[] | undefined; readonly instructionSets: Readonly<{ title?: string | undefined; callOut?: Readonly<{ message?: string | undefined; iconType?: string | undefined; } & { title: string; }> | undefined; statusCheck?: Readonly<{ title?: string | undefined; error?: string | undefined; text?: string | undefined; success?: string | undefined; btnLabel?: string | undefined; } & { esHitsCheck: Readonly<{} & { query: Record; index: string | string[]; }>; }> | undefined; } & { instructionVariants: Readonly<{ initialSelected?: boolean | undefined; } & { id: string; instructions: Readonly<{ title?: string | undefined; commands?: string[] | undefined; textPre?: string | undefined; textPost?: string | undefined; customComponentName?: string | undefined; } & {}>[]; }>[]; }>[]; }" + "{ readonly params?: Readonly<{ defaultValue?: any; } & { label: string; id: string; type: \"string\" | \"number\"; }>[] | undefined; readonly instructionSets: Readonly<{ title?: string | undefined; callOut?: Readonly<{ message?: string | undefined; iconType?: string | undefined; } & { title: string; }> | undefined; statusCheck?: Readonly<{ error?: string | undefined; success?: string | undefined; text?: string | undefined; title?: string | undefined; btnLabel?: string | undefined; } & { esHitsCheck: Readonly<{} & { query: Record; index: string | string[]; }>; }> | undefined; } & { instructionVariants: Readonly<{ initialSelected?: boolean | undefined; } & { id: string; instructions: Readonly<{ title?: string | undefined; commands?: string[] | undefined; textPre?: string | undefined; textPost?: string | undefined; customComponentName?: string | undefined; } & {}>[]; }>[]; }>[]; }" ], "path": "src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts", "deprecated": false, @@ -1607,7 +1603,7 @@ "signature": [ "{ getSampleDatasets: () => ", "Writable", - "[]; defaultIndex: string; previewImagePath: string; overviewDashboard: string; dataIndices: Readonly<{} & { id: string; fields: Record; timeFields: string[]; dataPath: string; currentTimeMarker: string; preserveDayOfWeekTimeOfDay: boolean; }>[]; }>>[]; addSavedObjectsToSampleDataset: (id: string, savedObjects: ", + "[]; defaultIndex: string; previewImagePath: string; overviewDashboard: string; dataIndices: Readonly<{} & { id: string; fields: Record; timeFields: string[]; dataPath: string; currentTimeMarker: string; preserveDayOfWeekTimeOfDay: boolean; }>[]; }>>[]; addSavedObjectsToSampleDataset: (id: string, savedObjects: ", "SavedObject", "[]) => void; addAppLinksToSampleDataset: (id: string, appLinks: ", { @@ -1641,7 +1637,7 @@ "signature": [ "() => ", "Writable", - "[]; defaultIndex: string; previewImagePath: string; overviewDashboard: string; dataIndices: Readonly<{} & { id: string; fields: Record; timeFields: string[]; dataPath: string; currentTimeMarker: string; preserveDayOfWeekTimeOfDay: boolean; }>[]; }>>" + "[]; defaultIndex: string; previewImagePath: string; overviewDashboard: string; dataIndices: Readonly<{} & { id: string; fields: Record; timeFields: string[]; dataPath: string; currentTimeMarker: string; preserveDayOfWeekTimeOfDay: boolean; }>[]; }>>" ], "path": "src/plugins/home/server/services/sample_data/lib/sample_dataset_registry_types.ts", "deprecated": false, @@ -1695,7 +1691,7 @@ "section": "def-server.TutorialContext", "text": "TutorialContext" }, - ") => Readonly<{ isBeta?: boolean | undefined; savedObjects?: any[] | undefined; euiIconType?: string | undefined; previewImagePath?: string | undefined; moduleName?: string | undefined; completionTimeMinutes?: number | undefined; elasticCloud?: Readonly<{ params?: Readonly<{ defaultValue?: any; } & { label: string; type: \"string\" | \"number\"; id: string; }>[] | undefined; } & { instructionSets: Readonly<{ title?: string | undefined; callOut?: Readonly<{ message?: string | undefined; iconType?: string | undefined; } & { title: string; }> | undefined; statusCheck?: Readonly<{ title?: string | undefined; error?: string | undefined; text?: string | undefined; success?: string | undefined; btnLabel?: string | undefined; } & { esHitsCheck: Readonly<{} & { query: Record; index: string | string[]; }>; }> | undefined; } & { instructionVariants: Readonly<{ initialSelected?: boolean | undefined; } & { id: string; instructions: Readonly<{ title?: string | undefined; commands?: string[] | undefined; textPre?: string | undefined; textPost?: string | undefined; customComponentName?: string | undefined; } & {}>[]; }>[]; }>[]; }> | undefined; onPremElasticCloud?: Readonly<{ params?: Readonly<{ defaultValue?: any; } & { label: string; type: \"string\" | \"number\"; id: string; }>[] | undefined; } & { instructionSets: Readonly<{ title?: string | undefined; callOut?: Readonly<{ message?: string | undefined; iconType?: string | undefined; } & { title: string; }> | undefined; statusCheck?: Readonly<{ title?: string | undefined; error?: string | undefined; text?: string | undefined; success?: string | undefined; btnLabel?: string | undefined; } & { esHitsCheck: Readonly<{} & { query: Record; index: string | string[]; }>; }> | undefined; } & { instructionVariants: Readonly<{ initialSelected?: boolean | undefined; } & { id: string; instructions: Readonly<{ title?: string | undefined; commands?: string[] | undefined; textPre?: string | undefined; textPost?: string | undefined; customComponentName?: string | undefined; } & {}>[]; }>[]; }>[]; }> | undefined; artifacts?: Readonly<{ application?: Readonly<{} & { label: string; path: string; }> | undefined; exportedFields?: Readonly<{} & { documentationUrl: string; }> | undefined; } & { dashboards: Readonly<{ linkLabel?: string | undefined; } & { id: string; isOverview: boolean; }>[]; }> | undefined; savedObjectsInstallMsg?: string | undefined; customStatusCheckName?: string | undefined; integrationBrowserCategories?: string[] | undefined; eprPackageOverlap?: string | undefined; } & { id: string; name: string; category: \"other\" | \"security\" | \"metrics\" | \"logging\"; shortDescription: string; longDescription: string; onPrem: Readonly<{ params?: Readonly<{ defaultValue?: any; } & { label: string; type: \"string\" | \"number\"; id: string; }>[] | undefined; } & { instructionSets: Readonly<{ title?: string | undefined; callOut?: Readonly<{ message?: string | undefined; iconType?: string | undefined; } & { title: string; }> | undefined; statusCheck?: Readonly<{ title?: string | undefined; error?: string | undefined; text?: string | undefined; success?: string | undefined; btnLabel?: string | undefined; } & { esHitsCheck: Readonly<{} & { query: Record; index: string | string[]; }>; }> | undefined; } & { instructionVariants: Readonly<{ initialSelected?: boolean | undefined; } & { id: string; instructions: Readonly<{ title?: string | undefined; commands?: string[] | undefined; textPre?: string | undefined; textPost?: string | undefined; customComponentName?: string | undefined; } & {}>[]; }>[]; }>[]; }>; }>" + ") => Readonly<{ isBeta?: boolean | undefined; savedObjects?: any[] | undefined; euiIconType?: string | undefined; previewImagePath?: string | undefined; moduleName?: string | undefined; completionTimeMinutes?: number | undefined; elasticCloud?: Readonly<{ params?: Readonly<{ defaultValue?: any; } & { label: string; id: string; type: \"string\" | \"number\"; }>[] | undefined; } & { instructionSets: Readonly<{ title?: string | undefined; callOut?: Readonly<{ message?: string | undefined; iconType?: string | undefined; } & { title: string; }> | undefined; statusCheck?: Readonly<{ error?: string | undefined; success?: string | undefined; text?: string | undefined; title?: string | undefined; btnLabel?: string | undefined; } & { esHitsCheck: Readonly<{} & { query: Record; index: string | string[]; }>; }> | undefined; } & { instructionVariants: Readonly<{ initialSelected?: boolean | undefined; } & { id: string; instructions: Readonly<{ title?: string | undefined; commands?: string[] | undefined; textPre?: string | undefined; textPost?: string | undefined; customComponentName?: string | undefined; } & {}>[]; }>[]; }>[]; }> | undefined; onPremElasticCloud?: Readonly<{ params?: Readonly<{ defaultValue?: any; } & { label: string; id: string; type: \"string\" | \"number\"; }>[] | undefined; } & { instructionSets: Readonly<{ title?: string | undefined; callOut?: Readonly<{ message?: string | undefined; iconType?: string | undefined; } & { title: string; }> | undefined; statusCheck?: Readonly<{ error?: string | undefined; success?: string | undefined; text?: string | undefined; title?: string | undefined; btnLabel?: string | undefined; } & { esHitsCheck: Readonly<{} & { query: Record; index: string | string[]; }>; }> | undefined; } & { instructionVariants: Readonly<{ initialSelected?: boolean | undefined; } & { id: string; instructions: Readonly<{ title?: string | undefined; commands?: string[] | undefined; textPre?: string | undefined; textPost?: string | undefined; customComponentName?: string | undefined; } & {}>[]; }>[]; }>[]; }> | undefined; artifacts?: Readonly<{ application?: Readonly<{} & { label: string; path: string; }> | undefined; exportedFields?: Readonly<{} & { documentationUrl: string; }> | undefined; } & { dashboards: Readonly<{ linkLabel?: string | undefined; } & { id: string; isOverview: boolean; }>[]; }> | undefined; savedObjectsInstallMsg?: string | undefined; customStatusCheckName?: string | undefined; integrationBrowserCategories?: string[] | undefined; eprPackageOverlap?: string | undefined; } & { id: string; name: string; category: \"other\" | \"security\" | \"metrics\" | \"logging\"; shortDescription: string; longDescription: string; onPrem: Readonly<{ params?: Readonly<{ defaultValue?: any; } & { label: string; id: string; type: \"string\" | \"number\"; }>[] | undefined; } & { instructionSets: Readonly<{ title?: string | undefined; callOut?: Readonly<{ message?: string | undefined; iconType?: string | undefined; } & { title: string; }> | undefined; statusCheck?: Readonly<{ error?: string | undefined; success?: string | undefined; text?: string | undefined; title?: string | undefined; btnLabel?: string | undefined; } & { esHitsCheck: Readonly<{} & { query: Record; index: string | string[]; }>; }> | undefined; } & { instructionVariants: Readonly<{ initialSelected?: boolean | undefined; } & { id: string; instructions: Readonly<{ title?: string | undefined; commands?: string[] | undefined; textPre?: string | undefined; textPost?: string | undefined; customComponentName?: string | undefined; } & {}>[]; }>[]; }>[]; }>; }>" ], "path": "src/plugins/home/server/services/tutorials/lib/tutorials_registry_types.ts", "deprecated": false, @@ -1731,7 +1727,7 @@ "label": "TutorialSchema", "description": [], "signature": [ - "{ readonly isBeta?: boolean | undefined; readonly savedObjects?: any[] | undefined; readonly euiIconType?: string | undefined; readonly previewImagePath?: string | undefined; readonly moduleName?: string | undefined; readonly completionTimeMinutes?: number | undefined; readonly elasticCloud?: Readonly<{ params?: Readonly<{ defaultValue?: any; } & { label: string; type: \"string\" | \"number\"; id: string; }>[] | undefined; } & { instructionSets: Readonly<{ title?: string | undefined; callOut?: Readonly<{ message?: string | undefined; iconType?: string | undefined; } & { title: string; }> | undefined; statusCheck?: Readonly<{ title?: string | undefined; error?: string | undefined; text?: string | undefined; success?: string | undefined; btnLabel?: string | undefined; } & { esHitsCheck: Readonly<{} & { query: Record; index: string | string[]; }>; }> | undefined; } & { instructionVariants: Readonly<{ initialSelected?: boolean | undefined; } & { id: string; instructions: Readonly<{ title?: string | undefined; commands?: string[] | undefined; textPre?: string | undefined; textPost?: string | undefined; customComponentName?: string | undefined; } & {}>[]; }>[]; }>[]; }> | undefined; readonly onPremElasticCloud?: Readonly<{ params?: Readonly<{ defaultValue?: any; } & { label: string; type: \"string\" | \"number\"; id: string; }>[] | undefined; } & { instructionSets: Readonly<{ title?: string | undefined; callOut?: Readonly<{ message?: string | undefined; iconType?: string | undefined; } & { title: string; }> | undefined; statusCheck?: Readonly<{ title?: string | undefined; error?: string | undefined; text?: string | undefined; success?: string | undefined; btnLabel?: string | undefined; } & { esHitsCheck: Readonly<{} & { query: Record; index: string | string[]; }>; }> | undefined; } & { instructionVariants: Readonly<{ initialSelected?: boolean | undefined; } & { id: string; instructions: Readonly<{ title?: string | undefined; commands?: string[] | undefined; textPre?: string | undefined; textPost?: string | undefined; customComponentName?: string | undefined; } & {}>[]; }>[]; }>[]; }> | undefined; readonly artifacts?: Readonly<{ application?: Readonly<{} & { label: string; path: string; }> | undefined; exportedFields?: Readonly<{} & { documentationUrl: string; }> | undefined; } & { dashboards: Readonly<{ linkLabel?: string | undefined; } & { id: string; isOverview: boolean; }>[]; }> | undefined; readonly savedObjectsInstallMsg?: string | undefined; readonly customStatusCheckName?: string | undefined; readonly integrationBrowserCategories?: string[] | undefined; readonly eprPackageOverlap?: string | undefined; readonly id: string; readonly name: string; readonly category: \"other\" | \"security\" | \"metrics\" | \"logging\"; readonly shortDescription: string; readonly longDescription: string; readonly onPrem: Readonly<{ params?: Readonly<{ defaultValue?: any; } & { label: string; type: \"string\" | \"number\"; id: string; }>[] | undefined; } & { instructionSets: Readonly<{ title?: string | undefined; callOut?: Readonly<{ message?: string | undefined; iconType?: string | undefined; } & { title: string; }> | undefined; statusCheck?: Readonly<{ title?: string | undefined; error?: string | undefined; text?: string | undefined; success?: string | undefined; btnLabel?: string | undefined; } & { esHitsCheck: Readonly<{} & { query: Record; index: string | string[]; }>; }> | undefined; } & { instructionVariants: Readonly<{ initialSelected?: boolean | undefined; } & { id: string; instructions: Readonly<{ title?: string | undefined; commands?: string[] | undefined; textPre?: string | undefined; textPost?: string | undefined; customComponentName?: string | undefined; } & {}>[]; }>[]; }>[]; }>; }" + "{ readonly isBeta?: boolean | undefined; readonly savedObjects?: any[] | undefined; readonly euiIconType?: string | undefined; readonly previewImagePath?: string | undefined; readonly moduleName?: string | undefined; readonly completionTimeMinutes?: number | undefined; readonly elasticCloud?: Readonly<{ params?: Readonly<{ defaultValue?: any; } & { label: string; id: string; type: \"string\" | \"number\"; }>[] | undefined; } & { instructionSets: Readonly<{ title?: string | undefined; callOut?: Readonly<{ message?: string | undefined; iconType?: string | undefined; } & { title: string; }> | undefined; statusCheck?: Readonly<{ error?: string | undefined; success?: string | undefined; text?: string | undefined; title?: string | undefined; btnLabel?: string | undefined; } & { esHitsCheck: Readonly<{} & { query: Record; index: string | string[]; }>; }> | undefined; } & { instructionVariants: Readonly<{ initialSelected?: boolean | undefined; } & { id: string; instructions: Readonly<{ title?: string | undefined; commands?: string[] | undefined; textPre?: string | undefined; textPost?: string | undefined; customComponentName?: string | undefined; } & {}>[]; }>[]; }>[]; }> | undefined; readonly onPremElasticCloud?: Readonly<{ params?: Readonly<{ defaultValue?: any; } & { label: string; id: string; type: \"string\" | \"number\"; }>[] | undefined; } & { instructionSets: Readonly<{ title?: string | undefined; callOut?: Readonly<{ message?: string | undefined; iconType?: string | undefined; } & { title: string; }> | undefined; statusCheck?: Readonly<{ error?: string | undefined; success?: string | undefined; text?: string | undefined; title?: string | undefined; btnLabel?: string | undefined; } & { esHitsCheck: Readonly<{} & { query: Record; index: string | string[]; }>; }> | undefined; } & { instructionVariants: Readonly<{ initialSelected?: boolean | undefined; } & { id: string; instructions: Readonly<{ title?: string | undefined; commands?: string[] | undefined; textPre?: string | undefined; textPost?: string | undefined; customComponentName?: string | undefined; } & {}>[]; }>[]; }>[]; }> | undefined; readonly artifacts?: Readonly<{ application?: Readonly<{} & { label: string; path: string; }> | undefined; exportedFields?: Readonly<{} & { documentationUrl: string; }> | undefined; } & { dashboards: Readonly<{ linkLabel?: string | undefined; } & { id: string; isOverview: boolean; }>[]; }> | undefined; readonly savedObjectsInstallMsg?: string | undefined; readonly customStatusCheckName?: string | undefined; readonly integrationBrowserCategories?: string[] | undefined; readonly eprPackageOverlap?: string | undefined; readonly id: string; readonly name: string; readonly category: \"other\" | \"security\" | \"metrics\" | \"logging\"; readonly shortDescription: string; readonly longDescription: string; readonly onPrem: Readonly<{ params?: Readonly<{ defaultValue?: any; } & { label: string; id: string; type: \"string\" | \"number\"; }>[] | undefined; } & { instructionSets: Readonly<{ title?: string | undefined; callOut?: Readonly<{ message?: string | undefined; iconType?: string | undefined; } & { title: string; }> | undefined; statusCheck?: Readonly<{ error?: string | undefined; success?: string | undefined; text?: string | undefined; title?: string | undefined; btnLabel?: string | undefined; } & { esHitsCheck: Readonly<{} & { query: Record; index: string | string[]; }>; }> | undefined; } & { instructionVariants: Readonly<{ initialSelected?: boolean | undefined; } & { id: string; instructions: Readonly<{ title?: string | undefined; commands?: string[] | undefined; textPre?: string | undefined; textPost?: string | undefined; customComponentName?: string | undefined; } & {}>[]; }>[]; }>[]; }>; }" ], "path": "src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts", "deprecated": false, @@ -1990,7 +1986,7 @@ "signature": [ "{ getSampleDatasets: () => ", "Writable", - "[]; defaultIndex: string; previewImagePath: string; overviewDashboard: string; dataIndices: Readonly<{} & { id: string; fields: Record; timeFields: string[]; dataPath: string; currentTimeMarker: string; preserveDayOfWeekTimeOfDay: boolean; }>[]; }>>[]; addSavedObjectsToSampleDataset: (id: string, savedObjects: ", + "[]; defaultIndex: string; previewImagePath: string; overviewDashboard: string; dataIndices: Readonly<{} & { id: string; fields: Record; timeFields: string[]; dataPath: string; currentTimeMarker: string; preserveDayOfWeekTimeOfDay: boolean; }>[]; }>>[]; addSavedObjectsToSampleDataset: (id: string, savedObjects: ", "SavedObject", "[]) => void; addAppLinksToSampleDataset: (id: string, appLinks: ", { diff --git a/api_docs/home.mdx b/api_docs/home.mdx index ba9950de8c4df..2f65dcfe6a656 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github summary: API docs for the home plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] warning: 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. --- @@ -40,9 +40,6 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que ### Interfaces -### Enums - - ### Consts, variables and types diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 4b3d1ee7c566a..3b508d1e26456 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github summary: API docs for the indexLifecycleManagement plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] warning: 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. --- diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index 9350e37eebf19..521963fb92b77 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github summary: API docs for the indexManagement plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] warning: 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. --- diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 1ad1b38648945..599b5bd6cfcab 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github summary: API docs for the infra plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] warning: 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. --- diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 5ebe20d7baa93..fff575d6b0e7c 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github summary: API docs for the inspector plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] warning: 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. --- diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index 2e1ad77a61fb9..869fe720402e9 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github summary: API docs for the interactiveSetup plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] warning: 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. --- diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index 28ed37ce7ba84..b937c8289d677 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ace plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] warning: 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. --- diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index feba71871e675..09fb9c17c487d 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/alerts plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] warning: 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. --- diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 523542b3a852d..045b97520eb11 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/analytics plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] warning: 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. --- diff --git a/api_docs/kbn_analytics_client.devdocs.json b/api_docs/kbn_analytics_client.devdocs.json new file mode 100644 index 0000000000000..b2111b6f6fbfd --- /dev/null +++ b/api_docs/kbn_analytics_client.devdocs.json @@ -0,0 +1,1818 @@ +{ + "id": "@kbn/analytics-client", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.createAnalytics", + "type": "Function", + "tags": [], + "label": "createAnalytics", + "description": [ + "\nCreates an {@link AnalyticsClient}." + ], + "signature": [ + "(initContext: ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.AnalyticsClientInitContext", + "text": "AnalyticsClientInitContext" + }, + ") => ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.IAnalyticsClient", + "text": "IAnalyticsClient" + } + ], + "path": "packages/analytics/client/src/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.createAnalytics.$1", + "type": "Object", + "tags": [], + "label": "initContext", + "description": [ + "The initial context to create the client {@link AnalyticsClientInitContext }" + ], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.AnalyticsClientInitContext", + "text": "AnalyticsClientInitContext" + } + ], + "path": "packages/analytics/client/src/index.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.AnalyticsClientInitContext", + "type": "Interface", + "tags": [], + "label": "AnalyticsClientInitContext", + "description": [ + "\nGeneral settings of the analytics client" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.AnalyticsClientInitContext.isDev", + "type": "boolean", + "tags": [], + "label": "isDev", + "description": [ + "\nBoolean indicating if it's running in developer mode." + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.AnalyticsClientInitContext.sendTo", + "type": "CompoundType", + "tags": [], + "label": "sendTo", + "description": [ + "\nSpecify if the shippers should send their data to the production or staging environments." + ], + "signature": [ + "\"staging\" | \"production\"" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.AnalyticsClientInitContext.logger", + "type": "Object", + "tags": [], + "label": "logger", + "description": [ + "\nApplication-provided logger." + ], + "signature": [ + "Logger" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.ContextProviderOpts", + "type": "Interface", + "tags": [], + "label": "ContextProviderOpts", + "description": [ + "\nDefinition of a context provider" + ], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.ContextProviderOpts", + "text": "ContextProviderOpts" + }, + "" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.ContextProviderOpts.name", + "type": "string", + "tags": [], + "label": "name", + "description": [ + "\nThe name of the provider." + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.ContextProviderOpts.context$", + "type": "Object", + "tags": [], + "label": "context$", + "description": [ + "\nObservable that emits the custom context." + ], + "signature": [ + "Observable", + "" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.ContextProviderOpts.schema", + "type": "Object", + "tags": [], + "label": "schema", + "description": [ + "\nSchema declaring and documenting the expected output in the context$\n" + ], + "signature": [ + "{ [Key in keyof Required]: ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaValue", + "text": "SchemaValue" + }, + "; }" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.Event", + "type": "Interface", + "tags": [], + "label": "Event", + "description": [ + "\nDefinition of the full event structure" + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.Event.timestamp", + "type": "string", + "tags": [], + "label": "timestamp", + "description": [ + "\nThe time the event was generated in ISO format." + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.Event.event_type", + "type": "string", + "tags": [], + "label": "event_type", + "description": [ + "\nThe event type." + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.Event.properties", + "type": "Object", + "tags": [], + "label": "properties", + "description": [ + "\nThe specific properties of the event type." + ], + "signature": [ + "{ [x: string]: unknown; }" + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.Event.context", + "type": "Object", + "tags": [], + "label": "context", + "description": [ + "\nThe {@link EventContext} enriched during the processing pipeline." + ], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.EventContext", + "text": "EventContext" + } + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.EventContext", + "type": "Interface", + "tags": [], + "label": "EventContext", + "description": [ + "\nDefinition of the context that can be appended to the events through the {@link IAnalyticsClient.registerContextProvider}." + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.EventContext.userId", + "type": "string", + "tags": [], + "label": "userId", + "description": [ + "\nThe unique user ID." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.EventContext.cloudId", + "type": "string", + "tags": [], + "label": "cloudId", + "description": [ + "\nThe Cloud ID." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.EventContext.version", + "type": "string", + "tags": [], + "label": "version", + "description": [ + "\nThe product's version." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.EventContext.pageName", + "type": "string", + "tags": [], + "label": "pageName", + "description": [ + "\nThe name of the current page." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.EventContext.applicationId", + "type": "string", + "tags": [], + "label": "applicationId", + "description": [ + "\nThe current application ID." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.EventContext.entityId", + "type": "string", + "tags": [], + "label": "entityId", + "description": [ + "\nThe current entity ID (dashboard ID, visualization ID, etc.)." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.EventContext.Unnamed", + "type": "IndexSignature", + "tags": [], + "label": "[key: string]: unknown", + "description": [], + "signature": [ + "[key: string]: unknown" + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.EventTypeOpts", + "type": "Interface", + "tags": [], + "label": "EventTypeOpts", + "description": [ + "\nDefinition of an Event Type." + ], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.EventTypeOpts", + "text": "EventTypeOpts" + }, + "" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.EventTypeOpts.eventType", + "type": "string", + "tags": [], + "label": "eventType", + "description": [ + "\nThe event type's unique name." + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.EventTypeOpts.schema", + "type": "Object", + "tags": [], + "label": "schema", + "description": [ + "\nSchema declaring and documenting the expected structure of this event type.\n" + ], + "signature": [ + "{ [Key in keyof Required]: ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaValue", + "text": "SchemaValue" + }, + "; }" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient", + "type": "Interface", + "tags": [], + "label": "IAnalyticsClient", + "description": [ + "\nAnalytics client's public APIs" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient.reportEvent", + "type": "Function", + "tags": [], + "label": "reportEvent", + "description": [ + "\nReports a telemetry event." + ], + "signature": [ + ">(eventType: string, eventData: EventTypeData) => void" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient.reportEvent.$1", + "type": "string", + "tags": [], + "label": "eventType", + "description": [ + "The event type registered via the `registerEventType` API." + ], + "signature": [ + "string" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient.reportEvent.$2", + "type": "Uncategorized", + "tags": [], + "label": "eventData", + "description": [ + "The properties matching the schema declared in the `registerEventType` API." + ], + "signature": [ + "EventTypeData" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient.registerEventType", + "type": "Function", + "tags": [], + "label": "registerEventType", + "description": [ + "\nRegisters the event type that will be emitted via the reportEvent API." + ], + "signature": [ + "(eventTypeOps: ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.EventTypeOpts", + "text": "EventTypeOpts" + }, + ") => void" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient.registerEventType.$1", + "type": "Object", + "tags": [], + "label": "eventTypeOps", + "description": [], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.EventTypeOpts", + "text": "EventTypeOpts" + }, + "" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient.registerShipper", + "type": "Function", + "tags": [], + "label": "registerShipper", + "description": [ + "\nSet up the shipper that will be used to report the telemetry events." + ], + "signature": [ + "(Shipper: ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.ShipperClassConstructor", + "text": "ShipperClassConstructor" + }, + ", shipperConfig: ShipperConfig, opts?: ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.RegisterShipperOpts", + "text": "RegisterShipperOpts" + }, + " | undefined) => void" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient.registerShipper.$1", + "type": "Object", + "tags": [], + "label": "Shipper", + "description": [ + "The {@link IShipper } class to instantiate the shipper." + ], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.ShipperClassConstructor", + "text": "ShipperClassConstructor" + }, + "" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient.registerShipper.$2", + "type": "Uncategorized", + "tags": [], + "label": "shipperConfig", + "description": [ + "The config specific to the Shipper to instantiate." + ], + "signature": [ + "ShipperConfig" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient.registerShipper.$3", + "type": "Object", + "tags": [], + "label": "opts", + "description": [ + "Additional options to register the shipper {@link RegisterShipperOpts }." + ], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.RegisterShipperOpts", + "text": "RegisterShipperOpts" + }, + " | undefined" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient.optIn", + "type": "Function", + "tags": [], + "label": "optIn", + "description": [ + "\nUsed to control the user's consent to report the data.\nIn the advanced mode, it allows to \"cherry-pick\" which events and shippers are enabled/disabled." + ], + "signature": [ + "(optInConfig: ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.OptInConfig", + "text": "OptInConfig" + }, + ") => void" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient.optIn.$1", + "type": "Object", + "tags": [], + "label": "optInConfig", + "description": [ + "{@link OptInConfig }" + ], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.OptInConfig", + "text": "OptInConfig" + } + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient.registerContextProvider", + "type": "Function", + "tags": [], + "label": "registerContextProvider", + "description": [ + "\nRegisters the context provider to enrich the any reported events." + ], + "signature": [ + "(contextProviderOpts: ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.ContextProviderOpts", + "text": "ContextProviderOpts" + }, + ") => void" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient.registerContextProvider.$1", + "type": "Object", + "tags": [], + "label": "contextProviderOpts", + "description": [ + "{@link ContextProviderOpts }" + ], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.ContextProviderOpts", + "text": "ContextProviderOpts" + }, + "" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient.removeContextProvider", + "type": "Function", + "tags": [], + "label": "removeContextProvider", + "description": [ + "\nRemoves the context provider and stop enriching the events from its context." + ], + "signature": [ + "(contextProviderName: string) => void" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient.removeContextProvider.$1", + "type": "string", + "tags": [], + "label": "contextProviderName", + "description": [ + "The name of the context provider to remove." + ], + "signature": [ + "string" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IAnalyticsClient.telemetryCounter$", + "type": "Object", + "tags": [], + "label": "telemetryCounter$", + "description": [ + "\nObservable to emit the stats of the processed events." + ], + "signature": [ + "Observable", + "<", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.TelemetryCounter", + "text": "TelemetryCounter" + }, + ">" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IShipper", + "type": "Interface", + "tags": [], + "label": "IShipper", + "description": [ + "\nBasic structure of a Shipper" + ], + "path": "packages/analytics/client/src/shippers/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IShipper.reportEvents", + "type": "Function", + "tags": [], + "label": "reportEvents", + "description": [ + "\nAdapts and ships the event to the persisting/analytics solution." + ], + "signature": [ + "(events: ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.Event", + "text": "Event" + }, + "[]) => void" + ], + "path": "packages/analytics/client/src/shippers/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IShipper.reportEvents.$1", + "type": "Array", + "tags": [], + "label": "events", + "description": [ + "batched events {@link Event }" + ], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.Event", + "text": "Event" + }, + "[]" + ], + "path": "packages/analytics/client/src/shippers/types.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IShipper.optIn", + "type": "Function", + "tags": [], + "label": "optIn", + "description": [ + "\nStops/restarts the shipping mechanism based on the value of isOptedIn" + ], + "signature": [ + "(isOptedIn: boolean) => void" + ], + "path": "packages/analytics/client/src/shippers/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IShipper.optIn.$1", + "type": "boolean", + "tags": [], + "label": "isOptedIn", + "description": [ + "`true` for resume sending events. `false` to stop." + ], + "signature": [ + "boolean" + ], + "path": "packages/analytics/client/src/shippers/types.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IShipper.extendContext", + "type": "Function", + "tags": [], + "label": "extendContext", + "description": [ + "\nPerform any necessary calls to the persisting/analytics solution to set the event's context." + ], + "signature": [ + "((newContext: ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.EventContext", + "text": "EventContext" + }, + ") => void) | undefined" + ], + "path": "packages/analytics/client/src/shippers/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IShipper.extendContext.$1", + "type": "Object", + "tags": [], + "label": "newContext", + "description": [], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.EventContext", + "text": "EventContext" + } + ], + "path": "packages/analytics/client/src/shippers/types.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.IShipper.telemetryCounter$", + "type": "Object", + "tags": [], + "label": "telemetryCounter$", + "description": [ + "\nObservable to emit the stats of the processed events." + ], + "signature": [ + "Observable", + "<", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.TelemetryCounter", + "text": "TelemetryCounter" + }, + "> | undefined" + ], + "path": "packages/analytics/client/src/shippers/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.OptInConfig", + "type": "Interface", + "tags": [], + "label": "OptInConfig", + "description": [ + "\n" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.OptInConfig.global", + "type": "Object", + "tags": [], + "label": "global", + "description": [ + "\nControls the global enabled/disabled behaviour of the client and shippers." + ], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.OptInConfigPerType", + "text": "OptInConfigPerType" + } + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.OptInConfig.event_types", + "type": "Object", + "tags": [], + "label": "event_types", + "description": [ + "\nControls if an event type should be disabled for a specific type of shipper." + ], + "signature": [ + "Record | undefined" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.OptInConfigPerType", + "type": "Interface", + "tags": [], + "label": "OptInConfigPerType", + "description": [ + "\nSets whether a type of event is enabled/disabled globally or per shipper." + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.OptInConfigPerType.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [ + "\nThe event type is globally enabled." + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.OptInConfigPerType.shippers", + "type": "Object", + "tags": [], + "label": "shippers", + "description": [ + "\nControls if an event type should be disabled for a specific type of shipper." + ], + "signature": [ + "Record | undefined" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.RegisterShipperOpts", + "type": "Interface", + "tags": [], + "label": "RegisterShipperOpts", + "description": [ + "\nOptional options to register a shipper" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "children": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.SchemaArray", + "type": "Interface", + "tags": [], + "label": "SchemaArray", + "description": [ + "\nSchema to represent an array" + ], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaArray", + "text": "SchemaArray" + }, + " extends ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaMeta", + "text": "SchemaMeta" + }, + "" + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.SchemaArray.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"array\"" + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.SchemaArray.items", + "type": "CompoundType", + "tags": [], + "label": "items", + "description": [], + "signature": [ + "{ type: \"pass_through\"; _meta: { description: string; } & ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaMetaOptional", + "text": "SchemaMetaOptional" + }, + "; } | (unknown extends Value ? ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaArray", + "text": "SchemaArray" + }, + " | ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaObject", + "text": "SchemaObject" + }, + " | ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaChildValue", + "text": "SchemaChildValue" + }, + " : NonNullable extends (infer U)[] ? ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaArray", + "text": "SchemaArray" + }, + " : NonNullable extends object ? ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaObject", + "text": "SchemaObject" + }, + " : ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaChildValue", + "text": "SchemaChildValue" + }, + ")" + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.SchemaChildValue", + "type": "Interface", + "tags": [], + "label": "SchemaChildValue", + "description": [], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaChildValue", + "text": "SchemaChildValue" + }, + "" + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.SchemaChildValue.type", + "type": "Uncategorized", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "NonNullable extends string ? ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.AllowedSchemaStringTypes", + "text": "AllowedSchemaStringTypes" + }, + " : NonNullable extends number ? ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.AllowedSchemaNumberTypes", + "text": "AllowedSchemaNumberTypes" + }, + " : NonNullable extends boolean ? \"boolean\" : ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.AllowedSchemaTypes", + "text": "AllowedSchemaTypes" + } + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.SchemaChildValue._meta", + "type": "CompoundType", + "tags": [], + "label": "_meta", + "description": [], + "signature": [ + "{ description: string; } & ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaMetaOptional", + "text": "SchemaMetaOptional" + }, + "" + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.SchemaMeta", + "type": "Interface", + "tags": [], + "label": "SchemaMeta", + "description": [ + "\nSchema meta with optional description" + ], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaMeta", + "text": "SchemaMeta" + }, + "" + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.SchemaMeta._meta", + "type": "CompoundType", + "tags": [], + "label": "_meta", + "description": [], + "signature": [ + "({ description?: string | undefined; } & ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaMetaOptional", + "text": "SchemaMetaOptional" + }, + ") | undefined" + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.SchemaObject", + "type": "Interface", + "tags": [], + "label": "SchemaObject", + "description": [ + "\nSchema to represent an object" + ], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaObject", + "text": "SchemaObject" + }, + " extends ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaMeta", + "text": "SchemaMeta" + }, + "" + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.SchemaObject.properties", + "type": "Object", + "tags": [], + "label": "properties", + "description": [], + "signature": [ + "{ [Key in keyof Required]: ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaValue", + "text": "SchemaValue" + }, + "; }" + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.ShipperClassConstructor", + "type": "Interface", + "tags": [], + "label": "ShipperClassConstructor", + "description": [ + "\nConstructor of a {@link IShipper}" + ], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.ShipperClassConstructor", + "text": "ShipperClassConstructor" + }, + "" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.ShipperClassConstructor.shipperName", + "type": "string", + "tags": [], + "label": "shipperName", + "description": [ + "\nThe shipper's unique name" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.ShipperClassConstructor.Unnamed", + "type": "Any", + "tags": [], + "label": "Unnamed", + "description": [ + "\nThe constructor" + ], + "signature": [ + "any" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.TelemetryCounter", + "type": "Interface", + "tags": [], + "label": "TelemetryCounter", + "description": [ + "\nShape of the events emitted by the telemetryCounter$ observable" + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.TelemetryCounter.type", + "type": "Enum", + "tags": [], + "label": "type", + "description": [ + "\nIndicates if the event contains data about succeeded, failed or dropped events." + ], + "signature": [ + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.TelemetryCounterType", + "text": "TelemetryCounterType" + } + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.TelemetryCounter.source", + "type": "string", + "tags": [], + "label": "source", + "description": [ + "\nWho emitted the event? It can be \"client\" or the name of the shipper." + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.TelemetryCounter.event_type", + "type": "string", + "tags": [], + "label": "event_type", + "description": [ + "\nThe event type the success/failure/drop event refers to." + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.TelemetryCounter.code", + "type": "string", + "tags": [], + "label": "code", + "description": [ + "\nCode to provide additional information about the success or failure. Examples are 200/400/504/ValidationError/UnknownError" + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.TelemetryCounter.count", + "type": "number", + "tags": [], + "label": "count", + "description": [ + "\nThe number of events that this counter refers to." + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ], + "enums": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.TelemetryCounterType", + "type": "Enum", + "tags": [], + "label": "TelemetryCounterType", + "description": [ + "\nTypes of the Telemetry Counter: It allows to differentiate what happened to the events" + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false, + "initialIsOpen": false + } + ], + "misc": [ + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.AllowedSchemaBooleanTypes", + "type": "Type", + "tags": [], + "label": "AllowedSchemaBooleanTypes", + "description": [ + "Types matching boolean values" + ], + "signature": [ + "\"boolean\"" + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.AllowedSchemaNumberTypes", + "type": "Type", + "tags": [], + "label": "AllowedSchemaNumberTypes", + "description": [ + "Types matching number values" + ], + "signature": [ + "\"date\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"double\"" + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.AllowedSchemaStringTypes", + "type": "Type", + "tags": [], + "label": "AllowedSchemaStringTypes", + "description": [ + "Types matching string values" + ], + "signature": [ + "\"keyword\" | \"text\" | \"date\"" + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.AllowedSchemaTypes", + "type": "Type", + "tags": [], + "label": "AllowedSchemaTypes", + "description": [ + "\nPossible type values in the schema" + ], + "signature": [ + "\"boolean\" | \"keyword\" | \"text\" | \"date\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"double\"" + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.EventType", + "type": "Type", + "tags": [], + "label": "EventType", + "description": [ + "\nEvent Type used for indexed structures. Only used to improve the readability of the types" + ], + "signature": [ + "string" + ], + "path": "packages/analytics/client/src/events/types.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.PossibleSchemaTypes", + "type": "Type", + "tags": [], + "label": "PossibleSchemaTypes", + "description": [ + "\nHelper to ensure the declared types match the schema types" + ], + "signature": [ + "Value extends string ? ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.AllowedSchemaStringTypes", + "text": "AllowedSchemaStringTypes" + }, + " : Value extends number ? ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.AllowedSchemaNumberTypes", + "text": "AllowedSchemaNumberTypes" + }, + " : Value extends boolean ? \"boolean\" : ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.AllowedSchemaTypes", + "text": "AllowedSchemaTypes" + } + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.RootSchema", + "type": "Type", + "tags": [], + "label": "RootSchema", + "description": [ + "\nSchema definition to match the structure of the properties provided.\n" + ], + "signature": [ + "{ [Key in keyof Required]: ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaValue", + "text": "SchemaValue" + }, + "; }" + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.SchemaMetaOptional", + "type": "Type", + "tags": [], + "label": "SchemaMetaOptional", + "description": [ + "\nEnforces { optional: true } if the value can be undefined" + ], + "signature": [ + "unknown extends Value ? { optional?: boolean | undefined; } : undefined extends Value ? { optional: true; } : { optional?: false | undefined; }" + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.SchemaValue", + "type": "Type", + "tags": [], + "label": "SchemaValue", + "description": [ + "\nType that defines all the possible values that the Schema accepts.\nThese types definitions are helping to identify earlier the possible missing `properties` nesting when\nmanually defining the schemas." + ], + "signature": [ + "{ type: \"pass_through\"; _meta: { description: string; } & ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaMetaOptional", + "text": "SchemaMetaOptional" + }, + "; } | (unknown extends Value ? ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaArray", + "text": "SchemaArray" + }, + " | ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaObject", + "text": "SchemaObject" + }, + " | ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaChildValue", + "text": "SchemaChildValue" + }, + " : NonNullable extends (infer U)[] ? ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaArray", + "text": "SchemaArray" + }, + " : NonNullable extends object ? ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaObject", + "text": "SchemaObject" + }, + " : ", + { + "pluginId": "@kbn/analytics-client", + "scope": "server", + "docId": "kibKbnAnalyticsClientPluginApi", + "section": "def-server.SchemaChildValue", + "text": "SchemaChildValue" + }, + ")" + ], + "path": "packages/analytics/client/src/schema/types.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/analytics-client", + "id": "def-server.ShipperName", + "type": "Type", + "tags": [], + "label": "ShipperName", + "description": [ + "\nShipper Name used for indexed structures. Only used to improve the readability of the types" + ], + "signature": [ + "string" + ], + "path": "packages/analytics/client/src/analytics_client/types.ts", + "deprecated": false, + "initialIsOpen": false + } + ], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx new file mode 100644 index 0000000000000..e51ae9a874e9f --- /dev/null +++ b/api_docs/kbn_analytics_client.mdx @@ -0,0 +1,36 @@ +--- +id: kibKbnAnalyticsClientPluginApi +slug: /kibana-dev-docs/api/kbn-analytics-client +title: "@kbn/analytics-client" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/analytics-client plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] +warning: 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. +--- +import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 88 | 1 | 10 | 0 | + +## Server + +### Functions + + +### Interfaces + + +### Enums + + +### Consts, variables and types + + diff --git a/api_docs/kbn_analytics_shippers_fullstory.devdocs.json b/api_docs/kbn_analytics_shippers_fullstory.devdocs.json new file mode 100644 index 0000000000000..3f79e43e3a09c --- /dev/null +++ b/api_docs/kbn_analytics_shippers_fullstory.devdocs.json @@ -0,0 +1,314 @@ +{ + "id": "@kbn/analytics-shippers-fullstory", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [ + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStoryShipper", + "type": "Class", + "tags": [], + "label": "FullStoryShipper", + "description": [], + "signature": [ + { + "pluginId": "@kbn/analytics-shippers-fullstory", + "scope": "server", + "docId": "kibKbnAnalyticsShippersFullstoryPluginApi", + "section": "def-server.FullStoryShipper", + "text": "FullStoryShipper" + }, + " implements ", + "IShipper" + ], + "path": "packages/analytics/shippers/fullstory/src/fullstory_shipper.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStoryShipper.shipperName", + "type": "string", + "tags": [], + "label": "shipperName", + "description": [], + "path": "packages/analytics/shippers/fullstory/src/fullstory_shipper.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStoryShipper.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/analytics/shippers/fullstory/src/fullstory_shipper.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStoryShipper.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "config", + "description": [], + "signature": [ + { + "pluginId": "@kbn/analytics-shippers-fullstory", + "scope": "server", + "docId": "kibKbnAnalyticsShippersFullstoryPluginApi", + "section": "def-server.FullStorySnippetConfig", + "text": "FullStorySnippetConfig" + } + ], + "path": "packages/analytics/shippers/fullstory/src/fullstory_shipper.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStoryShipper.Unnamed.$2", + "type": "Object", + "tags": [], + "label": "initContext", + "description": [], + "signature": [ + "AnalyticsClientInitContext" + ], + "path": "packages/analytics/shippers/fullstory/src/fullstory_shipper.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStoryShipper.extendContext", + "type": "Function", + "tags": [], + "label": "extendContext", + "description": [], + "signature": [ + "(newContext: ", + "EventContext", + ") => void" + ], + "path": "packages/analytics/shippers/fullstory/src/fullstory_shipper.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStoryShipper.extendContext.$1", + "type": "Object", + "tags": [], + "label": "newContext", + "description": [], + "signature": [ + "EventContext" + ], + "path": "packages/analytics/shippers/fullstory/src/fullstory_shipper.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStoryShipper.optIn", + "type": "Function", + "tags": [], + "label": "optIn", + "description": [], + "signature": [ + "(isOptedIn: boolean) => void" + ], + "path": "packages/analytics/shippers/fullstory/src/fullstory_shipper.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStoryShipper.optIn.$1", + "type": "boolean", + "tags": [], + "label": "isOptedIn", + "description": [], + "signature": [ + "boolean" + ], + "path": "packages/analytics/shippers/fullstory/src/fullstory_shipper.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStoryShipper.reportEvents", + "type": "Function", + "tags": [], + "label": "reportEvents", + "description": [], + "signature": [ + "(events: ", + "Event", + "[]) => void" + ], + "path": "packages/analytics/shippers/fullstory/src/fullstory_shipper.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStoryShipper.reportEvents.$1", + "type": "Array", + "tags": [], + "label": "events", + "description": [], + "signature": [ + "Event", + "[]" + ], + "path": "packages/analytics/shippers/fullstory/src/fullstory_shipper.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [], + "interfaces": [ + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStorySnippetConfig", + "type": "Interface", + "tags": [], + "label": "FullStorySnippetConfig", + "description": [], + "path": "packages/analytics/shippers/fullstory/src/load_snippet.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStorySnippetConfig.fullStoryOrgId", + "type": "string", + "tags": [], + "label": "fullStoryOrgId", + "description": [ + "\nThe FullStory account id." + ], + "path": "packages/analytics/shippers/fullstory/src/load_snippet.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStorySnippetConfig.host", + "type": "string", + "tags": [], + "label": "host", + "description": [ + "\nThe host to send the data to. Used to overcome AdBlockers by using custom DNSs.\nIf not specified, it defaults to `fullstory.com`." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/analytics/shippers/fullstory/src/load_snippet.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStorySnippetConfig.scriptUrl", + "type": "string", + "tags": [], + "label": "scriptUrl", + "description": [ + "\nThe URL to load the FullStory client from. Falls back to `edge.fullstory.com/s/fs.js` if not specified." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/analytics/shippers/fullstory/src/load_snippet.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStorySnippetConfig.debug", + "type": "CompoundType", + "tags": [], + "label": "debug", + "description": [ + "\nWhether the debug logs should be printed to the console." + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/analytics/shippers/fullstory/src/load_snippet.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStorySnippetConfig.namespace", + "type": "string", + "tags": [], + "label": "namespace", + "description": [ + "\nThe name of the variable where the API is stored: `window[namespace]`. Defaults to `FS`." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/analytics/shippers/fullstory/src/load_snippet.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/analytics-shippers-fullstory", + "id": "def-server.FullStoryShipperConfig", + "type": "Type", + "tags": [], + "label": "FullStoryShipperConfig", + "description": [], + "signature": [ + { + "pluginId": "@kbn/analytics-shippers-fullstory", + "scope": "server", + "docId": "kibKbnAnalyticsShippersFullstoryPluginApi", + "section": "def-server.FullStorySnippetConfig", + "text": "FullStorySnippetConfig" + } + ], + "path": "packages/analytics/shippers/fullstory/src/fullstory_shipper.ts", + "deprecated": false, + "initialIsOpen": false + } + ], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx new file mode 100644 index 0000000000000..326388928255e --- /dev/null +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -0,0 +1,33 @@ +--- +id: kibKbnAnalyticsShippersFullstoryPluginApi +slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory +title: "@kbn/analytics-shippers-fullstory" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/analytics-shippers-fullstory plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] +warning: 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. +--- +import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 18 | 0 | 13 | 0 | + +## Server + +### Classes + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 2ed7dbdbfda88..5e544916f7581 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/apm-config-loader plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] warning: 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. --- diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 49fb44941d1dc..6b8bd73db26cd 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/apm-utils plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] warning: 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. --- diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index 6acea95d20cc4..91196a5c6fff9 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/axe-config plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] warning: 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. --- diff --git a/api_docs/kbn_bazel_packages.devdocs.json b/api_docs/kbn_bazel_packages.devdocs.json index babc54a49973c..993554aca7636 100644 --- a/api_docs/kbn_bazel_packages.devdocs.json +++ b/api_docs/kbn_bazel_packages.devdocs.json @@ -29,7 +29,7 @@ "tags": [], "label": "fromDir", "description": [ - "\nCreate a BazelPackage object from a package directory. Reads some files from the package and returns\na Promise for a BazelPackage instance" + "\nCreate a BazelPackage object from a package directory. Reads some files from the package and returns\na Promise for a BazelPackage instance." ], "signature": [ "(dir: string) => Promise<", @@ -154,6 +154,23 @@ "children": [], "returnComment": [] }, + { + "parentPluginId": "@kbn/bazel-packages", + "id": "def-server.BazelPackage.isDevOnly", + "type": "Function", + "tags": [], + "label": "isDevOnly", + "description": [ + "\nReturns true if the package is not intended to be in the build" + ], + "signature": [ + "() => boolean" + ], + "path": "packages/kbn-bazel-packages/src/bazel_package.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "@kbn/bazel-packages", "id": "def-server.BazelPackage.inspect.custom", @@ -176,17 +193,46 @@ } ], "functions": [ + { + "parentPluginId": "@kbn/bazel-packages", + "id": "def-server.discoverBazelPackageLocations", + "type": "Function", + "tags": [], + "label": "discoverBazelPackageLocations", + "description": [], + "signature": [ + "(repoRoot: string) => string[]" + ], + "path": "packages/kbn-bazel-packages/src/discover_packages.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/bazel-packages", + "id": "def-server.discoverBazelPackageLocations.$1", + "type": "string", + "tags": [], + "label": "repoRoot", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-bazel-packages/src/discover_packages.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/bazel-packages", "id": "def-server.discoverBazelPackages", "type": "Function", "tags": [], "label": "discoverBazelPackages", - "description": [ - "\nSearch the local Kibana repo for bazel packages and return an array of BazelPackage objects\nrepresenting each package found." - ], + "description": [], "signature": [ - "() => Promise<", + "(repoRoot: string) => Promise<", { "pluginId": "@kbn/bazel-packages", "scope": "server", @@ -198,6 +244,57 @@ ], "path": "packages/kbn-bazel-packages/src/discover_packages.ts", "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/bazel-packages", + "id": "def-server.discoverBazelPackages.$1", + "type": "string", + "tags": [], + "label": "repoRoot", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-bazel-packages/src/discover_packages.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/bazel-packages", + "id": "def-server.getAllBazelPackageDirs", + "type": "Function", + "tags": [], + "label": "getAllBazelPackageDirs", + "description": [ + "\nResolve all the BAZEL_PACKAGE_DIRS to absolute paths" + ], + "signature": [ + "() => EntryInternal[] & string[]" + ], + "path": "packages/kbn-bazel-packages/src/bazel_package_dirs.ts", + "deprecated": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/bazel-packages", + "id": "def-server.getAllRepoRelativeBazelPackageDirs", + "type": "Function", + "tags": [], + "label": "getAllRepoRelativeBazelPackageDirs", + "description": [ + "\nResolve all the BAZEL_PACKAGE_DIRS to repo-relative paths" + ], + "signature": [ + "() => string[]" + ], + "path": "packages/kbn-bazel-packages/src/bazel_package_dirs.ts", + "deprecated": false, "children": [], "returnComment": [], "initialIsOpen": false @@ -213,7 +310,7 @@ "tags": [], "label": "BAZEL_PACKAGE_DIRS", "description": [ - "\nThis is a list of repo-relative paths to directories containing packages. Do not\ninclude `**` in these, one or two `*` segments is acceptable, we need this search\nto be super fast so please avoid deep recursive searching.\n\n eg. src/vis-editors => would find a package at src/vis-editors/foo/package.json\n src/vis-editors/* => would find a package at src/vis-editors/foo/bar/package.json" + "\nThis is a list of repo-relative paths to directories containing packages. Do not\ninclude `**` in these, one or two `*` segments is acceptable, we need this search\nto be super fast so please avoid deep recursive searching.\n\n eg. src/vis_editors => would find a package at src/vis_editors/foo/package.json\n src/vis_editors/* => would find a package at src/vis_editors/foo/bar/package.json" ], "signature": [ "string[]" diff --git a/api_docs/kbn_bazel_packages.mdx b/api_docs/kbn_bazel_packages.mdx index 4cecba08dd051..a6dbbd7872270 100644 --- a/api_docs/kbn_bazel_packages.mdx +++ b/api_docs/kbn_bazel_packages.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-bazel-packages title: "@kbn/bazel-packages" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/bazel-packages plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bazel-packages'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 12 | 0 | 5 | 1 | +| 18 | 0 | 9 | 1 | ## Server diff --git a/api_docs/kbn_bazel_runner.devdocs.json b/api_docs/kbn_bazel_runner.devdocs.json new file mode 100644 index 0000000000000..e5f5473949f65 --- /dev/null +++ b/api_docs/kbn_bazel_runner.devdocs.json @@ -0,0 +1,90 @@ +{ + "id": "@kbn/bazel-runner", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/bazel-runner", + "id": "def-server.runBazel", + "type": "Function", + "tags": [], + "label": "runBazel", + "description": [], + "signature": [ + "(options: BazelRunOptions) => Promise" + ], + "path": "packages/kbn-bazel-runner/src/bazel_runner.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/bazel-runner", + "id": "def-server.runBazel.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "BazelRunOptions" + ], + "path": "packages/kbn-bazel-runner/src/bazel_runner.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/bazel-runner", + "id": "def-server.runIBazel", + "type": "Function", + "tags": [], + "label": "runIBazel", + "description": [], + "signature": [ + "(options: BazelRunOptions) => Promise" + ], + "path": "packages/kbn-bazel-runner/src/bazel_runner.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/bazel-runner", + "id": "def-server.runIBazel.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "BazelRunOptions" + ], + "path": "packages/kbn-bazel-runner/src/bazel_runner.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_bazel_runner.mdx b/api_docs/kbn_bazel_runner.mdx new file mode 100644 index 0000000000000..e26e06d6d8241 --- /dev/null +++ b/api_docs/kbn_bazel_runner.mdx @@ -0,0 +1,27 @@ +--- +id: kibKbnBazelRunnerPluginApi +slug: /kibana-dev-docs/api/kbn-bazel-runner +title: "@kbn/bazel-runner" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/bazel-runner plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bazel-runner'] +warning: 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. +--- +import kbnBazelRunnerObj from './kbn_bazel_runner.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 4 | 0 | 4 | 0 | + +## Server + +### Functions + + diff --git a/api_docs/kbn_ci_stats_client.devdocs.json b/api_docs/kbn_ci_stats_client.devdocs.json new file mode 100644 index 0000000000000..bc6b6ba628f44 --- /dev/null +++ b/api_docs/kbn_ci_stats_client.devdocs.json @@ -0,0 +1,158 @@ +{ + "id": "@kbn/ci-stats-client", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [ + { + "parentPluginId": "@kbn/ci-stats-client", + "id": "def-server.CiStatsClient", + "type": "Class", + "tags": [], + "label": "CiStatsClient", + "description": [], + "path": "packages/kbn-ci-stats-client/src/ci_stats_client.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-client", + "id": "def-server.CiStatsClient.fromEnv", + "type": "Function", + "tags": [], + "label": "fromEnv", + "description": [ + "\nCreate a CiStatsReporter by inspecting the ENV for the necessary config" + ], + "signature": [ + "(log: ", + "ToolingLog", + ") => ", + { + "pluginId": "@kbn/ci-stats-client", + "scope": "server", + "docId": "kibKbnCiStatsClientPluginApi", + "section": "def-server.CiStatsClient", + "text": "CiStatsClient" + } + ], + "path": "packages/kbn-ci-stats-client/src/ci_stats_client.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-client", + "id": "def-server.CiStatsClient.fromEnv.$1", + "type": "Object", + "tags": [], + "label": "log", + "description": [], + "signature": [ + "ToolingLog" + ], + "path": "packages/kbn-ci-stats-client/src/ci_stats_client.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/ci-stats-client", + "id": "def-server.CiStatsClient.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-ci-stats-client/src/ci_stats_client.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-client", + "id": "def-server.CiStatsClient.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "config", + "description": [], + "signature": [ + "Config", + " | undefined" + ], + "path": "packages/kbn-ci-stats-client/src/ci_stats_client.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/ci-stats-client", + "id": "def-server.CiStatsClient.isEnabled", + "type": "Function", + "tags": [], + "label": "isEnabled", + "description": [], + "signature": [ + "() => boolean" + ], + "path": "packages/kbn-ci-stats-client/src/ci_stats_client.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/ci-stats-client", + "id": "def-server.CiStatsClient.getLatestTestGroupStats", + "type": "Function", + "tags": [], + "label": "getLatestTestGroupStats", + "description": [], + "signature": [ + "(options: LatestTestGroupStatsOptions) => Promise" + ], + "path": "packages/kbn-ci-stats-client/src/ci_stats_client.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-client", + "id": "def-server.CiStatsClient.getLatestTestGroupStats.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "LatestTestGroupStatsOptions" + ], + "path": "packages/kbn-ci-stats-client/src/ci_stats_client.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_ci_stats_client.mdx b/api_docs/kbn_ci_stats_client.mdx new file mode 100644 index 0000000000000..7e081672825d1 --- /dev/null +++ b/api_docs/kbn_ci_stats_client.mdx @@ -0,0 +1,27 @@ +--- +id: kibKbnCiStatsClientPluginApi +slug: /kibana-dev-docs/api/kbn-ci-stats-client +title: "@kbn/ci-stats-client" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/ci-stats-client plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-client'] +warning: 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. +--- +import kbnCiStatsClientObj from './kbn_ci_stats_client.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 8 | 0 | 7 | 0 | + +## Server + +### Classes + + diff --git a/api_docs/kbn_ci_stats_core.devdocs.json b/api_docs/kbn_ci_stats_core.devdocs.json new file mode 100644 index 0000000000000..d088822187665 --- /dev/null +++ b/api_docs/kbn_ci_stats_core.devdocs.json @@ -0,0 +1,139 @@ +{ + "id": "@kbn/ci-stats-core", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/ci-stats-core", + "id": "def-server.parseConfig", + "type": "Function", + "tags": [], + "label": "parseConfig", + "description": [], + "signature": [ + "(log: ", + "ToolingLog", + ") => ", + { + "pluginId": "@kbn/ci-stats-core", + "scope": "server", + "docId": "kibKbnCiStatsCorePluginApi", + "section": "def-server.Config", + "text": "Config" + }, + " | undefined" + ], + "path": "packages/kbn-ci-stats-core/src/ci_stats_config.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-core", + "id": "def-server.parseConfig.$1", + "type": "Object", + "tags": [], + "label": "log", + "description": [], + "signature": [ + "ToolingLog" + ], + "path": "packages/kbn-ci-stats-core/src/ci_stats_config.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/ci-stats-core", + "id": "def-server.CiStatsMetadata", + "type": "Interface", + "tags": [], + "label": "CiStatsMetadata", + "description": [ + "Container for metadata that can be attached to different ci-stats objects" + ], + "path": "packages/kbn-ci-stats-core/src/ci_stats_metadata.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-core", + "id": "def-server.CiStatsMetadata.Unnamed", + "type": "IndexSignature", + "tags": [], + "label": "[key: string]: string | number | boolean | string[] | undefined", + "description": [ + "\nArbitrary key-value pairs which can be attached to CiStatsTiming and CiStatsMetric\nobjects stored in the ci-stats service" + ], + "signature": [ + "[key: string]: string | number | boolean | string[] | undefined" + ], + "path": "packages/kbn-ci-stats-core/src/ci_stats_metadata.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ci-stats-core", + "id": "def-server.Config", + "type": "Interface", + "tags": [], + "label": "Config", + "description": [ + "\nInformation about how CiStatsReporter should talk to the ci-stats service. Normally\nit is read from a JSON environment variable using the `parseConfig()` function\nexported by this module." + ], + "path": "packages/kbn-ci-stats-core/src/ci_stats_config.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-core", + "id": "def-server.Config.apiToken", + "type": "string", + "tags": [], + "label": "apiToken", + "description": [ + "ApiToken necessary for writing build data to ci-stats service" + ], + "path": "packages/kbn-ci-stats-core/src/ci_stats_config.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-core", + "id": "def-server.Config.buildId", + "type": "string", + "tags": [], + "label": "buildId", + "description": [ + "\nuuid which should be obtained by first creating a build with the\nci-stats service and then passing it to all subsequent steps" + ], + "path": "packages/kbn-ci-stats-core/src/ci_stats_config.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx new file mode 100644 index 0000000000000..a309e10212e64 --- /dev/null +++ b/api_docs/kbn_ci_stats_core.mdx @@ -0,0 +1,30 @@ +--- +id: kibKbnCiStatsCorePluginApi +slug: /kibana-dev-docs/api/kbn-ci-stats-core +title: "@kbn/ci-stats-core" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/ci-stats-core plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] +warning: 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. +--- +import kbnCiStatsCoreObj from './kbn_ci_stats_core.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 7 | 0 | 2 | 0 | + +## Server + +### Functions + + +### Interfaces + + diff --git a/api_docs/kbn_ci_stats_reporter.devdocs.json b/api_docs/kbn_ci_stats_reporter.devdocs.json new file mode 100644 index 0000000000000..f579d304c08db --- /dev/null +++ b/api_docs/kbn_ci_stats_reporter.devdocs.json @@ -0,0 +1,968 @@ +{ + "id": "@kbn/ci-stats-reporter", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReporter", + "type": "Class", + "tags": [], + "label": "CiStatsReporter", + "description": [ + "Object that helps report data to the ci-stats service" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReporter.fromEnv", + "type": "Function", + "tags": [], + "label": "fromEnv", + "description": [ + "\nCreate a CiStatsReporter by inspecting the ENV for the necessary config" + ], + "signature": [ + "(log: ", + "ToolingLog", + ") => ", + { + "pluginId": "@kbn/ci-stats-reporter", + "scope": "server", + "docId": "kibKbnCiStatsReporterPluginApi", + "section": "def-server.CiStatsReporter", + "text": "CiStatsReporter" + } + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReporter.fromEnv.$1", + "type": "Object", + "tags": [], + "label": "log", + "description": [], + "signature": [ + "ToolingLog" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReporter.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReporter.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "config", + "description": [], + "signature": [ + "Config", + " | undefined" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "isRequired": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReporter.Unnamed.$2", + "type": "Object", + "tags": [], + "label": "log", + "description": [], + "signature": [ + "ToolingLog" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReporter.isEnabled", + "type": "Function", + "tags": [], + "label": "isEnabled", + "description": [ + "\nDetermine if CI_STATS is explicitly disabled by the environment. To determine\nif the CiStatsReporter has enough information in the environment to send metrics\nfor builds use #hasBuildConfig()." + ], + "signature": [ + "() => boolean" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReporter.hasBuildConfig", + "type": "Function", + "tags": [], + "label": "hasBuildConfig", + "description": [ + "\nDetermines if the CiStatsReporter is disabled by the environment, or properly\nconfigured and able to send stats" + ], + "signature": [ + "() => boolean" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReporter.timings", + "type": "Function", + "tags": [], + "label": "timings", + "description": [ + "\nReport timings data to the ci-stats service. If running in CI then the reporter\nwill include the buildId in the report with the access token, otherwise the timings\ndata will be recorded as anonymous timing data." + ], + "signature": [ + "(options: ", + { + "pluginId": "@kbn/ci-stats-reporter", + "scope": "server", + "docId": "kibKbnCiStatsReporterPluginApi", + "section": "def-server.TimingsOptions", + "text": "TimingsOptions" + }, + ") => Promise" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReporter.timings.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/ci-stats-reporter", + "scope": "server", + "docId": "kibKbnCiStatsReporterPluginApi", + "section": "def-server.TimingsOptions", + "text": "TimingsOptions" + } + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReporter.metrics", + "type": "Function", + "tags": [], + "label": "metrics", + "description": [ + "\nReport metrics data to the ci-stats service. If running outside of CI this method\ndoes nothing as metrics can only be reported when associated with a specific CI build." + ], + "signature": [ + "(metrics: ", + { + "pluginId": "@kbn/ci-stats-reporter", + "scope": "server", + "docId": "kibKbnCiStatsReporterPluginApi", + "section": "def-server.CiStatsMetric", + "text": "CiStatsMetric" + }, + "[], options?: ", + { + "pluginId": "@kbn/ci-stats-reporter", + "scope": "server", + "docId": "kibKbnCiStatsReporterPluginApi", + "section": "def-server.MetricsOptions", + "text": "MetricsOptions" + }, + " | undefined) => Promise" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReporter.metrics.$1", + "type": "Array", + "tags": [], + "label": "metrics", + "description": [], + "signature": [ + { + "pluginId": "@kbn/ci-stats-reporter", + "scope": "server", + "docId": "kibKbnCiStatsReporterPluginApi", + "section": "def-server.CiStatsMetric", + "text": "CiStatsMetric" + }, + "[]" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReporter.metrics.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/ci-stats-reporter", + "scope": "server", + "docId": "kibKbnCiStatsReporterPluginApi", + "section": "def-server.MetricsOptions", + "text": "MetricsOptions" + }, + " | undefined" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReporter.reportTests", + "type": "Function", + "tags": [], + "label": "reportTests", + "description": [ + "\nSend test reports to ci-stats" + ], + "signature": [ + "({ group, testRuns }: ", + { + "pluginId": "@kbn/ci-stats-reporter", + "scope": "server", + "docId": "kibKbnCiStatsReporterPluginApi", + "section": "def-server.CiStatsReportTestsOptions", + "text": "CiStatsReportTestsOptions" + }, + ") => Promise" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReporter.reportTests.$1", + "type": "Object", + "tags": [], + "label": "{ group, testRuns }", + "description": [], + "signature": [ + { + "pluginId": "@kbn/ci-stats-reporter", + "scope": "server", + "docId": "kibKbnCiStatsReporterPluginApi", + "section": "def-server.CiStatsReportTestsOptions", + "text": "CiStatsReportTestsOptions" + } + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.getTimeReporter", + "type": "Function", + "tags": [], + "label": "getTimeReporter", + "description": [], + "signature": [ + "(log: ", + "ToolingLog", + ", group: string) => (startTime: number, id: string, meta: Record) => Promise" + ], + "path": "packages/kbn-ci-stats-reporter/src/report_time.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.getTimeReporter.$1", + "type": "Object", + "tags": [], + "label": "log", + "description": [], + "signature": [ + "ToolingLog" + ], + "path": "packages/kbn-ci-stats-reporter/src/report_time.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.getTimeReporter.$2", + "type": "string", + "tags": [], + "label": "group", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-ci-stats-reporter/src/report_time.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsMetric", + "type": "Interface", + "tags": [], + "label": "CiStatsMetric", + "description": [ + "A ci-stats metric record" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsMetric.group", + "type": "string", + "tags": [], + "label": "group", + "description": [ + "Top-level categorization for the metric, e.g. \"page load bundle size\"" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsMetric.id", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "Specific sub-set of the \"group\", e.g. \"dashboard\"" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsMetric.value", + "type": "number", + "tags": [], + "label": "value", + "description": [ + "integer value recorded as the value of this metric" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsMetric.limit", + "type": "number", + "tags": [], + "label": "limit", + "description": [ + "optional limit which will generate an error on PRs when the metric exceeds the limit" + ], + "signature": [ + "number | undefined" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsMetric.limitConfigPath", + "type": "string", + "tags": [], + "label": "limitConfigPath", + "description": [ + "\npath, relative to the repo, where the config file contianing limits\nis kept. Linked from PR comments instructing contributors how to fix\ntheir PRs." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsMetric.meta", + "type": "Object", + "tags": [], + "label": "meta", + "description": [ + "Arbitrary key-value pairs which can be used for additional filtering/reporting" + ], + "signature": [ + "CiStatsMetadata", + " | undefined" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReportTestsOptions", + "type": "Interface", + "tags": [], + "label": "CiStatsReportTestsOptions", + "description": [ + "Options for reporting tests to ci-stats" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReportTestsOptions.group", + "type": "Object", + "tags": [], + "label": "group", + "description": [ + "\nInformation about the group of tests that were run" + ], + "signature": [ + { + "pluginId": "@kbn/ci-stats-reporter", + "scope": "server", + "docId": "kibKbnCiStatsReporterPluginApi", + "section": "def-server.CiStatsTestGroupInfo", + "text": "CiStatsTestGroupInfo" + } + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsReportTestsOptions.testRuns", + "type": "Array", + "tags": [], + "label": "testRuns", + "description": [ + "\nInformation about each test that ran, including failure information" + ], + "signature": [ + { + "pluginId": "@kbn/ci-stats-reporter", + "scope": "server", + "docId": "kibKbnCiStatsReporterPluginApi", + "section": "def-server.CiStatsTestRun", + "text": "CiStatsTestRun" + }, + "[]" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestGroupInfo", + "type": "Interface", + "tags": [], + "label": "CiStatsTestGroupInfo", + "description": [], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestGroupInfo.startTime", + "type": "string", + "tags": [], + "label": "startTime", + "description": [ + "\nISO-8601 formatted datetime representing when the group of tests started running" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestGroupInfo.durationMs", + "type": "number", + "tags": [], + "label": "durationMs", + "description": [ + "\nThe number of miliseconds that the tests ran for" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestGroupInfo.type", + "type": "string", + "tags": [], + "label": "type", + "description": [ + "\nThe type of tests run in this group, any value is valid but test groups are groupped by type in the UI so use something consistent" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestGroupInfo.name", + "type": "string", + "tags": [], + "label": "name", + "description": [ + "\nThe name of this specific group (within the \"type\")" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestGroupInfo.meta", + "type": "Object", + "tags": [], + "label": "meta", + "description": [ + "\nArbitrary metadata associated with this group. We currently look for a ciGroup metadata property for highlighting that when appropriate" + ], + "signature": [ + "CiStatsMetadata" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestRun", + "type": "Interface", + "tags": [], + "label": "CiStatsTestRun", + "description": [], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestRun.startTime", + "type": "string", + "tags": [], + "label": "startTime", + "description": [ + "\nISO-8601 formatted datetime representing when the tests started running" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestRun.durationMs", + "type": "number", + "tags": [], + "label": "durationMs", + "description": [ + "\nDuration of the tests in milliseconds" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestRun.seq", + "type": "number", + "tags": [], + "label": "seq", + "description": [ + "\nA sequence number, this is used to order the tests in a specific test run" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestRun.type", + "type": "CompoundType", + "tags": [], + "label": "type", + "description": [ + "\nThe type of this \"test run\", usually this is just \"test\" but when reporting issues in hooks it can be set to the type of hook" + ], + "signature": [ + "\"after all hook\" | \"after each hook\" | \"before all hook\" | \"before each hook\" | \"test\"" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestRun.result", + "type": "CompoundType", + "tags": [], + "label": "result", + "description": [ + "\n\"fail\", \"pass\" or \"skip\", the result of the tests" + ], + "signature": [ + "\"fail\" | \"pass\" | \"skip\"" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestRun.suites", + "type": "Array", + "tags": [], + "label": "suites", + "description": [ + "\nThe list of suite names containing this test, the first being the outermost suite" + ], + "signature": [ + "string[]" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestRun.name", + "type": "string", + "tags": [], + "label": "name", + "description": [ + "\nThe name of this specific test run" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestRun.file", + "type": "string", + "tags": [], + "label": "file", + "description": [ + "\nRelative path from the root of the repo contianing this test" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestRun.error", + "type": "string", + "tags": [], + "label": "error", + "description": [ + "\nError message if the test failed" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestRun.stdout", + "type": "string", + "tags": [], + "label": "stdout", + "description": [ + "\nDebug output/stdout produced by the test" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestRun.screenshots", + "type": "Array", + "tags": [], + "label": "screenshots", + "description": [ + "\nScreenshots captured during the test run" + ], + "signature": [ + "{ name: string; base64Png: string; }[] | undefined" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTiming", + "type": "Interface", + "tags": [], + "label": "CiStatsTiming", + "description": [ + "A ci-stats timing event" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTiming.group", + "type": "string", + "tags": [], + "label": "group", + "description": [ + "Top-level categorization for the timing, e.g. \"scripts/foo\", process type, etc." + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTiming.id", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "Specific timing (witin the \"group\" being tracked) e.g. \"total\"" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTiming.ms", + "type": "number", + "tags": [], + "label": "ms", + "description": [ + "time in milliseconds which should be recorded" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTiming.meta", + "type": "Object", + "tags": [], + "label": "meta", + "description": [ + "hash of key-value pairs which will be stored with the timing for additional filtering and reporting" + ], + "signature": [ + "CiStatsMetadata", + " | undefined" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.MetricsOptions", + "type": "Interface", + "tags": [], + "label": "MetricsOptions", + "description": [ + "Options for reporting metrics to ci-stats" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.MetricsOptions.defaultMeta", + "type": "Object", + "tags": [], + "label": "defaultMeta", + "description": [ + "Default metadata to add to each metric" + ], + "signature": [ + "CiStatsMetadata", + " | undefined" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.TimingsOptions", + "type": "Interface", + "tags": [], + "label": "TimingsOptions", + "description": [ + "Options for reporting timings to ci-stats" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.TimingsOptions.timings", + "type": "Array", + "tags": [], + "label": "timings", + "description": [ + "list of timings to record" + ], + "signature": [ + { + "pluginId": "@kbn/ci-stats-reporter", + "scope": "server", + "docId": "kibKbnCiStatsReporterPluginApi", + "section": "def-server.CiStatsTiming", + "text": "CiStatsTiming" + }, + "[]" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.TimingsOptions.upstreamBranch", + "type": "string", + "tags": [], + "label": "upstreamBranch", + "description": [ + "master, 7.x, etc, automatically detected from package.json if not specified" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.TimingsOptions.kibanaUuid", + "type": "CompoundType", + "tags": [], + "label": "kibanaUuid", + "description": [ + "value of data/uuid, automatically loaded if not specified" + ], + "signature": [ + "string | null | undefined" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestResult", + "type": "Type", + "tags": [], + "label": "CiStatsTestResult", + "description": [], + "signature": [ + "\"fail\" | \"pass\" | \"skip\"" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ci-stats-reporter", + "id": "def-server.CiStatsTestType", + "type": "Type", + "tags": [], + "label": "CiStatsTestType", + "description": [], + "signature": [ + "\"after all hook\" | \"after each hook\" | \"before all hook\" | \"before each hook\" | \"test\"" + ], + "path": "packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts", + "deprecated": false, + "initialIsOpen": false + } + ], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx new file mode 100644 index 0000000000000..0cc182de00726 --- /dev/null +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -0,0 +1,36 @@ +--- +id: kibKbnCiStatsReporterPluginApi +slug: /kibana-dev-docs/api/kbn-ci-stats-reporter +title: "@kbn/ci-stats-reporter" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/ci-stats-reporter plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] +warning: 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. +--- +import kbnCiStatsReporterObj from './kbn_ci_stats_reporter.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 59 | 0 | 15 | 0 | + +## Server + +### Functions + + +### Classes + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index c3491abc76979..5f3a928f804bc 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/cli-dev-mode plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] warning: 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. --- diff --git a/api_docs/kbn_coloring.devdocs.json b/api_docs/kbn_coloring.devdocs.json new file mode 100644 index 0000000000000..b6c631706bfaf --- /dev/null +++ b/api_docs/kbn_coloring.devdocs.json @@ -0,0 +1,1791 @@ +{ + "id": "@kbn/coloring", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.checkIsMaxContinuity", + "type": "Function", + "tags": [], + "label": "checkIsMaxContinuity", + "description": [], + "signature": [ + "(continuity: ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.PaletteContinuity", + "text": "PaletteContinuity" + }, + " | undefined) => boolean" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.checkIsMaxContinuity.$1", + "type": "CompoundType", + "tags": [], + "label": "continuity", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.PaletteContinuity", + "text": "PaletteContinuity" + }, + " | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.checkIsMinContinuity", + "type": "Function", + "tags": [], + "label": "checkIsMinContinuity", + "description": [], + "signature": [ + "(continuity: ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.PaletteContinuity", + "text": "PaletteContinuity" + }, + " | undefined) => boolean" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.checkIsMinContinuity.$1", + "type": "CompoundType", + "tags": [], + "label": "continuity", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.PaletteContinuity", + "text": "PaletteContinuity" + }, + " | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CustomizablePalette", + "type": "Function", + "tags": [], + "label": "CustomizablePalette", + "description": [ + "\nA `CustomizablePalette` component that is wrapped by the `withSuspense` HOC. This component can\nbe used directly by consumers and will load the `KibanaPageTemplateSolutionNavAvatarLazy` component lazily with\na predefined fallback and error boundary." + ], + "signature": [ + "React.ForwardRefExoticComponent<", + "CustomizablePaletteProps", + " & React.RefAttributes<{}>>" + ], + "path": "packages/kbn-coloring/src/shared_components/index.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CustomizablePalette.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CustomizablePaletteLazy", + "type": "Function", + "tags": [], + "label": "CustomizablePaletteLazy", + "description": [ + "\nThe Lazily-loaded `CustomizablePalette` component. Consumers should use `React.Suspense` or\nthe withSuspense` HOC to load this component." + ], + "signature": [ + "React.ExoticComponent<", + "CustomizablePaletteProps", + "> & { readonly _result: ({ palettes, activePalette, setPalette, dataBounds, showExtraActions, showRangeTypeSelector, disableSwitchingContinuity, }: ", + "CustomizablePaletteProps", + ") => JSX.Element; }" + ], + "path": "packages/kbn-coloring/src/shared_components/index.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CustomizablePaletteLazy.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getDataMinMax", + "type": "Function", + "tags": [], + "label": "getDataMinMax", + "description": [], + "signature": [ + "(rangeType: \"number\" | \"percent\" | undefined, dataBounds: ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.DataBounds", + "text": "DataBounds" + }, + ") => { min: number; max: number; }" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getDataMinMax.$1", + "type": "CompoundType", + "tags": [], + "label": "rangeType", + "description": [], + "signature": [ + "\"number\" | \"percent\" | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "isRequired": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getDataMinMax.$2", + "type": "Object", + "tags": [], + "label": "dataBounds", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.DataBounds", + "text": "DataBounds" + } + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getFallbackDataBounds", + "type": "Function", + "tags": [], + "label": "getFallbackDataBounds", + "description": [], + "signature": [ + "(rangeType?: \"number\" | \"percent\" | undefined) => ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.DataBounds", + "text": "DataBounds" + } + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getFallbackDataBounds.$1", + "type": "CompoundType", + "tags": [], + "label": "rangeType", + "description": [], + "signature": [ + "\"number\" | \"percent\" | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getPaletteStops", + "type": "Function", + "tags": [], + "label": "getPaletteStops", + "description": [ + "\nThis is a generic function to compute stops from the current parameters." + ], + "signature": [ + "(palettes: ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.PaletteRegistry", + "text": "PaletteRegistry" + }, + ", activePaletteParams: ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.CustomPaletteParams", + "text": "CustomPaletteParams" + }, + ", {\n prevPalette,\n dataBounds,\n mapFromMinValue,\n defaultPaletteName,\n }: { prevPalette?: string | undefined; dataBounds: ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.DataBounds", + "text": "DataBounds" + }, + "; mapFromMinValue?: boolean | undefined; defaultPaletteName?: string | undefined; }) => { stop: number; color: string; }[]" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getPaletteStops.$1", + "type": "Object", + "tags": [], + "label": "palettes", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.PaletteRegistry", + "text": "PaletteRegistry" + } + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getPaletteStops.$2", + "type": "Object", + "tags": [], + "label": "activePaletteParams", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.CustomPaletteParams", + "text": "CustomPaletteParams" + } + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getPaletteStops.$3", + "type": "Object", + "tags": [], + "label": "{\n prevPalette,\n dataBounds,\n mapFromMinValue,\n defaultPaletteName,\n }", + "description": [], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getPaletteStops.$3.prevPalette", + "type": "string", + "tags": [], + "label": "prevPalette", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getPaletteStops.$3.dataBounds", + "type": "Object", + "tags": [], + "label": "dataBounds", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.DataBounds", + "text": "DataBounds" + } + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getPaletteStops.$3.mapFromMinValue", + "type": "CompoundType", + "tags": [], + "label": "mapFromMinValue", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getPaletteStops.$3.defaultPaletteName", + "type": "string", + "tags": [], + "label": "defaultPaletteName", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getStepValue", + "type": "Function", + "tags": [], + "label": "getStepValue", + "description": [], + "signature": [ + "(colorStops: ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.ColorStop", + "text": "ColorStop" + }, + "[], newColorStops: ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.ColorStop", + "text": "ColorStop" + }, + "[], max: number) => number" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getStepValue.$1", + "type": "Array", + "tags": [], + "label": "colorStops", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.ColorStop", + "text": "ColorStop" + }, + "[]" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getStepValue.$2", + "type": "Array", + "tags": [], + "label": "newColorStops", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.ColorStop", + "text": "ColorStop" + }, + "[]" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.getStepValue.$3", + "type": "number", + "tags": [], + "label": "max", + "description": [], + "signature": [ + "number" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.remapStopsByNewInterval", + "type": "Function", + "tags": [], + "label": "remapStopsByNewInterval", + "description": [], + "signature": [ + "(controlStops: ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.ColorStop", + "text": "ColorStop" + }, + "[], {\n newInterval,\n oldInterval,\n newMin,\n oldMin,\n }: { newInterval: number; oldInterval: number; newMin: number; oldMin: number; }) => { color: string; stop: number; }[]" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.remapStopsByNewInterval.$1", + "type": "Array", + "tags": [], + "label": "controlStops", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.ColorStop", + "text": "ColorStop" + }, + "[]" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.remapStopsByNewInterval.$2", + "type": "Object", + "tags": [], + "label": "{\n newInterval,\n oldInterval,\n newMin,\n oldMin,\n }", + "description": [], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.remapStopsByNewInterval.$2.newInterval", + "type": "number", + "tags": [], + "label": "newInterval", + "description": [], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.remapStopsByNewInterval.$2.oldInterval", + "type": "number", + "tags": [], + "label": "oldInterval", + "description": [], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.remapStopsByNewInterval.$2.newMin", + "type": "number", + "tags": [], + "label": "newMin", + "description": [], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.remapStopsByNewInterval.$2.oldMin", + "type": "number", + "tags": [], + "label": "oldMin", + "description": [], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.reversePalette", + "type": "Function", + "tags": [], + "label": "reversePalette", + "description": [], + "signature": [ + "(paletteColorRepresentation: ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.ColorStop", + "text": "ColorStop" + }, + "[]) => { color: string; stop: number; }[]" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.reversePalette.$1", + "type": "Array", + "tags": [], + "label": "paletteColorRepresentation", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.ColorStop", + "text": "ColorStop" + }, + "[]" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.roundValue", + "type": "Function", + "tags": [], + "label": "roundValue", + "description": [], + "signature": [ + "(value: number, fractionDigits: number) => number" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.roundValue.$1", + "type": "number", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "number" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.roundValue.$2", + "type": "number", + "tags": [], + "label": "fractionDigits", + "description": [], + "signature": [ + "number" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.shiftPalette", + "type": "Function", + "tags": [], + "label": "shiftPalette", + "description": [], + "signature": [ + "(stops: ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.ColorStop", + "text": "ColorStop" + }, + "[], max: number) => { stop: number; color: string; }[]" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.shiftPalette.$1", + "type": "Array", + "tags": [], + "label": "stops", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.ColorStop", + "text": "ColorStop" + }, + "[]" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.shiftPalette.$2", + "type": "number", + "tags": [], + "label": "max", + "description": [], + "signature": [ + "number" + ], + "path": "packages/kbn-coloring/src/palettes/utils.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.ChartColorConfiguration", + "type": "Interface", + "tags": [], + "label": "ChartColorConfiguration", + "description": [ + "\nInformation about the structure of a chart to determine the color of a series within it." + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.ChartColorConfiguration.totalSeries", + "type": "number", + "tags": [], + "label": "totalSeries", + "description": [ + "\nOverall number of series in the current chart" + ], + "signature": [ + "number | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.ChartColorConfiguration.maxDepth", + "type": "number", + "tags": [], + "label": "maxDepth", + "description": [ + "\nMax nesting depth of the series tree" + ], + "signature": [ + "number | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.ChartColorConfiguration.behindText", + "type": "CompoundType", + "tags": [], + "label": "behindText", + "description": [ + "\nFlag whether the color will be used behind text. The palette can use this information to\nadjust colors for better a11y. Might be ignored depending on the palette." + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.ChartColorConfiguration.syncColors", + "type": "CompoundType", + "tags": [], + "label": "syncColors", + "description": [ + "\nFlag whether a color assignment to a given key should be remembered and re-used the next time the key shows up.\nThis setting might be ignored based on the palette." + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.ColorStop", + "type": "Interface", + "tags": [], + "label": "ColorStop", + "description": [], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.ColorStop.color", + "type": "string", + "tags": [], + "label": "color", + "description": [], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.ColorStop.stop", + "type": "number", + "tags": [], + "label": "stop", + "description": [], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CustomPaletteParams", + "type": "Interface", + "tags": [], + "label": "CustomPaletteParams", + "description": [], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CustomPaletteParams.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CustomPaletteParams.reverse", + "type": "CompoundType", + "tags": [], + "label": "reverse", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CustomPaletteParams.rangeType", + "type": "CompoundType", + "tags": [], + "label": "rangeType", + "description": [], + "signature": [ + "\"number\" | \"percent\" | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CustomPaletteParams.continuity", + "type": "CompoundType", + "tags": [], + "label": "continuity", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.PaletteContinuity", + "text": "PaletteContinuity" + }, + " | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CustomPaletteParams.progression", + "type": "string", + "tags": [], + "label": "progression", + "description": [], + "signature": [ + "\"fixed\" | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CustomPaletteParams.rangeMin", + "type": "number", + "tags": [], + "label": "rangeMin", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CustomPaletteParams.rangeMax", + "type": "number", + "tags": [], + "label": "rangeMax", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CustomPaletteParams.stops", + "type": "Array", + "tags": [], + "label": "stops", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.ColorStop", + "text": "ColorStop" + }, + "[] | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CustomPaletteParams.colorStops", + "type": "Array", + "tags": [], + "label": "colorStops", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.ColorStop", + "text": "ColorStop" + }, + "[] | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CustomPaletteParams.steps", + "type": "number", + "tags": [], + "label": "steps", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CustomPaletteParams.maxSteps", + "type": "number", + "tags": [], + "label": "maxSteps", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.DataBounds", + "type": "Interface", + "tags": [], + "label": "DataBounds", + "description": [], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.DataBounds.min", + "type": "number", + "tags": [], + "label": "min", + "description": [], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.DataBounds.max", + "type": "number", + "tags": [], + "label": "max", + "description": [], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.DataBounds.fallback", + "type": "CompoundType", + "tags": [], + "label": "fallback", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition", + "type": "Interface", + "tags": [], + "label": "PaletteDefinition", + "description": [ + "\nDefinition of a global palette.\n\nA palette controls the appearance of Lens charts on an editor level.\nThe palette wont get reset when switching charts.\n\nA palette can hold internal state (e.g. for customizations) and also includes\nan editor component to edit the internal state." + ], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.PaletteDefinition", + "text": "PaletteDefinition" + }, + "" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.id", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "\nUnique id of the palette (this will be persisted along with the visualization state)" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.title", + "type": "string", + "tags": [], + "label": "title", + "description": [ + "\nUser facing title (should be i18n-ized)" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.internal", + "type": "CompoundType", + "tags": [], + "label": "internal", + "description": [ + "\nFlag indicating whether users should be able to pick this palette manually." + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.toExpression", + "type": "Function", + "tags": [], + "label": "toExpression", + "description": [ + "\nSerialize the internal state of the palette into an expression function.\nThis function should be used to pass the palette to the expression function applying color and other styles" + ], + "signature": [ + "(state?: T | undefined) => ", + "Ast" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.toExpression.$1", + "type": "Uncategorized", + "tags": [], + "label": "state", + "description": [ + "The internal state of the palette" + ], + "signature": [ + "T | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.getCategoricalColor", + "type": "Function", + "tags": [], + "label": "getCategoricalColor", + "description": [ + "\nColor a series according to the internal rules of the palette." + ], + "signature": [ + "(series: ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.SeriesLayer", + "text": "SeriesLayer" + }, + "[], chartConfiguration?: ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.ChartColorConfiguration", + "text": "ChartColorConfiguration" + }, + " | undefined, state?: T | undefined) => string | null" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.getCategoricalColor.$1", + "type": "Array", + "tags": [], + "label": "series", + "description": [ + "The current series along with its ancestors." + ], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.SeriesLayer", + "text": "SeriesLayer" + }, + "[]" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.getCategoricalColor.$2", + "type": "Object", + "tags": [], + "label": "chartConfiguration", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.ChartColorConfiguration", + "text": "ChartColorConfiguration" + }, + " | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "isRequired": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.getCategoricalColor.$3", + "type": "Uncategorized", + "tags": [], + "label": "state", + "description": [ + "The internal state of the palette" + ], + "signature": [ + "T | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.getCategoricalColors", + "type": "Function", + "tags": [], + "label": "getCategoricalColors", + "description": [ + "\nGet a spectrum of colors of the current palette.\nThis can be used if the chart wants to control color assignment locally." + ], + "signature": [ + "(size: number, state?: T | undefined) => string[]" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.getCategoricalColors.$1", + "type": "number", + "tags": [], + "label": "size", + "description": [], + "signature": [ + "number" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.getCategoricalColors.$2", + "type": "Uncategorized", + "tags": [], + "label": "state", + "description": [], + "signature": [ + "T | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.canDynamicColoring", + "type": "CompoundType", + "tags": [], + "label": "canDynamicColoring", + "description": [ + "\nDefine whether a palette supports dynamic coloring (i.e. gradient colors mapped to number values)" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.getColorForValue", + "type": "Function", + "tags": [], + "label": "getColorForValue", + "description": [ + "\nGet the assigned color for the given value based on its data domain and state settings.\nThis can be used for dynamic coloring based on uniform color distribution or custom stops." + ], + "signature": [ + "((value: number | undefined, state: T, { min, max }: { min: number; max: number; }) => string | undefined) | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.getColorForValue.$1", + "type": "number", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "isRequired": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.getColorForValue.$2", + "type": "Uncategorized", + "tags": [], + "label": "state", + "description": [], + "signature": [ + "T" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.getColorForValue.$3", + "type": "Object", + "tags": [], + "label": "{ min, max }", + "description": [], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.getColorForValue.$3.min", + "type": "number", + "tags": [], + "label": "min", + "description": [], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteDefinition.getColorForValue.$3.max", + "type": "number", + "tags": [], + "label": "max", + "description": [], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + } + ] + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteOutput", + "type": "Interface", + "tags": [], + "label": "PaletteOutput", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.PaletteOutput", + "text": "PaletteOutput" + }, + "" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteOutput.type", + "type": "CompoundType", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"system_palette\" | \"palette\"" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteOutput.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteOutput.params", + "type": "Uncategorized", + "tags": [], + "label": "params", + "description": [], + "signature": [ + "T | undefined" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteRegistry", + "type": "Interface", + "tags": [], + "label": "PaletteRegistry", + "description": [], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteRegistry.get", + "type": "Function", + "tags": [], + "label": "get", + "description": [], + "signature": [ + "(name: string) => ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.PaletteDefinition", + "text": "PaletteDefinition" + }, + "" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteRegistry.get.$1", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteRegistry.getAll", + "type": "Function", + "tags": [], + "label": "getAll", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.PaletteDefinition", + "text": "PaletteDefinition" + }, + "[]" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.SeriesLayer", + "type": "Interface", + "tags": [], + "label": "SeriesLayer", + "description": [ + "\nInformation about a series in a chart used to determine its color.\nSeries layers can be nested, this means each series layer can have an ancestor." + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.SeriesLayer.name", + "type": "string", + "tags": [], + "label": "name", + "description": [ + "\nName of the series (can be used for lookup-based coloring)" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.SeriesLayer.rankAtDepth", + "type": "number", + "tags": [], + "label": "rankAtDepth", + "description": [ + "\nRank of the series compared to siblings with the same ancestor" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.SeriesLayer.totalSeriesAtDepth", + "type": "number", + "tags": [], + "label": "totalSeriesAtDepth", + "description": [ + "\nTotal number of series with the same ancestor" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.CUSTOM_PALETTE", + "type": "string", + "tags": [], + "label": "CUSTOM_PALETTE", + "description": [], + "signature": [ + "\"custom\"" + ], + "path": "packages/kbn-coloring/src/palettes/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.DEFAULT_COLOR_STEPS", + "type": "number", + "tags": [], + "label": "DEFAULT_COLOR_STEPS", + "description": [], + "signature": [ + "5" + ], + "path": "packages/kbn-coloring/src/palettes/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.DEFAULT_CONTINUITY", + "type": "string", + "tags": [], + "label": "DEFAULT_CONTINUITY", + "description": [], + "signature": [ + "\"above\"" + ], + "path": "packages/kbn-coloring/src/palettes/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.DEFAULT_MAX_STOP", + "type": "number", + "tags": [], + "label": "DEFAULT_MAX_STOP", + "description": [], + "signature": [ + "100" + ], + "path": "packages/kbn-coloring/src/palettes/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.DEFAULT_MIN_STOP", + "type": "number", + "tags": [], + "label": "DEFAULT_MIN_STOP", + "description": [], + "signature": [ + "0" + ], + "path": "packages/kbn-coloring/src/palettes/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.DEFAULT_PALETTE_NAME", + "type": "string", + "tags": [], + "label": "DEFAULT_PALETTE_NAME", + "description": [], + "signature": [ + "\"positive\"" + ], + "path": "packages/kbn-coloring/src/palettes/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.DEFAULT_RANGE_TYPE", + "type": "string", + "tags": [], + "label": "DEFAULT_RANGE_TYPE", + "description": [], + "signature": [ + "\"percent\"" + ], + "path": "packages/kbn-coloring/src/palettes/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.FIXED_PROGRESSION", + "type": "string", + "tags": [], + "label": "FIXED_PROGRESSION", + "description": [], + "signature": [ + "\"fixed\"" + ], + "path": "packages/kbn-coloring/src/palettes/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.PaletteContinuity", + "type": "Type", + "tags": [], + "label": "PaletteContinuity", + "description": [], + "signature": [ + "\"above\" | \"below\" | \"none\" | \"all\"" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/coloring", + "id": "def-common.RequiredPaletteParamTypes", + "type": "Type", + "tags": [], + "label": "RequiredPaletteParamTypes", + "description": [], + "signature": [ + "{ name: string; reverse: boolean; rangeMin: number; rangeType: \"number\" | \"percent\"; continuity: ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.PaletteContinuity", + "text": "PaletteContinuity" + }, + "; progression: \"fixed\"; rangeMax: number; stops: ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.ColorStop", + "text": "ColorStop" + }, + "[]; colorStops: ", + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.ColorStop", + "text": "ColorStop" + }, + "[]; steps: number; maxSteps?: number | undefined; }" + ], + "path": "packages/kbn-coloring/src/palettes/types.ts", + "deprecated": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx new file mode 100644 index 0000000000000..6138cd2a95837 --- /dev/null +++ b/api_docs/kbn_coloring.mdx @@ -0,0 +1,33 @@ +--- +id: kibKbnColoringPluginApi +slug: /kibana-dev-docs/api/kbn-coloring +title: "@kbn/coloring" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/coloring plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] +warning: 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. +--- +import kbnColoringObj from './kbn_coloring.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 106 | 0 | 80 | 1 | + +## Common + +### Functions + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_config.devdocs.json b/api_docs/kbn_config.devdocs.json index 383df777d15f6..f87e73f517cbe 100644 --- a/api_docs/kbn_config.devdocs.json +++ b/api_docs/kbn_config.devdocs.json @@ -55,37 +55,6 @@ } ], "functions": [ - { - "parentPluginId": "@kbn/config", - "id": "def-server.getPluginSearchPaths", - "type": "Function", - "tags": [], - "label": "getPluginSearchPaths", - "description": [], - "signature": [ - "({ rootDir, oss, examples }: SearchOptions) => string[]" - ], - "path": "packages/kbn-config/src/plugins/plugin_search_paths.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/config", - "id": "def-server.getPluginSearchPaths.$1", - "type": "Object", - "tags": [], - "label": "{ rootDir, oss, examples }", - "description": [], - "signature": [ - "SearchOptions" - ], - "path": "packages/kbn-config/src/plugins/plugin_search_paths.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/config", "id": "def-server.hasConfigPathIntersection", diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index ae52de0962bce..1ce3218bfedb3 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/config plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 66 | 0 | 46 | 2 | +| 64 | 0 | 44 | 2 | ## Server diff --git a/api_docs/kbn_config_schema.devdocs.json b/api_docs/kbn_config_schema.devdocs.json index b2c350d51f4ce..b765279edb5d2 100644 --- a/api_docs/kbn_config_schema.devdocs.json +++ b/api_docs/kbn_config_schema.devdocs.json @@ -1031,6 +1031,29 @@ ], "returnComment": [] }, + { + "parentPluginId": "@kbn/config-schema", + "id": "def-server.Type.getSchemaStructure", + "type": "Function", + "tags": [], + "label": "getSchemaStructure", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "@kbn/config-schema", + "scope": "server", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-server.SchemaStructureEntry", + "text": "SchemaStructureEntry" + }, + "[]" + ], + "path": "packages/kbn-config-schema/src/types/type.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "@kbn/config-schema", "id": "def-server.Type.handleError", @@ -1207,7 +1230,44 @@ "initialIsOpen": false } ], - "interfaces": [], + "interfaces": [ + { + "parentPluginId": "@kbn/config-schema", + "id": "def-server.SchemaStructureEntry", + "type": "Interface", + "tags": [], + "label": "SchemaStructureEntry", + "description": [], + "path": "packages/kbn-config-schema/src/types/type.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/config-schema", + "id": "def-server.SchemaStructureEntry.path", + "type": "Array", + "tags": [], + "label": "path", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/kbn-config-schema/src/types/type.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/config-schema", + "id": "def-server.SchemaStructureEntry.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-config-schema/src/types/type.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ], "enums": [], "misc": [ { diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index 2eebafa2279fe..784035631256b 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/config-schema plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 125 | 3 | 123 | 17 | +| 129 | 3 | 127 | 17 | ## Server @@ -31,6 +31,9 @@ Contact [Owner missing] for questions regarding this plugin. ### Classes +### Interfaces + + ### Consts, variables and types diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index de871c1b01bbd..f044e9213163c 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/crypto plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] warning: 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. --- diff --git a/api_docs/kbn_datemath.devdocs.json b/api_docs/kbn_datemath.devdocs.json new file mode 100644 index 0000000000000..4905365143524 --- /dev/null +++ b/api_docs/kbn_datemath.devdocs.json @@ -0,0 +1,578 @@ +{ + "id": "@kbn/datemath", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.parse", + "type": "Function", + "tags": [], + "label": "parse", + "description": [], + "signature": [ + "(input: string, options: { roundUp?: boolean | undefined; momentInstance?: typeof moment | undefined; forceNow?: Date | undefined; }) => moment.Moment | undefined" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.parse.$1", + "type": "string", + "tags": [], + "label": "input", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.parse.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.parse.$2.roundUp", + "type": "CompoundType", + "tags": [], + "label": "roundUp", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.parse.$2.momentInstance", + "type": "Function", + "tags": [], + "label": "momentInstance", + "description": [], + "signature": [ + "typeof moment | undefined" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.parse.$2.forceNow", + "type": "Object", + "tags": [], + "label": "forceNow", + "description": [], + "signature": [ + "Date | undefined" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.Unit", + "type": "Type", + "tags": [], + "label": "Unit", + "description": [], + "signature": [ + "\"y\" | \"M\" | \"w\" | \"d\" | \"h\" | \"m\" | \"s\" | \"ms\"" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.units", + "type": "Array", + "tags": [], + "label": "units", + "description": [], + "signature": [ + { + "pluginId": "@kbn/datemath", + "scope": "server", + "docId": "kibKbnDatemathPluginApi", + "section": "def-server.Unit", + "text": "Unit" + }, + "[]" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsAsc", + "type": "Array", + "tags": [], + "label": "unitsAsc", + "description": [], + "signature": [ + { + "pluginId": "@kbn/datemath", + "scope": "server", + "docId": "kibKbnDatemathPluginApi", + "section": "def-server.Unit", + "text": "Unit" + }, + "[]" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsDesc", + "type": "Array", + "tags": [], + "label": "unitsDesc", + "description": [], + "signature": [ + { + "pluginId": "@kbn/datemath", + "scope": "server", + "docId": "kibKbnDatemathPluginApi", + "section": "def-server.Unit", + "text": "Unit" + }, + "[]" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.UnitsMap", + "type": "Type", + "tags": [], + "label": "UnitsMap", + "description": [], + "signature": [ + "{ y: { weight: number; type: \"calendar\" | \"fixed\" | \"mixed\"; base: number; }; M: { weight: number; type: \"calendar\" | \"fixed\" | \"mixed\"; base: number; }; w: { weight: number; type: \"calendar\" | \"fixed\" | \"mixed\"; base: number; }; d: { weight: number; type: \"calendar\" | \"fixed\" | \"mixed\"; base: number; }; h: { weight: number; type: \"calendar\" | \"fixed\" | \"mixed\"; base: number; }; m: { weight: number; type: \"calendar\" | \"fixed\" | \"mixed\"; base: number; }; s: { weight: number; type: \"calendar\" | \"fixed\" | \"mixed\"; base: number; }; ms: { weight: number; type: \"calendar\" | \"fixed\" | \"mixed\"; base: number; }; }" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "initialIsOpen": false + } + ], + "objects": [ + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap", + "type": "Object", + "tags": [], + "label": "unitsMap", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.ms", + "type": "Object", + "tags": [], + "label": "ms", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.ms.weight", + "type": "number", + "tags": [], + "label": "weight", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.ms.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"fixed\"" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.ms.base", + "type": "number", + "tags": [], + "label": "base", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.s", + "type": "Object", + "tags": [], + "label": "s", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.s.weight", + "type": "number", + "tags": [], + "label": "weight", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.s.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"fixed\"" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.s.base", + "type": "number", + "tags": [], + "label": "base", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.m", + "type": "Object", + "tags": [], + "label": "m", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.m.weight", + "type": "number", + "tags": [], + "label": "weight", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.m.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"mixed\"" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.m.base", + "type": "number", + "tags": [], + "label": "base", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.h", + "type": "Object", + "tags": [], + "label": "h", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.h.weight", + "type": "number", + "tags": [], + "label": "weight", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.h.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"mixed\"" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.h.base", + "type": "number", + "tags": [], + "label": "base", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.d", + "type": "Object", + "tags": [], + "label": "d", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.d.weight", + "type": "number", + "tags": [], + "label": "weight", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.d.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"mixed\"" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.d.base", + "type": "number", + "tags": [], + "label": "base", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.w", + "type": "Object", + "tags": [], + "label": "w", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.w.weight", + "type": "number", + "tags": [], + "label": "weight", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.w.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"calendar\"" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.w.base", + "type": "number", + "tags": [], + "label": "base", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.M", + "type": "Object", + "tags": [], + "label": "M", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.M.weight", + "type": "number", + "tags": [], + "label": "weight", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.M.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"calendar\"" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.M.base", + "type": "number", + "tags": [], + "label": "base", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.y", + "type": "Object", + "tags": [], + "label": "y", + "description": [ + "// q: { weight: 8, type: 'calendar' }, // TODO: moment duration does not support quarter" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.y.weight", + "type": "number", + "tags": [], + "label": "weight", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.y.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"calendar\"" + ], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/datemath", + "id": "def-server.unitsMap.y.base", + "type": "number", + "tags": [], + "label": "base", + "description": [], + "path": "packages/kbn-datemath/src/index.ts", + "deprecated": false + } + ] + } + ], + "initialIsOpen": false + } + ] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx new file mode 100644 index 0000000000000..782b942ba952b --- /dev/null +++ b/api_docs/kbn_datemath.mdx @@ -0,0 +1,33 @@ +--- +id: kibKbnDatemathPluginApi +slug: /kibana-dev-docs/api/kbn-datemath +title: "@kbn/datemath" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/datemath plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] +warning: 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. +--- +import kbnDatemathObj from './kbn_datemath.devdocs.json'; + +elasticsearch datemath parser, used in kibana + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 44 | 0 | 43 | 0 | + +## Server + +### Objects + + +### Functions + + +### Consts, variables and types + + diff --git a/api_docs/kbn_dev_utils.devdocs.json b/api_docs/kbn_dev_utils.devdocs.json index c45d0515c0bb4..7ffb74a9f9220 100644 --- a/api_docs/kbn_dev_utils.devdocs.json +++ b/api_docs/kbn_dev_utils.devdocs.json @@ -12,61 +12,42 @@ "classes": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsClient", + "id": "def-server.ProcRunner", "type": "Class", - "tags": [], - "label": "CiStatsClient", - "description": [], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_client.ts", + "tags": [ + "class" + ], + "label": "ProcRunner", + "description": [ + "\n Helper for starting and managing processes. In many ways it resembles the\n API from `grunt_run`, processes are named and can be started, waited for,\n backgrounded once they log something matching a RegExp...\n" + ], + "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsClient.fromEnv", + "id": "def-server.ProcRunner.Unnamed", "type": "Function", "tags": [], - "label": "fromEnv", - "description": [ - "\nCreate a CiStatsReporter by inspecting the ENV for the necessary config" - ], + "label": "Constructor", + "description": [], "signature": [ - "(log: ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLog", - "text": "ToolingLog" - }, - ") => ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.CiStatsClient", - "text": "CiStatsClient" - } + "any" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_client.ts", + "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsClient.fromEnv.$1", + "id": "def-server.ProcRunner.Unnamed.$1", "type": "Object", "tags": [], "label": "log", "description": [], "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLog", - "text": "ToolingLog" - } + "ToolingLog" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_client.ts", + "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", "deprecated": false, "isRequired": true } @@ -75,157 +56,181 @@ }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsClient.Unnamed", + "id": "def-server.ProcRunner.run", "type": "Function", - "tags": [], - "label": "Constructor", - "description": [], + "tags": [ + "property", + "property", + "property", + "property", + "return" + ], + "label": "run", + "description": [ + "\n Start a process, tracking it by `name`" + ], "signature": [ - "any" + "(name: string, options: RunOptions) => Promise" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_client.ts", + "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsClient.Unnamed.$1", + "id": "def-server.ProcRunner.run.$1", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/dev-utils", + "id": "def-server.ProcRunner.run.$2", "type": "Object", "tags": [], - "label": "config", + "label": "options", "description": [], "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Config", - "text": "Config" - }, - " | undefined" + "RunOptions" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_client.ts", + "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], "returnComment": [] }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsClient.isEnabled", + "id": "def-server.ProcRunner.stop", "type": "Function", "tags": [], - "label": "isEnabled", - "description": [], - "signature": [ - "() => boolean" + "label": "stop", + "description": [ + "\n Stop a named proc" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_client.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsClient.getLatestTestGroupStats", - "type": "Function", - "tags": [], - "label": "getLatestTestGroupStats", - "description": [], "signature": [ - "(options: LatestTestGroupStatsOptions) => Promise" + "(name: string, signal?: NodeJS.Signals) => Promise" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_client.ts", + "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsClient.getLatestTestGroupStats.$1", - "type": "Object", + "id": "def-server.ProcRunner.stop.$1", + "type": "string", "tags": [], - "label": "options", + "label": "name", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/dev-utils", + "id": "def-server.ProcRunner.stop.$2", + "type": "CompoundType", + "tags": [], + "label": "signal", "description": [], "signature": [ - "LatestTestGroupStatsOptions" + "NodeJS.Signals" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_client.ts", + "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", "deprecated": false, "isRequired": true } ], "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReporter", - "type": "Class", - "tags": [], - "label": "CiStatsReporter", - "description": [ - "Object that helps report data to the ci-stats service" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false, - "children": [ + }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReporter.fromEnv", + "id": "def-server.ProcRunner.waitForAllToStop", "type": "Function", - "tags": [], - "label": "fromEnv", + "tags": [ + "return" + ], + "label": "waitForAllToStop", "description": [ - "\nCreate a CiStatsReporter by inspecting the ENV for the necessary config" + "\n Wait for all running processes to stop naturally" ], "signature": [ - "(log: ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLog", - "text": "ToolingLog" - }, - ") => ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.CiStatsReporter", - "text": "CiStatsReporter" - } + "() => Promise" + ], + "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/dev-utils", + "id": "def-server.ProcRunner.teardown", + "type": "Function", + "tags": [ + "return" + ], + "label": "teardown", + "description": [ + "\n Close the ProcRunner and stop all running\n processes with `signal`\n" + ], + "signature": [ + "(signal?: \"exit\" | NodeJS.Signals) => Promise" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", + "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReporter.fromEnv.$1", - "type": "Object", + "id": "def-server.ProcRunner.teardown.$1", + "type": "CompoundType", "tags": [], - "label": "log", + "label": "signal", "description": [], "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLog", - "text": "ToolingLog" - } + "\"exit\" | NodeJS.Signals" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", + "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", "deprecated": false, "isRequired": true } ], "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/dev-utils", + "id": "def-server.RunWithCommands", + "type": "Class", + "tags": [], + "label": "RunWithCommands", + "description": [], + "signature": [ + { + "pluginId": "@kbn/dev-utils", + "scope": "server", + "docId": "kibKbnDevUtilsPluginApi", + "section": "def-server.RunWithCommands", + "text": "RunWithCommands" }, + "" + ], + "path": "packages/kbn-dev-utils/src/run/run_with_commands.ts", + "deprecated": false, + "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReporter.Unnamed", + "id": "def-server.RunWithCommands.Unnamed", "type": "Function", "tags": [], "label": "Constructor", @@ -233,47 +238,48 @@ "signature": [ "any" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", + "path": "packages/kbn-dev-utils/src/run/run_with_commands.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReporter.Unnamed.$1", + "id": "def-server.RunWithCommands.Unnamed.$1", "type": "Object", "tags": [], - "label": "config", + "label": "options", "description": [], "signature": [ { "pluginId": "@kbn/dev-utils", "scope": "server", "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Config", - "text": "Config" + "section": "def-server.RunWithCommandsOptions", + "text": "RunWithCommandsOptions" }, - " | undefined" + "" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", + "path": "packages/kbn-dev-utils/src/run/run_with_commands.ts", "deprecated": false, - "isRequired": false + "isRequired": true }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReporter.Unnamed.$2", - "type": "Object", + "id": "def-server.RunWithCommands.Unnamed.$2", + "type": "Array", "tags": [], - "label": "log", + "label": "commands", "description": [], "signature": [ { "pluginId": "@kbn/dev-utils", "scope": "server", "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLog", - "text": "ToolingLog" - } + "section": "def-server.Command", + "text": "Command" + }, + "[]" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", + "path": "packages/kbn-dev-utils/src/run/run_with_commands.ts", "deprecated": false, "isRequired": true } @@ -282,139 +288,36 @@ }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReporter.isEnabled", - "type": "Function", - "tags": [], - "label": "isEnabled", - "description": [ - "\nDetermine if CI_STATS is explicitly disabled by the environment. To determine\nif the CiStatsReporter has enough information in the environment to send metrics\nfor builds use #hasBuildConfig()." - ], - "signature": [ - "() => boolean" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReporter.hasBuildConfig", - "type": "Function", - "tags": [], - "label": "hasBuildConfig", - "description": [ - "\nDetermines if the CiStatsReporter is disabled by the environment, or properly\nconfigured and able to send stats" - ], - "signature": [ - "() => boolean" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReporter.timings", + "id": "def-server.RunWithCommands.command", "type": "Function", "tags": [], - "label": "timings", - "description": [ - "\nReport timings data to the ci-stats service. If running in CI then the reporter\nwill include the buildId in the report with the access token, otherwise the timings\ndata will be recorded as anonymous timing data." - ], + "label": "command", + "description": [], "signature": [ "(options: ", { "pluginId": "@kbn/dev-utils", "scope": "server", "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.TimingsOptions", - "text": "TimingsOptions" + "section": "def-server.Command", + "text": "Command" }, - ") => Promise" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReporter.timings.$1", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.TimingsOptions", - "text": "TimingsOptions" - } - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReporter.metrics", - "type": "Function", - "tags": [], - "label": "metrics", - "description": [ - "\nReport metrics data to the ci-stats service. If running outside of CI this method\ndoes nothing as metrics can only be reported when associated with a specific CI build." - ], - "signature": [ - "(metrics: ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.CiStatsMetric", - "text": "CiStatsMetric" - }, - "[], options?: ", + ") => ", { "pluginId": "@kbn/dev-utils", "scope": "server", "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.MetricsOptions", - "text": "MetricsOptions" + "section": "def-server.RunWithCommands", + "text": "RunWithCommands" }, - " | undefined) => Promise" + "" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", + "path": "packages/kbn-dev-utils/src/run/run_with_commands.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReporter.metrics.$1", - "type": "Array", - "tags": [], - "label": "metrics", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.CiStatsMetric", - "text": "CiStatsMetric" - }, - "[]" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReporter.metrics.$2", + "id": "def-server.RunWithCommands.command.$1", "type": "Object", "tags": [], "label": "options", @@ -424,1968 +327,138 @@ "pluginId": "@kbn/dev-utils", "scope": "server", "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.MetricsOptions", - "text": "MetricsOptions" + "section": "def-server.Command", + "text": "Command" }, - " | undefined" + "" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", + "path": "packages/kbn-dev-utils/src/run/run_with_commands.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], "returnComment": [] }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReporter.reportTests", + "id": "def-server.RunWithCommands.execute", "type": "Function", "tags": [], - "label": "reportTests", - "description": [ - "\nSend test reports to ci-stats" - ], + "label": "execute", + "description": [], "signature": [ - "({ group, testRuns }: ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.CiStatsReportTestsOptions", - "text": "CiStatsReportTestsOptions" - }, - ") => Promise" + "() => Promise" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", + "path": "packages/kbn-dev-utils/src/run/run_with_commands.ts", "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReporter.reportTests.$1", - "type": "Object", - "tags": [], - "label": "{ group, testRuns }", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.CiStatsReportTestsOptions", - "text": "CiStatsReportTestsOptions" - } - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false, - "isRequired": true - } - ], + "children": [], "returnComment": [] } ], "initialIsOpen": false - }, + } + ], + "functions": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ProcRunner", - "type": "Class", - "tags": [ - "class" - ], - "label": "ProcRunner", - "description": [ - "\n Helper for starting and managing processes. In many ways it resembles the\n API from `grunt_run`, processes are named and can be started, waited for,\n backgrounded once they log something matching a RegExp...\n" + "id": "def-server.combineErrors", + "type": "Function", + "tags": [], + "label": "combineErrors", + "description": [], + "signature": [ + "(errors: (Error | FailError)[]) => Error" ], - "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", + "path": "packages/kbn-dev-utils/src/run/fail.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ProcRunner.Unnamed", - "type": "Function", + "id": "def-server.combineErrors.$1", + "type": "Array", "tags": [], - "label": "Constructor", + "label": "errors", "description": [], "signature": [ - "any" + "(Error | FailError)[]" ], - "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", + "path": "packages/kbn-dev-utils/src/run/fail.ts", "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ProcRunner.Unnamed.$1", - "type": "Object", - "tags": [], - "label": "log", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLog", - "text": "ToolingLog" - } - ], - "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/dev-utils", + "id": "def-server.createFailError", + "type": "Function", + "tags": [], + "label": "createFailError", + "description": [], + "signature": [ + "(reason: string, options: FailErrorOptions) => FailError" + ], + "path": "packages/kbn-dev-utils/src/run/fail.ts", + "deprecated": false, + "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ProcRunner.run", - "type": "Function", - "tags": [ - "property", - "property", - "property", - "property", - "return" - ], - "label": "run", - "description": [ - "\n Start a process, tracking it by `name`" - ], + "id": "def-server.createFailError.$1", + "type": "string", + "tags": [], + "label": "reason", + "description": [], "signature": [ - "(name: string, options: RunOptions) => Promise" + "string" ], - "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", + "path": "packages/kbn-dev-utils/src/run/fail.ts", "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ProcRunner.run.$1", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "signature": [ - "string" - ], - "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ProcRunner.run.$2", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "signature": [ - "RunOptions" - ], - "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] + "isRequired": true }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ProcRunner.stop", - "type": "Function", + "id": "def-server.createFailError.$2", + "type": "Object", "tags": [], - "label": "stop", - "description": [ - "\n Stop a named proc" + "label": "options", + "description": [], + "signature": [ + "FailErrorOptions" ], + "path": "packages/kbn-dev-utils/src/run/fail.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/dev-utils", + "id": "def-server.createFlagError", + "type": "Function", + "tags": [], + "label": "createFlagError", + "description": [], + "signature": [ + "(reason: string) => FailError" + ], + "path": "packages/kbn-dev-utils/src/run/fail.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/dev-utils", + "id": "def-server.createFlagError.$1", + "type": "string", + "tags": [], + "label": "reason", + "description": [], "signature": [ - "(name: string, signal?: NodeJS.Signals) => Promise" + "string" ], - "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ProcRunner.stop.$1", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "signature": [ - "string" - ], - "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ProcRunner.stop.$2", - "type": "CompoundType", - "tags": [], - "label": "signal", - "description": [], - "signature": [ - "NodeJS.Signals" - ], - "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ProcRunner.waitForAllToStop", - "type": "Function", - "tags": [ - "return" - ], - "label": "waitForAllToStop", - "description": [ - "\n Wait for all running processes to stop naturally" - ], - "signature": [ - "() => Promise" - ], - "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ProcRunner.teardown", - "type": "Function", - "tags": [ - "return" - ], - "label": "teardown", - "description": [ - "\n Close the ProcRunner and stop all running\n processes with `signal`\n" - ], - "signature": [ - "(signal?: \"exit\" | NodeJS.Signals) => Promise" - ], - "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ProcRunner.teardown.$1", - "type": "CompoundType", - "tags": [], - "label": "signal", - "description": [], - "signature": [ - "\"exit\" | NodeJS.Signals" - ], - "path": "packages/kbn-dev-utils/src/proc_runner/proc_runner.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.RunWithCommands", - "type": "Class", - "tags": [], - "label": "RunWithCommands", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.RunWithCommands", - "text": "RunWithCommands" - }, - "" - ], - "path": "packages/kbn-dev-utils/src/run/run_with_commands.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.RunWithCommands.Unnamed", - "type": "Function", - "tags": [], - "label": "Constructor", - "description": [], - "signature": [ - "any" - ], - "path": "packages/kbn-dev-utils/src/run/run_with_commands.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.RunWithCommands.Unnamed.$1", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.RunWithCommandsOptions", - "text": "RunWithCommandsOptions" - }, - "" - ], - "path": "packages/kbn-dev-utils/src/run/run_with_commands.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.RunWithCommands.Unnamed.$2", - "type": "Array", - "tags": [], - "label": "commands", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Command", - "text": "Command" - }, - "[]" - ], - "path": "packages/kbn-dev-utils/src/run/run_with_commands.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.RunWithCommands.command", - "type": "Function", - "tags": [], - "label": "command", - "description": [], - "signature": [ - "(options: ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Command", - "text": "Command" - }, - ") => ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.RunWithCommands", - "text": "RunWithCommands" - }, - "" - ], - "path": "packages/kbn-dev-utils/src/run/run_with_commands.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.RunWithCommands.command.$1", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Command", - "text": "Command" - }, - "" - ], - "path": "packages/kbn-dev-utils/src/run/run_with_commands.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.RunWithCommands.execute", - "type": "Function", - "tags": [], - "label": "execute", - "description": [], - "signature": [ - "() => Promise" - ], - "path": "packages/kbn-dev-utils/src/run/run_with_commands.ts", - "deprecated": false, - "children": [], - "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog", - "type": "Class", - "tags": [], - "label": "ToolingLog", - "description": [], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.Unnamed", - "type": "Function", - "tags": [], - "label": "Constructor", - "description": [], - "signature": [ - "any" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.Unnamed.$1", - "type": "Object", - "tags": [], - "label": "writerConfig", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLogTextWriterConfig", - "text": "ToolingLogTextWriterConfig" - }, - " | undefined" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.Unnamed.$2", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLogOptions", - "text": "ToolingLogOptions" - }, - " | undefined" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.getIndent", - "type": "Function", - "tags": [], - "label": "getIndent", - "description": [ - "\nGet the current indentation level of the ToolingLog" - ], - "signature": [ - "() => number" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.indent", - "type": "Function", - "tags": [], - "label": "indent", - "description": [], - "signature": [ - "{ (delta: number): void; (delta: number, block: () => Promise): Promise; (delta: number, block: () => T): T; }" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.indent.$1", - "type": "number", - "tags": [], - "label": "delta", - "description": [], - "signature": [ - "number" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.indent.$2", - "type": "Function", - "tags": [], - "label": "block", - "description": [], - "signature": [ - "(() => T | Promise) | undefined" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.verbose", - "type": "Function", - "tags": [], - "label": "verbose", - "description": [], - "signature": [ - "(...args: any[]) => void" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.verbose.$1", - "type": "Array", - "tags": [], - "label": "args", - "description": [], - "signature": [ - "any[]" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.debug", - "type": "Function", - "tags": [], - "label": "debug", - "description": [], - "signature": [ - "(...args: any[]) => void" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.debug.$1", - "type": "Array", - "tags": [], - "label": "args", - "description": [], - "signature": [ - "any[]" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.info", - "type": "Function", - "tags": [], - "label": "info", - "description": [], - "signature": [ - "(...args: any[]) => void" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.info.$1", - "type": "Array", - "tags": [], - "label": "args", - "description": [], - "signature": [ - "any[]" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.success", - "type": "Function", - "tags": [], - "label": "success", - "description": [], - "signature": [ - "(...args: any[]) => void" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.success.$1", - "type": "Array", - "tags": [], - "label": "args", - "description": [], - "signature": [ - "any[]" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.warning", - "type": "Function", - "tags": [], - "label": "warning", - "description": [], - "signature": [ - "(...args: any[]) => void" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.warning.$1", - "type": "Array", - "tags": [], - "label": "args", - "description": [], - "signature": [ - "any[]" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.error", - "type": "Function", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "(error: string | Error) => void" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.error.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "string | Error" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.write", - "type": "Function", - "tags": [], - "label": "write", - "description": [], - "signature": [ - "(...args: any[]) => void" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.write.$1", - "type": "Array", - "tags": [], - "label": "args", - "description": [], - "signature": [ - "any[]" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.getWriters", - "type": "Function", - "tags": [], - "label": "getWriters", - "description": [], - "signature": [ - "() => ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Writer", - "text": "Writer" - }, - "[]" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.setWriters", - "type": "Function", - "tags": [], - "label": "setWriters", - "description": [], - "signature": [ - "(writers: ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Writer", - "text": "Writer" - }, - "[]) => void" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.setWriters.$1", - "type": "Array", - "tags": [], - "label": "writers", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Writer", - "text": "Writer" - }, - "[]" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.getWritten$", - "type": "Function", - "tags": [], - "label": "getWritten$", - "description": [], - "signature": [ - "() => ", - "Observable", - "<", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Message", - "text": "Message" - }, - ">" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.withType", - "type": "Function", - "tags": [], - "label": "withType", - "description": [ - "\nCreate a new ToolingLog which sets a different \"type\", allowing messages to be filtered out by \"source\"" - ], - "signature": [ - "(type: string) => ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLog", - "text": "ToolingLog" - } - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLog.withType.$1", - "type": "string", - "tags": [], - "label": "type", - "description": [ - "A string that will be passed along with messages from this logger which can be used to filter messages with `ignoreSources`" - ], - "signature": [ - "string" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogCollectingWriter", - "type": "Class", - "tags": [], - "label": "ToolingLogCollectingWriter", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLogCollectingWriter", - "text": "ToolingLogCollectingWriter" - }, - " extends ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLogTextWriter", - "text": "ToolingLogTextWriter" - } - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_collecting_writer.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogCollectingWriter.messages", - "type": "Array", - "tags": [], - "label": "messages", - "description": [], - "signature": [ - "string[]" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_collecting_writer.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogCollectingWriter.Unnamed", - "type": "Function", - "tags": [], - "label": "Constructor", - "description": [], - "signature": [ - "any" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_collecting_writer.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogCollectingWriter.Unnamed.$1", - "type": "CompoundType", - "tags": [], - "label": "level", - "description": [], - "signature": [ - "\"error\" | \"info\" | \"success\" | \"warning\" | \"debug\" | \"silent\" | \"verbose\"" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_collecting_writer.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogCollectingWriter.write", - "type": "Function", - "tags": [], - "label": "write", - "description": [ - "\nCalled by ToolingLog, extends messages with the source if message includes one." - ], - "signature": [ - "(msg: ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Message", - "text": "Message" - }, - ") => boolean" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_collecting_writer.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogCollectingWriter.write.$1", - "type": "Object", - "tags": [], - "label": "msg", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Message", - "text": "Message" - } - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_collecting_writer.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogTextWriter", - "type": "Class", - "tags": [], - "label": "ToolingLogTextWriter", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLogTextWriter", - "text": "ToolingLogTextWriter" - }, - " implements ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Writer", - "text": "Writer" - } - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogTextWriter.level", - "type": "Object", - "tags": [], - "label": "level", - "description": [], - "signature": [ - "{ name: \"error\" | \"info\" | \"success\" | \"warning\" | \"debug\" | \"silent\" | \"verbose\"; flags: { error: boolean; info: boolean; success: boolean; warning: boolean; debug: boolean; silent: boolean; verbose: boolean; }; }" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogTextWriter.writeTo", - "type": "Object", - "tags": [], - "label": "writeTo", - "description": [], - "signature": [ - "{ write(msg: string): void; }" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogTextWriter.Unnamed", - "type": "Function", - "tags": [], - "label": "Constructor", - "description": [], - "signature": [ - "any" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogTextWriter.Unnamed.$1", - "type": "Object", - "tags": [], - "label": "config", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLogTextWriterConfig", - "text": "ToolingLogTextWriterConfig" - } - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogTextWriter.write", - "type": "Function", - "tags": [], - "label": "write", - "description": [], - "signature": [ - "(msg: ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Message", - "text": "Message" - }, - ") => boolean" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogTextWriter.write.$1", - "type": "Object", - "tags": [], - "label": "msg", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Message", - "text": "Message" - } - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogTextWriter.write", - "type": "Function", - "tags": [], - "label": "write", - "description": [], - "signature": [ - "(writeTo: { write(msg: string): void; }, prefix: string, msg: ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Message", - "text": "Message" - }, - ") => void" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogTextWriter.write.$1", - "type": "Object", - "tags": [], - "label": "writeTo", - "description": [], - "signature": [ - "{ write(msg: string): void; }" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogTextWriter.write.$2", - "type": "string", - "tags": [], - "label": "prefix", - "description": [], - "signature": [ - "string" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogTextWriter.write.$3", - "type": "Object", - "tags": [], - "label": "msg", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Message", - "text": "Message" - } - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - } - ], - "initialIsOpen": false - } - ], - "functions": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.combineErrors", - "type": "Function", - "tags": [], - "label": "combineErrors", - "description": [], - "signature": [ - "(errors: (Error | FailError)[]) => Error" - ], - "path": "packages/kbn-dev-utils/src/run/fail.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.combineErrors.$1", - "type": "Array", - "tags": [], - "label": "errors", - "description": [], - "signature": [ - "(Error | FailError)[]" - ], - "path": "packages/kbn-dev-utils/src/run/fail.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createAbsolutePathSerializer", - "type": "Function", - "tags": [], - "label": "createAbsolutePathSerializer", - "description": [], - "signature": [ - "(rootPath: string, replacement: string) => { test: (value: any) => boolean; serialize: (value: string) => string; }" - ], - "path": "packages/kbn-dev-utils/src/serializers/absolute_path_serializer.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createAbsolutePathSerializer.$1", - "type": "string", - "tags": [], - "label": "rootPath", - "description": [], - "signature": [ - "string" - ], - "path": "packages/kbn-dev-utils/src/serializers/absolute_path_serializer.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createAbsolutePathSerializer.$2", - "type": "string", - "tags": [], - "label": "replacement", - "description": [], - "signature": [ - "string" - ], - "path": "packages/kbn-dev-utils/src/serializers/absolute_path_serializer.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createAnyInstanceSerializer", - "type": "Function", - "tags": [], - "label": "createAnyInstanceSerializer", - "description": [], - "signature": [ - "(Class: Function, name: string | ((instance: any) => string) | undefined) => { test: (v: any) => boolean; serialize: (v: any) => string; }" - ], - "path": "packages/kbn-dev-utils/src/serializers/any_instance_serizlizer.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createAnyInstanceSerializer.$1", - "type": "Object", - "tags": [], - "label": "Class", - "description": [], - "signature": [ - "Function" - ], - "path": "packages/kbn-dev-utils/src/serializers/any_instance_serizlizer.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createAnyInstanceSerializer.$2", - "type": "CompoundType", - "tags": [], - "label": "name", - "description": [], - "signature": [ - "string | ((instance: any) => string) | undefined" - ], - "path": "packages/kbn-dev-utils/src/serializers/any_instance_serizlizer.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createFailError", - "type": "Function", - "tags": [], - "label": "createFailError", - "description": [], - "signature": [ - "(reason: string, options: FailErrorOptions) => FailError" - ], - "path": "packages/kbn-dev-utils/src/run/fail.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createFailError.$1", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string" - ], - "path": "packages/kbn-dev-utils/src/run/fail.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createFailError.$2", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "signature": [ - "FailErrorOptions" - ], - "path": "packages/kbn-dev-utils/src/run/fail.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createFlagError", - "type": "Function", - "tags": [], - "label": "createFlagError", - "description": [], - "signature": [ - "(reason: string) => FailError" - ], - "path": "packages/kbn-dev-utils/src/run/fail.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createFlagError.$1", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string" - ], - "path": "packages/kbn-dev-utils/src/run/fail.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createRecursiveSerializer", - "type": "Function", - "tags": [], - "label": "createRecursiveSerializer", - "description": [], - "signature": [ - "(test: (v: any) => boolean, print: (v: any, printRaw: (v: string) => RawPrint) => string | RawPrint) => { test: (v: any) => boolean; serialize: (v: any, ...rest: any[]) => any; }" - ], - "path": "packages/kbn-dev-utils/src/serializers/recursive_serializer.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createRecursiveSerializer.$1", - "type": "Function", - "tags": [], - "label": "test", - "description": [], - "signature": [ - "(v: any) => boolean" - ], - "path": "packages/kbn-dev-utils/src/serializers/recursive_serializer.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createRecursiveSerializer.$2", - "type": "Function", - "tags": [], - "label": "print", - "description": [], - "signature": [ - "(v: any, printRaw: (v: string) => RawPrint) => string | RawPrint" - ], - "path": "packages/kbn-dev-utils/src/serializers/recursive_serializer.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createReplaceSerializer", - "type": "Function", - "tags": [], - "label": "createReplaceSerializer", - "description": [], - "signature": [ - "(toReplace: string | RegExp, replaceWith: string | Replacer) => { test: (v: any) => boolean; serialize: (v: any, ...rest: any[]) => any; }" - ], - "path": "packages/kbn-dev-utils/src/serializers/replace_serializer.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createReplaceSerializer.$1", - "type": "CompoundType", - "tags": [], - "label": "toReplace", - "description": [], - "signature": [ - "string | RegExp" - ], - "path": "packages/kbn-dev-utils/src/serializers/replace_serializer.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createReplaceSerializer.$2", - "type": "CompoundType", - "tags": [], - "label": "replaceWith", - "description": [], - "signature": [ - "string | Replacer" - ], - "path": "packages/kbn-dev-utils/src/serializers/replace_serializer.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.createStripAnsiSerializer", - "type": "Function", - "tags": [], - "label": "createStripAnsiSerializer", - "description": [], - "signature": [ - "() => { test: (v: any) => boolean; serialize: (v: any, ...rest: any[]) => any; }" - ], - "path": "packages/kbn-dev-utils/src/serializers/strip_ansi_serializer.ts", - "deprecated": false, - "children": [], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.diffStrings", - "type": "Function", - "tags": [], - "label": "diffStrings", - "description": [ - "\nProduces a diff string which is nicely formatted to show the differences between two strings. This will\nbe a multi-line string so it's generally a good idea to include a `\\n` before this first line of the diff\nif you are concatenating it with another message." - ], - "signature": [ - "(expected: string, received: string) => string | undefined" - ], - "path": "packages/kbn-dev-utils/src/diff_strings.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.diffStrings.$1", - "type": "string", - "tags": [], - "label": "expected", - "description": [], - "signature": [ - "string" - ], - "path": "packages/kbn-dev-utils/src/diff_strings.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.diffStrings.$2", - "type": "string", - "tags": [], - "label": "received", - "description": [], - "signature": [ - "string" - ], - "path": "packages/kbn-dev-utils/src/diff_strings.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.extract", - "type": "Function", - "tags": [], - "label": "extract", - "description": [ - "\nExtract tar and zip archives using a single function, supporting stripComponents\nfor both archive types, only tested with familiar archives we create so might not\nsupport some weird exotic zip features we don't use in our own snapshot/build tooling" - ], - "signature": [ - "({\n archivePath,\n targetDir,\n stripComponents = 0,\n setModifiedTimes,\n}: Options) => Promise" - ], - "path": "packages/kbn-dev-utils/src/extract.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.extract.$1", - "type": "Object", - "tags": [], - "label": "{\n archivePath,\n targetDir,\n stripComponents = 0,\n setModifiedTimes,\n}", - "description": [], - "signature": [ - "Options" - ], - "path": "packages/kbn-dev-utils/src/extract.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.getFlags", - "type": "Function", - "tags": [], - "label": "getFlags", - "description": [], - "signature": [ - "(argv: string[], flagOptions: ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.FlagOptions", - "text": "FlagOptions" - }, - " | undefined, defaultLogLevel: string) => ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Flags", - "text": "Flags" - } - ], - "path": "packages/kbn-dev-utils/src/run/flags.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.getFlags.$1", - "type": "Array", - "tags": [], - "label": "argv", - "description": [], - "signature": [ - "string[]" - ], - "path": "packages/kbn-dev-utils/src/run/flags.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.getFlags.$2", - "type": "Object", - "tags": [], - "label": "flagOptions", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.FlagOptions", - "text": "FlagOptions" - }, - " | undefined" - ], - "path": "packages/kbn-dev-utils/src/run/flags.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.getFlags.$3", - "type": "string", - "tags": [], - "label": "defaultLogLevel", - "description": [], - "signature": [ - "string" - ], - "path": "packages/kbn-dev-utils/src/run/flags.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.getTimeReporter", - "type": "Function", - "tags": [], - "label": "getTimeReporter", - "description": [], - "signature": [ - "(log: ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLog", - "text": "ToolingLog" - }, - ", group: string) => (startTime: number, id: string, meta: Record) => Promise" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/report_time.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.getTimeReporter.$1", - "type": "Object", - "tags": [], - "label": "log", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLog", - "text": "ToolingLog" - } - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/report_time.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.getTimeReporter.$2", - "type": "string", - "tags": [], - "label": "group", - "description": [], - "signature": [ - "string" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/report_time.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.isAxiosRequestError", - "type": "Function", - "tags": [], - "label": "isAxiosRequestError", - "description": [], - "signature": [ - "(error: any) => error is ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.AxiosRequestError", - "text": "AxiosRequestError" - } - ], - "path": "packages/kbn-dev-utils/src/axios/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.isAxiosRequestError.$1", - "type": "Any", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "any" - ], - "path": "packages/kbn-dev-utils/src/axios/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.isAxiosResponseError", - "type": "Function", - "tags": [], - "label": "isAxiosResponseError", - "description": [], - "signature": [ - "(error: any) => error is ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.AxiosResponseError", - "text": "AxiosResponseError" - }, - "" - ], - "path": "packages/kbn-dev-utils/src/axios/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.isAxiosResponseError.$1", - "type": "Any", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "any" - ], - "path": "packages/kbn-dev-utils/src/axios/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.isFailError", - "type": "Function", - "tags": [], - "label": "isFailError", - "description": [], - "signature": [ - "(error: any) => boolean" - ], - "path": "packages/kbn-dev-utils/src/run/fail.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.isFailError.$1", - "type": "Any", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "any" - ], - "path": "packages/kbn-dev-utils/src/run/fail.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.mergeFlagOptions", - "type": "Function", - "tags": [], - "label": "mergeFlagOptions", - "description": [], - "signature": [ - "(global: ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.FlagOptions", - "text": "FlagOptions" - }, - ", local: ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.FlagOptions", - "text": "FlagOptions" - }, - ") => ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.FlagOptions", - "text": "FlagOptions" - } - ], - "path": "packages/kbn-dev-utils/src/run/flags.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.mergeFlagOptions.$1", - "type": "Object", - "tags": [], - "label": "global", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.FlagOptions", - "text": "FlagOptions" - } - ], - "path": "packages/kbn-dev-utils/src/run/flags.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.mergeFlagOptions.$2", - "type": "Object", - "tags": [], - "label": "local", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.FlagOptions", - "text": "FlagOptions" - } - ], - "path": "packages/kbn-dev-utils/src/run/flags.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.observeLines", - "type": "Function", - "tags": [ - "return" - ], - "label": "observeLines", - "description": [ - "\n Creates an Observable from a Readable Stream that:\n - splits data from `readable` into lines\n - completes when `readable` emits \"end\"\n - fails if `readable` emits \"errors\"\n" - ], - "signature": [ - "(readable: ", - "Readable", - ") => ", - "Observable", - "" - ], - "path": "packages/kbn-dev-utils/src/stdio/observe_lines.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.observeLines.$1", - "type": "Object", - "tags": [], - "label": "readable", - "description": [], - "signature": [ - "Readable" - ], - "path": "packages/kbn-dev-utils/src/stdio/observe_lines.ts", + "path": "packages/kbn-dev-utils/src/run/fail.ts", "deprecated": false, "isRequired": true } @@ -2395,315 +468,44 @@ }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.observeReadable", + "id": "def-server.diffStrings", "type": "Function", "tags": [], - "label": "observeReadable", + "label": "diffStrings", "description": [ - "\n Produces an Observable from a ReadableSteam that:\n - completes on the first \"end\" event\n - fails on the first \"error\" event" - ], - "signature": [ - "(readable: ", - "Readable", - ") => ", - "Observable", - "" - ], - "path": "packages/kbn-dev-utils/src/stdio/observe_readable.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.observeReadable.$1", - "type": "Object", - "tags": [], - "label": "readable", - "description": [], - "signature": [ - "Readable" - ], - "path": "packages/kbn-dev-utils/src/stdio/observe_readable.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.parseLogLevel", - "type": "Function", - "tags": [], - "label": "parseLogLevel", - "description": [], - "signature": [ - "(name: \"error\" | \"info\" | \"success\" | \"warning\" | \"debug\" | \"silent\" | \"verbose\") => { name: \"error\" | \"info\" | \"success\" | \"warning\" | \"debug\" | \"silent\" | \"verbose\"; flags: { error: boolean; info: boolean; success: boolean; warning: boolean; debug: boolean; silent: boolean; verbose: boolean; }; }" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/log_levels.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.parseLogLevel.$1", - "type": "CompoundType", - "tags": [], - "label": "name", - "description": [], - "signature": [ - "\"error\" | \"info\" | \"success\" | \"warning\" | \"debug\" | \"silent\" | \"verbose\"" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/log_levels.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.pickLevelFromFlags", - "type": "Function", - "tags": [], - "label": "pickLevelFromFlags", - "description": [], - "signature": [ - "(flags: Record, options: { default?: \"error\" | \"info\" | \"success\" | \"warning\" | \"debug\" | \"silent\" | \"verbose\" | undefined; }) => \"error\" | \"info\" | \"success\" | \"warning\" | \"debug\" | \"silent\" | \"verbose\"" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/log_levels.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.pickLevelFromFlags.$1", - "type": "Object", - "tags": [], - "label": "flags", - "description": [], - "signature": [ - "Record" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/log_levels.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.pickLevelFromFlags.$2", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "path": "packages/kbn-dev-utils/src/tooling_log/log_levels.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.pickLevelFromFlags.$2.default", - "type": "CompoundType", - "tags": [], - "label": "default", - "description": [], - "signature": [ - "\"error\" | \"info\" | \"success\" | \"warning\" | \"debug\" | \"silent\" | \"verbose\" | undefined" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/log_levels.ts", - "deprecated": false - } - ] - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.run", - "type": "Function", - "tags": [], - "label": "run", - "description": [], - "signature": [ - "(fn: ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.RunFn", - "text": "RunFn" - }, - ", options: ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.RunOptions", - "text": "RunOptions" - }, - ") => Promise" - ], - "path": "packages/kbn-dev-utils/src/run/run.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.run.$1", - "type": "Function", - "tags": [], - "label": "fn", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.RunFn", - "text": "RunFn" - } - ], - "path": "packages/kbn-dev-utils/src/run/run.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.run.$2", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.RunOptions", - "text": "RunOptions" - } - ], - "path": "packages/kbn-dev-utils/src/run/run.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.runPluginListCli", - "type": "Function", - "tags": [], - "label": "runPluginListCli", - "description": [], - "signature": [ - "() => void" - ], - "path": "packages/kbn-dev-utils/src/plugin_list/run_plugin_list_cli.ts", - "deprecated": false, - "children": [], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.runUpdateVscodeConfigCli", - "type": "Function", - "tags": [], - "label": "runUpdateVscodeConfigCli", - "description": [], - "signature": [ - "() => void" - ], - "path": "packages/kbn-dev-utils/src/vscode_config/update_vscode_config_cli.ts", - "deprecated": false, - "children": [], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.shipCiStatsCli", - "type": "Function", - "tags": [], - "label": "shipCiStatsCli", - "description": [], - "signature": [ - "() => void" + "\nProduces a diff string which is nicely formatted to show the differences between two strings. This will\nbe a multi-line string so it's generally a good idea to include a `\\n` before this first line of the diff\nif you are concatenating it with another message." ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ship_ci_stats_cli.ts", - "deprecated": false, - "children": [], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.sortPackageJson", - "type": "Function", - "tags": [], - "label": "sortPackageJson", - "description": [], "signature": [ - "(json: string) => string" + "(expected: string, received: string) => string | undefined" ], - "path": "packages/kbn-dev-utils/src/sort_package_json.ts", + "path": "packages/kbn-dev-utils/src/diff_strings.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.sortPackageJson.$1", + "id": "def-server.diffStrings.$1", "type": "string", "tags": [], - "label": "json", + "label": "expected", "description": [], "signature": [ "string" ], - "path": "packages/kbn-dev-utils/src/sort_package_json.ts", + "path": "packages/kbn-dev-utils/src/diff_strings.ts", "deprecated": false, "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.transformFileStream", - "type": "Function", - "tags": [], - "label": "transformFileStream", - "description": [ - "\nCreate a transform stream that processes Vinyl fs streams and\ncalls a function for each file, allowing the function to either\nmutate the file, replace it with another file (return a new File\nobject), or drop it from the stream (return null)" - ], - "signature": [ - "(fn: (file: BufferedFile) => void | ", - "node_modules/@types/vinyl/index", - " | Promise | null) => ", - "Transform" - ], - "path": "packages/kbn-dev-utils/src/streams.ts", - "deprecated": false, - "children": [ + }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.transformFileStream.$1", - "type": "Function", + "id": "def-server.diffStrings.$2", + "type": "string", "tags": [], - "label": "fn", + "label": "received", "description": [], "signature": [ - "(file: BufferedFile) => void | ", - "node_modules/@types/vinyl/index", - " | Promise | null" + "string" ], - "path": "packages/kbn-dev-utils/src/streams.ts", + "path": "packages/kbn-dev-utils/src/diff_strings.ts", "deprecated": false, "isRequired": true } @@ -2713,32 +515,30 @@ }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.transformFileWithBabel", + "id": "def-server.extract", "type": "Function", "tags": [], - "label": "transformFileWithBabel", + "label": "extract", "description": [ - "\nReturns a promise that resolves when the file has been\nmutated so the contents of the file are tranformed with\nbabel, include inline sourcemaps, and the filename has\nbeen updated to use .js.\n\nIf the file was previously transformed with this function\nthe promise will just resolve immediately." + "\nExtract tar and zip archives using a single function, supporting stripComponents\nfor both archive types, only tested with familiar archives we create so might not\nsupport some weird exotic zip features we don't use in our own snapshot/build tooling" ], "signature": [ - "(file: ", - "node_modules/@types/vinyl/index", - ") => Promise" + "({\n archivePath,\n targetDir,\n stripComponents = 0,\n setModifiedTimes,\n}: Options) => Promise" ], - "path": "packages/kbn-dev-utils/src/babel.ts", + "path": "packages/kbn-dev-utils/src/extract.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.transformFileWithBabel.$1", + "id": "def-server.extract.$1", "type": "Object", "tags": [], - "label": "file", + "label": "{\n archivePath,\n targetDir,\n stripComponents = 0,\n setModifiedTimes,\n}", "description": [], "signature": [ - "node_modules/@types/vinyl/index" + "Options" ], - "path": "packages/kbn-dev-utils/src/babel.ts", + "path": "packages/kbn-dev-utils/src/extract.ts", "deprecated": false, "isRequired": true } @@ -2748,131 +548,132 @@ }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.withProcRunner", + "id": "def-server.getFlags", "type": "Function", - "tags": [ - "return" - ], - "label": "withProcRunner", - "description": [ - "\n Create a ProcRunner and pass it to an async function. When\n the async function finishes the ProcRunner is torn-down\n automatically\n" - ], + "tags": [], + "label": "getFlags", + "description": [], "signature": [ - "(log: ", + "(argv: string[], flagOptions: ", { "pluginId": "@kbn/dev-utils", "scope": "server", "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLog", - "text": "ToolingLog" + "section": "def-server.FlagOptions", + "text": "FlagOptions" }, - ", fn: (procs: ", + " | undefined, defaultLogLevel: string) => ", { "pluginId": "@kbn/dev-utils", "scope": "server", "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ProcRunner", - "text": "ProcRunner" - }, - ") => Promise) => Promise" + "section": "def-server.Flags", + "text": "Flags" + } ], - "path": "packages/kbn-dev-utils/src/proc_runner/with_proc_runner.ts", + "path": "packages/kbn-dev-utils/src/run/flags.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.withProcRunner.$1", - "type": "Object", + "id": "def-server.getFlags.$1", + "type": "Array", "tags": [], - "label": "log", + "label": "argv", "description": [], "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLog", - "text": "ToolingLog" - } + "string[]" ], - "path": "packages/kbn-dev-utils/src/proc_runner/with_proc_runner.ts", + "path": "packages/kbn-dev-utils/src/run/flags.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.withProcRunner.$2", - "type": "Function", + "id": "def-server.getFlags.$2", + "type": "Object", "tags": [], - "label": "fn", + "label": "flagOptions", "description": [], "signature": [ - "(procs: ", { "pluginId": "@kbn/dev-utils", "scope": "server", "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ProcRunner", - "text": "ProcRunner" + "section": "def-server.FlagOptions", + "text": "FlagOptions" }, - ") => Promise" + " | undefined" ], - "path": "packages/kbn-dev-utils/src/proc_runner/with_proc_runner.ts", + "path": "packages/kbn-dev-utils/src/run/flags.ts", + "deprecated": false, + "isRequired": false + }, + { + "parentPluginId": "@kbn/dev-utils", + "id": "def-server.getFlags.$3", + "type": "string", + "tags": [], + "label": "defaultLogLevel", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-dev-utils/src/run/flags.ts", "deprecated": false, "isRequired": true } ], "returnComment": [], "initialIsOpen": false - } - ], - "interfaces": [ + }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.AxiosRequestError", - "type": "Interface", + "id": "def-server.isAxiosRequestError", + "type": "Function", "tags": [], - "label": "AxiosRequestError", + "label": "isAxiosRequestError", "description": [], "signature": [ + "(error: any) => error is ", { "pluginId": "@kbn/dev-utils", "scope": "server", "docId": "kibKbnDevUtilsPluginApi", "section": "def-server.AxiosRequestError", "text": "AxiosRequestError" - }, - " extends ", - "AxiosError", - "" + } ], "path": "packages/kbn-dev-utils/src/axios/errors.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.AxiosRequestError.response", - "type": "Uncategorized", + "id": "def-server.isAxiosRequestError.$1", + "type": "Any", "tags": [], - "label": "response", + "label": "error", "description": [], "signature": [ - "undefined" + "any" ], "path": "packages/kbn-dev-utils/src/axios/errors.ts", - "deprecated": false + "deprecated": false, + "isRequired": true } ], + "returnComment": [], "initialIsOpen": false }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.AxiosResponseError", - "type": "Interface", + "id": "def-server.isAxiosResponseError", + "type": "Function", "tags": [], - "label": "AxiosResponseError", + "label": "isAxiosResponseError", "description": [], "signature": [ + "(error: any) => error is ", { "pluginId": "@kbn/dev-utils", "scope": "server", @@ -2880,486 +681,479 @@ "section": "def-server.AxiosResponseError", "text": "AxiosResponseError" }, - " extends ", - "AxiosError", - "" + "" ], "path": "packages/kbn-dev-utils/src/axios/errors.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.AxiosResponseError.response", - "type": "Object", + "id": "def-server.isAxiosResponseError.$1", + "type": "Any", "tags": [], - "label": "response", + "label": "error", "description": [], "signature": [ - "AxiosResponse", - "" + "any" ], "path": "packages/kbn-dev-utils/src/axios/errors.ts", - "deprecated": false + "deprecated": false, + "isRequired": true } ], + "returnComment": [], "initialIsOpen": false }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsMetric", - "type": "Interface", + "id": "def-server.isFailError", + "type": "Function", "tags": [], - "label": "CiStatsMetric", - "description": [ - "A ci-stats metric record" + "label": "isFailError", + "description": [], + "signature": [ + "(error: any) => boolean" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", + "path": "packages/kbn-dev-utils/src/run/fail.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsMetric.group", - "type": "string", + "id": "def-server.isFailError.$1", + "type": "Any", "tags": [], - "label": "group", - "description": [ - "Top-level categorization for the metric, e.g. \"page load bundle size\"" + "label": "error", + "description": [], + "signature": [ + "any" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false - }, + "path": "packages/kbn-dev-utils/src/run/fail.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/dev-utils", + "id": "def-server.mergeFlagOptions", + "type": "Function", + "tags": [], + "label": "mergeFlagOptions", + "description": [], + "signature": [ + "(global: ", { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsMetric.id", - "type": "string", - "tags": [], - "label": "id", - "description": [ - "Specific sub-set of the \"group\", e.g. \"dashboard\"" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false + "pluginId": "@kbn/dev-utils", + "scope": "server", + "docId": "kibKbnDevUtilsPluginApi", + "section": "def-server.FlagOptions", + "text": "FlagOptions" }, + ", local: ", { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsMetric.value", - "type": "number", - "tags": [], - "label": "value", - "description": [ - "integer value recorded as the value of this metric" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false + "pluginId": "@kbn/dev-utils", + "scope": "server", + "docId": "kibKbnDevUtilsPluginApi", + "section": "def-server.FlagOptions", + "text": "FlagOptions" }, + ") => ", { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsMetric.limit", - "type": "number", - "tags": [], - "label": "limit", - "description": [ - "optional limit which will generate an error on PRs when the metric exceeds the limit" - ], - "signature": [ - "number | undefined" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false - }, + "pluginId": "@kbn/dev-utils", + "scope": "server", + "docId": "kibKbnDevUtilsPluginApi", + "section": "def-server.FlagOptions", + "text": "FlagOptions" + } + ], + "path": "packages/kbn-dev-utils/src/run/flags.ts", + "deprecated": false, + "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsMetric.limitConfigPath", - "type": "string", + "id": "def-server.mergeFlagOptions.$1", + "type": "Object", "tags": [], - "label": "limitConfigPath", - "description": [ - "\npath, relative to the repo, where the config file contianing limits\nis kept. Linked from PR comments instructing contributors how to fix\ntheir PRs." - ], + "label": "global", + "description": [], "signature": [ - "string | undefined" + { + "pluginId": "@kbn/dev-utils", + "scope": "server", + "docId": "kibKbnDevUtilsPluginApi", + "section": "def-server.FlagOptions", + "text": "FlagOptions" + } ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false + "path": "packages/kbn-dev-utils/src/run/flags.ts", + "deprecated": false, + "isRequired": true }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsMetric.meta", + "id": "def-server.mergeFlagOptions.$2", "type": "Object", "tags": [], - "label": "meta", - "description": [ - "Arbitrary key-value pairs which can be used for additional filtering/reporting" - ], + "label": "local", + "description": [], "signature": [ - "CiStatsMetadata", - " | undefined" + { + "pluginId": "@kbn/dev-utils", + "scope": "server", + "docId": "kibKbnDevUtilsPluginApi", + "section": "def-server.FlagOptions", + "text": "FlagOptions" + } ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false + "path": "packages/kbn-dev-utils/src/run/flags.ts", + "deprecated": false, + "isRequired": true } ], + "returnComment": [], "initialIsOpen": false }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReportTestsOptions", - "type": "Interface", + "id": "def-server.run", + "type": "Function", "tags": [], - "label": "CiStatsReportTestsOptions", - "description": [ - "Options for reporting tests to ci-stats" + "label": "run", + "description": [], + "signature": [ + "(fn: ", + { + "pluginId": "@kbn/dev-utils", + "scope": "server", + "docId": "kibKbnDevUtilsPluginApi", + "section": "def-server.RunFn", + "text": "RunFn" + }, + ", options: ", + { + "pluginId": "@kbn/dev-utils", + "scope": "server", + "docId": "kibKbnDevUtilsPluginApi", + "section": "def-server.RunOptions", + "text": "RunOptions" + }, + ") => Promise" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", + "path": "packages/kbn-dev-utils/src/run/run.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReportTestsOptions.group", - "type": "Object", + "id": "def-server.run.$1", + "type": "Function", "tags": [], - "label": "group", - "description": [ - "\nInformation about the group of tests that were run" - ], + "label": "fn", + "description": [], "signature": [ { "pluginId": "@kbn/dev-utils", "scope": "server", "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.CiStatsTestGroupInfo", - "text": "CiStatsTestGroupInfo" + "section": "def-server.RunFn", + "text": "RunFn" } ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false + "path": "packages/kbn-dev-utils/src/run/run.ts", + "deprecated": false, + "isRequired": true }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsReportTestsOptions.testRuns", - "type": "Array", + "id": "def-server.run.$2", + "type": "Object", "tags": [], - "label": "testRuns", - "description": [ - "\nInformation about each test that ran, including failure information" - ], + "label": "options", + "description": [], "signature": [ { "pluginId": "@kbn/dev-utils", "scope": "server", "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.CiStatsTestRun", - "text": "CiStatsTestRun" - }, - "[]" + "section": "def-server.RunOptions", + "text": "RunOptions" + } ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false + "path": "packages/kbn-dev-utils/src/run/run.ts", + "deprecated": false, + "isRequired": true } ], + "returnComment": [], "initialIsOpen": false }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestGroupInfo", - "type": "Interface", + "id": "def-server.runPluginListCli", + "type": "Function", + "tags": [], + "label": "runPluginListCli", + "description": [], + "signature": [ + "() => void" + ], + "path": "packages/kbn-dev-utils/src/plugin_list/run_plugin_list_cli.ts", + "deprecated": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/dev-utils", + "id": "def-server.runUpdateVscodeConfigCli", + "type": "Function", + "tags": [], + "label": "runUpdateVscodeConfigCli", + "description": [], + "signature": [ + "() => void" + ], + "path": "packages/kbn-dev-utils/src/vscode_config/update_vscode_config_cli.ts", + "deprecated": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/dev-utils", + "id": "def-server.shipCiStatsCli", + "type": "Function", "tags": [], - "label": "CiStatsTestGroupInfo", + "label": "shipCiStatsCli", "description": [], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", + "signature": [ + "() => void" + ], + "path": "packages/kbn-dev-utils/src/ship_ci_stats_cli.ts", + "deprecated": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/dev-utils", + "id": "def-server.transformFileStream", + "type": "Function", + "tags": [], + "label": "transformFileStream", + "description": [ + "\nCreate a transform stream that processes Vinyl fs streams and\ncalls a function for each file, allowing the function to either\nmutate the file, replace it with another file (return a new File\nobject), or drop it from the stream (return null)" + ], + "signature": [ + "(fn: (file: BufferedFile) => void | ", + "node_modules/@types/vinyl/index", + " | Promise | null) => ", + "Transform" + ], + "path": "packages/kbn-dev-utils/src/streams.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestGroupInfo.startTime", - "type": "string", - "tags": [], - "label": "startTime", - "description": [ - "\nISO-8601 formatted datetime representing when the group of tests started running" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestGroupInfo.durationMs", - "type": "number", - "tags": [], - "label": "durationMs", - "description": [ - "\nThe number of miliseconds that the tests ran for" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestGroupInfo.type", - "type": "string", - "tags": [], - "label": "type", - "description": [ - "\nThe type of tests run in this group, any value is valid but test groups are groupped by type in the UI so use something consistent" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestGroupInfo.name", - "type": "string", - "tags": [], - "label": "name", - "description": [ - "\nThe name of this specific group (within the \"type\")" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestGroupInfo.meta", - "type": "Object", + "id": "def-server.transformFileStream.$1", + "type": "Function", "tags": [], - "label": "meta", - "description": [ - "\nArbitrary metadata associated with this group. We currently look for a ciGroup metadata property for highlighting that when appropriate" - ], + "label": "fn", + "description": [], "signature": [ - "CiStatsMetadata" + "(file: BufferedFile) => void | ", + "node_modules/@types/vinyl/index", + " | Promise | null" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false + "path": "packages/kbn-dev-utils/src/streams.ts", + "deprecated": false, + "isRequired": true } ], + "returnComment": [], "initialIsOpen": false }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestRun", - "type": "Interface", + "id": "def-server.transformFileWithBabel", + "type": "Function", "tags": [], - "label": "CiStatsTestRun", - "description": [], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", + "label": "transformFileWithBabel", + "description": [ + "\nReturns a promise that resolves when the file has been\nmutated so the contents of the file are tranformed with\nbabel, include inline sourcemaps, and the filename has\nbeen updated to use .js.\n\nIf the file was previously transformed with this function\nthe promise will just resolve immediately." + ], + "signature": [ + "(file: ", + "node_modules/@types/vinyl/index", + ") => Promise" + ], + "path": "packages/kbn-dev-utils/src/babel.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestRun.startTime", - "type": "string", - "tags": [], - "label": "startTime", - "description": [ - "\nISO-8601 formatted datetime representing when the tests started running" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestRun.durationMs", - "type": "number", - "tags": [], - "label": "durationMs", - "description": [ - "\nDuration of the tests in milliseconds" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestRun.seq", - "type": "number", - "tags": [], - "label": "seq", - "description": [ - "\nA sequence number, this is used to order the tests in a specific test run" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestRun.type", - "type": "CompoundType", - "tags": [], - "label": "type", - "description": [ - "\nThe type of this \"test run\", usually this is just \"test\" but when reporting issues in hooks it can be set to the type of hook" - ], - "signature": [ - "\"after all hook\" | \"after each hook\" | \"before all hook\" | \"before each hook\" | \"test\"" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestRun.result", - "type": "CompoundType", - "tags": [], - "label": "result", - "description": [ - "\n\"fail\", \"pass\" or \"skip\", the result of the tests" - ], - "signature": [ - "\"fail\" | \"pass\" | \"skip\"" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestRun.suites", - "type": "Array", - "tags": [], - "label": "suites", - "description": [ - "\nThe list of suite names containing this test, the first being the outermost suite" - ], - "signature": [ - "string[]" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestRun.name", - "type": "string", - "tags": [], - "label": "name", - "description": [ - "\nThe name of this specific test run" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestRun.file", - "type": "string", + "id": "def-server.transformFileWithBabel.$1", + "type": "Object", "tags": [], "label": "file", - "description": [ - "\nRelative path from the root of the repo contianing this test" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestRun.error", - "type": "string", - "tags": [], - "label": "error", - "description": [ - "\nError message if the test failed" - ], + "description": [], "signature": [ - "string | undefined" + "node_modules/@types/vinyl/index" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false + "path": "packages/kbn-dev-utils/src/babel.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/dev-utils", + "id": "def-server.withProcRunner", + "type": "Function", + "tags": [ + "return" + ], + "label": "withProcRunner", + "description": [ + "\n Create a ProcRunner and pass it to an async function. When\n the async function finishes the ProcRunner is torn-down\n automatically\n" + ], + "signature": [ + "(log: ", + "ToolingLog", + ", fn: (procs: ", + { + "pluginId": "@kbn/dev-utils", + "scope": "server", + "docId": "kibKbnDevUtilsPluginApi", + "section": "def-server.ProcRunner", + "text": "ProcRunner" }, + ") => Promise) => Promise" + ], + "path": "packages/kbn-dev-utils/src/proc_runner/with_proc_runner.ts", + "deprecated": false, + "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestRun.stdout", - "type": "string", + "id": "def-server.withProcRunner.$1", + "type": "Object", "tags": [], - "label": "stdout", - "description": [ - "\nDebug output/stdout produced by the test" - ], + "label": "log", + "description": [], "signature": [ - "string | undefined" + "ToolingLog" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false + "path": "packages/kbn-dev-utils/src/proc_runner/with_proc_runner.ts", + "deprecated": false, + "isRequired": true }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestRun.screenshots", - "type": "Array", + "id": "def-server.withProcRunner.$2", + "type": "Function", "tags": [], - "label": "screenshots", - "description": [ - "\nScreenshots captured during the test run" - ], + "label": "fn", + "description": [], "signature": [ - "{ name: string; base64Png: string; }[] | undefined" + "(procs: ", + { + "pluginId": "@kbn/dev-utils", + "scope": "server", + "docId": "kibKbnDevUtilsPluginApi", + "section": "def-server.ProcRunner", + "text": "ProcRunner" + }, + ") => Promise" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false + "path": "packages/kbn-dev-utils/src/proc_runner/with_proc_runner.ts", + "deprecated": false, + "isRequired": true } ], + "returnComment": [], "initialIsOpen": false - }, + } + ], + "interfaces": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTiming", + "id": "def-server.AxiosRequestError", "type": "Interface", "tags": [], - "label": "CiStatsTiming", - "description": [ - "A ci-stats timing event" + "label": "AxiosRequestError", + "description": [], + "signature": [ + { + "pluginId": "@kbn/dev-utils", + "scope": "server", + "docId": "kibKbnDevUtilsPluginApi", + "section": "def-server.AxiosRequestError", + "text": "AxiosRequestError" + }, + " extends ", + "AxiosError", + "" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", + "path": "packages/kbn-dev-utils/src/axios/errors.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTiming.group", - "type": "string", - "tags": [], - "label": "group", - "description": [ - "Top-level categorization for the timing, e.g. \"scripts/foo\", process type, etc." - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTiming.id", - "type": "string", + "id": "def-server.AxiosRequestError.response", + "type": "Uncategorized", "tags": [], - "label": "id", - "description": [ - "Specific timing (witin the \"group\" being tracked) e.g. \"total\"" + "label": "response", + "description": [], + "signature": [ + "undefined" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", + "path": "packages/kbn-dev-utils/src/axios/errors.ts", "deprecated": false - }, + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/dev-utils", + "id": "def-server.AxiosResponseError", + "type": "Interface", + "tags": [], + "label": "AxiosResponseError", + "description": [], + "signature": [ { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTiming.ms", - "type": "number", - "tags": [], - "label": "ms", - "description": [ - "time in milliseconds which should be recorded" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false + "pluginId": "@kbn/dev-utils", + "scope": "server", + "docId": "kibKbnDevUtilsPluginApi", + "section": "def-server.AxiosResponseError", + "text": "AxiosResponseError" }, + " extends ", + "AxiosError", + "" + ], + "path": "packages/kbn-dev-utils/src/axios/errors.ts", + "deprecated": false, + "children": [ { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTiming.meta", + "id": "def-server.AxiosResponseError.response", "type": "Object", "tags": [], - "label": "meta", - "description": [ - "hash of key-value pairs which will be stored with the timing for additional filtering and reporting" - ], + "label": "response", + "description": [], "signature": [ - "CiStatsMetadata", - " | undefined" + "AxiosResponse", + "" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", + "path": "packages/kbn-dev-utils/src/axios/errors.ts", "deprecated": false } ], @@ -3488,45 +1282,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.Config", - "type": "Interface", - "tags": [], - "label": "Config", - "description": [ - "\nInformation about how CiStatsReporter should talk to the ci-stats service. Normally\nit is read from a JSON environment variable using the `parseConfig()` function\nexported by this module." - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_config.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.Config.apiToken", - "type": "string", - "tags": [], - "label": "apiToken", - "description": [ - "ApiToken necessary for writing build data to ci-stats service" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_config.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.Config.buildId", - "type": "string", - "tags": [], - "label": "buildId", - "description": [ - "\nuuid which should be obtained by first creating a build with the\nci-stats service and then passing it to all subsequent steps" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_config.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/dev-utils", "id": "def-server.FlagOptions", @@ -3687,150 +1442,47 @@ "type": "boolean", "tags": [], "label": "help", - "description": [], - "path": "packages/kbn-dev-utils/src/run/flags.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.Flags._", - "type": "Array", - "tags": [], - "label": "_", - "description": [], - "signature": [ - "string[]" - ], - "path": "packages/kbn-dev-utils/src/run/flags.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.Flags.unexpected", - "type": "Array", - "tags": [], - "label": "unexpected", - "description": [], - "signature": [ - "string[]" - ], - "path": "packages/kbn-dev-utils/src/run/flags.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.Flags.Unnamed", - "type": "IndexSignature", - "tags": [], - "label": "[key: string]: string | boolean | string[] | undefined", - "description": [], - "signature": [ - "[key: string]: string | boolean | string[] | undefined" - ], - "path": "packages/kbn-dev-utils/src/run/flags.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.Message", - "type": "Interface", - "tags": [], - "label": "Message", - "description": [ - "\nThe object shape passed to ToolingLog writers each time the log is used." - ], - "path": "packages/kbn-dev-utils/src/tooling_log/message.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.Message.type", - "type": "CompoundType", - "tags": [], - "label": "type", - "description": [ - "level/type of message" - ], - "signature": [ - "\"error\" | \"write\" | \"info\" | \"success\" | \"warning\" | \"debug\" | \"verbose\"" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/message.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.Message.indent", - "type": "number", - "tags": [], - "label": "indent", - "description": [ - "indentation intended when message written to a text log" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/message.ts", + "description": [], + "path": "packages/kbn-dev-utils/src/run/flags.ts", "deprecated": false }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.Message.source", - "type": "string", + "id": "def-server.Flags._", + "type": "Array", "tags": [], - "label": "source", - "description": [ - "type of logger this message came from" - ], + "label": "_", + "description": [], "signature": [ - "string | undefined" + "string[]" ], - "path": "packages/kbn-dev-utils/src/tooling_log/message.ts", + "path": "packages/kbn-dev-utils/src/run/flags.ts", "deprecated": false }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.Message.args", + "id": "def-server.Flags.unexpected", "type": "Array", "tags": [], - "label": "args", - "description": [ - "args passed to the logging method" - ], + "label": "unexpected", + "description": [], "signature": [ - "any[]" + "string[]" ], - "path": "packages/kbn-dev-utils/src/tooling_log/message.ts", + "path": "packages/kbn-dev-utils/src/run/flags.ts", "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.MetricsOptions", - "type": "Interface", - "tags": [], - "label": "MetricsOptions", - "description": [ - "Options for reporting metrics to ci-stats" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false, - "children": [ + }, { "parentPluginId": "@kbn/dev-utils", - "id": "def-server.MetricsOptions.defaultMeta", - "type": "Object", + "id": "def-server.Flags.Unnamed", + "type": "IndexSignature", "tags": [], - "label": "defaultMeta", - "description": [ - "Default metadata to add to each metric" - ], + "label": "[key: string]: string | boolean | string[] | undefined", + "description": [], "signature": [ - "CiStatsMetadata", - " | undefined" + "[key: string]: string | boolean | string[] | undefined" ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", + "path": "packages/kbn-dev-utils/src/run/flags.ts", "deprecated": false } ], @@ -3854,13 +1506,7 @@ "label": "log", "description": [], "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLog", - "text": "ToolingLog" - } + "ToolingLog" ], "path": "packages/kbn-dev-utils/src/run/run.ts", "deprecated": false @@ -4007,7 +1653,7 @@ "label": "log", "description": [], "signature": [ - "{ defaultLevel?: \"error\" | \"info\" | \"success\" | \"warning\" | \"debug\" | \"silent\" | \"verbose\" | undefined; } | undefined" + "{ defaultLevel?: \"error\" | \"success\" | \"warning\" | \"info\" | \"debug\" | \"silent\" | \"verbose\" | undefined; } | undefined" ], "path": "packages/kbn-dev-utils/src/run/run.ts", "deprecated": false @@ -4063,7 +1709,7 @@ "label": "log", "description": [], "signature": [ - "{ defaultLevel?: \"error\" | \"info\" | \"success\" | \"warning\" | \"debug\" | \"silent\" | \"verbose\" | undefined; } | undefined" + "{ defaultLevel?: \"error\" | \"success\" | \"warning\" | \"info\" | \"debug\" | \"silent\" | \"verbose\" | undefined; } | undefined" ], "path": "packages/kbn-dev-utils/src/run/run_with_commands.ts", "deprecated": false @@ -4160,244 +1806,6 @@ } ], "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.TimingsOptions", - "type": "Interface", - "tags": [], - "label": "TimingsOptions", - "description": [ - "Options for reporting timings to ci-stats" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.TimingsOptions.timings", - "type": "Array", - "tags": [], - "label": "timings", - "description": [ - "list of timings to record" - ], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.CiStatsTiming", - "text": "CiStatsTiming" - }, - "[]" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.TimingsOptions.upstreamBranch", - "type": "string", - "tags": [], - "label": "upstreamBranch", - "description": [ - "master, 7.x, etc, automatically detected from package.json if not specified" - ], - "signature": [ - "string | undefined" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.TimingsOptions.kibanaUuid", - "type": "CompoundType", - "tags": [], - "label": "kibanaUuid", - "description": [ - "value of data/uuid, automatically loaded if not specified" - ], - "signature": [ - "string | null | undefined" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogOptions", - "type": "Interface", - "tags": [], - "label": "ToolingLogOptions", - "description": [], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogOptions.type", - "type": "string", - "tags": [], - "label": "type", - "description": [ - "\ntype name for this logger, will be assigned to the \"source\"\nproperties of messages produced by this logger" - ], - "signature": [ - "string | undefined" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogOptions.parent", - "type": "Object", - "tags": [], - "label": "parent", - "description": [ - "\nparent ToolingLog. When a ToolingLog has a parent they will both\nshare indent and writers state. Changing the indent width or\nwriters on either log will update the other too." - ], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.ToolingLog", - "text": "ToolingLog" - }, - " | undefined" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogTextWriterConfig", - "type": "Interface", - "tags": [], - "label": "ToolingLogTextWriterConfig", - "description": [], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogTextWriterConfig.level", - "type": "CompoundType", - "tags": [], - "label": "level", - "description": [ - "\nLog level, messages below this level will be ignored" - ], - "signature": [ - "\"error\" | \"info\" | \"success\" | \"warning\" | \"debug\" | \"silent\" | \"verbose\"" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogTextWriterConfig.ignoreSources", - "type": "Array", - "tags": [], - "label": "ignoreSources", - "description": [ - "\nList of message sources/ToolingLog types which will be ignored. Create\na logger with `ToolingLog#withType()` to create messages with a specific\nsource. Ignored messages will be dropped without writing." - ], - "signature": [ - "string[] | undefined" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ToolingLogTextWriterConfig.writeTo", - "type": "Object", - "tags": [], - "label": "writeTo", - "description": [ - "\nTarget which will receive formatted message lines, a common value for `writeTo`\nis process.stdout" - ], - "signature": [ - "{ write(s: string): void; }" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.Writer", - "type": "Interface", - "tags": [], - "label": "Writer", - "description": [ - "\nAn object which received ToolingLog `Messages` and sends them to\nsome interface for collecting logs like stdio, or a file" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/writer.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.Writer.write", - "type": "Function", - "tags": [], - "label": "write", - "description": [ - "\nCalled with every log message, should return true if the message\nwas written and false if it was ignored." - ], - "signature": [ - "(msg: ", - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Message", - "text": "Message" - }, - ") => boolean" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/writer.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.Writer.write.$1", - "type": "Object", - "tags": [], - "label": "msg", - "description": [ - "The log message to write" - ], - "signature": [ - { - "pluginId": "@kbn/dev-utils", - "scope": "server", - "docId": "kibKbnDevUtilsPluginApi", - "section": "def-server.Message", - "text": "Message" - } - ], - "path": "packages/kbn-dev-utils/src/tooling_log/writer.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - } - ], - "initialIsOpen": false } ], "enums": [], @@ -4413,34 +1821,6 @@ "deprecated": false, "initialIsOpen": false }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestResult", - "type": "Type", - "tags": [], - "label": "CiStatsTestResult", - "description": [], - "signature": [ - "\"fail\" | \"pass\" | \"skip\"" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.CiStatsTestType", - "type": "Type", - "tags": [], - "label": "CiStatsTestType", - "description": [], - "signature": [ - "\"after all hook\" | \"after each hook\" | \"before all hook\" | \"before each hook\" | \"test\"" - ], - "path": "packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_test_group_types.ts", - "deprecated": false, - "initialIsOpen": false - }, { "parentPluginId": "@kbn/dev-utils", "id": "def-server.CleanupTask", @@ -4620,34 +2000,6 @@ "deprecated": false, "initialIsOpen": false }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.LogLevel", - "type": "Type", - "tags": [], - "label": "LogLevel", - "description": [], - "signature": [ - "\"error\" | \"info\" | \"success\" | \"warning\" | \"debug\" | \"silent\" | \"verbose\"" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/log_levels.ts", - "deprecated": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/dev-utils", - "id": "def-server.ParsedLogLevel", - "type": "Type", - "tags": [], - "label": "ParsedLogLevel", - "description": [], - "signature": [ - "{ name: \"error\" | \"info\" | \"success\" | \"warning\" | \"debug\" | \"silent\" | \"verbose\"; flags: { error: boolean; info: boolean; success: boolean; warning: boolean; debug: boolean; silent: boolean; verbose: boolean; }; }" - ], - "path": "packages/kbn-dev-utils/src/tooling_log/log_levels.ts", - "deprecated": false, - "initialIsOpen": false - }, { "parentPluginId": "@kbn/dev-utils", "id": "def-server.RunFn", diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index 39d04687e47e7..66ac63e6464b6 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/dev-utils plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 277 | 3 | 199 | 1 | +| 120 | 3 | 109 | 0 | ## Server diff --git a/api_docs/kbn_doc_links.devdocs.json b/api_docs/kbn_doc_links.devdocs.json index 48d1068132518..d0d228ea60e36 100644 --- a/api_docs/kbn_doc_links.devdocs.json +++ b/api_docs/kbn_doc_links.devdocs.json @@ -261,7 +261,7 @@ "label": "appSearch", "description": [], "signature": [ - "{ readonly apiRef: string; readonly apiClients: string; readonly apiKeys: string; readonly authentication: string; readonly crawlRules: string; readonly curations: string; readonly duplicateDocuments: string; readonly entryPoints: string; readonly guide: string; readonly indexingDocuments: string; readonly indexingDocumentsSchema: string; readonly logSettings: string; readonly metaEngines: string; readonly precisionTuning: string; readonly relevanceTuning: string; readonly resultSettings: string; readonly searchUI: string; readonly security: string; readonly synonyms: string; readonly webCrawler: string; readonly webCrawlerEventLogs: string; }" + "{ readonly apiRef: string; readonly apiClients: string; readonly apiKeys: string; readonly authentication: string; readonly crawlRules: string; readonly curations: string; readonly duplicateDocuments: string; readonly elasticsearchIndexedEngines: string; readonly entryPoints: string; readonly guide: string; readonly indexingDocuments: string; readonly indexingDocumentsSchema: string; readonly logSettings: string; readonly metaEngines: string; readonly precisionTuning: string; readonly relevanceTuning: string; readonly resultSettings: string; readonly searchUI: string; readonly security: string; readonly synonyms: string; readonly webCrawler: string; readonly webCrawlerEventLogs: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false @@ -326,7 +326,7 @@ "label": "logstash", "description": [], "signature": [ - "{ readonly base: string; }" + "{ readonly base: string; readonly inputElasticAgent: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false @@ -440,7 +440,7 @@ "label": "kibana", "description": [], "signature": [ - "{ readonly guide: string; readonly autocompleteSuggestions: string; readonly xpackSecurity: string; }" + "{ readonly guide: string; readonly autocompleteSuggestions: string; readonly secureSavedObject: string; readonly xpackSecurity: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false @@ -489,7 +489,7 @@ "label": "siem", "description": [], "signature": [ - "{ readonly privileges: string; readonly guide: string; readonly gettingStarted: string; readonly ml: string; readonly ruleChangeLog: string; readonly detectionsReq: string; readonly networkMap: string; readonly troubleshootGaps: string; }" + "{ readonly privileges: string; readonly guide: string; readonly gettingStarted: string; readonly ml: string; readonly ruleChangeLog: string; readonly detectionsReq: string; readonly networkMap: string; readonly troubleshootGaps: string; readonly ruleApiOverview: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false @@ -502,7 +502,7 @@ "label": "securitySolution", "description": [], "signature": [ - "{ readonly trustedApps: string; readonly eventFilters: string; }" + "{ readonly trustedApps: string; readonly eventFilters: string; readonly blocklist: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false @@ -749,7 +749,7 @@ "label": "fleet", "description": [], "signature": [ - "{ readonly beatsAgentComparison: string; readonly guide: string; readonly fleetServer: string; readonly fleetServerAddFleetServer: string; readonly settings: string; readonly settingsFleetServerHostSettings: string; readonly settingsFleetServerProxySettings: string; readonly troubleshooting: string; readonly elasticAgent: string; readonly datastreams: string; readonly datastreamsILM: string; readonly datastreamsNamingScheme: string; readonly installElasticAgent: string; readonly installElasticAgentStandalone: string; readonly upgradeElasticAgent: string; readonly upgradeElasticAgent712lower: string; readonly learnMoreBlog: string; readonly apiKeysLearnMore: string; readonly onPremRegistry: string; }" + "{ readonly beatsAgentComparison: string; readonly guide: string; readonly fleetServer: string; readonly fleetServerAddFleetServer: string; readonly settings: string; readonly settingsFleetServerHostSettings: string; readonly settingsFleetServerProxySettings: string; readonly troubleshooting: string; readonly elasticAgent: string; readonly datastreams: string; readonly datastreamsILM: string; readonly datastreamsNamingScheme: string; readonly installElasticAgent: string; readonly installElasticAgentStandalone: string; readonly upgradeElasticAgent: string; readonly upgradeElasticAgent712lower: string; readonly learnMoreBlog: string; readonly apiKeysLearnMore: string; readonly onPremRegistry: string; readonly secureLogstash: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false @@ -805,6 +805,19 @@ ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false + }, + { + "parentPluginId": "@kbn/doc-links", + "id": "def-server.DocLinks.kibanaUpgradeSavedObjects", + "type": "Object", + "tags": [], + "label": "kibanaUpgradeSavedObjects", + "description": [], + "signature": [ + "{ readonly resolveMigrationFailures: string; }" + ], + "path": "packages/kbn-doc-links/src/types.ts", + "deprecated": false } ], "initialIsOpen": false diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 3f83c0371ca56..4d23bdfc99533 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/doc-links plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 63 | 0 | 63 | 2 | +| 64 | 0 | 64 | 2 | ## Server diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index a2568bc87ea2a..3fd9067f34bae 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/docs-utils plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] warning: 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. --- diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 965ed6526f6a9..2372ce2f44342 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/es-archiver plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] warning: 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. --- diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 3c39bf2f5d09d..f51fa006128aa 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/es-query plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] warning: 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. --- diff --git a/api_docs/kbn_eslint_plugin_imports.devdocs.json b/api_docs/kbn_eslint_plugin_imports.devdocs.json index 47400f2102348..c1b952dc57cd6 100644 --- a/api_docs/kbn_eslint_plugin_imports.devdocs.json +++ b/api_docs/kbn_eslint_plugin_imports.devdocs.json @@ -13,56 +13,34 @@ "functions": [ { "parentPluginId": "@kbn/eslint-plugin-imports", - "id": "def-server.resolveKibanaImport", + "id": "def-server.getImportResolver", "type": "Function", "tags": [], - "label": "resolveKibanaImport", + "label": "getImportResolver", "description": [ - "\nResolve an import request. All import requests in the repository should return a result, if they don't it's a bug\nwhich should be caught by the `@kbn/import/no_unresolved` rule, which should never be disabled. If you need help\nadding support for an import style please reach out to operations.\n" + "\nCreate a request resolver for ESLint, requires a PluginPackageResolver from @kbn/bazel-packages which will\nbe created and cached on contextServices automatically.\n\nAll import requests in the repository should return a result, if they don't it's a bug\nwhich should be caught by the `@kbn/import/no_unresolved` rule, which should never be disabled. If you need help\nadding support for an import style please reach out to operations." ], "signature": [ - "(req: string, dirname: string) => ", - { - "pluginId": "@kbn/eslint-plugin-imports", - "scope": "server", - "docId": "kibKbnEslintPluginImportsPluginApi", - "section": "def-server.ResolveResult", - "text": "ResolveResult" - }, - " | null" + "(context: ", + "Rule", + ".RuleContext) => ", + "ImportResolver" ], - "path": "packages/kbn-eslint-plugin-imports/src/resolve_kibana_import.ts", + "path": "packages/kbn-eslint-plugin-imports/src/get_import_resolver.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/eslint-plugin-imports", - "id": "def-server.resolveKibanaImport.$1", - "type": "string", - "tags": [], - "label": "req", - "description": [ - "Text from an import/require, like `../../src/core/public` or `@kbn/std`" - ], - "signature": [ - "string" - ], - "path": "packages/kbn-eslint-plugin-imports/src/resolve_kibana_import.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/eslint-plugin-imports", - "id": "def-server.resolveKibanaImport.$2", - "type": "string", + "id": "def-server.getImportResolver.$1", + "type": "Object", "tags": [], - "label": "dirname", - "description": [ - "The directory of the file where the req was found" - ], + "label": "context", + "description": [], "signature": [ - "string" + "Rule", + ".RuleContext" ], - "path": "packages/kbn-eslint-plugin-imports/src/resolve_kibana_import.ts", + "path": "packages/kbn-eslint-plugin-imports/src/get_import_resolver.ts", "deprecated": false, "isRequired": true } @@ -71,166 +49,9 @@ "initialIsOpen": false } ], - "interfaces": [ - { - "parentPluginId": "@kbn/eslint-plugin-imports", - "id": "def-server.FileResult", - "type": "Interface", - "tags": [], - "label": "FileResult", - "description": [ - "\nResolution result indicating that the import resolves to a specific file, possible in a nodeModule, with\nthe path of that file and the name of the nodeModule if applicable" - ], - "path": "packages/kbn-eslint-plugin-imports/src/resolve_result.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/eslint-plugin-imports", - "id": "def-server.FileResult.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"file\"" - ], - "path": "packages/kbn-eslint-plugin-imports/src/resolve_result.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/eslint-plugin-imports", - "id": "def-server.FileResult.absolute", - "type": "string", - "tags": [], - "label": "absolute", - "description": [], - "path": "packages/kbn-eslint-plugin-imports/src/resolve_result.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/eslint-plugin-imports", - "id": "def-server.FileResult.nodeModule", - "type": "string", - "tags": [], - "label": "nodeModule", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/kbn-eslint-plugin-imports/src/resolve_result.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/eslint-plugin-imports", - "id": "def-server.IgnoreResult", - "type": "Interface", - "tags": [], - "label": "IgnoreResult", - "description": [ - "\nResolution result indicating that the import request can't be resolved, but it shouldn't need to be\nbecause the file that is imported can't be resolved from the source alone, usually because it is explicitly\nimporting a build asset. Import requests which meet this criteria are manually added to the resolver and\ncan be trusted to exist after the build it complete or in their used location." - ], - "path": "packages/kbn-eslint-plugin-imports/src/resolve_result.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/eslint-plugin-imports", - "id": "def-server.IgnoreResult.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"ignore\"" - ], - "path": "packages/kbn-eslint-plugin-imports/src/resolve_result.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/eslint-plugin-imports", - "id": "def-server.TypesResult", - "type": "Interface", - "tags": [], - "label": "TypesResult", - "description": [ - "\nResolution result indicating that the import only resolves to an @types package, including the name of\nthe @types package. We don't validate the sub-path of these import strings and assume that TS will validate\nthem, we just need to know that they don't map to actual files on the filesystem or modules which will\nend up in the build or running code." - ], - "path": "packages/kbn-eslint-plugin-imports/src/resolve_result.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/eslint-plugin-imports", - "id": "def-server.TypesResult.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"@types\"" - ], - "path": "packages/kbn-eslint-plugin-imports/src/resolve_result.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/eslint-plugin-imports", - "id": "def-server.TypesResult.module", - "type": "string", - "tags": [], - "label": "module", - "description": [], - "path": "packages/kbn-eslint-plugin-imports/src/resolve_result.ts", - "deprecated": false - } - ], - "initialIsOpen": false - } - ], + "interfaces": [], "enums": [], - "misc": [ - { - "parentPluginId": "@kbn/eslint-plugin-imports", - "id": "def-server.ResolveResult", - "type": "Type", - "tags": [], - "label": "ResolveResult", - "description": [ - "\nPossible resolve result types" - ], - "signature": [ - { - "pluginId": "@kbn/eslint-plugin-imports", - "scope": "server", - "docId": "kibKbnEslintPluginImportsPluginApi", - "section": "def-server.IgnoreResult", - "text": "IgnoreResult" - }, - " | ", - { - "pluginId": "@kbn/eslint-plugin-imports", - "scope": "server", - "docId": "kibKbnEslintPluginImportsPluginApi", - "section": "def-server.TypesResult", - "text": "TypesResult" - }, - " | ", - { - "pluginId": "@kbn/eslint-plugin-imports", - "scope": "server", - "docId": "kibKbnEslintPluginImportsPluginApi", - "section": "def-server.FileResult", - "text": "FileResult" - } - ], - "path": "packages/kbn-eslint-plugin-imports/src/resolve_result.ts", - "deprecated": false, - "initialIsOpen": false - } - ], + "misc": [], "objects": [] }, "common": { diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index b1469acd5ce18..39fd23924e882 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/eslint-plugin-imports plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] warning: 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. --- @@ -18,16 +18,10 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 13 | 0 | 6 | 0 | +| 2 | 0 | 1 | 0 | ## Server ### Functions -### Interfaces - - -### Consts, variables and types - - diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index b100124a4b173..34d156709b95c 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/field-types plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] warning: 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. --- diff --git a/api_docs/kbn_find_used_node_modules.devdocs.json b/api_docs/kbn_find_used_node_modules.devdocs.json new file mode 100644 index 0000000000000..8635fa4c975ef --- /dev/null +++ b/api_docs/kbn_find_used_node_modules.devdocs.json @@ -0,0 +1,61 @@ +{ + "id": "@kbn/find-used-node-modules", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/find-used-node-modules", + "id": "def-server.findUsedNodeModules", + "type": "Function", + "tags": [], + "label": "findUsedNodeModules", + "description": [ + "\nParse a list of entry paths and find the node_modules which are required by them. If the\nentry path requires/imports a non-node_module then that file is scanned too, deeply, until\nall referenced files are scanned.\n\nOptionally, we can find the used peers of the used node_modules. This will keep track of all\nthe paths we use to enter a node_module and then traverse from those points, finding the\nused modules and comparing those to the `peerDependencies` listed in the node_module's package.json\nfile. If a used dependeny is in the `peerDependencies` and is used by the node_module it will\nbe included in the results.\n\nThis was implemented mostly for `@emotion/react` which is used by @elastic/eui but only listed\nas a peerDependency. If we didn't keep it in the Kibana package.json then the package would not\nbe installed and cause an error on startup because `@emotion/react` can't be found. We used to\nsolve this by scanning the node_modules directory for all the packages which are used but that\nwas much slower and lead to extra entries in package.json." + ], + "signature": [ + "(options: Options) => Promise" + ], + "path": "packages/kbn-find-used-node-modules/src/find_used_node_modules.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/find-used-node-modules", + "id": "def-server.findUsedNodeModules.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "Options" + ], + "path": "packages/kbn-find-used-node-modules/src/find_used_node_modules.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx new file mode 100644 index 0000000000000..95310ec27972b --- /dev/null +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -0,0 +1,27 @@ +--- +id: kibKbnFindUsedNodeModulesPluginApi +slug: /kibana-dev-docs/api/kbn-find-used-node-modules +title: "@kbn/find-used-node-modules" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/find-used-node-modules plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] +warning: 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. +--- +import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 2 | 0 | 0 | 0 | + +## Server + +### Functions + + diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index f678ebba804cc..891a77319d2c0 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/generate plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] warning: 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. --- diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index cadd77a894b98..f1c2d35173a65 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/i18n plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] warning: 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. --- diff --git a/api_docs/kbn_import_resolver.devdocs.json b/api_docs/kbn_import_resolver.devdocs.json new file mode 100644 index 0000000000000..50e7cdda82ff7 --- /dev/null +++ b/api_docs/kbn_import_resolver.devdocs.json @@ -0,0 +1,715 @@ +{ + "id": "@kbn/import-resolver", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver", + "type": "Class", + "tags": [], + "label": "ImportResolver", + "description": [], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [], + "signature": [ + "(repoRoot: string) => ", + { + "pluginId": "@kbn/import-resolver", + "scope": "server", + "docId": "kibKbnImportResolverPluginApi", + "section": "def-server.ImportResolver", + "text": "ImportResolver" + } + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.create.$1", + "type": "string", + "tags": [], + "label": "repoRoot", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.Unnamed.$1", + "type": "string", + "tags": [], + "label": "cwd", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.Unnamed.$2", + "type": "Object", + "tags": [], + "label": "pkgMap", + "description": [], + "signature": [ + "PackageMap" + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.Unnamed.$3", + "type": "Object", + "tags": [], + "label": "synthPkgMap", + "description": [], + "signature": [ + "PackageMap" + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.getPackageIdForPath", + "type": "Function", + "tags": [], + "label": "getPackageIdForPath", + "description": [], + "signature": [ + "(path: string) => string | null" + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.getPackageIdForPath.$1", + "type": "string", + "tags": [], + "label": "path", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.getAbsolutePackageDir", + "type": "Function", + "tags": [], + "label": "getAbsolutePackageDir", + "description": [], + "signature": [ + "(pkgId: string) => string | null" + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.getAbsolutePackageDir.$1", + "type": "string", + "tags": [], + "label": "pkgId", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.isBazelPackage", + "type": "Function", + "tags": [], + "label": "isBazelPackage", + "description": [], + "signature": [ + "(pkgId: string) => boolean" + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.isBazelPackage.$1", + "type": "string", + "tags": [], + "label": "pkgId", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.isSyntheticPackage", + "type": "Function", + "tags": [], + "label": "isSyntheticPackage", + "description": [], + "signature": [ + "(pkgId: string) => boolean" + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.isSyntheticPackage.$1", + "type": "string", + "tags": [], + "label": "pkgId", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.resolve", + "type": "Function", + "tags": [], + "label": "resolve", + "description": [ + "\nResolve an import request from a file in the given dirname" + ], + "signature": [ + "(req: string, dirname: string) => ", + { + "pluginId": "@kbn/import-resolver", + "scope": "server", + "docId": "kibKbnImportResolverPluginApi", + "section": "def-server.ResolveResult", + "text": "ResolveResult" + }, + " | null" + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.resolve.$1", + "type": "string", + "tags": [], + "label": "req", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportResolver.resolve.$2", + "type": "string", + "tags": [], + "label": "dirname", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-import-resolver/src/import_resolver.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.getPackageRelativeImportReq", + "type": "Function", + "tags": [], + "label": "getPackageRelativeImportReq", + "description": [], + "signature": [ + "(options: PackageRelativeImportReqOptions) => string" + ], + "path": "packages/kbn-import-resolver/src/helpers/import_req.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.getPackageRelativeImportReq.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "PackageRelativeImportReqOptions" + ], + "path": "packages/kbn-import-resolver/src/helpers/import_req.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.getRelativeImportReq", + "type": "Function", + "tags": [], + "label": "getRelativeImportReq", + "description": [], + "signature": [ + "(options: RelativeImportReqOptions) => string" + ], + "path": "packages/kbn-import-resolver/src/helpers/import_req.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.getRelativeImportReq.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "RelativeImportReqOptions" + ], + "path": "packages/kbn-import-resolver/src/helpers/import_req.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.reduceImportRequest", + "type": "Function", + "tags": [], + "label": "reduceImportRequest", + "description": [], + "signature": [ + "(req: string, type: ", + { + "pluginId": "@kbn/import-resolver", + "scope": "server", + "docId": "kibKbnImportResolverPluginApi", + "section": "def-server.ImportType", + "text": "ImportType" + }, + ", original: string | undefined) => string" + ], + "path": "packages/kbn-import-resolver/src/helpers/import_req.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.reduceImportRequest.$1", + "type": "string", + "tags": [], + "label": "req", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-import-resolver/src/helpers/import_req.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.reduceImportRequest.$2", + "type": "CompoundType", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/import-resolver", + "scope": "server", + "docId": "kibKbnImportResolverPluginApi", + "section": "def-server.ImportType", + "text": "ImportType" + } + ], + "path": "packages/kbn-import-resolver/src/helpers/import_req.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.reduceImportRequest.$3", + "type": "string", + "tags": [], + "label": "original", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-import-resolver/src/helpers/import_req.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.BuiltInResult", + "type": "Interface", + "tags": [], + "label": "BuiltInResult", + "description": [ + "\nResolution result indicating that the import request resolves to a built-in node library" + ], + "path": "packages/kbn-import-resolver/src/resolve_result.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.BuiltInResult.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"built-in\"" + ], + "path": "packages/kbn-import-resolver/src/resolve_result.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.FileResult", + "type": "Interface", + "tags": [], + "label": "FileResult", + "description": [ + "\nResolution result indicating that the import resolves to a specific file, possible in a nodeModule, with\nthe path of that file and the name of the nodeModule if applicable" + ], + "path": "packages/kbn-import-resolver/src/resolve_result.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.FileResult.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"file\"" + ], + "path": "packages/kbn-import-resolver/src/resolve_result.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.FileResult.absolute", + "type": "string", + "tags": [], + "label": "absolute", + "description": [], + "path": "packages/kbn-import-resolver/src/resolve_result.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.FileResult.nodeModule", + "type": "string", + "tags": [], + "label": "nodeModule", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-import-resolver/src/resolve_result.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.FileResult.prefix", + "type": "string", + "tags": [], + "label": "prefix", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-import-resolver/src/resolve_result.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.FileResult.postfix", + "type": "string", + "tags": [], + "label": "postfix", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-import-resolver/src/resolve_result.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.IgnoreResult", + "type": "Interface", + "tags": [], + "label": "IgnoreResult", + "description": [ + "\nResolution result indicating that the import request can't be resolved, but it shouldn't need to be\nbecause the file that is imported can't be resolved from the source alone, usually because it is explicitly\nimporting a build asset. Import requests which meet this criteria are manually added to the resolver and\ncan be trusted to exist after the build it complete or in their used location." + ], + "path": "packages/kbn-import-resolver/src/resolve_result.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.IgnoreResult.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"ignore\"" + ], + "path": "packages/kbn-import-resolver/src/resolve_result.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.OptionalDepResult", + "type": "Interface", + "tags": [], + "label": "OptionalDepResult", + "description": [ + "\nResolution result indicating that the import request resolves to an npm dep which isn't\ncurrently installed so assumed to be optional" + ], + "path": "packages/kbn-import-resolver/src/resolve_result.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.OptionalDepResult.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"optional-and-missing\"" + ], + "path": "packages/kbn-import-resolver/src/resolve_result.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.TypesResult", + "type": "Interface", + "tags": [], + "label": "TypesResult", + "description": [ + "\nResolution result indicating that the import only resolves to an @types package, including the name of\nthe @types package. We don't validate the sub-path of these import strings and assume that TS will validate\nthem, we just need to know that they don't map to actual files on the filesystem or modules which will\nend up in the build or running code." + ], + "path": "packages/kbn-import-resolver/src/resolve_result.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.TypesResult.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"@types\"" + ], + "path": "packages/kbn-import-resolver/src/resolve_result.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.TypesResult.module", + "type": "string", + "tags": [], + "label": "module", + "description": [], + "path": "packages/kbn-import-resolver/src/resolve_result.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ImportType", + "type": "Type", + "tags": [], + "label": "ImportType", + "description": [], + "signature": [ + "\"esm\" | \"require\" | \"require-resolve\" | \"jest\"" + ], + "path": "packages/kbn-import-resolver/src/helpers/import_req.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/import-resolver", + "id": "def-server.ResolveResult", + "type": "Type", + "tags": [], + "label": "ResolveResult", + "description": [ + "\nPossible resolve result types" + ], + "signature": [ + { + "pluginId": "@kbn/import-resolver", + "scope": "server", + "docId": "kibKbnImportResolverPluginApi", + "section": "def-server.BuiltInResult", + "text": "BuiltInResult" + }, + " | ", + { + "pluginId": "@kbn/import-resolver", + "scope": "server", + "docId": "kibKbnImportResolverPluginApi", + "section": "def-server.IgnoreResult", + "text": "IgnoreResult" + }, + " | ", + { + "pluginId": "@kbn/import-resolver", + "scope": "server", + "docId": "kibKbnImportResolverPluginApi", + "section": "def-server.TypesResult", + "text": "TypesResult" + }, + " | ", + { + "pluginId": "@kbn/import-resolver", + "scope": "server", + "docId": "kibKbnImportResolverPluginApi", + "section": "def-server.FileResult", + "text": "FileResult" + }, + " | ", + { + "pluginId": "@kbn/import-resolver", + "scope": "server", + "docId": "kibKbnImportResolverPluginApi", + "section": "def-server.OptionalDepResult", + "text": "OptionalDepResult" + } + ], + "path": "packages/kbn-import-resolver/src/resolve_result.ts", + "deprecated": false, + "initialIsOpen": false + } + ], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx new file mode 100644 index 0000000000000..ea7b7ff9aba1d --- /dev/null +++ b/api_docs/kbn_import_resolver.mdx @@ -0,0 +1,36 @@ +--- +id: kibKbnImportResolverPluginApi +slug: /kibana-dev-docs/api/kbn-import-resolver +title: "@kbn/import-resolver" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/import-resolver plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] +warning: 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. +--- +import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 43 | 0 | 36 | 0 | + +## Server + +### Functions + + +### Classes + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index d07e5a0a44859..7a6b18efd63c5 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/interpreter plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] warning: 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. --- diff --git a/api_docs/kbn_io_ts_utils.devdocs.json b/api_docs/kbn_io_ts_utils.devdocs.json index 6a4ae05e82231..0eac86c5269a4 100644 --- a/api_docs/kbn_io_ts_utils.devdocs.json +++ b/api_docs/kbn_io_ts_utils.devdocs.json @@ -59,10 +59,10 @@ " | ", "StringType", " | ", - "NumberType", - " | ", "BooleanType", " | ", + "NumberType", + " | ", "RecordC", "<", "Mixed", diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 2e6530549dee3..0890c33d45907 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/io-ts-utils plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] warning: 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. --- diff --git a/api_docs/kbn_jest_serializers.devdocs.json b/api_docs/kbn_jest_serializers.devdocs.json new file mode 100644 index 0000000000000..f4595b16f9025 --- /dev/null +++ b/api_docs/kbn_jest_serializers.devdocs.json @@ -0,0 +1,224 @@ +{ + "id": "@kbn/jest-serializers", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/jest-serializers", + "id": "def-server.createAbsolutePathSerializer", + "type": "Function", + "tags": [], + "label": "createAbsolutePathSerializer", + "description": [], + "signature": [ + "(rootPath: string, replacement: string) => { test: (value: any) => boolean; serialize: (value: string) => string; }" + ], + "path": "packages/kbn-jest-serializers/src/absolute_path_serializer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/jest-serializers", + "id": "def-server.createAbsolutePathSerializer.$1", + "type": "string", + "tags": [], + "label": "rootPath", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-jest-serializers/src/absolute_path_serializer.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/jest-serializers", + "id": "def-server.createAbsolutePathSerializer.$2", + "type": "string", + "tags": [], + "label": "replacement", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-jest-serializers/src/absolute_path_serializer.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/jest-serializers", + "id": "def-server.createAnyInstanceSerializer", + "type": "Function", + "tags": [], + "label": "createAnyInstanceSerializer", + "description": [], + "signature": [ + "(Class: Function, name: string | ((instance: any) => string) | undefined) => { test: (v: any) => boolean; serialize: (v: any) => string; }" + ], + "path": "packages/kbn-jest-serializers/src/any_instance_serizlizer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/jest-serializers", + "id": "def-server.createAnyInstanceSerializer.$1", + "type": "Object", + "tags": [], + "label": "Class", + "description": [], + "signature": [ + "Function" + ], + "path": "packages/kbn-jest-serializers/src/any_instance_serizlizer.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/jest-serializers", + "id": "def-server.createAnyInstanceSerializer.$2", + "type": "CompoundType", + "tags": [], + "label": "name", + "description": [], + "signature": [ + "string | ((instance: any) => string) | undefined" + ], + "path": "packages/kbn-jest-serializers/src/any_instance_serizlizer.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/jest-serializers", + "id": "def-server.createRecursiveSerializer", + "type": "Function", + "tags": [], + "label": "createRecursiveSerializer", + "description": [], + "signature": [ + "(test: (v: any) => boolean, print: (v: any, printRaw: (v: string) => RawPrint) => string | RawPrint) => { test: (v: any) => boolean; serialize: (v: any, ...rest: any[]) => any; }" + ], + "path": "packages/kbn-jest-serializers/src/recursive_serializer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/jest-serializers", + "id": "def-server.createRecursiveSerializer.$1", + "type": "Function", + "tags": [], + "label": "test", + "description": [], + "signature": [ + "(v: any) => boolean" + ], + "path": "packages/kbn-jest-serializers/src/recursive_serializer.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/jest-serializers", + "id": "def-server.createRecursiveSerializer.$2", + "type": "Function", + "tags": [], + "label": "print", + "description": [], + "signature": [ + "(v: any, printRaw: (v: string) => RawPrint) => string | RawPrint" + ], + "path": "packages/kbn-jest-serializers/src/recursive_serializer.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/jest-serializers", + "id": "def-server.createReplaceSerializer", + "type": "Function", + "tags": [], + "label": "createReplaceSerializer", + "description": [], + "signature": [ + "(toReplace: string | RegExp, replaceWith: string | Replacer) => { test: (v: any) => boolean; serialize: (v: any, ...rest: any[]) => any; }" + ], + "path": "packages/kbn-jest-serializers/src/replace_serializer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/jest-serializers", + "id": "def-server.createReplaceSerializer.$1", + "type": "CompoundType", + "tags": [], + "label": "toReplace", + "description": [], + "signature": [ + "string | RegExp" + ], + "path": "packages/kbn-jest-serializers/src/replace_serializer.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/jest-serializers", + "id": "def-server.createReplaceSerializer.$2", + "type": "CompoundType", + "tags": [], + "label": "replaceWith", + "description": [], + "signature": [ + "string | Replacer" + ], + "path": "packages/kbn-jest-serializers/src/replace_serializer.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/jest-serializers", + "id": "def-server.createStripAnsiSerializer", + "type": "Function", + "tags": [], + "label": "createStripAnsiSerializer", + "description": [], + "signature": [ + "() => { test: (v: any) => boolean; serialize: (v: any, ...rest: any[]) => any; }" + ], + "path": "packages/kbn-jest-serializers/src/strip_ansi_serializer.ts", + "deprecated": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx new file mode 100644 index 0000000000000..f9e1f71dc7804 --- /dev/null +++ b/api_docs/kbn_jest_serializers.mdx @@ -0,0 +1,27 @@ +--- +id: kibKbnJestSerializersPluginApi +slug: /kibana-dev-docs/api/kbn-jest-serializers +title: "@kbn/jest-serializers" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/jest-serializers plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] +warning: 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. +--- +import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 13 | 0 | 13 | 0 | + +## Server + +### Functions + + diff --git a/api_docs/kbn_kibana_json_schema.devdocs.json b/api_docs/kbn_kibana_json_schema.devdocs.json new file mode 100644 index 0000000000000..0cc8a1e25feb5 --- /dev/null +++ b/api_docs/kbn_kibana_json_schema.devdocs.json @@ -0,0 +1,844 @@ +{ + "id": "@kbn/kibana-json-schema", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema", + "type": "Object", + "tags": [], + "label": "KibanaJsonSchema", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"object\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.required", + "type": "Array", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties", + "type": "Object", + "tags": [], + "label": "properties", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.id", + "type": "Object", + "tags": [], + "label": "id", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.id.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.id.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"string\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.id.pattern", + "type": "string", + "tags": [], + "label": "pattern", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.version", + "type": "Object", + "tags": [], + "label": "version", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.version.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.version.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"string\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.version.pattern", + "type": "string", + "tags": [], + "label": "pattern", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.kibanaVersion", + "type": "Object", + "tags": [], + "label": "kibanaVersion", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.kibanaVersion.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.kibanaVersion.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"string\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.kibanaVersion.pattern", + "type": "string", + "tags": [], + "label": "pattern", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.type", + "type": "Object", + "tags": [], + "label": "type", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.type.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.type.enum", + "type": "Array", + "tags": [], + "label": "enum", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.configPath", + "type": "Object", + "tags": [], + "label": "configPath", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.configPath.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.configPath.oneOf", + "type": "Array", + "tags": [], + "label": "oneOf", + "description": [], + "signature": [ + "({ type: \"string\"; } | { type: \"array\"; items: { type: \"string\"; }; })[]" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.requiredPlugins", + "type": "Object", + "tags": [], + "label": "requiredPlugins", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.requiredPlugins.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.requiredPlugins.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"array\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.requiredPlugins.items", + "type": "Object", + "tags": [], + "label": "items", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.requiredPlugins.items.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"string\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.optionalPlugins", + "type": "Object", + "tags": [], + "label": "optionalPlugins", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.optionalPlugins.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.optionalPlugins.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"array\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.optionalPlugins.items", + "type": "Object", + "tags": [], + "label": "items", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.optionalPlugins.items.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"string\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.requiredBundles", + "type": "Object", + "tags": [], + "label": "requiredBundles", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.requiredBundles.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.requiredBundles.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"array\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.requiredBundles.items", + "type": "Object", + "tags": [], + "label": "items", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.requiredBundles.items.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"string\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.ui", + "type": "Object", + "tags": [], + "label": "ui", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.ui.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.ui.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"boolean\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.server", + "type": "Object", + "tags": [], + "label": "server", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.server.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.server.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"boolean\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.extraPublicDirs", + "type": "Object", + "tags": [], + "label": "extraPublicDirs", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.extraPublicDirs.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.extraPublicDirs.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"array\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.extraPublicDirs.items", + "type": "Object", + "tags": [], + "label": "items", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.extraPublicDirs.items.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"string\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.serviceFolders", + "type": "Object", + "tags": [], + "label": "serviceFolders", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.serviceFolders.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.serviceFolders.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"array\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.serviceFolders.items", + "type": "Object", + "tags": [], + "label": "items", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.serviceFolders.items.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"string\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.owner", + "type": "Object", + "tags": [], + "label": "owner", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.owner.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"object\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.owner.required", + "type": "Array", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.owner.properties", + "type": "Object", + "tags": [], + "label": "properties", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.owner.properties.name", + "type": "Object", + "tags": [], + "label": "name", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.owner.properties.name.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.owner.properties.name.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"string\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.owner.properties.githubTeam", + "type": "Object", + "tags": [], + "label": "githubTeam", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.owner.properties.githubTeam.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.owner.properties.githubTeam.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"string\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + } + ] + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.description", + "type": "Object", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.description.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.description.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"string\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.enabledOnAnonymousPages", + "type": "Object", + "tags": [], + "label": "enabledOnAnonymousPages", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.enabledOnAnonymousPages.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/kibana-json-schema", + "id": "def-server.KibanaJsonSchema.properties.enabledOnAnonymousPages.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"boolean\"" + ], + "path": "packages/kbn-kibana-json-schema/src/kibana_json_schema.ts", + "deprecated": false + } + ] + } + ] + } + ], + "initialIsOpen": false + } + ] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_kibana_json_schema.mdx b/api_docs/kbn_kibana_json_schema.mdx new file mode 100644 index 0000000000000..16776b0a20e55 --- /dev/null +++ b/api_docs/kbn_kibana_json_schema.mdx @@ -0,0 +1,27 @@ +--- +id: kibKbnKibanaJsonSchemaPluginApi +slug: /kibana-dev-docs/api/kbn-kibana-json-schema +title: "@kbn/kibana-json-schema" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/kibana-json-schema plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-json-schema'] +warning: 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. +--- +import kbnKibanaJsonSchemaObj from './kbn_kibana_json_schema.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 69 | 0 | 69 | 0 | + +## Server + +### Objects + + diff --git a/api_docs/kbn_logging.devdocs.json b/api_docs/kbn_logging.devdocs.json index f9bca4daaf11f..00bfe3a5f742b 100644 --- a/api_docs/kbn_logging.devdocs.json +++ b/api_docs/kbn_logging.devdocs.json @@ -644,7 +644,7 @@ "label": "EcsEventOutcome", "description": [], "signature": [ - "\"unknown\" | \"success\" | \"failure\"" + "\"success\" | \"unknown\" | \"failure\"" ], "path": "packages/kbn-logging/src/ecs/event.ts", "deprecated": false, @@ -658,7 +658,7 @@ "label": "EcsEventType", "description": [], "signature": [ - "\"start\" | \"user\" | \"error\" | \"info\" | \"group\" | \"end\" | \"protocol\" | \"connection\" | \"access\" | \"admin\" | \"allowed\" | \"change\" | \"creation\" | \"deletion\" | \"denied\" | \"installation\"" + "\"start\" | \"error\" | \"user\" | \"info\" | \"group\" | \"end\" | \"admin\" | \"protocol\" | \"connection\" | \"access\" | \"allowed\" | \"change\" | \"creation\" | \"deletion\" | \"denied\" | \"installation\"" ], "path": "packages/kbn-logging/src/ecs/event.ts", "deprecated": false, diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 071f96e712bb7..d1bd851ab5758 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/logging plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] warning: 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. --- diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index 3eee38ebb640b..490414113832e 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/logging-mocks plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] warning: 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. --- diff --git a/api_docs/kbn_mapbox_gl.devdocs.json b/api_docs/kbn_mapbox_gl.devdocs.json index e7df5d6118ad9..845de56441c91 100644 --- a/api_docs/kbn_mapbox_gl.devdocs.json +++ b/api_docs/kbn_mapbox_gl.devdocs.json @@ -12,77 +12,276 @@ "classes": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLat", + "id": "def-server.GeoJSONSource", "type": "Class", "tags": [], - "label": "LngLat", - "description": [ - "\nLngLat" - ], + "label": "GeoJSONSource", + "description": [], "signature": [ - "maplibregl.LngLat" + "maplibregl.GeoJSONSource extends maplibregl.Evented implements maplibregl.Source" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLat.lng", + "id": "def-server.GeoJSONSource.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"geojson\"" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.minzoom", "type": "number", "tags": [], - "label": "lng", + "label": "minzoom", "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLat.lat", + "id": "def-server.GeoJSONSource.maxzoom", "type": "number", "tags": [], - "label": "lat", + "label": "maxzoom", "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLat.Unnamed", - "type": "Function", + "id": "def-server.GeoJSONSource.tileSize", + "type": "number", + "tags": [], + "label": "tileSize", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.attribution", + "type": "string", + "tags": [], + "label": "attribution", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.promoteId", + "type": "CompoundType", + "tags": [], + "label": "promoteId", + "description": [], + "signature": [ + "string | { [_: string]: string; }" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.isTileClipped", + "type": "boolean", + "tags": [], + "label": "isTileClipped", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.reparseOverscaled", + "type": "boolean", + "tags": [], + "label": "reparseOverscaled", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource._data", + "type": "CompoundType", + "tags": [], + "label": "_data", + "description": [], + "signature": [ + "string | GeoJSON.GeoJSON" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource._options", + "type": "Any", + "tags": [], + "label": "_options", + "description": [], + "signature": [ + "any" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.workerOptions", + "type": "Any", + "tags": [], + "label": "workerOptions", + "description": [], + "signature": [ + "any" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.map", + "type": "Object", + "tags": [], + "label": "map", + "description": [], + "signature": [ + "maplibregl.Map" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.actor", + "type": "Object", + "tags": [], + "label": "actor", + "description": [], + "signature": [ + "maplibregl.Actor" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource._pendingLoads", + "type": "number", + "tags": [], + "label": "_pendingLoads", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource._collectResourceTiming", + "type": "boolean", + "tags": [], + "label": "_collectResourceTiming", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource._removed", + "type": "boolean", "tags": [], + "label": "_removed", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.Unnamed", + "type": "Function", + "tags": [ + "private" + ], "label": "Constructor", "description": [], "signature": [ "any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLat.Unnamed.$1", - "type": "number", + "id": "def-server.GeoJSONSource.Unnamed.$1", + "type": "string", "tags": [], - "label": "lng", + "label": "id", "description": [], "signature": [ - "number" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLat.Unnamed.$2", - "type": "number", + "id": "def-server.GeoJSONSource.Unnamed.$2", + "type": "CompoundType", "tags": [], - "label": "lat", + "label": "options", "description": [], "signature": [ - "number" + "maplibregl.GeoJSONSourceOptions" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.Unnamed.$3", + "type": "Object", + "tags": [], + "label": "dispatcher", + "description": [], + "signature": [ + "maplibregl.Dispatcher" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.Unnamed.$4", + "type": "Object", + "tags": [], + "label": "eventedParent", + "description": [], + "signature": [ + "maplibregl.Evented" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } @@ -91,81 +290,43 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLat.wrap", - "type": "Function", - "tags": [], - "label": "wrap", - "description": [ - "Return a new LngLat object whose longitude is wrapped to the range (-180, 180)." - ], - "signature": [ - "() => maplibregl.LngLat" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLat.toArray", - "type": "Function", - "tags": [], - "label": "toArray", - "description": [ - "Return a LngLat as an array" - ], - "signature": [ - "() => number[]" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLat.toString", + "id": "def-server.GeoJSONSource.load", "type": "Function", "tags": [], - "label": "toString", - "description": [ - "Return a LngLat as a string" - ], + "label": "load", + "description": [], "signature": [ - "() => string" + "() => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLat.distanceTo", + "id": "def-server.GeoJSONSource.onAdd", "type": "Function", "tags": [], - "label": "distanceTo", - "description": [ - "Returns the approximate distance between a pair of coordinates in meters\nUses the Haversine Formula (from R.W. Sinnott, \"Virtues of the Haversine\", Sky and Telescope, vol. 68, no. 2, 1984, p. 159)" - ], + "label": "onAdd", + "description": [], "signature": [ - "(lngLat: maplibregl.LngLat) => number" + "(map: maplibregl.Map) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLat.distanceTo.$1", + "id": "def-server.GeoJSONSource.onAdd.$1", "type": "Object", "tags": [], - "label": "lngLat", + "label": "map", "description": [], "signature": [ - "maplibregl.LngLat" + "maplibregl.Map" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } @@ -174,206 +335,252 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLat.toBounds", + "id": "def-server.GeoJSONSource.setData", "type": "Function", "tags": [], - "label": "toBounds", - "description": [], + "label": "setData", + "description": [ + "\nSets the GeoJSON data and re-renders the map.\n" + ], "signature": [ - "(radius: number) => maplibregl.LngLatBounds" + "(data: string | GeoJSON.GeoJSON) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLat.toBounds.$1", - "type": "number", + "id": "def-server.GeoJSONSource.setData.$1", + "type": "CompoundType", "tags": [], - "label": "radius", - "description": [], + "label": "data", + "description": [ + "A GeoJSON data object or a URL to one. The latter is preferable in the case of large GeoJSON files." + ], "signature": [ - "number" + "string | GeoJSON.GeoJSON" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "this" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLat.convert", + "id": "def-server.GeoJSONSource.getClusterExpansionZoom", "type": "Function", "tags": [], - "label": "convert", - "description": [], + "label": "getClusterExpansionZoom", + "description": [ + "\nFor clustered sources, fetches the zoom at which the given cluster expands.\n" + ], "signature": [ - "(input: maplibregl.LngLatLike) => maplibregl.LngLat" + "(clusterId: number, callback: maplibregl.Callback) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLat.convert.$1", - "type": "CompoundType", + "id": "def-server.GeoJSONSource.getClusterExpansionZoom.$1", + "type": "number", "tags": [], - "label": "input", - "description": [], + "label": "clusterId", + "description": [ + "The value of the cluster's `cluster_id` property." + ], "signature": [ - "maplibregl.LngLatLike" + "number" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.getClusterExpansionZoom.$2", + "type": "Function", + "tags": [], + "label": "callback", + "description": [ + "A callback to be called when the zoom value is retrieved (`(error, zoom) => { ... }`)." + ], + "signature": [ + "maplibregl.Callback" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } ], - "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds", - "type": "Class", - "tags": [], - "label": "LngLatBounds", - "description": [ - "\nLngLatBounds" - ], - "signature": [ - "maplibregl.LngLatBounds" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.sw", - "type": "CompoundType", - "tags": [], - "label": "sw", - "description": [], - "signature": [ - "[number, number] | maplibregl.LngLat | { lng: number; lat: number; } | { lon: number; lat: number; }" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.ne", - "type": "CompoundType", - "tags": [], - "label": "ne", - "description": [], - "signature": [ - "[number, number] | maplibregl.LngLat | { lng: number; lat: number; } | { lon: number; lat: number; }" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "returnComment": [ + "this" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.Unnamed", + "id": "def-server.GeoJSONSource.getClusterChildren", "type": "Function", "tags": [], - "label": "Constructor", - "description": [], + "label": "getClusterChildren", + "description": [ + "\nFor clustered sources, fetches the children of the given cluster on the next zoom level (as an array of GeoJSON features).\n" + ], "signature": [ - "any" + "(clusterId: number, callback: maplibregl.Callback[]>) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.Unnamed.$1", - "type": "CompoundType", + "id": "def-server.GeoJSONSource.getClusterChildren.$1", + "type": "number", "tags": [], - "label": "boundsLike", - "description": [], + "label": "clusterId", + "description": [ + "The value of the cluster's `cluster_id` property." + ], "signature": [ - "[number, number, number, number] | [maplibregl.LngLatLike, maplibregl.LngLatLike] | undefined" + "number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.getClusterChildren.$2", + "type": "Function", + "tags": [], + "label": "callback", + "description": [ + "A callback to be called when the features are retrieved (`(error, features) => { ... }`)." + ], + "signature": [ + "maplibregl.Callback[]>" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "this" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.Unnamed", + "id": "def-server.GeoJSONSource.getClusterLeaves", "type": "Function", "tags": [], - "label": "Constructor", - "description": [], + "label": "getClusterLeaves", + "description": [ + "\nFor clustered sources, fetches the original points that belong to the cluster (as an array of GeoJSON features).\n" + ], "signature": [ - "any" + "(clusterId: number, limit: number, offset: number, callback: maplibregl.Callback[]>) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.Unnamed.$1", - "type": "CompoundType", + "id": "def-server.GeoJSONSource.getClusterLeaves.$1", + "type": "number", "tags": [], - "label": "sw", - "description": [], + "label": "clusterId", + "description": [ + "The value of the cluster's `cluster_id` property." + ], "signature": [ - "maplibregl.LngLatLike" + "number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.Unnamed.$2", - "type": "CompoundType", + "id": "def-server.GeoJSONSource.getClusterLeaves.$2", + "type": "number", "tags": [], - "label": "ne", - "description": [], + "label": "limit", + "description": [ + "The maximum number of features to return." + ], "signature": [ - "maplibregl.LngLatLike" + "number" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.getClusterLeaves.$3", + "type": "number", + "tags": [], + "label": "offset", + "description": [ + "The number of features to skip (e.g. for pagination)." + ], + "signature": [ + "number" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.getClusterLeaves.$4", + "type": "Function", + "tags": [], + "label": "callback", + "description": [ + "A callback to be called when the features are retrieved (`(error, features) => { ... }`)." + ], + "signature": [ + "maplibregl.Callback[]>" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "this" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.setNorthEast", + "id": "def-server.GeoJSONSource._updateWorkerData", "type": "Function", "tags": [], - "label": "setNorthEast", + "label": "_updateWorkerData", "description": [], "signature": [ - "(ne: maplibregl.LngLatLike) => this" + "(sourceDataType: maplibregl.MapSourceDataType) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.setNorthEast.$1", + "id": "def-server.GeoJSONSource._updateWorkerData.$1", "type": "CompoundType", "tags": [], - "label": "ne", + "label": "sourceDataType", "description": [], "signature": [ - "maplibregl.LngLatLike" + "maplibregl.MapSourceDataType" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } @@ -382,28 +589,57 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.setSouthWest", + "id": "def-server.GeoJSONSource.loaded", "type": "Function", "tags": [], - "label": "setSouthWest", + "label": "loaded", "description": [], "signature": [ - "(sw: maplibregl.LngLatLike) => this" + "() => boolean" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.loadTile", + "type": "Function", + "tags": [], + "label": "loadTile", + "description": [], + "signature": [ + "(tile: maplibregl.Tile, callback: maplibregl.Callback) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.setSouthWest.$1", - "type": "CompoundType", + "id": "def-server.GeoJSONSource.loadTile.$1", + "type": "Object", "tags": [], - "label": "sw", + "label": "tile", "description": [], "signature": [ - "maplibregl.LngLatLike" + "maplibregl.Tile" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.GeoJSONSource.loadTile.$2", + "type": "Function", + "tags": [], + "label": "callback", + "description": [], + "signature": [ + "maplibregl.Callback" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } @@ -412,30 +648,28 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.contains", + "id": "def-server.GeoJSONSource.abortTile", "type": "Function", "tags": [], - "label": "contains", - "description": [ - "Check if the point is within the bounding box." - ], + "label": "abortTile", + "description": [], "signature": [ - "(lnglat: maplibregl.LngLatLike) => boolean" + "(tile: maplibregl.Tile) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.contains.$1", - "type": "CompoundType", + "id": "def-server.GeoJSONSource.abortTile.$1", + "type": "Object", "tags": [], - "label": "lnglat", + "label": "tile", "description": [], "signature": [ - "maplibregl.LngLatLike" + "maplibregl.Tile" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } @@ -444,30 +678,28 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.extend", + "id": "def-server.GeoJSONSource.unloadTile", "type": "Function", "tags": [], - "label": "extend", - "description": [ - "Extend the bounds to include a given LngLat or LngLatBounds." - ], + "label": "unloadTile", + "description": [], "signature": [ - "(obj: [number, number] | [number, number, number, number] | maplibregl.LngLatBounds | [maplibregl.LngLatLike, maplibregl.LngLatLike] | maplibregl.LngLat | { lng: number; lat: number; } | { lon: number; lat: number; }) => this" + "(tile: maplibregl.Tile) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.extend.$1", - "type": "CompoundType", + "id": "def-server.GeoJSONSource.unloadTile.$1", + "type": "Object", "tags": [], - "label": "obj", + "label": "tile", "description": [], "signature": [ - "[number, number] | [number, number, number, number] | maplibregl.LngLatBounds | [maplibregl.LngLatLike, maplibregl.LngLatLike] | maplibregl.LngLat | { lng: number; lat: number; } | { lon: number; lat: number; }" + "maplibregl.Tile" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } @@ -476,1942 +708,2184 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.getCenter", + "id": "def-server.GeoJSONSource.onRemove", "type": "Function", "tags": [], - "label": "getCenter", - "description": [ - "Get the point equidistant from this box's corners" - ], + "label": "onRemove", + "description": [], "signature": [ - "() => maplibregl.LngLat" + "() => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.getSouthWest", + "id": "def-server.GeoJSONSource.serialize", "type": "Function", "tags": [], - "label": "getSouthWest", - "description": [ - "Get southwest corner" - ], + "label": "serialize", + "description": [], "signature": [ - "() => maplibregl.LngLat" + "() => any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.getNorthEast", + "id": "def-server.GeoJSONSource.hasTransition", "type": "Function", "tags": [], - "label": "getNorthEast", - "description": [ - "Get northeast corner" - ], + "label": "hasTransition", + "description": [], "signature": [ - "() => maplibregl.LngLat" + "() => boolean" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], "returnComment": [] - }, + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.LngLat", + "type": "Class", + "tags": [], + "label": "LngLat", + "description": [], + "signature": [ + "maplibregl.LngLat" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.getNorthWest", - "type": "Function", + "id": "def-server.LngLat.lng", + "type": "number", "tags": [], - "label": "getNorthWest", - "description": [ - "Get northwest corner" - ], - "signature": [ - "() => maplibregl.LngLat" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] + "label": "lng", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.getSouthEast", - "type": "Function", + "id": "def-server.LngLat.lat", + "type": "number", "tags": [], - "label": "getSouthEast", - "description": [ - "Get southeast corner" - ], - "signature": [ - "() => maplibregl.LngLat" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] + "label": "lat", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.getWest", + "id": "def-server.LngLat.Unnamed", "type": "Function", "tags": [], - "label": "getWest", - "description": [ - "Get west edge longitude" - ], + "label": "Constructor", + "description": [], "signature": [ - "() => number" + "any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.getSouth", - "type": "Function", - "tags": [], - "label": "getSouth", - "description": [ - "Get south edge latitude" - ], - "signature": [ - "() => number" + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.LngLat.Unnamed.$1", + "type": "number", + "tags": [], + "label": "lng", + "description": [], + "signature": [ + "number" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.LngLat.Unnamed.$2", + "type": "number", + "tags": [], + "label": "lat", + "description": [], + "signature": [ + "number" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.getEast", + "id": "def-server.LngLat.wrap", "type": "Function", "tags": [], - "label": "getEast", + "label": "wrap", "description": [ - "Get east edge longitude" + "\nReturns a new `LngLat` object whose longitude is wrapped to the range (-180, 180).\n" ], "signature": [ - "() => number" + "() => maplibregl.LngLat" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The wrapped `LngLat` object." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.getNorth", + "id": "def-server.LngLat.toArray", "type": "Function", "tags": [], - "label": "getNorth", + "label": "toArray", "description": [ - "Get north edge latitude" + "\nReturns the coordinates represented as an array of two numbers.\n" ], "signature": [ - "() => number" + "() => number[]" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The coordinates represeted as an array of longitude and latitude." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.toArray", + "id": "def-server.LngLat.toString", "type": "Function", "tags": [], - "label": "toArray", + "label": "toString", "description": [ - "Returns a LngLatBounds as an array" + "\nReturns the coordinates represent as a string.\n" ], "signature": [ - "() => number[][]" + "() => string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The coordinates represented as a string of the format `'LngLat(lng, lat)'`." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.toString", + "id": "def-server.LngLat.distanceTo", "type": "Function", "tags": [], - "label": "toString", + "label": "distanceTo", "description": [ - "Return a LngLatBounds as a string" + "\nReturns the approximate distance between a pair of coordinates in meters\nUses the Haversine Formula (from R.W. Sinnott, \"Virtues of the Haversine\", Sky and Telescope, vol. 68, no. 2, 1984, p. 159)\n" ], "signature": [ - "() => string" + "(lngLat: maplibregl.LngLat) => number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "children": [], - "returnComment": [] + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.LngLat.distanceTo.$1", + "type": "Object", + "tags": [], + "label": "lngLat", + "description": [ + "coordinates to compute the distance to" + ], + "signature": [ + "maplibregl.LngLat" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [ + "Distance in meters between the two coordinates." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.isEmpty", + "id": "def-server.LngLat.toBounds", "type": "Function", "tags": [], - "label": "isEmpty", + "label": "toBounds", "description": [ - "Returns a boolean" + "\nReturns a `LngLatBounds` from the coordinates extended by a given `radius`. The returned `LngLatBounds` completely contains the `radius`.\n" ], "signature": [ - "() => boolean" + "(radius?: number | undefined) => maplibregl.LngLatBounds" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "children": [], - "returnComment": [] + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.LngLat.toBounds.$1", + "type": "number", + "tags": [], + "label": "radius", + "description": [ + "Distance in meters from the coordinates to extend the bounds." + ], + "signature": [ + "number | undefined" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [ + "A new `LngLatBounds` object representing the coordinates extended by the `radius`." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.convert", + "id": "def-server.LngLat.convert", "type": "Function", "tags": [], "label": "convert", "description": [ - "Convert an array to a LngLatBounds object, or return an existing LngLatBounds object unchanged." + "\nConverts an array of two numbers or an object with `lng` and `lat` or `lon` and `lat` properties\nto a `LngLat` object.\n\nIf a `LngLat` object is passed in, the function returns it unchanged.\n" ], "signature": [ - "(input: maplibregl.LngLatBoundsLike) => maplibregl.LngLatBounds" + "(input: maplibregl.LngLatLike) => maplibregl.LngLat" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.LngLatBounds.convert.$1", + "id": "def-server.LngLat.convert.$1", "type": "CompoundType", "tags": [], "label": "input", - "description": [], + "description": [ + "An array of two numbers or object to convert, or a `LngLat` object to return." + ], "signature": [ - "maplibregl.LngLatBoundsLike" + "maplibregl.LngLatLike" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "A new `LngLat` object, if a conversion occurred, or the original `LngLat` object." + ] } ], "initialIsOpen": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map", + "id": "def-server.LngLatBounds", "type": "Class", "tags": [], - "label": "Map", - "description": [ - "\nMap" - ], + "label": "LngLatBounds", + "description": [], "signature": [ - "maplibregl.Map extends maplibregl.Evented" + "maplibregl.LngLatBounds" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.Unnamed", - "type": "Function", + "id": "def-server.LngLatBounds._ne", + "type": "Object", "tags": [], - "label": "Constructor", + "label": "_ne", "description": [], "signature": [ - "any" + "maplibregl.LngLat" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.Unnamed.$1", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "signature": [ - "maplibregl.MapboxOptions | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - } + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.LngLatBounds._sw", + "type": "Object", + "tags": [], + "label": "_sw", + "description": [], + "signature": [ + "maplibregl.LngLat" ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.addControl", + "id": "def-server.LngLatBounds.Unnamed", "type": "Function", "tags": [], - "label": "addControl", + "label": "Constructor", "description": [], "signature": [ - "(control: maplibregl.Control | maplibregl.IControl, position?: \"top-right\" | \"top-left\" | \"bottom-right\" | \"bottom-left\" | undefined) => this" + "any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.addControl.$1", - "type": "CompoundType", + "id": "def-server.LngLatBounds.Unnamed.$1", + "type": "Any", "tags": [], - "label": "control", + "label": "sw", "description": [], "signature": [ - "maplibregl.Control | maplibregl.IControl" + "any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.addControl.$2", - "type": "CompoundType", + "id": "def-server.LngLatBounds.Unnamed.$2", + "type": "Any", "tags": [], - "label": "position", + "label": "ne", "description": [], "signature": [ - "\"top-right\" | \"top-left\" | \"bottom-right\" | \"bottom-left\" | undefined" + "any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.removeControl", + "id": "def-server.LngLatBounds.setNorthEast", "type": "Function", "tags": [], - "label": "removeControl", - "description": [], + "label": "setNorthEast", + "description": [ + "\nSet the northeast corner of the bounding box\n" + ], "signature": [ - "(control: maplibregl.Control | maplibregl.IControl) => this" + "(ne: maplibregl.LngLatLike) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.removeControl.$1", + "id": "def-server.LngLatBounds.setNorthEast.$1", "type": "CompoundType", "tags": [], - "label": "control", - "description": [], + "label": "ne", + "description": [ + "a {@link LngLatLike } object describing the northeast corner of the bounding box." + ], "signature": [ - "maplibregl.Control | maplibregl.IControl" + "maplibregl.LngLatLike" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.hasControl", + "id": "def-server.LngLatBounds.setSouthWest", "type": "Function", "tags": [], - "label": "hasControl", + "label": "setSouthWest", "description": [ - "\nChecks if a control exists on the map.\n" + "\nSet the southwest corner of the bounding box\n" ], "signature": [ - "(control: maplibregl.IControl) => boolean" + "(sw: maplibregl.LngLatLike) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.hasControl.$1", - "type": "Object", + "id": "def-server.LngLatBounds.setSouthWest.$1", + "type": "CompoundType", "tags": [], - "label": "control", + "label": "sw", "description": [ - "The {@link IControl } to check." + "a {@link LngLatLike } object describing the southwest corner of the bounding box." ], "signature": [ - "maplibregl.IControl" + "maplibregl.LngLatLike" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } ], "returnComment": [ - "True if map contains control." + "`this`" ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.resize", + "id": "def-server.LngLatBounds.extend", "type": "Function", "tags": [], - "label": "resize", - "description": [], + "label": "extend", + "description": [ + "\nExtend the bounds to include a given LngLatLike or LngLatBoundsLike.\n" + ], "signature": [ - "(eventData?: maplibregl.EventData | undefined) => this" + "(obj: maplibregl.LngLatBoundsLike | maplibregl.LngLatLike) => any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.resize.$1", - "type": "Object", + "id": "def-server.LngLatBounds.extend.$1", + "type": "CompoundType", "tags": [], - "label": "eventData", - "description": [], + "label": "obj", + "description": [ + "object to extend to" + ], "signature": [ - "maplibregl.EventData | undefined" + "maplibregl.LngLatBoundsLike | maplibregl.LngLatLike" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getBounds", + "id": "def-server.LngLatBounds.getCenter", "type": "Function", "tags": [], - "label": "getBounds", - "description": [], + "label": "getCenter", + "description": [ + "\nReturns the geographical coordinate equidistant from the bounding box's corners.\n" + ], "signature": [ - "() => maplibregl.LngLatBounds" + "() => maplibregl.LngLat" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The bounding box's center." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getMaxBounds", + "id": "def-server.LngLatBounds.getSouthWest", "type": "Function", "tags": [], - "label": "getMaxBounds", - "description": [], + "label": "getSouthWest", + "description": [ + "\nReturns the southwest corner of the bounding box.\n" + ], "signature": [ - "() => maplibregl.LngLatBounds | null" + "() => maplibregl.LngLat" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The southwest corner of the bounding box." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setMaxBounds", + "id": "def-server.LngLatBounds.getNorthEast", "type": "Function", "tags": [], - "label": "setMaxBounds", - "description": [], + "label": "getNorthEast", + "description": [ + "\nReturns the northeast corner of the bounding box.\n" + ], "signature": [ - "(lnglatbounds?: maplibregl.LngLatBoundsLike | undefined) => this" + "() => maplibregl.LngLat" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setMaxBounds.$1", - "type": "CompoundType", - "tags": [], - "label": "lnglatbounds", - "description": [], - "signature": [ - "maplibregl.LngLatBoundsLike | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] + "children": [], + "returnComment": [ + "The northeast corner of the bounding box." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setMinZoom", + "id": "def-server.LngLatBounds.getNorthWest", "type": "Function", "tags": [], - "label": "setMinZoom", - "description": [], + "label": "getNorthWest", + "description": [ + "\nReturns the northwest corner of the bounding box.\n" + ], "signature": [ - "(minZoom?: number | null | undefined) => this" + "() => maplibregl.LngLat" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setMinZoom.$1", - "type": "CompoundType", - "tags": [], - "label": "minZoom", - "description": [], - "signature": [ - "number | null | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] + "children": [], + "returnComment": [ + "The northwest corner of the bounding box." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getMinZoom", + "id": "def-server.LngLatBounds.getSouthEast", "type": "Function", "tags": [], - "label": "getMinZoom", - "description": [], + "label": "getSouthEast", + "description": [ + "\nReturns the southeast corner of the bounding box.\n" + ], "signature": [ - "() => number" + "() => maplibregl.LngLat" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The southeast corner of the bounding box." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setMaxZoom", + "id": "def-server.LngLatBounds.getWest", "type": "Function", "tags": [], - "label": "setMaxZoom", - "description": [], + "label": "getWest", + "description": [ + "\nReturns the west edge of the bounding box.\n" + ], "signature": [ - "(maxZoom?: number | null | undefined) => this" + "() => number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setMaxZoom.$1", - "type": "CompoundType", - "tags": [], - "label": "maxZoom", - "description": [], - "signature": [ - "number | null | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] + "children": [], + "returnComment": [ + "The west edge of the bounding box." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getMaxZoom", + "id": "def-server.LngLatBounds.getSouth", "type": "Function", "tags": [], - "label": "getMaxZoom", - "description": [], + "label": "getSouth", + "description": [ + "\nReturns the south edge of the bounding box.\n" + ], "signature": [ "() => number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The south edge of the bounding box." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setMinPitch", + "id": "def-server.LngLatBounds.getEast", "type": "Function", "tags": [], - "label": "setMinPitch", - "description": [], + "label": "getEast", + "description": [ + "\nReturns the east edge of the bounding box.\n" + ], "signature": [ - "(minPitch?: number | null | undefined) => this" + "() => number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setMinPitch.$1", - "type": "CompoundType", - "tags": [], - "label": "minPitch", - "description": [], - "signature": [ - "number | null | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] + "children": [], + "returnComment": [ + "The east edge of the bounding box." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getMinPitch", + "id": "def-server.LngLatBounds.getNorth", "type": "Function", "tags": [], - "label": "getMinPitch", - "description": [], + "label": "getNorth", + "description": [ + "\nReturns the north edge of the bounding box.\n" + ], "signature": [ "() => number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The north edge of the bounding box." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setMaxPitch", + "id": "def-server.LngLatBounds.toArray", "type": "Function", "tags": [], - "label": "setMaxPitch", - "description": [], + "label": "toArray", + "description": [ + "\nReturns the bounding box represented as an array.\n" + ], "signature": [ - "(maxPitch?: number | null | undefined) => this" + "() => number[][]" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setMaxPitch.$1", - "type": "CompoundType", - "tags": [], - "label": "maxPitch", - "description": [], - "signature": [ - "number | null | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] + "children": [], + "returnComment": [ + "The bounding box represented as an array, consisting of the\nsouthwest and northeast coordinates of the bounding represented as arrays of numbers." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getMaxPitch", + "id": "def-server.LngLatBounds.toString", "type": "Function", "tags": [], - "label": "getMaxPitch", - "description": [], + "label": "toString", + "description": [ + "\nReturn the bounding box represented as a string.\n" + ], "signature": [ - "() => number" + "() => string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The bounding box represents as a string of the format\n`'LngLatBounds(LngLat(lng, lat), LngLat(lng, lat))'`." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getRenderWorldCopies", + "id": "def-server.LngLatBounds.isEmpty", "type": "Function", "tags": [], - "label": "getRenderWorldCopies", - "description": [], + "label": "isEmpty", + "description": [ + "\nCheck if the bounding box is an empty/`null`-type box.\n" + ], "signature": [ "() => boolean" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "True if bounds have been defined, otherwise false." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setRenderWorldCopies", + "id": "def-server.LngLatBounds.contains", "type": "Function", "tags": [], - "label": "setRenderWorldCopies", - "description": [], + "label": "contains", + "description": [ + "\nCheck if the point is within the bounding box.\n" + ], "signature": [ - "(renderWorldCopies?: boolean | undefined) => this" + "(lnglat: maplibregl.LngLatLike) => boolean" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setRenderWorldCopies.$1", + "id": "def-server.LngLatBounds.contains.$1", "type": "CompoundType", "tags": [], - "label": "renderWorldCopies", - "description": [], + "label": "lnglat", + "description": [ + "geographic point to check against." + ], "signature": [ - "boolean | undefined" + "maplibregl.LngLatLike" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "True if the point is within the bounding box." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.project", + "id": "def-server.LngLatBounds.convert", "type": "Function", "tags": [], - "label": "project", - "description": [], + "label": "convert", + "description": [ + "\nConverts an array to a `LngLatBounds` object.\n\nIf a `LngLatBounds` object is passed in, the function returns it unchanged.\n\nInternally, the function calls `LngLat#convert` to convert arrays to `LngLat` values.\n" + ], "signature": [ - "(lnglat: maplibregl.LngLatLike) => maplibregl.Point" + "(input: maplibregl.LngLatBoundsLike | null) => maplibregl.LngLatBounds" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.project.$1", + "id": "def-server.LngLatBounds.convert.$1", "type": "CompoundType", "tags": [], - "label": "lnglat", - "description": [], + "label": "input", + "description": [ + "An array of two coordinates to convert, or a `LngLatBounds` object to return." + ], "signature": [ - "maplibregl.LngLatLike" + "maplibregl.LngLatBoundsLike | null" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": true + "isRequired": false } ], - "returnComment": [] - }, + "returnComment": [ + "A new `LngLatBounds` object, if a conversion occurred, or the original `LngLatBounds` object." + ] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map", + "type": "Class", + "tags": [], + "label": "Map", + "description": [], + "signature": [ + "maplibregl.Map extends Camera" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.unproject", - "type": "Function", + "id": "def-server.Map.style", + "type": "Object", "tags": [], - "label": "unproject", + "label": "style", "description": [], "signature": [ - "(point: maplibregl.PointLike) => maplibregl.LngLat" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.unproject.$1", - "type": "CompoundType", - "tags": [], - "label": "point", - "description": [], - "signature": [ - "maplibregl.PointLike" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } + "maplibregl.Style" ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.isMoving", - "type": "Function", + "id": "def-server.Map.painter", + "type": "Object", "tags": [], - "label": "isMoving", + "label": "painter", "description": [], "signature": [ - "() => boolean" + "maplibregl.Painter" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.isZooming", - "type": "Function", + "id": "def-server.Map.handlers", + "type": "Object", "tags": [], - "label": "isZooming", + "label": "handlers", "description": [], "signature": [ - "() => boolean" + "maplibregl.HandlerManager" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.isRotating", - "type": "Function", + "id": "def-server.Map._container", + "type": "Object", "tags": [], - "label": "isRotating", + "label": "_container", "description": [], "signature": [ - "() => boolean" + "HTMLElement" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.queryRenderedFeatures", - "type": "Function", + "id": "def-server.Map._canvasContainer", + "type": "Object", "tags": [], - "label": "queryRenderedFeatures", - "description": [ - "\nReturns an array of GeoJSON Feature objects representing visible features that satisfy the query parameters.\n\nThe properties value of each returned feature object contains the properties of its source feature. For GeoJSON sources, only string and numeric property values are supported (i.e. null, Array, and Object values are not supported).\n\nEach feature includes top-level layer, source, and sourceLayer properties. The layer property is an object representing the style layer to which the feature belongs. Layout and paint properties in this object contain values which are fully evaluated for the given zoom level and feature.\n\nOnly features that are currently rendered are included. Some features will not be included, like:\n\n- Features from layers whose visibility property is \"none\".\n- Features from layers whose zoom range excludes the current zoom level.\n- Symbol features that have been hidden due to text or icon collision.\n\nFeatures from all other layers are included, including features that may have no visible contribution to the rendered result; for example, because the layer's opacity or color alpha component is set to 0.\n\nThe topmost rendered feature appears first in the returned array, and subsequent features are sorted by descending z-order. Features that are rendered multiple times (due to wrapping across the antimeridian at low zoom levels) are returned only once (though subject to the following caveat).\n\nBecause features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple times in query results. For example, suppose there is a highway running through the bounding rectangle of a query. The results of the query will be those parts of the highway that lie within the map tiles covering the bounding rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple tiles due to tile buffering.\n" - ], + "label": "_canvasContainer", + "description": [], "signature": [ - "(pointOrBox?: maplibregl.PointLike | [maplibregl.PointLike, maplibregl.PointLike] | undefined, options?: ({ layers?: string[] | undefined; filter?: any[] | undefined; } & maplibregl.FilterOptions) | undefined) => maplibregl.MapboxGeoJSONFeature[]" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.queryRenderedFeatures.$1", - "type": "CompoundType", - "tags": [], - "label": "pointOrBox", - "description": [ - "The geometry of the query region: either a single point or southwest and northeast points describing a bounding box. Omitting this parameter (i.e. calling Map#queryRenderedFeatures with zero arguments, or with only a options argument) is equivalent to passing a bounding box encompassing the entire map viewport." - ], - "signature": [ - "maplibregl.PointLike | [maplibregl.PointLike, maplibregl.PointLike] | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.queryRenderedFeatures.$2", - "type": "CompoundType", - "tags": [], - "label": "options", - "description": [], - "signature": [ - "({ layers?: string[] | undefined; filter?: any[] | undefined; } & maplibregl.FilterOptions) | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - } + "HTMLElement" ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.querySourceFeatures", - "type": "Function", + "id": "def-server.Map._controlContainer", + "type": "Object", "tags": [], - "label": "querySourceFeatures", - "description": [ - "\nReturns an array of GeoJSON Feature objects representing features within the specified vector tile or GeoJSON source that satisfy the query parameters.\n\nIn contrast to Map#queryRenderedFeatures, this function returns all features matching the query parameters, whether or not they are rendered by the current style (i.e. visible). The domain of the query includes all currently-loaded vector tiles and GeoJSON source tiles: this function does not check tiles outside the currently visible viewport.\n\nBecause features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple times in query results. For example, suppose there is a highway running through the bounding rectangle of a query. The results of the query will be those parts of the highway that lie within the map tiles covering the bounding rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple tiles due to tile buffering.\n" - ], + "label": "_controlContainer", + "description": [], "signature": [ - "(sourceID: string, parameters?: ({ sourceLayer?: string | undefined; filter?: any[] | undefined; } & maplibregl.FilterOptions) | undefined) => maplibregl.MapboxGeoJSONFeature[]" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.querySourceFeatures.$1", - "type": "string", - "tags": [], - "label": "sourceID", - "description": [ - "The ID of the vector tile or GeoJSON source to query." - ], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.querySourceFeatures.$2", - "type": "CompoundType", - "tags": [], - "label": "parameters", - "description": [], - "signature": [ - "({ sourceLayer?: string | undefined; filter?: any[] | undefined; } & maplibregl.FilterOptions) | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - } + "HTMLElement" ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setStyle", - "type": "Function", + "id": "def-server.Map._controlPositions", + "type": "Object", "tags": [], - "label": "setStyle", + "label": "_controlPositions", "description": [], "signature": [ - "(style: string | maplibregl.Style, options?: { diff?: boolean | undefined; localIdeographFontFamily?: string | undefined; } | undefined) => this" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setStyle.$1", - "type": "CompoundType", - "tags": [], - "label": "style", - "description": [], - "signature": [ - "string | maplibregl.Style" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setStyle.$2", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setStyle.$2.diff", - "type": "CompoundType", - "tags": [], - "label": "diff", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setStyle.$2.localIdeographFontFamily", - "type": "string", - "tags": [], - "label": "localIdeographFontFamily", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - } - ] - } + "{ [_: string]: HTMLElement; }" ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setTransformRequest", - "type": "Function", + "id": "def-server.Map._interactive", + "type": "boolean", "tags": [], - "label": "setTransformRequest", + "label": "_interactive", "description": [], - "signature": [ - "(transformRequest: maplibregl.TransformRequestFunction) => void" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setTransformRequest.$1", - "type": "Function", - "tags": [], - "label": "transformRequest", - "description": [], - "signature": [ - "maplibregl.TransformRequestFunction" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getStyle", - "type": "Function", + "id": "def-server.Map._showTileBoundaries", + "type": "boolean", "tags": [], - "label": "getStyle", + "label": "_showTileBoundaries", "description": [], - "signature": [ - "() => maplibregl.Style" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.isStyleLoaded", - "type": "Function", + "id": "def-server.Map._showCollisionBoxes", + "type": "boolean", "tags": [], - "label": "isStyleLoaded", + "label": "_showCollisionBoxes", "description": [], - "signature": [ - "() => boolean" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.addSource", - "type": "Function", + "id": "def-server.Map._showPadding", + "type": "boolean", "tags": [], - "label": "addSource", + "label": "_showPadding", "description": [], - "signature": [ - "(id: string, source: maplibregl.AnySourceData) => this" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.addSource.$1", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.addSource.$2", - "type": "CompoundType", - "tags": [], - "label": "source", - "description": [], - "signature": [ - "maplibregl.AnySourceData" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.isSourceLoaded", - "type": "Function", + "id": "def-server.Map._showOverdrawInspector", + "type": "boolean", "tags": [], - "label": "isSourceLoaded", + "label": "_showOverdrawInspector", "description": [], - "signature": [ - "(id: string) => boolean" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.isSourceLoaded.$1", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.areTilesLoaded", - "type": "Function", + "id": "def-server.Map._repaint", + "type": "boolean", "tags": [], - "label": "areTilesLoaded", + "label": "_repaint", "description": [], - "signature": [ - "() => boolean" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.removeSource", - "type": "Function", + "id": "def-server.Map._vertices", + "type": "boolean", "tags": [], - "label": "removeSource", + "label": "_vertices", "description": [], - "signature": [ - "(id: string) => this" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.removeSource.$1", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getSource", - "type": "Function", + "id": "def-server.Map._canvas", + "type": "Object", "tags": [], - "label": "getSource", + "label": "_canvas", "description": [], "signature": [ - "(id: string) => maplibregl.AnySourceImpl" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getSource.$1", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } + "HTMLCanvasElement" ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.addImage", - "type": "Function", + "id": "def-server.Map._maxTileCacheSize", + "type": "number", "tags": [], - "label": "addImage", + "label": "_maxTileCacheSize", "description": [], - "signature": [ - "(name: string, image: ArrayBufferView | HTMLImageElement | { width: number; height: number; data: Uint8Array | Uint8ClampedArray; } | ImageData | ImageBitmap, options?: { pixelRatio?: number | undefined; sdf?: boolean | undefined; } | undefined) => this" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.addImage.$1", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.addImage.$2", - "type": "CompoundType", - "tags": [], - "label": "image", - "description": [], - "signature": [ - "ArrayBufferView | HTMLImageElement | { width: number; height: number; data: Uint8Array | Uint8ClampedArray; } | ImageData | ImageBitmap" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.addImage.$3", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.addImage.$3.pixelRatio", - "type": "number", - "tags": [], - "label": "pixelRatio", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.addImage.$3.sdf", - "type": "CompoundType", - "tags": [], - "label": "sdf", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - } - ] - } - ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.hasImage", - "type": "Function", + "id": "def-server.Map._frame", + "type": "Object", "tags": [], - "label": "hasImage", + "label": "_frame", "description": [], "signature": [ - "(name: string) => boolean" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.hasImage.$1", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } + "{ cancel: () => void; }" ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.removeImage", - "type": "Function", + "id": "def-server.Map._styleDirty", + "type": "boolean", "tags": [], - "label": "removeImage", + "label": "_styleDirty", "description": [], - "signature": [ - "(name: string) => this" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.removeImage.$1", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.loadImage", - "type": "Function", + "id": "def-server.Map._sourcesDirty", + "type": "boolean", "tags": [], - "label": "loadImage", + "label": "_sourcesDirty", "description": [], - "signature": [ - "(url: string, callback: Function) => this" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.loadImage.$1", - "type": "string", - "tags": [], - "label": "url", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.loadImage.$2", - "type": "Object", - "tags": [], - "label": "callback", - "description": [], - "signature": [ - "Function" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.listImages", - "type": "Function", + "id": "def-server.Map._placementDirty", + "type": "boolean", "tags": [], - "label": "listImages", + "label": "_placementDirty", "description": [], - "signature": [ - "() => string[]" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.addLayer", - "type": "Function", + "id": "def-server.Map._loaded", + "type": "boolean", "tags": [], - "label": "addLayer", + "label": "_loaded", "description": [], - "signature": [ - "(layer: maplibregl.AnyLayer, before?: string | undefined) => this" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._fullyLoaded", + "type": "boolean", + "tags": [], + "label": "_fullyLoaded", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._trackResize", + "type": "boolean", + "tags": [], + "label": "_trackResize", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._preserveDrawingBuffer", + "type": "boolean", + "tags": [], + "label": "_preserveDrawingBuffer", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._failIfMajorPerformanceCaveat", + "type": "boolean", + "tags": [], + "label": "_failIfMajorPerformanceCaveat", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._antialias", + "type": "boolean", + "tags": [], + "label": "_antialias", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._refreshExpiredTiles", + "type": "boolean", + "tags": [], + "label": "_refreshExpiredTiles", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._hash", + "type": "Object", + "tags": [], + "label": "_hash", + "description": [], + "signature": [ + "maplibregl.Hash" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._delegatedListeners", + "type": "Any", + "tags": [], + "label": "_delegatedListeners", + "description": [], + "signature": [ + "any" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._fadeDuration", + "type": "number", + "tags": [], + "label": "_fadeDuration", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._crossSourceCollisions", + "type": "boolean", + "tags": [], + "label": "_crossSourceCollisions", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._crossFadingFactor", + "type": "number", + "tags": [], + "label": "_crossFadingFactor", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._collectResourceTiming", + "type": "boolean", + "tags": [], + "label": "_collectResourceTiming", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._renderTaskQueue", + "type": "Object", + "tags": [], + "label": "_renderTaskQueue", + "description": [], + "signature": [ + "maplibregl.TaskQueue" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._controls", + "type": "Array", + "tags": [], + "label": "_controls", + "description": [], + "signature": [ + "maplibregl.IControl[]" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._mapId", + "type": "number", + "tags": [], + "label": "_mapId", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._localIdeographFontFamily", + "type": "string", + "tags": [], + "label": "_localIdeographFontFamily", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._requestManager", + "type": "Object", + "tags": [], + "label": "_requestManager", + "description": [], + "signature": [ + "maplibregl.RequestManager" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._locale", + "type": "Any", + "tags": [], + "label": "_locale", + "description": [], + "signature": [ + "any" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._removed", + "type": "boolean", + "tags": [], + "label": "_removed", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._clickTolerance", + "type": "number", + "tags": [], + "label": "_clickTolerance", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._pixelRatio", + "type": "number", + "tags": [], + "label": "_pixelRatio", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.scrollZoom", + "type": "Object", + "tags": [], + "label": "scrollZoom", + "description": [ + "\nThe map's {@link ScrollZoomHandler}, which implements zooming in and out with a scroll wheel or trackpad.\nFind more details and examples using `scrollZoom` in the {@link ScrollZoomHandler} section." + ], + "signature": [ + "maplibregl.ScrollZoomHandler" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.boxZoom", + "type": "Object", + "tags": [], + "label": "boxZoom", + "description": [ + "\nThe map's {@link BoxZoomHandler}, which implements zooming using a drag gesture with the Shift key pressed.\nFind more details and examples using `boxZoom` in the {@link BoxZoomHandler} section." + ], + "signature": [ + "maplibregl.BoxZoomHandler" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.dragRotate", + "type": "Object", + "tags": [], + "label": "dragRotate", + "description": [ + "\nThe map's {@link DragRotateHandler}, which implements rotating the map while dragging with the right\nmouse button or with the Control key pressed. Find more details and examples using `dragRotate`\nin the {@link DragRotateHandler} section." + ], + "signature": [ + "maplibregl.DragRotateHandler" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.dragPan", + "type": "Object", + "tags": [], + "label": "dragPan", + "description": [ + "\nThe map's {@link DragPanHandler}, which implements dragging the map with a mouse or touch gesture.\nFind more details and examples using `dragPan` in the {@link DragPanHandler} section." + ], + "signature": [ + "maplibregl.DragPanHandler" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.keyboard", + "type": "Object", + "tags": [], + "label": "keyboard", + "description": [ + "\nThe map's {@link KeyboardHandler}, which allows the user to zoom, rotate, and pan the map using keyboard\nshortcuts. Find more details and examples using `keyboard` in the {@link KeyboardHandler} section." + ], + "signature": [ + "maplibregl.KeyboardHandler" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.doubleClickZoom", + "type": "Object", + "tags": [], + "label": "doubleClickZoom", + "description": [ + "\nThe map's {@link DoubleClickZoomHandler}, which allows the user to zoom by double clicking.\nFind more details and examples using `doubleClickZoom` in the {@link DoubleClickZoomHandler} section." + ], + "signature": [ + "maplibregl.DoubleClickZoomHandler" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.touchZoomRotate", + "type": "Object", + "tags": [], + "label": "touchZoomRotate", + "description": [ + "\nThe map's {@link TouchZoomRotateHandler}, which allows the user to zoom or rotate the map with touch gestures.\nFind more details and examples using `touchZoomRotate` in the {@link TouchZoomRotateHandler} section." + ], + "signature": [ + "maplibregl.TouchZoomRotateHandler" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.touchPitch", + "type": "Object", + "tags": [], + "label": "touchPitch", + "description": [ + "\nThe map's {@link TouchPitchHandler}, which allows the user to pitch the map with touch gestures.\nFind more details and examples using `touchPitch` in the {@link TouchPitchHandler} section." + ], + "signature": [ + "maplibregl.TouchPitchHandler" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "maplibregl.MapOptions" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._getMapId", + "type": "Function", + "tags": [], + "label": "_getMapId", + "description": [], + "signature": [ + "() => number" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.addControl", + "type": "Function", + "tags": [ + "see" + ], + "label": "addControl", + "description": [ + "\nAdds an {@link IControl} to the map, calling `control.onAdd(this)`.\n" + ], + "signature": [ + "(control: maplibregl.IControl, position?: maplibregl.ControlPosition | undefined) => this" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.addControl.$1", + "type": "Object", + "tags": [], + "label": "control", + "description": [ + "The {@link IControl } to add." + ], + "signature": [ + "maplibregl.IControl" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.addControl.$2", + "type": "CompoundType", + "tags": [], + "label": "position", + "description": [ + "position on the map to which the control will be added.\nValid values are `'top-left'`, `'top-right'`, `'bottom-left'`, and `'bottom-right'`. Defaults to `'top-right'`." + ], + "signature": [ + "maplibregl.ControlPosition | undefined" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [ + "`this`" + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.removeControl", + "type": "Function", + "tags": [], + "label": "removeControl", + "description": [ + "\nRemoves the control from the map.\n" + ], + "signature": [ + "(control: maplibregl.IControl) => this" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.removeControl.$1", + "type": "Object", + "tags": [], + "label": "control", + "description": [ + "The {@link IControl } to remove." + ], + "signature": [ + "maplibregl.IControl" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [ + "`this`" + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.hasControl", + "type": "Function", + "tags": [], + "label": "hasControl", + "description": [ + "\nChecks if a control exists on the map.\n" + ], + "signature": [ + "(control: maplibregl.IControl) => boolean" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.hasControl.$1", + "type": "Object", + "tags": [], + "label": "control", + "description": [ + "The {@link IControl } to check." + ], + "signature": [ + "maplibregl.IControl" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [ + "True if map contains control." + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.resize", + "type": "Function", + "tags": [], + "label": "resize", + "description": [ + "\nResizes the map according to the dimensions of its\n`container` element.\n\nChecks if the map container size changed and updates the map if it has changed.\nThis method must be called after the map's `container` is resized programmatically\nor when the map is shown after being initially hidden with CSS.\n" + ], + "signature": [ + "(eventData?: any) => this" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.resize.$1", + "type": "Any", + "tags": [], + "label": "eventData", + "description": [ + "Additional properties to be passed to `movestart`, `move`, `resize`, and `moveend`\nevents that get triggered as a result of resize. This can be useful for differentiating the\nsource of an event (for example, user-initiated or programmatically-triggered events)." + ], + "signature": [ + "any" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [ + "`this`" + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.getPixelRatio", + "type": "Function", + "tags": [], + "label": "getPixelRatio", + "description": [ + "\nReturns the map's pixel ratio." + ], + "signature": [ + "() => number" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [ + "The pixel ratio." + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.setPixelRatio", + "type": "Function", + "tags": [], + "label": "setPixelRatio", + "description": [ + "\nSets the map's pixel ratio. This allows to override `devicePixelRatio`.\nAfter this call, the canvas' `width` attribute will be `container.clientWidth * pixelRatio`\nand its height attribute will be `container.clientHeight * pixelRatio`." + ], + "signature": [ + "(pixelRatio: number) => void" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.setPixelRatio.$1", + "type": "number", + "tags": [], + "label": "pixelRatio", + "description": [ + "The pixel ratio." + ], + "signature": [ + "number" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.getBounds", + "type": "Function", + "tags": [], + "label": "getBounds", + "description": [ + "\nReturns the map's geographical bounds. When the bearing or pitch is non-zero, the visible region is not\nan axis-aligned rectangle, and the result is the smallest bounds that encompasses the visible region." + ], + "signature": [ + "() => maplibregl.LngLatBounds" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [ + "The geographical bounds of the map as {@link LngLatBounds }." + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.getMaxBounds", + "type": "Function", + "tags": [], + "label": "getMaxBounds", + "description": [ + "\nReturns the maximum geographical bounds the map is constrained to, or `null` if none set." + ], + "signature": [ + "() => maplibregl.LngLatBounds | null" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [ + "The map object." + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.setMaxBounds", + "type": "Function", + "tags": [], + "label": "setMaxBounds", + "description": [ + "\nSets or clears the map's geographical bounds.\n\nPan and zoom operations are constrained within these bounds.\nIf a pan or zoom is performed that would\ndisplay regions outside these bounds, the map will\ninstead display a position and zoom level\nas close as possible to the operation's request while still\nremaining within the bounds.\n" + ], + "signature": [ + "(bounds?: maplibregl.LngLatBoundsLike | null | undefined) => this" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.addLayer.$1", + "id": "def-server.Map.setMaxBounds.$1", "type": "CompoundType", "tags": [], - "label": "layer", - "description": [], - "signature": [ - "maplibregl.AnyLayer" + "label": "bounds", + "description": [ + "The maximum bounds to set. If `null` or `undefined` is provided, the function removes the map's maximum bounds." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.addLayer.$2", - "type": "string", - "tags": [], - "label": "before", - "description": [], "signature": [ - "string | undefined" + "maplibregl.LngLatBoundsLike | null | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": false } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.moveLayer", + "id": "def-server.Map.setMinZoom", "type": "Function", "tags": [], - "label": "moveLayer", - "description": [], + "label": "setMinZoom", + "description": [ + "\nSets or clears the map's minimum zoom level.\nIf the map's current zoom level is lower than the new minimum,\nthe map will zoom to the new minimum.\n\nIt is not always possible to zoom out and reach the set `minZoom`.\nOther factors such as map height may restrict zooming. For example,\nif the map is 512px tall it will not be possible to zoom below zoom 0\nno matter what the `minZoom` is set to.\n" + ], "signature": [ - "(id: string, beforeId?: string | undefined) => this" + "(minZoom?: number | null | undefined) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.moveLayer.$1", - "type": "string", + "id": "def-server.Map.setMinZoom.$1", + "type": "CompoundType", "tags": [], - "label": "id", - "description": [], - "signature": [ - "string" + "label": "minZoom", + "description": [ + "The minimum zoom level to set (-2 - 24).\nIf `null` or `undefined` is provided, the function removes the current minimum zoom (i.e. sets it to -2)." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.moveLayer.$2", - "type": "string", - "tags": [], - "label": "beforeId", - "description": [], "signature": [ - "string | undefined" + "number | null | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": false } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.removeLayer", + "id": "def-server.Map.getMinZoom", "type": "Function", "tags": [], - "label": "removeLayer", - "description": [], + "label": "getMinZoom", + "description": [ + "\nReturns the map's minimum allowable zoom level.\n" + ], "signature": [ - "(id: string) => this" + "() => number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.removeLayer.$1", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] + "children": [], + "returnComment": [ + "minZoom" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getLayer", + "id": "def-server.Map.setMaxZoom", "type": "Function", "tags": [], - "label": "getLayer", - "description": [], + "label": "setMaxZoom", + "description": [ + "\nSets or clears the map's maximum zoom level.\nIf the map's current zoom level is higher than the new maximum,\nthe map will zoom to the new maximum.\n" + ], "signature": [ - "(id: string) => maplibregl.AnyLayer" + "(maxZoom?: number | null | undefined) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getLayer.$1", - "type": "string", + "id": "def-server.Map.setMaxZoom.$1", + "type": "CompoundType", "tags": [], - "label": "id", - "description": [], + "label": "maxZoom", + "description": [ + "The maximum zoom level to set.\nIf `null` or `undefined` is provided, the function removes the current maximum zoom (sets it to 22)." + ], "signature": [ - "string" + "number | null | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": true + "isRequired": false } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setFilter", + "id": "def-server.Map.getMaxZoom", "type": "Function", "tags": [], - "label": "setFilter", - "description": [], + "label": "getMaxZoom", + "description": [ + "\nReturns the map's maximum allowable zoom level.\n" + ], + "signature": [ + "() => number" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [ + "maxZoom" + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.setMinPitch", + "type": "Function", + "tags": [], + "label": "setMinPitch", + "description": [ + "\nSets or clears the map's minimum pitch.\nIf the map's current pitch is lower than the new minimum,\nthe map will pitch to the new minimum.\n" + ], "signature": [ - "(layer: string, filter?: boolean | any[] | null | undefined, options?: maplibregl.FilterOptions | null | undefined) => this" + "(minPitch?: number | null | undefined) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setFilter.$1", - "type": "string", - "tags": [], - "label": "layer", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setFilter.$2", + "id": "def-server.Map.setMinPitch.$1", "type": "CompoundType", "tags": [], - "label": "filter", - "description": [], - "signature": [ - "boolean | any[] | null | undefined" + "label": "minPitch", + "description": [ + "The minimum pitch to set (0-85). Values greater than 60 degrees are experimental and may result in rendering issues. If you encounter any, please raise an issue with details in the MapLibre project.\nIf `null` or `undefined` is provided, the function removes the current minimum pitch (i.e. sets it to 0)." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setFilter.$3", - "type": "CompoundType", - "tags": [], - "label": "options", - "description": [], "signature": [ - "maplibregl.FilterOptions | null | undefined" + "number | null | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": false } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setLayerZoomRange", + "id": "def-server.Map.getMinPitch", "type": "Function", "tags": [], - "label": "setLayerZoomRange", - "description": [], + "label": "getMinPitch", + "description": [ + "\nReturns the map's minimum allowable pitch.\n" + ], "signature": [ - "(layerId: string, minzoom: number, maxzoom: number) => this" + "() => number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [ + "minPitch" + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.setMaxPitch", + "type": "Function", + "tags": [], + "label": "setMaxPitch", + "description": [ + "\nSets or clears the map's maximum pitch.\nIf the map's current pitch is higher than the new maximum,\nthe map will pitch to the new maximum.\n" + ], + "signature": [ + "(maxPitch?: number | null | undefined) => this" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setLayerZoomRange.$1", - "type": "string", - "tags": [], - "label": "layerId", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setLayerZoomRange.$2", - "type": "number", + "id": "def-server.Map.setMaxPitch.$1", + "type": "CompoundType", "tags": [], - "label": "minzoom", - "description": [], - "signature": [ - "number" + "label": "maxPitch", + "description": [ + "The maximum pitch to set (0-85). Values greater than 60 degrees are experimental and may result in rendering issues. If you encounter any, please raise an issue with details in the MapLibre project.\nIf `null` or `undefined` is provided, the function removes the current maximum pitch (sets it to 60)." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setLayerZoomRange.$3", - "type": "number", - "tags": [], - "label": "maxzoom", - "description": [], "signature": [ - "number" + "number | null | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": true + "isRequired": false } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.getMaxPitch", + "type": "Function", + "tags": [], + "label": "getMaxPitch", + "description": [ + "\nReturns the map's maximum allowable pitch.\n" + ], + "signature": [ + "() => number" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [ + "maxPitch" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getFilter", + "id": "def-server.Map.getRenderWorldCopies", "type": "Function", - "tags": [], - "label": "getFilter", - "description": [], + "tags": [ + "see" + ], + "label": "getRenderWorldCopies", + "description": [ + "\nReturns the state of `renderWorldCopies`. If `true`, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to `false`:\n- When the map is zoomed out far enough that a single representation of the world does not fill the map's entire\ncontainer, there will be blank space beyond 180 and -180 degrees longitude.\n- Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the\nmap and the other on the left edge of the map) at every zoom level." + ], "signature": [ - "(layer: string) => any[]" + "() => boolean" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getFilter.$1", - "type": "string", - "tags": [], - "label": "layer", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] + "children": [], + "returnComment": [ + "renderWorldCopies" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setPaintProperty", + "id": "def-server.Map.setRenderWorldCopies", "type": "Function", - "tags": [], - "label": "setPaintProperty", - "description": [], + "tags": [ + "see" + ], + "label": "setRenderWorldCopies", + "description": [ + "\nSets the state of `renderWorldCopies`.\n" + ], "signature": [ - "(layer: string, name: string, value: any, klass?: string | undefined) => this" + "(renderWorldCopies?: boolean | null | undefined) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setPaintProperty.$1", - "type": "string", - "tags": [], - "label": "layer", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setPaintProperty.$2", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setPaintProperty.$3", - "type": "Any", + "id": "def-server.Map.setRenderWorldCopies.$1", + "type": "CompoundType", "tags": [], - "label": "value", - "description": [], - "signature": [ - "any" + "label": "renderWorldCopies", + "description": [ + "If `true`, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to `false`:\n- When the map is zoomed out far enough that a single representation of the world does not fill the map's entire\ncontainer, there will be blank space beyond 180 and -180 degrees longitude.\n- Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the\nmap and the other on the left edge of the map) at every zoom level.\n\n`undefined` is treated as `true`, `null` is treated as `false`." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setPaintProperty.$4", - "type": "string", - "tags": [], - "label": "klass", - "description": [], "signature": [ - "string | undefined" + "boolean | null | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": false } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getPaintProperty", + "id": "def-server.Map.project", "type": "Function", "tags": [], - "label": "getPaintProperty", - "description": [], + "label": "project", + "description": [ + "\nReturns a [Point](https://github.com/mapbox/point-geometry) representing pixel coordinates, relative to the map's `container`,\nthat correspond to the specified geographical location.\n" + ], "signature": [ - "(layer: string, name: string) => any" + "(lnglat: maplibregl.LngLatLike) => ", + "node_modules/@types/mapbox__point-geometry/index" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getPaintProperty.$1", - "type": "string", + "id": "def-server.Map.project.$1", + "type": "CompoundType", "tags": [], - "label": "layer", - "description": [], - "signature": [ - "string" + "label": "lnglat", + "description": [ + "The geographical location to project." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getPaintProperty.$2", - "type": "string", - "tags": [], - "label": "name", - "description": [], "signature": [ - "string" + "maplibregl.LngLatLike" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The [Point](https://github.com/mapbox/point-geometry) corresponding to `lnglat`, relative to the map's `container`." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setLayoutProperty", + "id": "def-server.Map.unproject", "type": "Function", "tags": [], - "label": "setLayoutProperty", - "description": [], + "label": "unproject", + "description": [ + "\nReturns a {@link LngLat} representing geographical coordinates that correspond\nto the specified pixel coordinates.\n" + ], "signature": [ - "(layer: string, name: string, value: any) => this" + "(point: maplibregl.PointLike) => maplibregl.LngLat" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setLayoutProperty.$1", - "type": "string", - "tags": [], - "label": "layer", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setLayoutProperty.$2", - "type": "string", + "id": "def-server.Map.unproject.$1", + "type": "CompoundType", "tags": [], - "label": "name", - "description": [], - "signature": [ - "string" + "label": "point", + "description": [ + "The pixel coordinates to unproject." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setLayoutProperty.$3", - "type": "Any", - "tags": [], - "label": "value", - "description": [], "signature": [ - "any" + "maplibregl.PointLike" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The {@link LngLat } corresponding to `point`." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getLayoutProperty", + "id": "def-server.Map.isMoving", "type": "Function", "tags": [], - "label": "getLayoutProperty", - "description": [], + "label": "isMoving", + "description": [ + "\nReturns true if the map is panning, zooming, rotating, or pitching due to a camera animation or user gesture." + ], "signature": [ - "(layer: string, name: string) => any" + "() => boolean" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getLayoutProperty.$1", - "type": "string", - "tags": [], - "label": "layer", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getLayoutProperty.$2", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "signature": [ - "string" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } + "children": [], + "returnComment": [ + "True if the map is moving." + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.isZooming", + "type": "Function", + "tags": [], + "label": "isZooming", + "description": [ + "\nReturns true if the map is zooming due to a camera animation or user gesture." ], - "returnComment": [] + "signature": [ + "() => boolean" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [ + "True if the map is zooming." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setLight", + "id": "def-server.Map.isRotating", "type": "Function", "tags": [], - "label": "setLight", + "label": "isRotating", + "description": [ + "\nReturns true if the map is rotating due to a camera animation or user gesture." + ], + "signature": [ + "() => boolean" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [ + "True if the map is rotating." + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._createDelegatedListener", + "type": "Function", + "tags": [], + "label": "_createDelegatedListener", "description": [], "signature": [ - "(options: maplibregl.Light, lightOptions?: any) => this" + "(type: string, layerId: string, listener: maplibregl.Listener) => { layer: string; listener: maplibregl.Listener; delegates: { error?: ((e: any) => void) | undefined; load?: ((e: any) => void) | undefined; idle?: ((e: any) => void) | undefined; remove?: ((e: any) => void) | undefined; render?: ((e: any) => void) | undefined; resize?: ((e: any) => void) | undefined; webglcontextlost?: ((e: any) => void) | undefined; webglcontextrestored?: ((e: any) => void) | undefined; dataloading?: ((e: any) => void) | undefined; data?: ((e: any) => void) | undefined; tiledataloading?: ((e: any) => void) | undefined; sourcedataloading?: ((e: any) => void) | undefined; styledataloading?: ((e: any) => void) | undefined; sourcedata?: ((e: any) => void) | undefined; styledata?: ((e: any) => void) | undefined; styleimagemissing?: ((e: any) => void) | undefined; dataabort?: ((e: any) => void) | undefined; sourcedataabort?: ((e: any) => void) | undefined; boxzoomcancel?: ((e: any) => void) | undefined; boxzoomstart?: ((e: any) => void) | undefined; boxzoomend?: ((e: any) => void) | undefined; touchcancel?: ((e: any) => void) | undefined; touchmove?: ((e: any) => void) | undefined; touchend?: ((e: any) => void) | undefined; touchstart?: ((e: any) => void) | undefined; click?: ((e: any) => void) | undefined; contextmenu?: ((e: any) => void) | undefined; dblclick?: ((e: any) => void) | undefined; mousemove?: ((e: any) => void) | undefined; mouseup?: ((e: any) => void) | undefined; mousedown?: ((e: any) => void) | undefined; mouseout?: ((e: any) => void) | undefined; mouseover?: ((e: any) => void) | undefined; movestart?: ((e: any) => void) | undefined; move?: ((e: any) => void) | undefined; moveend?: ((e: any) => void) | undefined; zoomstart?: ((e: any) => void) | undefined; zoom?: ((e: any) => void) | undefined; zoomend?: ((e: any) => void) | undefined; rotatestart?: ((e: any) => void) | undefined; rotate?: ((e: any) => void) | undefined; rotateend?: ((e: any) => void) | undefined; dragstart?: ((e: any) => void) | undefined; drag?: ((e: any) => void) | undefined; dragend?: ((e: any) => void) | undefined; pitchstart?: ((e: any) => void) | undefined; pitch?: ((e: any) => void) | undefined; pitchend?: ((e: any) => void) | undefined; wheel?: ((e: any) => void) | undefined; }; }" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setLight.$1", - "type": "Object", + "id": "def-server.Map._createDelegatedListener.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._createDelegatedListener.$2", + "type": "string", "tags": [], - "label": "options", + "label": "layerId", "description": [], "signature": [ - "maplibregl.Light" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setLight.$2", - "type": "Any", + "id": "def-server.Map._createDelegatedListener.$3", + "type": "Function", "tags": [], - "label": "lightOptions", + "label": "listener", "description": [], "signature": [ - "any" + "maplibregl.Listener" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } @@ -2420,98 +2894,113 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getLight", + "id": "def-server.Map.on", "type": "Function", - "tags": [], - "label": "getLight", - "description": [], - "signature": [ - "() => maplibregl.Light" + "tags": [ + "see", + "see", + "see", + "see" + ], + "label": "on", + "description": [ + "\nAdds a listener for events of a specified type, optionally limited to features in a specified style layer.\n" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setFeatureState", - "type": "Function", - "tags": [], - "label": "setFeatureState", - "description": [], "signature": [ - "(feature: maplibregl.MapboxGeoJSONFeature | maplibregl.FeatureIdentifier, state: { [key: string]: any; }) => void" + "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & Object) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & Object) => void): this; (type: string, listener: maplibregl.Listener): this; }" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setFeatureState.$1", - "type": "CompoundType", + "id": "def-server.Map.on.$1", + "type": "Uncategorized", "tags": [], - "label": "feature", - "description": [], + "label": "type", + "description": [ + "The event type to listen for. Events compatible with the optional `layerId` parameter are triggered\nwhen the cursor enters a visible portion of the specified layer from outside that layer or outside the map canvas.\n\n| Event | Compatible with `layerId` |\n|-----------------------------------------------------------|---------------------------|\n| [`mousedown`](#map.event:mousedown) | yes |\n| [`mouseup`](#map.event:mouseup) | yes |\n| [`mouseover`](#map.event:mouseover) | yes |\n| [`mouseout`](#map.event:mouseout) | yes |\n| [`mousemove`](#map.event:mousemove) | yes |\n| [`mouseenter`](#map.event:mouseenter) | yes (required) |\n| [`mouseleave`](#map.event:mouseleave) | yes (required) |\n| [`click`](#map.event:click) | yes |\n| [`dblclick`](#map.event:dblclick) | yes |\n| [`contextmenu`](#map.event:contextmenu) | yes |\n| [`touchstart`](#map.event:touchstart) | yes |\n| [`touchend`](#map.event:touchend) | yes |\n| [`touchcancel`](#map.event:touchcancel) | yes |\n| [`wheel`](#map.event:wheel) | |\n| [`resize`](#map.event:resize) | |\n| [`remove`](#map.event:remove) | |\n| [`touchmove`](#map.event:touchmove) | |\n| [`movestart`](#map.event:movestart) | |\n| [`move`](#map.event:move) | |\n| [`moveend`](#map.event:moveend) | |\n| [`dragstart`](#map.event:dragstart) | |\n| [`drag`](#map.event:drag) | |\n| [`dragend`](#map.event:dragend) | |\n| [`zoomstart`](#map.event:zoomstart) | |\n| [`zoom`](#map.event:zoom) | |\n| [`zoomend`](#map.event:zoomend) | |\n| [`rotatestart`](#map.event:rotatestart) | |\n| [`rotate`](#map.event:rotate) | |\n| [`rotateend`](#map.event:rotateend) | |\n| [`pitchstart`](#map.event:pitchstart) | |\n| [`pitch`](#map.event:pitch) | |\n| [`pitchend`](#map.event:pitchend) | |\n| [`boxzoomstart`](#map.event:boxzoomstart) | |\n| [`boxzoomend`](#map.event:boxzoomend) | |\n| [`boxzoomcancel`](#map.event:boxzoomcancel) | |\n| [`webglcontextlost`](#map.event:webglcontextlost) | |\n| [`webglcontextrestored`](#map.event:webglcontextrestored) | |\n| [`load`](#map.event:load) | |\n| [`render`](#map.event:render) | |\n| [`idle`](#map.event:idle) | |\n| [`error`](#map.event:error) | |\n| [`data`](#map.event:data) | |\n| [`styledata`](#map.event:styledata) | |\n| [`sourcedata`](#map.event:sourcedata) | |\n| [`dataloading`](#map.event:dataloading) | |\n| [`styledataloading`](#map.event:styledataloading) | |\n| [`sourcedataloading`](#map.event:sourcedataloading) | |\n| [`styleimagemissing`](#map.event:styleimagemissing) | |\n| [`dataabort`](#map.event:dataabort) | |\n| [`sourcedataabort`](#map.event:sourcedataabort) | |" + ], "signature": [ - "maplibregl.MapboxGeoJSONFeature | maplibregl.FeatureIdentifier" + "T" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setFeatureState.$2", - "type": "Object", + "id": "def-server.Map.on.$2", + "type": "string", "tags": [], - "label": "state", + "label": "layer", "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setFeatureState.$2.Unnamed", - "type": "IndexSignature", - "tags": [], - "label": "[key: string]: any", - "description": [], - "signature": [ - "[key: string]: any" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - } - ] + "signature": [ + "string" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.on.$3", + "type": "Function", + "tags": [], + "label": "listener", + "description": [ + "The function to be called when the event is fired." + ], + "signature": [ + "(ev: maplibregl.MapLayerEventType[T] & Object) => void" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getFeatureState", + "id": "def-server.Map.on", "type": "Function", "tags": [], - "label": "getFeatureState", + "label": "on", "description": [], "signature": [ - "(feature: maplibregl.MapboxGeoJSONFeature | maplibregl.FeatureIdentifier) => { [key: string]: any; }" + "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & Object) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & Object) => void): this; (type: string, listener: maplibregl.Listener): this; }" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getFeatureState.$1", - "type": "CompoundType", + "id": "def-server.Map.on.$1", + "type": "Uncategorized", "tags": [], - "label": "feature", + "label": "type", + "description": [], + "signature": [ + "T" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.on.$2", + "type": "Function", + "tags": [], + "label": "listener", "description": [], "signature": [ - "maplibregl.MapboxGeoJSONFeature | maplibregl.FeatureIdentifier" + "(ev: maplibregl.MapEventType[T] & Object) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } @@ -2520,618 +3009,628 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.removeFeatureState", + "id": "def-server.Map.on", "type": "Function", "tags": [], - "label": "removeFeatureState", + "label": "on", "description": [], "signature": [ - "(target: maplibregl.MapboxGeoJSONFeature | maplibregl.FeatureIdentifier, key?: string | undefined) => void" + "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & Object) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & Object) => void): this; (type: string, listener: maplibregl.Listener): this; }" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.removeFeatureState.$1", - "type": "CompoundType", + "id": "def-server.Map.on.$1", + "type": "string", "tags": [], - "label": "target", + "label": "type", "description": [], "signature": [ - "maplibregl.MapboxGeoJSONFeature | maplibregl.FeatureIdentifier" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.removeFeatureState.$2", - "type": "string", + "id": "def-server.Map.on.$2", + "type": "Function", "tags": [], - "label": "key", + "label": "listener", "description": [], "signature": [ - "string | undefined" + "maplibregl.Listener" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getContainer", - "type": "Function", - "tags": [], - "label": "getContainer", - "description": [], - "signature": [ - "() => HTMLElement" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getCanvasContainer", - "type": "Function", - "tags": [], - "label": "getCanvasContainer", - "description": [], - "signature": [ - "() => HTMLElement" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getCanvas", - "type": "Function", - "tags": [], - "label": "getCanvas", - "description": [], - "signature": [ - "() => HTMLCanvasElement" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.loaded", - "type": "Function", - "tags": [], - "label": "loaded", - "description": [], - "signature": [ - "() => boolean" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.remove", - "type": "Function", - "tags": [], - "label": "remove", - "description": [], - "signature": [ - "() => void" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.triggerRepaint", + "id": "def-server.Map.once", "type": "Function", - "tags": [], - "label": "triggerRepaint", - "description": [], - "signature": [ - "() => void" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.showTileBoundaries", - "type": "boolean", - "tags": [], - "label": "showTileBoundaries", - "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.showCollisionBoxes", - "type": "boolean", - "tags": [], - "label": "showCollisionBoxes", - "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.showPadding", - "type": "boolean", "tags": [ + "method", "name", - "type", - "instance", - "memberof" + "memberof", + "instance" ], - "label": "showPadding", + "label": "once", "description": [ - "\nGets and sets a Boolean indicating whether the map will visualize\nthe padding offsets.\n" + "\nAdds a listener that will be called only once to a specified event type.\n\n\nAdds a listener that will be called only once to a specified event type occurring on features in a specified style layer.\n" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.repaint", - "type": "boolean", - "tags": [], - "label": "repaint", - "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getCenter", - "type": "Function", - "tags": [], - "label": "getCenter", - "description": [], "signature": [ - "() => maplibregl.LngLat" + "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & Object) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & Object) => void): this; (type: string, listener: maplibregl.Listener): this; }" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.once.$1", + "type": "Uncategorized", + "tags": [], + "label": "type", + "description": [ + "The event type to add a listener for." + ], + "signature": [ + "T" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.once.$2", + "type": "string", + "tags": [], + "label": "layer", + "description": [], + "signature": [ + "string" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.once.$3", + "type": "Function", + "tags": [], + "label": "listener", + "description": [ + "The function to be called when the event is fired.\nThe listener function is called with the data object passed to `fire`,\nextended with `target` and `type` properties." + ], + "signature": [ + "(ev: maplibregl.MapLayerEventType[T] & Object) => void" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setCenter", + "id": "def-server.Map.once", "type": "Function", "tags": [], - "label": "setCenter", + "label": "once", "description": [], "signature": [ - "(center: maplibregl.LngLatLike, eventData?: maplibregl.EventData | undefined) => this" + "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & Object) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & Object) => void): this; (type: string, listener: maplibregl.Listener): this; }" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setCenter.$1", - "type": "CompoundType", + "id": "def-server.Map.once.$1", + "type": "Uncategorized", "tags": [], - "label": "center", + "label": "type", "description": [], "signature": [ - "maplibregl.LngLatLike" + "T" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setCenter.$2", - "type": "Object", + "id": "def-server.Map.once.$2", + "type": "Function", "tags": [], - "label": "eventData", + "label": "listener", "description": [], "signature": [ - "maplibregl.EventData | undefined" + "(ev: maplibregl.MapEventType[T] & Object) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.panBy", + "id": "def-server.Map.once", "type": "Function", "tags": [], - "label": "panBy", + "label": "once", "description": [], "signature": [ - "(offset: maplibregl.PointLike, options?: maplibregl.AnimationOptions | undefined, eventData?: maplibregl.EventData | undefined) => this" + "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & Object) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & Object) => void): this; (type: string, listener: maplibregl.Listener): this; }" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.panBy.$1", - "type": "CompoundType", + "id": "def-server.Map.once.$1", + "type": "string", "tags": [], - "label": "offset", + "label": "type", "description": [], "signature": [ - "maplibregl.PointLike" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.panBy.$2", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "signature": [ - "maplibregl.AnimationOptions | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.panBy.$3", - "type": "Object", + "id": "def-server.Map.once.$2", + "type": "Function", "tags": [], - "label": "eventData", + "label": "listener", "description": [], "signature": [ - "maplibregl.EventData | undefined" + "maplibregl.Listener" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.panTo", + "id": "def-server.Map.off", "type": "Function", - "tags": [], - "label": "panTo", - "description": [], + "tags": [ + "method", + "name", + "memberof", + "instance" + ], + "label": "off", + "description": [ + "\nRemoves an event listener previously added with `Map#on`.\n\n\nRemoves an event listener for layer-specific events previously added with `Map#on`.\n" + ], "signature": [ - "(lnglat: maplibregl.LngLatLike, options?: maplibregl.AnimationOptions | undefined, eventdata?: maplibregl.EventData | undefined) => this" + "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & Object) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & Object) => void): this; (type: string, listener: maplibregl.Listener): this; }" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.panTo.$1", - "type": "CompoundType", + "id": "def-server.Map.off.$1", + "type": "Uncategorized", "tags": [], - "label": "lnglat", - "description": [], + "label": "type", + "description": [ + "The event type previously used to install the listener." + ], "signature": [ - "maplibregl.LngLatLike" + "T" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.panTo.$2", - "type": "Object", + "id": "def-server.Map.off.$2", + "type": "string", "tags": [], - "label": "options", + "label": "layer", "description": [], "signature": [ - "maplibregl.AnimationOptions | undefined" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.panTo.$3", - "type": "Object", + "id": "def-server.Map.off.$3", + "type": "Function", "tags": [], - "label": "eventdata", - "description": [], + "label": "listener", + "description": [ + "The function previously installed as a listener." + ], "signature": [ - "maplibregl.EventData | undefined" + "(ev: maplibregl.MapLayerEventType[T] & Object) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getZoom", + "id": "def-server.Map.off", "type": "Function", "tags": [], - "label": "getZoom", + "label": "off", "description": [], "signature": [ - "() => number" + "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & Object) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & Object) => void): this; (type: string, listener: maplibregl.Listener): this; }" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "children": [], + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.off.$1", + "type": "Uncategorized", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "T" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.off.$2", + "type": "Function", + "tags": [], + "label": "listener", + "description": [], + "signature": [ + "(ev: maplibregl.MapEventType[T] & Object) => void" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setZoom", + "id": "def-server.Map.off", "type": "Function", "tags": [], - "label": "setZoom", + "label": "off", "description": [], "signature": [ - "(zoom: number, eventData?: maplibregl.EventData | undefined) => this" + "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & Object) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & Object) => void): this; (type: string, listener: maplibregl.Listener): this; }" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setZoom.$1", - "type": "number", + "id": "def-server.Map.off.$1", + "type": "string", "tags": [], - "label": "zoom", + "label": "type", "description": [], "signature": [ - "number" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setZoom.$2", - "type": "Object", + "id": "def-server.Map.off.$2", + "type": "Function", "tags": [], - "label": "eventData", + "label": "listener", "description": [], "signature": [ - "maplibregl.EventData | undefined" + "maplibregl.Listener" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.zoomTo", + "id": "def-server.Map.queryRenderedFeatures", "type": "Function", - "tags": [], - "label": "zoomTo", - "description": [], + "tags": [ + "see" + ], + "label": "queryRenderedFeatures", + "description": [ + "\nReturns an array of MapGeoJSONFeature objects\nrepresenting visible features that satisfy the query parameters.\n" + ], "signature": [ - "(zoom: number, options?: maplibregl.AnimationOptions | undefined, eventData?: maplibregl.EventData | undefined) => this" + "(geometry?: maplibregl.PointLike | [maplibregl.PointLike, maplibregl.PointLike] | undefined, options?: any) => maplibregl.MapGeoJSONFeature[]" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.zoomTo.$1", - "type": "number", + "id": "def-server.Map.queryRenderedFeatures.$1", + "type": "CompoundType", "tags": [], - "label": "zoom", - "description": [], + "label": "geometry", + "description": [ + "- The geometry of the query region:\neither a single point or southwest and northeast points describing a bounding box.\nOmitting this parameter (i.e. calling {@link MapqueryRenderedFeatures } with zero arguments,\nor with only a `options` argument) is equivalent to passing a bounding box encompassing the entire\nmap viewport." + ], "signature": [ - "number" + "maplibregl.PointLike | [maplibregl.PointLike, maplibregl.PointLike] | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": true + "isRequired": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.zoomTo.$2", - "type": "Object", + "id": "def-server.Map.queryRenderedFeatures.$2", + "type": "Any", "tags": [], "label": "options", - "description": [], + "description": [ + "Options object." + ], "signature": [ - "maplibregl.AnimationOptions | undefined" + "any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true + } + ], + "returnComment": [ + "An array of MapGeoJSONFeature objects.\n\nThe `properties` value of each returned feature object contains the properties of its source feature. For GeoJSON sources, only\nstring and numeric property values are supported (i.e. `null`, `Array`, and `Object` values are not supported).\n\nEach feature includes top-level `layer`, `source`, and `sourceLayer` properties. The `layer` property is an object\nrepresenting the style layer to which the feature belongs. Layout and paint properties in this object contain values\nwhich are fully evaluated for the given zoom level and feature.\n\nOnly features that are currently rendered are included. Some features will **not** be included, like:\n\n- Features from layers whose `visibility` property is `\"none\"`.\n- Features from layers whose zoom range excludes the current zoom level.\n- Symbol features that have been hidden due to text or icon collision.\n\nFeatures from all other layers are included, including features that may have no visible\ncontribution to the rendered result; for example, because the layer's opacity or color alpha component is set to\n0.\n\nThe topmost rendered feature appears first in the returned array, and subsequent features are sorted by\ndescending z-order. Features that are rendered multiple times (due to wrapping across the antimeridian at low\nzoom levels) are returned only once (though subject to the following caveat).\n\nBecause features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature\ngeometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple\ntimes in query results. For example, suppose there is a highway running through the bounding rectangle of a query.\nThe results of the query will be those parts of the highway that lie within the map tiles covering the bounding\nrectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile\nwill be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple\ntiles due to tile buffering." + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.querySourceFeatures", + "type": "Function", + "tags": [], + "label": "querySourceFeatures", + "description": [ + "\nReturns an array of MapGeoJSONFeature objects\nrepresenting features within the specified vector tile or GeoJSON source that satisfy the query parameters.\n" + ], + "signature": [ + "(sourceId: string, parameters?: { sourceLayer: string; filter: any[]; validate?: boolean | undefined; } | null | undefined) => maplibregl.MapGeoJSONFeature[]" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.querySourceFeatures.$1", + "type": "string", + "tags": [], + "label": "sourceId", + "description": [ + "The ID of the vector tile or GeoJSON source to query." + ], + "signature": [ + "string" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.zoomTo.$3", - "type": "Object", + "id": "def-server.Map.querySourceFeatures.$2", + "type": "CompoundType", "tags": [], - "label": "eventData", - "description": [], + "label": "parameters", + "description": [ + "Options object." + ], "signature": [ - "maplibregl.EventData | undefined" + "{ sourceLayer: string; filter: any[]; validate?: boolean | undefined; } | null | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": false } ], - "returnComment": [] + "returnComment": [ + "An array of MapGeoJSONFeature objects.\n\nIn contrast to {@link MapqueryRenderedFeatures }, this function returns all features matching the query parameters,\nwhether or not they are rendered by the current style (i.e. visible). The domain of the query includes all currently-loaded\nvector tiles and GeoJSON source tiles: this function does not check tiles outside the currently\nvisible viewport.\n\nBecause features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature\ngeometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple\ntimes in query results. For example, suppose there is a highway running through the bounding rectangle of a query.\nThe results of the query will be those parts of the highway that lie within the map tiles covering the bounding\nrectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile\nwill be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple\ntiles due to tile buffering." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.zoomIn", + "id": "def-server.Map.setStyle", "type": "Function", "tags": [], - "label": "zoomIn", - "description": [], + "label": "setStyle", + "description": [ + "\nUpdates the map's MapLibre style object with a new value.\n\nIf a style is already set when this is used and options.diff is set to true, the map renderer will attempt to compare the given style\nagainst the map's current state and perform only the changes necessary to make the map style match the desired state. Changes in sprites\n(images used for icons and patterns) and glyphs (fonts for label text) **cannot** be diffed. If the sprites or fonts used in the current\nstyle and the given style are different in any way, the map renderer will force a full update, removing the current style and building\nthe given one from scratch.\n\n" + ], "signature": [ - "(options?: maplibregl.AnimationOptions | undefined, eventData?: maplibregl.EventData | undefined) => this" + "(style: string | maplibregl.StyleSpecification | null, options?: ({ diff?: boolean | undefined; } & maplibregl.StyleOptions) | undefined) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.zoomIn.$1", - "type": "Object", + "id": "def-server.Map.setStyle.$1", + "type": "CompoundType", "tags": [], - "label": "options", - "description": [], + "label": "style", + "description": [ + "A JSON object conforming to the schema described in the\n[MapLibre Style Specification](https://maplibre.org/maplibre-gl-js-docs/style-spec/), or a URL to such JSON." + ], "signature": [ - "maplibregl.AnimationOptions | undefined" + "string | maplibregl.StyleSpecification | null" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.zoomIn.$2", - "type": "Object", - "tags": [], - "label": "eventData", - "description": [], + "id": "def-server.Map.setStyle.$2", + "type": "CompoundType", + "tags": [], + "label": "options", + "description": [ + "Options object." + ], "signature": [ - "maplibregl.EventData | undefined" + "({ diff?: boolean | undefined; } & maplibregl.StyleOptions) | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": false } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.zoomOut", + "id": "def-server.Map.setTransformRequest", "type": "Function", "tags": [], - "label": "zoomOut", - "description": [], + "label": "setTransformRequest", + "description": [ + "\n Updates the requestManager's transform request with a new function\n" + ], "signature": [ - "(options?: maplibregl.AnimationOptions | undefined, eventData?: maplibregl.EventData | undefined) => this" + "(transformRequest: maplibregl.RequestTransformFunction) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.zoomOut.$1", - "type": "Object", + "id": "def-server.Map.setTransformRequest.$1", + "type": "Function", "tags": [], - "label": "options", - "description": [], - "signature": [ - "maplibregl.AnimationOptions | undefined" + "label": "transformRequest", + "description": [ + "A callback run before the Map makes a request for an external URL. The callback can be used to modify the url, set headers, or set the credentials property for cross-origin requests.\nExpected to return an object with a `url` property and optionally `headers` and `credentials` properties" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.zoomOut.$2", - "type": "Object", - "tags": [], - "label": "eventData", - "description": [], "signature": [ - "maplibregl.EventData | undefined" + "maplibregl.RequestTransformFunction" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getBearing", + "id": "def-server.Map._getUIString", "type": "Function", "tags": [], - "label": "getBearing", + "label": "_getUIString", "description": [], "signature": [ - "() => number" + "(key: string) => any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "children": [], + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._getUIString.$1", + "type": "string", + "tags": [], + "label": "key", + "description": [], + "signature": [ + "string" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setBearing", + "id": "def-server.Map._updateStyle", "type": "Function", "tags": [], - "label": "setBearing", + "label": "_updateStyle", "description": [], "signature": [ - "(bearing: number, eventData?: maplibregl.EventData | undefined) => this" + "(style: string | maplibregl.StyleSpecification | null, options?: ({ diff?: boolean | undefined; } & maplibregl.StyleOptions) | undefined) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setBearing.$1", - "type": "number", + "id": "def-server.Map._updateStyle.$1", + "type": "CompoundType", "tags": [], - "label": "bearing", + "label": "style", "description": [], "signature": [ - "number" + "string | maplibregl.StyleSpecification | null" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": true + "isRequired": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setBearing.$2", - "type": "Object", + "id": "def-server.Map._updateStyle.$2", + "type": "CompoundType", "tags": [], - "label": "eventData", + "label": "options", "description": [], "signature": [ - "maplibregl.EventData | undefined" + "({ diff?: boolean | undefined; } & maplibregl.StyleOptions) | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": false } @@ -3140,133 +3639,101 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getPadding", + "id": "def-server.Map._lazyInitEmptyStyle", "type": "Function", - "tags": [ - "memberof" - ], - "label": "getPadding", - "description": [ - "\nReturns the current padding applied around the map viewport.\n" - ], + "tags": [], + "label": "_lazyInitEmptyStyle", + "description": [], "signature": [ - "() => maplibregl.PaddingOptions" + "() => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], - "returnComment": [ - "The current padding around the map viewport." - ] + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setPadding", + "id": "def-server.Map._diffStyle", "type": "Function", - "tags": [ - "memberof", - "fires", - "fires" - ], - "label": "setPadding", - "description": [ - "\nSets the padding in pixels around the viewport.\n\nEquivalent to `jumpTo({padding: padding})`.\n" - ], + "tags": [], + "label": "_diffStyle", + "description": [], "signature": [ - "(padding: maplibregl.RequireAtLeastOne, eventData?: maplibregl.EventData | undefined) => this" + "(style: string | maplibregl.StyleSpecification, options?: ({ diff?: boolean | undefined; } & maplibregl.StyleOptions) | undefined) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setPadding.$1", + "id": "def-server.Map._diffStyle.$1", "type": "CompoundType", "tags": [], - "label": "padding", - "description": [ - "The desired padding. Format: { left: number, right: number, top: number, bottom: number }" - ], + "label": "style", + "description": [], "signature": [ - "maplibregl.RequireAtLeastOne" + "string | maplibregl.StyleSpecification" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setPadding.$2", - "type": "Object", + "id": "def-server.Map._diffStyle.$2", + "type": "CompoundType", "tags": [], - "label": "eventData", - "description": [ - "Additional properties to be added to event objects of events triggered by this method." - ], + "label": "options", + "description": [], "signature": [ - "maplibregl.EventData | undefined" + "({ diff?: boolean | undefined; } & maplibregl.StyleOptions) | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": false } ], - "returnComment": [ - "`this`" - ] + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.rotateTo", + "id": "def-server.Map._updateDiff", "type": "Function", "tags": [], - "label": "rotateTo", + "label": "_updateDiff", "description": [], "signature": [ - "(bearing: number, options?: maplibregl.AnimationOptions | undefined, eventData?: maplibregl.EventData | undefined) => this" + "(style: maplibregl.StyleSpecification, options?: ({ diff?: boolean | undefined; } & maplibregl.StyleOptions) | undefined) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.rotateTo.$1", - "type": "number", + "id": "def-server.Map._updateDiff.$1", + "type": "Object", "tags": [], - "label": "bearing", + "label": "style", "description": [], "signature": [ - "number" + "maplibregl.StyleSpecification" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.rotateTo.$2", - "type": "Object", + "id": "def-server.Map._updateDiff.$2", + "type": "CompoundType", "tags": [], "label": "options", "description": [], "signature": [ - "maplibregl.AnimationOptions | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.rotateTo.$3", - "type": "Object", - "tags": [], - "label": "eventData", - "description": [], - "signature": [ - "maplibregl.EventData | undefined" + "({ diff?: boolean | undefined; } & maplibregl.StyleOptions) | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": false } @@ -3275,377 +3742,357 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.resetNorth", + "id": "def-server.Map.getStyle", "type": "Function", "tags": [], - "label": "resetNorth", - "description": [], + "label": "getStyle", + "description": [ + "\nReturns the map's MapLibre style object, a JSON object which can be used to recreate the map's style.\n" + ], "signature": [ - "(options?: maplibregl.AnimationOptions | undefined, eventData?: maplibregl.EventData | undefined) => this" + "() => maplibregl.StyleSpecification" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.resetNorth.$1", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "signature": [ - "maplibregl.AnimationOptions | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.resetNorth.$2", - "type": "Object", - "tags": [], - "label": "eventData", - "description": [], - "signature": [ - "maplibregl.EventData | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] + "children": [], + "returnComment": [ + "The map's style JSON object." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.resetNorthPitch", + "id": "def-server.Map.isStyleLoaded", "type": "Function", "tags": [], - "label": "resetNorthPitch", - "description": [], + "label": "isStyleLoaded", + "description": [ + "\nReturns a Boolean indicating whether the map's style is fully loaded.\n" + ], + "signature": [ + "() => boolean | void" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [ + "A Boolean indicating whether the style is fully loaded." + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.addSource", + "type": "Function", + "tags": [ + "fires", + "see" + ], + "label": "addSource", + "description": [ + "\nAdds a source to the map's style.\n" + ], "signature": [ - "(options?: maplibregl.AnimationOptions | null | undefined, eventData?: maplibregl.EventData | null | undefined) => this" + "(id: string, source: maplibregl.SourceSpecification) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.resetNorthPitch.$1", - "type": "CompoundType", + "id": "def-server.Map.addSource.$1", + "type": "string", "tags": [], - "label": "options", - "description": [], + "label": "id", + "description": [ + "The ID of the source to add. Must not conflict with existing sources." + ], "signature": [ - "maplibregl.AnimationOptions | null | undefined" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.resetNorthPitch.$2", + "id": "def-server.Map.addSource.$2", "type": "CompoundType", "tags": [], - "label": "eventData", - "description": [], + "label": "source", + "description": [ + "The source object, conforming to the\nMapLibre Style Specification's [source definition](https://maplibre.org/maplibre-gl-js-docs/style-spec/#sources) or\n{@link CanvasSourceOptions }." + ], "signature": [ - "maplibregl.EventData | null | undefined" + "maplibregl.SourceSpecification" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.snapToNorth", + "id": "def-server.Map.isSourceLoaded", "type": "Function", "tags": [], - "label": "snapToNorth", - "description": [], + "label": "isSourceLoaded", + "description": [ + "\nReturns a Boolean indicating whether the source is loaded. Returns `true` if the source with\nthe given ID in the map's style has no outstanding network requests, otherwise `false`.\n" + ], "signature": [ - "(options?: maplibregl.AnimationOptions | undefined, eventData?: maplibregl.EventData | undefined) => this" + "(id: string) => boolean" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.snapToNorth.$1", - "type": "Object", + "id": "def-server.Map.isSourceLoaded.$1", + "type": "string", "tags": [], - "label": "options", - "description": [], - "signature": [ - "maplibregl.AnimationOptions | undefined" + "label": "id", + "description": [ + "The ID of the source to be checked." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.snapToNorth.$2", - "type": "Object", - "tags": [], - "label": "eventData", - "description": [], "signature": [ - "maplibregl.EventData | undefined" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "A Boolean indicating whether the source is loaded." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.getPitch", + "id": "def-server.Map.areTilesLoaded", "type": "Function", "tags": [], - "label": "getPitch", - "description": [], + "label": "areTilesLoaded", + "description": [ + "\nReturns a Boolean indicating whether all tiles in the viewport from all sources on\nthe style are loaded.\n" + ], "signature": [ - "() => number" + "() => boolean" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "A Boolean indicating whether all tiles are loaded." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setPitch", + "id": "def-server.Map.addSourceType", "type": "Function", - "tags": [], - "label": "setPitch", - "description": [], + "tags": [ + "private" + ], + "label": "addSourceType", + "description": [ + "\nAdds a [custom source type](#Custom Sources), making it available for use with\n{@link Map#addSource}." + ], "signature": [ - "(pitch: number, eventData?: maplibregl.EventData | undefined) => this" + "(name: string, SourceType: any, callback: maplibregl.Callback) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setPitch.$1", - "type": "number", + "id": "def-server.Map.addSourceType.$1", + "type": "string", "tags": [], - "label": "pitch", - "description": [], + "label": "name", + "description": [ + "The name of the source type; source definition objects use this name in the `{type: ...}` field." + ], "signature": [ - "number" + "string" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.addSourceType.$2", + "type": "Any", + "tags": [], + "label": "SourceType", + "description": [ + "A {@link Source } constructor." + ], + "signature": [ + "any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.setPitch.$2", - "type": "Object", + "id": "def-server.Map.addSourceType.$3", + "type": "Function", "tags": [], - "label": "eventData", - "description": [], + "label": "callback", + "description": [ + "Called when the source type is ready or with an error argument if there is an error." + ], "signature": [ - "maplibregl.EventData | undefined" + "maplibregl.Callback" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.cameraForBounds", + "id": "def-server.Map.removeSource", "type": "Function", "tags": [], - "label": "cameraForBounds", - "description": [], + "label": "removeSource", + "description": [ + "\nRemoves a source from the map's style.\n" + ], "signature": [ - "(bounds: maplibregl.LngLatBoundsLike, options?: maplibregl.CameraForBoundsOptions | undefined) => maplibregl.CameraForBoundsResult | undefined" + "(id: string) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.cameraForBounds.$1", - "type": "CompoundType", + "id": "def-server.Map.removeSource.$1", + "type": "string", "tags": [], - "label": "bounds", - "description": [], - "signature": [ - "maplibregl.LngLatBoundsLike" + "label": "id", + "description": [ + "The ID of the source to remove." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.cameraForBounds.$2", - "type": "Object", - "tags": [], - "label": "options", - "description": [], "signature": [ - "maplibregl.CameraForBoundsOptions | undefined" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.fitBounds", + "id": "def-server.Map.getSource", "type": "Function", - "tags": [], - "label": "fitBounds", - "description": [], + "tags": [ + "see", + "see", + "see" + ], + "label": "getSource", + "description": [ + "\nReturns the source with the specified ID in the map's style.\n\nThis method is often used to update a source using the instance members for the relevant\nsource type as defined in [Sources](#sources).\nFor example, setting the `data` for a GeoJSON source or updating the `url` and `coordinates`\nof an image source.\n" + ], "signature": [ - "(bounds: maplibregl.LngLatBoundsLike, options?: maplibregl.FitBoundsOptions | undefined, eventData?: maplibregl.EventData | undefined) => this" + "(id: string) => maplibregl.Source | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.fitBounds.$1", - "type": "CompoundType", - "tags": [], - "label": "bounds", - "description": [], - "signature": [ - "maplibregl.LngLatBoundsLike" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.fitBounds.$2", - "type": "Object", + "id": "def-server.Map.getSource.$1", + "type": "string", "tags": [], - "label": "options", - "description": [], - "signature": [ - "maplibregl.FitBoundsOptions | undefined" + "label": "id", + "description": [ + "The ID of the source to get." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.fitBounds.$3", - "type": "Object", - "tags": [], - "label": "eventData", - "description": [], "signature": [ - "maplibregl.EventData | undefined" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The style source with the specified ID or `undefined` if the ID\ncorresponds to no existing sources.\nThe shape of the object varies by source type.\nA list of options for each source type is available on the MapLibre Style Specification's\n[Sources](https://maplibre.org/maplibre-gl-js-docs/style-spec/sources/) page." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.fitScreenCoordinates", + "id": "def-server.Map.addImage", "type": "Function", - "tags": [], - "label": "fitScreenCoordinates", - "description": [], + "tags": [ + "see", + "see" + ], + "label": "addImage", + "description": [ + "\nAdd an image to the style. This image can be displayed on the map like any other icon in the style's\nsprite using the image's ID with\n[`icon-image`](https://maplibre.org/maplibre-gl-js-docs/style-spec/#layout-symbol-icon-image),\n[`background-pattern`](https://maplibre.org/maplibre-gl-js-docs/style-spec/#paint-background-background-pattern),\n[`fill-pattern`](https://maplibre.org/maplibre-gl-js-docs/style-spec/#paint-fill-fill-pattern),\nor [`line-pattern`](https://maplibre.org/maplibre-gl-js-docs/style-spec/#paint-line-line-pattern).\nA {@link Map.event:error} event will be fired if there is not enough space in the sprite to add this image.\n" + ], "signature": [ - "(p0: maplibregl.PointLike, p1: maplibregl.PointLike, bearing: number, options?: (maplibregl.AnimationOptions & maplibregl.CameraOptions) | undefined, eventData?: maplibregl.EventData | undefined) => this" + "(id: string, image: HTMLImageElement | ImageBitmap | ImageData | { width: number; height: number; data: Uint8Array | Uint8ClampedArray; } | maplibregl.StyleImageInterface, { pixelRatio, sdf, stretchX, stretchY, content }?: Partial | undefined) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.fitScreenCoordinates.$1", - "type": "CompoundType", + "id": "def-server.Map.addImage.$1", + "type": "string", "tags": [], - "label": "p0", - "description": [], + "label": "id", + "description": [ + "The ID of the image." + ], "signature": [ - "maplibregl.PointLike" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.fitScreenCoordinates.$2", + "id": "def-server.Map.addImage.$2", "type": "CompoundType", "tags": [], - "label": "p1", - "description": [], - "signature": [ - "maplibregl.PointLike" + "label": "image", + "description": [ + "The image as an `HTMLImageElement`, `ImageData`, `ImageBitmap` or object with `width`, `height`, and `data`\nproperties with the same format as `ImageData`." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.fitScreenCoordinates.$3", - "type": "number", - "tags": [], - "label": "bearing", - "description": [], "signature": [ - "number" + "HTMLImageElement | ImageBitmap | ImageData | { width: number; height: number; data: Uint8Array | Uint8ClampedArray; } | maplibregl.StyleImageInterface" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.fitScreenCoordinates.$4", - "type": "CompoundType", - "tags": [], - "label": "options", - "description": [], - "signature": [ - "(maplibregl.AnimationOptions & maplibregl.CameraOptions) | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.fitScreenCoordinates.$5", + "id": "def-server.Map.addImage.$3", "type": "Object", "tags": [], - "label": "eventData", + "label": "{ pixelRatio, sdf, stretchX, stretchY, content }", "description": [], "signature": [ - "maplibregl.EventData | undefined" + "Partial | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": false } @@ -3654,218 +4101,170 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.jumpTo", + "id": "def-server.Map.updateImage", "type": "Function", "tags": [], - "label": "jumpTo", - "description": [], + "label": "updateImage", + "description": [ + "\nUpdate an existing image in a style. This image can be displayed on the map like any other icon in the style's\nsprite using the image's ID with\n[`icon-image`](https://maplibre.org/maplibre-gl-js-docs/style-spec/#layout-symbol-icon-image),\n[`background-pattern`](https://maplibre.org/maplibre-gl-js-docs/style-spec/#paint-background-background-pattern),\n[`fill-pattern`](https://maplibre.org/maplibre-gl-js-docs/style-spec/#paint-fill-fill-pattern),\nor [`line-pattern`](https://maplibre.org/maplibre-gl-js-docs/style-spec/#paint-line-line-pattern).\n" + ], "signature": [ - "(options: maplibregl.CameraOptions, eventData?: maplibregl.EventData | undefined) => this" + "(id: string, image: HTMLImageElement | ImageBitmap | ImageData | maplibregl.StyleImageInterface | { width: number; height: number; data: Uint8Array | Uint8ClampedArray; }) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.jumpTo.$1", - "type": "Object", + "id": "def-server.Map.updateImage.$1", + "type": "string", "tags": [], - "label": "options", - "description": [], + "label": "id", + "description": [ + "The ID of the image." + ], "signature": [ - "maplibregl.CameraOptions" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.jumpTo.$2", - "type": "Object", + "id": "def-server.Map.updateImage.$2", + "type": "CompoundType", "tags": [], - "label": "eventData", - "description": [], + "label": "image", + "description": [ + "The image as an `HTMLImageElement`, `ImageData`, `ImageBitmap` or object with `width`, `height`, and `data`\nproperties with the same format as `ImageData`." + ], "signature": [ - "maplibregl.EventData | undefined" + "HTMLImageElement | ImageBitmap | ImageData | maplibregl.StyleImageInterface | { width: number; height: number; data: Uint8Array | Uint8ClampedArray; }" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.easeTo", + "id": "def-server.Map.hasImage", "type": "Function", "tags": [], - "label": "easeTo", - "description": [], + "label": "hasImage", + "description": [ + "\nCheck whether or not an image with a specific ID exists in the style. This checks both images\nin the style's original sprite and any images\nthat have been added at runtime using {@link Map#addImage}.\n" + ], "signature": [ - "(options: maplibregl.EaseToOptions, eventData?: maplibregl.EventData | undefined) => this" + "(id: string) => boolean" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.easeTo.$1", - "type": "Object", + "id": "def-server.Map.hasImage.$1", + "type": "string", "tags": [], - "label": "options", - "description": [], - "signature": [ - "maplibregl.EaseToOptions" + "label": "id", + "description": [ + "The ID of the image." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.easeTo.$2", - "type": "Object", - "tags": [], - "label": "eventData", - "description": [], "signature": [ - "maplibregl.EventData | undefined" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "A Boolean indicating whether the image exists." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.flyTo", + "id": "def-server.Map.removeImage", "type": "Function", "tags": [], - "label": "flyTo", - "description": [], + "label": "removeImage", + "description": [ + "\nRemove an image from a style. This can be an image from the style's original\nsprite or any images\nthat have been added at runtime using {@link Map#addImage}.\n" + ], "signature": [ - "(options: maplibregl.FlyToOptions, eventData?: maplibregl.EventData | undefined) => this" + "(id: string) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.flyTo.$1", - "type": "Object", + "id": "def-server.Map.removeImage.$1", + "type": "string", "tags": [], - "label": "options", - "description": [], - "signature": [ - "maplibregl.FlyToOptions" + "label": "id", + "description": [ + "The ID of the image." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.flyTo.$2", - "type": "Object", - "tags": [], - "label": "eventData", - "description": [], "signature": [ - "maplibregl.EventData | undefined" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": false + "isRequired": true } ], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.isEasing", + "id": "def-server.Map.loadImage", "type": "Function", - "tags": [], - "label": "isEasing", - "description": [], - "signature": [ - "() => boolean" + "tags": [ + "see" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.stop", - "type": "Function", - "tags": [], - "label": "stop", - "description": [], - "signature": [ - "() => this" + "label": "loadImage", + "description": [ + "\nLoad an image from an external URL to be used with {@link Map#addImage}. External\ndomains must support [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS).\n" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.on", - "type": "Function", - "tags": [], - "label": "on", - "description": [], "signature": [ - "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & maplibregl.EventData) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & maplibregl.EventData) => void): this; (type: string, listener: (ev: any) => void): this; }" + "(url: string, callback: maplibregl.GetImageCallback) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.on.$1", - "type": "Uncategorized", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "T" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.on.$2", + "id": "def-server.Map.loadImage.$1", "type": "string", "tags": [], - "label": "layer", - "description": [], + "label": "url", + "description": [ + "The URL of the image file. Image file must be in png, webp, or jpg format." + ], "signature": [ "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.on.$3", + "id": "def-server.Map.loadImage.$2", "type": "Function", "tags": [], - "label": "listener", - "description": [], + "label": "callback", + "description": [ + "Expecting `callback(error, data)`. Called when the image has loaded or with an error argument if there is an error." + ], "signature": [ - "(ev: maplibregl.MapLayerEventType[T] & maplibregl.EventData) => void" + "maplibregl.GetImageCallback" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } @@ -3874,144 +4273,161 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.on", + "id": "def-server.Map.listImages", "type": "Function", "tags": [], - "label": "on", - "description": [], + "label": "listImages", + "description": [ + "\nReturns an Array of strings containing the IDs of all images currently available in the map.\nThis includes both images from the style's original sprite\nand any images that have been added at runtime using {@link Map#addImage}.\n" + ], + "signature": [ + "() => string[]" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [ + "An Array of strings containing the names of all sprites/images currently available in the map." + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.addLayer", + "type": "Function", + "tags": [ + "see", + "see", + "see" + ], + "label": "addLayer", + "description": [ + "\nAdds a [MapLibre style layer](https://maplibre.org/maplibre-gl-js-docs/style-spec/#layers)\nto the map's style.\n\nA layer defines how data from a specified source will be styled. Read more about layer types\nand available paint and layout properties in the [MapLibre Style Specification](https://maplibre.org/maplibre-gl-js-docs/style-spec/#layers).\n" + ], "signature": [ - "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & maplibregl.EventData) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & maplibregl.EventData) => void): this; (type: string, listener: (ev: any) => void): this; }" + "(layer: maplibregl.LayerSpecification | maplibregl.CustomLayerInterface, beforeId?: string | undefined) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.on.$1", - "type": "Uncategorized", + "id": "def-server.Map.addLayer.$1", + "type": "CompoundType", "tags": [], - "label": "type", - "description": [], + "label": "layer", + "description": [ + "The layer to add, conforming to either the MapLibre Style Specification's [layer definition](https://maplibre.org/maplibre-gl-js-docs/style-spec/#layers) or, less commonly, the {@link CustomLayerInterface } specification.\nThe MapLibre Style Specification's layer definition is appropriate for most layers." + ], "signature": [ - "T" + "maplibregl.LayerSpecification | maplibregl.CustomLayerInterface" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.on.$2", - "type": "Function", + "id": "def-server.Map.addLayer.$2", + "type": "string", "tags": [], - "label": "listener", - "description": [], + "label": "beforeId", + "description": [ + "The ID of an existing layer to insert the new layer before,\nresulting in the new layer appearing visually beneath the existing layer.\nIf this argument is not specified, the layer will be appended to the end of the layers array\nand appear visually above all other layers." + ], "signature": [ - "(ev: maplibregl.MapEventType[T] & maplibregl.EventData) => void" + "string | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": true + "isRequired": false } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.on", + "id": "def-server.Map.moveLayer", "type": "Function", "tags": [], - "label": "on", - "description": [], + "label": "moveLayer", + "description": [ + "\nMoves a layer to a different z-position.\n" + ], "signature": [ - "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & maplibregl.EventData) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & maplibregl.EventData) => void): this; (type: string, listener: (ev: any) => void): this; }" + "(id: string, beforeId?: string | undefined) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.on.$1", + "id": "def-server.Map.moveLayer.$1", "type": "string", "tags": [], - "label": "type", - "description": [], + "label": "id", + "description": [ + "The ID of the layer to move." + ], "signature": [ "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.on.$2", - "type": "Function", + "id": "def-server.Map.moveLayer.$2", + "type": "string", "tags": [], - "label": "listener", - "description": [], + "label": "beforeId", + "description": [ + "The ID of an existing layer to insert the new layer before. When viewing the map, the `id` layer will appear beneath the `beforeId` layer. If `beforeId` is omitted, the layer will be appended to the end of the layers array and appear above all other layers on the map." + ], "signature": [ - "(ev: any) => void" + "string | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": true + "isRequired": false } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.once", + "id": "def-server.Map.removeLayer", "type": "Function", - "tags": [], - "label": "once", - "description": [], + "tags": [ + "fires" + ], + "label": "removeLayer", + "description": [ + "\nRemoves the layer with the given ID from the map's style.\n\nIf no such layer exists, an `error` event is fired.\n" + ], "signature": [ - "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & maplibregl.EventData) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & maplibregl.EventData) => void): this; (type: string, listener: (ev: any) => void): this; }" + "(id: string) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.once.$1", - "type": "Uncategorized", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "T" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.once.$2", + "id": "def-server.Map.removeLayer.$1", "type": "string", "tags": [], - "label": "layer", - "description": [], - "signature": [ - "string" + "label": "id", + "description": [ + "id of the layer to remove" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.once.$3", - "type": "Function", - "tags": [], - "label": "listener", - "description": [], "signature": [ - "(ev: maplibregl.MapLayerEventType[T] & maplibregl.EventData) => void" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } @@ -4020,633 +4436,609 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.once", + "id": "def-server.Map.getLayer", "type": "Function", - "tags": [], - "label": "once", - "description": [], + "tags": [ + "see", + "see" + ], + "label": "getLayer", + "description": [ + "\nReturns the layer with the specified ID in the map's style.\n" + ], "signature": [ - "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & maplibregl.EventData) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & maplibregl.EventData) => void): this; (type: string, listener: (ev: any) => void): this; }" + "(id: string) => StyleLayer" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.once.$1", - "type": "Uncategorized", + "id": "def-server.Map.getLayer.$1", + "type": "string", "tags": [], - "label": "type", - "description": [], - "signature": [ - "T" + "label": "id", + "description": [ + "The ID of the layer to get." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.once.$2", - "type": "Function", - "tags": [], - "label": "listener", - "description": [], "signature": [ - "(ev: maplibregl.MapEventType[T] & maplibregl.EventData) => void" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The layer with the specified ID, or `undefined`\nif the ID corresponds to no existing layers." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.once", + "id": "def-server.Map.setLayerZoomRange", "type": "Function", "tags": [], - "label": "once", - "description": [], + "label": "setLayerZoomRange", + "description": [ + "\nSets the zoom extent for the specified style layer. The zoom extent includes the\n[minimum zoom level](https://maplibre.org/maplibre-gl-js-docs/style-spec/#layer-minzoom)\nand [maximum zoom level](https://maplibre.org/maplibre-gl-js-docs/style-spec/#layer-maxzoom))\nat which the layer will be rendered.\n\nNote: For style layers using vector sources, style layers cannot be rendered at zoom levels lower than the\nminimum zoom level of the _source layer_ because the data does not exist at those zoom levels. If the minimum\nzoom level of the source layer is higher than the minimum zoom level defined in the style layer, the style\nlayer will not be rendered at all zoom levels in the zoom range.\n" + ], "signature": [ - "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & maplibregl.EventData) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & maplibregl.EventData) => void): this; (type: string, listener: (ev: any) => void): this; }" + "(layerId: string, minzoom: number, maxzoom: number) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.once.$1", + "id": "def-server.Map.setLayerZoomRange.$1", "type": "string", "tags": [], - "label": "type", - "description": [], + "label": "layerId", + "description": [ + "The ID of the layer to which the zoom extent will be applied." + ], "signature": [ "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.once.$2", - "type": "Function", + "id": "def-server.Map.setLayerZoomRange.$2", + "type": "number", "tags": [], - "label": "listener", - "description": [], + "label": "minzoom", + "description": [ + "The minimum zoom to set (0-24)." + ], + "signature": [ + "number" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.setLayerZoomRange.$3", + "type": "number", + "tags": [], + "label": "maxzoom", + "description": [ + "The maximum zoom to set (0-24)." + ], "signature": [ - "(ev: any) => void" + "number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.off", + "id": "def-server.Map.setFilter", "type": "Function", - "tags": [], - "label": "off", - "description": [], + "tags": [ + "see" + ], + "label": "setFilter", + "description": [ + "\nSets the filter for the specified style layer.\n\nFilters control which features a style layer renders from its source.\nAny feature for which the filter expression evaluates to `true` will be\nrendered on the map. Those that are false will be hidden.\n\nUse `setFilter` to show a subset of your source data.\n\nTo clear the filter, pass `null` or `undefined` as the second parameter.\n" + ], "signature": [ - "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & maplibregl.EventData) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & maplibregl.EventData) => void): this; (type: string, listener: (ev: any) => void): this; }" + "(layerId: string, filter?: maplibregl.FilterSpecification | null | undefined, options?: maplibregl.StyleSetterOptions | undefined) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.off.$1", - "type": "Uncategorized", + "id": "def-server.Map.setFilter.$1", + "type": "string", "tags": [], - "label": "type", - "description": [], + "label": "layerId", + "description": [ + "The ID of the layer to which the filter will be applied." + ], "signature": [ - "T" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.off.$2", - "type": "string", + "id": "def-server.Map.setFilter.$2", + "type": "CompoundType", "tags": [], - "label": "layer", - "description": [], + "label": "filter", + "description": [ + "The filter, conforming to the MapLibre Style Specification's\n[filter definition](https://maplibre.org/maplibre-gl-js-docs/style-spec/layers/#filter). If `null` or `undefined` is provided, the function removes any existing filter from the layer." + ], "signature": [ - "string" + "maplibregl.FilterSpecification | null | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": true + "isRequired": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.off.$3", - "type": "Function", + "id": "def-server.Map.setFilter.$3", + "type": "Object", "tags": [], - "label": "listener", - "description": [], + "label": "options", + "description": [ + "Options object." + ], "signature": [ - "(ev: maplibregl.MapLayerEventType[T] & maplibregl.EventData) => void" + "maplibregl.StyleSetterOptions | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": true + "isRequired": false } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.off", + "id": "def-server.Map.getFilter", "type": "Function", - "tags": [], - "label": "off", - "description": [], + "tags": [], + "label": "getFilter", + "description": [ + "\nReturns the filter applied to the specified style layer.\n" + ], "signature": [ - "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & maplibregl.EventData) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & maplibregl.EventData) => void): this; (type: string, listener: (ev: any) => void): this; }" + "(layerId: string) => void | maplibregl.FilterSpecification" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.off.$1", - "type": "Uncategorized", + "id": "def-server.Map.getFilter.$1", + "type": "string", "tags": [], - "label": "type", - "description": [], - "signature": [ - "T" + "label": "layerId", + "description": [ + "The ID of the style layer whose filter to get." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.off.$2", - "type": "Function", - "tags": [], - "label": "listener", - "description": [], "signature": [ - "(ev: maplibregl.MapEventType[T] & maplibregl.EventData) => void" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The layer's filter." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.off", + "id": "def-server.Map.setPaintProperty", "type": "Function", - "tags": [], - "label": "off", - "description": [], + "tags": [ + "see", + "see" + ], + "label": "setPaintProperty", + "description": [ + "\nSets the value of a paint property in the specified style layer.\n" + ], "signature": [ - "{ (type: T, layer: string, listener: (ev: maplibregl.MapLayerEventType[T] & maplibregl.EventData) => void): this; (type: T, listener: (ev: maplibregl.MapEventType[T] & maplibregl.EventData) => void): this; (type: string, listener: (ev: any) => void): this; }" + "(layerId: string, name: string, value: any, options?: maplibregl.StyleSetterOptions | undefined) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.off.$1", + "id": "def-server.Map.setPaintProperty.$1", "type": "string", "tags": [], - "label": "type", - "description": [], + "label": "layerId", + "description": [ + "The ID of the layer to set the paint property in." + ], "signature": [ "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.off.$2", - "type": "Function", + "id": "def-server.Map.setPaintProperty.$2", + "type": "string", "tags": [], - "label": "listener", - "description": [], + "label": "name", + "description": [ + "The name of the paint property to set." + ], "signature": [ - "(ev: any) => void" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.scrollZoom", - "type": "Object", - "tags": [], - "label": "scrollZoom", - "description": [], - "signature": [ - "maplibregl.ScrollZoomHandler" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.boxZoom", - "type": "Object", - "tags": [], - "label": "boxZoom", - "description": [], - "signature": [ - "maplibregl.BoxZoomHandler" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.dragRotate", - "type": "Object", - "tags": [], - "label": "dragRotate", - "description": [], - "signature": [ - "maplibregl.DragRotateHandler" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.dragPan", - "type": "Object", - "tags": [], - "label": "dragPan", - "description": [], - "signature": [ - "maplibregl.DragPanHandler" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.keyboard", - "type": "Object", - "tags": [], - "label": "keyboard", - "description": [], - "signature": [ - "maplibregl.KeyboardHandler" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.doubleClickZoom", - "type": "Object", - "tags": [], - "label": "doubleClickZoom", - "description": [], - "signature": [ - "maplibregl.DoubleClickZoomHandler" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.touchZoomRotate", - "type": "Object", - "tags": [], - "label": "touchZoomRotate", - "description": [], - "signature": [ - "maplibregl.TouchZoomRotateHandler" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Map.touchPitch", - "type": "Object", - "tags": [], - "label": "touchPitch", - "description": [], - "signature": [ - "maplibregl.TouchPitchHandler" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapMouseEvent", - "type": "Class", - "tags": [], - "label": "MapMouseEvent", - "description": [], - "signature": [ - "maplibregl.MapMouseEvent extends maplibregl.MapboxEvent" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapMouseEvent.type", - "type": "CompoundType", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"click\" | \"dblclick\" | \"mousedown\" | \"mouseup\" | \"mousemove\" | \"mouseenter\" | \"mouseleave\" | \"mouseover\" | \"mouseout\" | \"contextmenu\"" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapMouseEvent.point", - "type": "Object", - "tags": [], - "label": "point", - "description": [], - "signature": [ - "maplibregl.Point" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapMouseEvent.lngLat", - "type": "Object", - "tags": [], - "label": "lngLat", - "description": [], - "signature": [ - "maplibregl.LngLat" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapMouseEvent.preventDefault", - "type": "Function", - "tags": [], - "label": "preventDefault", - "description": [], - "signature": [ - "() => void" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapMouseEvent.defaultPrevented", - "type": "boolean", - "tags": [], - "label": "defaultPrevented", - "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point", - "type": "Class", - "tags": [], - "label": "Point", - "description": [ - "\nPoint" - ], - "signature": [ - "maplibregl.Point" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.x", - "type": "number", - "tags": [], - "label": "x", - "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.y", - "type": "number", - "tags": [], - "label": "y", - "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.setPaintProperty.$3", + "type": "Any", + "tags": [], + "label": "value", + "description": [ + "The value of the paint property to set.\nMust be of a type appropriate for the property, as defined in the [MapLibre Style Specification](https://maplibre.org/maplibre-gl-js-docs/style-spec/)." + ], + "signature": [ + "any" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.setPaintProperty.$4", + "type": "Object", + "tags": [], + "label": "options", + "description": [ + "Options object." + ], + "signature": [ + "maplibregl.StyleSetterOptions | undefined" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.Unnamed", + "id": "def-server.Map.getPaintProperty", "type": "Function", "tags": [], - "label": "Constructor", - "description": [], + "label": "getPaintProperty", + "description": [ + "\nReturns the value of a paint property in the specified style layer.\n" + ], "signature": [ - "any" + "(layerId: string, name: string) => unknown" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.Unnamed.$1", - "type": "number", + "id": "def-server.Map.getPaintProperty.$1", + "type": "string", "tags": [], - "label": "x", - "description": [], + "label": "layerId", + "description": [ + "The ID of the layer to get the paint property from." + ], "signature": [ - "number" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.Unnamed.$2", - "type": "number", + "id": "def-server.Map.getPaintProperty.$2", + "type": "string", "tags": [], - "label": "y", - "description": [], + "label": "name", + "description": [ + "The name of a paint property to get." + ], "signature": [ - "number" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The value of the specified paint property." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.clone", + "id": "def-server.Map.setLayoutProperty", "type": "Function", "tags": [], - "label": "clone", - "description": [], - "signature": [ - "() => maplibregl.Point" + "label": "setLayoutProperty", + "description": [ + "\nSets the value of a layout property in the specified style layer.\n" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.add", - "type": "Function", - "tags": [], - "label": "add", - "description": [], "signature": [ - "(p: maplibregl.Point) => maplibregl.Point" + "(layerId: string, name: string, value: any, options?: maplibregl.StyleSetterOptions | undefined) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.add.$1", - "type": "Object", + "id": "def-server.Map.setLayoutProperty.$1", + "type": "string", "tags": [], - "label": "p", - "description": [], + "label": "layerId", + "description": [ + "The ID of the layer to set the layout property in." + ], + "signature": [ + "string" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.setLayoutProperty.$2", + "type": "string", + "tags": [], + "label": "name", + "description": [ + "The name of the layout property to set." + ], + "signature": [ + "string" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.setLayoutProperty.$3", + "type": "Any", + "tags": [], + "label": "value", + "description": [ + "The value of the layout property. Must be of a type appropriate for the property, as defined in the [MapLibre Style Specification](https://maplibre.org/maplibre-gl-js-docs/style-spec/)." + ], "signature": [ - "maplibregl.Point" + "any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.setLayoutProperty.$4", + "type": "Object", + "tags": [], + "label": "options", + "description": [ + "Options object." + ], + "signature": [ + "maplibregl.StyleSetterOptions | undefined" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": false } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.sub", + "id": "def-server.Map.getLayoutProperty", "type": "Function", "tags": [], - "label": "sub", - "description": [], + "label": "getLayoutProperty", + "description": [ + "\nReturns the value of a layout property in the specified style layer.\n" + ], "signature": [ - "(p: maplibregl.Point) => maplibregl.Point" + "(layerId: string, name: string) => any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.sub.$1", - "type": "Object", + "id": "def-server.Map.getLayoutProperty.$1", + "type": "string", "tags": [], - "label": "p", - "description": [], + "label": "layerId", + "description": [ + "The ID of the layer to get the layout property from." + ], + "signature": [ + "string" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.getLayoutProperty.$2", + "type": "string", + "tags": [], + "label": "name", + "description": [ + "The name of the layout property to get." + ], "signature": [ - "maplibregl.Point" + "string" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The value of the specified layout property." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.mult", + "id": "def-server.Map.setLight", "type": "Function", "tags": [], - "label": "mult", - "description": [], + "label": "setLight", + "description": [ + "\nSets the any combination of light values.\n" + ], "signature": [ - "(k: number) => maplibregl.Point" + "(light: maplibregl.LightSpecification, options?: maplibregl.StyleSetterOptions | undefined) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.mult.$1", - "type": "number", + "id": "def-server.Map.setLight.$1", + "type": "Object", "tags": [], - "label": "k", - "description": [], + "label": "light", + "description": [ + "Light properties to set. Must conform to the [MapLibre Style Specification](https://maplibre.org/maplibre-gl-js-docs/style-spec/#light)." + ], "signature": [ - "number" + "maplibregl.LightSpecification" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.setLight.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [ + "Options object." + ], + "signature": [ + "maplibregl.StyleSetterOptions | undefined" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": false } ], - "returnComment": [] + "returnComment": [ + "`this`" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.div", + "id": "def-server.Map.getLight", "type": "Function", "tags": [], - "label": "div", - "description": [], + "label": "getLight", + "description": [ + "\nReturns the value of the light object.\n" + ], + "signature": [ + "() => any" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [ + "light Light properties of the style." + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.setFeatureState", + "type": "Function", + "tags": [ + "see" + ], + "label": "setFeatureState", + "description": [ + "\nSets the `state` of a feature.\nA feature's `state` is a set of user-defined key-value pairs that are assigned to a feature at runtime.\nWhen using this method, the `state` object is merged with any existing key-value pairs in the feature's state.\nFeatures are identified by their `feature.id` attribute, which can be any number or string.\n\nThis method can only be used with sources that have a `feature.id` attribute. The `feature.id` attribute can be defined in three ways:\n- For vector or GeoJSON sources, including an `id` attribute in the original data file.\n- For vector or GeoJSON sources, using the [`promoteId`](https://maplibre.org/maplibre-gl-js-docs/style-spec/sources/#vector-promoteId) option at the time the source is defined.\n- For GeoJSON sources, using the [`generateId`](https://maplibre.org/maplibre-gl-js-docs/style-spec/sources/#geojson-generateId) option to auto-assign an `id` based on the feature's index in the source data. If you change feature data using `map.getSource('some id').setData(..)`, you may need to re-apply state taking into account updated `id` values.\n\n_Note: You can use the [`feature-state` expression](https://maplibre.org/maplibre-gl-js-docs/style-spec/expressions/#feature-state) to access the values in a feature's state object for the purposes of styling._\n" + ], "signature": [ - "(k: number) => maplibregl.Point" + "(feature: maplibregl.FeatureIdentifier, state: any) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.div.$1", - "type": "number", + "id": "def-server.Map.setFeatureState.$1", + "type": "Object", "tags": [], - "label": "k", - "description": [], + "label": "feature", + "description": [ + "Feature identifier. Feature objects returned from\n{@link MapqueryRenderedFeatures } or event handlers can be used as feature identifiers." + ], "signature": [ - "number" + "maplibregl.FeatureIdentifier" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.setFeatureState.$2", + "type": "Any", + "tags": [], + "label": "state", + "description": [ + "A set of key-value pairs. The values should be valid JSON types." + ], + "signature": [ + "any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } @@ -4655,208 +5047,235 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.rotate", + "id": "def-server.Map.removeFeatureState", "type": "Function", "tags": [], - "label": "rotate", - "description": [], + "label": "removeFeatureState", + "description": [ + "\nRemoves the `state` of a feature, setting it back to the default behavior.\nIf only a `target.source` is specified, it will remove the state for all features from that source.\nIf `target.id` is also specified, it will remove all keys for that feature's state.\nIf `key` is also specified, it removes only that key from that feature's state.\nFeatures are identified by their `feature.id` attribute, which can be any number or string.\n" + ], "signature": [ - "(a: number) => maplibregl.Point" + "(target: maplibregl.FeatureIdentifier, key?: string | undefined) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.rotate.$1", - "type": "number", + "id": "def-server.Map.removeFeatureState.$1", + "type": "Object", "tags": [], - "label": "a", - "description": [], + "label": "target", + "description": [ + "Identifier of where to remove state. It can be a source, a feature, or a specific key of feature.\nFeature objects returned from {@link MapqueryRenderedFeatures } or event handlers can be used as feature identifiers." + ], "signature": [ - "number" + "maplibregl.FeatureIdentifier" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.removeFeatureState.$2", + "type": "string", + "tags": [], + "label": "key", + "description": [ + "(optional) The key in the feature state to reset." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": false } ], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.matMult", + "id": "def-server.Map.getFeatureState", "type": "Function", "tags": [], - "label": "matMult", - "description": [], + "label": "getFeatureState", + "description": [ + "\nGets the `state` of a feature.\nA feature's `state` is a set of user-defined key-value pairs that are assigned to a feature at runtime.\nFeatures are identified by their `feature.id` attribute, which can be any number or string.\n\n_Note: To access the values in a feature's state object for the purposes of styling the feature, use the [`feature-state` expression](https://maplibre.org/maplibre-gl-js-docs/style-spec/expressions/#feature-state)._\n" + ], "signature": [ - "(m: number) => maplibregl.Point" + "(feature: maplibregl.FeatureIdentifier) => any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.matMult.$1", - "type": "number", + "id": "def-server.Map.getFeatureState.$1", + "type": "Object", "tags": [], - "label": "m", - "description": [], + "label": "feature", + "description": [ + "Feature identifier. Feature objects returned from\n{@link MapqueryRenderedFeatures } or event handlers can be used as feature identifiers." + ], "signature": [ - "number" + "maplibregl.FeatureIdentifier" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The state of the feature: a set of key-value pairs that was assigned to the feature at runtime." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.unit", + "id": "def-server.Map.getContainer", "type": "Function", "tags": [], - "label": "unit", - "description": [], + "label": "getContainer", + "description": [ + "\nReturns the map's containing HTML element.\n" + ], "signature": [ - "() => maplibregl.Point" + "() => HTMLElement" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The map's container." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.perp", + "id": "def-server.Map.getCanvasContainer", "type": "Function", - "tags": [], - "label": "perp", - "description": [], + "tags": [ + "see" + ], + "label": "getCanvasContainer", + "description": [ + "\nReturns the HTML element containing the map's `` element.\n\nIf you want to add non-GL overlays to the map, you should append them to this element.\n\nThis is the element to which event bindings for map interactivity (such as panning and zooming) are\nattached. It will receive bubbled events from child elements such as the ``, but not from\nmap controls.\n" + ], "signature": [ - "() => maplibregl.Point" + "() => HTMLElement" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The container of the map's ``." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.round", + "id": "def-server.Map.getCanvas", + "type": "Function", + "tags": [ + "see", + "see", + "see" + ], + "label": "getCanvas", + "description": [ + "\nReturns the map's `` element.\n" + ], + "signature": [ + "() => HTMLCanvasElement" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [ + "The map's `` element." + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._containerDimensions", "type": "Function", "tags": [], - "label": "round", + "label": "_containerDimensions", "description": [], "signature": [ - "() => maplibregl.Point" + "() => number[]" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.mag", + "id": "def-server.Map._setupContainer", "type": "Function", "tags": [], - "label": "mag", + "label": "_setupContainer", "description": [], "signature": [ - "() => number" + "() => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.equals", + "id": "def-server.Map._resizeCanvas", "type": "Function", "tags": [], - "label": "equals", + "label": "_resizeCanvas", "description": [], "signature": [ - "(p: maplibregl.Point) => boolean" + "(width: number, height: number, pixelRatio: number) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.equals.$1", - "type": "Object", + "id": "def-server.Map._resizeCanvas.$1", + "type": "number", "tags": [], - "label": "p", + "label": "width", "description": [], "signature": [ - "maplibregl.Point" + "number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.dist", - "type": "Function", - "tags": [], - "label": "dist", - "description": [], - "signature": [ - "(p: maplibregl.Point) => number" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ + }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.dist.$1", - "type": "Object", + "id": "def-server.Map._resizeCanvas.$2", + "type": "number", "tags": [], - "label": "p", + "label": "height", "description": [], "signature": [ - "maplibregl.Point" + "number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.distSqr", - "type": "Function", - "tags": [], - "label": "distSqr", - "description": [], - "signature": [ - "(p: maplibregl.Point) => number" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ + }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.distSqr.$1", - "type": "Object", + "id": "def-server.Map._resizeCanvas.$3", + "type": "number", "tags": [], - "label": "p", + "label": "pixelRatio", "description": [], "signature": [ - "maplibregl.Point" + "number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } @@ -4865,73 +5284,43 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.angle", + "id": "def-server.Map._setupPainter", "type": "Function", "tags": [], - "label": "angle", + "label": "_setupPainter", "description": [], "signature": [ - "() => number" + "() => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [], "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.angleTo", - "type": "Function", - "tags": [], - "label": "angleTo", - "description": [], - "signature": [ - "(p: maplibregl.Point) => number" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.angleTo.$1", - "type": "Object", - "tags": [], - "label": "p", - "description": [], - "signature": [ - "maplibregl.Point" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.angleWidth", + "id": "def-server.Map._contextLost", "type": "Function", "tags": [], - "label": "angleWidth", + "label": "_contextLost", "description": [], "signature": [ - "(p: maplibregl.Point) => number" + "(event: any) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.angleWidth.$1", - "type": "Object", + "id": "def-server.Map._contextLost.$1", + "type": "Any", "tags": [], - "label": "p", + "label": "event", "description": [], "signature": [ - "maplibregl.Point" + "any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } @@ -4940,42 +5329,28 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.angleWithSep", + "id": "def-server.Map._contextRestored", "type": "Function", "tags": [], - "label": "angleWithSep", + "label": "_contextRestored", "description": [], "signature": [ - "(x: number, y: number) => number" + "(event: any) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.angleWithSep.$1", - "type": "number", - "tags": [], - "label": "x", - "description": [], - "signature": [ - "number" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.angleWithSep.$2", - "type": "number", + "id": "def-server.Map._contextRestored.$1", + "type": "Any", "tags": [], - "label": "y", + "label": "event", "description": [], "signature": [ - "number" + "any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } @@ -4984,235 +5359,151 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.convert", + "id": "def-server.Map._onMapScroll", "type": "Function", "tags": [], - "label": "convert", + "label": "_onMapScroll", "description": [], "signature": [ - "(a: maplibregl.PointLike) => maplibregl.Point" + "(event: any) => boolean" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Point.convert.$1", - "type": "CompoundType", + "id": "def-server.Map._onMapScroll.$1", + "type": "Any", "tags": [], - "label": "a", + "label": "event", "description": [], "signature": [ - "maplibregl.PointLike" + "any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } ], "returnComment": [] - } - ], - "initialIsOpen": false - } - ], - "functions": [], - "interfaces": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.CustomLayerInterface", - "type": "Interface", - "tags": [], - "label": "CustomLayerInterface", - "description": [], - "signature": [ - "maplibregl.CustomLayerInterface" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.CustomLayerInterface.id", - "type": "string", - "tags": [], - "label": "id", - "description": [ - "A unique layer id." - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.CustomLayerInterface.type", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "\"custom\"" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.CustomLayerInterface.renderingMode", - "type": "CompoundType", - "tags": [], - "label": "renderingMode", - "description": [], - "signature": [ - "\"2d\" | \"3d\" | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.CustomLayerInterface.onRemove", + "id": "def-server.Map.loaded", "type": "Function", "tags": [], - "label": "onRemove", + "label": "loaded", "description": [ - "\nOptional method called when the layer has been removed from the Map with Map#removeLayer.\nThis gives the layer a chance to clean up gl resources and event listeners." + "\nReturns a Boolean indicating whether the map is fully loaded.\n\nReturns `false` if the style is not yet fully loaded,\nor if there has been a change to the sources or style that\nhas not yet fully loaded.\n" ], "signature": [ - "((map: maplibregl.Map, gl: WebGLRenderingContext) => void) | undefined" + "() => boolean" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.CustomLayerInterface.onRemove.$1", - "type": "Object", - "tags": [], - "label": "map", - "description": [ - "The Map this custom layer was just added to." - ], - "signature": [ - "maplibregl.Map" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.CustomLayerInterface.onRemove.$2", - "type": "Object", - "tags": [], - "label": "gl", - "description": [ - "The gl context for the map." - ], - "signature": [ - "WebGLRenderingContext" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] + "children": [], + "returnComment": [ + "A Boolean indicating whether the map is fully loaded." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.CustomLayerInterface.onAdd", + "id": "def-server.Map._update", "type": "Function", - "tags": [], - "label": "onAdd", + "tags": [ + "private" + ], + "label": "_update", "description": [ - "\nOptional method called when the layer has been added to the Map with Map#addLayer.\nThis gives the layer a chance to initialize gl resources and register event listeners." + "\nUpdate this map's style and sources, and re-render the map.\n" ], "signature": [ - "((map: maplibregl.Map, gl: WebGLRenderingContext) => void) | undefined" + "(updateStyle?: boolean | undefined) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.CustomLayerInterface.onAdd.$1", - "type": "Object", - "tags": [], - "label": "map", - "description": [ - "The Map this custom layer was just added to." - ], - "signature": [ - "maplibregl.Map" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.CustomLayerInterface.onAdd.$2", - "type": "Object", + "id": "def-server.Map._update.$1", + "type": "CompoundType", "tags": [], - "label": "gl", + "label": "updateStyle", "description": [ - "The gl context for the map." + "mark the map's style for reprocessing as\nwell as its sources" ], "signature": [ - "WebGLRenderingContext" + "boolean | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, - "isRequired": true + "isRequired": false } ], - "returnComment": [] + "returnComment": [ + "this" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.CustomLayerInterface.prerender", + "id": "def-server.Map._requestRenderFrame", "type": "Function", - "tags": [], - "label": "prerender", + "tags": [ + "private" + ], + "label": "_requestRenderFrame", "description": [ - "\nOptional method called during a render frame to allow a layer to prepare resources\nor render into a texture.\n\nThe layer cannot make any assumptions about the current GL state and must bind a framebuffer\nbefore rendering." + "\nRequest that the given callback be executed during the next render\nframe. Schedule a render frame if one is not already scheduled." ], "signature": [ - "((gl: WebGLRenderingContext, matrix: number[]) => void) | undefined" + "(callback: () => void) => number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.CustomLayerInterface.prerender.$1", - "type": "Object", + "id": "def-server.Map._requestRenderFrame.$1", + "type": "Function", "tags": [], - "label": "gl", - "description": [ - "The map's gl context." - ], + "label": "callback", + "description": [], "signature": [ - "WebGLRenderingContext" + "() => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true - }, + } + ], + "returnComment": [ + "An id that can be used to cancel the callback" + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._cancelRenderFrame", + "type": "Function", + "tags": [], + "label": "_cancelRenderFrame", + "description": [], + "signature": [ + "(id: number) => void" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.CustomLayerInterface.prerender.$2", - "type": "Array", + "id": "def-server.Map._cancelRenderFrame.$1", + "type": "number", "tags": [], - "label": "matrix", - "description": [ - "The map's camera matrix. It projects spherical mercator coordinates to gl\ncoordinates. The mercator coordinate [0, 0] represents the top left corner of\nthe mercator world and [1, 1] represents the bottom right corner. When the\nrenderingMode is \"3d\" , the z coordinate is conformal. A box with identical\nx, y, and z lengths in mercator units would be rendered as a cube.\nMercatorCoordinate .fromLatLng can be used to project a LngLat to a mercator\ncoordinate." - ], + "label": "id", + "description": [], "signature": [ - "number[]" + "number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } @@ -5221,283 +5512,307 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.CustomLayerInterface.render", + "id": "def-server.Map._render", "type": "Function", - "tags": [], - "label": "render", + "tags": [ + "private" + ], + "label": "_render", "description": [ - "\nCalled during a render frame allowing the layer to draw into the GL context.\n\nThe layer can assume blending and depth state is set to allow the layer to properly blend\nand clip other layers. The layer cannot make any other assumptions about the current GL state.\n\nIf the layer needs to render to a texture, it should implement the prerender method to do this\nand only use the render method for drawing directly into the main framebuffer.\n\nThe blend function is set to gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA). This expects\ncolors to be provided in premultiplied alpha form where the r, g and b values are already\nmultiplied by the a value. If you are unable to provide colors in premultiplied form you may\nwant to change the blend function to\ngl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA).\n" + "\nCall when a (re-)render of the map is required:\n- The style has changed (`setPaintProperty()`, etc.)\n- Source data has changed (e.g. tiles have finished loading)\n- The map has is moving (or just finished moving)\n- A transition is in progress\n" ], "signature": [ - "(gl: WebGLRenderingContext, matrix: number[]) => void" + "(paintStartTimeStamp: number) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.CustomLayerInterface.render.$1", - "type": "Object", + "id": "def-server.Map._render.$1", + "type": "number", "tags": [], - "label": "gl", + "label": "paintStartTimeStamp", "description": [ - "The map's gl context." + "The time when the animation frame began executing." ], "signature": [ - "WebGLRenderingContext" + "number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true - }, + } + ], + "returnComment": [ + "this" + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.redraw", + "type": "Function", + "tags": [], + "label": "redraw", + "description": [ + "\nForce a synchronous redraw of the map." + ], + "signature": [ + "() => maplibregl.Map" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [ + "`this`" + ] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.remove", + "type": "Function", + "tags": [], + "label": "remove", + "description": [ + "\nClean up and release all internal resources associated with this map.\n\nThis includes DOM elements, event bindings, web workers, and WebGL resources.\n\nUse this method when you are done using the map and wish to ensure that it no\nlonger consumes browser resources. Afterwards, you must not call any other\nmethods on the map." + ], + "signature": [ + "() => void" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.triggerRepaint", + "type": "Function", + "tags": [ + "see", + "see" + ], + "label": "triggerRepaint", + "description": [ + "\nTrigger the rendering of a single frame. Use this method with custom layers to\nrepaint the map when the layer changes. Calling this multiple times before the\nnext frame is rendered will still result in only a single frame being rendered." + ], + "signature": [ + "() => void" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._onWindowOnline", + "type": "Function", + "tags": [], + "label": "_onWindowOnline", + "description": [], + "signature": [ + "() => void" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map._onWindowResize", + "type": "Function", + "tags": [], + "label": "_onWindowResize", + "description": [], + "signature": [ + "(event: maplibregl.Event) => void" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.CustomLayerInterface.render.$2", - "type": "Array", + "id": "def-server.Map._onWindowResize.$1", + "type": "Object", "tags": [], - "label": "matrix", - "description": [ - "The map's camera matrix. It projects spherical mercator coordinates to gl\ncoordinates. The mercator coordinate [0, 0] represents the top left corner of\nthe mercator world and [1, 1] represents the bottom right corner. When the\nrenderingMode is \"3d\" , the z coordinate is conformal. A box with identical\nx, y, and z lengths in mercator units would be rendered as a cube.\nMercatorCoordinate .fromLatLng can be used to project a LngLat to a mercator\ncoordinate." - ], + "label": "event", + "description": [], "signature": [ - "number[]" + "maplibregl.Event" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } ], "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.FeatureIdentifier", - "type": "Interface", - "tags": [], - "label": "FeatureIdentifier", - "description": [], - "signature": [ - "maplibregl.FeatureIdentifier" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ + }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.FeatureIdentifier.id", - "type": "CompoundType", + "id": "def-server.Map.showTileBoundaries", + "type": "boolean", + "tags": [ + "name", + "type", + "instance", + "memberof" + ], + "label": "showTileBoundaries", + "description": [ + "\nGets and sets a Boolean indicating whether the map will render an outline\naround each tile and the tile ID. These tile boundaries are useful for\ndebugging.\n\nThe uncompressed file size of the first vector source is drawn in the top left\ncorner of each tile, next to the tile ID.\n" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.showTileBoundaries", + "type": "boolean", "tags": [], - "label": "id", + "label": "showTileBoundaries", "description": [], - "signature": [ - "string | number | undefined" + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.showPadding", + "type": "boolean", + "tags": [ + "name", + "type", + "instance", + "memberof" + ], + "label": "showPadding", + "description": [ + "\nGets and sets a Boolean indicating whether the map will visualize\nthe padding offsets.\n" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.FeatureIdentifier.source", - "type": "string", + "id": "def-server.Map.showPadding", + "type": "boolean", "tags": [], - "label": "source", + "label": "showPadding", "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.FeatureIdentifier.sourceLayer", - "type": "string", + "id": "def-server.Map.showCollisionBoxes", + "type": "boolean", + "tags": [ + "name", + "type", + "instance", + "memberof" + ], + "label": "showCollisionBoxes", + "description": [ + "\nGets and sets a Boolean indicating whether the map will render boxes\naround all symbols in the data source, revealing which symbols\nwere rendered or which were hidden due to collisions.\nThis information is useful for debugging.\n" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.showCollisionBoxes", + "type": "boolean", "tags": [], - "label": "sourceLayer", + "label": "showCollisionBoxes", "description": [], - "signature": [ - "string | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.GeoJSONSource", - "type": "Interface", - "tags": [], - "label": "GeoJSONSource", - "description": [], - "signature": [ - "maplibregl.GeoJSONSource extends maplibregl.GeoJSONSourceRaw" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ + }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.GeoJSONSource.type", - "type": "string", + "id": "def-server.Map.showOverdrawInspector", + "type": "boolean", "tags": [], - "label": "type", + "label": "showOverdrawInspector", "description": [], - "signature": [ - "\"geojson\"" + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.showOverdrawInspector", + "type": "boolean", + "tags": [], + "label": "showOverdrawInspector", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Map.repaint", + "type": "boolean", + "tags": [ + "name", + "type", + "instance", + "memberof" + ], + "label": "repaint", + "description": [ + "\nGets and sets a Boolean indicating whether the map will\ncontinuously repaint. This information is useful for analyzing performance.\n" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.GeoJSONSource.setData", - "type": "Function", + "id": "def-server.Map.repaint", + "type": "boolean", "tags": [], - "label": "setData", + "label": "repaint", "description": [], - "signature": [ - "(data: String | GeoJSON.FeatureCollection | GeoJSON.Feature) => this" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.GeoJSONSource.setData.$1", - "type": "CompoundType", - "tags": [], - "label": "data", - "description": [], - "signature": [ - "String | GeoJSON.FeatureCollection | GeoJSON.Feature" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.GeoJSONSource.getClusterExpansionZoom", - "type": "Function", + "id": "def-server.Map.vertices", + "type": "boolean", "tags": [], - "label": "getClusterExpansionZoom", + "label": "vertices", "description": [], - "signature": [ - "(clusterId: number, callback: (error: any, zoom: number) => void) => this" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.GeoJSONSource.getClusterExpansionZoom.$1", - "type": "number", - "tags": [], - "label": "clusterId", - "description": [], - "signature": [ - "number" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.GeoJSONSource.getClusterExpansionZoom.$2", - "type": "Function", - "tags": [], - "label": "callback", - "description": [], - "signature": [ - "(error: any, zoom: number) => void" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.GeoJSONSource.getClusterChildren", - "type": "Function", + "id": "def-server.Map.vertices", + "type": "boolean", "tags": [], - "label": "getClusterChildren", + "label": "vertices", "description": [], - "signature": [ - "(clusterId: number, callback: (error: any, features: GeoJSON.Feature[]) => void) => this" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.GeoJSONSource.getClusterChildren.$1", - "type": "number", - "tags": [], - "label": "clusterId", - "description": [], - "signature": [ - "number" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.GeoJSONSource.getClusterChildren.$2", - "type": "Function", - "tags": [], - "label": "callback", - "description": [], - "signature": [ - "(error: any, features: GeoJSON.Feature[]) => void" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.GeoJSONSource.getClusterLeaves", + "id": "def-server.Map._setCacheLimits", "type": "Function", "tags": [], - "label": "getClusterLeaves", + "label": "_setCacheLimits", "description": [], "signature": [ - "(cluserId: number, limit: number, offset: number, callback: (error: any, features: GeoJSON.Feature[]) => void) => this" + "(limit: number, checkThreshold: number) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.GeoJSONSource.getClusterLeaves.$1", - "type": "number", - "tags": [], - "label": "cluserId", - "description": [], - "signature": [ - "number" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.GeoJSONSource.getClusterLeaves.$2", + "id": "def-server.Map._setCacheLimits.$1", "type": "number", "tags": [], "label": "limit", @@ -5505,35 +5820,21 @@ "signature": [ "number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.GeoJSONSource.getClusterLeaves.$3", + "id": "def-server.Map._setCacheLimits.$2", "type": "number", "tags": [], - "label": "offset", + "label": "checkThreshold", "description": [], "signature": [ "number" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.GeoJSONSource.getClusterLeaves.$4", - "type": "Function", - "tags": [], - "label": "callback", - "description": [], - "signature": [ - "(error: any, features: GeoJSON.Feature[]) => void" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "isRequired": true } @@ -5545,851 +5846,1102 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Layer", - "type": "Interface", + "id": "def-server.MapMouseEvent", + "type": "Class", "tags": [], - "label": "Layer", + "label": "MapMouseEvent", "description": [], "signature": [ - "maplibregl.Layer" + "maplibregl.MapMouseEvent extends maplibregl.Event implements maplibregl.MapLibreEvent" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Layer.id", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Layer.type", - "type": "string", + "id": "def-server.MapMouseEvent.type", + "type": "CompoundType", "tags": [], "label": "type", - "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Layer.metadata", - "type": "Any", - "tags": [], - "label": "metadata", - "description": [], - "signature": [ - "any" + "description": [ + "\nThe event type (one of {@link Map.event:mousedown},\n{@link Map.event:mouseup},\n{@link Map.event:click},\n{@link Map.event:dblclick},\n{@link Map.event:mousemove},\n{@link Map.event:mouseover},\n{@link Map.event:mouseenter},\n{@link Map.event:mouseleave},\n{@link Map.event:mouseout},\n{@link Map.event:contextmenu})." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Layer.ref", - "type": "string", - "tags": [], - "label": "ref", - "description": [], "signature": [ - "string | undefined" + "\"mousedown\" | \"mouseup\" | \"mouseover\" | \"mousemove\" | \"click\" | \"dblclick\" | \"mouseenter\" | \"mouseleave\" | \"mouseout\" | \"contextmenu\"" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Layer.source", - "type": "CompoundType", + "id": "def-server.MapMouseEvent.target", + "type": "Object", "tags": [], - "label": "source", - "description": [], + "label": "target", + "description": [ + "\nThe `Map` object that fired the event." + ], "signature": [ - "string | maplibregl.AnySourceData | undefined" + "maplibregl.Map" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Layer.sourcelayer", - "type": "string", + "id": "def-server.MapMouseEvent.originalEvent", + "type": "Object", "tags": [], - "label": "'source-layer'", - "description": [], + "label": "originalEvent", + "description": [ + "\nThe DOM event which caused the map event." + ], "signature": [ - "string | undefined" + "MouseEvent" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Layer.minzoom", - "type": "number", + "id": "def-server.MapMouseEvent.point", + "type": "Object", "tags": [], - "label": "minzoom", - "description": [], + "label": "point", + "description": [ + "\nThe pixel coordinates of the mouse cursor, relative to the map and measured from the top left corner." + ], "signature": [ - "number | undefined" + "node_modules/@types/mapbox__point-geometry/index" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Layer.maxzoom", - "type": "number", + "id": "def-server.MapMouseEvent.lngLat", + "type": "Object", "tags": [], - "label": "maxzoom", - "description": [], + "label": "lngLat", + "description": [ + "\nThe geographic location on the map of the mouse cursor." + ], "signature": [ - "number | undefined" + "maplibregl.LngLat" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Layer.interactive", - "type": "CompoundType", + "id": "def-server.MapMouseEvent.preventDefault", + "type": "Function", "tags": [], - "label": "interactive", - "description": [], + "label": "preventDefault", + "description": [ + "\nPrevents subsequent default processing of the event by the map.\n\nCalling this method will prevent the following default map behaviors:\n\n * On `mousedown` events, the behavior of {@link DragPanHandler}\n * On `mousedown` events, the behavior of {@link DragRotateHandler}\n * On `mousedown` events, the behavior of {@link BoxZoomHandler}\n * On `dblclick` events, the behavior of {@link DoubleClickZoomHandler}\n" + ], "signature": [ - "boolean | undefined" + "() => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Layer.filter", - "type": "Array", - "tags": [], - "label": "filter", - "description": [], - "signature": [ - "any[] | undefined" + "id": "def-server.MapMouseEvent.defaultPrevented", + "type": "boolean", + "tags": [ + "private" + ], + "label": "defaultPrevented", + "description": [ + "\n`true` if `preventDefault` has been called." ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Layer.layout", - "type": "Object", + "id": "def-server.MapMouseEvent._defaultPrevented", + "type": "boolean", "tags": [], - "label": "layout", + "label": "_defaultPrevented", "description": [], - "signature": [ - "maplibregl.Layout | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Layer.paint", - "type": "Uncategorized", - "tags": [], - "label": "paint", + "id": "def-server.MapMouseEvent.Unnamed", + "type": "Function", + "tags": [ + "private" + ], + "label": "Constructor", "description": [], "signature": [ - "object | undefined" + "any" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.MapMouseEvent.Unnamed.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.MapMouseEvent.Unnamed.$2", + "type": "Object", + "tags": [], + "label": "map", + "description": [], + "signature": [ + "maplibregl.Map" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.MapMouseEvent.Unnamed.$3", + "type": "Object", + "tags": [], + "label": "originalEvent", + "description": [], + "signature": [ + "MouseEvent" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.MapMouseEvent.Unnamed.$4", + "type": "Any", + "tags": [], + "label": "data", + "description": [], + "signature": [ + "any" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "returnComment": [] } ], "initialIsOpen": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions", - "type": "Interface", + "id": "def-server.VectorTileSource", + "type": "Class", "tags": [], - "label": "MapboxOptions", + "label": "VectorTileSource", "description": [], "signature": [ - "maplibregl.MapboxOptions" + "maplibregl.VectorTileSource extends maplibregl.Evented implements maplibregl.Source" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.antialias", - "type": "CompoundType", + "id": "def-server.VectorTileSource.type", + "type": "string", "tags": [], - "label": "antialias", - "description": [ - "\nIf true, the gl context will be created with MSA antialiasing, which can be useful for antialiasing custom layers.\nThis is false by default as a performance optimization." - ], + "label": "type", + "description": [], "signature": [ - "boolean | undefined" + "\"vector\"" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.attributionControl", - "type": "CompoundType", + "id": "def-server.VectorTileSource.id", + "type": "string", "tags": [], - "label": "attributionControl", - "description": [ - "If true, an attribution control will be added to the map." - ], - "signature": [ - "boolean | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "label": "id", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.bearing", + "id": "def-server.VectorTileSource.minzoom", "type": "number", "tags": [], - "label": "bearing", + "label": "minzoom", "description": [], - "signature": [ - "number | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.bearingSnap", + "id": "def-server.VectorTileSource.maxzoom", "type": "number", "tags": [], - "label": "bearingSnap", - "description": [ - "Snap to north threshold in degrees." - ], - "signature": [ - "number | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.bounds", - "type": "CompoundType", - "tags": [], - "label": "bounds", - "description": [ - "The initial bounds of the map. If bounds is specified, it overrides center and zoom constructor options." - ], - "signature": [ - "maplibregl.LngLatBoundsLike | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "label": "maxzoom", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.boxZoom", - "type": "CompoundType", + "id": "def-server.VectorTileSource.url", + "type": "string", "tags": [], - "label": "boxZoom", - "description": [ - "If true, enable the \"box zoom\" interaction (see BoxZoomHandler)" - ], - "signature": [ - "boolean | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "label": "url", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.center", - "type": "CompoundType", + "id": "def-server.VectorTileSource.scheme", + "type": "string", "tags": [], - "label": "center", - "description": [ - "initial map center" - ], - "signature": [ - "maplibregl.LngLatLike | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "label": "scheme", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.clickTolerance", + "id": "def-server.VectorTileSource.tileSize", "type": "number", - "tags": [ - "default" - ], - "label": "clickTolerance", - "description": [ - "\nThe max number of pixels a user can shift the mouse pointer during a click for it to be\nconsidered a valid click (as opposed to a mouse drag).\n" - ], - "signature": [ - "number | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.collectResourceTiming", - "type": "CompoundType", - "tags": [ - "default" - ], - "label": "collectResourceTiming", - "description": [ - "\nIf `true`, Resource Timing API information will be collected for requests made by GeoJSON\nand Vector Tile web workers (this information is normally inaccessible from the main\nJavascript thread). Information will be returned in a `resourceTiming` property of\nrelevant `data` events.\n" - ], - "signature": [ - "boolean | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "tags": [], + "label": "tileSize", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.crossSourceCollisions", + "id": "def-server.VectorTileSource.promoteId", "type": "CompoundType", - "tags": [ - "default" - ], - "label": "crossSourceCollisions", - "description": [ - "\nIf `true`, symbols from multiple sources can collide with each other during collision\ndetection. If `false`, collision detection is run separately for the symbols in each source.\n" - ], + "tags": [], + "label": "promoteId", + "description": [], "signature": [ - "boolean | undefined" + "string | { [_: string]: string; }" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.container", - "type": "CompoundType", + "id": "def-server.VectorTileSource._options", + "type": "Object", "tags": [], - "label": "container", - "description": [ - "ID of the container element" - ], + "label": "_options", + "description": [], "signature": [ - "string | HTMLElement" + "{ type: \"vector\"; url?: string | undefined; tiles?: string[] | undefined; bounds?: [number, number, number, number] | undefined; scheme?: \"xyz\" | \"tms\" | undefined; minzoom?: number | undefined; maxzoom?: number | undefined; attribution?: string | undefined; promoteId?: maplibregl.PromoteIdSpecification | undefined; volatile?: boolean | undefined; }" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.customAttribution", - "type": "CompoundType", + "id": "def-server.VectorTileSource._collectResourceTiming", + "type": "boolean", "tags": [], - "label": "customAttribution", - "description": [ - "String or strings to show in an AttributionControl.\nOnly applicable if options.attributionControl is `true`." - ], - "signature": [ - "string | string[] | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "label": "_collectResourceTiming", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.dragPan", - "type": "CompoundType", + "id": "def-server.VectorTileSource.dispatcher", + "type": "Object", "tags": [], - "label": "dragPan", - "description": [ - "If true, enable the \"drag to pan\" interaction (see DragPanHandler)." - ], + "label": "dispatcher", + "description": [], "signature": [ - "boolean | undefined" + "maplibregl.Dispatcher" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.dragRotate", - "type": "CompoundType", + "id": "def-server.VectorTileSource.map", + "type": "Object", "tags": [], - "label": "dragRotate", - "description": [ - "If true, enable the \"drag to rotate\" interaction (see DragRotateHandler)." - ], + "label": "map", + "description": [], "signature": [ - "boolean | undefined" + "maplibregl.Map" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.doubleClickZoom", - "type": "CompoundType", + "id": "def-server.VectorTileSource.bounds", + "type": "Object", "tags": [], - "label": "doubleClickZoom", - "description": [ - "If true, enable the \"double click to zoom\" interaction (see DoubleClickZoomHandler)." - ], + "label": "bounds", + "description": [], "signature": [ - "boolean | undefined" + "[number, number, number, number]" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.hash", - "type": "CompoundType", + "id": "def-server.VectorTileSource.tiles", + "type": "Array", "tags": [], - "label": "hash", - "description": [ - "If `true`, the map's position (zoom, center latitude, center longitude, bearing, and pitch) will be synced with the hash fragment of the page's URL.\nFor example, `http://path/to/my/page.html#2.59/39.26/53.07/-24.1/60`.\nAn additional string may optionally be provided to indicate a parameter-styled hash,\ne.g. http://path/to/my/page.html#map=2.59/39.26/53.07/-24.1/60&foo=bar, where foo\nis a custom parameter and bar is an arbitrary hash distinct from the map hash." - ], + "label": "tiles", + "description": [], "signature": [ - "string | boolean | undefined" + "string[]" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.fadeDuration", - "type": "number", - "tags": [ - "default" - ], - "label": "fadeDuration", - "description": [ - "\nControls the duration of the fade-in/fade-out animation for label collisions, in milliseconds.\nThis setting affects all symbol layers. This setting does not affect the duration of runtime\nstyling transitions or raster tile cross-fading.\n" - ], + "id": "def-server.VectorTileSource.tileBounds", + "type": "Object", + "tags": [], + "label": "tileBounds", + "description": [], "signature": [ - "number | undefined" + "maplibregl.TileBounds" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.failIfMajorPerformanceCaveat", - "type": "CompoundType", + "id": "def-server.VectorTileSource.reparseOverscaled", + "type": "boolean", "tags": [], - "label": "failIfMajorPerformanceCaveat", - "description": [ - "If true, map creation will fail if the implementation determines that the performance of the created WebGL context would be dramatically lower than expected." - ], - "signature": [ - "boolean | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "label": "reparseOverscaled", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.fitBoundsOptions", - "type": "Object", + "id": "def-server.VectorTileSource.isTileClipped", + "type": "boolean", "tags": [], - "label": "fitBoundsOptions", - "description": [ - "A fitBounds options object to use only when setting the bounds option." - ], - "signature": [ - "maplibregl.FitBoundsOptions | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "label": "isTileClipped", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.interactive", - "type": "CompoundType", + "id": "def-server.VectorTileSource._tileJSONRequest", + "type": "Object", "tags": [], - "label": "interactive", - "description": [ - "If false, no mouse, touch, or keyboard listeners are attached to the map, so it will not respond to input" - ], + "label": "_tileJSONRequest", + "description": [], "signature": [ - "boolean | undefined" + "{ cancel: () => void; }" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.keyboard", - "type": "CompoundType", + "id": "def-server.VectorTileSource._loaded", + "type": "boolean", "tags": [], - "label": "keyboard", - "description": [ - "If true, enable keyboard shortcuts (see KeyboardHandler)." - ], - "signature": [ - "boolean | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "label": "_loaded", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.locale", - "type": "Object", + "id": "def-server.VectorTileSource.Unnamed", + "type": "Function", "tags": [], - "label": "locale", - "description": [ - "A patch to apply to the default localization table for UI strings, e.g. control tooltips.\nThe `locale` object maps namespaced UI string IDs to translated strings in the target language;\nsee `src/ui/default_locale.js` for an example with all supported string IDs.\nThe object may specify all UI strings (thereby adding support for a new translation) or\nonly a subset of strings (thereby patching the default translation table)." - ], + "label": "Constructor", + "description": [], "signature": [ - "{ [key: string]: string; } | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.localIdeographFontFamily", - "type": "string", - "tags": [ - "default" - ], - "label": "localIdeographFontFamily", - "description": [ - "\nIf specified, defines a CSS font-family for locally overriding generation of glyphs in the\n'CJK Unified Ideographs' and 'Hangul Syllables' ranges. In these ranges, font settings from\nthe map's style will be ignored, except for font-weight keywords (light/regular/medium/bold).\nThe purpose of this option is to avoid bandwidth-intensive glyph server requests.\n" + "any" ], - "signature": [ - "string | undefined" + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.VectorTileSource.Unnamed.$1", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.VectorTileSource.Unnamed.$2", + "type": "CompoundType", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "maplibregl.VectorSourceSpecification & { collectResourceTiming: boolean; }" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.VectorTileSource.Unnamed.$3", + "type": "Object", + "tags": [], + "label": "dispatcher", + "description": [], + "signature": [ + "maplibregl.Dispatcher" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.VectorTileSource.Unnamed.$4", + "type": "Object", + "tags": [], + "label": "eventedParent", + "description": [], + "signature": [ + "maplibregl.Evented" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.logoPosition", - "type": "CompoundType", - "tags": [ - "default" - ], - "label": "logoPosition", - "description": [ - "\nA string representing the position of the Mapbox wordmark on the map.\n" - ], + "id": "def-server.VectorTileSource.load", + "type": "Function", + "tags": [], + "label": "load", + "description": [], "signature": [ - "\"top-right\" | \"top-left\" | \"bottom-right\" | \"bottom-left\" | undefined" + "() => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.maxBounds", - "type": "CompoundType", + "id": "def-server.VectorTileSource.loaded", + "type": "Function", "tags": [], - "label": "maxBounds", - "description": [ - "If set, the map is constrained to the given bounds." - ], + "label": "loaded", + "description": [], "signature": [ - "maplibregl.LngLatBoundsLike | undefined" + "() => boolean" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.maxPitch", - "type": "number", + "id": "def-server.VectorTileSource.hasTile", + "type": "Function", "tags": [], - "label": "maxPitch", - "description": [ - "Maximum pitch of the map." - ], + "label": "hasTile", + "description": [], "signature": [ - "number | undefined" + "(tileID: maplibregl.OverscaledTileID) => boolean" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.VectorTileSource.hasTile.$1", + "type": "Object", + "tags": [], + "label": "tileID", + "description": [], + "signature": [ + "maplibregl.OverscaledTileID" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.maxZoom", - "type": "number", + "id": "def-server.VectorTileSource.onAdd", + "type": "Function", "tags": [], - "label": "maxZoom", - "description": [ - "Maximum zoom of the map." - ], + "label": "onAdd", + "description": [], "signature": [ - "number | undefined" + "(map: maplibregl.Map) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.VectorTileSource.onAdd.$1", + "type": "Object", + "tags": [], + "label": "map", + "description": [], + "signature": [ + "maplibregl.Map" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.minPitch", - "type": "number", + "id": "def-server.VectorTileSource.setSourceProperty", + "type": "Function", "tags": [], - "label": "minPitch", - "description": [ - "Minimum pitch of the map." - ], + "label": "setSourceProperty", + "description": [], "signature": [ - "number | undefined" + "(callback: Function) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.VectorTileSource.setSourceProperty.$1", + "type": "Object", + "tags": [], + "label": "callback", + "description": [], + "signature": [ + "Function" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.minZoom", - "type": "number", + "id": "def-server.VectorTileSource.setTiles", + "type": "Function", "tags": [], - "label": "minZoom", + "label": "setTiles", "description": [ - "Minimum zoom of the map." + "\nSets the source `tiles` property and re-renders the map.\n" ], "signature": [ - "number | undefined" + "(tiles: string[]) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.VectorTileSource.setTiles.$1", + "type": "Array", + "tags": [], + "label": "tiles", + "description": [ + "An array of one or more tile source URLs, as in the TileJSON spec." + ], + "signature": [ + "string[]" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [ + "this" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.preserveDrawingBuffer", - "type": "CompoundType", + "id": "def-server.VectorTileSource.setUrl", + "type": "Function", "tags": [], - "label": "preserveDrawingBuffer", + "label": "setUrl", "description": [ - "If true, The maps canvas can be exported to a PNG using map.getCanvas().toDataURL();. This is false by default as a performance optimization." + "\nSets the source `url` property and re-renders the map.\n" ], "signature": [ - "boolean | undefined" + "(url: string) => this" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.VectorTileSource.setUrl.$1", + "type": "string", + "tags": [], + "label": "url", + "description": [ + "A URL to a TileJSON resource. Supported protocols are `http:` and `https:`." + ], + "signature": [ + "string" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [ + "this" + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.pitch", - "type": "number", - "tags": [ - "default" - ], - "label": "pitch", - "description": [ - "\nThe initial pitch (tilt) of the map, measured in degrees away from the plane of the\nscreen (0-60).\n" - ], + "id": "def-server.VectorTileSource.onRemove", + "type": "Function", + "tags": [], + "label": "onRemove", + "description": [], "signature": [ - "number | undefined" + "() => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.pitchWithRotate", - "type": "CompoundType", - "tags": [ - "default" - ], - "label": "pitchWithRotate", - "description": [ - "\nIf `false`, the map's pitch (tilt) control with \"drag to rotate\" interaction will be disabled.\n" - ], + "id": "def-server.VectorTileSource.serialize", + "type": "Function", + "tags": [], + "label": "serialize", + "description": [], "signature": [ - "boolean | undefined" + "() => any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.refreshExpiredTiles", - "type": "CompoundType", - "tags": [ - "default" - ], - "label": "refreshExpiredTiles", - "description": [ - "\nIf `false`, the map won't attempt to re-request tiles once they expire per their HTTP\n`cacheControl`/`expires` headers.\n" - ], + "id": "def-server.VectorTileSource.loadTile", + "type": "Function", + "tags": [], + "label": "loadTile", + "description": [], "signature": [ - "boolean | undefined" + "(tile: maplibregl.Tile, callback: maplibregl.Callback) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.VectorTileSource.loadTile.$1", + "type": "Object", + "tags": [], + "label": "tile", + "description": [], + "signature": [ + "maplibregl.Tile" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.VectorTileSource.loadTile.$2", + "type": "Function", + "tags": [], + "label": "callback", + "description": [], + "signature": [ + "maplibregl.Callback" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.renderWorldCopies", - "type": "CompoundType", - "tags": [ - "default" + "id": "def-server.VectorTileSource.abortTile", + "type": "Function", + "tags": [], + "label": "abortTile", + "description": [], + "signature": [ + "(tile: maplibregl.Tile) => void" ], - "label": "renderWorldCopies", - "description": [ - "\nIf `true`, multiple copies of the world will be rendered, when zoomed out.\n" + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.VectorTileSource.abortTile.$1", + "type": "Object", + "tags": [], + "label": "tile", + "description": [], + "signature": [ + "maplibregl.Tile" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.VectorTileSource.unloadTile", + "type": "Function", + "tags": [], + "label": "unloadTile", + "description": [], "signature": [ - "boolean | undefined" + "(tile: maplibregl.Tile) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.VectorTileSource.unloadTile.$1", + "type": "Object", + "tags": [], + "label": "tile", + "description": [], + "signature": [ + "maplibregl.Tile" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.scrollZoom", - "type": "CompoundType", + "id": "def-server.VectorTileSource.hasTransition", + "type": "Function", "tags": [], - "label": "scrollZoom", - "description": [ - "If true, enable the \"scroll to zoom\" interaction" - ], + "label": "hasTransition", + "description": [], "signature": [ - "boolean | undefined" + "() => boolean" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [], + "interfaces": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.CustomLayerInterface", + "type": "Interface", + "tags": [ + "interface" + ], + "label": "CustomLayerInterface", + "description": [ + "\nInterface for custom style layers. This is a specification for\nimplementers to model: it is not an exported method or class.\n\nCustom layers allow a user to render directly into the map's GL context using the map's camera.\nThese layers can be added between any regular layers using {@link Map#addLayer}.\n\nCustom layers must have a unique `id` and must have the `type` of `\"custom\"`.\nThey must implement `render` and may implement `prerender`, `onAdd` and `onRemove`.\nThey can trigger rendering using {@link Map#triggerRepaint}\nand they should appropriately handle {@link Map.event:webglcontextlost} and\n{@link Map.event:webglcontextrestored}.\n\nThe `renderingMode` property controls whether the layer is treated as a `\"2d\"` or `\"3d\"` map layer. Use:\n- `\"renderingMode\": \"3d\"` to use the depth buffer and share it with other layers\n- `\"renderingMode\": \"2d\"` to add a layer with no depth. If you need to use the depth buffer for a `\"2d\"` layer you must use an offscreen\n framebuffer and {@link CustomLayerInterface#prerender}\n" + ], + "signature": [ + "maplibregl.CustomLayerInterface" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.CustomLayerInterface.id", + "type": "string", + "tags": [ + "property" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "label": "id", + "description": [], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.style", - "type": "CompoundType", - "tags": [], - "label": "style", - "description": [ - "stylesheet location" + "id": "def-server.CustomLayerInterface.type", + "type": "string", + "tags": [ + "property" ], + "label": "type", + "description": [], "signature": [ - "string | maplibregl.Style | undefined" + "\"custom\"" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.trackResize", + "id": "def-server.CustomLayerInterface.renderingMode", "type": "CompoundType", - "tags": [], - "label": "trackResize", - "description": [ - "If true, the map will automatically resize when the browser window resizes" + "tags": [ + "property" ], + "label": "renderingMode", + "description": [], "signature": [ - "boolean | undefined" + "\"2d\" | \"3d\" | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.transformRequest", + "id": "def-server.CustomLayerInterface.render", "type": "Function", "tags": [ - "default" - ], - "label": "transformRequest", - "description": [ - "\nA callback run before the Map makes a request for an external URL. The callback can be\nused to modify the url, set headers, or set the credentials property for cross-origin requests.\n" - ], - "signature": [ - "maplibregl.TransformRequestFunction | undefined" + "function", + "memberof", + "instance", + "name" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.touchZoomRotate", - "type": "CompoundType", - "tags": [], - "label": "touchZoomRotate", + "label": "render", "description": [ - "If true, enable the \"pinch to rotate and zoom\" interaction (see TouchZoomRotateHandler)." + "\nCalled during a render frame allowing the layer to draw into the GL context.\n\nThe layer can assume blending and depth state is set to allow the layer to properly\nblend and clip other layers. The layer cannot make any other assumptions about the\ncurrent GL state.\n\nIf the layer needs to render to a texture, it should implement the `prerender` method\nto do this and only use the `render` method for drawing directly into the main framebuffer.\n\nThe blend function is set to `gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)`. This expects\ncolors to be provided in premultiplied alpha form where the `r`, `g` and `b` values are already\nmultiplied by the `a` value. If you are unable to provide colors in premultiplied form you\nmay want to change the blend function to\n`gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA)`.\n" ], "signature": [ - "boolean | undefined" + "(gl: WebGLRenderingContext, matrix: ", + "mat4", + ") => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.CustomLayerInterface.render.$1", + "type": "Object", + "tags": [], + "label": "gl", + "description": [ + "The map's gl context." + ], + "signature": [ + "WebGLRenderingContext" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.CustomLayerInterface.render.$2", + "type": "CompoundType", + "tags": [], + "label": "matrix", + "description": [ + "The map's camera matrix. It projects spherical mercator\ncoordinates to gl coordinates. The spherical mercator coordinate `[0, 0]` represents the\ntop left corner of the mercator world and `[1, 1]` represents the bottom right corner. When\nthe `renderingMode` is `\"3d\"`, the z coordinate is conformal. A box with identical x, y, and z\nlengths in mercator units would be rendered as a cube. {@link MercatorCoordinate }.fromLngLat\ncan be used to project a `LngLat` to a mercator coordinate." + ], + "signature": [ + "Float32Array | [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number]" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false + } + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.touchPitch", - "type": "CompoundType", - "tags": [], - "label": "touchPitch", - "description": [ - "If true, the \"drag to pitch\" interaction is enabled" - ], - "signature": [ - "boolean | undefined" + "id": "def-server.CustomLayerInterface.prerender", + "type": "Function", + "tags": [ + "function", + "memberof", + "instance", + "name" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.zoom", - "type": "number", - "tags": [], - "label": "zoom", + "label": "prerender", "description": [ - "Initial zoom level" + "\nOptional method called during a render frame to allow a layer to prepare resources or render into a texture.\n\nThe layer cannot make any assumptions about the current GL state and must bind a framebuffer before rendering.\n" ], "signature": [ - "number | undefined" + "maplibregl.CustomRenderMethod | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.maxTileCacheSize", - "type": "number", + "id": "def-server.CustomLayerInterface.onAdd", + "type": "Function", "tags": [ - "default" + "function", + "memberof", + "instance", + "name" ], - "label": "maxTileCacheSize", + "label": "onAdd", "description": [ - "\nThe maximum number of tiles stored in the tile cache for a given source. If omitted, the\ncache will be dynamically sized based on the current viewport.\n" + "\nOptional method called when the layer has been added to the Map with {@link Map#addLayer}. This\ngives the layer a chance to initialize gl resources and register event listeners.\n" ], "signature": [ - "number | undefined" + "((map: maplibregl.Map, gl: WebGLRenderingContext) => void) | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.CustomLayerInterface.onAdd.$1", + "type": "Object", + "tags": [], + "label": "map", + "description": [ + "The Map this custom layer was just added to." + ], + "signature": [ + "maplibregl.Map" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.CustomLayerInterface.onAdd.$2", + "type": "Object", + "tags": [], + "label": "gl", + "description": [ + "The gl context for the map." + ], + "signature": [ + "WebGLRenderingContext" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxOptions.accessToken", - "type": "string", + "id": "def-server.CustomLayerInterface.onRemove", + "type": "Function", "tags": [ - "default" + "function", + "memberof", + "instance", + "name" ], - "label": "accessToken", + "label": "onRemove", "description": [ - "\nIf specified, map will use this token instead of the one defined in maplibregl.accessToken.\n" + "\nOptional method called when the layer has been removed from the Map with {@link Map#removeLayer}. This\ngives the layer a chance to clean up gl resources and event listeners.\n" ], "signature": [ - "string | undefined" + "((map: maplibregl.Map, gl: WebGLRenderingContext) => void) | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.CustomLayerInterface.onRemove.$1", + "type": "Object", + "tags": [], + "label": "map", + "description": [ + "The Map this custom layer was just added to." + ], + "signature": [ + "maplibregl.Map" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.CustomLayerInterface.onRemove.$2", + "type": "Object", + "tags": [], + "label": "gl", + "description": [ + "The gl context for the map." + ], + "signature": [ + "WebGLRenderingContext" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] } ], "initialIsOpen": false @@ -6402,9 +6954,9 @@ "label": "MapSourceDataEvent", "description": [], "signature": [ - "maplibregl.MapSourceDataEvent extends maplibregl.MapboxEvent" + "maplibregl.MapSourceDataEvent extends maplibregl.MapLibreEvent" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { @@ -6417,7 +6969,7 @@ "signature": [ "\"source\"" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { @@ -6427,20 +6979,20 @@ "tags": [], "label": "isSourceLoaded", "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", "id": "def-server.MapSourceDataEvent.source", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "source", "description": [], "signature": [ - "maplibregl.Source" + "maplibregl.VectorSourceSpecification | maplibregl.RasterSourceSpecification | maplibregl.RasterDEMSourceSpecification | maplibregl.GeoJSONSourceSpecification | maplibregl.VideoSourceSpecification | maplibregl.ImageSourceSpecification" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { @@ -6450,7 +7002,7 @@ "tags": [], "label": "sourceId", "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { @@ -6463,7 +7015,7 @@ "signature": [ "\"metadata\" | \"content\"" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { @@ -6476,20 +7028,7 @@ "signature": [ "any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapSourceDataEvent.coord", - "type": "Object", - "tags": [], - "label": "coord", - "description": [], - "signature": [ - "maplibregl.Coordinate" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false } ], @@ -6497,315 +7036,471 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Style", + "id": "def-server.Source", "type": "Interface", - "tags": [], - "label": "Style", - "description": [], + "tags": [ + "private", + "fires", + "property", + "property", + "property", + "property", + "property", + "property" + ], + "label": "Source", + "description": [ + "\nThe `Source` interface must be implemented by each source type, including \"core\" types (`vector`, `raster`,\n`video`, etc.) and all custom, third-party types.\n" + ], "signature": [ - "maplibregl.Style" + "maplibregl.Source" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Style.bearing", - "type": "number", + "id": "def-server.Source.type", + "type": "string", "tags": [], - "label": "bearing", + "label": "type", "description": [], - "signature": [ - "number | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Style.center", - "type": "Array", + "id": "def-server.Source.id", + "type": "string", "tags": [], - "label": "center", + "label": "id", "description": [], - "signature": [ - "number[] | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Style.glyphs", - "type": "string", + "id": "def-server.Source.minzoom", + "type": "number", "tags": [], - "label": "glyphs", + "label": "minzoom", "description": [], - "signature": [ - "string | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Style.layers", - "type": "Array", + "id": "def-server.Source.maxzoom", + "type": "number", "tags": [], - "label": "layers", + "label": "maxzoom", "description": [], - "signature": [ - "maplibregl.AnyLayer[] | undefined" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Style.metadata", - "type": "Any", + "id": "def-server.Source.tileSize", + "type": "number", "tags": [], - "label": "metadata", + "label": "tileSize", "description": [], - "signature": [ - "any" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Style.name", + "id": "def-server.Source.attribution", "type": "string", "tags": [], - "label": "name", + "label": "attribution", "description": [], "signature": [ "string | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Style.pitch", - "type": "number", + "id": "def-server.Source.roundZoom", + "type": "CompoundType", "tags": [], - "label": "pitch", + "label": "roundZoom", "description": [], "signature": [ - "number | undefined" + "boolean | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Style.light", - "type": "Object", + "id": "def-server.Source.isTileClipped", + "type": "CompoundType", "tags": [], - "label": "light", + "label": "isTileClipped", "description": [], "signature": [ - "maplibregl.Light | undefined" + "boolean | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Style.sources", + "id": "def-server.Source.tileID", "type": "Object", "tags": [], - "label": "sources", + "label": "tileID", "description": [], "signature": [ - "maplibregl.Sources | undefined" + "maplibregl.CanonicalTileID | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Style.sprite", - "type": "string", + "id": "def-server.Source.reparseOverscaled", + "type": "CompoundType", "tags": [], - "label": "sprite", + "label": "reparseOverscaled", "description": [], "signature": [ - "string | undefined" + "boolean | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Style.transition", - "type": "Object", + "id": "def-server.Source.vectorLayerIds", + "type": "Array", "tags": [], - "label": "transition", + "label": "vectorLayerIds", "description": [], "signature": [ - "maplibregl.Transition | undefined" + "string[] | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Style.version", - "type": "number", + "id": "def-server.Source.hasTransition", + "type": "Function", "tags": [], - "label": "version", + "label": "hasTransition", "description": [], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "signature": [ + "() => boolean" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.Style.zoom", - "type": "number", + "id": "def-server.Source.loaded", + "type": "Function", "tags": [], - "label": "zoom", + "label": "loaded", "description": [], "signature": [ - "number | undefined" + "() => boolean" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.VectorSource", - "type": "Interface", - "tags": [], - "label": "VectorSource", - "description": [], - "signature": [ - "maplibregl.VectorSource extends maplibregl.Source" - ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false, - "children": [ + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.VectorSource.type", - "type": "string", + "id": "def-server.Source.fire", + "type": "Function", "tags": [], - "label": "type", + "label": "fire", "description": [], "signature": [ - "\"vector\"" + "(event: maplibregl.Event) => unknown" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Source.fire.$1", + "type": "Object", + "tags": [], + "label": "event", + "description": [], + "signature": [ + "maplibregl.Event" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.VectorSource.url", - "type": "string", + "id": "def-server.Source.onAdd", + "type": "Function", "tags": [], - "label": "url", + "label": "onAdd", "description": [], "signature": [ - "string | undefined" + "((map: maplibregl.Map) => void) | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Source.onAdd.$1", + "type": "Object", + "tags": [], + "label": "map", + "description": [], + "signature": [ + "maplibregl.Map" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.VectorSource.tiles", - "type": "Array", + "id": "def-server.Source.onRemove", + "type": "Function", "tags": [], - "label": "tiles", + "label": "onRemove", "description": [], "signature": [ - "string[] | undefined" + "((map: maplibregl.Map) => void) | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Source.onRemove.$1", + "type": "Object", + "tags": [], + "label": "map", + "description": [], + "signature": [ + "maplibregl.Map" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.VectorSource.bounds", - "type": "Array", + "id": "def-server.Source.loadTile", + "type": "Function", "tags": [], - "label": "bounds", + "label": "loadTile", "description": [], "signature": [ - "number[] | undefined" + "(tile: maplibregl.Tile, callback: maplibregl.Callback) => void" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Source.loadTile.$1", + "type": "Object", + "tags": [], + "label": "tile", + "description": [], + "signature": [ + "maplibregl.Tile" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Source.loadTile.$2", + "type": "Function", + "tags": [], + "label": "callback", + "description": [], + "signature": [ + "maplibregl.Callback" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.VectorSource.scheme", - "type": "CompoundType", + "id": "def-server.Source.hasTile", + "type": "Function", "tags": [], - "label": "scheme", + "label": "hasTile", "description": [], "signature": [ - "\"xyz\" | \"tms\" | undefined" + "((tileID: maplibregl.OverscaledTileID) => boolean) | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Source.hasTile.$1", + "type": "Object", + "tags": [], + "label": "tileID", + "description": [], + "signature": [ + "maplibregl.OverscaledTileID" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.VectorSource.minzoom", - "type": "number", + "id": "def-server.Source.abortTile", + "type": "Function", "tags": [], - "label": "minzoom", + "label": "abortTile", "description": [], "signature": [ - "number | undefined" + "((tile: maplibregl.Tile, callback: maplibregl.Callback) => void) | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Source.abortTile.$1", + "type": "Object", + "tags": [], + "label": "tile", + "description": [], + "signature": [ + "maplibregl.Tile" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Source.abortTile.$2", + "type": "Function", + "tags": [], + "label": "callback", + "description": [], + "signature": [ + "maplibregl.Callback" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.VectorSource.maxzoom", - "type": "number", + "id": "def-server.Source.unloadTile", + "type": "Function", "tags": [], - "label": "maxzoom", + "label": "unloadTile", "description": [], "signature": [ - "number | undefined" + "((tile: maplibregl.Tile, callback: maplibregl.Callback) => void) | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Source.unloadTile.$1", + "type": "Object", + "tags": [], + "label": "tile", + "description": [], + "signature": [ + "maplibregl.Tile" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Source.unloadTile.$2", + "type": "Function", + "tags": [], + "label": "callback", + "description": [], + "signature": [ + "maplibregl.Callback" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.VectorSource.attribution", - "type": "string", - "tags": [], - "label": "attribution", + "id": "def-server.Source.serialize", + "type": "Function", + "tags": [ + "private" + ], + "label": "serialize", "description": [], "signature": [ - "string | undefined" + "() => any" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [ + "A plain (stringifiable) JS object representing the current state of the source.\nCreating a source using the returned object as the `options` should result in a Source that is\nequivalent to this one." + ] }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.VectorSource.promoteId", - "type": "CompoundType", + "id": "def-server.Source.prepare", + "type": "Function", "tags": [], - "label": "promoteId", + "label": "prepare", "description": [], "signature": [ - "maplibregl.PromoteIdSpecification | undefined" + "(() => void) | undefined" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", - "deprecated": false + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "children": [], + "returnComment": [] } ], "initialIsOpen": false @@ -6815,38 +7510,80 @@ "misc": [ { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.AnyLayer", + "id": "def-server.FeatureIdentifier", + "type": "Type", + "tags": [], + "label": "FeatureIdentifier", + "description": [], + "signature": [ + "{ id?: string | number | undefined; source: string; sourceLayer?: string | undefined; }" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.FilterSpecification", + "type": "Type", + "tags": [], + "label": "FilterSpecification", + "description": [], + "signature": [ + "[\"at\", number, (string | number)[]] | [\"get\", string, (Record | undefined)?] | [\"has\", string, (Record | undefined)?] | [\"in\", ...maplibregl.FilterSpecificationInputType[], maplibregl.FilterSpecificationInputType | maplibregl.FilterSpecificationInputType[]] | [\"index-of\", maplibregl.FilterSpecificationInputType, maplibregl.FilterSpecificationInputType | maplibregl.FilterSpecificationInputType[]] | [\"length\", string | string[]] | [\"slice\", string | string[], number] | [\"!\", maplibregl.FilterSpecification] | [\"!=\", string | maplibregl.FilterSpecification, maplibregl.FilterSpecificationInputType] | [\"<\", string | maplibregl.FilterSpecification, maplibregl.FilterSpecificationInputType] | [\"<=\", string | maplibregl.FilterSpecification, maplibregl.FilterSpecificationInputType] | [\"==\", string | maplibregl.FilterSpecification, maplibregl.FilterSpecificationInputType] | [\">\", string | maplibregl.FilterSpecification, maplibregl.FilterSpecificationInputType] | [\">=\", string | maplibregl.FilterSpecification, maplibregl.FilterSpecificationInputType] | [\"all\", ...maplibregl.FilterSpecification[], maplibregl.FilterSpecificationInputType] | [\"any\", ...maplibregl.FilterSpecification[], maplibregl.FilterSpecificationInputType] | [\"case\", ...maplibregl.FilterSpecification[], maplibregl.FilterSpecificationInputType] | [\"coalesce\", ...maplibregl.FilterSpecification[], maplibregl.FilterSpecificationInputType] | [\"match\", ...maplibregl.FilterSpecification[], maplibregl.FilterSpecificationInputType] | [\"within\", ...maplibregl.FilterSpecification[], maplibregl.FilterSpecificationInputType] | [\"!in\", ...maplibregl.FilterSpecification[], maplibregl.FilterSpecificationInputType] | [\"!has\", ...maplibregl.FilterSpecification[], maplibregl.FilterSpecificationInputType] | [\"none\", ...maplibregl.FilterSpecification[], maplibregl.FilterSpecificationInputType] | (string | maplibregl.FilterSpecification)[]" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.LayerSpecification", + "type": "Type", + "tags": [], + "label": "LayerSpecification", + "description": [], + "signature": [ + "maplibregl.FillLayerSpecification | maplibregl.LineLayerSpecification | maplibregl.SymbolLayerSpecification | maplibregl.CircleLayerSpecification | maplibregl.HeatmapLayerSpecification | maplibregl.FillExtrusionLayerSpecification | maplibregl.RasterLayerSpecification | maplibregl.HillshadeLayerSpecification | maplibregl.BackgroundLayerSpecification" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.MapEvent", "type": "Type", "tags": [], - "label": "AnyLayer", + "label": "MapEvent", "description": [], "signature": [ - "maplibregl.BackgroundLayer | maplibregl.CircleLayer | maplibregl.FillExtrusionLayer | maplibregl.FillLayer | maplibregl.HeatmapLayer | maplibregl.HillshadeLayer | maplibregl.LineLayer | maplibregl.RasterLayer | maplibregl.SymbolLayer | maplibregl.CustomLayerInterface" + "\"remove\" | \"error\" | \"data\" | \"render\" | \"move\" | \"rotate\" | \"idle\" | \"resize\" | \"zoom\" | \"mousedown\" | \"mouseup\" | \"mouseover\" | \"mousemove\" | \"click\" | \"dblclick\" | \"mouseenter\" | \"mouseleave\" | \"mouseout\" | \"contextmenu\" | \"wheel\" | \"touchstart\" | \"touchend\" | \"touchmove\" | \"touchcancel\" | \"movestart\" | \"moveend\" | \"dragstart\" | \"drag\" | \"dragend\" | \"zoomstart\" | \"zoomend\" | \"rotatestart\" | \"rotateend\" | \"pitchstart\" | \"pitch\" | \"pitchend\" | \"boxzoomstart\" | \"boxzoomend\" | \"boxzoomcancel\" | \"webglcontextlost\" | \"webglcontextrestored\" | \"load\" | \"styledata\" | \"sourcedata\" | \"dataloading\" | \"styledataloading\" | \"sourcedataloading\" | \"styleimagemissing\" | \"style.load\" | \"dataabort\" | \"sourcedataabort\"" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "initialIsOpen": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.MapboxGeoJSONFeature", + "id": "def-server.MapGeoJSONFeature", "type": "Type", "tags": [], - "label": "MapboxGeoJSONFeature", + "label": "MapGeoJSONFeature", "description": [], "signature": [ - "GeoJSON.Feature & { layer: maplibregl.Layer; source: string; sourceLayer: string; state: { [key: string]: any; }; }" + "maplibregl.GeoJSONFeature & { layer: Omit & { source: string; }; source: string; sourceLayer?: string | undefined; state: { [key: string]: any; }; }" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "initialIsOpen": false }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.mapboxgl", + "id": "def-server.maplibregl", "type": "Any", "tags": [], - "label": "mapboxgl", + "label": "maplibregl", "description": [], "signature": [ "any" @@ -6857,15 +7594,62 @@ }, { "parentPluginId": "@kbn/mapbox-gl", - "id": "def-server.PointLike", + "id": "def-server.MapOptions", "type": "Type", "tags": [], + "label": "MapOptions", + "description": [], + "signature": [ + "{ hash?: string | boolean | undefined; interactive?: boolean | undefined; container: string | HTMLElement; bearingSnap?: number | undefined; attributionControl?: boolean | undefined; customAttribution?: string | string[] | undefined; maplibreLogo?: boolean | undefined; logoPosition?: maplibregl.ControlPosition | undefined; failIfMajorPerformanceCaveat?: boolean | undefined; preserveDrawingBuffer?: boolean | undefined; antialias?: boolean | undefined; refreshExpiredTiles?: boolean | undefined; maxBounds?: maplibregl.LngLatBoundsLike | undefined; scrollZoom?: boolean | undefined; minZoom?: number | null | undefined; maxZoom?: number | null | undefined; minPitch?: number | null | undefined; maxPitch?: number | null | undefined; boxZoom?: boolean | undefined; dragRotate?: boolean | undefined; dragPan?: boolean | maplibregl.DragPanOptions | undefined; keyboard?: boolean | undefined; doubleClickZoom?: boolean | undefined; touchZoomRotate?: boolean | undefined; touchPitch?: boolean | undefined; trackResize?: boolean | undefined; center?: maplibregl.LngLatLike | undefined; zoom?: number | undefined; bearing?: number | undefined; pitch?: number | undefined; renderWorldCopies?: boolean | undefined; maxTileCacheSize?: number | undefined; transformRequest?: maplibregl.RequestTransformFunction | undefined; locale?: any; fadeDuration?: number | undefined; crossSourceCollisions?: boolean | undefined; collectResourceTiming?: boolean | undefined; clickTolerance?: number | undefined; bounds?: maplibregl.LngLatBoundsLike | undefined; fitBoundsOptions?: Object | undefined; localIdeographFontFamily?: string | undefined; style: string | maplibregl.StyleSpecification; pitchWithRotate?: boolean | undefined; pixelRatio?: number | undefined; }" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.Point2D", + "type": "Type", + "tags": [], + "label": "Point2D", + "description": [], + "signature": [ + "{ x: number; y: number; }" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.PointLike", + "type": "Type", + "tags": [ + "typedef" + ], "label": "PointLike", + "description": [ + "\nA [Point](https://github.com/mapbox/point-geometry) or an array of two numbers representing `x` and `y` screen coordinates in pixels.\n" + ], + "signature": [ + "[number, number] | ", + "node_modules/@types/mapbox__point-geometry/index" + ], + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/mapbox-gl", + "id": "def-server.StyleSpecification", + "type": "Type", + "tags": [], + "label": "StyleSpecification", "description": [], "signature": [ - "[number, number] | maplibregl.Point" + "{ version: 8; name?: string | undefined; metadata?: unknown; center?: number[] | undefined; zoom?: number | undefined; bearing?: number | undefined; pitch?: number | undefined; light?: maplibregl.LightSpecification | undefined; sources: { [_: string]: maplibregl.SourceSpecification; }; sprite?: string | undefined; glyphs?: string | undefined; transition?: maplibregl.TransitionSpecification | undefined; layers: maplibregl.LayerSpecification[]; }" ], - "path": "node_modules/maplibre-gl/src/index.d.ts", + "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, "initialIsOpen": false } diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 53aa377e5c7a7..98e67f9b8208e 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/mapbox-gl plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 466 | 1 | 1 | 0 | +| 494 | 1 | 1 | 0 | ## Server diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 71a68c280f4c4..e0dbc611af5a0 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/monaco plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] warning: 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. --- diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 2a473e955b4f0..589a60a957724 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/optimizer plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] warning: 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. --- diff --git a/api_docs/kbn_plugin_discovery.devdocs.json b/api_docs/kbn_plugin_discovery.devdocs.json index f3e8e676b568f..331ea07e1db83 100644 --- a/api_docs/kbn_plugin_discovery.devdocs.json +++ b/api_docs/kbn_plugin_discovery.devdocs.json @@ -19,7 +19,7 @@ "label": "getPluginSearchPaths", "description": [], "signature": [ - "({ rootDir, oss, examples }: ", + "({ rootDir, oss, examples, testPlugins }: ", { "pluginId": "@kbn/plugin-discovery", "scope": "server", @@ -37,7 +37,7 @@ "id": "def-server.getPluginSearchPaths.$1", "type": "Object", "tags": [], - "label": "{ rootDir, oss, examples }", + "label": "{ rootDir, oss, examples, testPlugins }", "description": [], "signature": [ { @@ -289,6 +289,19 @@ "path": "packages/kbn-plugin-discovery/src/parse_kibana_platform_plugin.ts", "deprecated": false }, + { + "parentPluginId": "@kbn/plugin-discovery", + "id": "def-server.KibanaPlatformPluginManifest.enabledOnAnonymousPages", + "type": "CompoundType", + "tags": [], + "label": "enabledOnAnonymousPages", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-plugin-discovery/src/parse_kibana_platform_plugin.ts", + "deprecated": false + }, { "parentPluginId": "@kbn/plugin-discovery", "id": "def-server.KibanaPlatformPluginManifest.serviceFolders", @@ -396,6 +409,19 @@ "description": [], "path": "packages/kbn-plugin-discovery/src/plugin_search_paths.ts", "deprecated": false + }, + { + "parentPluginId": "@kbn/plugin-discovery", + "id": "def-server.SearchOptions.testPlugins", + "type": "CompoundType", + "tags": [], + "label": "testPlugins", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-plugin-discovery/src/plugin_search_paths.ts", + "deprecated": false } ], "initialIsOpen": false diff --git a/api_docs/kbn_plugin_discovery.mdx b/api_docs/kbn_plugin_discovery.mdx index 0bdfc493aa7d4..1e30d398d906c 100644 --- a/api_docs/kbn_plugin_discovery.mdx +++ b/api_docs/kbn_plugin_discovery.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-discovery title: "@kbn/plugin-discovery" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/plugin-discovery plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-discovery'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 28 | 0 | 27 | 0 | +| 30 | 0 | 29 | 0 | ## Server diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 6803a60cd1d81..85a2f9ace93de 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/plugin-generator plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] warning: 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. --- diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index b549f24fa74fa..abcb0613f1d31 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/plugin-helpers plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] warning: 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. --- diff --git a/api_docs/kbn_pm.devdocs.json b/api_docs/kbn_pm.devdocs.json index ec66a824a63a3..d43dd55273e42 100644 --- a/api_docs/kbn_pm.devdocs.json +++ b/api_docs/kbn_pm.devdocs.json @@ -328,23 +328,6 @@ "children": [], "returnComment": [] }, - { - "parentPluginId": "@kbn/pm", - "id": "def-server.Project.getIntermediateBuildDirectory", - "type": "Function", - "tags": [], - "label": "getIntermediateBuildDirectory", - "description": [ - "\nReturns the directory that should be copied into the Kibana build artifact.\nThis config can be specified to only include the project's build artifacts\ninstead of everything located in the project directory." - ], - "signature": [ - "() => string" - ], - "path": "packages/kbn-pm/src/utils/project.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, { "parentPluginId": "@kbn/pm", "id": "def-server.Project.getCleanConfig", @@ -578,177 +561,12 @@ "deprecated": false, "children": [], "returnComment": [] - }, - { - "parentPluginId": "@kbn/pm", - "id": "def-server.Project.installDependencies", - "type": "Function", - "tags": [], - "label": "installDependencies", - "description": [], - "signature": [ - "(options?: { extraArgs?: string[] | undefined; }) => Promise" - ], - "path": "packages/kbn-pm/src/utils/project.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/pm", - "id": "def-server.Project.installDependencies.$1", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "path": "packages/kbn-pm/src/utils/project.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/pm", - "id": "def-server.Project.installDependencies.$1.extraArgs", - "type": "Array", - "tags": [], - "label": "extraArgs", - "description": [], - "signature": [ - "string[] | undefined" - ], - "path": "packages/kbn-pm/src/utils/project.ts", - "deprecated": false - } - ] - } - ], - "returnComment": [] } ], "initialIsOpen": false } ], "functions": [ - { - "parentPluginId": "@kbn/pm", - "id": "def-server.buildBazelProductionProjects", - "type": "Function", - "tags": [], - "label": "buildBazelProductionProjects", - "description": [], - "signature": [ - "({\n kibanaRoot,\n buildRoot,\n onlyOSS,\n}: { kibanaRoot: string; buildRoot: string; onlyOSS?: boolean | undefined; }) => Promise" - ], - "path": "packages/kbn-pm/src/production/build_bazel_production_projects.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/pm", - "id": "def-server.buildBazelProductionProjects.$1", - "type": "Object", - "tags": [], - "label": "{\n kibanaRoot,\n buildRoot,\n onlyOSS,\n}", - "description": [], - "path": "packages/kbn-pm/src/production/build_bazel_production_projects.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/pm", - "id": "def-server.buildBazelProductionProjects.$1.kibanaRoot", - "type": "string", - "tags": [], - "label": "kibanaRoot", - "description": [], - "path": "packages/kbn-pm/src/production/build_bazel_production_projects.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/pm", - "id": "def-server.buildBazelProductionProjects.$1.buildRoot", - "type": "string", - "tags": [], - "label": "buildRoot", - "description": [], - "path": "packages/kbn-pm/src/production/build_bazel_production_projects.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/pm", - "id": "def-server.buildBazelProductionProjects.$1.onlyOSS", - "type": "CompoundType", - "tags": [], - "label": "onlyOSS", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "packages/kbn-pm/src/production/build_bazel_production_projects.ts", - "deprecated": false - } - ] - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/pm", - "id": "def-server.buildNonBazelProductionProjects", - "type": "Function", - "tags": [], - "label": "buildNonBazelProductionProjects", - "description": [], - "signature": [ - "({\n kibanaRoot,\n buildRoot,\n onlyOSS,\n}: { kibanaRoot: string; buildRoot: string; onlyOSS?: boolean | undefined; }) => Promise" - ], - "path": "packages/kbn-pm/src/production/build_non_bazel_production_projects.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/pm", - "id": "def-server.buildNonBazelProductionProjects.$1", - "type": "Object", - "tags": [], - "label": "{\n kibanaRoot,\n buildRoot,\n onlyOSS,\n}", - "description": [], - "path": "packages/kbn-pm/src/production/build_non_bazel_production_projects.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/pm", - "id": "def-server.buildNonBazelProductionProjects.$1.kibanaRoot", - "type": "string", - "tags": [], - "label": "kibanaRoot", - "description": [], - "path": "packages/kbn-pm/src/production/build_non_bazel_production_projects.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/pm", - "id": "def-server.buildNonBazelProductionProjects.$1.buildRoot", - "type": "string", - "tags": [], - "label": "buildRoot", - "description": [], - "path": "packages/kbn-pm/src/production/build_non_bazel_production_projects.ts", - "deprecated": false - }, - { - "parentPluginId": "@kbn/pm", - "id": "def-server.buildNonBazelProductionProjects.$1.onlyOSS", - "type": "CompoundType", - "tags": [], - "label": "onlyOSS", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "packages/kbn-pm/src/production/build_non_bazel_production_projects.ts", - "deprecated": false - } - ] - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/pm", "id": "def-server.getProjectPaths", @@ -889,42 +707,6 @@ ], "returnComment": [], "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/pm", - "id": "def-server.transformDependencies", - "type": "Function", - "tags": [], - "label": "transformDependencies", - "description": [ - "\nReplaces `link:` dependencies with `file:` dependencies. When installing\ndependencies, these `file:` dependencies will be copied into `node_modules`\ninstead of being symlinked.\n\nThis will allow us to copy packages into the build and run `yarn`, which\nwill then _copy_ the `file:` dependencies into `node_modules` instead of\nsymlinking like we do in development.\n\nAdditionally it also taken care of replacing `link:bazel-bin/` with\n`file:` so we can also support the copy of the Bazel packages dist already into\nbuild/packages to be copied into the node_modules" - ], - "signature": [ - "(dependencies: ", - "IPackageDependencies", - ") => ", - "IPackageDependencies" - ], - "path": "packages/kbn-pm/src/utils/package_json.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/pm", - "id": "def-server.transformDependencies.$1", - "type": "Object", - "tags": [], - "label": "dependencies", - "description": [], - "signature": [ - "IPackageDependencies" - ], - "path": "packages/kbn-pm/src/utils/package_json.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false } ], "interfaces": [], diff --git a/api_docs/kbn_pm.mdx b/api_docs/kbn_pm.mdx index c71a8c2a1e351..b720c9ac68580 100644 --- a/api_docs/kbn_pm.mdx +++ b/api_docs/kbn_pm.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-pm title: "@kbn/pm" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/pm plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/pm'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 63 | 0 | 49 | 5 | +| 47 | 0 | 35 | 5 | ## Server diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index 5ada92c6138c5..6253496021689 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/react-field plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] warning: 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. --- diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 13068a7957a81..94645ec075d4d 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/rule-data-utils plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] warning: 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. --- diff --git a/api_docs/kbn_securitysolution_autocomplete.devdocs.json b/api_docs/kbn_securitysolution_autocomplete.devdocs.json index ee08de8665996..3c23bf9f43822 100644 --- a/api_docs/kbn_securitysolution_autocomplete.devdocs.json +++ b/api_docs/kbn_securitysolution_autocomplete.devdocs.json @@ -305,9 +305,9 @@ "\nGiven an array of lists and optionally a field this will return all\nthe lists that match against the field based on the types from the field\n\nNOTE: That we support one additional property from \"FieldSpec\" located here:\nsrc/plugins/data/common/index_patterns/fields/types.ts\nThis type property is esTypes. If it exists and is on there we will read off the esTypes." ], "signature": [ - "(lists: { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }[], field?: (", + "(lists: { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }[], field?: (", "DataViewFieldBase", - " & { esTypes?: string[] | undefined; }) | undefined) => { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }[]" + " & { esTypes?: string[] | undefined; }) | undefined) => { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }[]" ], "path": "packages/kbn-securitysolution-autocomplete/src/filter_field_to_list/index.ts", "deprecated": false, @@ -322,7 +322,7 @@ "The lists to match against the field" ], "signature": [ - "{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }[]" + "{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }[]" ], "path": "packages/kbn-securitysolution-autocomplete/src/filter_field_to_list/index.ts", "deprecated": false, diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index c2866fcd43b3f..e007783b67ee3 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] warning: 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. --- diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index ed7d867290493..4662dfc9e1a4e 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-es-utils plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] warning: 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. --- diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 41a05b8d05265..4894eb915413e 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] warning: 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. --- diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index bb61fa2b22770..f0e0356561dd8 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] warning: 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. --- diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.devdocs.json b/api_docs/kbn_securitysolution_io_ts_list_types.devdocs.json index f3b10dd30d340..333ab73f958fc 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.devdocs.json +++ b/api_docs/kbn_securitysolution_io_ts_list_types.devdocs.json @@ -27,7 +27,7 @@ "label": "updateExceptionListItemValidate", "description": [], "signature": [ - "(schema: { description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }) => string[]" + "(schema: { description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }) => string[]" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_validation/index.ts", "deprecated": false, @@ -40,7 +40,7 @@ "label": "schema", "description": [], "signature": [ - "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" + "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_validation/index.ts", "deprecated": false, @@ -58,7 +58,7 @@ "label": "validateComments", "description": [], "signature": [ - "(item: { description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }) => string[]" + "(item: { description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }) => string[]" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_validation/index.ts", "deprecated": false, @@ -71,7 +71,7 @@ "label": "item", "description": [], "signature": [ - "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" + "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_validation/index.ts", "deprecated": false, @@ -153,7 +153,7 @@ "label": "listItem", "description": [], "signature": [ - "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" + "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false @@ -1216,7 +1216,7 @@ "label": "listItem", "description": [], "signature": [ - "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" + "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false @@ -1307,7 +1307,7 @@ "label": "exceptions", "description": [], "signature": [ - "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[]" + "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[]" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false @@ -1897,7 +1897,7 @@ "label": "CreateEndpointListItemSchemaDecoded", "description": [], "signature": [ - "Omit<{ description: string; entries: ({ field: string; operator: \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; comments: { comment: string; }[] | undefined; item_id: string | undefined; meta: object | undefined; os_types: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags: string[] | undefined; }, \"tags\" | \"entries\" | \"comments\" | \"item_id\" | \"os_types\"> & { comments: { comment: string; }[]; tags: string[]; item_id: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; os_types: (\"windows\" | \"linux\" | \"macos\")[]; }" + "Omit<{ description: string; entries: ({ field: string; operator: \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; comments: { comment: string; }[] | undefined; item_id: string | undefined; meta: object | undefined; os_types: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags: string[] | undefined; }, \"tags\" | \"entries\" | \"comments\" | \"item_id\" | \"os_types\"> & { comments: { comment: string; }[]; tags: string[]; item_id: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; os_types: (\"windows\" | \"linux\" | \"macos\")[]; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/create_endpoint_list_item_schema/index.ts", "deprecated": false, @@ -1925,7 +1925,7 @@ "label": "CreateExceptionListItemSchema", "description": [], "signature": [ - "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" + "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.ts", "deprecated": false, @@ -1939,7 +1939,7 @@ "label": "CreateExceptionListItemSchemaDecoded", "description": [], "signature": [ - "Omit<{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; comments: { comment: string; }[] | undefined; item_id: string | undefined; meta: object | undefined; namespace_type: \"single\" | \"agnostic\" | undefined; os_types: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags: string[] | undefined; }, \"tags\" | \"entries\" | \"comments\" | \"item_id\" | \"namespace_type\"> & { comments: { comment: string; }[]; tags: string[]; item_id: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; }" + "Omit<{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; comments: { comment: string; }[] | undefined; item_id: string | undefined; meta: object | undefined; namespace_type: \"single\" | \"agnostic\" | undefined; os_types: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags: string[] | undefined; }, \"tags\" | \"entries\" | \"comments\" | \"item_id\" | \"namespace_type\"> & { comments: { comment: string; }[]; tags: string[]; item_id: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/create_exception_list_item_schema/index.ts", "deprecated": false, @@ -2009,7 +2009,7 @@ "label": "CreateListSchema", "description": [], "signature": [ - "{ description: string; name: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; } & { deserializer?: string | undefined; id?: string | undefined; meta?: object | undefined; serializer?: string | undefined; version?: number | undefined; }" + "{ description: string; name: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; } & { deserializer?: string | undefined; id?: string | undefined; meta?: object | undefined; serializer?: string | undefined; version?: number | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.ts", "deprecated": false, @@ -2023,7 +2023,7 @@ "label": "CreateListSchemaDecoded", "description": [], "signature": [ - "{ type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; id: string | undefined; description: string; name: string; meta: object | undefined; serializer: string | undefined; deserializer: string | undefined; } & { version: number; }" + "{ id: string | undefined; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; description: string; name: string; meta: object | undefined; serializer: string | undefined; deserializer: string | undefined; } & { version: number; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/create_list_schema/index.ts", "deprecated": false, @@ -2317,7 +2317,7 @@ "label": "EntriesArray", "description": [], "signature": [ - "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]" + "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.ts", "deprecated": false, @@ -2331,7 +2331,7 @@ "label": "EntriesArrayOrUndefined", "description": [], "signature": [ - "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[] | undefined" + "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[] | undefined" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.ts", "deprecated": false, @@ -2345,7 +2345,7 @@ "label": "Entry", "description": [], "signature": [ - "{ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; }" + "{ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/common/entries/index.ts", "deprecated": false, @@ -2373,7 +2373,7 @@ "label": "EntryList", "description": [], "signature": [ - "{ field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; }" + "{ field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/common/entries_list/index.ts", "deprecated": false, @@ -2443,7 +2443,7 @@ "label": "ExceptionListItemSchema", "description": [], "signature": [ - "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }" + "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/response/exception_list_item_schema/index.ts", "deprecated": false, @@ -2765,7 +2765,7 @@ "label": "FoundExceptionListItemSchema", "description": [], "signature": [ - "{ data: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[]; page: number; per_page: number; total: number; } & { pit?: string | undefined; }" + "{ data: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[]; page: number; per_page: number; total: number; } & { pit?: string | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/response/found_exception_list_item_schema/index.ts", "deprecated": false, @@ -2793,7 +2793,7 @@ "label": "FoundListItemSchema", "description": [], "signature": [ - "{ cursor: string; data: { _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]; page: number; per_page: number; total: number; }" + "{ cursor: string; data: { _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]; page: number; per_page: number; total: number; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_item_schema/index.ts", "deprecated": false, @@ -2807,7 +2807,7 @@ "label": "FoundListSchema", "description": [], "signature": [ - "{ cursor: string; data: { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }[]; page: number; per_page: number; total: number; }" + "{ cursor: string; data: { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }[]; page: number; per_page: number; total: number; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/response/found_list_schema/index.ts", "deprecated": false, @@ -2919,7 +2919,7 @@ "label": "ImportExceptionListItemSchema", "description": [], "signature": [ - "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; item_id: string; list_id: string; name: string; type: \"simple\"; } & { id?: string | undefined; comments?: (({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; }) | { comment: string; })[] | undefined; created_at?: string | undefined; updated_at?: string | undefined; created_by?: string | undefined; updated_by?: string | undefined; _version?: string | undefined; tie_breaker_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" + "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; item_id: string; list_id: string; name: string; type: \"simple\"; } & { id?: string | undefined; comments?: (({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; }) | { comment: string; })[] | undefined; created_at?: string | undefined; updated_at?: string | undefined; created_by?: string | undefined; updated_by?: string | undefined; _version?: string | undefined; tie_breaker_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.ts", "deprecated": false, @@ -2933,7 +2933,7 @@ "label": "ImportExceptionListItemSchemaDecoded", "description": [], "signature": [ - "Omit<{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; item_id: string; list_id: string; name: string; type: \"simple\"; } & { id?: string | undefined; comments?: (({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; }) | { comment: string; })[] | undefined; created_at?: string | undefined; updated_at?: string | undefined; created_by?: string | undefined; updated_by?: string | undefined; _version?: string | undefined; tie_breaker_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }, \"tags\" | \"entries\" | \"comments\" | \"item_id\" | \"namespace_type\"> & { comments: (({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; }) | { comment: string; })[]; tags: string[]; item_id: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; }" + "Omit<{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; item_id: string; list_id: string; name: string; type: \"simple\"; } & { id?: string | undefined; comments?: (({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; }) | { comment: string; })[] | undefined; created_at?: string | undefined; updated_at?: string | undefined; created_by?: string | undefined; updated_by?: string | undefined; _version?: string | undefined; tie_breaker_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }, \"tags\" | \"entries\" | \"comments\" | \"item_id\" | \"namespace_type\"> & { comments: (({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; }) | { comment: string; })[]; tags: string[]; item_id: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/import_exception_item_schema/index.ts", "deprecated": false, @@ -2989,7 +2989,7 @@ "label": "ImportListItemQuerySchema", "description": [], "signature": [ - "{ deserializer: string | undefined; list_id: string | undefined; serializer: string | undefined; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\" | undefined; }" + "{ deserializer: string | undefined; list_id: string | undefined; serializer: string | undefined; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\" | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.ts", "deprecated": false, @@ -3003,7 +3003,7 @@ "label": "ImportListItemQuerySchemaEncoded", "description": [], "signature": [ - "{ deserializer?: string | undefined; list_id?: string | undefined; serializer?: string | undefined; type?: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\" | undefined; }" + "{ deserializer?: string | undefined; list_id?: string | undefined; serializer?: string | undefined; type?: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\" | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/import_list_item_query_schema/index.ts", "deprecated": false, @@ -3115,7 +3115,7 @@ "label": "ListArraySchema", "description": [], "signature": [ - "{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }[]" + "{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }[]" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.ts", "deprecated": false, @@ -3157,7 +3157,7 @@ "label": "ListItemArraySchema", "description": [], "signature": [ - "{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]" + "{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.ts", "deprecated": false, @@ -3185,7 +3185,7 @@ "label": "ListItemSchema", "description": [], "signature": [ - "{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }" + "{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/response/list_item_schema/index.ts", "deprecated": false, @@ -3213,7 +3213,7 @@ "label": "ListSchema", "description": [], "signature": [ - "{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }" + "{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/response/list_schema/index.ts", "deprecated": false, @@ -3423,7 +3423,7 @@ "label": "NonEmptyEntriesArray", "description": [], "signature": [ - "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]" + "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_entries_array/index.ts", "deprecated": false, @@ -3437,7 +3437,7 @@ "label": "NonEmptyEntriesArrayDecoded", "description": [], "signature": [ - "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]" + "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_entries_array/index.ts", "deprecated": false, @@ -3829,7 +3829,7 @@ "label": "SearchListItemArraySchema", "description": [], "signature": [ - "{ items: { _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]; value: unknown; }[]" + "{ items: { _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]; value: unknown; }[]" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.ts", "deprecated": false, @@ -3843,7 +3843,7 @@ "label": "SearchListItemSchema", "description": [], "signature": [ - "{ items: { _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]; value: unknown; }" + "{ items: { _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]; value: unknown; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/response/search_list_item_schema/index.ts", "deprecated": false, @@ -3983,7 +3983,7 @@ "label": "Type", "description": [], "signature": [ - "\"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"" + "\"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/common/type/index.ts", "deprecated": false, @@ -3997,7 +3997,7 @@ "label": "TypeOrUndefined", "description": [], "signature": [ - "\"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\" | undefined" + "\"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\" | undefined" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/common/type/index.ts", "deprecated": false, @@ -4053,7 +4053,7 @@ "label": "UpdateEndpointListItemSchema", "description": [], "signature": [ - "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" + "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.ts", "deprecated": false, @@ -4067,7 +4067,7 @@ "label": "UpdateEndpointListItemSchemaDecoded", "description": [], "signature": [ - "Omit<{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; _version: string | undefined; comments: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id: string | undefined; item_id: string | undefined; meta: object | undefined; os_types: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags: string[] | undefined; }, \"tags\" | \"entries\" | \"comments\"> & { comments: ({ comment: string; } & { id?: string | undefined; })[]; tags: string[]; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; os_types: (\"windows\" | \"linux\" | \"macos\")[]; }" + "Omit<{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; _version: string | undefined; comments: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id: string | undefined; item_id: string | undefined; meta: object | undefined; os_types: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags: string[] | undefined; }, \"tags\" | \"entries\" | \"comments\"> & { comments: ({ comment: string; } & { id?: string | undefined; })[]; tags: string[]; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; os_types: (\"windows\" | \"linux\" | \"macos\")[]; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/update_endpoint_list_item_schema/index.ts", "deprecated": false, @@ -4081,7 +4081,7 @@ "label": "UpdateExceptionListItemSchema", "description": [], "signature": [ - "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" + "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.ts", "deprecated": false, @@ -4095,7 +4095,7 @@ "label": "UpdateExceptionListItemSchemaDecoded", "description": [], "signature": [ - "Omit<{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; _version: string | undefined; comments: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id: string | undefined; item_id: string | undefined; meta: object | undefined; namespace_type: \"single\" | \"agnostic\" | undefined; os_types: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags: string[] | undefined; }, \"tags\" | \"entries\" | \"comments\" | \"namespace_type\" | \"os_types\"> & { comments: ({ comment: string; } & { id?: string | undefined; })[]; tags: string[]; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; }" + "Omit<{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; _version: string | undefined; comments: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id: string | undefined; item_id: string | undefined; meta: object | undefined; namespace_type: \"single\" | \"agnostic\" | undefined; os_types: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags: string[] | undefined; }, \"tags\" | \"entries\" | \"comments\" | \"namespace_type\" | \"os_types\"> & { comments: ({ comment: string; } & { id?: string | undefined; })[]; tags: string[]; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; }" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/update_exception_list_item_schema/index.ts", "deprecated": false, @@ -4628,7 +4628,7 @@ "StringC", "; entries: ", "Type", - "<({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], unknown>; list_id: ", + "<({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], unknown>; list_id: ", "Type", "; name: ", "StringC", @@ -6914,7 +6914,7 @@ "StringC", "; entries: ", "Type", - "<({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], unknown>; item_id: ", + "<({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], unknown>; item_id: ", "Type", "; list_id: ", "Type", @@ -7847,7 +7847,7 @@ ], "signature": [ "Type", - "<({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], unknown>" + "<({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], unknown>" ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/common/non_empty_entries_array/index.ts", "deprecated": false, @@ -8780,7 +8780,7 @@ "StringC", "; entries: ", "Type", - "<({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], unknown>; name: ", + "<({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], unknown>; name: ", "StringC", "; type: ", "KeyofC", @@ -8833,7 +8833,7 @@ "StringC", "; entries: ", "Type", - "<({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], unknown>; name: ", + "<({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[], unknown>; name: ", "StringC", "; type: ", "KeyofC", diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index dd8f1bf8648d9..22b0760147191 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -4,7 +4,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 summary: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] warning: 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. --- diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index 65d532fec19e4..40ecac1736349 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] warning: 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. --- diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index a5c39ba338990..04e891298a380 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] warning: 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. --- diff --git a/api_docs/kbn_securitysolution_list_api.devdocs.json b/api_docs/kbn_securitysolution_list_api.devdocs.json index 816e93b590a41..8c92d4257224a 100644 --- a/api_docs/kbn_securitysolution_list_api.devdocs.json +++ b/api_docs/kbn_securitysolution_list_api.devdocs.json @@ -62,7 +62,7 @@ "signature": [ "({ http, listItem, signal, }: ", "AddExceptionListItemProps", - ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }>" + ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }>" ], "path": "packages/kbn-securitysolution-list-api/src/api/index.ts", "deprecated": false, @@ -206,7 +206,7 @@ "signature": [ "({ http, id, namespaceType, signal, }: ", "ApiCallByIdProps", - ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }>" + ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }>" ], "path": "packages/kbn-securitysolution-list-api/src/api/index.ts", "deprecated": false, @@ -245,7 +245,7 @@ "section": "def-common.DeleteListParams", "text": "DeleteListParams" }, - ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }>" + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }>" ], "path": "packages/kbn-securitysolution-list-api/src/list_api/index.ts", "deprecated": false, @@ -399,7 +399,7 @@ "signature": [ "({ http, id, namespaceType, signal, }: ", "ApiCallByIdProps", - ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }>" + ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }>" ], "path": "packages/kbn-securitysolution-list-api/src/api/index.ts", "deprecated": false, @@ -432,7 +432,7 @@ "signature": [ "({ filterOptions, http, listIds, namespaceTypes, pagination, signal, }: ", "ApiCallByListIdProps", - ") => Promise<{ data: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[]; page: number; per_page: number; total: number; } & { pit?: string | undefined; }>" + ") => Promise<{ data: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[]; page: number; per_page: number; total: number; } & { pit?: string | undefined; }>" ], "path": "packages/kbn-securitysolution-list-api/src/api/index.ts", "deprecated": false, @@ -504,7 +504,7 @@ "section": "def-common.FindListsParams", "text": "FindListsParams" }, - ") => Promise<{ cursor: string; data: { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }[]; page: number; per_page: number; total: number; }>" + ") => Promise<{ cursor: string; data: { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }[]; page: number; per_page: number; total: number; }>" ], "path": "packages/kbn-securitysolution-list-api/src/list_api/index.ts", "deprecated": false, @@ -549,7 +549,7 @@ "section": "def-common.ImportListParams", "text": "ImportListParams" }, - ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }>" + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }>" ], "path": "packages/kbn-securitysolution-list-api/src/list_api/index.ts", "deprecated": false, @@ -743,7 +743,7 @@ "signature": [ "({ http, listItem, signal, }: ", "UpdateExceptionListItemProps", - ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }>" + ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }>" ], "path": "packages/kbn-securitysolution-list-api/src/api/index.ts", "deprecated": false, @@ -1076,7 +1076,7 @@ "label": "type", "description": [], "signature": [ - "\"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\" | undefined" + "\"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\" | undefined" ], "path": "packages/kbn-securitysolution-list-api/src/list_api/types.ts", "deprecated": false diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index 0f81fc34f3478..8b6b8cf07c15a 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-list-api plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] warning: 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. --- diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 698ac13af76d0..6615bac9efb38 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-list-constants plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] warning: 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. --- diff --git a/api_docs/kbn_securitysolution_list_hooks.devdocs.json b/api_docs/kbn_securitysolution_list_hooks.devdocs.json index 248881607be2d..48a866f37b9f3 100644 --- a/api_docs/kbn_securitysolution_list_hooks.devdocs.json +++ b/api_docs/kbn_securitysolution_list_hooks.devdocs.json @@ -29,7 +29,7 @@ "\nThis adds an id to the incoming exception item entries as ReactJS prefers to have\nan id added to them for use as a stable id. Later if we decide to change the data\nmodel to have id's within the array then this code should be removed. If not, then\nthis code should stay as an adapter for ReactJS.\n\nThis does break the type system slightly as we are lying a bit to the type system as we return\nthe same exceptionItem as we have previously but are augmenting the arrays with an id which TypeScript\ndoesn't mind us doing here. However, downstream you will notice that you have an id when the type\ndoes not indicate it. In that case use (ExceptionItem & { id: string }) temporarily if you're using the id. If you're not,\nyou can ignore the id and just use the normal TypeScript with ReactJS.\n" ], "signature": [ - "(exceptionItem: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }) => { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }" + "(exceptionItem: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }) => { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }" ], "path": "packages/kbn-securitysolution-list-hooks/src/transforms/index.ts", "deprecated": false, @@ -44,7 +44,7 @@ "The exceptionItem to add an id to the threat matches." ], "signature": [ - "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }" + "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }" ], "path": "packages/kbn-securitysolution-list-hooks/src/transforms/index.ts", "deprecated": false, @@ -66,7 +66,7 @@ "\nThis removes an id from the exceptionItem entries as ReactJS prefers to have\nan id added to them for use as a stable id. Later if we decide to change the data\nmodel to have id's within the array then this code should be removed. If not, then\nthis code should stay as an adapter for ReactJS.\n" ], "signature": [ - "(exceptionItem: T) => T" + "(exceptionItem: T) => T" ], "path": "packages/kbn-securitysolution-list-hooks/src/transforms/index.ts", "deprecated": false, @@ -103,7 +103,7 @@ "\nTransforms the output of rules to compensate for technical debt or UI concerns such as\nReactJS preferences for having ids within arrays if the data is not modeled that way.\n\nIf you add a new transform of the input called \"myNewTransform\" do it\nin the form of:\nflow(addIdToExceptionItemEntries, myNewTransform)(exceptionItem)\n" ], "signature": [ - "(exceptionItem: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }) => { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }" + "(exceptionItem: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }) => { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }" ], "path": "packages/kbn-securitysolution-list-hooks/src/transforms/index.ts", "deprecated": false, @@ -118,7 +118,7 @@ "The exceptionItem to transform the output of" ], "signature": [ - "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }" + "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }" ], "path": "packages/kbn-securitysolution-list-hooks/src/transforms/index.ts", "deprecated": false, @@ -138,7 +138,7 @@ "label": "transformNewItemOutput", "description": [], "signature": [ - "(exceptionItem: { description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }) => { description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" + "(exceptionItem: { description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }) => { description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" ], "path": "packages/kbn-securitysolution-list-hooks/src/transforms/index.ts", "deprecated": false, @@ -151,7 +151,7 @@ "label": "exceptionItem", "description": [], "signature": [ - "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" + "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" ], "path": "packages/kbn-securitysolution-list-hooks/src/transforms/index.ts", "deprecated": false, @@ -171,7 +171,7 @@ "\nTransforms the output of exception items to compensate for technical debt or UI concerns such as\nReactJS preferences for having ids within arrays if the data is not modeled that way.\n\nIf you add a new transform of the output called \"myNewTransform\" do it\nin the form of:\nflow(removeIdFromExceptionItemsEntries, myNewTransform)(exceptionItem)\n" ], "signature": [ - "(exceptionItem: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; })) => { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; })" + "(exceptionItem: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; })) => { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; })" ], "path": "packages/kbn-securitysolution-list-hooks/src/transforms/index.ts", "deprecated": false, @@ -186,7 +186,7 @@ "The exceptionItem to transform the output of" ], "signature": [ - "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; })" + "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; })" ], "path": "packages/kbn-securitysolution-list-hooks/src/transforms/index.ts", "deprecated": false, @@ -317,7 +317,7 @@ "OptionalSignalArgs", "<", "DeleteListParams", - ">], { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }>" + ">], { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }>" ], "path": "packages/kbn-securitysolution-list-hooks/src/use_delete_list/index.ts", "deprecated": false, @@ -445,7 +445,7 @@ "OptionalSignalArgs", "<", "FindListsParams", - ">], { cursor: string; data: { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }[]; page: number; per_page: number; total: number; }>" + ">], { cursor: string; data: { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }[]; page: number; per_page: number; total: number; }>" ], "path": "packages/kbn-securitysolution-list-hooks/src/use_find_lists/index.ts", "deprecated": false, @@ -467,7 +467,7 @@ "OptionalSignalArgs", "<", "ImportListParams", - ">], { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }>" + ">], { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }>" ], "path": "packages/kbn-securitysolution-list-hooks/src/use_import_list/index.ts", "deprecated": false, @@ -623,7 +623,7 @@ "label": "addExceptionListItem", "description": [], "signature": [ - "(arg: { listItem: { description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }; }) => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }>" + "(arg: { listItem: { description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }; }) => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }>" ], "path": "packages/kbn-securitysolution-list-hooks/src/use_api/index.ts", "deprecated": false, @@ -646,7 +646,7 @@ "label": "listItem", "description": [], "signature": [ - "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" + "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" ], "path": "packages/kbn-securitysolution-list-hooks/src/use_api/index.ts", "deprecated": false @@ -664,7 +664,7 @@ "label": "updateExceptionListItem", "description": [], "signature": [ - "(arg: { listItem: { description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }; }) => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }>" + "(arg: { listItem: { description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }; }) => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }>" ], "path": "packages/kbn-securitysolution-list-hooks/src/use_api/index.ts", "deprecated": false, @@ -687,7 +687,7 @@ "label": "listItem", "description": [], "signature": [ - "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" + "{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }" ], "path": "packages/kbn-securitysolution-list-hooks/src/use_api/index.ts", "deprecated": false @@ -771,7 +771,7 @@ "signature": [ "(arg: ", "ApiCallMemoProps", - " & { onSuccess: (arg: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }) => void; }) => Promise" + " & { onSuccess: (arg: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }) => void; }) => Promise" ], "path": "packages/kbn-securitysolution-list-hooks/src/use_api/index.ts", "deprecated": false, @@ -785,7 +785,7 @@ "description": [], "signature": [ "ApiCallMemoProps", - " & { onSuccess: (arg: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }) => void; }" + " & { onSuccess: (arg: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }) => void; }" ], "path": "packages/kbn-securitysolution-list-hooks/src/use_api/index.ts", "deprecated": false, @@ -954,7 +954,7 @@ "label": "ReturnExceptionListAndItems", "description": [], "signature": [ - "[boolean, { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[], ", + "[boolean, { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[], ", "Pagination", ", Func | null]" ], @@ -996,7 +996,7 @@ "label": "ReturnPersistExceptionItem", "description": [], "signature": [ - "[PersistReturnExceptionItem, React.Dispatch<({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }) | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }) | null>]" + "[PersistReturnExceptionItem, React.Dispatch<({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }) | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; name: string; type: \"simple\"; } & { _version?: string | undefined; comments?: ({ comment: string; } & { id?: string | undefined; })[] | undefined; id?: string | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }) | null>]" ], "path": "packages/kbn-securitysolution-list-hooks/src/use_persist_exception_item/index.ts", "deprecated": false, diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index db34eab6092e6..70e03cbfed95c 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] warning: 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. --- diff --git a/api_docs/kbn_securitysolution_list_utils.devdocs.json b/api_docs/kbn_securitysolution_list_utils.devdocs.json index e10ed1097e954..89f36219cbe6b 100644 --- a/api_docs/kbn_securitysolution_list_utils.devdocs.json +++ b/api_docs/kbn_securitysolution_list_utils.devdocs.json @@ -27,7 +27,7 @@ "label": "addIdToEntries", "description": [], "signature": [ - "(entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]) => ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]" + "(entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]) => ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]" ], "path": "packages/kbn-securitysolution-list-utils/src/helpers/index.ts", "deprecated": false, @@ -40,7 +40,7 @@ "label": "entries", "description": [], "signature": [ - "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]" + "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]" ], "path": "packages/kbn-securitysolution-list-utils/src/helpers/index.ts", "deprecated": false, @@ -58,7 +58,7 @@ "label": "buildExceptionFilter", "description": [], "signature": [ - "({ lists, excludeExceptions, chunkSize, alias, }: { lists: ({ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }))[]; excludeExceptions: boolean; chunkSize: number; alias: string | null; }) => ", + "({ lists, excludeExceptions, chunkSize, alias, }: { lists: ({ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }))[]; excludeExceptions: boolean; chunkSize: number; alias: string | null; }) => ", "Filter", " | undefined" ], @@ -83,7 +83,7 @@ "label": "lists", "description": [], "signature": [ - "({ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }))[]" + "({ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }))[]" ], "path": "packages/kbn-securitysolution-list-utils/src/build_exception_filter/index.ts", "deprecated": false @@ -703,7 +703,7 @@ "section": "def-common.ExceptionsBuilderExceptionItem", "text": "ExceptionsBuilderExceptionItem" }, - "[]) => ({ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }))[]" + "[]) => ({ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }))[]" ], "path": "packages/kbn-securitysolution-list-utils/src/helpers/index.ts", "deprecated": false, @@ -957,7 +957,7 @@ "section": "def-common.FormattedBuilderEntry", "text": "FormattedBuilderEntry" }, - ") => ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; }) & { id?: string | undefined; }" + ") => ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; }) & { id?: string | undefined; }" ], "path": "packages/kbn-securitysolution-list-utils/src/helpers/index.ts", "deprecated": false, @@ -1099,7 +1099,7 @@ "section": "def-common.FormattedBuilderEntry", "text": "FormattedBuilderEntry" }, - ", newField: { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }) => { index: number; updatedEntry: ", + ", newField: { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }) => { index: number; updatedEntry: ", { "pluginId": "@kbn/securitysolution-list-utils", "scope": "common", @@ -1144,7 +1144,7 @@ "- newly selected list" ], "signature": [ - "{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }" + "{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }" ], "path": "packages/kbn-securitysolution-list-utils/src/helpers/index.ts", "deprecated": false, @@ -2529,7 +2529,7 @@ "label": "hasLargeValueList", "description": [], "signature": [ - "(entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]) => boolean" + "(entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]) => boolean" ], "path": "packages/kbn-securitysolution-list-utils/src/has_large_value_list/index.ts", "deprecated": false, @@ -2542,7 +2542,7 @@ "label": "entries", "description": [], "signature": [ - "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]" + "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]" ], "path": "packages/kbn-securitysolution-list-utils/src/has_large_value_list/index.ts", "deprecated": false, @@ -3170,7 +3170,7 @@ "label": "BuilderEntry", "description": [], "signature": [ - "(({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; }) & { id?: string | undefined; }) | ", + "(({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; }) & { id?: string | undefined; }) | ", { "pluginId": "@kbn/securitysolution-list-utils", "scope": "common", @@ -3229,7 +3229,7 @@ "label": "CreateExceptionListItemBuilderSchema", "description": [], "signature": [ - "Omit<{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }, \"meta\" | \"entries\"> & { meta: { temporaryUuid: string; }; entries: ", + "Omit<{ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }, \"meta\" | \"entries\"> & { meta: { temporaryUuid: string; }; entries: ", { "pluginId": "@kbn/securitysolution-list-utils", "scope": "common", @@ -3363,7 +3363,7 @@ "label": "ExceptionListItemBuilderSchema", "description": [], "signature": [ - "Omit<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }, \"entries\"> & { entries: ", + "Omit<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }, \"entries\"> & { entries: ", { "pluginId": "@kbn/securitysolution-list-utils", "scope": "common", diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index 935139d5e2871..39b7d58043270 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-list-utils plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] warning: 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. --- diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index 8063a4d5dd5fa..e0a298ed33ecc 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-rules plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] warning: 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. --- diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index 6b5ef155d35b4..269196aeeda43 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-t-grid plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] warning: 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. --- diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index d9c6d570aa91b..f89634ac45a1b 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-utils plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] warning: 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. --- diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index 5947022a02051..b7177420222a4 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/server-http-tools plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] warning: 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. --- diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index 59f1273b9499f..b4ded054b09ea 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/server-route-repository plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] warning: 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. --- diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.devdocs.json b/api_docs/kbn_shared_ux_button_exit_full_screen.devdocs.json new file mode 100644 index 0000000000000..bec75a657ccc1 --- /dev/null +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.devdocs.json @@ -0,0 +1,242 @@ +{ + "id": "@kbn/shared-ux-button-exit-full-screen", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/shared-ux-button-exit-full-screen", + "id": "def-common.ExitFullScreenButton", + "type": "Function", + "tags": [], + "label": "ExitFullScreenButton", + "description": [ + "\nA component that can be used to exit full screen mode in Kibana. Requires a Provider for\nrelevant services." + ], + "signature": [ + "React.ForwardRefExoticComponent<", + "Props", + " & React.RefAttributes<{}>>" + ], + "path": "packages/shared-ux/button/exit_full_screen/src/index.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/shared-ux-button-exit-full-screen", + "id": "def-common.ExitFullScreenButton.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-button-exit-full-screen", + "id": "def-common.ExitFullScreenButtonComponent", + "type": "Function", + "tags": [], + "label": "ExitFullScreenButtonComponent", + "description": [ + "\nA pure component that resembles a button to exit full screen mode." + ], + "signature": [ + "React.ForwardRefExoticComponent<", + "Props", + " & React.RefAttributes<{}>>" + ], + "path": "packages/shared-ux/button/exit_full_screen/src/index.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/shared-ux-button-exit-full-screen", + "id": "def-common.ExitFullScreenButtonComponent.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-button-exit-full-screen", + "id": "def-common.ExitFullScreenButtonKibanaProvider", + "type": "Function", + "tags": [], + "label": "ExitFullScreenButtonKibanaProvider", + "description": [ + "\nKibana-specific Provider that maps to known dependency types." + ], + "signature": [ + "({ children, ...services }: React.PropsWithChildren<", + "KibanaServices", + ">) => JSX.Element" + ], + "path": "packages/shared-ux/button/exit_full_screen/src/services.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-button-exit-full-screen", + "id": "def-common.ExitFullScreenButtonKibanaProvider.$1", + "type": "CompoundType", + "tags": [], + "label": "{\n children,\n ...services\n}", + "description": [], + "signature": [ + "React.PropsWithChildren<", + "KibanaServices", + ">" + ], + "path": "packages/shared-ux/button/exit_full_screen/src/services.tsx", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-button-exit-full-screen", + "id": "def-common.ExitFullScreenButtonProvider", + "type": "Function", + "tags": [], + "label": "ExitFullScreenButtonProvider", + "description": [ + "\nAbstract external service Provider." + ], + "signature": [ + "({ children, ...services }: React.PropsWithChildren<", + "Services", + ">) => JSX.Element" + ], + "path": "packages/shared-ux/button/exit_full_screen/src/services.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-button-exit-full-screen", + "id": "def-common.ExitFullScreenButtonProvider.$1", + "type": "CompoundType", + "tags": [], + "label": "{ children, ...services }", + "description": [], + "signature": [ + "React.PropsWithChildren<", + "Services", + ">" + ], + "path": "packages/shared-ux/button/exit_full_screen/src/services.tsx", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-button-exit-full-screen", + "id": "def-common.LazyExitFullScreenButton", + "type": "Function", + "tags": [], + "label": "LazyExitFullScreenButton", + "description": [ + "\nLazy-loaded connected component. Must be wrapped in `React.Suspense` and a Provider." + ], + "signature": [ + "React.ExoticComponent<", + "Props", + "> & { readonly _result: ({ onExit, toggleChrome }: ", + "Props", + ") => JSX.Element; }" + ], + "path": "packages/shared-ux/button/exit_full_screen/src/index.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/shared-ux-button-exit-full-screen", + "id": "def-common.LazyExitFullScreenButton.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-button-exit-full-screen", + "id": "def-common.LazyExitFullScreenButtonComponent", + "type": "Function", + "tags": [], + "label": "LazyExitFullScreenButtonComponent", + "description": [ + "\nLazy-loaded pure component. Must be wrapped in `React.Suspense`." + ], + "signature": [ + "React.ExoticComponent<", + "Props", + "> & { readonly _result: ({ onClick, className }: ", + "Props", + ") => JSX.Element; }" + ], + "path": "packages/shared-ux/button/exit_full_screen/src/index.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/shared-ux-button-exit-full-screen", + "id": "def-common.LazyExitFullScreenButtonComponent.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx new file mode 100644 index 0000000000000..16c76cd894f90 --- /dev/null +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -0,0 +1,27 @@ +--- +id: kibKbnSharedUxButtonExitFullScreenPluginApi +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 +summary: API docs for the @kbn/shared-ux-button-exit-full-screen plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] +warning: 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. +--- +import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 12 | 0 | 2 | 3 | + +## Common + +### Functions + + diff --git a/api_docs/kbn_shared_ux_components.devdocs.json b/api_docs/kbn_shared_ux_components.devdocs.json index 77f778bb3f118..fd3d022aeaef2 100644 --- a/api_docs/kbn_shared_ux_components.devdocs.json +++ b/api_docs/kbn_shared_ux_components.devdocs.json @@ -54,12 +54,12 @@ }, { "parentPluginId": "@kbn/shared-ux-components", - "id": "def-common.ExitFullScreenButton", + "id": "def-common.IconButtonGroup", "type": "Function", "tags": [], - "label": "ExitFullScreenButton", + "label": "IconButtonGroup", "description": [ - "\nA `ExitFullScreenButton` component that is wrapped by the `withSuspense` HOC. This component can\nbe used directly by consumers and will load the `LazyExitFullScreenButton` component lazily with\na predefined fallback and error boundary." + "\nThe IconButtonGroup component that is wrapped by the `withSuspence` HOC." ], "signature": [ "React.ForwardRefExoticComponent<", @@ -72,7 +72,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-components", - "id": "def-common.ExitFullScreenButton.$1", + "id": "def-common.IconButtonGroup.$1", "type": "Uncategorized", "tags": [], "label": "props", @@ -88,17 +88,35 @@ }, { "parentPluginId": "@kbn/shared-ux-components", - "id": "def-common.IconButtonGroup", + "id": "def-common.KibanaPageTemplate", "type": "Function", "tags": [], - "label": "IconButtonGroup", + "label": "KibanaPageTemplate", "description": [ - "\nThe IconButtonGroup component that is wrapped by the `withSuspence` HOC." + "\nA `KibanaPageTemplate` component that is wrapped by the `withSuspense` HOC. This component can\nbe used directly by consumers and will load the `KibanaPageTemplateLazy` component lazily with\na predefined fallback and error boundary." ], "signature": [ - "React.ForwardRefExoticComponent<", - "Props", - " & React.RefAttributes<{}>>" + "React.ForwardRefExoticComponent & { template?: \"default\" | \"empty\" | \"centeredBody\" | \"centeredContent\" | undefined; paddingSize?: \"none\" | \"m\" | \"s\" | \"l\" | undefined; pageSideBar?: React.ReactNode; pageSideBarProps?: ", + "EuiPageSideBarProps", + " | undefined; pageHeader?: ", + "EuiPageHeaderProps", + " | undefined; pageBodyProps?: ", + "EuiPageBodyProps", + "<\"main\"> | undefined; pageContentProps?: ", + "EuiPageContentProps", + " | undefined; pageContentBodyProps?: ", + "EuiPageContentBodyProps", + " | undefined; bottomBar?: React.ReactNode; bottomBarProps?: ", + "EuiBottomBarProps", + " | undefined; fullHeight?: boolean | \"noscroll\" | undefined; minHeight?: ", + "MinHeightProperty", + " | undefined; } & { isEmptyState?: boolean | undefined; solutionNav?: ", + "KibanaPageTemplateSolutionNavProps", + " | undefined; noDataConfig?: ", + "NoDataPageProps", + " | undefined; } & React.RefAttributes<{}>>" ], "path": "packages/kbn-shared-ux-components/src/index.ts", "deprecated": false, @@ -106,7 +124,61 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-components", - "id": "def-common.IconButtonGroup.$1", + "id": "def-common.KibanaPageTemplate.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-components", + "id": "def-common.KibanaPageTemplateLazy", + "type": "Function", + "tags": [], + "label": "KibanaPageTemplateLazy", + "description": [ + "\nThe lazily loaded `KibanaPageTemplate` component that is wrapped by the `withSuspense` HOC. Consumers should use\n`React.Suspense` or `withSuspense` HOC to load this component." + ], + "signature": [ + "React.ExoticComponent & { template?: \"default\" | \"empty\" | \"centeredBody\" | \"centeredContent\" | undefined; paddingSize?: \"none\" | \"m\" | \"s\" | \"l\" | undefined; pageSideBar?: React.ReactNode; pageSideBarProps?: ", + "EuiPageSideBarProps", + " | undefined; pageHeader?: ", + "EuiPageHeaderProps", + " | undefined; pageBodyProps?: ", + "EuiPageBodyProps", + "<\"main\"> | undefined; pageContentProps?: ", + "EuiPageContentProps", + " | undefined; pageContentBodyProps?: ", + "EuiPageContentBodyProps", + " | undefined; bottomBar?: React.ReactNode; bottomBarProps?: ", + "EuiBottomBarProps", + " | undefined; fullHeight?: boolean | \"noscroll\" | undefined; minHeight?: ", + "MinHeightProperty", + " | undefined; } & { isEmptyState?: boolean | undefined; solutionNav?: ", + "KibanaPageTemplateSolutionNavProps", + " | undefined; noDataConfig?: ", + "NoDataPageProps", + " | undefined; } & { children?: React.ReactNode; }> & { readonly _result: React.FunctionComponent<", + "KibanaPageTemplateProps", + ">; }" + ], + "path": "packages/kbn-shared-ux-components/src/index.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/shared-ux-components", + "id": "def-common.KibanaPageTemplateLazy.$1", "type": "Uncategorized", "tags": [], "label": "props", @@ -280,42 +352,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "@kbn/shared-ux-components", - "id": "def-common.LazyExitFullScreenButton", - "type": "Function", - "tags": [], - "label": "LazyExitFullScreenButton", - "description": [ - "\nThe Lazily-loaded `ExitFullScreenButton` component. Consumers should use `React.Suspennse` or the\n`withSuspense` HOC to load this component." - ], - "signature": [ - "React.ExoticComponent<", - "Props", - "> & { readonly _result: ({ onExit, toggleChrome }: ", - "Props", - ") => JSX.Element; }" - ], - "path": "packages/kbn-shared-ux-components/src/index.ts", - "deprecated": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "@kbn/shared-ux-components", - "id": "def-common.LazyExitFullScreenButton.$1", - "type": "Uncategorized", - "tags": [], - "label": "props", - "description": [], - "signature": [ - "P" - ], - "path": "node_modules/@types/react/index.d.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/shared-ux-components", "id": "def-common.LazyIconButtonGroup", @@ -323,7 +359,7 @@ "tags": [], "label": "LazyIconButtonGroup", "description": [ - "\nThe Lazily-loaded `IconButtonGroup` component. Consumers should use `React.Suspennse` or the\n`withSuspense` HOC to load this component." + "\nThe Lazily-loaded `IconButtonGroup` component. Consumers should use `React.Suspense` or the\n`withSuspense` HOC to load this component." ], "signature": [ "React.ExoticComponent<", @@ -359,7 +395,7 @@ "tags": [], "label": "LazyNoDataViews", "description": [ - "\nThe Lazily-loaded `NoDataViews` component. Consumers should use `React.Suspennse` or the\n`withSuspense` HOC to load this component." + "\nThe Lazily-loaded `NoDataViews` component. Consumers should use `React.Suspense` or the\n`withSuspense` HOC to load this component." ], "signature": [ "React.ExoticComponent<", @@ -395,7 +431,7 @@ "tags": [], "label": "LazyNoDataViewsComponent", "description": [ - "\nA pure `NoDataViews` component, with no services hooks. Consumers should use `React.Suspennse` or the\n`withSuspense` HOC to load this component." + "\nA pure `NoDataViews` component, with no services hooks. Consumers should use `React.Suspense` or the\n`withSuspense` HOC to load this component." ], "signature": [ "React.ExoticComponent<", @@ -434,7 +470,7 @@ "signature": [ "React.ExoticComponent<", "Props", - "> & { readonly _result: ({ label, ...rest }: ", + "> & { readonly _result: ({ label, iconSide, ...rest }: ", "Props", ") => JSX.Element; }" ], @@ -536,7 +572,7 @@ "signature": [ "React.ExoticComponent & { ref?: React.RefObject | ((instance: HTMLDivElement | null) => void) | null | undefined; }> & { readonly _result: React.FC<", + " & { children?: React.ReactNode; }, \"children\" | \"color\" | \"className\" | \"title\" | \"onChange\" | \"onKeyDown\" | \"onClick\" | \"key\" | \"id\" | \"css\" | \"security\" | \"defaultValue\" | \"hidden\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"style\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"role\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-label\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"currentAppId$\" | \"navigateToUrl\"> & { ref?: React.RefObject | ((instance: HTMLDivElement | null) => void) | null | undefined; }> & { readonly _result: React.FC<", "RedirectAppLinksProps", ">; }" ], @@ -593,6 +629,39 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-components", + "id": "def-common.ToolbarPopover", + "type": "Function", + "tags": [], + "label": "ToolbarPopover", + "description": [], + "signature": [ + "({ label, iconType, children, iconSide, ...popover }: ", + "Props", + ") => JSX.Element" + ], + "path": "packages/kbn-shared-ux-components/src/toolbar/popovers/popover.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-components", + "id": "def-common.ToolbarPopover.$1", + "type": "CompoundType", + "tags": [], + "label": "{ label, iconType, children, iconSide, ...popover }", + "description": [], + "signature": [ + "Props" + ], + "path": "packages/kbn-shared-ux-components/src/toolbar/popovers/popover.tsx", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [], diff --git a/api_docs/kbn_shared_ux_components.mdx b/api_docs/kbn_shared_ux_components.mdx index f5b118c1b36d2..41704c6d85694 100644 --- a/api_docs/kbn_shared_ux_components.mdx +++ b/api_docs/kbn_shared_ux_components.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-components title: "@kbn/shared-ux-components" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-components plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-components'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 32 | 0 | 4 | 4 | +| 34 | 0 | 6 | 6 | ## Common diff --git a/api_docs/kbn_shared_ux_services.mdx b/api_docs/kbn_shared_ux_services.mdx index e2cb765ac7161..98fc062b43b0b 100644 --- a/api_docs/kbn_shared_ux_services.mdx +++ b/api_docs/kbn_shared_ux_services.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-services title: "@kbn/shared-ux-services" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-services plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-services'] warning: 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. --- diff --git a/api_docs/kbn_shared_ux_storybook.mdx b/api_docs/kbn_shared_ux_storybook.mdx index 7643af3cf9f90..2ce78307c21c8 100644 --- a/api_docs/kbn_shared_ux_storybook.mdx +++ b/api_docs/kbn_shared_ux_storybook.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook title: "@kbn/shared-ux-storybook" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-storybook plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook'] warning: 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. --- diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index 31ccc6897e4cb..5d8a6344b7906 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-utility plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] warning: 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. --- diff --git a/api_docs/kbn_sort_package_json.devdocs.json b/api_docs/kbn_sort_package_json.devdocs.json new file mode 100644 index 0000000000000..1c3fb56277e17 --- /dev/null +++ b/api_docs/kbn_sort_package_json.devdocs.json @@ -0,0 +1,59 @@ +{ + "id": "@kbn/sort-package-json", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/sort-package-json", + "id": "def-server.sortPackageJson", + "type": "Function", + "tags": [], + "label": "sortPackageJson", + "description": [], + "signature": [ + "(json: string) => string" + ], + "path": "packages/kbn-sort-package-json/src/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/sort-package-json", + "id": "def-server.sortPackageJson.$1", + "type": "string", + "tags": [], + "label": "json", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-sort-package-json/src/index.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_sort_package_json.mdx b/api_docs/kbn_sort_package_json.mdx new file mode 100644 index 0000000000000..de6879b4cef66 --- /dev/null +++ b/api_docs/kbn_sort_package_json.mdx @@ -0,0 +1,27 @@ +--- +id: kibKbnSortPackageJsonPluginApi +slug: /kibana-dev-docs/api/kbn-sort-package-json +title: "@kbn/sort-package-json" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/sort-package-json plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-package-json'] +warning: 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. +--- +import kbnSortPackageJsonObj from './kbn_sort_package_json.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 2 | 0 | 2 | 0 | + +## Server + +### Functions + + diff --git a/api_docs/kbn_std.devdocs.json b/api_docs/kbn_std.devdocs.json index 3c7b5119d2b21..95993490eec8d 100644 --- a/api_docs/kbn_std.devdocs.json +++ b/api_docs/kbn_std.devdocs.json @@ -55,7 +55,7 @@ ], "signature": [ "(iterable: ", - "IterableInput", + "ObservableInput", ", fn: ", "AsyncMapFn", ") => Promise" @@ -73,7 +73,7 @@ "Items to iterate" ], "signature": [ - "IterableInput", + "ObservableInput", "" ], "path": "packages/kbn-std/src/iteration/for_each.ts", @@ -112,7 +112,7 @@ ], "signature": [ "(iterable: ", - "IterableInput", + "ObservableInput", ", limit: number, fn: ", "AsyncMapFn", ") => Promise" @@ -130,7 +130,7 @@ "Items to iterate" ], "signature": [ - "IterableInput", + "ObservableInput", "" ], "path": "packages/kbn-std/src/iteration/for_each.ts", @@ -185,7 +185,7 @@ ], "signature": [ "(iterable: ", - "IterableInput", + "ObservableInput", ", fn: ", "AsyncMapFn", ") => Promise" @@ -203,7 +203,7 @@ "Items to iterate" ], "signature": [ - "IterableInput", + "ObservableInput", "" ], "path": "packages/kbn-std/src/iteration/map.ts", @@ -242,7 +242,7 @@ ], "signature": [ "(iterable: ", - "IterableInput", + "ObservableInput", ", limit: number, fn: ", "AsyncMapFn", ") => Promise" @@ -260,7 +260,7 @@ "Items to iterate" ], "signature": [ - "IterableInput", + "ObservableInput", "" ], "path": "packages/kbn-std/src/iteration/map.ts", @@ -370,40 +370,6 @@ "returnComment": [], "initialIsOpen": false }, - { - "parentPluginId": "@kbn/std", - "id": "def-server.firstValueFrom", - "type": "Function", - "tags": [], - "label": "firstValueFrom", - "description": [], - "signature": [ - "(source: ", - "Observable", - ") => Promise" - ], - "path": "packages/kbn-std/src/rxjs_7.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/std", - "id": "def-server.firstValueFrom.$1", - "type": "Object", - "tags": [], - "label": "source", - "description": [], - "signature": [ - "Observable", - "" - ], - "path": "packages/kbn-std/src/rxjs_7.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/std", "id": "def-server.get", @@ -806,40 +772,6 @@ "returnComment": [], "initialIsOpen": false }, - { - "parentPluginId": "@kbn/std", - "id": "def-server.lastValueFrom", - "type": "Function", - "tags": [], - "label": "lastValueFrom", - "description": [], - "signature": [ - "(source: ", - "Observable", - ") => Promise" - ], - "path": "packages/kbn-std/src/rxjs_7.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/std", - "id": "def-server.lastValueFrom.$1", - "type": "Object", - "tags": [], - "label": "source", - "description": [], - "signature": [ - "Observable", - "" - ], - "path": "packages/kbn-std/src/rxjs_7.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/std", "id": "def-server.map$", @@ -851,7 +783,7 @@ ], "signature": [ "(iterable: ", - "IterableInput", + "ObservableInput", ", fn: ", "AsyncMapFn", ") => ", @@ -871,7 +803,7 @@ "Items to iterate" ], "signature": [ - "IterableInput", + "ObservableInput", "" ], "path": "packages/kbn-std/src/iteration/observable.ts", @@ -941,7 +873,7 @@ ], "signature": [ "(iterable: ", - "IterableInput", + "ObservableInput", ", limit: number, fn: ", "AsyncMapFn", ") => ", @@ -961,7 +893,7 @@ "Items to iterate" ], "signature": [ - "IterableInput", + "ObservableInput", "" ], "path": "packages/kbn-std/src/iteration/observable.ts", diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index ad1559fa9ea9d..f998a00c60e83 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/std plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 96 | 1 | 63 | 2 | +| 92 | 1 | 59 | 1 | ## Server diff --git a/api_docs/kbn_stdio_dev_helpers.devdocs.json b/api_docs/kbn_stdio_dev_helpers.devdocs.json new file mode 100644 index 0000000000000..478b3ea7c54e8 --- /dev/null +++ b/api_docs/kbn_stdio_dev_helpers.devdocs.json @@ -0,0 +1,104 @@ +{ + "id": "@kbn/stdio-dev-helpers", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/stdio-dev-helpers", + "id": "def-server.observeLines", + "type": "Function", + "tags": [ + "return" + ], + "label": "observeLines", + "description": [ + "\n Creates an Observable from a Readable Stream that:\n - splits data from `readable` into lines\n - completes when `readable` emits \"end\"\n - fails if `readable` emits \"errors\"\n" + ], + "signature": [ + "(readable: ", + "Readable", + ") => ", + "Observable", + "" + ], + "path": "packages/kbn-stdio-dev-helpers/src/observe_lines.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/stdio-dev-helpers", + "id": "def-server.observeLines.$1", + "type": "Object", + "tags": [], + "label": "readable", + "description": [], + "signature": [ + "Readable" + ], + "path": "packages/kbn-stdio-dev-helpers/src/observe_lines.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/stdio-dev-helpers", + "id": "def-server.observeReadable", + "type": "Function", + "tags": [], + "label": "observeReadable", + "description": [ + "\n Produces an Observable from a ReadableSteam that:\n - completes on the first \"end\" event\n - fails on the first \"error\" event" + ], + "signature": [ + "(readable: ", + "Readable", + ") => ", + "Observable", + "" + ], + "path": "packages/kbn-stdio-dev-helpers/src/observe_readable.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/stdio-dev-helpers", + "id": "def-server.observeReadable.$1", + "type": "Object", + "tags": [], + "label": "readable", + "description": [], + "signature": [ + "Readable" + ], + "path": "packages/kbn-stdio-dev-helpers/src/observe_readable.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx new file mode 100644 index 0000000000000..0397b21fa5d78 --- /dev/null +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -0,0 +1,27 @@ +--- +id: kibKbnStdioDevHelpersPluginApi +slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers +title: "@kbn/stdio-dev-helpers" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/stdio-dev-helpers plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] +warning: 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. +--- +import kbnStdioDevHelpersObj from './kbn_stdio_dev_helpers.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 4 | 0 | 2 | 0 | + +## Server + +### Functions + + diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index 98453e30ef707..c44717161b692 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/storybook plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] warning: 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. --- diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index 0e9beedb79fde..e925254e723ee 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/telemetry-tools plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] warning: 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. --- diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index f44a7d8a0b889..5edb2515010a2 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/test plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] warning: 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. --- diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 4c9edbd805851..0f711e998f1c7 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/test-jest-helpers plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] warning: 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. --- diff --git a/api_docs/kbn_tooling_log.devdocs.json b/api_docs/kbn_tooling_log.devdocs.json new file mode 100644 index 0000000000000..6442d3feca9f3 --- /dev/null +++ b/api_docs/kbn_tooling_log.devdocs.json @@ -0,0 +1,1251 @@ +{ + "id": "@kbn/tooling-log", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog", + "type": "Class", + "tags": [], + "label": "ToolingLog", + "description": [], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "writerConfig", + "description": [], + "signature": [ + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.ToolingLogTextWriterConfig", + "text": "ToolingLogTextWriterConfig" + }, + " | undefined" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "isRequired": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.Unnamed.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.ToolingLogOptions", + "text": "ToolingLogOptions" + }, + " | undefined" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.getIndent", + "type": "Function", + "tags": [], + "label": "getIndent", + "description": [ + "\nGet the current indentation level of the ToolingLog" + ], + "signature": [ + "() => number" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.indent", + "type": "Function", + "tags": [], + "label": "indent", + "description": [], + "signature": [ + "{ (delta: number): void; (delta: number, block: () => Promise): Promise; (delta: number, block: () => T): T; }" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.indent.$1", + "type": "number", + "tags": [], + "label": "delta", + "description": [], + "signature": [ + "number" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.indent.$2", + "type": "Function", + "tags": [], + "label": "block", + "description": [], + "signature": [ + "(() => T | Promise) | undefined" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.verbose", + "type": "Function", + "tags": [], + "label": "verbose", + "description": [], + "signature": [ + "(...args: any[]) => void" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.verbose.$1", + "type": "Array", + "tags": [], + "label": "args", + "description": [], + "signature": [ + "any[]" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.debug", + "type": "Function", + "tags": [], + "label": "debug", + "description": [], + "signature": [ + "(...args: any[]) => void" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.debug.$1", + "type": "Array", + "tags": [], + "label": "args", + "description": [], + "signature": [ + "any[]" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.info", + "type": "Function", + "tags": [], + "label": "info", + "description": [], + "signature": [ + "(...args: any[]) => void" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.info.$1", + "type": "Array", + "tags": [], + "label": "args", + "description": [], + "signature": [ + "any[]" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.success", + "type": "Function", + "tags": [], + "label": "success", + "description": [], + "signature": [ + "(...args: any[]) => void" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.success.$1", + "type": "Array", + "tags": [], + "label": "args", + "description": [], + "signature": [ + "any[]" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.warning", + "type": "Function", + "tags": [], + "label": "warning", + "description": [], + "signature": [ + "(...args: any[]) => void" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.warning.$1", + "type": "Array", + "tags": [], + "label": "args", + "description": [], + "signature": [ + "any[]" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.error", + "type": "Function", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "(error: string | Error) => void" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.error.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "string | Error" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.write", + "type": "Function", + "tags": [], + "label": "write", + "description": [], + "signature": [ + "(...args: any[]) => void" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.write.$1", + "type": "Array", + "tags": [], + "label": "args", + "description": [], + "signature": [ + "any[]" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.getWriters", + "type": "Function", + "tags": [], + "label": "getWriters", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.Writer", + "text": "Writer" + }, + "[]" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.setWriters", + "type": "Function", + "tags": [], + "label": "setWriters", + "description": [], + "signature": [ + "(writers: ", + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.Writer", + "text": "Writer" + }, + "[]) => void" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.setWriters.$1", + "type": "Array", + "tags": [], + "label": "writers", + "description": [], + "signature": [ + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.Writer", + "text": "Writer" + }, + "[]" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.getWritten$", + "type": "Function", + "tags": [], + "label": "getWritten$", + "description": [], + "signature": [ + "() => ", + "Observable", + "<", + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.Message", + "text": "Message" + }, + ">" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.withType", + "type": "Function", + "tags": [], + "label": "withType", + "description": [ + "\nCreate a new ToolingLog which sets a different \"type\", allowing messages to be filtered out by \"source\"" + ], + "signature": [ + "(type: string) => ", + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.ToolingLog", + "text": "ToolingLog" + } + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLog.withType.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [ + "A string that will be passed along with messages from this logger which can be used to filter messages with `ignoreSources`" + ], + "signature": [ + "string" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogCollectingWriter", + "type": "Class", + "tags": [], + "label": "ToolingLogCollectingWriter", + "description": [], + "signature": [ + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.ToolingLogCollectingWriter", + "text": "ToolingLogCollectingWriter" + }, + " extends ", + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.ToolingLogTextWriter", + "text": "ToolingLogTextWriter" + } + ], + "path": "packages/kbn-tooling-log/src/tooling_log_collecting_writer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogCollectingWriter.messages", + "type": "Array", + "tags": [], + "label": "messages", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/kbn-tooling-log/src/tooling_log_collecting_writer.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogCollectingWriter.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-tooling-log/src/tooling_log_collecting_writer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogCollectingWriter.Unnamed.$1", + "type": "CompoundType", + "tags": [], + "label": "level", + "description": [], + "signature": [ + "\"error\" | \"success\" | \"warning\" | \"info\" | \"debug\" | \"silent\" | \"verbose\"" + ], + "path": "packages/kbn-tooling-log/src/tooling_log_collecting_writer.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogCollectingWriter.write", + "type": "Function", + "tags": [], + "label": "write", + "description": [ + "\nCalled by ToolingLog, extends messages with the source if message includes one." + ], + "signature": [ + "(msg: ", + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.Message", + "text": "Message" + }, + ") => boolean" + ], + "path": "packages/kbn-tooling-log/src/tooling_log_collecting_writer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogCollectingWriter.write.$1", + "type": "Object", + "tags": [], + "label": "msg", + "description": [], + "signature": [ + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.Message", + "text": "Message" + } + ], + "path": "packages/kbn-tooling-log/src/tooling_log_collecting_writer.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogTextWriter", + "type": "Class", + "tags": [], + "label": "ToolingLogTextWriter", + "description": [], + "signature": [ + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.ToolingLogTextWriter", + "text": "ToolingLogTextWriter" + }, + " implements ", + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.Writer", + "text": "Writer" + } + ], + "path": "packages/kbn-tooling-log/src/tooling_log_text_writer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogTextWriter.level", + "type": "Object", + "tags": [], + "label": "level", + "description": [], + "signature": [ + "{ name: \"error\" | \"success\" | \"warning\" | \"info\" | \"debug\" | \"silent\" | \"verbose\"; flags: { error: boolean; success: boolean; warning: boolean; info: boolean; debug: boolean; silent: boolean; verbose: boolean; }; }" + ], + "path": "packages/kbn-tooling-log/src/tooling_log_text_writer.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogTextWriter.writeTo", + "type": "Object", + "tags": [], + "label": "writeTo", + "description": [], + "signature": [ + "{ write(msg: string): void; }" + ], + "path": "packages/kbn-tooling-log/src/tooling_log_text_writer.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogTextWriter.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-tooling-log/src/tooling_log_text_writer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogTextWriter.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "config", + "description": [], + "signature": [ + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.ToolingLogTextWriterConfig", + "text": "ToolingLogTextWriterConfig" + } + ], + "path": "packages/kbn-tooling-log/src/tooling_log_text_writer.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogTextWriter.write", + "type": "Function", + "tags": [], + "label": "write", + "description": [], + "signature": [ + "(msg: ", + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.Message", + "text": "Message" + }, + ") => boolean" + ], + "path": "packages/kbn-tooling-log/src/tooling_log_text_writer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogTextWriter.write.$1", + "type": "Object", + "tags": [], + "label": "msg", + "description": [], + "signature": [ + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.Message", + "text": "Message" + } + ], + "path": "packages/kbn-tooling-log/src/tooling_log_text_writer.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogTextWriter.write", + "type": "Function", + "tags": [], + "label": "write", + "description": [], + "signature": [ + "(writeTo: { write(msg: string): void; }, prefix: string, msg: ", + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.Message", + "text": "Message" + }, + ") => void" + ], + "path": "packages/kbn-tooling-log/src/tooling_log_text_writer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogTextWriter.write.$1", + "type": "Object", + "tags": [], + "label": "writeTo", + "description": [], + "signature": [ + "{ write(msg: string): void; }" + ], + "path": "packages/kbn-tooling-log/src/tooling_log_text_writer.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogTextWriter.write.$2", + "type": "string", + "tags": [], + "label": "prefix", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-tooling-log/src/tooling_log_text_writer.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogTextWriter.write.$3", + "type": "Object", + "tags": [], + "label": "msg", + "description": [], + "signature": [ + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.Message", + "text": "Message" + } + ], + "path": "packages/kbn-tooling-log/src/tooling_log_text_writer.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.getLogLevelFlagsHelp", + "type": "Function", + "tags": [], + "label": "getLogLevelFlagsHelp", + "description": [], + "signature": [ + "(defaultLogLevel: string) => string" + ], + "path": "packages/kbn-tooling-log/src/log_levels.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.getLogLevelFlagsHelp.$1", + "type": "string", + "tags": [], + "label": "defaultLogLevel", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-tooling-log/src/log_levels.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.parseLogLevel", + "type": "Function", + "tags": [], + "label": "parseLogLevel", + "description": [], + "signature": [ + "(name: \"error\" | \"success\" | \"warning\" | \"info\" | \"debug\" | \"silent\" | \"verbose\") => { name: \"error\" | \"success\" | \"warning\" | \"info\" | \"debug\" | \"silent\" | \"verbose\"; flags: { error: boolean; success: boolean; warning: boolean; info: boolean; debug: boolean; silent: boolean; verbose: boolean; }; }" + ], + "path": "packages/kbn-tooling-log/src/log_levels.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.parseLogLevel.$1", + "type": "CompoundType", + "tags": [], + "label": "name", + "description": [], + "signature": [ + "\"error\" | \"success\" | \"warning\" | \"info\" | \"debug\" | \"silent\" | \"verbose\"" + ], + "path": "packages/kbn-tooling-log/src/log_levels.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.pickLevelFromFlags", + "type": "Function", + "tags": [], + "label": "pickLevelFromFlags", + "description": [], + "signature": [ + "(flags: Record, options: { default?: \"error\" | \"success\" | \"warning\" | \"info\" | \"debug\" | \"silent\" | \"verbose\" | undefined; }) => \"error\" | \"success\" | \"warning\" | \"info\" | \"debug\" | \"silent\" | \"verbose\"" + ], + "path": "packages/kbn-tooling-log/src/log_levels.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.pickLevelFromFlags.$1", + "type": "Object", + "tags": [], + "label": "flags", + "description": [], + "signature": [ + "Record" + ], + "path": "packages/kbn-tooling-log/src/log_levels.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.pickLevelFromFlags.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "path": "packages/kbn-tooling-log/src/log_levels.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.pickLevelFromFlags.$2.default", + "type": "CompoundType", + "tags": [], + "label": "default", + "description": [], + "signature": [ + "\"error\" | \"success\" | \"warning\" | \"info\" | \"debug\" | \"silent\" | \"verbose\" | undefined" + ], + "path": "packages/kbn-tooling-log/src/log_levels.ts", + "deprecated": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.Message", + "type": "Interface", + "tags": [], + "label": "Message", + "description": [ + "\nThe object shape passed to ToolingLog writers each time the log is used." + ], + "path": "packages/kbn-tooling-log/src/message.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.Message.type", + "type": "CompoundType", + "tags": [], + "label": "type", + "description": [ + "level/type of message" + ], + "signature": [ + "\"error\" | \"success\" | \"warning\" | \"write\" | \"info\" | \"debug\" | \"verbose\"" + ], + "path": "packages/kbn-tooling-log/src/message.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.Message.indent", + "type": "number", + "tags": [], + "label": "indent", + "description": [ + "indentation intended when message written to a text log" + ], + "path": "packages/kbn-tooling-log/src/message.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.Message.source", + "type": "string", + "tags": [], + "label": "source", + "description": [ + "type of logger this message came from" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-tooling-log/src/message.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.Message.args", + "type": "Array", + "tags": [], + "label": "args", + "description": [ + "args passed to the logging method" + ], + "signature": [ + "any[]" + ], + "path": "packages/kbn-tooling-log/src/message.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogOptions", + "type": "Interface", + "tags": [], + "label": "ToolingLogOptions", + "description": [], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogOptions.type", + "type": "string", + "tags": [], + "label": "type", + "description": [ + "\ntype name for this logger, will be assigned to the \"source\"\nproperties of messages produced by this logger" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogOptions.parent", + "type": "Object", + "tags": [], + "label": "parent", + "description": [ + "\nparent ToolingLog. When a ToolingLog has a parent they will both\nshare indent and writers state. Changing the indent width or\nwriters on either log will update the other too." + ], + "signature": [ + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.ToolingLog", + "text": "ToolingLog" + }, + " | undefined" + ], + "path": "packages/kbn-tooling-log/src/tooling_log.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogTextWriterConfig", + "type": "Interface", + "tags": [], + "label": "ToolingLogTextWriterConfig", + "description": [], + "path": "packages/kbn-tooling-log/src/tooling_log_text_writer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogTextWriterConfig.level", + "type": "CompoundType", + "tags": [], + "label": "level", + "description": [ + "\nLog level, messages below this level will be ignored" + ], + "signature": [ + "\"error\" | \"success\" | \"warning\" | \"info\" | \"debug\" | \"silent\" | \"verbose\"" + ], + "path": "packages/kbn-tooling-log/src/tooling_log_text_writer.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogTextWriterConfig.ignoreSources", + "type": "Array", + "tags": [], + "label": "ignoreSources", + "description": [ + "\nList of message sources/ToolingLog types which will be ignored. Create\na logger with `ToolingLog#withType()` to create messages with a specific\nsource. Ignored messages will be dropped without writing." + ], + "signature": [ + "string[] | undefined" + ], + "path": "packages/kbn-tooling-log/src/tooling_log_text_writer.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ToolingLogTextWriterConfig.writeTo", + "type": "Object", + "tags": [], + "label": "writeTo", + "description": [ + "\nTarget which will receive formatted message lines, a common value for `writeTo`\nis process.stdout" + ], + "signature": [ + "{ write(s: string): void; }" + ], + "path": "packages/kbn-tooling-log/src/tooling_log_text_writer.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.Writer", + "type": "Interface", + "tags": [], + "label": "Writer", + "description": [ + "\nAn object which received ToolingLog `Messages` and sends them to\nsome interface for collecting logs like stdio, or a file" + ], + "path": "packages/kbn-tooling-log/src/writer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.Writer.write", + "type": "Function", + "tags": [], + "label": "write", + "description": [ + "\nCalled with every log message, should return true if the message\nwas written and false if it was ignored." + ], + "signature": [ + "(msg: ", + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.Message", + "text": "Message" + }, + ") => boolean" + ], + "path": "packages/kbn-tooling-log/src/writer.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.Writer.write.$1", + "type": "Object", + "tags": [], + "label": "msg", + "description": [ + "The log message to write" + ], + "signature": [ + { + "pluginId": "@kbn/tooling-log", + "scope": "server", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-server.Message", + "text": "Message" + } + ], + "path": "packages/kbn-tooling-log/src/writer.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.DEFAULT_LOG_LEVEL", + "type": "string", + "tags": [], + "label": "DEFAULT_LOG_LEVEL", + "description": [], + "signature": [ + "\"info\"" + ], + "path": "packages/kbn-tooling-log/src/log_levels.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.LOG_LEVEL_FLAGS", + "type": "Array", + "tags": [], + "label": "LOG_LEVEL_FLAGS", + "description": [], + "signature": [ + "{ name: string; help: string; }[]" + ], + "path": "packages/kbn-tooling-log/src/log_levels.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.LogLevel", + "type": "Type", + "tags": [], + "label": "LogLevel", + "description": [], + "signature": [ + "\"error\" | \"success\" | \"warning\" | \"info\" | \"debug\" | \"silent\" | \"verbose\"" + ], + "path": "packages/kbn-tooling-log/src/log_levels.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/tooling-log", + "id": "def-server.ParsedLogLevel", + "type": "Type", + "tags": [], + "label": "ParsedLogLevel", + "description": [], + "signature": [ + "{ name: \"error\" | \"success\" | \"warning\" | \"info\" | \"debug\" | \"silent\" | \"verbose\"; flags: { error: boolean; success: boolean; warning: boolean; info: boolean; debug: boolean; silent: boolean; verbose: boolean; }; }" + ], + "path": "packages/kbn-tooling-log/src/log_levels.ts", + "deprecated": false, + "initialIsOpen": false + } + ], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx new file mode 100644 index 0000000000000..7dcb0b67415cb --- /dev/null +++ b/api_docs/kbn_tooling_log.mdx @@ -0,0 +1,36 @@ +--- +id: kibKbnToolingLogPluginApi +slug: /kibana-dev-docs/api/kbn-tooling-log +title: "@kbn/tooling-log" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/tooling-log plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] +warning: 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. +--- +import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 72 | 0 | 55 | 0 | + +## Server + +### Functions + + +### Classes + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_type_summarizer.mdx b/api_docs/kbn_type_summarizer.mdx index 385d3c755dfe2..c715205758b5e 100644 --- a/api_docs/kbn_type_summarizer.mdx +++ b/api_docs/kbn_type_summarizer.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer title: "@kbn/type-summarizer" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/type-summarizer plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer'] warning: 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. --- diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index 02f14ac7a325b..66d08cdf52378 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/typed-react-router-config plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] warning: 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. --- diff --git a/api_docs/kbn_ui_theme.devdocs.json b/api_docs/kbn_ui_theme.devdocs.json index c4272d206e8bd..bcfd5d506638a 100644 --- a/api_docs/kbn_ui_theme.devdocs.json +++ b/api_docs/kbn_ui_theme.devdocs.json @@ -52,7 +52,7 @@ "label": "Theme", "description": [], "signature": [ - "{ paddingSizes: { xs: string; s: string; m: string; l: string; xl: string; }; avatarSizing: { s: { size: string; 'font-size': string; }; m: { size: string; 'font-size': string; }; l: { size: string; 'font-size': string; }; xl: { size: string; 'font-size': string; }; }; euiBadgeGroupGutterTypes: { gutterExtraSmall: string; gutterSmall: string; }; euiBreadcrumbSpacing: string; euiBreadcrumbTruncateWidth: string; euiButtonEmptyTypes: { primary: string; danger: string; disabled: string; ghost: string; text: string; success: string; warning: string; }; euiCallOutTypes: { primary: string; success: string; warning: string; danger: string; }; euiCardSpacing: string; euiCardBottomNodeHeight: string; euiCardSelectButtonBorders: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardSelectButtonBackgrounds: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCheckableCardPadding: string; euiCodeBlockPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCollapsibleNavGroupLightBackgroundColor: string; euiCollapsibleNavGroupDarkBackgroundColor: string; euiCollapsibleNavGroupDarkHighContrastColor: string; euiColorPickerValueRange0: string; euiColorPickerValueRange1: string; euiColorPickerSaturationRange0: string; euiColorPickerSaturationRange1: string; euiColorPickerIndicatorSize: string; euiColorPickerWidth: string; euiColorPaletteDisplaySizes: { sizeExtraSmall: string; sizeSmall: string; sizeMedium: string; }; euiContextMenuWidth: string; euiControlBarBackground: string; euiControlBarText: string; euiControlBarBorderColor: string; euiControlBarInitialHeight: string; euiControlBarMaxHeight: string; euiControlBarHeights: { s: string; m: string; l: string; }; euiDataGridPrefix: string; euiDataGridStyles: string; euiZDataGrid: number; euiZHeaderBelowDataGrid: number; euiZDataGridCellPopover: number; euiDataGridColumnResizerWidth: string; euiDataGridPopoverMaxHeight: string; euiDataGridCellPaddingS: string; euiDataGridCellPaddingM: string; euiDataGridCellPaddingL: string; euiDataGridVerticalBorder: string; euiSuperDatePickerWidth: string; euiSuperDatePickerButtonWidth: string; euiDragAndDropSpacing: { s: string; m: string; l: string; }; euiExpressionColors: { subdued: string; primary: string; success: string; warning: string; danger: string; accent: string; }; euiFacetGutterSizes: { gutterNone: number; gutterSmall: string; gutterMedium: string; gutterLarge: string; }; gutterTypes: { gutterExtraSmall: string; gutterSmall: string; gutterMedium: string; gutterLarge: string; gutterExtraLarge: string; }; fractions: { fourths: { percentage: string; count: number; }; thirds: { percentage: string; count: number; }; halves: { percentage: string; count: number; }; single: { percentage: string; count: number; }; }; flyoutSizes: { small: { min: string; width: string; max: string; }; medium: { min: string; width: string; max: string; }; large: { min: string; width: string; max: string; }; }; euiFlyoutBorder: string; euiFlyoutPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiFilePickerTallHeight: string; euiRangeLevelColors: { primary: string; success: string; warning: string; danger: string; }; textareaResizing: { vertical: string; horizontal: string; both: string; none: string; }; euiHeaderLinksGutterSizes: { gutterXS: string; gutterS: string; gutterM: string; gutterL: string; }; ruleMargins: { marginXSmall: string; marginSmall: string; marginMedium: string; marginLarge: string; marginXLarge: string; marginXXLarge: string; }; euiIconLoadingOpacity: number; euiIconColors: { accent: string; danger: string; ghost: string; primary: string; success: string; subdued: string; text: string; warning: string; inherit: string; }; euiIconSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiKeyPadMenuSize: string; euiKeyPadMenuMarginSize: string; euiLinkColors: { subdued: string; primary: string; success: string; accent: string; warning: string; danger: string; text: string; ghost: string; }; euiListGroupItemHoverBackground: string; euiListGroupItemHoverBackgroundGhost: string; euiListGroupGutterTypes: { gutterSmall: string; gutterMedium: string; }; euiListGroupItemColorTypes: { primary: string; text: string; subdued: string; ghost: string; }; euiListGroupItemSizeTypes: { xSmall: string; small: string; medium: string; large: string; }; euiGradientStartStop: string; euiGradientMiddle: string; euiLoadingSpinnerSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiMarkdownEditorMinHeight: string; euiPopoverArrowSize: string; euiPopoverTranslateDistance: string; euiProgressSizes: { xs: string; s: string; m: string; l: string; }; euiProgressColors: { primary: string; success: string; warning: string; danger: string; accent: string; subdued: string; vis0: string; vis1: string; vis2: string; vis3: string; vis4: string; vis5: string; vis6: string; vis7: string; vis8: string; vis9: string; customColor: string; }; euiResizableButtonTransitionSpeed: string; euiResizableButtonSize: string; euiSelectableListItemBorder: string; euiSelectableListItemPadding: string; euiSelectableTemplateSitewideTypes: { application: { color: string; 'font-weight': number; }; deployment: { color: string; 'font-weight': number; }; article: { color: string; 'font-weight': number; }; case: { color: string; 'font-weight': number; }; platform: { color: string; 'font-weight': number; }; }; euiSideNavEmphasizedBackgroundColor: string; euiSideNavRootTextcolor: string; euiSideNavBranchTextcolor: string; euiSideNavSelectedTextcolor: string; euiSideNavDisabledTextcolor: string; spacerSizes: { xs: string; s: string; m: string; l: string; xl: string; xxl: string; }; euiStepNumberSize: string; euiStepNumberSmallSize: string; euiStepNumberMargin: string; euiStepStatusColorsToFade: { warning: string; danger: string; disabled: string; incomplete: string; }; euiSuggestItemColors: { tint0: string; tint1: string; tint2: string; tint3: string; tint4: string; tint5: string; tint6: string; tint7: string; tint8: string; tint9: string; tint10: string; }; euiTableCellContentPadding: string; euiTableCellContentPaddingCompressed: string; euiTableCellCheckboxWidth: string; euiTableActionsAreaWidth: string; euiTableHoverColor: string; euiTableSelectedColor: string; euiTableHoverSelectedColor: string; euiTableActionsBorderColor: string; euiTableHoverClickableColor: string; euiTableFocusClickableColor: string; euiTextColors: { default: string; subdued: string; success: string; accent: string; warning: string; danger: string; ghost: string; inherit: string; }; euiTextConstrainedMaxWidth: string; euiToastWidth: string; euiToastTypes: { primary: string; success: string; warning: string; danger: string; }; euiTokenGrayColor: string; euiTokenTypes: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; gray: { graphic: string; behindText: string; }; }; euiTokenTypeKeys: string; euiContrastRatioText: number; euiContrastRatioGraphic: number; euiContrastRatioDisabled: number; euiAnimSlightBounce: string; euiAnimSlightResistance: string; euiAnimSpeedExtraFast: string; euiAnimSpeedFast: string; euiAnimSpeedNormal: string; euiAnimSpeedSlow: string; euiAnimSpeedExtraSlow: string; euiBorderWidthThin: string; euiBorderWidthThick: string; euiBorderColor: string; euiBorderRadius: string; euiBorderRadiusSmall: string; euiBorderThick: string; euiBorderThin: string; euiBorderEditable: string; euiButtonHeight: string; euiButtonHeightSmall: string; euiButtonHeightXSmall: string; euiButtonColorDisabled: string; euiButtonColorDisabledText: string; euiButtonColorGhostDisabled: string; euiButtonTypes: { primary: string; accent: string; success: string; warning: string; danger: string; subdued: string; ghost: string; text: string; }; euiCodeBlockBackgroundColor: string; euiCodeBlockColor: string; euiCodeBlockSelectedBackgroundColor: string; euiCodeBlockCommentColor: string; euiCodeBlockSelectorTagColor: string; euiCodeBlockStringColor: string; euiCodeBlockTagColor: string; euiCodeBlockNameColor: string; euiCodeBlockNumberColor: string; euiCodeBlockKeywordColor: string; euiCodeBlockFunctionTitleColor: string; euiCodeBlockTypeColor: string; euiCodeBlockAttributeColor: string; euiCodeBlockSymbolColor: string; euiCodeBlockParamsColor: string; euiCodeBlockMetaColor: string; euiCodeBlockTitleColor: string; euiCodeBlockSectionColor: string; euiCodeBlockAdditionColor: string; euiCodeBlockDeletionColor: string; euiCodeBlockSelectorClassColor: string; euiCodeBlockSelectorIdColor: string; euiPaletteColorBlind: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; }; euiPaletteColorBlindKeys: string; euiColorVis0: string; euiColorVis1: string; euiColorVis2: string; euiColorVis3: string; euiColorVis4: string; euiColorVis5: string; euiColorVis6: string; euiColorVis7: string; euiColorVis8: string; euiColorVis9: string; euiColorVis0_behindText: string; euiColorVis1_behindText: string; euiColorVis2_behindText: string; euiColorVis3_behindText: string; euiColorVis4_behindText: string; euiColorVis5_behindText: string; euiColorVis6_behindText: string; euiColorVis7_behindText: string; euiColorVis8_behindText: string; euiColorVis9_behindText: string; euiFontWeightLight: number; euiFontWeightRegular: number; euiFontWeightMedium: number; euiFontWeightSemiBold: number; euiFontWeightBold: number; euiCodeFontWeightRegular: number; euiCodeFontWeightBold: number; euiFormMaxWidth: string; euiFormControlHeight: string; euiFormControlCompressedHeight: string; euiFormControlPadding: string; euiFormControlCompressedPadding: string; euiFormControlBorderRadius: string; euiFormControlCompressedBorderRadius: string; euiRadioSize: string; euiCheckBoxSize: string; euiCheckboxBorderRadius: string; euiSwitchHeight: string; euiSwitchWidth: string; euiSwitchThumbSize: string; euiSwitchIconHeight: string; euiSwitchHeightCompressed: string; euiSwitchWidthCompressed: string; euiSwitchThumbSizeCompressed: string; euiSwitchHeightMini: string; euiSwitchWidthMini: string; euiSwitchThumbSizeMini: string; euiFormBackgroundColor: string; euiFormBackgroundDisabledColor: string; euiFormBackgroundReadOnlyColor: string; euiFormBorderOpaqueColor: string; euiFormBorderColor: string; euiFormBorderDisabledColor: string; euiFormCustomControlDisabledIconColor: string; euiFormCustomControlBorderColor: string; euiFormControlDisabledColor: string; euiFormControlBoxShadow: string; euiFormControlPlaceholderText: string; euiFormInputGroupLabelBackground: string; euiFormInputGroupBorder: string; euiSwitchOffColor: string; euiFormControlLayoutGroupInputHeight: string; euiFormControlLayoutGroupInputCompressedHeight: string; euiFormControlLayoutGroupInputCompressedBorderRadius: string; euiRangeTrackColor: string; euiRangeThumbRadius: string; euiRangeThumbHeight: string; euiRangeThumbWidth: string; euiRangeThumbBorderColor: string; euiRangeTrackWidth: string; euiRangeTrackHeight: string; euiRangeTrackBorderWidth: number; euiRangeTrackBorderColor: string; euiRangeTrackRadius: string; euiRangeDisabledOpacity: number; euiRangeHighlightHeight: string; euiHeaderBackgroundColor: string; euiHeaderDarkBackgroundColor: string; euiHeaderBorderColor: string; euiHeaderBreadcrumbColor: string; euiHeaderHeight: string; euiHeaderChildSize: string; euiHeaderHeightCompensation: string; euiPageDefaultMaxWidth: string; euiPageSidebarMinWidth: string; euiPanelPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiPanelBorderRadiusModifiers: { borderRadiusNone: number; borderRadiusMedium: string; }; euiPanelBackgroundColorModifiers: { transparent: string; plain: string; subdued: string; accent: string; primary: string; success: string; warning: string; danger: string; }; euiBreakpoints: { xs: number; s: string; m: string; l: string; xl: string; }; euiBreakpointKeys: string; euiShadowColor: string; euiShadowColorLarge: string; euiSize: string; euiSizeXS: string; euiSizeS: string; euiSizeM: string; euiSizeL: string; euiSizeXL: string; euiSizeXXL: string; euiButtonMinWidth: string; euiScrollBar: string; euiScrollBarCorner: string; euiScrollBarCornerThin: string; euiFocusRingColor: string; euiFocusRingAnimStartColor: string; euiFocusRingAnimStartSize: string; euiFocusRingAnimStartSizeLarge: string; euiFocusRingSizeLarge: string; euiFocusRingSize: string; euiFocusTransparency: number; euiFocusTransparencyPercent: string; euiFocusBackgroundColor: string; euiTooltipBackgroundColor: string; euiTooltipBorderColor: string; euiTooltipAnimations: { top: string; left: string; bottom: string; right: string; }; euiFontFamily: string; euiCodeFontFamily: string; euiFontFeatureSettings: string; euiTextScale: string; euiFontSize: string; euiFontSizeXS: string; euiFontSizeS: string; euiFontSizeM: string; euiFontSizeL: string; euiFontSizeXL: string; euiFontSizeXXL: string; euiLineHeight: number; euiBodyLineHeight: number; euiTitles: { xxxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; s: { 'font-size': string; 'line-height': string; 'font-weight': number; }; m: { 'font-size': string; 'line-height': string; 'font-weight': number; }; l: { 'font-size': string; 'line-height': string; 'font-weight': number; }; }; euiZLevel0: number; euiZLevel1: number; euiZLevel2: number; euiZLevel3: number; euiZLevel4: number; euiZLevel5: number; euiZLevel6: number; euiZLevel7: number; euiZLevel8: number; euiZLevel9: number; euiZToastList: number; euiZModal: number; euiZMask: number; euiZNavigation: number; euiZContentMenu: number; euiZHeader: number; euiZFlyout: number; euiZMaskBelowHeader: number; euiZContent: number; euiColorGhost: string; euiColorInk: string; euiColorPrimary: string; euiColorAccent: string; euiColorSuccess: string; euiColorWarning: string; euiColorDanger: string; euiColorEmptyShade: string; euiColorLightestShade: string; euiColorLightShade: string; euiColorMediumShade: string; euiColorDarkShade: string; euiColorDarkestShade: string; euiColorFullShade: string; euiPageBackgroundColor: string; euiColorHighlight: string; euiTextColor: string; euiTitleColor: string; euiTextSubduedColor: string; euiColorDisabled: string; euiColorPrimaryText: string; euiColorSuccessText: string; euiColorAccentText: string; euiColorWarningText: string; euiColorDangerText: string; euiColorDisabledText: string; euiLinkColor: string; euiColorChartLines: string; euiColorChartBand: string; euiDatePickerCalendarWidth: string; euiDatePickerPadding: string; euiDatePickerGap: string; euiDatePickerCalendarColumns: number; euiDatePickerButtonSize: string; euiDatePickerMinControlWidth: string; euiDatePickerMaxControlWidth: string; euiButtonDefaultTransparency: number; euiButtonFontWeight: number; euiRangeHighlightColor: string; euiRangeThumbBackgroundColor: string; euiRangeTrackCompressedHeight: string; euiRangeHighlightCompressedHeight: string; euiRangeHeight: string; euiRangeCompressedHeight: string; euiStepStatusColors: { default: string; complete: string; warning: string; danger: string; }; }" + "{ paddingSizes: { xs: string; s: string; m: string; l: string; xl: string; }; euiBadgeGroupGutterTypes: { gutterExtraSmall: string; gutterSmall: string; }; euiBreadcrumbSpacing: string; euiBreadcrumbTruncateWidth: string; euiButtonEmptyTypes: { primary: string; danger: string; disabled: string; ghost: string; text: string; success: string; warning: string; }; euiCallOutTypes: { primary: string; success: string; warning: string; danger: string; }; euiCardSpacing: string; euiCardBottomNodeHeight: string; euiCardSelectButtonBorders: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardSelectButtonBackgrounds: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCheckableCardPadding: string; euiCodeBlockPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCollapsibleNavGroupLightBackgroundColor: string; euiCollapsibleNavGroupDarkBackgroundColor: string; euiCollapsibleNavGroupDarkHighContrastColor: string; euiColorPickerValueRange0: string; euiColorPickerValueRange1: string; euiColorPickerSaturationRange0: string; euiColorPickerSaturationRange1: string; euiColorPickerIndicatorSize: string; euiColorPickerWidth: string; euiColorPaletteDisplaySizes: { sizeExtraSmall: string; sizeSmall: string; sizeMedium: string; }; euiContextMenuWidth: string; euiControlBarBackground: string; euiControlBarText: string; euiControlBarBorderColor: string; euiControlBarInitialHeight: string; euiControlBarMaxHeight: string; euiControlBarHeights: { s: string; m: string; l: string; }; euiDataGridPrefix: string; euiDataGridStyles: string; euiZDataGrid: number; euiZHeaderBelowDataGrid: number; euiZDataGridCellPopover: number; euiDataGridColumnResizerWidth: string; euiDataGridPopoverMaxHeight: string; euiDataGridCellPaddingS: string; euiDataGridCellPaddingM: string; euiDataGridCellPaddingL: string; euiDataGridVerticalBorder: string; euiSuperDatePickerWidth: string; euiSuperDatePickerButtonWidth: string; euiDragAndDropSpacing: { s: string; m: string; l: string; }; euiExpressionColors: { subdued: string; primary: string; success: string; warning: string; danger: string; accent: string; }; euiFacetGutterSizes: { gutterNone: number; gutterSmall: string; gutterMedium: string; gutterLarge: string; }; gutterTypes: { gutterExtraSmall: string; gutterSmall: string; gutterMedium: string; gutterLarge: string; gutterExtraLarge: string; }; fractions: { fourths: { percentage: string; count: number; }; thirds: { percentage: string; count: number; }; halves: { percentage: string; count: number; }; single: { percentage: string; count: number; }; }; flyoutSizes: { small: { min: string; width: string; max: string; }; medium: { min: string; width: string; max: string; }; large: { min: string; width: string; max: string; }; }; euiFlyoutBorder: string; euiFlyoutPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiFilePickerTallHeight: string; euiRangeLevelColors: { primary: string; success: string; warning: string; danger: string; }; textareaResizing: { vertical: string; horizontal: string; both: string; none: string; }; euiHeaderLinksGutterSizes: { gutterXS: string; gutterS: string; gutterM: string; gutterL: string; }; ruleMargins: { marginXSmall: string; marginSmall: string; marginMedium: string; marginLarge: string; marginXLarge: string; marginXXLarge: string; }; euiIconLoadingOpacity: number; euiIconColors: { accent: string; danger: string; ghost: string; primary: string; success: string; subdued: string; text: string; warning: string; inherit: string; }; euiIconSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiKeyPadMenuSize: string; euiKeyPadMenuMarginSize: string; euiLinkColors: { subdued: string; primary: string; success: string; accent: string; warning: string; danger: string; text: string; ghost: string; }; euiListGroupItemHoverBackground: string; euiListGroupItemHoverBackgroundGhost: string; euiListGroupGutterTypes: { gutterSmall: string; gutterMedium: string; }; euiListGroupItemColorTypes: { primary: string; text: string; subdued: string; ghost: string; }; euiListGroupItemSizeTypes: { xSmall: string; small: string; medium: string; large: string; }; euiGradientStartStop: string; euiGradientMiddle: string; euiLoadingSpinnerSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiMarkdownEditorMinHeight: string; euiPopoverArrowSize: string; euiPopoverTranslateDistance: string; euiProgressSizes: { xs: string; s: string; m: string; l: string; }; euiProgressColors: { primary: string; success: string; warning: string; danger: string; accent: string; subdued: string; vis0: string; vis1: string; vis2: string; vis3: string; vis4: string; vis5: string; vis6: string; vis7: string; vis8: string; vis9: string; customColor: string; }; euiResizableButtonTransitionSpeed: string; euiResizableButtonSize: string; euiSelectableListItemBorder: string; euiSelectableListItemPadding: string; euiSelectableTemplateSitewideTypes: { application: { color: string; 'font-weight': number; }; deployment: { color: string; 'font-weight': number; }; article: { color: string; 'font-weight': number; }; case: { color: string; 'font-weight': number; }; platform: { color: string; 'font-weight': number; }; }; euiSideNavEmphasizedBackgroundColor: string; euiSideNavRootTextcolor: string; euiSideNavBranchTextcolor: string; euiSideNavSelectedTextcolor: string; euiSideNavDisabledTextcolor: string; spacerSizes: { xs: string; s: string; m: string; l: string; xl: string; xxl: string; }; euiStepNumberSize: string; euiStepNumberSmallSize: string; euiStepNumberMargin: string; euiStepStatusColorsToFade: { warning: string; danger: string; disabled: string; incomplete: string; }; euiSuggestItemColors: { tint0: string; tint1: string; tint2: string; tint3: string; tint4: string; tint5: string; tint6: string; tint7: string; tint8: string; tint9: string; tint10: string; }; euiTableCellContentPadding: string; euiTableCellContentPaddingCompressed: string; euiTableCellCheckboxWidth: string; euiTableActionsAreaWidth: string; euiTableHoverColor: string; euiTableSelectedColor: string; euiTableHoverSelectedColor: string; euiTableActionsBorderColor: string; euiTableHoverClickableColor: string; euiTableFocusClickableColor: string; euiTextColors: { default: string; subdued: string; success: string; accent: string; warning: string; danger: string; ghost: string; inherit: string; }; euiTextConstrainedMaxWidth: string; euiToastWidth: string; euiToastTypes: { primary: string; success: string; warning: string; danger: string; }; euiTokenGrayColor: string; euiTokenTypes: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; gray: { graphic: string; behindText: string; }; }; euiTokenTypeKeys: string; euiContrastRatioText: number; euiContrastRatioGraphic: number; euiContrastRatioDisabled: number; euiAnimSlightBounce: string; euiAnimSlightResistance: string; euiAnimSpeedExtraFast: string; euiAnimSpeedFast: string; euiAnimSpeedNormal: string; euiAnimSpeedSlow: string; euiAnimSpeedExtraSlow: string; euiBorderWidthThin: string; euiBorderWidthThick: string; euiBorderColor: string; euiBorderRadius: string; euiBorderRadiusSmall: string; euiBorderThick: string; euiBorderThin: string; euiBorderEditable: string; euiButtonHeight: string; euiButtonHeightSmall: string; euiButtonHeightXSmall: string; euiButtonColorDisabled: string; euiButtonColorDisabledText: string; euiButtonColorGhostDisabled: string; euiButtonTypes: { primary: string; accent: string; success: string; warning: string; danger: string; subdued: string; ghost: string; text: string; }; euiCodeBlockBackgroundColor: string; euiCodeBlockColor: string; euiCodeBlockSelectedBackgroundColor: string; euiCodeBlockCommentColor: string; euiCodeBlockSelectorTagColor: string; euiCodeBlockStringColor: string; euiCodeBlockTagColor: string; euiCodeBlockNameColor: string; euiCodeBlockNumberColor: string; euiCodeBlockKeywordColor: string; euiCodeBlockFunctionTitleColor: string; euiCodeBlockTypeColor: string; euiCodeBlockAttributeColor: string; euiCodeBlockSymbolColor: string; euiCodeBlockParamsColor: string; euiCodeBlockMetaColor: string; euiCodeBlockTitleColor: string; euiCodeBlockSectionColor: string; euiCodeBlockAdditionColor: string; euiCodeBlockDeletionColor: string; euiCodeBlockSelectorClassColor: string; euiCodeBlockSelectorIdColor: string; euiPaletteColorBlind: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; }; euiPaletteColorBlindKeys: string; euiColorVis0: string; euiColorVis1: string; euiColorVis2: string; euiColorVis3: string; euiColorVis4: string; euiColorVis5: string; euiColorVis6: string; euiColorVis7: string; euiColorVis8: string; euiColorVis9: string; euiColorVis0_behindText: string; euiColorVis1_behindText: string; euiColorVis2_behindText: string; euiColorVis3_behindText: string; euiColorVis4_behindText: string; euiColorVis5_behindText: string; euiColorVis6_behindText: string; euiColorVis7_behindText: string; euiColorVis8_behindText: string; euiColorVis9_behindText: string; euiFontWeightLight: number; euiFontWeightRegular: number; euiFontWeightMedium: number; euiFontWeightSemiBold: number; euiFontWeightBold: number; euiCodeFontWeightRegular: number; euiCodeFontWeightBold: number; euiFormMaxWidth: string; euiFormControlHeight: string; euiFormControlCompressedHeight: string; euiFormControlPadding: string; euiFormControlCompressedPadding: string; euiFormControlBorderRadius: string; euiFormControlCompressedBorderRadius: string; euiRadioSize: string; euiCheckBoxSize: string; euiCheckboxBorderRadius: string; euiSwitchHeight: string; euiSwitchWidth: string; euiSwitchThumbSize: string; euiSwitchIconHeight: string; euiSwitchHeightCompressed: string; euiSwitchWidthCompressed: string; euiSwitchThumbSizeCompressed: string; euiSwitchHeightMini: string; euiSwitchWidthMini: string; euiSwitchThumbSizeMini: string; euiFormBackgroundColor: string; euiFormBackgroundDisabledColor: string; euiFormBackgroundReadOnlyColor: string; euiFormBorderOpaqueColor: string; euiFormBorderColor: string; euiFormBorderDisabledColor: string; euiFormCustomControlDisabledIconColor: string; euiFormCustomControlBorderColor: string; euiFormControlDisabledColor: string; euiFormControlBoxShadow: string; euiFormControlPlaceholderText: string; euiFormInputGroupLabelBackground: string; euiFormInputGroupBorder: string; euiSwitchOffColor: string; euiFormControlLayoutGroupInputHeight: string; euiFormControlLayoutGroupInputCompressedHeight: string; euiFormControlLayoutGroupInputCompressedBorderRadius: string; euiRangeTrackColor: string; euiRangeThumbRadius: string; euiRangeThumbHeight: string; euiRangeThumbWidth: string; euiRangeThumbBorderColor: string; euiRangeTrackWidth: string; euiRangeTrackHeight: string; euiRangeTrackBorderWidth: number; euiRangeTrackBorderColor: string; euiRangeTrackRadius: string; euiRangeDisabledOpacity: number; euiRangeHighlightHeight: string; euiHeaderBackgroundColor: string; euiHeaderDarkBackgroundColor: string; euiHeaderBorderColor: string; euiHeaderBreadcrumbColor: string; euiHeaderHeight: string; euiHeaderChildSize: string; euiHeaderHeightCompensation: string; euiPageDefaultMaxWidth: string; euiPageSidebarMinWidth: string; euiPanelPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiPanelBorderRadiusModifiers: { borderRadiusNone: number; borderRadiusMedium: string; }; euiPanelBackgroundColorModifiers: { transparent: string; plain: string; subdued: string; accent: string; primary: string; success: string; warning: string; danger: string; }; euiBreakpoints: { xs: number; s: string; m: string; l: string; xl: string; }; euiBreakpointKeys: string; euiShadowColor: string; euiShadowColorLarge: string; euiSize: string; euiSizeXS: string; euiSizeS: string; euiSizeM: string; euiSizeL: string; euiSizeXL: string; euiSizeXXL: string; euiButtonMinWidth: string; euiScrollBar: string; euiScrollBarCorner: string; euiScrollBarCornerThin: string; euiFocusRingColor: string; euiFocusRingAnimStartColor: string; euiFocusRingAnimStartSize: string; euiFocusRingAnimStartSizeLarge: string; euiFocusRingSizeLarge: string; euiFocusRingSize: string; euiFocusTransparency: number; euiFocusTransparencyPercent: string; euiFocusBackgroundColor: string; euiTooltipBackgroundColor: string; euiTooltipBorderColor: string; euiTooltipAnimations: { top: string; left: string; bottom: string; right: string; }; euiFontFamily: string; euiCodeFontFamily: string; euiFontFeatureSettings: string; euiTextScale: string; euiFontSize: string; euiFontSizeXS: string; euiFontSizeS: string; euiFontSizeM: string; euiFontSizeL: string; euiFontSizeXL: string; euiFontSizeXXL: string; euiLineHeight: number; euiBodyLineHeight: number; euiTitles: { xxxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; s: { 'font-size': string; 'line-height': string; 'font-weight': number; }; m: { 'font-size': string; 'line-height': string; 'font-weight': number; }; l: { 'font-size': string; 'line-height': string; 'font-weight': number; }; }; euiZLevel0: number; euiZLevel1: number; euiZLevel2: number; euiZLevel3: number; euiZLevel4: number; euiZLevel5: number; euiZLevel6: number; euiZLevel7: number; euiZLevel8: number; euiZLevel9: number; euiZToastList: number; euiZModal: number; euiZMask: number; euiZNavigation: number; euiZContentMenu: number; euiZHeader: number; euiZFlyout: number; euiZMaskBelowHeader: number; euiZContent: number; euiColorGhost: string; euiColorInk: string; euiColorPrimary: string; euiColorAccent: string; euiColorSuccess: string; euiColorWarning: string; euiColorDanger: string; euiColorEmptyShade: string; euiColorLightestShade: string; euiColorLightShade: string; euiColorMediumShade: string; euiColorDarkShade: string; euiColorDarkestShade: string; euiColorFullShade: string; euiPageBackgroundColor: string; euiColorHighlight: string; euiTextColor: string; euiTitleColor: string; euiTextSubduedColor: string; euiColorDisabled: string; euiColorPrimaryText: string; euiColorSuccessText: string; euiColorAccentText: string; euiColorWarningText: string; euiColorDangerText: string; euiColorDisabledText: string; euiLinkColor: string; euiColorChartLines: string; euiColorChartBand: string; euiDatePickerCalendarWidth: string; euiDatePickerPadding: string; euiDatePickerGap: string; euiDatePickerCalendarColumns: number; euiDatePickerButtonSize: string; euiDatePickerMinControlWidth: string; euiDatePickerMaxControlWidth: string; euiButtonDefaultTransparency: number; euiButtonFontWeight: number; euiRangeHighlightColor: string; euiRangeThumbBackgroundColor: string; euiRangeTrackCompressedHeight: string; euiRangeHighlightCompressedHeight: string; euiRangeHeight: string; euiRangeCompressedHeight: string; euiStepStatusColors: { default: string; complete: string; warning: string; danger: string; }; }" ], "path": "packages/kbn-ui-theme/src/theme.ts", "deprecated": false, @@ -82,7 +82,7 @@ "label": "euiDarkVars", "description": [], "signature": [ - "{ paddingSizes: { xs: string; s: string; m: string; l: string; xl: string; }; avatarSizing: { s: { size: string; 'font-size': string; }; m: { size: string; 'font-size': string; }; l: { size: string; 'font-size': string; }; xl: { size: string; 'font-size': string; }; }; euiBadgeGroupGutterTypes: { gutterExtraSmall: string; gutterSmall: string; }; euiBreadcrumbSpacing: string; euiBreadcrumbTruncateWidth: string; euiButtonEmptyTypes: { primary: string; danger: string; disabled: string; ghost: string; text: string; success: string; warning: string; }; euiCallOutTypes: { primary: string; success: string; warning: string; danger: string; }; euiCardSpacing: string; euiCardBottomNodeHeight: string; euiCardSelectButtonBorders: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardSelectButtonBackgrounds: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCheckableCardPadding: string; euiCodeBlockPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCollapsibleNavGroupLightBackgroundColor: string; euiCollapsibleNavGroupDarkBackgroundColor: string; euiCollapsibleNavGroupDarkHighContrastColor: string; euiColorPickerValueRange0: string; euiColorPickerValueRange1: string; euiColorPickerSaturationRange0: string; euiColorPickerSaturationRange1: string; euiColorPickerIndicatorSize: string; euiColorPickerWidth: string; euiColorPaletteDisplaySizes: { sizeExtraSmall: string; sizeSmall: string; sizeMedium: string; }; euiContextMenuWidth: string; euiControlBarBackground: string; euiControlBarText: string; euiControlBarBorderColor: string; euiControlBarInitialHeight: string; euiControlBarMaxHeight: string; euiControlBarHeights: { s: string; m: string; l: string; }; euiDataGridPrefix: string; euiDataGridStyles: string; euiZDataGrid: number; euiZHeaderBelowDataGrid: number; euiZDataGridCellPopover: number; euiDataGridColumnResizerWidth: string; euiDataGridPopoverMaxHeight: string; euiDataGridCellPaddingS: string; euiDataGridCellPaddingM: string; euiDataGridCellPaddingL: string; euiDataGridVerticalBorder: string; euiSuperDatePickerWidth: string; euiSuperDatePickerButtonWidth: string; euiDragAndDropSpacing: { s: string; m: string; l: string; }; euiExpressionColors: { subdued: string; primary: string; success: string; warning: string; danger: string; accent: string; }; euiFacetGutterSizes: { gutterNone: number; gutterSmall: string; gutterMedium: string; gutterLarge: string; }; gutterTypes: { gutterExtraSmall: string; gutterSmall: string; gutterMedium: string; gutterLarge: string; gutterExtraLarge: string; }; fractions: { fourths: { percentage: string; count: number; }; thirds: { percentage: string; count: number; }; halves: { percentage: string; count: number; }; single: { percentage: string; count: number; }; }; flyoutSizes: { small: { min: string; width: string; max: string; }; medium: { min: string; width: string; max: string; }; large: { min: string; width: string; max: string; }; }; euiFlyoutBorder: string; euiFlyoutPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiFilePickerTallHeight: string; euiRangeLevelColors: { primary: string; success: string; warning: string; danger: string; }; textareaResizing: { vertical: string; horizontal: string; both: string; none: string; }; euiHeaderLinksGutterSizes: { gutterXS: string; gutterS: string; gutterM: string; gutterL: string; }; ruleMargins: { marginXSmall: string; marginSmall: string; marginMedium: string; marginLarge: string; marginXLarge: string; marginXXLarge: string; }; euiIconLoadingOpacity: number; euiIconColors: { accent: string; danger: string; ghost: string; primary: string; success: string; subdued: string; text: string; warning: string; inherit: string; }; euiIconSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiKeyPadMenuSize: string; euiKeyPadMenuMarginSize: string; euiLinkColors: { subdued: string; primary: string; success: string; accent: string; warning: string; danger: string; text: string; ghost: string; }; euiListGroupItemHoverBackground: string; euiListGroupItemHoverBackgroundGhost: string; euiListGroupGutterTypes: { gutterSmall: string; gutterMedium: string; }; euiListGroupItemColorTypes: { primary: string; text: string; subdued: string; ghost: string; }; euiListGroupItemSizeTypes: { xSmall: string; small: string; medium: string; large: string; }; euiGradientStartStop: string; euiGradientMiddle: string; euiLoadingSpinnerSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiMarkdownEditorMinHeight: string; euiPopoverArrowSize: string; euiPopoverTranslateDistance: string; euiProgressSizes: { xs: string; s: string; m: string; l: string; }; euiProgressColors: { primary: string; success: string; warning: string; danger: string; accent: string; subdued: string; vis0: string; vis1: string; vis2: string; vis3: string; vis4: string; vis5: string; vis6: string; vis7: string; vis8: string; vis9: string; customColor: string; }; euiResizableButtonTransitionSpeed: string; euiResizableButtonSize: string; euiSelectableListItemBorder: string; euiSelectableListItemPadding: string; euiSelectableTemplateSitewideTypes: { application: { color: string; 'font-weight': number; }; deployment: { color: string; 'font-weight': number; }; article: { color: string; 'font-weight': number; }; case: { color: string; 'font-weight': number; }; platform: { color: string; 'font-weight': number; }; }; euiSideNavEmphasizedBackgroundColor: string; euiSideNavRootTextcolor: string; euiSideNavBranchTextcolor: string; euiSideNavSelectedTextcolor: string; euiSideNavDisabledTextcolor: string; spacerSizes: { xs: string; s: string; m: string; l: string; xl: string; xxl: string; }; euiStepNumberSize: string; euiStepNumberSmallSize: string; euiStepNumberMargin: string; euiStepStatusColorsToFade: { warning: string; danger: string; disabled: string; incomplete: string; }; euiSuggestItemColors: { tint0: string; tint1: string; tint2: string; tint3: string; tint4: string; tint5: string; tint6: string; tint7: string; tint8: string; tint9: string; tint10: string; }; euiTableCellContentPadding: string; euiTableCellContentPaddingCompressed: string; euiTableCellCheckboxWidth: string; euiTableActionsAreaWidth: string; euiTableHoverColor: string; euiTableSelectedColor: string; euiTableHoverSelectedColor: string; euiTableActionsBorderColor: string; euiTableHoverClickableColor: string; euiTableFocusClickableColor: string; euiTextColors: { default: string; subdued: string; success: string; accent: string; warning: string; danger: string; ghost: string; inherit: string; }; euiTextConstrainedMaxWidth: string; euiToastWidth: string; euiToastTypes: { primary: string; success: string; warning: string; danger: string; }; euiTokenGrayColor: string; euiTokenTypes: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; gray: { graphic: string; behindText: string; }; }; euiTokenTypeKeys: string; euiContrastRatioText: number; euiContrastRatioGraphic: number; euiContrastRatioDisabled: number; euiAnimSlightBounce: string; euiAnimSlightResistance: string; euiAnimSpeedExtraFast: string; euiAnimSpeedFast: string; euiAnimSpeedNormal: string; euiAnimSpeedSlow: string; euiAnimSpeedExtraSlow: string; euiBorderWidthThin: string; euiBorderWidthThick: string; euiBorderColor: string; euiBorderRadius: string; euiBorderRadiusSmall: string; euiBorderThick: string; euiBorderThin: string; euiBorderEditable: string; euiButtonHeight: string; euiButtonHeightSmall: string; euiButtonHeightXSmall: string; euiButtonColorDisabled: string; euiButtonColorDisabledText: string; euiButtonColorGhostDisabled: string; euiButtonTypes: { primary: string; accent: string; success: string; warning: string; danger: string; subdued: string; ghost: string; text: string; }; euiCodeBlockBackgroundColor: string; euiCodeBlockColor: string; euiCodeBlockSelectedBackgroundColor: string; euiCodeBlockCommentColor: string; euiCodeBlockSelectorTagColor: string; euiCodeBlockStringColor: string; euiCodeBlockTagColor: string; euiCodeBlockNameColor: string; euiCodeBlockNumberColor: string; euiCodeBlockKeywordColor: string; euiCodeBlockFunctionTitleColor: string; euiCodeBlockTypeColor: string; euiCodeBlockAttributeColor: string; euiCodeBlockSymbolColor: string; euiCodeBlockParamsColor: string; euiCodeBlockMetaColor: string; euiCodeBlockTitleColor: string; euiCodeBlockSectionColor: string; euiCodeBlockAdditionColor: string; euiCodeBlockDeletionColor: string; euiCodeBlockSelectorClassColor: string; euiCodeBlockSelectorIdColor: string; euiPaletteColorBlind: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; }; euiPaletteColorBlindKeys: string; euiColorVis0: string; euiColorVis1: string; euiColorVis2: string; euiColorVis3: string; euiColorVis4: string; euiColorVis5: string; euiColorVis6: string; euiColorVis7: string; euiColorVis8: string; euiColorVis9: string; euiColorVis0_behindText: string; euiColorVis1_behindText: string; euiColorVis2_behindText: string; euiColorVis3_behindText: string; euiColorVis4_behindText: string; euiColorVis5_behindText: string; euiColorVis6_behindText: string; euiColorVis7_behindText: string; euiColorVis8_behindText: string; euiColorVis9_behindText: string; euiFontWeightLight: number; euiFontWeightRegular: number; euiFontWeightMedium: number; euiFontWeightSemiBold: number; euiFontWeightBold: number; euiCodeFontWeightRegular: number; euiCodeFontWeightBold: number; euiFormMaxWidth: string; euiFormControlHeight: string; euiFormControlCompressedHeight: string; euiFormControlPadding: string; euiFormControlCompressedPadding: string; euiFormControlBorderRadius: string; euiFormControlCompressedBorderRadius: string; euiRadioSize: string; euiCheckBoxSize: string; euiCheckboxBorderRadius: string; euiSwitchHeight: string; euiSwitchWidth: string; euiSwitchThumbSize: string; euiSwitchIconHeight: string; euiSwitchHeightCompressed: string; euiSwitchWidthCompressed: string; euiSwitchThumbSizeCompressed: string; euiSwitchHeightMini: string; euiSwitchWidthMini: string; euiSwitchThumbSizeMini: string; euiFormBackgroundColor: string; euiFormBackgroundDisabledColor: string; euiFormBackgroundReadOnlyColor: string; euiFormBorderOpaqueColor: string; euiFormBorderColor: string; euiFormBorderDisabledColor: string; euiFormCustomControlDisabledIconColor: string; euiFormCustomControlBorderColor: string; euiFormControlDisabledColor: string; euiFormControlBoxShadow: string; euiFormControlPlaceholderText: string; euiFormInputGroupLabelBackground: string; euiFormInputGroupBorder: string; euiSwitchOffColor: string; euiFormControlLayoutGroupInputHeight: string; euiFormControlLayoutGroupInputCompressedHeight: string; euiFormControlLayoutGroupInputCompressedBorderRadius: string; euiRangeTrackColor: string; euiRangeThumbRadius: string; euiRangeThumbHeight: string; euiRangeThumbWidth: string; euiRangeThumbBorderColor: string; euiRangeTrackWidth: string; euiRangeTrackHeight: string; euiRangeTrackBorderWidth: number; euiRangeTrackBorderColor: string; euiRangeTrackRadius: string; euiRangeDisabledOpacity: number; euiRangeHighlightHeight: string; euiHeaderBackgroundColor: string; euiHeaderDarkBackgroundColor: string; euiHeaderBorderColor: string; euiHeaderBreadcrumbColor: string; euiHeaderHeight: string; euiHeaderChildSize: string; euiHeaderHeightCompensation: string; euiPageDefaultMaxWidth: string; euiPageSidebarMinWidth: string; euiPanelPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiPanelBorderRadiusModifiers: { borderRadiusNone: number; borderRadiusMedium: string; }; euiPanelBackgroundColorModifiers: { transparent: string; plain: string; subdued: string; accent: string; primary: string; success: string; warning: string; danger: string; }; euiBreakpoints: { xs: number; s: string; m: string; l: string; xl: string; }; euiBreakpointKeys: string; euiShadowColor: string; euiShadowColorLarge: string; euiSize: string; euiSizeXS: string; euiSizeS: string; euiSizeM: string; euiSizeL: string; euiSizeXL: string; euiSizeXXL: string; euiButtonMinWidth: string; euiScrollBar: string; euiScrollBarCorner: string; euiScrollBarCornerThin: string; euiFocusRingColor: string; euiFocusRingAnimStartColor: string; euiFocusRingAnimStartSize: string; euiFocusRingAnimStartSizeLarge: string; euiFocusRingSizeLarge: string; euiFocusRingSize: string; euiFocusTransparency: number; euiFocusTransparencyPercent: string; euiFocusBackgroundColor: string; euiTooltipBackgroundColor: string; euiTooltipBorderColor: string; euiTooltipAnimations: { top: string; left: string; bottom: string; right: string; }; euiFontFamily: string; euiCodeFontFamily: string; euiFontFeatureSettings: string; euiTextScale: string; euiFontSize: string; euiFontSizeXS: string; euiFontSizeS: string; euiFontSizeM: string; euiFontSizeL: string; euiFontSizeXL: string; euiFontSizeXXL: string; euiLineHeight: number; euiBodyLineHeight: number; euiTitles: { xxxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; s: { 'font-size': string; 'line-height': string; 'font-weight': number; }; m: { 'font-size': string; 'line-height': string; 'font-weight': number; }; l: { 'font-size': string; 'line-height': string; 'font-weight': number; }; }; euiZLevel0: number; euiZLevel1: number; euiZLevel2: number; euiZLevel3: number; euiZLevel4: number; euiZLevel5: number; euiZLevel6: number; euiZLevel7: number; euiZLevel8: number; euiZLevel9: number; euiZToastList: number; euiZModal: number; euiZMask: number; euiZNavigation: number; euiZContentMenu: number; euiZHeader: number; euiZFlyout: number; euiZMaskBelowHeader: number; euiZContent: number; euiColorGhost: string; euiColorInk: string; euiColorPrimary: string; euiColorAccent: string; euiColorSuccess: string; euiColorWarning: string; euiColorDanger: string; euiColorEmptyShade: string; euiColorLightestShade: string; euiColorLightShade: string; euiColorMediumShade: string; euiColorDarkShade: string; euiColorDarkestShade: string; euiColorFullShade: string; euiPageBackgroundColor: string; euiColorHighlight: string; euiTextColor: string; euiTitleColor: string; euiTextSubduedColor: string; euiColorDisabled: string; euiColorPrimaryText: string; euiColorSuccessText: string; euiColorAccentText: string; euiColorWarningText: string; euiColorDangerText: string; euiColorDisabledText: string; euiLinkColor: string; euiColorChartLines: string; euiColorChartBand: string; euiDatePickerCalendarWidth: string; euiDatePickerPadding: string; euiDatePickerGap: string; euiDatePickerCalendarColumns: number; euiDatePickerButtonSize: string; euiDatePickerMinControlWidth: string; euiDatePickerMaxControlWidth: string; euiButtonDefaultTransparency: number; euiButtonFontWeight: number; euiRangeHighlightColor: string; euiRangeThumbBackgroundColor: string; euiRangeTrackCompressedHeight: string; euiRangeHighlightCompressedHeight: string; euiRangeHeight: string; euiRangeCompressedHeight: string; euiStepStatusColors: { default: string; complete: string; warning: string; danger: string; }; }" + "{ paddingSizes: { xs: string; s: string; m: string; l: string; xl: string; }; euiBadgeGroupGutterTypes: { gutterExtraSmall: string; gutterSmall: string; }; euiBreadcrumbSpacing: string; euiBreadcrumbTruncateWidth: string; euiButtonEmptyTypes: { primary: string; danger: string; disabled: string; ghost: string; text: string; success: string; warning: string; }; euiCallOutTypes: { primary: string; success: string; warning: string; danger: string; }; euiCardSpacing: string; euiCardBottomNodeHeight: string; euiCardSelectButtonBorders: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardSelectButtonBackgrounds: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCheckableCardPadding: string; euiCodeBlockPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCollapsibleNavGroupLightBackgroundColor: string; euiCollapsibleNavGroupDarkBackgroundColor: string; euiCollapsibleNavGroupDarkHighContrastColor: string; euiColorPickerValueRange0: string; euiColorPickerValueRange1: string; euiColorPickerSaturationRange0: string; euiColorPickerSaturationRange1: string; euiColorPickerIndicatorSize: string; euiColorPickerWidth: string; euiColorPaletteDisplaySizes: { sizeExtraSmall: string; sizeSmall: string; sizeMedium: string; }; euiContextMenuWidth: string; euiControlBarBackground: string; euiControlBarText: string; euiControlBarBorderColor: string; euiControlBarInitialHeight: string; euiControlBarMaxHeight: string; euiControlBarHeights: { s: string; m: string; l: string; }; euiDataGridPrefix: string; euiDataGridStyles: string; euiZDataGrid: number; euiZHeaderBelowDataGrid: number; euiZDataGridCellPopover: number; euiDataGridColumnResizerWidth: string; euiDataGridPopoverMaxHeight: string; euiDataGridCellPaddingS: string; euiDataGridCellPaddingM: string; euiDataGridCellPaddingL: string; euiDataGridVerticalBorder: string; euiSuperDatePickerWidth: string; euiSuperDatePickerButtonWidth: string; euiDragAndDropSpacing: { s: string; m: string; l: string; }; euiExpressionColors: { subdued: string; primary: string; success: string; warning: string; danger: string; accent: string; }; euiFacetGutterSizes: { gutterNone: number; gutterSmall: string; gutterMedium: string; gutterLarge: string; }; gutterTypes: { gutterExtraSmall: string; gutterSmall: string; gutterMedium: string; gutterLarge: string; gutterExtraLarge: string; }; fractions: { fourths: { percentage: string; count: number; }; thirds: { percentage: string; count: number; }; halves: { percentage: string; count: number; }; single: { percentage: string; count: number; }; }; flyoutSizes: { small: { min: string; width: string; max: string; }; medium: { min: string; width: string; max: string; }; large: { min: string; width: string; max: string; }; }; euiFlyoutBorder: string; euiFlyoutPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiFilePickerTallHeight: string; euiRangeLevelColors: { primary: string; success: string; warning: string; danger: string; }; textareaResizing: { vertical: string; horizontal: string; both: string; none: string; }; euiHeaderLinksGutterSizes: { gutterXS: string; gutterS: string; gutterM: string; gutterL: string; }; ruleMargins: { marginXSmall: string; marginSmall: string; marginMedium: string; marginLarge: string; marginXLarge: string; marginXXLarge: string; }; euiIconLoadingOpacity: number; euiIconColors: { accent: string; danger: string; ghost: string; primary: string; success: string; subdued: string; text: string; warning: string; inherit: string; }; euiIconSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiKeyPadMenuSize: string; euiKeyPadMenuMarginSize: string; euiLinkColors: { subdued: string; primary: string; success: string; accent: string; warning: string; danger: string; text: string; ghost: string; }; euiListGroupItemHoverBackground: string; euiListGroupItemHoverBackgroundGhost: string; euiListGroupGutterTypes: { gutterSmall: string; gutterMedium: string; }; euiListGroupItemColorTypes: { primary: string; text: string; subdued: string; ghost: string; }; euiListGroupItemSizeTypes: { xSmall: string; small: string; medium: string; large: string; }; euiGradientStartStop: string; euiGradientMiddle: string; euiLoadingSpinnerSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiMarkdownEditorMinHeight: string; euiPopoverArrowSize: string; euiPopoverTranslateDistance: string; euiProgressSizes: { xs: string; s: string; m: string; l: string; }; euiProgressColors: { primary: string; success: string; warning: string; danger: string; accent: string; subdued: string; vis0: string; vis1: string; vis2: string; vis3: string; vis4: string; vis5: string; vis6: string; vis7: string; vis8: string; vis9: string; customColor: string; }; euiResizableButtonTransitionSpeed: string; euiResizableButtonSize: string; euiSelectableListItemBorder: string; euiSelectableListItemPadding: string; euiSelectableTemplateSitewideTypes: { application: { color: string; 'font-weight': number; }; deployment: { color: string; 'font-weight': number; }; article: { color: string; 'font-weight': number; }; case: { color: string; 'font-weight': number; }; platform: { color: string; 'font-weight': number; }; }; euiSideNavEmphasizedBackgroundColor: string; euiSideNavRootTextcolor: string; euiSideNavBranchTextcolor: string; euiSideNavSelectedTextcolor: string; euiSideNavDisabledTextcolor: string; spacerSizes: { xs: string; s: string; m: string; l: string; xl: string; xxl: string; }; euiStepNumberSize: string; euiStepNumberSmallSize: string; euiStepNumberMargin: string; euiStepStatusColorsToFade: { warning: string; danger: string; disabled: string; incomplete: string; }; euiSuggestItemColors: { tint0: string; tint1: string; tint2: string; tint3: string; tint4: string; tint5: string; tint6: string; tint7: string; tint8: string; tint9: string; tint10: string; }; euiTableCellContentPadding: string; euiTableCellContentPaddingCompressed: string; euiTableCellCheckboxWidth: string; euiTableActionsAreaWidth: string; euiTableHoverColor: string; euiTableSelectedColor: string; euiTableHoverSelectedColor: string; euiTableActionsBorderColor: string; euiTableHoverClickableColor: string; euiTableFocusClickableColor: string; euiTextColors: { default: string; subdued: string; success: string; accent: string; warning: string; danger: string; ghost: string; inherit: string; }; euiTextConstrainedMaxWidth: string; euiToastWidth: string; euiToastTypes: { primary: string; success: string; warning: string; danger: string; }; euiTokenGrayColor: string; euiTokenTypes: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; gray: { graphic: string; behindText: string; }; }; euiTokenTypeKeys: string; euiContrastRatioText: number; euiContrastRatioGraphic: number; euiContrastRatioDisabled: number; euiAnimSlightBounce: string; euiAnimSlightResistance: string; euiAnimSpeedExtraFast: string; euiAnimSpeedFast: string; euiAnimSpeedNormal: string; euiAnimSpeedSlow: string; euiAnimSpeedExtraSlow: string; euiBorderWidthThin: string; euiBorderWidthThick: string; euiBorderColor: string; euiBorderRadius: string; euiBorderRadiusSmall: string; euiBorderThick: string; euiBorderThin: string; euiBorderEditable: string; euiButtonHeight: string; euiButtonHeightSmall: string; euiButtonHeightXSmall: string; euiButtonColorDisabled: string; euiButtonColorDisabledText: string; euiButtonColorGhostDisabled: string; euiButtonTypes: { primary: string; accent: string; success: string; warning: string; danger: string; subdued: string; ghost: string; text: string; }; euiCodeBlockBackgroundColor: string; euiCodeBlockColor: string; euiCodeBlockSelectedBackgroundColor: string; euiCodeBlockCommentColor: string; euiCodeBlockSelectorTagColor: string; euiCodeBlockStringColor: string; euiCodeBlockTagColor: string; euiCodeBlockNameColor: string; euiCodeBlockNumberColor: string; euiCodeBlockKeywordColor: string; euiCodeBlockFunctionTitleColor: string; euiCodeBlockTypeColor: string; euiCodeBlockAttributeColor: string; euiCodeBlockSymbolColor: string; euiCodeBlockParamsColor: string; euiCodeBlockMetaColor: string; euiCodeBlockTitleColor: string; euiCodeBlockSectionColor: string; euiCodeBlockAdditionColor: string; euiCodeBlockDeletionColor: string; euiCodeBlockSelectorClassColor: string; euiCodeBlockSelectorIdColor: string; euiPaletteColorBlind: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; }; euiPaletteColorBlindKeys: string; euiColorVis0: string; euiColorVis1: string; euiColorVis2: string; euiColorVis3: string; euiColorVis4: string; euiColorVis5: string; euiColorVis6: string; euiColorVis7: string; euiColorVis8: string; euiColorVis9: string; euiColorVis0_behindText: string; euiColorVis1_behindText: string; euiColorVis2_behindText: string; euiColorVis3_behindText: string; euiColorVis4_behindText: string; euiColorVis5_behindText: string; euiColorVis6_behindText: string; euiColorVis7_behindText: string; euiColorVis8_behindText: string; euiColorVis9_behindText: string; euiFontWeightLight: number; euiFontWeightRegular: number; euiFontWeightMedium: number; euiFontWeightSemiBold: number; euiFontWeightBold: number; euiCodeFontWeightRegular: number; euiCodeFontWeightBold: number; euiFormMaxWidth: string; euiFormControlHeight: string; euiFormControlCompressedHeight: string; euiFormControlPadding: string; euiFormControlCompressedPadding: string; euiFormControlBorderRadius: string; euiFormControlCompressedBorderRadius: string; euiRadioSize: string; euiCheckBoxSize: string; euiCheckboxBorderRadius: string; euiSwitchHeight: string; euiSwitchWidth: string; euiSwitchThumbSize: string; euiSwitchIconHeight: string; euiSwitchHeightCompressed: string; euiSwitchWidthCompressed: string; euiSwitchThumbSizeCompressed: string; euiSwitchHeightMini: string; euiSwitchWidthMini: string; euiSwitchThumbSizeMini: string; euiFormBackgroundColor: string; euiFormBackgroundDisabledColor: string; euiFormBackgroundReadOnlyColor: string; euiFormBorderOpaqueColor: string; euiFormBorderColor: string; euiFormBorderDisabledColor: string; euiFormCustomControlDisabledIconColor: string; euiFormCustomControlBorderColor: string; euiFormControlDisabledColor: string; euiFormControlBoxShadow: string; euiFormControlPlaceholderText: string; euiFormInputGroupLabelBackground: string; euiFormInputGroupBorder: string; euiSwitchOffColor: string; euiFormControlLayoutGroupInputHeight: string; euiFormControlLayoutGroupInputCompressedHeight: string; euiFormControlLayoutGroupInputCompressedBorderRadius: string; euiRangeTrackColor: string; euiRangeThumbRadius: string; euiRangeThumbHeight: string; euiRangeThumbWidth: string; euiRangeThumbBorderColor: string; euiRangeTrackWidth: string; euiRangeTrackHeight: string; euiRangeTrackBorderWidth: number; euiRangeTrackBorderColor: string; euiRangeTrackRadius: string; euiRangeDisabledOpacity: number; euiRangeHighlightHeight: string; euiHeaderBackgroundColor: string; euiHeaderDarkBackgroundColor: string; euiHeaderBorderColor: string; euiHeaderBreadcrumbColor: string; euiHeaderHeight: string; euiHeaderChildSize: string; euiHeaderHeightCompensation: string; euiPageDefaultMaxWidth: string; euiPageSidebarMinWidth: string; euiPanelPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiPanelBorderRadiusModifiers: { borderRadiusNone: number; borderRadiusMedium: string; }; euiPanelBackgroundColorModifiers: { transparent: string; plain: string; subdued: string; accent: string; primary: string; success: string; warning: string; danger: string; }; euiBreakpoints: { xs: number; s: string; m: string; l: string; xl: string; }; euiBreakpointKeys: string; euiShadowColor: string; euiShadowColorLarge: string; euiSize: string; euiSizeXS: string; euiSizeS: string; euiSizeM: string; euiSizeL: string; euiSizeXL: string; euiSizeXXL: string; euiButtonMinWidth: string; euiScrollBar: string; euiScrollBarCorner: string; euiScrollBarCornerThin: string; euiFocusRingColor: string; euiFocusRingAnimStartColor: string; euiFocusRingAnimStartSize: string; euiFocusRingAnimStartSizeLarge: string; euiFocusRingSizeLarge: string; euiFocusRingSize: string; euiFocusTransparency: number; euiFocusTransparencyPercent: string; euiFocusBackgroundColor: string; euiTooltipBackgroundColor: string; euiTooltipBorderColor: string; euiTooltipAnimations: { top: string; left: string; bottom: string; right: string; }; euiFontFamily: string; euiCodeFontFamily: string; euiFontFeatureSettings: string; euiTextScale: string; euiFontSize: string; euiFontSizeXS: string; euiFontSizeS: string; euiFontSizeM: string; euiFontSizeL: string; euiFontSizeXL: string; euiFontSizeXXL: string; euiLineHeight: number; euiBodyLineHeight: number; euiTitles: { xxxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; s: { 'font-size': string; 'line-height': string; 'font-weight': number; }; m: { 'font-size': string; 'line-height': string; 'font-weight': number; }; l: { 'font-size': string; 'line-height': string; 'font-weight': number; }; }; euiZLevel0: number; euiZLevel1: number; euiZLevel2: number; euiZLevel3: number; euiZLevel4: number; euiZLevel5: number; euiZLevel6: number; euiZLevel7: number; euiZLevel8: number; euiZLevel9: number; euiZToastList: number; euiZModal: number; euiZMask: number; euiZNavigation: number; euiZContentMenu: number; euiZHeader: number; euiZFlyout: number; euiZMaskBelowHeader: number; euiZContent: number; euiColorGhost: string; euiColorInk: string; euiColorPrimary: string; euiColorAccent: string; euiColorSuccess: string; euiColorWarning: string; euiColorDanger: string; euiColorEmptyShade: string; euiColorLightestShade: string; euiColorLightShade: string; euiColorMediumShade: string; euiColorDarkShade: string; euiColorDarkestShade: string; euiColorFullShade: string; euiPageBackgroundColor: string; euiColorHighlight: string; euiTextColor: string; euiTitleColor: string; euiTextSubduedColor: string; euiColorDisabled: string; euiColorPrimaryText: string; euiColorSuccessText: string; euiColorAccentText: string; euiColorWarningText: string; euiColorDangerText: string; euiColorDisabledText: string; euiLinkColor: string; euiColorChartLines: string; euiColorChartBand: string; euiDatePickerCalendarWidth: string; euiDatePickerPadding: string; euiDatePickerGap: string; euiDatePickerCalendarColumns: number; euiDatePickerButtonSize: string; euiDatePickerMinControlWidth: string; euiDatePickerMaxControlWidth: string; euiButtonDefaultTransparency: number; euiButtonFontWeight: number; euiRangeHighlightColor: string; euiRangeThumbBackgroundColor: string; euiRangeTrackCompressedHeight: string; euiRangeHighlightCompressedHeight: string; euiRangeHeight: string; euiRangeCompressedHeight: string; euiStepStatusColors: { default: string; complete: string; warning: string; danger: string; }; }" ], "path": "packages/kbn-ui-theme/src/theme.ts", "deprecated": false, @@ -96,7 +96,7 @@ "label": "euiLightVars", "description": [], "signature": [ - "{ paddingSizes: { xs: string; s: string; m: string; l: string; xl: string; }; avatarSizing: { s: { size: string; 'font-size': string; }; m: { size: string; 'font-size': string; }; l: { size: string; 'font-size': string; }; xl: { size: string; 'font-size': string; }; }; euiBadgeGroupGutterTypes: { gutterExtraSmall: string; gutterSmall: string; }; euiBreadcrumbSpacing: string; euiBreadcrumbTruncateWidth: string; euiButtonEmptyTypes: { primary: string; danger: string; disabled: string; ghost: string; text: string; success: string; warning: string; }; euiCallOutTypes: { primary: string; success: string; warning: string; danger: string; }; euiCardSpacing: string; euiCardBottomNodeHeight: string; euiCardSelectButtonBorders: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardSelectButtonBackgrounds: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCheckableCardPadding: string; euiCodeBlockPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCollapsibleNavGroupLightBackgroundColor: string; euiCollapsibleNavGroupDarkBackgroundColor: string; euiCollapsibleNavGroupDarkHighContrastColor: string; euiColorPickerValueRange0: string; euiColorPickerValueRange1: string; euiColorPickerSaturationRange0: string; euiColorPickerSaturationRange1: string; euiColorPickerIndicatorSize: string; euiColorPickerWidth: string; euiColorPaletteDisplaySizes: { sizeExtraSmall: string; sizeSmall: string; sizeMedium: string; }; euiContextMenuWidth: string; euiControlBarBackground: string; euiControlBarText: string; euiControlBarBorderColor: string; euiControlBarInitialHeight: string; euiControlBarMaxHeight: string; euiControlBarHeights: { s: string; m: string; l: string; }; euiDataGridPrefix: string; euiDataGridStyles: string; euiZDataGrid: number; euiZHeaderBelowDataGrid: number; euiZDataGridCellPopover: number; euiDataGridColumnResizerWidth: string; euiDataGridPopoverMaxHeight: string; euiDataGridCellPaddingS: string; euiDataGridCellPaddingM: string; euiDataGridCellPaddingL: string; euiDataGridVerticalBorder: string; euiSuperDatePickerWidth: string; euiSuperDatePickerButtonWidth: string; euiDragAndDropSpacing: { s: string; m: string; l: string; }; euiExpressionColors: { subdued: string; primary: string; success: string; warning: string; danger: string; accent: string; }; euiFacetGutterSizes: { gutterNone: number; gutterSmall: string; gutterMedium: string; gutterLarge: string; }; gutterTypes: { gutterExtraSmall: string; gutterSmall: string; gutterMedium: string; gutterLarge: string; gutterExtraLarge: string; }; fractions: { fourths: { percentage: string; count: number; }; thirds: { percentage: string; count: number; }; halves: { percentage: string; count: number; }; single: { percentage: string; count: number; }; }; flyoutSizes: { small: { min: string; width: string; max: string; }; medium: { min: string; width: string; max: string; }; large: { min: string; width: string; max: string; }; }; euiFlyoutBorder: string; euiFlyoutPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiFilePickerTallHeight: string; euiRangeLevelColors: { primary: string; success: string; warning: string; danger: string; }; textareaResizing: { vertical: string; horizontal: string; both: string; none: string; }; euiHeaderLinksGutterSizes: { gutterXS: string; gutterS: string; gutterM: string; gutterL: string; }; ruleMargins: { marginXSmall: string; marginSmall: string; marginMedium: string; marginLarge: string; marginXLarge: string; marginXXLarge: string; }; euiIconLoadingOpacity: number; euiIconColors: { accent: string; danger: string; ghost: string; primary: string; success: string; subdued: string; text: string; warning: string; inherit: string; }; euiIconSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiKeyPadMenuSize: string; euiKeyPadMenuMarginSize: string; euiLinkColors: { subdued: string; primary: string; success: string; accent: string; warning: string; danger: string; text: string; ghost: string; }; euiListGroupItemHoverBackground: string; euiListGroupItemHoverBackgroundGhost: string; euiListGroupGutterTypes: { gutterSmall: string; gutterMedium: string; }; euiListGroupItemColorTypes: { primary: string; text: string; subdued: string; ghost: string; }; euiListGroupItemSizeTypes: { xSmall: string; small: string; medium: string; large: string; }; euiGradientStartStop: string; euiGradientMiddle: string; euiLoadingSpinnerSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiMarkdownEditorMinHeight: string; euiPopoverArrowSize: string; euiPopoverTranslateDistance: string; euiProgressSizes: { xs: string; s: string; m: string; l: string; }; euiProgressColors: { primary: string; success: string; warning: string; danger: string; accent: string; subdued: string; vis0: string; vis1: string; vis2: string; vis3: string; vis4: string; vis5: string; vis6: string; vis7: string; vis8: string; vis9: string; customColor: string; }; euiResizableButtonTransitionSpeed: string; euiResizableButtonSize: string; euiSelectableListItemBorder: string; euiSelectableListItemPadding: string; euiSelectableTemplateSitewideTypes: { application: { color: string; 'font-weight': number; }; deployment: { color: string; 'font-weight': number; }; article: { color: string; 'font-weight': number; }; case: { color: string; 'font-weight': number; }; platform: { color: string; 'font-weight': number; }; }; euiSideNavEmphasizedBackgroundColor: string; euiSideNavRootTextcolor: string; euiSideNavBranchTextcolor: string; euiSideNavSelectedTextcolor: string; euiSideNavDisabledTextcolor: string; spacerSizes: { xs: string; s: string; m: string; l: string; xl: string; xxl: string; }; euiStepNumberSize: string; euiStepNumberSmallSize: string; euiStepNumberMargin: string; euiStepStatusColorsToFade: { warning: string; danger: string; disabled: string; incomplete: string; }; euiSuggestItemColors: { tint0: string; tint1: string; tint2: string; tint3: string; tint4: string; tint5: string; tint6: string; tint7: string; tint8: string; tint9: string; tint10: string; }; euiTableCellContentPadding: string; euiTableCellContentPaddingCompressed: string; euiTableCellCheckboxWidth: string; euiTableActionsAreaWidth: string; euiTableHoverColor: string; euiTableSelectedColor: string; euiTableHoverSelectedColor: string; euiTableActionsBorderColor: string; euiTableHoverClickableColor: string; euiTableFocusClickableColor: string; euiTextColors: { default: string; subdued: string; success: string; accent: string; warning: string; danger: string; ghost: string; inherit: string; }; euiTextConstrainedMaxWidth: string; euiToastWidth: string; euiToastTypes: { primary: string; success: string; warning: string; danger: string; }; euiTokenGrayColor: string; euiTokenTypes: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; gray: { graphic: string; behindText: string; }; }; euiTokenTypeKeys: string; euiContrastRatioText: number; euiContrastRatioGraphic: number; euiContrastRatioDisabled: number; euiAnimSlightBounce: string; euiAnimSlightResistance: string; euiAnimSpeedExtraFast: string; euiAnimSpeedFast: string; euiAnimSpeedNormal: string; euiAnimSpeedSlow: string; euiAnimSpeedExtraSlow: string; euiBorderWidthThin: string; euiBorderWidthThick: string; euiBorderColor: string; euiBorderRadius: string; euiBorderRadiusSmall: string; euiBorderThick: string; euiBorderThin: string; euiBorderEditable: string; euiButtonHeight: string; euiButtonHeightSmall: string; euiButtonHeightXSmall: string; euiButtonColorDisabled: string; euiButtonColorDisabledText: string; euiButtonColorGhostDisabled: string; euiButtonTypes: { primary: string; accent: string; success: string; warning: string; danger: string; subdued: string; ghost: string; text: string; }; euiCodeBlockBackgroundColor: string; euiCodeBlockColor: string; euiCodeBlockSelectedBackgroundColor: string; euiCodeBlockCommentColor: string; euiCodeBlockSelectorTagColor: string; euiCodeBlockStringColor: string; euiCodeBlockTagColor: string; euiCodeBlockNameColor: string; euiCodeBlockNumberColor: string; euiCodeBlockKeywordColor: string; euiCodeBlockFunctionTitleColor: string; euiCodeBlockTypeColor: string; euiCodeBlockAttributeColor: string; euiCodeBlockSymbolColor: string; euiCodeBlockParamsColor: string; euiCodeBlockMetaColor: string; euiCodeBlockTitleColor: string; euiCodeBlockSectionColor: string; euiCodeBlockAdditionColor: string; euiCodeBlockDeletionColor: string; euiCodeBlockSelectorClassColor: string; euiCodeBlockSelectorIdColor: string; euiPaletteColorBlind: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; }; euiPaletteColorBlindKeys: string; euiColorVis0: string; euiColorVis1: string; euiColorVis2: string; euiColorVis3: string; euiColorVis4: string; euiColorVis5: string; euiColorVis6: string; euiColorVis7: string; euiColorVis8: string; euiColorVis9: string; euiColorVis0_behindText: string; euiColorVis1_behindText: string; euiColorVis2_behindText: string; euiColorVis3_behindText: string; euiColorVis4_behindText: string; euiColorVis5_behindText: string; euiColorVis6_behindText: string; euiColorVis7_behindText: string; euiColorVis8_behindText: string; euiColorVis9_behindText: string; euiFontWeightLight: number; euiFontWeightRegular: number; euiFontWeightMedium: number; euiFontWeightSemiBold: number; euiFontWeightBold: number; euiCodeFontWeightRegular: number; euiCodeFontWeightBold: number; euiFormMaxWidth: string; euiFormControlHeight: string; euiFormControlCompressedHeight: string; euiFormControlPadding: string; euiFormControlCompressedPadding: string; euiFormControlBorderRadius: string; euiFormControlCompressedBorderRadius: string; euiRadioSize: string; euiCheckBoxSize: string; euiCheckboxBorderRadius: string; euiSwitchHeight: string; euiSwitchWidth: string; euiSwitchThumbSize: string; euiSwitchIconHeight: string; euiSwitchHeightCompressed: string; euiSwitchWidthCompressed: string; euiSwitchThumbSizeCompressed: string; euiSwitchHeightMini: string; euiSwitchWidthMini: string; euiSwitchThumbSizeMini: string; euiFormBackgroundColor: string; euiFormBackgroundDisabledColor: string; euiFormBackgroundReadOnlyColor: string; euiFormBorderOpaqueColor: string; euiFormBorderColor: string; euiFormBorderDisabledColor: string; euiFormCustomControlDisabledIconColor: string; euiFormCustomControlBorderColor: string; euiFormControlDisabledColor: string; euiFormControlBoxShadow: string; euiFormControlPlaceholderText: string; euiFormInputGroupLabelBackground: string; euiFormInputGroupBorder: string; euiSwitchOffColor: string; euiFormControlLayoutGroupInputHeight: string; euiFormControlLayoutGroupInputCompressedHeight: string; euiFormControlLayoutGroupInputCompressedBorderRadius: string; euiRangeTrackColor: string; euiRangeThumbRadius: string; euiRangeThumbHeight: string; euiRangeThumbWidth: string; euiRangeThumbBorderColor: string; euiRangeTrackWidth: string; euiRangeTrackHeight: string; euiRangeTrackBorderWidth: number; euiRangeTrackBorderColor: string; euiRangeTrackRadius: string; euiRangeDisabledOpacity: number; euiRangeHighlightHeight: string; euiHeaderBackgroundColor: string; euiHeaderDarkBackgroundColor: string; euiHeaderBorderColor: string; euiHeaderBreadcrumbColor: string; euiHeaderHeight: string; euiHeaderChildSize: string; euiHeaderHeightCompensation: string; euiPageDefaultMaxWidth: string; euiPageSidebarMinWidth: string; euiPanelPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiPanelBorderRadiusModifiers: { borderRadiusNone: number; borderRadiusMedium: string; }; euiPanelBackgroundColorModifiers: { transparent: string; plain: string; subdued: string; accent: string; primary: string; success: string; warning: string; danger: string; }; euiBreakpoints: { xs: number; s: string; m: string; l: string; xl: string; }; euiBreakpointKeys: string; euiShadowColor: string; euiShadowColorLarge: string; euiSize: string; euiSizeXS: string; euiSizeS: string; euiSizeM: string; euiSizeL: string; euiSizeXL: string; euiSizeXXL: string; euiButtonMinWidth: string; euiScrollBar: string; euiScrollBarCorner: string; euiScrollBarCornerThin: string; euiFocusRingColor: string; euiFocusRingAnimStartColor: string; euiFocusRingAnimStartSize: string; euiFocusRingAnimStartSizeLarge: string; euiFocusRingSizeLarge: string; euiFocusRingSize: string; euiFocusTransparency: number; euiFocusTransparencyPercent: string; euiFocusBackgroundColor: string; euiTooltipBackgroundColor: string; euiTooltipBorderColor: string; euiTooltipAnimations: { top: string; left: string; bottom: string; right: string; }; euiFontFamily: string; euiCodeFontFamily: string; euiFontFeatureSettings: string; euiTextScale: string; euiFontSize: string; euiFontSizeXS: string; euiFontSizeS: string; euiFontSizeM: string; euiFontSizeL: string; euiFontSizeXL: string; euiFontSizeXXL: string; euiLineHeight: number; euiBodyLineHeight: number; euiTitles: { xxxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; s: { 'font-size': string; 'line-height': string; 'font-weight': number; }; m: { 'font-size': string; 'line-height': string; 'font-weight': number; }; l: { 'font-size': string; 'line-height': string; 'font-weight': number; }; }; euiZLevel0: number; euiZLevel1: number; euiZLevel2: number; euiZLevel3: number; euiZLevel4: number; euiZLevel5: number; euiZLevel6: number; euiZLevel7: number; euiZLevel8: number; euiZLevel9: number; euiZToastList: number; euiZModal: number; euiZMask: number; euiZNavigation: number; euiZContentMenu: number; euiZHeader: number; euiZFlyout: number; euiZMaskBelowHeader: number; euiZContent: number; euiColorGhost: string; euiColorInk: string; euiColorPrimary: string; euiColorAccent: string; euiColorSuccess: string; euiColorWarning: string; euiColorDanger: string; euiColorEmptyShade: string; euiColorLightestShade: string; euiColorLightShade: string; euiColorMediumShade: string; euiColorDarkShade: string; euiColorDarkestShade: string; euiColorFullShade: string; euiPageBackgroundColor: string; euiColorHighlight: string; euiTextColor: string; euiTitleColor: string; euiTextSubduedColor: string; euiColorDisabled: string; euiColorPrimaryText: string; euiColorSuccessText: string; euiColorAccentText: string; euiColorWarningText: string; euiColorDangerText: string; euiColorDisabledText: string; euiLinkColor: string; euiColorChartLines: string; euiColorChartBand: string; euiDatePickerCalendarWidth: string; euiDatePickerPadding: string; euiDatePickerGap: string; euiDatePickerCalendarColumns: number; euiDatePickerButtonSize: string; euiDatePickerMinControlWidth: string; euiDatePickerMaxControlWidth: string; euiButtonDefaultTransparency: number; euiButtonFontWeight: number; euiRangeHighlightColor: string; euiRangeThumbBackgroundColor: string; euiRangeTrackCompressedHeight: string; euiRangeHighlightCompressedHeight: string; euiRangeHeight: string; euiRangeCompressedHeight: string; euiStepStatusColors: { default: string; complete: string; warning: string; danger: string; }; }" + "{ paddingSizes: { xs: string; s: string; m: string; l: string; xl: string; }; euiBadgeGroupGutterTypes: { gutterExtraSmall: string; gutterSmall: string; }; euiBreadcrumbSpacing: string; euiBreadcrumbTruncateWidth: string; euiButtonEmptyTypes: { primary: string; danger: string; disabled: string; ghost: string; text: string; success: string; warning: string; }; euiCallOutTypes: { primary: string; success: string; warning: string; danger: string; }; euiCardSpacing: string; euiCardBottomNodeHeight: string; euiCardSelectButtonBorders: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardSelectButtonBackgrounds: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCheckableCardPadding: string; euiCodeBlockPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCollapsibleNavGroupLightBackgroundColor: string; euiCollapsibleNavGroupDarkBackgroundColor: string; euiCollapsibleNavGroupDarkHighContrastColor: string; euiColorPickerValueRange0: string; euiColorPickerValueRange1: string; euiColorPickerSaturationRange0: string; euiColorPickerSaturationRange1: string; euiColorPickerIndicatorSize: string; euiColorPickerWidth: string; euiColorPaletteDisplaySizes: { sizeExtraSmall: string; sizeSmall: string; sizeMedium: string; }; euiContextMenuWidth: string; euiControlBarBackground: string; euiControlBarText: string; euiControlBarBorderColor: string; euiControlBarInitialHeight: string; euiControlBarMaxHeight: string; euiControlBarHeights: { s: string; m: string; l: string; }; euiDataGridPrefix: string; euiDataGridStyles: string; euiZDataGrid: number; euiZHeaderBelowDataGrid: number; euiZDataGridCellPopover: number; euiDataGridColumnResizerWidth: string; euiDataGridPopoverMaxHeight: string; euiDataGridCellPaddingS: string; euiDataGridCellPaddingM: string; euiDataGridCellPaddingL: string; euiDataGridVerticalBorder: string; euiSuperDatePickerWidth: string; euiSuperDatePickerButtonWidth: string; euiDragAndDropSpacing: { s: string; m: string; l: string; }; euiExpressionColors: { subdued: string; primary: string; success: string; warning: string; danger: string; accent: string; }; euiFacetGutterSizes: { gutterNone: number; gutterSmall: string; gutterMedium: string; gutterLarge: string; }; gutterTypes: { gutterExtraSmall: string; gutterSmall: string; gutterMedium: string; gutterLarge: string; gutterExtraLarge: string; }; fractions: { fourths: { percentage: string; count: number; }; thirds: { percentage: string; count: number; }; halves: { percentage: string; count: number; }; single: { percentage: string; count: number; }; }; flyoutSizes: { small: { min: string; width: string; max: string; }; medium: { min: string; width: string; max: string; }; large: { min: string; width: string; max: string; }; }; euiFlyoutBorder: string; euiFlyoutPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiFilePickerTallHeight: string; euiRangeLevelColors: { primary: string; success: string; warning: string; danger: string; }; textareaResizing: { vertical: string; horizontal: string; both: string; none: string; }; euiHeaderLinksGutterSizes: { gutterXS: string; gutterS: string; gutterM: string; gutterL: string; }; ruleMargins: { marginXSmall: string; marginSmall: string; marginMedium: string; marginLarge: string; marginXLarge: string; marginXXLarge: string; }; euiIconLoadingOpacity: number; euiIconColors: { accent: string; danger: string; ghost: string; primary: string; success: string; subdued: string; text: string; warning: string; inherit: string; }; euiIconSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiKeyPadMenuSize: string; euiKeyPadMenuMarginSize: string; euiLinkColors: { subdued: string; primary: string; success: string; accent: string; warning: string; danger: string; text: string; ghost: string; }; euiListGroupItemHoverBackground: string; euiListGroupItemHoverBackgroundGhost: string; euiListGroupGutterTypes: { gutterSmall: string; gutterMedium: string; }; euiListGroupItemColorTypes: { primary: string; text: string; subdued: string; ghost: string; }; euiListGroupItemSizeTypes: { xSmall: string; small: string; medium: string; large: string; }; euiGradientStartStop: string; euiGradientMiddle: string; euiLoadingSpinnerSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiMarkdownEditorMinHeight: string; euiPopoverArrowSize: string; euiPopoverTranslateDistance: string; euiProgressSizes: { xs: string; s: string; m: string; l: string; }; euiProgressColors: { primary: string; success: string; warning: string; danger: string; accent: string; subdued: string; vis0: string; vis1: string; vis2: string; vis3: string; vis4: string; vis5: string; vis6: string; vis7: string; vis8: string; vis9: string; customColor: string; }; euiResizableButtonTransitionSpeed: string; euiResizableButtonSize: string; euiSelectableListItemBorder: string; euiSelectableListItemPadding: string; euiSelectableTemplateSitewideTypes: { application: { color: string; 'font-weight': number; }; deployment: { color: string; 'font-weight': number; }; article: { color: string; 'font-weight': number; }; case: { color: string; 'font-weight': number; }; platform: { color: string; 'font-weight': number; }; }; euiSideNavEmphasizedBackgroundColor: string; euiSideNavRootTextcolor: string; euiSideNavBranchTextcolor: string; euiSideNavSelectedTextcolor: string; euiSideNavDisabledTextcolor: string; spacerSizes: { xs: string; s: string; m: string; l: string; xl: string; xxl: string; }; euiStepNumberSize: string; euiStepNumberSmallSize: string; euiStepNumberMargin: string; euiStepStatusColorsToFade: { warning: string; danger: string; disabled: string; incomplete: string; }; euiSuggestItemColors: { tint0: string; tint1: string; tint2: string; tint3: string; tint4: string; tint5: string; tint6: string; tint7: string; tint8: string; tint9: string; tint10: string; }; euiTableCellContentPadding: string; euiTableCellContentPaddingCompressed: string; euiTableCellCheckboxWidth: string; euiTableActionsAreaWidth: string; euiTableHoverColor: string; euiTableSelectedColor: string; euiTableHoverSelectedColor: string; euiTableActionsBorderColor: string; euiTableHoverClickableColor: string; euiTableFocusClickableColor: string; euiTextColors: { default: string; subdued: string; success: string; accent: string; warning: string; danger: string; ghost: string; inherit: string; }; euiTextConstrainedMaxWidth: string; euiToastWidth: string; euiToastTypes: { primary: string; success: string; warning: string; danger: string; }; euiTokenGrayColor: string; euiTokenTypes: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; gray: { graphic: string; behindText: string; }; }; euiTokenTypeKeys: string; euiContrastRatioText: number; euiContrastRatioGraphic: number; euiContrastRatioDisabled: number; euiAnimSlightBounce: string; euiAnimSlightResistance: string; euiAnimSpeedExtraFast: string; euiAnimSpeedFast: string; euiAnimSpeedNormal: string; euiAnimSpeedSlow: string; euiAnimSpeedExtraSlow: string; euiBorderWidthThin: string; euiBorderWidthThick: string; euiBorderColor: string; euiBorderRadius: string; euiBorderRadiusSmall: string; euiBorderThick: string; euiBorderThin: string; euiBorderEditable: string; euiButtonHeight: string; euiButtonHeightSmall: string; euiButtonHeightXSmall: string; euiButtonColorDisabled: string; euiButtonColorDisabledText: string; euiButtonColorGhostDisabled: string; euiButtonTypes: { primary: string; accent: string; success: string; warning: string; danger: string; subdued: string; ghost: string; text: string; }; euiCodeBlockBackgroundColor: string; euiCodeBlockColor: string; euiCodeBlockSelectedBackgroundColor: string; euiCodeBlockCommentColor: string; euiCodeBlockSelectorTagColor: string; euiCodeBlockStringColor: string; euiCodeBlockTagColor: string; euiCodeBlockNameColor: string; euiCodeBlockNumberColor: string; euiCodeBlockKeywordColor: string; euiCodeBlockFunctionTitleColor: string; euiCodeBlockTypeColor: string; euiCodeBlockAttributeColor: string; euiCodeBlockSymbolColor: string; euiCodeBlockParamsColor: string; euiCodeBlockMetaColor: string; euiCodeBlockTitleColor: string; euiCodeBlockSectionColor: string; euiCodeBlockAdditionColor: string; euiCodeBlockDeletionColor: string; euiCodeBlockSelectorClassColor: string; euiCodeBlockSelectorIdColor: string; euiPaletteColorBlind: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; }; euiPaletteColorBlindKeys: string; euiColorVis0: string; euiColorVis1: string; euiColorVis2: string; euiColorVis3: string; euiColorVis4: string; euiColorVis5: string; euiColorVis6: string; euiColorVis7: string; euiColorVis8: string; euiColorVis9: string; euiColorVis0_behindText: string; euiColorVis1_behindText: string; euiColorVis2_behindText: string; euiColorVis3_behindText: string; euiColorVis4_behindText: string; euiColorVis5_behindText: string; euiColorVis6_behindText: string; euiColorVis7_behindText: string; euiColorVis8_behindText: string; euiColorVis9_behindText: string; euiFontWeightLight: number; euiFontWeightRegular: number; euiFontWeightMedium: number; euiFontWeightSemiBold: number; euiFontWeightBold: number; euiCodeFontWeightRegular: number; euiCodeFontWeightBold: number; euiFormMaxWidth: string; euiFormControlHeight: string; euiFormControlCompressedHeight: string; euiFormControlPadding: string; euiFormControlCompressedPadding: string; euiFormControlBorderRadius: string; euiFormControlCompressedBorderRadius: string; euiRadioSize: string; euiCheckBoxSize: string; euiCheckboxBorderRadius: string; euiSwitchHeight: string; euiSwitchWidth: string; euiSwitchThumbSize: string; euiSwitchIconHeight: string; euiSwitchHeightCompressed: string; euiSwitchWidthCompressed: string; euiSwitchThumbSizeCompressed: string; euiSwitchHeightMini: string; euiSwitchWidthMini: string; euiSwitchThumbSizeMini: string; euiFormBackgroundColor: string; euiFormBackgroundDisabledColor: string; euiFormBackgroundReadOnlyColor: string; euiFormBorderOpaqueColor: string; euiFormBorderColor: string; euiFormBorderDisabledColor: string; euiFormCustomControlDisabledIconColor: string; euiFormCustomControlBorderColor: string; euiFormControlDisabledColor: string; euiFormControlBoxShadow: string; euiFormControlPlaceholderText: string; euiFormInputGroupLabelBackground: string; euiFormInputGroupBorder: string; euiSwitchOffColor: string; euiFormControlLayoutGroupInputHeight: string; euiFormControlLayoutGroupInputCompressedHeight: string; euiFormControlLayoutGroupInputCompressedBorderRadius: string; euiRangeTrackColor: string; euiRangeThumbRadius: string; euiRangeThumbHeight: string; euiRangeThumbWidth: string; euiRangeThumbBorderColor: string; euiRangeTrackWidth: string; euiRangeTrackHeight: string; euiRangeTrackBorderWidth: number; euiRangeTrackBorderColor: string; euiRangeTrackRadius: string; euiRangeDisabledOpacity: number; euiRangeHighlightHeight: string; euiHeaderBackgroundColor: string; euiHeaderDarkBackgroundColor: string; euiHeaderBorderColor: string; euiHeaderBreadcrumbColor: string; euiHeaderHeight: string; euiHeaderChildSize: string; euiHeaderHeightCompensation: string; euiPageDefaultMaxWidth: string; euiPageSidebarMinWidth: string; euiPanelPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiPanelBorderRadiusModifiers: { borderRadiusNone: number; borderRadiusMedium: string; }; euiPanelBackgroundColorModifiers: { transparent: string; plain: string; subdued: string; accent: string; primary: string; success: string; warning: string; danger: string; }; euiBreakpoints: { xs: number; s: string; m: string; l: string; xl: string; }; euiBreakpointKeys: string; euiShadowColor: string; euiShadowColorLarge: string; euiSize: string; euiSizeXS: string; euiSizeS: string; euiSizeM: string; euiSizeL: string; euiSizeXL: string; euiSizeXXL: string; euiButtonMinWidth: string; euiScrollBar: string; euiScrollBarCorner: string; euiScrollBarCornerThin: string; euiFocusRingColor: string; euiFocusRingAnimStartColor: string; euiFocusRingAnimStartSize: string; euiFocusRingAnimStartSizeLarge: string; euiFocusRingSizeLarge: string; euiFocusRingSize: string; euiFocusTransparency: number; euiFocusTransparencyPercent: string; euiFocusBackgroundColor: string; euiTooltipBackgroundColor: string; euiTooltipBorderColor: string; euiTooltipAnimations: { top: string; left: string; bottom: string; right: string; }; euiFontFamily: string; euiCodeFontFamily: string; euiFontFeatureSettings: string; euiTextScale: string; euiFontSize: string; euiFontSizeXS: string; euiFontSizeS: string; euiFontSizeM: string; euiFontSizeL: string; euiFontSizeXL: string; euiFontSizeXXL: string; euiLineHeight: number; euiBodyLineHeight: number; euiTitles: { xxxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; s: { 'font-size': string; 'line-height': string; 'font-weight': number; }; m: { 'font-size': string; 'line-height': string; 'font-weight': number; }; l: { 'font-size': string; 'line-height': string; 'font-weight': number; }; }; euiZLevel0: number; euiZLevel1: number; euiZLevel2: number; euiZLevel3: number; euiZLevel4: number; euiZLevel5: number; euiZLevel6: number; euiZLevel7: number; euiZLevel8: number; euiZLevel9: number; euiZToastList: number; euiZModal: number; euiZMask: number; euiZNavigation: number; euiZContentMenu: number; euiZHeader: number; euiZFlyout: number; euiZMaskBelowHeader: number; euiZContent: number; euiColorGhost: string; euiColorInk: string; euiColorPrimary: string; euiColorAccent: string; euiColorSuccess: string; euiColorWarning: string; euiColorDanger: string; euiColorEmptyShade: string; euiColorLightestShade: string; euiColorLightShade: string; euiColorMediumShade: string; euiColorDarkShade: string; euiColorDarkestShade: string; euiColorFullShade: string; euiPageBackgroundColor: string; euiColorHighlight: string; euiTextColor: string; euiTitleColor: string; euiTextSubduedColor: string; euiColorDisabled: string; euiColorPrimaryText: string; euiColorSuccessText: string; euiColorAccentText: string; euiColorWarningText: string; euiColorDangerText: string; euiColorDisabledText: string; euiLinkColor: string; euiColorChartLines: string; euiColorChartBand: string; euiDatePickerCalendarWidth: string; euiDatePickerPadding: string; euiDatePickerGap: string; euiDatePickerCalendarColumns: number; euiDatePickerButtonSize: string; euiDatePickerMinControlWidth: string; euiDatePickerMaxControlWidth: string; euiButtonDefaultTransparency: number; euiButtonFontWeight: number; euiRangeHighlightColor: string; euiRangeThumbBackgroundColor: string; euiRangeTrackCompressedHeight: string; euiRangeHighlightCompressedHeight: string; euiRangeHeight: string; euiRangeCompressedHeight: string; euiStepStatusColors: { default: string; complete: string; warning: string; danger: string; }; }" ], "path": "packages/kbn-ui-theme/src/theme.ts", "deprecated": false, @@ -112,7 +112,7 @@ "\nEUI Theme vars that automatically adjust to light/dark theme" ], "signature": [ - "{ paddingSizes: { xs: string; s: string; m: string; l: string; xl: string; }; avatarSizing: { s: { size: string; 'font-size': string; }; m: { size: string; 'font-size': string; }; l: { size: string; 'font-size': string; }; xl: { size: string; 'font-size': string; }; }; euiBadgeGroupGutterTypes: { gutterExtraSmall: string; gutterSmall: string; }; euiBreadcrumbSpacing: string; euiBreadcrumbTruncateWidth: string; euiButtonEmptyTypes: { primary: string; danger: string; disabled: string; ghost: string; text: string; success: string; warning: string; }; euiCallOutTypes: { primary: string; success: string; warning: string; danger: string; }; euiCardSpacing: string; euiCardBottomNodeHeight: string; euiCardSelectButtonBorders: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardSelectButtonBackgrounds: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCheckableCardPadding: string; euiCodeBlockPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCollapsibleNavGroupLightBackgroundColor: string; euiCollapsibleNavGroupDarkBackgroundColor: string; euiCollapsibleNavGroupDarkHighContrastColor: string; euiColorPickerValueRange0: string; euiColorPickerValueRange1: string; euiColorPickerSaturationRange0: string; euiColorPickerSaturationRange1: string; euiColorPickerIndicatorSize: string; euiColorPickerWidth: string; euiColorPaletteDisplaySizes: { sizeExtraSmall: string; sizeSmall: string; sizeMedium: string; }; euiContextMenuWidth: string; euiControlBarBackground: string; euiControlBarText: string; euiControlBarBorderColor: string; euiControlBarInitialHeight: string; euiControlBarMaxHeight: string; euiControlBarHeights: { s: string; m: string; l: string; }; euiDataGridPrefix: string; euiDataGridStyles: string; euiZDataGrid: number; euiZHeaderBelowDataGrid: number; euiZDataGridCellPopover: number; euiDataGridColumnResizerWidth: string; euiDataGridPopoverMaxHeight: string; euiDataGridCellPaddingS: string; euiDataGridCellPaddingM: string; euiDataGridCellPaddingL: string; euiDataGridVerticalBorder: string; euiSuperDatePickerWidth: string; euiSuperDatePickerButtonWidth: string; euiDragAndDropSpacing: { s: string; m: string; l: string; }; euiExpressionColors: { subdued: string; primary: string; success: string; warning: string; danger: string; accent: string; }; euiFacetGutterSizes: { gutterNone: number; gutterSmall: string; gutterMedium: string; gutterLarge: string; }; gutterTypes: { gutterExtraSmall: string; gutterSmall: string; gutterMedium: string; gutterLarge: string; gutterExtraLarge: string; }; fractions: { fourths: { percentage: string; count: number; }; thirds: { percentage: string; count: number; }; halves: { percentage: string; count: number; }; single: { percentage: string; count: number; }; }; flyoutSizes: { small: { min: string; width: string; max: string; }; medium: { min: string; width: string; max: string; }; large: { min: string; width: string; max: string; }; }; euiFlyoutBorder: string; euiFlyoutPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiFilePickerTallHeight: string; euiRangeLevelColors: { primary: string; success: string; warning: string; danger: string; }; textareaResizing: { vertical: string; horizontal: string; both: string; none: string; }; euiHeaderLinksGutterSizes: { gutterXS: string; gutterS: string; gutterM: string; gutterL: string; }; ruleMargins: { marginXSmall: string; marginSmall: string; marginMedium: string; marginLarge: string; marginXLarge: string; marginXXLarge: string; }; euiIconLoadingOpacity: number; euiIconColors: { accent: string; danger: string; ghost: string; primary: string; success: string; subdued: string; text: string; warning: string; inherit: string; }; euiIconSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiKeyPadMenuSize: string; euiKeyPadMenuMarginSize: string; euiLinkColors: { subdued: string; primary: string; success: string; accent: string; warning: string; danger: string; text: string; ghost: string; }; euiListGroupItemHoverBackground: string; euiListGroupItemHoverBackgroundGhost: string; euiListGroupGutterTypes: { gutterSmall: string; gutterMedium: string; }; euiListGroupItemColorTypes: { primary: string; text: string; subdued: string; ghost: string; }; euiListGroupItemSizeTypes: { xSmall: string; small: string; medium: string; large: string; }; euiGradientStartStop: string; euiGradientMiddle: string; euiLoadingSpinnerSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiMarkdownEditorMinHeight: string; euiPopoverArrowSize: string; euiPopoverTranslateDistance: string; euiProgressSizes: { xs: string; s: string; m: string; l: string; }; euiProgressColors: { primary: string; success: string; warning: string; danger: string; accent: string; subdued: string; vis0: string; vis1: string; vis2: string; vis3: string; vis4: string; vis5: string; vis6: string; vis7: string; vis8: string; vis9: string; customColor: string; }; euiResizableButtonTransitionSpeed: string; euiResizableButtonSize: string; euiSelectableListItemBorder: string; euiSelectableListItemPadding: string; euiSelectableTemplateSitewideTypes: { application: { color: string; 'font-weight': number; }; deployment: { color: string; 'font-weight': number; }; article: { color: string; 'font-weight': number; }; case: { color: string; 'font-weight': number; }; platform: { color: string; 'font-weight': number; }; }; euiSideNavEmphasizedBackgroundColor: string; euiSideNavRootTextcolor: string; euiSideNavBranchTextcolor: string; euiSideNavSelectedTextcolor: string; euiSideNavDisabledTextcolor: string; spacerSizes: { xs: string; s: string; m: string; l: string; xl: string; xxl: string; }; euiStepNumberSize: string; euiStepNumberSmallSize: string; euiStepNumberMargin: string; euiStepStatusColorsToFade: { warning: string; danger: string; disabled: string; incomplete: string; }; euiSuggestItemColors: { tint0: string; tint1: string; tint2: string; tint3: string; tint4: string; tint5: string; tint6: string; tint7: string; tint8: string; tint9: string; tint10: string; }; euiTableCellContentPadding: string; euiTableCellContentPaddingCompressed: string; euiTableCellCheckboxWidth: string; euiTableActionsAreaWidth: string; euiTableHoverColor: string; euiTableSelectedColor: string; euiTableHoverSelectedColor: string; euiTableActionsBorderColor: string; euiTableHoverClickableColor: string; euiTableFocusClickableColor: string; euiTextColors: { default: string; subdued: string; success: string; accent: string; warning: string; danger: string; ghost: string; inherit: string; }; euiTextConstrainedMaxWidth: string; euiToastWidth: string; euiToastTypes: { primary: string; success: string; warning: string; danger: string; }; euiTokenGrayColor: string; euiTokenTypes: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; gray: { graphic: string; behindText: string; }; }; euiTokenTypeKeys: string; euiContrastRatioText: number; euiContrastRatioGraphic: number; euiContrastRatioDisabled: number; euiAnimSlightBounce: string; euiAnimSlightResistance: string; euiAnimSpeedExtraFast: string; euiAnimSpeedFast: string; euiAnimSpeedNormal: string; euiAnimSpeedSlow: string; euiAnimSpeedExtraSlow: string; euiBorderWidthThin: string; euiBorderWidthThick: string; euiBorderColor: string; euiBorderRadius: string; euiBorderRadiusSmall: string; euiBorderThick: string; euiBorderThin: string; euiBorderEditable: string; euiButtonHeight: string; euiButtonHeightSmall: string; euiButtonHeightXSmall: string; euiButtonColorDisabled: string; euiButtonColorDisabledText: string; euiButtonColorGhostDisabled: string; euiButtonTypes: { primary: string; accent: string; success: string; warning: string; danger: string; subdued: string; ghost: string; text: string; }; euiCodeBlockBackgroundColor: string; euiCodeBlockColor: string; euiCodeBlockSelectedBackgroundColor: string; euiCodeBlockCommentColor: string; euiCodeBlockSelectorTagColor: string; euiCodeBlockStringColor: string; euiCodeBlockTagColor: string; euiCodeBlockNameColor: string; euiCodeBlockNumberColor: string; euiCodeBlockKeywordColor: string; euiCodeBlockFunctionTitleColor: string; euiCodeBlockTypeColor: string; euiCodeBlockAttributeColor: string; euiCodeBlockSymbolColor: string; euiCodeBlockParamsColor: string; euiCodeBlockMetaColor: string; euiCodeBlockTitleColor: string; euiCodeBlockSectionColor: string; euiCodeBlockAdditionColor: string; euiCodeBlockDeletionColor: string; euiCodeBlockSelectorClassColor: string; euiCodeBlockSelectorIdColor: string; euiPaletteColorBlind: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; }; euiPaletteColorBlindKeys: string; euiColorVis0: string; euiColorVis1: string; euiColorVis2: string; euiColorVis3: string; euiColorVis4: string; euiColorVis5: string; euiColorVis6: string; euiColorVis7: string; euiColorVis8: string; euiColorVis9: string; euiColorVis0_behindText: string; euiColorVis1_behindText: string; euiColorVis2_behindText: string; euiColorVis3_behindText: string; euiColorVis4_behindText: string; euiColorVis5_behindText: string; euiColorVis6_behindText: string; euiColorVis7_behindText: string; euiColorVis8_behindText: string; euiColorVis9_behindText: string; euiFontWeightLight: number; euiFontWeightRegular: number; euiFontWeightMedium: number; euiFontWeightSemiBold: number; euiFontWeightBold: number; euiCodeFontWeightRegular: number; euiCodeFontWeightBold: number; euiFormMaxWidth: string; euiFormControlHeight: string; euiFormControlCompressedHeight: string; euiFormControlPadding: string; euiFormControlCompressedPadding: string; euiFormControlBorderRadius: string; euiFormControlCompressedBorderRadius: string; euiRadioSize: string; euiCheckBoxSize: string; euiCheckboxBorderRadius: string; euiSwitchHeight: string; euiSwitchWidth: string; euiSwitchThumbSize: string; euiSwitchIconHeight: string; euiSwitchHeightCompressed: string; euiSwitchWidthCompressed: string; euiSwitchThumbSizeCompressed: string; euiSwitchHeightMini: string; euiSwitchWidthMini: string; euiSwitchThumbSizeMini: string; euiFormBackgroundColor: string; euiFormBackgroundDisabledColor: string; euiFormBackgroundReadOnlyColor: string; euiFormBorderOpaqueColor: string; euiFormBorderColor: string; euiFormBorderDisabledColor: string; euiFormCustomControlDisabledIconColor: string; euiFormCustomControlBorderColor: string; euiFormControlDisabledColor: string; euiFormControlBoxShadow: string; euiFormControlPlaceholderText: string; euiFormInputGroupLabelBackground: string; euiFormInputGroupBorder: string; euiSwitchOffColor: string; euiFormControlLayoutGroupInputHeight: string; euiFormControlLayoutGroupInputCompressedHeight: string; euiFormControlLayoutGroupInputCompressedBorderRadius: string; euiRangeTrackColor: string; euiRangeThumbRadius: string; euiRangeThumbHeight: string; euiRangeThumbWidth: string; euiRangeThumbBorderColor: string; euiRangeTrackWidth: string; euiRangeTrackHeight: string; euiRangeTrackBorderWidth: number; euiRangeTrackBorderColor: string; euiRangeTrackRadius: string; euiRangeDisabledOpacity: number; euiRangeHighlightHeight: string; euiHeaderBackgroundColor: string; euiHeaderDarkBackgroundColor: string; euiHeaderBorderColor: string; euiHeaderBreadcrumbColor: string; euiHeaderHeight: string; euiHeaderChildSize: string; euiHeaderHeightCompensation: string; euiPageDefaultMaxWidth: string; euiPageSidebarMinWidth: string; euiPanelPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiPanelBorderRadiusModifiers: { borderRadiusNone: number; borderRadiusMedium: string; }; euiPanelBackgroundColorModifiers: { transparent: string; plain: string; subdued: string; accent: string; primary: string; success: string; warning: string; danger: string; }; euiBreakpoints: { xs: number; s: string; m: string; l: string; xl: string; }; euiBreakpointKeys: string; euiShadowColor: string; euiShadowColorLarge: string; euiSize: string; euiSizeXS: string; euiSizeS: string; euiSizeM: string; euiSizeL: string; euiSizeXL: string; euiSizeXXL: string; euiButtonMinWidth: string; euiScrollBar: string; euiScrollBarCorner: string; euiScrollBarCornerThin: string; euiFocusRingColor: string; euiFocusRingAnimStartColor: string; euiFocusRingAnimStartSize: string; euiFocusRingAnimStartSizeLarge: string; euiFocusRingSizeLarge: string; euiFocusRingSize: string; euiFocusTransparency: number; euiFocusTransparencyPercent: string; euiFocusBackgroundColor: string; euiTooltipBackgroundColor: string; euiTooltipBorderColor: string; euiTooltipAnimations: { top: string; left: string; bottom: string; right: string; }; euiFontFamily: string; euiCodeFontFamily: string; euiFontFeatureSettings: string; euiTextScale: string; euiFontSize: string; euiFontSizeXS: string; euiFontSizeS: string; euiFontSizeM: string; euiFontSizeL: string; euiFontSizeXL: string; euiFontSizeXXL: string; euiLineHeight: number; euiBodyLineHeight: number; euiTitles: { xxxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; s: { 'font-size': string; 'line-height': string; 'font-weight': number; }; m: { 'font-size': string; 'line-height': string; 'font-weight': number; }; l: { 'font-size': string; 'line-height': string; 'font-weight': number; }; }; euiZLevel0: number; euiZLevel1: number; euiZLevel2: number; euiZLevel3: number; euiZLevel4: number; euiZLevel5: number; euiZLevel6: number; euiZLevel7: number; euiZLevel8: number; euiZLevel9: number; euiZToastList: number; euiZModal: number; euiZMask: number; euiZNavigation: number; euiZContentMenu: number; euiZHeader: number; euiZFlyout: number; euiZMaskBelowHeader: number; euiZContent: number; euiColorGhost: string; euiColorInk: string; euiColorPrimary: string; euiColorAccent: string; euiColorSuccess: string; euiColorWarning: string; euiColorDanger: string; euiColorEmptyShade: string; euiColorLightestShade: string; euiColorLightShade: string; euiColorMediumShade: string; euiColorDarkShade: string; euiColorDarkestShade: string; euiColorFullShade: string; euiPageBackgroundColor: string; euiColorHighlight: string; euiTextColor: string; euiTitleColor: string; euiTextSubduedColor: string; euiColorDisabled: string; euiColorPrimaryText: string; euiColorSuccessText: string; euiColorAccentText: string; euiColorWarningText: string; euiColorDangerText: string; euiColorDisabledText: string; euiLinkColor: string; euiColorChartLines: string; euiColorChartBand: string; euiDatePickerCalendarWidth: string; euiDatePickerPadding: string; euiDatePickerGap: string; euiDatePickerCalendarColumns: number; euiDatePickerButtonSize: string; euiDatePickerMinControlWidth: string; euiDatePickerMaxControlWidth: string; euiButtonDefaultTransparency: number; euiButtonFontWeight: number; euiRangeHighlightColor: string; euiRangeThumbBackgroundColor: string; euiRangeTrackCompressedHeight: string; euiRangeHighlightCompressedHeight: string; euiRangeHeight: string; euiRangeCompressedHeight: string; euiStepStatusColors: { default: string; complete: string; warning: string; danger: string; }; }" + "{ paddingSizes: { xs: string; s: string; m: string; l: string; xl: string; }; euiBadgeGroupGutterTypes: { gutterExtraSmall: string; gutterSmall: string; }; euiBreadcrumbSpacing: string; euiBreadcrumbTruncateWidth: string; euiButtonEmptyTypes: { primary: string; danger: string; disabled: string; ghost: string; text: string; success: string; warning: string; }; euiCallOutTypes: { primary: string; success: string; warning: string; danger: string; }; euiCardSpacing: string; euiCardBottomNodeHeight: string; euiCardSelectButtonBorders: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardSelectButtonBackgrounds: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCheckableCardPadding: string; euiCodeBlockPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCollapsibleNavGroupLightBackgroundColor: string; euiCollapsibleNavGroupDarkBackgroundColor: string; euiCollapsibleNavGroupDarkHighContrastColor: string; euiColorPickerValueRange0: string; euiColorPickerValueRange1: string; euiColorPickerSaturationRange0: string; euiColorPickerSaturationRange1: string; euiColorPickerIndicatorSize: string; euiColorPickerWidth: string; euiColorPaletteDisplaySizes: { sizeExtraSmall: string; sizeSmall: string; sizeMedium: string; }; euiContextMenuWidth: string; euiControlBarBackground: string; euiControlBarText: string; euiControlBarBorderColor: string; euiControlBarInitialHeight: string; euiControlBarMaxHeight: string; euiControlBarHeights: { s: string; m: string; l: string; }; euiDataGridPrefix: string; euiDataGridStyles: string; euiZDataGrid: number; euiZHeaderBelowDataGrid: number; euiZDataGridCellPopover: number; euiDataGridColumnResizerWidth: string; euiDataGridPopoverMaxHeight: string; euiDataGridCellPaddingS: string; euiDataGridCellPaddingM: string; euiDataGridCellPaddingL: string; euiDataGridVerticalBorder: string; euiSuperDatePickerWidth: string; euiSuperDatePickerButtonWidth: string; euiDragAndDropSpacing: { s: string; m: string; l: string; }; euiExpressionColors: { subdued: string; primary: string; success: string; warning: string; danger: string; accent: string; }; euiFacetGutterSizes: { gutterNone: number; gutterSmall: string; gutterMedium: string; gutterLarge: string; }; gutterTypes: { gutterExtraSmall: string; gutterSmall: string; gutterMedium: string; gutterLarge: string; gutterExtraLarge: string; }; fractions: { fourths: { percentage: string; count: number; }; thirds: { percentage: string; count: number; }; halves: { percentage: string; count: number; }; single: { percentage: string; count: number; }; }; flyoutSizes: { small: { min: string; width: string; max: string; }; medium: { min: string; width: string; max: string; }; large: { min: string; width: string; max: string; }; }; euiFlyoutBorder: string; euiFlyoutPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiFilePickerTallHeight: string; euiRangeLevelColors: { primary: string; success: string; warning: string; danger: string; }; textareaResizing: { vertical: string; horizontal: string; both: string; none: string; }; euiHeaderLinksGutterSizes: { gutterXS: string; gutterS: string; gutterM: string; gutterL: string; }; ruleMargins: { marginXSmall: string; marginSmall: string; marginMedium: string; marginLarge: string; marginXLarge: string; marginXXLarge: string; }; euiIconLoadingOpacity: number; euiIconColors: { accent: string; danger: string; ghost: string; primary: string; success: string; subdued: string; text: string; warning: string; inherit: string; }; euiIconSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiKeyPadMenuSize: string; euiKeyPadMenuMarginSize: string; euiLinkColors: { subdued: string; primary: string; success: string; accent: string; warning: string; danger: string; text: string; ghost: string; }; euiListGroupItemHoverBackground: string; euiListGroupItemHoverBackgroundGhost: string; euiListGroupGutterTypes: { gutterSmall: string; gutterMedium: string; }; euiListGroupItemColorTypes: { primary: string; text: string; subdued: string; ghost: string; }; euiListGroupItemSizeTypes: { xSmall: string; small: string; medium: string; large: string; }; euiGradientStartStop: string; euiGradientMiddle: string; euiLoadingSpinnerSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiMarkdownEditorMinHeight: string; euiPopoverArrowSize: string; euiPopoverTranslateDistance: string; euiProgressSizes: { xs: string; s: string; m: string; l: string; }; euiProgressColors: { primary: string; success: string; warning: string; danger: string; accent: string; subdued: string; vis0: string; vis1: string; vis2: string; vis3: string; vis4: string; vis5: string; vis6: string; vis7: string; vis8: string; vis9: string; customColor: string; }; euiResizableButtonTransitionSpeed: string; euiResizableButtonSize: string; euiSelectableListItemBorder: string; euiSelectableListItemPadding: string; euiSelectableTemplateSitewideTypes: { application: { color: string; 'font-weight': number; }; deployment: { color: string; 'font-weight': number; }; article: { color: string; 'font-weight': number; }; case: { color: string; 'font-weight': number; }; platform: { color: string; 'font-weight': number; }; }; euiSideNavEmphasizedBackgroundColor: string; euiSideNavRootTextcolor: string; euiSideNavBranchTextcolor: string; euiSideNavSelectedTextcolor: string; euiSideNavDisabledTextcolor: string; spacerSizes: { xs: string; s: string; m: string; l: string; xl: string; xxl: string; }; euiStepNumberSize: string; euiStepNumberSmallSize: string; euiStepNumberMargin: string; euiStepStatusColorsToFade: { warning: string; danger: string; disabled: string; incomplete: string; }; euiSuggestItemColors: { tint0: string; tint1: string; tint2: string; tint3: string; tint4: string; tint5: string; tint6: string; tint7: string; tint8: string; tint9: string; tint10: string; }; euiTableCellContentPadding: string; euiTableCellContentPaddingCompressed: string; euiTableCellCheckboxWidth: string; euiTableActionsAreaWidth: string; euiTableHoverColor: string; euiTableSelectedColor: string; euiTableHoverSelectedColor: string; euiTableActionsBorderColor: string; euiTableHoverClickableColor: string; euiTableFocusClickableColor: string; euiTextColors: { default: string; subdued: string; success: string; accent: string; warning: string; danger: string; ghost: string; inherit: string; }; euiTextConstrainedMaxWidth: string; euiToastWidth: string; euiToastTypes: { primary: string; success: string; warning: string; danger: string; }; euiTokenGrayColor: string; euiTokenTypes: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; gray: { graphic: string; behindText: string; }; }; euiTokenTypeKeys: string; euiContrastRatioText: number; euiContrastRatioGraphic: number; euiContrastRatioDisabled: number; euiAnimSlightBounce: string; euiAnimSlightResistance: string; euiAnimSpeedExtraFast: string; euiAnimSpeedFast: string; euiAnimSpeedNormal: string; euiAnimSpeedSlow: string; euiAnimSpeedExtraSlow: string; euiBorderWidthThin: string; euiBorderWidthThick: string; euiBorderColor: string; euiBorderRadius: string; euiBorderRadiusSmall: string; euiBorderThick: string; euiBorderThin: string; euiBorderEditable: string; euiButtonHeight: string; euiButtonHeightSmall: string; euiButtonHeightXSmall: string; euiButtonColorDisabled: string; euiButtonColorDisabledText: string; euiButtonColorGhostDisabled: string; euiButtonTypes: { primary: string; accent: string; success: string; warning: string; danger: string; subdued: string; ghost: string; text: string; }; euiCodeBlockBackgroundColor: string; euiCodeBlockColor: string; euiCodeBlockSelectedBackgroundColor: string; euiCodeBlockCommentColor: string; euiCodeBlockSelectorTagColor: string; euiCodeBlockStringColor: string; euiCodeBlockTagColor: string; euiCodeBlockNameColor: string; euiCodeBlockNumberColor: string; euiCodeBlockKeywordColor: string; euiCodeBlockFunctionTitleColor: string; euiCodeBlockTypeColor: string; euiCodeBlockAttributeColor: string; euiCodeBlockSymbolColor: string; euiCodeBlockParamsColor: string; euiCodeBlockMetaColor: string; euiCodeBlockTitleColor: string; euiCodeBlockSectionColor: string; euiCodeBlockAdditionColor: string; euiCodeBlockDeletionColor: string; euiCodeBlockSelectorClassColor: string; euiCodeBlockSelectorIdColor: string; euiPaletteColorBlind: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; }; euiPaletteColorBlindKeys: string; euiColorVis0: string; euiColorVis1: string; euiColorVis2: string; euiColorVis3: string; euiColorVis4: string; euiColorVis5: string; euiColorVis6: string; euiColorVis7: string; euiColorVis8: string; euiColorVis9: string; euiColorVis0_behindText: string; euiColorVis1_behindText: string; euiColorVis2_behindText: string; euiColorVis3_behindText: string; euiColorVis4_behindText: string; euiColorVis5_behindText: string; euiColorVis6_behindText: string; euiColorVis7_behindText: string; euiColorVis8_behindText: string; euiColorVis9_behindText: string; euiFontWeightLight: number; euiFontWeightRegular: number; euiFontWeightMedium: number; euiFontWeightSemiBold: number; euiFontWeightBold: number; euiCodeFontWeightRegular: number; euiCodeFontWeightBold: number; euiFormMaxWidth: string; euiFormControlHeight: string; euiFormControlCompressedHeight: string; euiFormControlPadding: string; euiFormControlCompressedPadding: string; euiFormControlBorderRadius: string; euiFormControlCompressedBorderRadius: string; euiRadioSize: string; euiCheckBoxSize: string; euiCheckboxBorderRadius: string; euiSwitchHeight: string; euiSwitchWidth: string; euiSwitchThumbSize: string; euiSwitchIconHeight: string; euiSwitchHeightCompressed: string; euiSwitchWidthCompressed: string; euiSwitchThumbSizeCompressed: string; euiSwitchHeightMini: string; euiSwitchWidthMini: string; euiSwitchThumbSizeMini: string; euiFormBackgroundColor: string; euiFormBackgroundDisabledColor: string; euiFormBackgroundReadOnlyColor: string; euiFormBorderOpaqueColor: string; euiFormBorderColor: string; euiFormBorderDisabledColor: string; euiFormCustomControlDisabledIconColor: string; euiFormCustomControlBorderColor: string; euiFormControlDisabledColor: string; euiFormControlBoxShadow: string; euiFormControlPlaceholderText: string; euiFormInputGroupLabelBackground: string; euiFormInputGroupBorder: string; euiSwitchOffColor: string; euiFormControlLayoutGroupInputHeight: string; euiFormControlLayoutGroupInputCompressedHeight: string; euiFormControlLayoutGroupInputCompressedBorderRadius: string; euiRangeTrackColor: string; euiRangeThumbRadius: string; euiRangeThumbHeight: string; euiRangeThumbWidth: string; euiRangeThumbBorderColor: string; euiRangeTrackWidth: string; euiRangeTrackHeight: string; euiRangeTrackBorderWidth: number; euiRangeTrackBorderColor: string; euiRangeTrackRadius: string; euiRangeDisabledOpacity: number; euiRangeHighlightHeight: string; euiHeaderBackgroundColor: string; euiHeaderDarkBackgroundColor: string; euiHeaderBorderColor: string; euiHeaderBreadcrumbColor: string; euiHeaderHeight: string; euiHeaderChildSize: string; euiHeaderHeightCompensation: string; euiPageDefaultMaxWidth: string; euiPageSidebarMinWidth: string; euiPanelPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiPanelBorderRadiusModifiers: { borderRadiusNone: number; borderRadiusMedium: string; }; euiPanelBackgroundColorModifiers: { transparent: string; plain: string; subdued: string; accent: string; primary: string; success: string; warning: string; danger: string; }; euiBreakpoints: { xs: number; s: string; m: string; l: string; xl: string; }; euiBreakpointKeys: string; euiShadowColor: string; euiShadowColorLarge: string; euiSize: string; euiSizeXS: string; euiSizeS: string; euiSizeM: string; euiSizeL: string; euiSizeXL: string; euiSizeXXL: string; euiButtonMinWidth: string; euiScrollBar: string; euiScrollBarCorner: string; euiScrollBarCornerThin: string; euiFocusRingColor: string; euiFocusRingAnimStartColor: string; euiFocusRingAnimStartSize: string; euiFocusRingAnimStartSizeLarge: string; euiFocusRingSizeLarge: string; euiFocusRingSize: string; euiFocusTransparency: number; euiFocusTransparencyPercent: string; euiFocusBackgroundColor: string; euiTooltipBackgroundColor: string; euiTooltipBorderColor: string; euiTooltipAnimations: { top: string; left: string; bottom: string; right: string; }; euiFontFamily: string; euiCodeFontFamily: string; euiFontFeatureSettings: string; euiTextScale: string; euiFontSize: string; euiFontSizeXS: string; euiFontSizeS: string; euiFontSizeM: string; euiFontSizeL: string; euiFontSizeXL: string; euiFontSizeXXL: string; euiLineHeight: number; euiBodyLineHeight: number; euiTitles: { xxxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; s: { 'font-size': string; 'line-height': string; 'font-weight': number; }; m: { 'font-size': string; 'line-height': string; 'font-weight': number; }; l: { 'font-size': string; 'line-height': string; 'font-weight': number; }; }; euiZLevel0: number; euiZLevel1: number; euiZLevel2: number; euiZLevel3: number; euiZLevel4: number; euiZLevel5: number; euiZLevel6: number; euiZLevel7: number; euiZLevel8: number; euiZLevel9: number; euiZToastList: number; euiZModal: number; euiZMask: number; euiZNavigation: number; euiZContentMenu: number; euiZHeader: number; euiZFlyout: number; euiZMaskBelowHeader: number; euiZContent: number; euiColorGhost: string; euiColorInk: string; euiColorPrimary: string; euiColorAccent: string; euiColorSuccess: string; euiColorWarning: string; euiColorDanger: string; euiColorEmptyShade: string; euiColorLightestShade: string; euiColorLightShade: string; euiColorMediumShade: string; euiColorDarkShade: string; euiColorDarkestShade: string; euiColorFullShade: string; euiPageBackgroundColor: string; euiColorHighlight: string; euiTextColor: string; euiTitleColor: string; euiTextSubduedColor: string; euiColorDisabled: string; euiColorPrimaryText: string; euiColorSuccessText: string; euiColorAccentText: string; euiColorWarningText: string; euiColorDangerText: string; euiColorDisabledText: string; euiLinkColor: string; euiColorChartLines: string; euiColorChartBand: string; euiDatePickerCalendarWidth: string; euiDatePickerPadding: string; euiDatePickerGap: string; euiDatePickerCalendarColumns: number; euiDatePickerButtonSize: string; euiDatePickerMinControlWidth: string; euiDatePickerMaxControlWidth: string; euiButtonDefaultTransparency: number; euiButtonFontWeight: number; euiRangeHighlightColor: string; euiRangeThumbBackgroundColor: string; euiRangeTrackCompressedHeight: string; euiRangeHighlightCompressedHeight: string; euiRangeHeight: string; euiRangeCompressedHeight: string; euiStepStatusColors: { default: string; complete: string; warning: string; danger: string; }; }" ], "path": "packages/kbn-ui-theme/src/theme.ts", "deprecated": false, diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index 48796dd5266f6..bb34a249143f0 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ui-theme plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] warning: 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. --- diff --git a/api_docs/kbn_utility_types.devdocs.json b/api_docs/kbn_utility_types.devdocs.json index 2734d6eadc135..bb3eb25102399 100644 --- a/api_docs/kbn_utility_types.devdocs.json +++ b/api_docs/kbn_utility_types.devdocs.json @@ -249,6 +249,22 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/utility-types", + "id": "def-server.AwaitedProperties", + "type": "Type", + "tags": [], + "label": "AwaitedProperties", + "description": [ + "\nUnwrap all promise attributes of the given type" + ], + "signature": [ + "{ [K in keyof T]: Awaited; }" + ], + "path": "packages/kbn-utility-types/src/index.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/utility-types", "id": "def-server.Class", diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index f000ebff89f1f..5af644c15e127 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/utility-types plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 27 | 0 | 10 | 1 | +| 28 | 0 | 10 | 1 | ## Server diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index 5359a84655647..3ada49f8bdb89 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/utils plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] warning: 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. --- diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index e61834056f960..155ccdd4f45b8 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github summary: API docs for the kibanaOverview plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] warning: 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. --- diff --git a/api_docs/kibana_react.devdocs.json b/api_docs/kibana_react.devdocs.json index fa0be3526203f..36fdcb2139cdf 100644 --- a/api_docs/kibana_react.devdocs.json +++ b/api_docs/kibana_react.devdocs.json @@ -885,7 +885,9 @@ "parentPluginId": "kibanaReact", "id": "def-public.KibanaPageTemplate", "type": "Function", - "tags": [], + "tags": [ + "deprecated" + ], "label": "KibanaPageTemplate", "description": [], "signature": [ @@ -900,7 +902,193 @@ ">) => JSX.Element" ], "path": "src/plugins/kibana_react/public/page_template/page_template.tsx", - "deprecated": false, + "deprecated": true, + "references": [ + { + "plugin": "management", + "path": "src/plugins/management/public/components/management_app/management_app.tsx" + }, + { + "plugin": "management", + "path": "src/plugins/management/public/components/management_app/management_app.tsx" + }, + { + "plugin": "management", + "path": "src/plugins/management/public/components/management_app/management_app.tsx" + }, + { + "plugin": "spaces", + "path": "x-pack/plugins/spaces/public/space_selector/space_selector.tsx" + }, + { + "plugin": "spaces", + "path": "x-pack/plugins/spaces/public/space_selector/space_selector.tsx" + }, + { + "plugin": "spaces", + "path": "x-pack/plugins/spaces/public/space_selector/space_selector.tsx" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx" + }, + { + "plugin": "ml", + "path": "x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx" + }, + { + "plugin": "ml", + "path": "x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx" + }, + { + "plugin": "ml", + "path": "x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx" + }, + { + "plugin": "canvas", + "path": "x-pack/plugins/canvas/public/components/home/home.component.tsx" + }, + { + "plugin": "canvas", + "path": "x-pack/plugins/canvas/public/components/home/home.component.tsx" + }, + { + "plugin": "canvas", + "path": "x-pack/plugins/canvas/public/components/home/home.component.tsx" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/shared/version_mismatch/version_mismatch_page.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/shared/version_mismatch/version_mismatch_page.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/shared/version_mismatch/version_mismatch_page.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/error_connecting/error_connecting.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/error_connecting/error_connecting.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/error_connecting/error_connecting.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/shared/layout/page_template.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/shared/layout/page_template.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/shared/layout/page_template.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/error_connecting/error_connecting.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/error_connecting/error_connecting.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/error_connecting/error_connecting.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/app_search/components/error_connecting/error_connecting.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/app_search/components/error_connecting/error_connecting.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/app_search/components/error_connecting/error_connecting.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/workplace_search/views/error_state/error_state.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/workplace_search/views/error_state/error_state.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/workplace_search/views/error_state/error_state.tsx" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/public/components/empty_state.tsx" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/public/components/empty_state.tsx" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx" + }, + { + "plugin": "kibanaOverview", + "path": "src/plugins/kibana_overview/public/components/overview/overview.tsx" + }, + { + "plugin": "kibanaOverview", + "path": "src/plugins/kibana_overview/public/components/overview/overview.tsx" + }, + { + "plugin": "kibanaOverview", + "path": "src/plugins/kibana_overview/public/components/overview/overview.tsx" + } + ], "children": [ { "parentPluginId": "kibanaReact", @@ -1689,44 +1877,44 @@ "path": "x-pack/plugins/index_lifecycle_management/public/application/index.tsx" }, { - "plugin": "upgradeAssistant", - "path": "x-pack/plugins/upgrade_assistant/public/shared_imports.ts" + "plugin": "synthetics", + "path": "x-pack/plugins/synthetics/public/lib/alert_types/alert_messages.tsx" }, { - "plugin": "upgradeAssistant", - "path": "x-pack/plugins/upgrade_assistant/public/application/app.tsx" + "plugin": "synthetics", + "path": "x-pack/plugins/synthetics/public/lib/alert_types/alert_messages.tsx" }, { - "plugin": "upgradeAssistant", - "path": "x-pack/plugins/upgrade_assistant/public/application/app.tsx" + "plugin": "synthetics", + "path": "x-pack/plugins/synthetics/public/lib/alert_types/alert_messages.tsx" }, { - "plugin": "upgradeAssistant", - "path": "x-pack/plugins/upgrade_assistant/public/application/app.tsx" + "plugin": "synthetics", + "path": "x-pack/plugins/synthetics/public/apps/uptime_app.tsx" }, { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/lib/alert_types/alert_messages.tsx" + "plugin": "synthetics", + "path": "x-pack/plugins/synthetics/public/apps/uptime_app.tsx" }, { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/lib/alert_types/alert_messages.tsx" + "plugin": "synthetics", + "path": "x-pack/plugins/synthetics/public/apps/uptime_app.tsx" }, { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/lib/alert_types/alert_messages.tsx" + "plugin": "upgradeAssistant", + "path": "x-pack/plugins/upgrade_assistant/public/shared_imports.ts" }, { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/apps/uptime_app.tsx" + "plugin": "upgradeAssistant", + "path": "x-pack/plugins/upgrade_assistant/public/application/app.tsx" }, { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/apps/uptime_app.tsx" + "plugin": "upgradeAssistant", + "path": "x-pack/plugins/upgrade_assistant/public/application/app.tsx" }, { - "plugin": "uptime", - "path": "x-pack/plugins/uptime/public/apps/uptime_app.tsx" + "plugin": "upgradeAssistant", + "path": "x-pack/plugins/upgrade_assistant/public/application/app.tsx" }, { "plugin": "ux", @@ -3370,6 +3558,25 @@ ], "path": "src/plugins/kibana_react/public/table_list_view/table_list_view.tsx", "deprecated": false + }, + { + "parentPluginId": "kibanaReact", + "id": "def-public.TableListViewProps.application", + "type": "Object", + "tags": [], + "label": "application", + "description": [], + "signature": [ + { + "pluginId": "core", + "scope": "public", + "docId": "kibCoreApplicationPluginApi", + "section": "def-public.ApplicationStart", + "text": "ApplicationStart" + } + ], + "path": "src/plugins/kibana_react/public/table_list_view/table_list_view.tsx", + "deprecated": false } ], "initialIsOpen": false @@ -3884,7 +4091,9 @@ "parentPluginId": "kibanaReact", "id": "def-public.KibanaPageTemplateProps", "type": "Type", - "tags": [], + "tags": [ + "deprecated" + ], "label": "KibanaPageTemplateProps", "description": [ "\nA thin wrapper around EuiPageTemplate with a few Kibana specific additions" @@ -3919,7 +4128,169 @@ " | undefined; }" ], "path": "src/plugins/kibana_react/public/page_template/page_template.tsx", - "deprecated": false, + "deprecated": true, + "references": [ + { + "plugin": "management", + "path": "src/plugins/management/public/components/management_app/management_app.tsx" + }, + { + "plugin": "management", + "path": "src/plugins/management/public/components/management_app/management_app.tsx" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/utils/no_data_config.ts" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/utils/no_data_config.ts" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/infra/public/pages/logs/page_template.tsx" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/infra/public/pages/logs/page_template.tsx" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/infra/public/pages/metrics/page_template.tsx" + }, + { + "plugin": "infra", + "path": "x-pack/plugins/infra/public/pages/metrics/page_template.tsx" + }, + { + "plugin": "apm", + "path": "x-pack/plugins/apm/public/components/routing/templates/no_data_config.ts" + }, + { + "plugin": "apm", + "path": "x-pack/plugins/apm/public/components/routing/templates/no_data_config.ts" + }, + { + "plugin": "apm", + "path": "x-pack/plugins/apm/public/components/routing/templates/apm_main_template.tsx" + }, + { + "plugin": "apm", + "path": "x-pack/plugins/apm/public/components/routing/templates/apm_main_template.tsx" + }, + { + "plugin": "apm", + "path": "x-pack/plugins/apm/public/components/routing/templates/service_group_template.tsx" + }, + { + "plugin": "apm", + "path": "x-pack/plugins/apm/public/components/routing/templates/service_group_template.tsx" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/components/csp_page_template.tsx" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/pages/rules/index.tsx" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/pages/rules/index.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/shared/layout/page_template.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/shared/layout/page_template.tsx" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_primary_navigation.tsx" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_primary_navigation.tsx" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/app/home/template_wrapper/bottom_bar/index.tsx" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/app/home/template_wrapper/bottom_bar/index.tsx" + }, + { + "plugin": "synthetics", + "path": "x-pack/plugins/synthetics/public/apps/use_no_data_config.ts" + }, + { + "plugin": "synthetics", + "path": "x-pack/plugins/synthetics/public/apps/use_no_data_config.ts" + }, + { + "plugin": "ux", + "path": "x-pack/plugins/ux/public/components/app/rum_dashboard/rum_home.tsx" + }, + { + "plugin": "ux", + "path": "x-pack/plugins/ux/public/components/app/rum_dashboard/rum_home.tsx" + }, + { + "plugin": "kibanaOverview", + "path": "src/plugins/kibana_overview/public/components/overview/overview.tsx" + }, + { + "plugin": "kibanaOverview", + "path": "src/plugins/kibana_overview/public/components/overview/overview.tsx" + } + ], "initialIsOpen": false }, { @@ -4333,14 +4704,6 @@ { "plugin": "dashboard", "path": "src/plugins/dashboard/public/plugin.tsx" - }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx" - }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx" } ], "initialIsOpen": false @@ -4793,7 +5156,7 @@ "label": "eui", "description": [], "signature": [ - "{ paddingSizes: { xs: string; s: string; m: string; l: string; xl: string; }; avatarSizing: { s: { size: string; 'font-size': string; }; m: { size: string; 'font-size': string; }; l: { size: string; 'font-size': string; }; xl: { size: string; 'font-size': string; }; }; euiBadgeGroupGutterTypes: { gutterExtraSmall: string; gutterSmall: string; }; euiBreadcrumbSpacing: string; euiBreadcrumbTruncateWidth: string; euiButtonEmptyTypes: { primary: string; danger: string; disabled: string; ghost: string; text: string; success: string; warning: string; }; euiCallOutTypes: { primary: string; success: string; warning: string; danger: string; }; euiCardSpacing: string; euiCardBottomNodeHeight: string; euiCardSelectButtonBorders: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardSelectButtonBackgrounds: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCheckableCardPadding: string; euiCodeBlockPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCollapsibleNavGroupLightBackgroundColor: string; euiCollapsibleNavGroupDarkBackgroundColor: string; euiCollapsibleNavGroupDarkHighContrastColor: string; euiColorPickerValueRange0: string; euiColorPickerValueRange1: string; euiColorPickerSaturationRange0: string; euiColorPickerSaturationRange1: string; euiColorPickerIndicatorSize: string; euiColorPickerWidth: string; euiColorPaletteDisplaySizes: { sizeExtraSmall: string; sizeSmall: string; sizeMedium: string; }; euiContextMenuWidth: string; euiControlBarBackground: string; euiControlBarText: string; euiControlBarBorderColor: string; euiControlBarInitialHeight: string; euiControlBarMaxHeight: string; euiControlBarHeights: { s: string; m: string; l: string; }; euiDataGridPrefix: string; euiDataGridStyles: string; euiZDataGrid: number; euiZHeaderBelowDataGrid: number; euiZDataGridCellPopover: number; euiDataGridColumnResizerWidth: string; euiDataGridPopoverMaxHeight: string; euiDataGridCellPaddingS: string; euiDataGridCellPaddingM: string; euiDataGridCellPaddingL: string; euiDataGridVerticalBorder: string; euiSuperDatePickerWidth: string; euiSuperDatePickerButtonWidth: string; euiDragAndDropSpacing: { s: string; m: string; l: string; }; euiExpressionColors: { subdued: string; primary: string; success: string; warning: string; danger: string; accent: string; }; euiFacetGutterSizes: { gutterNone: number; gutterSmall: string; gutterMedium: string; gutterLarge: string; }; gutterTypes: { gutterExtraSmall: string; gutterSmall: string; gutterMedium: string; gutterLarge: string; gutterExtraLarge: string; }; fractions: { fourths: { percentage: string; count: number; }; thirds: { percentage: string; count: number; }; halves: { percentage: string; count: number; }; single: { percentage: string; count: number; }; }; flyoutSizes: { small: { min: string; width: string; max: string; }; medium: { min: string; width: string; max: string; }; large: { min: string; width: string; max: string; }; }; euiFlyoutBorder: string; euiFlyoutPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiFilePickerTallHeight: string; euiRangeLevelColors: { primary: string; success: string; warning: string; danger: string; }; textareaResizing: { vertical: string; horizontal: string; both: string; none: string; }; euiHeaderLinksGutterSizes: { gutterXS: string; gutterS: string; gutterM: string; gutterL: string; }; ruleMargins: { marginXSmall: string; marginSmall: string; marginMedium: string; marginLarge: string; marginXLarge: string; marginXXLarge: string; }; euiIconLoadingOpacity: number; euiIconColors: { accent: string; danger: string; ghost: string; primary: string; success: string; subdued: string; text: string; warning: string; inherit: string; }; euiIconSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiKeyPadMenuSize: string; euiKeyPadMenuMarginSize: string; euiLinkColors: { subdued: string; primary: string; success: string; accent: string; warning: string; danger: string; text: string; ghost: string; }; euiListGroupItemHoverBackground: string; euiListGroupItemHoverBackgroundGhost: string; euiListGroupGutterTypes: { gutterSmall: string; gutterMedium: string; }; euiListGroupItemColorTypes: { primary: string; text: string; subdued: string; ghost: string; }; euiListGroupItemSizeTypes: { xSmall: string; small: string; medium: string; large: string; }; euiGradientStartStop: string; euiGradientMiddle: string; euiLoadingSpinnerSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiMarkdownEditorMinHeight: string; euiPopoverArrowSize: string; euiPopoverTranslateDistance: string; euiProgressSizes: { xs: string; s: string; m: string; l: string; }; euiProgressColors: { primary: string; success: string; warning: string; danger: string; accent: string; subdued: string; vis0: string; vis1: string; vis2: string; vis3: string; vis4: string; vis5: string; vis6: string; vis7: string; vis8: string; vis9: string; customColor: string; }; euiResizableButtonTransitionSpeed: string; euiResizableButtonSize: string; euiSelectableListItemBorder: string; euiSelectableListItemPadding: string; euiSelectableTemplateSitewideTypes: { application: { color: string; 'font-weight': number; }; deployment: { color: string; 'font-weight': number; }; article: { color: string; 'font-weight': number; }; case: { color: string; 'font-weight': number; }; platform: { color: string; 'font-weight': number; }; }; euiSideNavEmphasizedBackgroundColor: string; euiSideNavRootTextcolor: string; euiSideNavBranchTextcolor: string; euiSideNavSelectedTextcolor: string; euiSideNavDisabledTextcolor: string; spacerSizes: { xs: string; s: string; m: string; l: string; xl: string; xxl: string; }; euiStepNumberSize: string; euiStepNumberSmallSize: string; euiStepNumberMargin: string; euiStepStatusColorsToFade: { warning: string; danger: string; disabled: string; incomplete: string; }; euiSuggestItemColors: { tint0: string; tint1: string; tint2: string; tint3: string; tint4: string; tint5: string; tint6: string; tint7: string; tint8: string; tint9: string; tint10: string; }; euiTableCellContentPadding: string; euiTableCellContentPaddingCompressed: string; euiTableCellCheckboxWidth: string; euiTableActionsAreaWidth: string; euiTableHoverColor: string; euiTableSelectedColor: string; euiTableHoverSelectedColor: string; euiTableActionsBorderColor: string; euiTableHoverClickableColor: string; euiTableFocusClickableColor: string; euiTextColors: { default: string; subdued: string; success: string; accent: string; warning: string; danger: string; ghost: string; inherit: string; }; euiTextConstrainedMaxWidth: string; euiToastWidth: string; euiToastTypes: { primary: string; success: string; warning: string; danger: string; }; euiTokenGrayColor: string; euiTokenTypes: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; gray: { graphic: string; behindText: string; }; }; euiTokenTypeKeys: string; euiContrastRatioText: number; euiContrastRatioGraphic: number; euiContrastRatioDisabled: number; euiAnimSlightBounce: string; euiAnimSlightResistance: string; euiAnimSpeedExtraFast: string; euiAnimSpeedFast: string; euiAnimSpeedNormal: string; euiAnimSpeedSlow: string; euiAnimSpeedExtraSlow: string; euiBorderWidthThin: string; euiBorderWidthThick: string; euiBorderColor: string; euiBorderRadius: string; euiBorderRadiusSmall: string; euiBorderThick: string; euiBorderThin: string; euiBorderEditable: string; euiButtonHeight: string; euiButtonHeightSmall: string; euiButtonHeightXSmall: string; euiButtonColorDisabled: string; euiButtonColorDisabledText: string; euiButtonColorGhostDisabled: string; euiButtonTypes: { primary: string; accent: string; success: string; warning: string; danger: string; subdued: string; ghost: string; text: string; }; euiCodeBlockBackgroundColor: string; euiCodeBlockColor: string; euiCodeBlockSelectedBackgroundColor: string; euiCodeBlockCommentColor: string; euiCodeBlockSelectorTagColor: string; euiCodeBlockStringColor: string; euiCodeBlockTagColor: string; euiCodeBlockNameColor: string; euiCodeBlockNumberColor: string; euiCodeBlockKeywordColor: string; euiCodeBlockFunctionTitleColor: string; euiCodeBlockTypeColor: string; euiCodeBlockAttributeColor: string; euiCodeBlockSymbolColor: string; euiCodeBlockParamsColor: string; euiCodeBlockMetaColor: string; euiCodeBlockTitleColor: string; euiCodeBlockSectionColor: string; euiCodeBlockAdditionColor: string; euiCodeBlockDeletionColor: string; euiCodeBlockSelectorClassColor: string; euiCodeBlockSelectorIdColor: string; euiPaletteColorBlind: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; }; euiPaletteColorBlindKeys: string; euiColorVis0: string; euiColorVis1: string; euiColorVis2: string; euiColorVis3: string; euiColorVis4: string; euiColorVis5: string; euiColorVis6: string; euiColorVis7: string; euiColorVis8: string; euiColorVis9: string; euiColorVis0_behindText: string; euiColorVis1_behindText: string; euiColorVis2_behindText: string; euiColorVis3_behindText: string; euiColorVis4_behindText: string; euiColorVis5_behindText: string; euiColorVis6_behindText: string; euiColorVis7_behindText: string; euiColorVis8_behindText: string; euiColorVis9_behindText: string; euiFontWeightLight: number; euiFontWeightRegular: number; euiFontWeightMedium: number; euiFontWeightSemiBold: number; euiFontWeightBold: number; euiCodeFontWeightRegular: number; euiCodeFontWeightBold: number; euiFormMaxWidth: string; euiFormControlHeight: string; euiFormControlCompressedHeight: string; euiFormControlPadding: string; euiFormControlCompressedPadding: string; euiFormControlBorderRadius: string; euiFormControlCompressedBorderRadius: string; euiRadioSize: string; euiCheckBoxSize: string; euiCheckboxBorderRadius: string; euiSwitchHeight: string; euiSwitchWidth: string; euiSwitchThumbSize: string; euiSwitchIconHeight: string; euiSwitchHeightCompressed: string; euiSwitchWidthCompressed: string; euiSwitchThumbSizeCompressed: string; euiSwitchHeightMini: string; euiSwitchWidthMini: string; euiSwitchThumbSizeMini: string; euiFormBackgroundColor: string; euiFormBackgroundDisabledColor: string; euiFormBackgroundReadOnlyColor: string; euiFormBorderOpaqueColor: string; euiFormBorderColor: string; euiFormBorderDisabledColor: string; euiFormCustomControlDisabledIconColor: string; euiFormCustomControlBorderColor: string; euiFormControlDisabledColor: string; euiFormControlBoxShadow: string; euiFormControlPlaceholderText: string; euiFormInputGroupLabelBackground: string; euiFormInputGroupBorder: string; euiSwitchOffColor: string; euiFormControlLayoutGroupInputHeight: string; euiFormControlLayoutGroupInputCompressedHeight: string; euiFormControlLayoutGroupInputCompressedBorderRadius: string; euiRangeTrackColor: string; euiRangeThumbRadius: string; euiRangeThumbHeight: string; euiRangeThumbWidth: string; euiRangeThumbBorderColor: string; euiRangeTrackWidth: string; euiRangeTrackHeight: string; euiRangeTrackBorderWidth: number; euiRangeTrackBorderColor: string; euiRangeTrackRadius: string; euiRangeDisabledOpacity: number; euiRangeHighlightHeight: string; euiHeaderBackgroundColor: string; euiHeaderDarkBackgroundColor: string; euiHeaderBorderColor: string; euiHeaderBreadcrumbColor: string; euiHeaderHeight: string; euiHeaderChildSize: string; euiHeaderHeightCompensation: string; euiPageDefaultMaxWidth: string; euiPageSidebarMinWidth: string; euiPanelPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiPanelBorderRadiusModifiers: { borderRadiusNone: number; borderRadiusMedium: string; }; euiPanelBackgroundColorModifiers: { transparent: string; plain: string; subdued: string; accent: string; primary: string; success: string; warning: string; danger: string; }; euiBreakpoints: { xs: number; s: string; m: string; l: string; xl: string; }; euiBreakpointKeys: string; euiShadowColor: string; euiShadowColorLarge: string; euiSize: string; euiSizeXS: string; euiSizeS: string; euiSizeM: string; euiSizeL: string; euiSizeXL: string; euiSizeXXL: string; euiButtonMinWidth: string; euiScrollBar: string; euiScrollBarCorner: string; euiScrollBarCornerThin: string; euiFocusRingColor: string; euiFocusRingAnimStartColor: string; euiFocusRingAnimStartSize: string; euiFocusRingAnimStartSizeLarge: string; euiFocusRingSizeLarge: string; euiFocusRingSize: string; euiFocusTransparency: number; euiFocusTransparencyPercent: string; euiFocusBackgroundColor: string; euiTooltipBackgroundColor: string; euiTooltipBorderColor: string; euiTooltipAnimations: { top: string; left: string; bottom: string; right: string; }; euiFontFamily: string; euiCodeFontFamily: string; euiFontFeatureSettings: string; euiTextScale: string; euiFontSize: string; euiFontSizeXS: string; euiFontSizeS: string; euiFontSizeM: string; euiFontSizeL: string; euiFontSizeXL: string; euiFontSizeXXL: string; euiLineHeight: number; euiBodyLineHeight: number; euiTitles: { xxxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; s: { 'font-size': string; 'line-height': string; 'font-weight': number; }; m: { 'font-size': string; 'line-height': string; 'font-weight': number; }; l: { 'font-size': string; 'line-height': string; 'font-weight': number; }; }; euiZLevel0: number; euiZLevel1: number; euiZLevel2: number; euiZLevel3: number; euiZLevel4: number; euiZLevel5: number; euiZLevel6: number; euiZLevel7: number; euiZLevel8: number; euiZLevel9: number; euiZToastList: number; euiZModal: number; euiZMask: number; euiZNavigation: number; euiZContentMenu: number; euiZHeader: number; euiZFlyout: number; euiZMaskBelowHeader: number; euiZContent: number; euiColorGhost: string; euiColorInk: string; euiColorPrimary: string; euiColorAccent: string; euiColorSuccess: string; euiColorWarning: string; euiColorDanger: string; euiColorEmptyShade: string; euiColorLightestShade: string; euiColorLightShade: string; euiColorMediumShade: string; euiColorDarkShade: string; euiColorDarkestShade: string; euiColorFullShade: string; euiPageBackgroundColor: string; euiColorHighlight: string; euiTextColor: string; euiTitleColor: string; euiTextSubduedColor: string; euiColorDisabled: string; euiColorPrimaryText: string; euiColorSuccessText: string; euiColorAccentText: string; euiColorWarningText: string; euiColorDangerText: string; euiColorDisabledText: string; euiLinkColor: string; euiColorChartLines: string; euiColorChartBand: string; euiDatePickerCalendarWidth: string; euiDatePickerPadding: string; euiDatePickerGap: string; euiDatePickerCalendarColumns: number; euiDatePickerButtonSize: string; euiDatePickerMinControlWidth: string; euiDatePickerMaxControlWidth: string; euiButtonDefaultTransparency: number; euiButtonFontWeight: number; euiRangeHighlightColor: string; euiRangeThumbBackgroundColor: string; euiRangeTrackCompressedHeight: string; euiRangeHighlightCompressedHeight: string; euiRangeHeight: string; euiRangeCompressedHeight: string; euiStepStatusColors: { default: string; complete: string; warning: string; danger: string; }; }" + "{ paddingSizes: { xs: string; s: string; m: string; l: string; xl: string; }; euiBadgeGroupGutterTypes: { gutterExtraSmall: string; gutterSmall: string; }; euiBreadcrumbSpacing: string; euiBreadcrumbTruncateWidth: string; euiButtonEmptyTypes: { primary: string; danger: string; disabled: string; ghost: string; text: string; success: string; warning: string; }; euiCallOutTypes: { primary: string; success: string; warning: string; danger: string; }; euiCardSpacing: string; euiCardBottomNodeHeight: string; euiCardSelectButtonBorders: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardSelectButtonBackgrounds: { text: string; primary: string; success: string; danger: string; ghost: string; }; euiCardPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCheckableCardPadding: string; euiCodeBlockPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiCollapsibleNavGroupLightBackgroundColor: string; euiCollapsibleNavGroupDarkBackgroundColor: string; euiCollapsibleNavGroupDarkHighContrastColor: string; euiColorPickerValueRange0: string; euiColorPickerValueRange1: string; euiColorPickerSaturationRange0: string; euiColorPickerSaturationRange1: string; euiColorPickerIndicatorSize: string; euiColorPickerWidth: string; euiColorPaletteDisplaySizes: { sizeExtraSmall: string; sizeSmall: string; sizeMedium: string; }; euiContextMenuWidth: string; euiControlBarBackground: string; euiControlBarText: string; euiControlBarBorderColor: string; euiControlBarInitialHeight: string; euiControlBarMaxHeight: string; euiControlBarHeights: { s: string; m: string; l: string; }; euiDataGridPrefix: string; euiDataGridStyles: string; euiZDataGrid: number; euiZHeaderBelowDataGrid: number; euiZDataGridCellPopover: number; euiDataGridColumnResizerWidth: string; euiDataGridPopoverMaxHeight: string; euiDataGridCellPaddingS: string; euiDataGridCellPaddingM: string; euiDataGridCellPaddingL: string; euiDataGridVerticalBorder: string; euiSuperDatePickerWidth: string; euiSuperDatePickerButtonWidth: string; euiDragAndDropSpacing: { s: string; m: string; l: string; }; euiExpressionColors: { subdued: string; primary: string; success: string; warning: string; danger: string; accent: string; }; euiFacetGutterSizes: { gutterNone: number; gutterSmall: string; gutterMedium: string; gutterLarge: string; }; gutterTypes: { gutterExtraSmall: string; gutterSmall: string; gutterMedium: string; gutterLarge: string; gutterExtraLarge: string; }; fractions: { fourths: { percentage: string; count: number; }; thirds: { percentage: string; count: number; }; halves: { percentage: string; count: number; }; single: { percentage: string; count: number; }; }; flyoutSizes: { small: { min: string; width: string; max: string; }; medium: { min: string; width: string; max: string; }; large: { min: string; width: string; max: string; }; }; euiFlyoutBorder: string; euiFlyoutPaddingModifiers: { paddingNone: number; paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiFilePickerTallHeight: string; euiRangeLevelColors: { primary: string; success: string; warning: string; danger: string; }; textareaResizing: { vertical: string; horizontal: string; both: string; none: string; }; euiHeaderLinksGutterSizes: { gutterXS: string; gutterS: string; gutterM: string; gutterL: string; }; ruleMargins: { marginXSmall: string; marginSmall: string; marginMedium: string; marginLarge: string; marginXLarge: string; marginXXLarge: string; }; euiIconLoadingOpacity: number; euiIconColors: { accent: string; danger: string; ghost: string; primary: string; success: string; subdued: string; text: string; warning: string; inherit: string; }; euiIconSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiKeyPadMenuSize: string; euiKeyPadMenuMarginSize: string; euiLinkColors: { subdued: string; primary: string; success: string; accent: string; warning: string; danger: string; text: string; ghost: string; }; euiListGroupItemHoverBackground: string; euiListGroupItemHoverBackgroundGhost: string; euiListGroupGutterTypes: { gutterSmall: string; gutterMedium: string; }; euiListGroupItemColorTypes: { primary: string; text: string; subdued: string; ghost: string; }; euiListGroupItemSizeTypes: { xSmall: string; small: string; medium: string; large: string; }; euiGradientStartStop: string; euiGradientMiddle: string; euiLoadingSpinnerSizes: { small: string; medium: string; large: string; xLarge: string; xxLarge: string; }; euiMarkdownEditorMinHeight: string; euiPopoverArrowSize: string; euiPopoverTranslateDistance: string; euiProgressSizes: { xs: string; s: string; m: string; l: string; }; euiProgressColors: { primary: string; success: string; warning: string; danger: string; accent: string; subdued: string; vis0: string; vis1: string; vis2: string; vis3: string; vis4: string; vis5: string; vis6: string; vis7: string; vis8: string; vis9: string; customColor: string; }; euiResizableButtonTransitionSpeed: string; euiResizableButtonSize: string; euiSelectableListItemBorder: string; euiSelectableListItemPadding: string; euiSelectableTemplateSitewideTypes: { application: { color: string; 'font-weight': number; }; deployment: { color: string; 'font-weight': number; }; article: { color: string; 'font-weight': number; }; case: { color: string; 'font-weight': number; }; platform: { color: string; 'font-weight': number; }; }; euiSideNavEmphasizedBackgroundColor: string; euiSideNavRootTextcolor: string; euiSideNavBranchTextcolor: string; euiSideNavSelectedTextcolor: string; euiSideNavDisabledTextcolor: string; spacerSizes: { xs: string; s: string; m: string; l: string; xl: string; xxl: string; }; euiStepNumberSize: string; euiStepNumberSmallSize: string; euiStepNumberMargin: string; euiStepStatusColorsToFade: { warning: string; danger: string; disabled: string; incomplete: string; }; euiSuggestItemColors: { tint0: string; tint1: string; tint2: string; tint3: string; tint4: string; tint5: string; tint6: string; tint7: string; tint8: string; tint9: string; tint10: string; }; euiTableCellContentPadding: string; euiTableCellContentPaddingCompressed: string; euiTableCellCheckboxWidth: string; euiTableActionsAreaWidth: string; euiTableHoverColor: string; euiTableSelectedColor: string; euiTableHoverSelectedColor: string; euiTableActionsBorderColor: string; euiTableHoverClickableColor: string; euiTableFocusClickableColor: string; euiTextColors: { default: string; subdued: string; success: string; accent: string; warning: string; danger: string; ghost: string; inherit: string; }; euiTextConstrainedMaxWidth: string; euiToastWidth: string; euiToastTypes: { primary: string; success: string; warning: string; danger: string; }; euiTokenGrayColor: string; euiTokenTypes: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; gray: { graphic: string; behindText: string; }; }; euiTokenTypeKeys: string; euiContrastRatioText: number; euiContrastRatioGraphic: number; euiContrastRatioDisabled: number; euiAnimSlightBounce: string; euiAnimSlightResistance: string; euiAnimSpeedExtraFast: string; euiAnimSpeedFast: string; euiAnimSpeedNormal: string; euiAnimSpeedSlow: string; euiAnimSpeedExtraSlow: string; euiBorderWidthThin: string; euiBorderWidthThick: string; euiBorderColor: string; euiBorderRadius: string; euiBorderRadiusSmall: string; euiBorderThick: string; euiBorderThin: string; euiBorderEditable: string; euiButtonHeight: string; euiButtonHeightSmall: string; euiButtonHeightXSmall: string; euiButtonColorDisabled: string; euiButtonColorDisabledText: string; euiButtonColorGhostDisabled: string; euiButtonTypes: { primary: string; accent: string; success: string; warning: string; danger: string; subdued: string; ghost: string; text: string; }; euiCodeBlockBackgroundColor: string; euiCodeBlockColor: string; euiCodeBlockSelectedBackgroundColor: string; euiCodeBlockCommentColor: string; euiCodeBlockSelectorTagColor: string; euiCodeBlockStringColor: string; euiCodeBlockTagColor: string; euiCodeBlockNameColor: string; euiCodeBlockNumberColor: string; euiCodeBlockKeywordColor: string; euiCodeBlockFunctionTitleColor: string; euiCodeBlockTypeColor: string; euiCodeBlockAttributeColor: string; euiCodeBlockSymbolColor: string; euiCodeBlockParamsColor: string; euiCodeBlockMetaColor: string; euiCodeBlockTitleColor: string; euiCodeBlockSectionColor: string; euiCodeBlockAdditionColor: string; euiCodeBlockDeletionColor: string; euiCodeBlockSelectorClassColor: string; euiCodeBlockSelectorIdColor: string; euiPaletteColorBlind: { euiColorVis0: { graphic: string; behindText: string; }; euiColorVis1: { graphic: string; behindText: string; }; euiColorVis2: { graphic: string; behindText: string; }; euiColorVis3: { graphic: string; behindText: string; }; euiColorVis4: { graphic: string; behindText: string; }; euiColorVis5: { graphic: string; behindText: string; }; euiColorVis6: { graphic: string; behindText: string; }; euiColorVis7: { graphic: string; behindText: string; }; euiColorVis8: { graphic: string; behindText: string; }; euiColorVis9: { graphic: string; behindText: string; }; }; euiPaletteColorBlindKeys: string; euiColorVis0: string; euiColorVis1: string; euiColorVis2: string; euiColorVis3: string; euiColorVis4: string; euiColorVis5: string; euiColorVis6: string; euiColorVis7: string; euiColorVis8: string; euiColorVis9: string; euiColorVis0_behindText: string; euiColorVis1_behindText: string; euiColorVis2_behindText: string; euiColorVis3_behindText: string; euiColorVis4_behindText: string; euiColorVis5_behindText: string; euiColorVis6_behindText: string; euiColorVis7_behindText: string; euiColorVis8_behindText: string; euiColorVis9_behindText: string; euiFontWeightLight: number; euiFontWeightRegular: number; euiFontWeightMedium: number; euiFontWeightSemiBold: number; euiFontWeightBold: number; euiCodeFontWeightRegular: number; euiCodeFontWeightBold: number; euiFormMaxWidth: string; euiFormControlHeight: string; euiFormControlCompressedHeight: string; euiFormControlPadding: string; euiFormControlCompressedPadding: string; euiFormControlBorderRadius: string; euiFormControlCompressedBorderRadius: string; euiRadioSize: string; euiCheckBoxSize: string; euiCheckboxBorderRadius: string; euiSwitchHeight: string; euiSwitchWidth: string; euiSwitchThumbSize: string; euiSwitchIconHeight: string; euiSwitchHeightCompressed: string; euiSwitchWidthCompressed: string; euiSwitchThumbSizeCompressed: string; euiSwitchHeightMini: string; euiSwitchWidthMini: string; euiSwitchThumbSizeMini: string; euiFormBackgroundColor: string; euiFormBackgroundDisabledColor: string; euiFormBackgroundReadOnlyColor: string; euiFormBorderOpaqueColor: string; euiFormBorderColor: string; euiFormBorderDisabledColor: string; euiFormCustomControlDisabledIconColor: string; euiFormCustomControlBorderColor: string; euiFormControlDisabledColor: string; euiFormControlBoxShadow: string; euiFormControlPlaceholderText: string; euiFormInputGroupLabelBackground: string; euiFormInputGroupBorder: string; euiSwitchOffColor: string; euiFormControlLayoutGroupInputHeight: string; euiFormControlLayoutGroupInputCompressedHeight: string; euiFormControlLayoutGroupInputCompressedBorderRadius: string; euiRangeTrackColor: string; euiRangeThumbRadius: string; euiRangeThumbHeight: string; euiRangeThumbWidth: string; euiRangeThumbBorderColor: string; euiRangeTrackWidth: string; euiRangeTrackHeight: string; euiRangeTrackBorderWidth: number; euiRangeTrackBorderColor: string; euiRangeTrackRadius: string; euiRangeDisabledOpacity: number; euiRangeHighlightHeight: string; euiHeaderBackgroundColor: string; euiHeaderDarkBackgroundColor: string; euiHeaderBorderColor: string; euiHeaderBreadcrumbColor: string; euiHeaderHeight: string; euiHeaderChildSize: string; euiHeaderHeightCompensation: string; euiPageDefaultMaxWidth: string; euiPageSidebarMinWidth: string; euiPanelPaddingModifiers: { paddingSmall: string; paddingMedium: string; paddingLarge: string; }; euiPanelBorderRadiusModifiers: { borderRadiusNone: number; borderRadiusMedium: string; }; euiPanelBackgroundColorModifiers: { transparent: string; plain: string; subdued: string; accent: string; primary: string; success: string; warning: string; danger: string; }; euiBreakpoints: { xs: number; s: string; m: string; l: string; xl: string; }; euiBreakpointKeys: string; euiShadowColor: string; euiShadowColorLarge: string; euiSize: string; euiSizeXS: string; euiSizeS: string; euiSizeM: string; euiSizeL: string; euiSizeXL: string; euiSizeXXL: string; euiButtonMinWidth: string; euiScrollBar: string; euiScrollBarCorner: string; euiScrollBarCornerThin: string; euiFocusRingColor: string; euiFocusRingAnimStartColor: string; euiFocusRingAnimStartSize: string; euiFocusRingAnimStartSizeLarge: string; euiFocusRingSizeLarge: string; euiFocusRingSize: string; euiFocusTransparency: number; euiFocusTransparencyPercent: string; euiFocusBackgroundColor: string; euiTooltipBackgroundColor: string; euiTooltipBorderColor: string; euiTooltipAnimations: { top: string; left: string; bottom: string; right: string; }; euiFontFamily: string; euiCodeFontFamily: string; euiFontFeatureSettings: string; euiTextScale: string; euiFontSize: string; euiFontSizeXS: string; euiFontSizeS: string; euiFontSizeM: string; euiFontSizeL: string; euiFontSizeXL: string; euiFontSizeXXL: string; euiLineHeight: number; euiBodyLineHeight: number; euiTitles: { xxxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xxs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; xs: { 'font-size': string; 'line-height': string; 'font-weight': number; }; s: { 'font-size': string; 'line-height': string; 'font-weight': number; }; m: { 'font-size': string; 'line-height': string; 'font-weight': number; }; l: { 'font-size': string; 'line-height': string; 'font-weight': number; }; }; euiZLevel0: number; euiZLevel1: number; euiZLevel2: number; euiZLevel3: number; euiZLevel4: number; euiZLevel5: number; euiZLevel6: number; euiZLevel7: number; euiZLevel8: number; euiZLevel9: number; euiZToastList: number; euiZModal: number; euiZMask: number; euiZNavigation: number; euiZContentMenu: number; euiZHeader: number; euiZFlyout: number; euiZMaskBelowHeader: number; euiZContent: number; euiColorGhost: string; euiColorInk: string; euiColorPrimary: string; euiColorAccent: string; euiColorSuccess: string; euiColorWarning: string; euiColorDanger: string; euiColorEmptyShade: string; euiColorLightestShade: string; euiColorLightShade: string; euiColorMediumShade: string; euiColorDarkShade: string; euiColorDarkestShade: string; euiColorFullShade: string; euiPageBackgroundColor: string; euiColorHighlight: string; euiTextColor: string; euiTitleColor: string; euiTextSubduedColor: string; euiColorDisabled: string; euiColorPrimaryText: string; euiColorSuccessText: string; euiColorAccentText: string; euiColorWarningText: string; euiColorDangerText: string; euiColorDisabledText: string; euiLinkColor: string; euiColorChartLines: string; euiColorChartBand: string; euiDatePickerCalendarWidth: string; euiDatePickerPadding: string; euiDatePickerGap: string; euiDatePickerCalendarColumns: number; euiDatePickerButtonSize: string; euiDatePickerMinControlWidth: string; euiDatePickerMaxControlWidth: string; euiButtonDefaultTransparency: number; euiButtonFontWeight: number; euiRangeHighlightColor: string; euiRangeThumbBackgroundColor: string; euiRangeTrackCompressedHeight: string; euiRangeHighlightCompressedHeight: string; euiRangeHeight: string; euiRangeCompressedHeight: string; euiStepStatusColors: { default: string; complete: string; warning: string; danger: string; }; }" ], "path": "src/plugins/kibana_react/common/eui_styled_components.tsx", "deprecated": false diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index 60767318538de..3def8e96dfcf8 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github summary: API docs for the kibanaReact plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 239 | 0 | 203 | 5 | +| 240 | 0 | 204 | 5 | ## Client diff --git a/api_docs/kibana_utils.devdocs.json b/api_docs/kibana_utils.devdocs.json index 18bb3b4a35d4a..ff686632d0f4a 100644 --- a/api_docs/kibana_utils.devdocs.json +++ b/api_docs/kibana_utils.devdocs.json @@ -7606,6 +7606,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -7680,7 +7684,9 @@ "section": "def-server.ResponseErrorAttributes", "text": "ResponseErrorAttributes" }, - " | undefined; }>" + " | undefined; } | Buffer | ", + "Stream", + ">" ], "path": "src/plugins/kibana_utils/server/report_server_error.ts", "deprecated": false, @@ -7821,6 +7827,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index e049912e4e732..b639f92c9c96c 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github summary: API docs for the kibanaUtils plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] warning: 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. --- diff --git a/api_docs/lens.devdocs.json b/api_docs/lens.devdocs.json index 53b0ee481dc97..ae98881027a3b 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -335,13 +335,7 @@ "label": "palette", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<{ [key: string]: unknown; }>" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", @@ -491,7 +485,7 @@ "section": "def-common.Datatable", "text": "Datatable" }, - "> | undefined) => Record<\"enabled\" | \"disabled\", { kuery: ", + "> | undefined) => Record<\"disabled\" | \"enabled\", { kuery: ", "Query", "[][]; lucene: ", "Query", @@ -1958,13 +1952,7 @@ "label": "palette", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<{ [key: string]: unknown; }> | undefined" ], "path": "x-pack/plugins/lens/common/types.ts", @@ -2403,13 +2391,7 @@ "label": "mainPalette", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<{ [key: string]: unknown; }> | undefined" ], "path": "x-pack/plugins/lens/public/types.ts", @@ -2615,7 +2597,7 @@ "label": "params", "description": [], "signature": [ - "{ size: number; orderBy: { type: \"alphabetical\"; fallback?: boolean | undefined; } | { type: \"rare\"; maxDocCount: number; } | { type: \"column\"; columnId: string; }; orderDirection: \"asc\" | \"desc\"; otherBucket?: boolean | undefined; missingBucket?: boolean | undefined; secondaryFields?: string[] | undefined; format?: ", + "{ size: number; accuracyMode?: boolean | undefined; orderBy: { type: \"alphabetical\"; fallback?: boolean | undefined; } | { type: \"rare\"; maxDocCount: number; } | { type: \"column\"; columnId: string; }; orderDirection: \"asc\" | \"desc\"; otherBucket?: boolean | undefined; missingBucket?: boolean | undefined; secondaryFields?: string[] | undefined; format?: ", "ValueFormatConfig", " | undefined; parentFormat?: { id: string; } | undefined; }" ], @@ -2708,13 +2690,7 @@ ], "signature": [ "(addNewLayer: () => string, state?: T | undefined, mainPalette?: ", - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<{ [key: string]: unknown; }> | undefined) => T" ], "path": "x-pack/plugins/lens/public/types.ts", @@ -2756,13 +2732,7 @@ "label": "mainPalette", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<{ [key: string]: unknown; }> | undefined" ], "path": "x-pack/plugins/lens/public/types.ts", @@ -2781,13 +2751,7 @@ "description": [], "signature": [ "((state: T) => ", - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<{ [key: string]: unknown; }> | undefined) | undefined" ], "path": "x-pack/plugins/lens/public/types.ts", @@ -3805,15 +3769,9 @@ "label": "toExpression", "description": [], "signature": [ - "(state: T, datasourceLayers: Record, attributes?: Partial<{ title: string; description: string; }> | undefined) => string | ", + "(state: T, datasourceLayers: ", + "DatasourceLayers", + ", attributes?: Partial<{ title: string; description: string; }> | undefined) => string | ", { "pluginId": "expressions", "scope": "common", @@ -3848,15 +3806,7 @@ "label": "datasourceLayers", "description": [], "signature": [ - "Record" + "DatasourceLayers" ], "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, @@ -3889,15 +3839,9 @@ "\nExpression to render a preview version of the chart in very constrained space.\nIf there is no expression provided, the preview icon is used." ], "signature": [ - "((state: T, datasourceLayers: Record) => string | ", + "((state: T, datasourceLayers: ", + "DatasourceLayers", + ") => string | ", { "pluginId": "expressions", "scope": "common", @@ -3932,15 +3876,7 @@ "label": "datasourceLayers", "description": [], "signature": [ - "Record" + "DatasourceLayers" ], "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, @@ -3959,15 +3895,9 @@ "\nThe frame will call this function on all visualizations at few stages (pre-build/build error) in order\nto provide more context to the error and show it to the user" ], "signature": [ - "(state: T, datasourceLayers?: Record | undefined) => { shortMessage: string; longMessage: React.ReactNode; }[] | undefined" + "(state: T, datasourceLayers?: ", + "DatasourceLayers", + " | undefined) => { shortMessage: string; longMessage: React.ReactNode; }[] | undefined" ], "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, @@ -3994,15 +3924,8 @@ "label": "datasourceLayers", "description": [], "signature": [ - "Record | undefined" + "DatasourceLayers", + " | undefined" ], "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, @@ -4822,13 +4745,7 @@ "label": "palette", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<{ [key: string]: unknown; }> | undefined" ], "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", @@ -7445,37 +7362,13 @@ "description": [], "signature": [ "{ columns: { palette?: ", - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<", - { - "pluginId": "lens", - "scope": "common", - "docId": "kibLensPluginApi", - "section": "def-common.CustomPaletteParams", - "text": "CustomPaletteParams" - }, + "CustomPaletteParams", "> | undefined; colorMode?: \"none\" | \"text\" | \"cell\" | undefined; }[]; } | { palette?: ", - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<", - { - "pluginId": "lens", - "scope": "common", - "docId": "kibLensPluginApi", - "section": "def-common.CustomPaletteParams", - "text": "CustomPaletteParams" - }, + "CustomPaletteParams", "> | undefined; }" ], "path": "x-pack/plugins/lens/server/migrations/types.ts", @@ -7769,203 +7662,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "lens", - "id": "def-common.ColorStop", - "type": "Interface", - "tags": [], - "label": "ColorStop", - "description": [], - "path": "x-pack/plugins/lens/common/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "lens", - "id": "def-common.ColorStop.color", - "type": "string", - "tags": [], - "label": "color", - "description": [], - "path": "x-pack/plugins/lens/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "lens", - "id": "def-common.ColorStop.stop", - "type": "number", - "tags": [], - "label": "stop", - "description": [], - "path": "x-pack/plugins/lens/common/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "lens", - "id": "def-common.CustomPaletteParams", - "type": "Interface", - "tags": [], - "label": "CustomPaletteParams", - "description": [], - "path": "x-pack/plugins/lens/common/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "lens", - "id": "def-common.CustomPaletteParams.name", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "x-pack/plugins/lens/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "lens", - "id": "def-common.CustomPaletteParams.reverse", - "type": "CompoundType", - "tags": [], - "label": "reverse", - "description": [], - "signature": [ - "boolean | undefined" - ], - "path": "x-pack/plugins/lens/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "lens", - "id": "def-common.CustomPaletteParams.rangeType", - "type": "CompoundType", - "tags": [], - "label": "rangeType", - "description": [], - "signature": [ - "\"number\" | \"percent\" | undefined" - ], - "path": "x-pack/plugins/lens/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "lens", - "id": "def-common.CustomPaletteParams.continuity", - "type": "CompoundType", - "tags": [], - "label": "continuity", - "description": [], - "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteContinuity", - "text": "PaletteContinuity" - }, - " | undefined" - ], - "path": "x-pack/plugins/lens/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "lens", - "id": "def-common.CustomPaletteParams.progression", - "type": "string", - "tags": [], - "label": "progression", - "description": [], - "signature": [ - "\"fixed\" | undefined" - ], - "path": "x-pack/plugins/lens/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "lens", - "id": "def-common.CustomPaletteParams.rangeMin", - "type": "number", - "tags": [], - "label": "rangeMin", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "x-pack/plugins/lens/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "lens", - "id": "def-common.CustomPaletteParams.rangeMax", - "type": "number", - "tags": [], - "label": "rangeMax", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "x-pack/plugins/lens/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "lens", - "id": "def-common.CustomPaletteParams.stops", - "type": "Array", - "tags": [], - "label": "stops", - "description": [], - "signature": [ - { - "pluginId": "lens", - "scope": "common", - "docId": "kibLensPluginApi", - "section": "def-common.ColorStop", - "text": "ColorStop" - }, - "[] | undefined" - ], - "path": "x-pack/plugins/lens/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "lens", - "id": "def-common.CustomPaletteParams.colorStops", - "type": "Array", - "tags": [], - "label": "colorStops", - "description": [], - "signature": [ - { - "pluginId": "lens", - "scope": "common", - "docId": "kibLensPluginApi", - "section": "def-common.ColorStop", - "text": "ColorStop" - }, - "[] | undefined" - ], - "path": "x-pack/plugins/lens/common/types.ts", - "deprecated": false - }, - { - "parentPluginId": "lens", - "id": "def-common.CustomPaletteParams.steps", - "type": "number", - "tags": [], - "label": "steps", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "x-pack/plugins/lens/common/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "lens", "id": "def-common.DateRange", @@ -8271,21 +7967,9 @@ "label": "palette", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<", - { - "pluginId": "lens", - "scope": "common", - "docId": "kibLensPluginApi", - "section": "def-common.CustomPaletteParams", - "text": "CustomPaletteParams" - }, + "CustomPaletteParams", "> | undefined" ], "path": "x-pack/plugins/lens/common/types.ts", @@ -8527,13 +8211,7 @@ "label": "palette", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<{ [key: string]: unknown; }> | undefined" ], "path": "x-pack/plugins/lens/common/types.ts", @@ -8833,13 +8511,7 @@ "label": "CustomPaletteParamsConfig", "description": [], "signature": [ - { - "pluginId": "lens", - "scope": "common", - "docId": "kibLensPluginApi", - "section": "def-common.CustomPaletteParams", - "text": "CustomPaletteParams" - }, + "CustomPaletteParams", " & { maxSteps?: number | undefined; }" ], "path": "x-pack/plugins/lens/common/types.ts", @@ -9083,28 +8755,6 @@ "deprecated": false, "initialIsOpen": false }, - { - "parentPluginId": "lens", - "id": "def-common.RequiredPaletteParamTypes", - "type": "Type", - "tags": [], - "label": "RequiredPaletteParamTypes", - "description": [], - "signature": [ - "Required<", - { - "pluginId": "lens", - "scope": "common", - "docId": "kibLensPluginApi", - "section": "def-common.CustomPaletteParams", - "text": "CustomPaletteParams" - }, - "> & { maxSteps?: number | undefined; }" - ], - "path": "x-pack/plugins/lens/common/types.ts", - "deprecated": false, - "initialIsOpen": false - }, { "parentPluginId": "lens", "id": "def-common.SortingHint", diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 2974c7140ed53..13b19ffe60e56 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github summary: API docs for the lens plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 542 | 0 | 467 | 29 | +| 527 | 0 | 452 | 30 | ## Client diff --git a/api_docs/license_api_guard.devdocs.json b/api_docs/license_api_guard.devdocs.json index 3cb1dbcca2a83..06339738fe611 100644 --- a/api_docs/license_api_guard.devdocs.json +++ b/api_docs/license_api_guard.devdocs.json @@ -230,6 +230,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -420,6 +424,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -638,6 +646,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index ff9b643e9d694..875d654c0267b 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github summary: API docs for the licenseApiGuard plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] warning: 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. --- diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index f2ad339d8c0d8..d81ae2975260d 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github summary: API docs for the licenseManagement plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] warning: 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. --- diff --git a/api_docs/licensing.devdocs.json b/api_docs/licensing.devdocs.json index 7d63612c554c2..264859dfa2e6a 100644 --- a/api_docs/licensing.devdocs.json +++ b/api_docs/licensing.devdocs.json @@ -1037,6 +1037,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -1227,6 +1231,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", @@ -1449,6 +1457,8 @@ "section": "def-server.ResponseError", "text": "ResponseError" }, + " | Buffer | ", + "Stream", ">) => ", "KibanaResponse", "; redirected: (options: ", + " | undefined; } | Buffer | ", + "Stream", + ">; redirected: (options: ", { "pluginId": "core", "scope": "server", diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index e9208ee1f9f8e..cb395af82c70a 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github summary: API docs for the licensing plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] warning: 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. --- diff --git a/api_docs/lists.devdocs.json b/api_docs/lists.devdocs.json index 950e244c88b49..a2134a2c8ffb3 100644 --- a/api_docs/lists.devdocs.json +++ b/api_docs/lists.devdocs.json @@ -311,7 +311,7 @@ "label": "exceptionItems", "description": [], "signature": [ - "({ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }))[]" + "({ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | ({ description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; })[]; list_id: string; name: string; type: \"simple\"; } & { comments?: { comment: string; }[] | undefined; item_id?: string | undefined; meta?: object | undefined; namespace_type?: \"single\" | \"agnostic\" | undefined; os_types?: (\"windows\" | \"linux\" | \"macos\")[] | undefined; tags?: string[] | undefined; }))[]" ], "path": "x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.tsx", "deprecated": false @@ -324,7 +324,7 @@ "label": "exceptionsToDelete", "description": [], "signature": [ - "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[]" + "{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[]" ], "path": "x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.tsx", "deprecated": false @@ -463,7 +463,9 @@ "type": "Class", "tags": [], "label": "ExceptionListClient", - "description": [], + "description": [ + "\nClass for use for exceptions that are with trusted applications or\nwith rules." + ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts", "deprecated": false, "children": [ @@ -473,7 +475,9 @@ "type": "Function", "tags": [], "label": "Constructor", - "description": [], + "description": [ + "\nConstructs the exception list" + ], "signature": [ "any" ], @@ -501,12 +505,7 @@ "parentPluginId": "lists", "id": "def-server.ExceptionListClient.getExceptionList", "type": "Function", - "tags": [ - "params", - "params", - "params", - "return" - ], + "tags": [], "label": "getExceptionList", "description": [ "\nFetch an exception list parent container" @@ -535,20 +534,14 @@ } ], "returnComment": [ - "the found exception list or null if none exists" + "The found exception list or null if none exists" ] }, { "parentPluginId": "lists", "id": "def-server.ExceptionListClient.getExceptionListSummary", "type": "Function", - "tags": [ - "params", - "params", - "params", - "params", - "return" - ], + "tags": [], "label": "getExceptionListSummary", "description": [ "\nFetch an exception list parent container" @@ -577,19 +570,14 @@ } ], "returnComment": [ - "summary of exception list item os types" + "Summary of exception list item os types" ] }, { "parentPluginId": "lists", "id": "def-server.ExceptionListClient.getExceptionListItem", "type": "Function", - "tags": [ - "params", - "params", - "params", - "return" - ], + "tags": [], "label": "getExceptionListItem", "description": [ "\nFetch an exception list item container" @@ -597,7 +585,7 @@ "signature": [ "({ itemId, id, namespaceType, }: ", "GetExceptionListItemOptions", - ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | null>" + ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | null>" ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts", "deprecated": false, @@ -655,7 +643,9 @@ "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The exception list schema or null if it does not exist" + ] }, { "parentPluginId": "lists", @@ -669,7 +659,7 @@ "signature": [ "({ comments, description, entries, itemId, meta, name, osTypes, tags, type, }: ", "CreateEndpointListItemOptions", - ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }>" + ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }>" ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts", "deprecated": false, @@ -689,7 +679,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The exception list item created, otherwise null if not created" + ] }, { "parentPluginId": "lists", @@ -703,7 +695,7 @@ "signature": [ "({ _version, comments, description, entries, id, itemId, meta, name, osTypes, tags, type, }: ", "UpdateEndpointListItemOptions", - ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | null>" + ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | null>" ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts", "deprecated": false, @@ -723,7 +715,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The exception list item updated, otherwise null if not updated" + ] }, { "parentPluginId": "lists", @@ -737,7 +731,7 @@ "signature": [ "({ itemId, id, }: ", "GetEndpointListItemOptions", - ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | null>" + ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | null>" ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts", "deprecated": false, @@ -757,24 +751,15 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The exception list item found, otherwise null" + ] }, { "parentPluginId": "lists", "id": "def-server.ExceptionListClient.createExceptionList", "type": "Function", - "tags": [ - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "return" - ], + "tags": [], "label": "createExceptionList", "description": [ "\nCreate an exception list container" @@ -810,19 +795,7 @@ "parentPluginId": "lists", "id": "def-server.ExceptionListClient.updateExceptionList", "type": "Function", - "tags": [ - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "return" - ], + "tags": [], "label": "updateExceptionList", "description": [ "\nUpdate an existing exception list container" @@ -858,12 +831,7 @@ "parentPluginId": "lists", "id": "def-server.ExceptionListClient.deleteExceptionList", "type": "Function", - "tags": [ - "params", - "params", - "params", - "return" - ], + "tags": [], "label": "deleteExceptionList", "description": [ "\nDelete an exception list container by either id or list_id" @@ -899,19 +867,7 @@ "parentPluginId": "lists", "id": "def-server.ExceptionListClient.createExceptionListItem", "type": "Function", - "tags": [ - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "return" - ], + "tags": [], "label": "createExceptionListItem", "description": [ "\nCreate an exception list item container" @@ -925,7 +881,7 @@ "section": "def-server.CreateExceptionListItemOptions", "text": "CreateExceptionListItemOptions" }, - ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }>" + ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }>" ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts", "deprecated": false, @@ -959,21 +915,7 @@ "parentPluginId": "lists", "id": "def-server.ExceptionListClient.updateExceptionListItem", "type": "Function", - "tags": [ - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "params", - "return" - ], + "tags": [], "label": "updateExceptionListItem", "description": [ "\nUpdate an existing exception list item" @@ -987,7 +929,7 @@ "section": "def-server.UpdateExceptionListItemOptions", "text": "UpdateExceptionListItemOptions" }, - ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | null>" + ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | null>" ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts", "deprecated": false, @@ -1021,12 +963,7 @@ "parentPluginId": "lists", "id": "def-server.ExceptionListClient.deleteExceptionListItem", "type": "Function", - "tags": [ - "params", - "params", - "params", - "return" - ], + "tags": [], "label": "deleteExceptionListItem", "description": [ "\nDelete an exception list item by either id or item_id" @@ -1034,7 +971,7 @@ "signature": [ "({ id, itemId, namespaceType, }: ", "DeleteExceptionListItemOptions", - ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | null>" + ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | null>" ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts", "deprecated": false, @@ -1062,11 +999,7 @@ "parentPluginId": "lists", "id": "def-server.ExceptionListClient.deleteExceptionListItemById", "type": "Function", - "tags": [ - "params", - "params", - "return" - ], + "tags": [], "label": "deleteExceptionListItemById", "description": [ "\nDelete an exception list item by id" @@ -1103,12 +1036,12 @@ "tags": [], "label": "deleteEndpointListItem", "description": [ - "\nThis is the same as \"deleteExceptionListItem\" except it applies specifically to the endpoint list." + "\nThis is the same as \"deleteExceptionListItem\" except it applies specifically to the endpoint list.\nEither id or itemId has to be defined to delete but not both is required. If both are provided, the id\nis preferred." ], "signature": [ "({ id, itemId, }: ", "DeleteEndpointListItemOptions", - ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | null>" + ") => Promise<{ _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; } | null>" ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts", "deprecated": false, @@ -1136,11 +1069,13 @@ "type": "Function", "tags": [], "label": "findExceptionListItem", - "description": [], + "description": [ + "\nFinds an exception list item given a set of criteria." + ], "signature": [ "({ listId, filter, perPage, pit, page, searchAfter, sortField, sortOrder, namespaceType, }: ", "FindExceptionListItemOptions", - ") => Promise<({ data: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[]; page: number; per_page: number; total: number; } & { pit?: string | undefined; }) | null>" + ") => Promise<({ data: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[]; page: number; per_page: number; total: number; } & { pit?: string | undefined; }) | null>" ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts", "deprecated": false, @@ -1160,7 +1095,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The found exception list items or null if nothing is found" + ] }, { "parentPluginId": "lists", @@ -1168,11 +1105,13 @@ "type": "Function", "tags": [], "label": "findExceptionListsItem", - "description": [], + "description": [ + "\nFinds exception lists items given a set of criteria." + ], "signature": [ "({ listId, filter, perPage, pit, page, searchAfter, sortField, sortOrder, namespaceType, }: ", "FindExceptionListsItemOptions", - ") => Promise<({ data: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[]; page: number; per_page: number; total: number; } & { pit?: string | undefined; }) | null>" + ") => Promise<({ data: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[]; page: number; per_page: number; total: number; } & { pit?: string | undefined; }) | null>" ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts", "deprecated": false, @@ -1192,7 +1131,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The found exception lists items or null if nothing is found" + ] }, { "parentPluginId": "lists", @@ -1200,11 +1141,13 @@ "type": "Function", "tags": [], "label": "findValueListExceptionListItems", - "description": [], + "description": [ + "\nFinds value list exception items given a set of criteria." + ], "signature": [ "({ perPage, pit, page, searchAfter, sortField, sortOrder, valueListId, }: ", "FindValueListExceptionListsItems", - ") => Promise<({ data: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[]; page: number; per_page: number; total: number; } & { pit?: string | undefined; }) | null>" + ") => Promise<({ data: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[]; page: number; per_page: number; total: number; } & { pit?: string | undefined; }) | null>" ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts", "deprecated": false, @@ -1224,7 +1167,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The found value list exception list item or null if nothing is found" + ] }, { "parentPluginId": "lists", @@ -1232,7 +1177,9 @@ "type": "Function", "tags": [], "label": "findExceptionList", - "description": [], + "description": [ + "\nFinds exception lists given a set of criteria." + ], "signature": [ "({ filter, perPage, page, pit, searchAfter, sortField, sortOrder, namespaceType, }: ", "FindExceptionListOptions", @@ -1256,7 +1203,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The found exception lists or null if nothing is found" + ] }, { "parentPluginId": "lists", @@ -1270,7 +1219,7 @@ "signature": [ "({ filter, perPage, page, pit, searchAfter, sortField, sortOrder, }: ", "FindEndpointListItemOptions", - ") => Promise<({ data: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[]; page: number; per_page: number; total: number; } & { pit?: string | undefined; }) | null>" + ") => Promise<({ data: { _version: string | undefined; comments: ({ comment: string; created_at: string; created_by: string; id: string; } & { updated_at?: string | undefined; updated_by?: string | undefined; })[]; created_at: string; created_by: string; description: string; entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]; id: string; item_id: string; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"simple\"; updated_at: string; updated_by: string; }[]; page: number; per_page: number; total: number; } & { pit?: string | undefined; }) | null>" ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts", "deprecated": false, @@ -1290,18 +1239,15 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The found exception list items or null if nothing is found" + ] }, { "parentPluginId": "lists", "id": "def-server.ExceptionListClient.exportExceptionListAndItems", "type": "Function", - "tags": [ - "params", - "params", - "params", - "return" - ], + "tags": [], "label": "exportExceptionListAndItems", "description": [ "\nExport an exception list parent container and it's items" @@ -1345,12 +1291,7 @@ "parentPluginId": "lists", "id": "def-server.ExceptionListClient.importExceptionListAndItems", "type": "Function", - "tags": [ - "params", - "params", - "params", - "return" - ], + "tags": [], "label": "importExceptionListAndItems", "description": [ "\nImport exception lists parent containers and items as stream" @@ -1386,12 +1327,7 @@ "parentPluginId": "lists", "id": "def-server.ExceptionListClient.importExceptionListAndItemsAsArray", "type": "Function", - "tags": [ - "params", - "params", - "params", - "return" - ], + "tags": [], "label": "importExceptionListAndItemsAsArray", "description": [ "\nImport exception lists parent containers and items as array" @@ -1427,11 +1363,7 @@ "parentPluginId": "lists", "id": "def-server.ExceptionListClient.openPointInTime", "type": "Function", - "tags": [ - "params", - "params", - "return" - ], + "tags": [], "label": "openPointInTime", "description": [ "\nOpens a point in time (PIT) for either exception lists or exception list items.\nSee: https://www.elastic.co/guide/en/elasticsearch/reference/current/point-in-time-api.html" @@ -1475,10 +1407,7 @@ "parentPluginId": "lists", "id": "def-server.ExceptionListClient.closePointInTime", "type": "Function", - "tags": [ - "params", - "return" - ], + "tags": [], "label": "closePointInTime", "description": [ "\nCloses a point in time (PIT) for either exception lists or exception list items.\nSee: https://www.elastic.co/guide/en/elasticsearch/reference/current/point-in-time-api.html" @@ -1663,7 +1592,9 @@ "type": "Class", "tags": [], "label": "ListClient", - "description": [], + "description": [ + "\nClass for use for value lists are are associated with exception lists.\nSee {@link https://www.elastic.co/guide/en/security/current/lists-api-create-container.html}" + ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [ @@ -1673,7 +1604,9 @@ "type": "Function", "tags": [], "label": "Constructor", - "description": [], + "description": [ + "\nConstructs the value list" + ], "signature": [ "any" ], @@ -1703,14 +1636,18 @@ "type": "Function", "tags": [], "label": "getListIndex", - "description": [], + "description": [ + "\nReturns the list index name" + ], "signature": [ "() => string" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The list index name" + ] }, { "parentPluginId": "lists", @@ -1718,14 +1655,18 @@ "type": "Function", "tags": [], "label": "getListItemIndex", - "description": [], + "description": [ + "\nReturns the list item index name" + ], "signature": [ "() => string" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The list item index name" + ] }, { "parentPluginId": "lists", @@ -1733,11 +1674,13 @@ "type": "Function", "tags": [], "label": "getList", - "description": [], + "description": [ + "\nGiven a list id, this will return the list container" + ], "signature": [ "({ id }: ", "GetListOptions", - ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; } | null>" + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; } | null>" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, @@ -1757,7 +1700,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The List container if found, otherwise null" + ] }, { "parentPluginId": "lists", @@ -1765,11 +1710,13 @@ "type": "Function", "tags": [], "label": "createList", - "description": [], + "description": [ + "\nCreates a list, if given at least the \"name\", \"description\", \"type\", and \"version\"\nSee {@link https://www.elastic.co/guide/en/security/current/lists-api-create-container.html}\nfor more information around formats of the deserializer and serializer" + ], "signature": [ "({ id, deserializer, immutable, serializer, name, description, type, meta, version, }: ", "CreateListOptions", - ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }>" + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }>" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, @@ -1789,7 +1736,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The list created" + ] }, { "parentPluginId": "lists", @@ -1797,11 +1746,13 @@ "type": "Function", "tags": [], "label": "createListIfItDoesNotExist", - "description": [], + "description": [ + "\nCreates a list, if given at least the \"name\", \"description\", \"type\", and \"version\"\nSee {@link https://www.elastic.co/guide/en/security/current/lists-api-create-container.html}\nfor more information around formats of the deserializer and serializer.\nThis will create the list if it does not exist. If the list exists, this will ignore creating\nanything and just return the existing list." + ], "signature": [ "({ id, deserializer, serializer, name, description, immutable, type, meta, version, }: ", "CreateListIfItDoesNotExistOptions", - ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }>" + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }>" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, @@ -1821,7 +1772,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The list created" + ] }, { "parentPluginId": "lists", @@ -1829,14 +1782,18 @@ "type": "Function", "tags": [], "label": "getListIndexExists", - "description": [], + "description": [ + "\nTrue if the list index exists, otherwise false" + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "True if the list index exists, otherwise false" + ] }, { "parentPluginId": "lists", @@ -1844,14 +1801,18 @@ "type": "Function", "tags": [], "label": "getListItemIndexExists", - "description": [], + "description": [ + "\nTrue if the list index item exists, otherwise false" + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "True if the list item index exists, otherwise false" + ] }, { "parentPluginId": "lists", @@ -1859,14 +1820,18 @@ "type": "Function", "tags": [], "label": "createListBootStrapIndex", - "description": [], + "description": [ + "\nCreates the list boot strap index for ILM policies." + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The contents of the bootstrap response from Elasticsearch" + ] }, { "parentPluginId": "lists", @@ -1874,14 +1839,18 @@ "type": "Function", "tags": [], "label": "createListItemBootStrapIndex", - "description": [], + "description": [ + "\nCreates the list item boot strap index for ILM policies." + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The contents of the bootstrap response from Elasticsearch" + ] }, { "parentPluginId": "lists", @@ -1889,14 +1858,18 @@ "type": "Function", "tags": [], "label": "getListPolicyExists", - "description": [], + "description": [ + "\nReturns true if the list policy for ILM exists, otherwise false" + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "True if the list policy for ILM exists, otherwise false." + ] }, { "parentPluginId": "lists", @@ -1904,14 +1877,18 @@ "type": "Function", "tags": [], "label": "getListItemPolicyExists", - "description": [], + "description": [ + "\nReturns true if the list item policy for ILM exists, otherwise false" + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "True if the list item policy for ILM exists, otherwise false." + ] }, { "parentPluginId": "lists", @@ -1919,14 +1896,18 @@ "type": "Function", "tags": [], "label": "getListTemplateExists", - "description": [], + "description": [ + "\nReturns true if the list template for ILM exists, otherwise false" + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "True if the list template for ILM exists, otherwise false." + ] }, { "parentPluginId": "lists", @@ -1934,14 +1915,18 @@ "type": "Function", "tags": [], "label": "getListItemTemplateExists", - "description": [], + "description": [ + "\nReturns true if the list item template for ILM exists, otherwise false" + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "True if the list item template for ILM exists, otherwise false." + ] }, { "parentPluginId": "lists", @@ -1949,14 +1934,18 @@ "type": "Function", "tags": [], "label": "getListTemplate", - "description": [], + "description": [ + "\nReturns the list template for ILM." + ], "signature": [ "() => Record" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The contents of the list template for ILM." + ] }, { "parentPluginId": "lists", @@ -1964,14 +1953,18 @@ "type": "Function", "tags": [], "label": "getListItemTemplate", - "description": [], + "description": [ + "\nReturns the list item template for ILM." + ], "signature": [ "() => Record" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The contents of the list item template for ILM." + ] }, { "parentPluginId": "lists", @@ -1979,14 +1972,18 @@ "type": "Function", "tags": [], "label": "setListTemplate", - "description": [], + "description": [ + "\nSets the list template for ILM." + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The contents of the list template for ILM." + ] }, { "parentPluginId": "lists", @@ -1994,14 +1991,18 @@ "type": "Function", "tags": [], "label": "setListItemTemplate", - "description": [], + "description": [ + "\nSets the list item template for ILM." + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The contents of the list item template for ILM." + ] }, { "parentPluginId": "lists", @@ -2009,14 +2010,18 @@ "type": "Function", "tags": [], "label": "setListPolicy", - "description": [], + "description": [ + "\nSets the list policy" + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The contents of the list policy set" + ] }, { "parentPluginId": "lists", @@ -2024,14 +2029,18 @@ "type": "Function", "tags": [], "label": "setListItemPolicy", - "description": [], + "description": [ + "\nSets the list item policy" + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The contents of the list policy set" + ] }, { "parentPluginId": "lists", @@ -2039,14 +2048,18 @@ "type": "Function", "tags": [], "label": "deleteListIndex", - "description": [], + "description": [ + "\nDeletes the list index" + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "True if the list index was deleted, otherwise false" + ] }, { "parentPluginId": "lists", @@ -2054,14 +2067,18 @@ "type": "Function", "tags": [], "label": "deleteListItemIndex", - "description": [], + "description": [ + "\nDeletes the list item index" + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "True if the list item index was deleted, otherwise false" + ] }, { "parentPluginId": "lists", @@ -2069,14 +2086,18 @@ "type": "Function", "tags": [], "label": "deleteListPolicy", - "description": [], + "description": [ + "\nDeletes the list policy" + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The contents of the list policy" + ] }, { "parentPluginId": "lists", @@ -2084,14 +2105,18 @@ "type": "Function", "tags": [], "label": "deleteListItemPolicy", - "description": [], + "description": [ + "\nDeletes the list item policy" + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The contents of the list item policy" + ] }, { "parentPluginId": "lists", @@ -2099,14 +2124,18 @@ "type": "Function", "tags": [], "label": "deleteListTemplate", - "description": [], + "description": [ + "\nDeletes the list template" + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The contents of the list template" + ] }, { "parentPluginId": "lists", @@ -2114,14 +2143,18 @@ "type": "Function", "tags": [], "label": "deleteListItemTemplate", - "description": [], + "description": [ + "\nDeletes the list item template" + ], "signature": [ "() => Promise" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, "children": [], - "returnComment": [] + "returnComment": [ + "The contents of the list item template" + ] }, { "parentPluginId": "lists", @@ -2129,11 +2162,13 @@ "type": "Function", "tags": [], "label": "deleteListItem", - "description": [], + "description": [ + "\nGiven a list item id, this will delete the single list item" + ], "signature": [ "({ id }: ", "DeleteListItemOptions", - ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; } | null>" + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; } | null>" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, @@ -2153,7 +2188,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The list item if found, otherwise null" + ] }, { "parentPluginId": "lists", @@ -2161,11 +2198,13 @@ "type": "Function", "tags": [], "label": "deleteListItemByValue", - "description": [], + "description": [ + "\nGiven a list value, this will delete all list items that have that value" + ], "signature": [ "({ listId, value, type, }: ", "DeleteListItemByValueOptions", - ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]>" + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]>" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, @@ -2185,7 +2224,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The list items deleted." + ] }, { "parentPluginId": "lists", @@ -2193,11 +2234,13 @@ "type": "Function", "tags": [], "label": "deleteList", - "description": [], + "description": [ + "\nGiven a list id, this will delete the list from the id" + ], "signature": [ "({ id }: ", "DeleteListOptions", - ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; } | null>" + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; } | null>" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, @@ -2217,7 +2260,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The list deleted if found, otherwise null" + ] }, { "parentPluginId": "lists", @@ -2225,7 +2270,9 @@ "type": "Function", "tags": [], "label": "exportListItemsToStream", - "description": [], + "description": [ + "\nExports list items to a stream" + ], "signature": [ "({ stringToAppend, listId, stream, }: ", "ExportListItemsToStreamOptions", @@ -2257,11 +2304,13 @@ "type": "Function", "tags": [], "label": "importListItemsToStream", - "description": [], + "description": [ + "\nImports list items to a stream. If the list already exists, this will append the list items to the existing list.\nIf the list does not exist, this will auto-create the list and then add the items to that list.\nSee {@link https://www.elastic.co/guide/en/security/current/lists-api-create-container.html}\nfor more information around formats of the deserializer and serializer." + ], "signature": [ "({ deserializer, serializer, type, listId, stream, meta, version, }: ", "ImportListItemsToStreamOptions", - ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; } | null>" + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; } | null>" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, @@ -2289,11 +2338,13 @@ "type": "Function", "tags": [], "label": "getListItemByValue", - "description": [], + "description": [ + "\nReturns all list items found by value." + ], "signature": [ "({ listId, value, type, }: ", "GetListItemByValueOptions", - ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]>" + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]>" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, @@ -2313,7 +2364,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The list items by value found." + ] }, { "parentPluginId": "lists", @@ -2321,11 +2374,13 @@ "type": "Function", "tags": [], "label": "createListItem", - "description": [], + "description": [ + "\nCreates a list item given at least \"value\", \"type\", and a \"listId\" where \"listId\" is the parent container that this list\nitem belongs to.\nSee {@link https://www.elastic.co/guide/en/security/current/lists-api-create-container.html}\nfor more information around formats of the deserializer and serializer." + ], "signature": [ "({ id, deserializer, serializer, listId, value, type, meta, }: ", "CreateListItemOptions", - ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; } | null>" + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; } | null>" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, @@ -2353,11 +2408,13 @@ "type": "Function", "tags": [], "label": "updateListItem", - "description": [], + "description": [ + "\nUpdates a list item's value given the id of the list item.\nSee {@link https://www.elastic.co/guide/en/elasticsearch/reference/current/optimistic-concurrency-control.html}\nfor more information around optimistic concurrency control." + ], "signature": [ "({ _version, id, value, meta, }: ", "UpdateListItemOptions", - ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; } | null>" + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; } | null>" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, @@ -2385,11 +2442,13 @@ "type": "Function", "tags": [], "label": "updateList", - "description": [], + "description": [ + "\nUpdates a list container's value given the id of the list.\nSee {@link https://www.elastic.co/guide/en/elasticsearch/reference/current/optimistic-concurrency-control.html}\nfor more information around optimistic concurrency control." + ], "signature": [ "({ _version, id, name, description, meta, version, }: ", "UpdateListOptions", - ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; } | null>" + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; } | null>" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, @@ -2417,11 +2476,13 @@ "type": "Function", "tags": [], "label": "getListItem", - "description": [], + "description": [ + "\nGiven a list item id, this returns the list item if it exists, otherwise \"null\"." + ], "signature": [ "({ id }: ", "GetListItemOptions", - ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; } | null>" + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; } | null>" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, @@ -2441,7 +2502,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "The list item found if it exists, otherwise \"null\"." + ] }, { "parentPluginId": "lists", @@ -2449,11 +2512,13 @@ "type": "Function", "tags": [], "label": "getListItemByValues", - "description": [], + "description": [ + "\nGiven a list item value, this returns all list items found with that value." + ], "signature": [ "({ type, listId, value, }: ", "GetListItemsByValueOptions", - ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]>" + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]>" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, @@ -2473,7 +2538,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "All list items that match the value sent in." + ] }, { "parentPluginId": "lists", @@ -2481,11 +2548,13 @@ "type": "Function", "tags": [], "label": "searchListItemByValues", - "description": [], + "description": [ + "\nGiven a list item value, this search for all list items found with that value." + ], "signature": [ "({ type, listId, value, }: ", "SearchListItemByValuesOptions", - ") => Promise<{ items: { _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]; value: unknown; }[]>" + ") => Promise<{ items: { _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]; value: unknown; }[]>" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, @@ -2505,7 +2574,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "All list items that match the value sent in." + ] }, { "parentPluginId": "lists", @@ -2513,11 +2584,13 @@ "type": "Function", "tags": [], "label": "findList", - "description": [], + "description": [ + "\nFinds lists based on a filter passed in. This is a bit complicated as it existed before\nPIT (Point in Time) and other mechanisms. This uses an older way of doing \"hops\" and\naccepting a \"currentIndexPosition\" which acts like a pointer to where the search should continue." + ], "signature": [ "({ filter, currentIndexPosition, perPage, page, sortField, sortOrder, searchAfter, }: ", "FindListOptions", - ") => Promise<{ cursor: string; data: { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }[]; page: number; per_page: number; total: number; }>" + ") => Promise<{ cursor: string; data: { _version: string | undefined; created_at: string; created_by: string; description: string; deserializer: string | undefined; id: string; immutable: boolean; meta: object | undefined; name: string; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; version: number; }[]; page: number; per_page: number; total: number; }>" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, @@ -2537,7 +2610,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "All lists found based on the passed in filter." + ] }, { "parentPluginId": "lists", @@ -2545,11 +2620,13 @@ "type": "Function", "tags": [], "label": "findListItem", - "description": [], + "description": [ + "\nFinds list items based on a filter passed in. This is a bit complicated as it existed before\nPIT (Point in Time) and other mechanisms. This uses an older way of doing \"hops\" and\naccepting a \"currentIndexPosition\" which acts like a pointer to where the search should continue." + ], "signature": [ "({ listId, filter, currentIndexPosition, perPage, page, sortField, sortOrder, searchAfter, }: ", "FindListItemOptions", - ") => Promise<{ cursor: string; data: { _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]; page: number; per_page: number; total: number; } | null>" + ") => Promise<{ cursor: string; data: { _version: string | undefined; created_at: string; created_by: string; deserializer: string | undefined; id: string; list_id: string; meta: object | undefined; serializer: string | undefined; tie_breaker_id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; updated_at: string; updated_by: string; value: string; }[]; page: number; per_page: number; total: number; } | null>" ], "path": "x-pack/plugins/lists/server/services/lists/list_client.ts", "deprecated": false, @@ -2569,7 +2646,9 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "All list items found based on the passed in filter." + ] } ], "initialIsOpen": false @@ -2583,7 +2662,9 @@ "type": "Interface", "tags": [], "label": "CreateExceptionListItemOptions", - "description": [], + "description": [ + "\nExceptionListClient.createExceptionListItem\n{@link ExceptionListClient.createExceptionListItem}" + ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", "deprecated": false, "children": [ @@ -2593,7 +2674,9 @@ "type": "Array", "tags": [], "label": "comments", - "description": [], + "description": [ + "User comments for the exception list item" + ], "signature": [ "{ comment: string; }[]" ], @@ -2606,9 +2689,11 @@ "type": "Array", "tags": [], "label": "entries", - "description": [], + "description": [ + "an array with the exception list item entries" + ], "signature": [ - "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]" + "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]" ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", "deprecated": false @@ -2619,7 +2704,9 @@ "type": "string", "tags": [], "label": "itemId", - "description": [], + "description": [ + "the \"item_id\" of the exception list item" + ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", "deprecated": false }, @@ -2629,7 +2716,9 @@ "type": "string", "tags": [], "label": "listId", - "description": [], + "description": [ + "the \"list_id\" of the parent exception list" + ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", "deprecated": false }, @@ -2639,7 +2728,9 @@ "type": "CompoundType", "tags": [], "label": "namespaceType", - "description": [], + "description": [ + "saved object namespace (single | agnostic)" + ], "signature": [ "\"single\" | \"agnostic\"" ], @@ -2652,7 +2743,9 @@ "type": "string", "tags": [], "label": "name", - "description": [], + "description": [ + "the \"name\" of the exception list" + ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", "deprecated": false }, @@ -2662,7 +2755,9 @@ "type": "Array", "tags": [], "label": "osTypes", - "description": [], + "description": [ + "item os types to apply" + ], "signature": [ "(\"windows\" | \"linux\" | \"macos\")[]" ], @@ -2675,7 +2770,9 @@ "type": "string", "tags": [], "label": "description", - "description": [], + "description": [ + "a description of the exception list" + ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", "deprecated": false }, @@ -2685,7 +2782,9 @@ "type": "Uncategorized", "tags": [], "label": "meta", - "description": [], + "description": [ + "Optional meta data about the list item" + ], "signature": [ "object | undefined" ], @@ -2698,7 +2797,9 @@ "type": "Array", "tags": [], "label": "tags", - "description": [], + "description": [ + "user assigned tags of exception list" + ], "signature": [ "string[]" ], @@ -2711,7 +2812,9 @@ "type": "string", "tags": [], "label": "type", - "description": [], + "description": [ + "container type" + ], "signature": [ "\"simple\"" ], @@ -2836,7 +2939,9 @@ "type": "Interface", "tags": [], "label": "UpdateExceptionListItemOptions", - "description": [], + "description": [ + "\nExceptionListClient.updateExceptionListItem\n{@link ExceptionListClient.updateExceptionListItem}" + ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", "deprecated": false, "children": [ @@ -2846,7 +2951,9 @@ "type": "string", "tags": [], "label": "_version", - "description": [], + "description": [ + "document version" + ], "signature": [ "string | undefined" ], @@ -2859,7 +2966,9 @@ "type": "Array", "tags": [], "label": "comments", - "description": [], + "description": [ + "user comments attached to item" + ], "signature": [ "({ comment: string; } & { id?: string | undefined; })[]" ], @@ -2872,9 +2981,11 @@ "type": "Array", "tags": [], "label": "entries", - "description": [], + "description": [ + "item exception entries logic" + ], "signature": [ - "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"text\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]" + "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"keyword\" | \"ip\" | \"text\" | \"date\" | \"geo_point\" | \"geo_shape\" | \"binary\" | \"date_nanos\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"half_float\" | \"double\" | \"integer_range\" | \"float_range\" | \"long_range\" | \"double_range\" | \"date_range\" | \"ip_range\" | \"shape\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; })[]; field: string; type: \"nested\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"wildcard\"; value: string; })[]" ], "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", "deprecated": false @@ -2885,7 +2996,9 @@ "type": "string", "tags": [], "label": "id", - "description": [], + "description": [ + "the \"id\" of the exception list item" + ], "signature": [ "string | undefined" ], @@ -2898,7 +3011,9 @@ "type": "string", "tags": [], "label": "itemId", - "description": [], + "description": [ + "the \"item_id\" of the exception list item" + ], "signature": [ "string | undefined" ], @@ -2911,7 +3026,9 @@ "type": "CompoundType", "tags": [], "label": "namespaceType", - "description": [], + "description": [ + "saved object namespace (single | agnostic)" + ], "signature": [ "\"single\" | \"agnostic\"" ], @@ -2924,7 +3041,9 @@ "type": "string", "tags": [], "label": "name", - "description": [], + "description": [ + "the \"name\" of the exception list" + ], "signature": [ "string | undefined" ], @@ -2937,7 +3056,9 @@ "type": "Array", "tags": [], "label": "osTypes", - "description": [], + "description": [ + "item os types to apply" + ], "signature": [ "(\"windows\" | \"linux\" | \"macos\")[]" ], @@ -2950,7 +3071,9 @@ "type": "string", "tags": [], "label": "description", - "description": [], + "description": [ + "a description of the exception list" + ], "signature": [ "string | undefined" ], @@ -2963,7 +3086,9 @@ "type": "Uncategorized", "tags": [], "label": "meta", - "description": [], + "description": [ + "Optional meta data about the exception list item" + ], "signature": [ "object | undefined" ], @@ -2976,7 +3101,9 @@ "type": "Array", "tags": [], "label": "tags", - "description": [], + "description": [ + "user assigned tags of exception list" + ], "signature": [ "string[] | undefined" ], @@ -2989,7 +3116,9 @@ "type": "string", "tags": [], "label": "type", - "description": [], + "description": [ + "container type" + ], "signature": [ "\"simple\" | undefined" ], diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 4be9824f833d2..d41261a0cd841 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github summary: API docs for the lists plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Security detections response](https://github.com/orgs/elastic/teams/sec | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 198 | 0 | 162 | 49 | +| 198 | 0 | 90 | 49 | ## Client diff --git a/api_docs/management.mdx b/api_docs/management.mdx index 3f6a113909a8b..30294c621c92c 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github summary: API docs for the management plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] warning: 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. --- diff --git a/api_docs/maps.devdocs.json b/api_docs/maps.devdocs.json index 7b25903f0b349..63493ac786c58 100644 --- a/api_docs/maps.devdocs.json +++ b/api_docs/maps.devdocs.json @@ -80,29 +80,8 @@ "label": "getMeta", "description": [], "signature": [ - "() => Partial<", - "DataFilters", - " & { applyGlobalQuery: boolean; applyGlobalTime: boolean; applyForceRefresh: boolean; fieldNames: string[]; geogridPrecision?: number | undefined; timesliceMaskField?: string | undefined; sourceQuery?: ", - "Query", - " | undefined; sourceMeta: object | null; isForceRefresh: boolean; } & ", - "VectorJoinSourceRequestMeta", - " & { dynamicStyleFields: string[]; isTimeAware: boolean; sourceQuery: ", - "Query", - "; timeFilters: ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataQueryPluginApi", - "section": "def-common.TimeRange", - "text": "TimeRange" - }, - "; } & ", - "ESSearchSourceResponseMeta", - " & ", - "ESGeoLineSourceResponseMeta", - " & ", - "VectorTileLayerMeta", - ">" + "() => ", + "DataRequestMeta" ], "path": "x-pack/plugins/maps/public/classes/util/data_request.ts", "deprecated": false, @@ -1188,6 +1167,20 @@ ], "path": "x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.tsx", "deprecated": false + }, + { + "parentPluginId": "maps", + "id": "def-public.BoundsRequestMeta.joinKeyFilter", + "type": "Object", + "tags": [], + "label": "joinKeyFilter", + "description": [], + "signature": [ + "Filter", + " | undefined" + ], + "path": "x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.tsx", + "deprecated": false } ], "initialIsOpen": false @@ -3051,7 +3044,7 @@ "label": "SourceEditorArgs", "description": [], "signature": [ - "{ clearJoins: () => void; currentLayerType: string; hasJoins: boolean; onChange: (...args: ", + "{ currentLayerType: string; numberOfJoins: number; onChange: (...args: ", "OnSourceChangeArgs", "[]) => Promise; }" ], diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index ae9babe656796..980ecd53f8ee9 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github summary: API docs for the maps plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [GIS](https://github.com/orgs/elastic/teams/kibana-gis) for questions re | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 223 | 0 | 222 | 27 | +| 224 | 0 | 223 | 25 | ## Client diff --git a/api_docs/maps_ems.devdocs.json b/api_docs/maps_ems.devdocs.json index 18ef0a3cf340b..0649ee441acdf 100644 --- a/api_docs/maps_ems.devdocs.json +++ b/api_docs/maps_ems.devdocs.json @@ -88,7 +88,7 @@ "label": "MapConfig", "description": [], "signature": [ - "{ readonly tilemap: Readonly<{ url?: string | undefined; } & { options: Readonly<{ default?: boolean | undefined; tileSize?: number | undefined; subdomains?: string[] | undefined; errorTileUrl?: string | undefined; tms?: boolean | undefined; reuseTiles?: boolean | undefined; bounds?: number[] | undefined; } & { attribution: string; minZoom: number; maxZoom: number; }>; }>; readonly includeElasticMapsService: boolean; readonly emsUrl: string; readonly emsFileApiUrl: string; readonly emsTileApiUrl: string; readonly emsLandingPageUrl: string; readonly emsFontLibraryUrl: string; readonly emsTileLayerId: Readonly<{} & { bright: string; desaturated: string; dark: string; }>; }" + "{ readonly tilemap: Readonly<{ url?: string | undefined; } & { options: Readonly<{ default?: boolean | undefined; tileSize?: number | undefined; subdomains?: string[] | undefined; errorTileUrl?: string | undefined; tms?: boolean | undefined; reuseTiles?: boolean | undefined; bounds?: number[] | undefined; } & { attribution: string; minZoom: number; maxZoom: number; }>; }>; readonly includeElasticMapsService: boolean; readonly emsUrl: string; readonly emsFileApiUrl: string; readonly emsTileApiUrl: string; readonly emsLandingPageUrl: string; readonly emsFontLibraryUrl: string; readonly emsTileLayerId: Readonly<{} & { dark: string; bright: string; desaturated: string; }>; }" ], "path": "src/plugins/maps_ems/config.ts", "deprecated": false, @@ -141,7 +141,7 @@ "label": "config", "description": [], "signature": [ - "{ readonly tilemap: Readonly<{ url?: string | undefined; } & { options: Readonly<{ default?: boolean | undefined; tileSize?: number | undefined; subdomains?: string[] | undefined; errorTileUrl?: string | undefined; tms?: boolean | undefined; reuseTiles?: boolean | undefined; bounds?: number[] | undefined; } & { attribution: string; minZoom: number; maxZoom: number; }>; }>; readonly includeElasticMapsService: boolean; readonly emsUrl: string; readonly emsFileApiUrl: string; readonly emsTileApiUrl: string; readonly emsLandingPageUrl: string; readonly emsFontLibraryUrl: string; readonly emsTileLayerId: Readonly<{} & { bright: string; desaturated: string; dark: string; }>; }" + "{ readonly tilemap: Readonly<{ url?: string | undefined; } & { options: Readonly<{ default?: boolean | undefined; tileSize?: number | undefined; subdomains?: string[] | undefined; errorTileUrl?: string | undefined; tms?: boolean | undefined; reuseTiles?: boolean | undefined; bounds?: number[] | undefined; } & { attribution: string; minZoom: number; maxZoom: number; }>; }>; readonly includeElasticMapsService: boolean; readonly emsUrl: string; readonly emsFileApiUrl: string; readonly emsTileApiUrl: string; readonly emsLandingPageUrl: string; readonly emsFontLibraryUrl: string; readonly emsTileLayerId: Readonly<{} & { dark: string; bright: string; desaturated: string; }>; }" ], "path": "src/plugins/maps_ems/public/index.ts", "deprecated": false @@ -441,7 +441,7 @@ "section": "def-server.PluginInitializerContext", "text": "PluginInitializerContext" }, - "; }>; includeElasticMapsService: boolean; emsUrl: string; emsFileApiUrl: string; emsTileApiUrl: string; emsLandingPageUrl: string; emsFontLibraryUrl: string; emsTileLayerId: Readonly<{} & { bright: string; desaturated: string; dark: string; }>; }>>" + "; }>; includeElasticMapsService: boolean; emsUrl: string; emsFileApiUrl: string; emsTileApiUrl: string; emsLandingPageUrl: string; emsFontLibraryUrl: string; emsTileLayerId: Readonly<{} & { dark: string; bright: string; desaturated: string; }>; }>>" ], "path": "src/plugins/maps_ems/server/index.ts", "deprecated": false @@ -474,7 +474,7 @@ "section": "def-server.PluginInitializerContext", "text": "PluginInitializerContext" }, - "; }>; includeElasticMapsService: boolean; emsUrl: string; emsFileApiUrl: string; emsTileApiUrl: string; emsLandingPageUrl: string; emsFontLibraryUrl: string; emsTileLayerId: Readonly<{} & { bright: string; desaturated: string; dark: string; }>; }>>" + "; }>; includeElasticMapsService: boolean; emsUrl: string; emsFileApiUrl: string; emsTileApiUrl: string; emsLandingPageUrl: string; emsFontLibraryUrl: string; emsTileLayerId: Readonly<{} & { dark: string; bright: string; desaturated: string; }>; }>>" ], "path": "src/plugins/maps_ems/server/index.ts", "deprecated": false, @@ -499,7 +499,7 @@ "section": "def-server.CoreSetup", "text": "CoreSetup" }, - ", plugins: MapsEmsSetupServerDependencies) => { config: Readonly<{} & { tilemap: Readonly<{ url?: string | undefined; } & { options: Readonly<{ default?: boolean | undefined; tileSize?: number | undefined; subdomains?: string[] | undefined; errorTileUrl?: string | undefined; tms?: boolean | undefined; reuseTiles?: boolean | undefined; bounds?: number[] | undefined; } & { attribution: string; minZoom: number; maxZoom: number; }>; }>; includeElasticMapsService: boolean; emsUrl: string; emsFileApiUrl: string; emsTileApiUrl: string; emsLandingPageUrl: string; emsFontLibraryUrl: string; emsTileLayerId: Readonly<{} & { bright: string; desaturated: string; dark: string; }>; }>; createEMSSettings: () => ", + ", plugins: MapsEmsSetupServerDependencies) => { config: Readonly<{} & { tilemap: Readonly<{ url?: string | undefined; } & { options: Readonly<{ default?: boolean | undefined; tileSize?: number | undefined; subdomains?: string[] | undefined; errorTileUrl?: string | undefined; tms?: boolean | undefined; reuseTiles?: boolean | undefined; bounds?: number[] | undefined; } & { attribution: string; minZoom: number; maxZoom: number; }>; }>; includeElasticMapsService: boolean; emsUrl: string; emsFileApiUrl: string; emsTileApiUrl: string; emsLandingPageUrl: string; emsFontLibraryUrl: string; emsTileLayerId: Readonly<{} & { dark: string; bright: string; desaturated: string; }>; }>; createEMSSettings: () => ", { "pluginId": "mapsEms", "scope": "common", @@ -589,7 +589,7 @@ "label": "config", "description": [], "signature": [ - "{ readonly tilemap: Readonly<{ url?: string | undefined; } & { options: Readonly<{ default?: boolean | undefined; tileSize?: number | undefined; subdomains?: string[] | undefined; errorTileUrl?: string | undefined; tms?: boolean | undefined; reuseTiles?: boolean | undefined; bounds?: number[] | undefined; } & { attribution: string; minZoom: number; maxZoom: number; }>; }>; readonly includeElasticMapsService: boolean; readonly emsUrl: string; readonly emsFileApiUrl: string; readonly emsTileApiUrl: string; readonly emsLandingPageUrl: string; readonly emsFontLibraryUrl: string; readonly emsTileLayerId: Readonly<{} & { bright: string; desaturated: string; dark: string; }>; }" + "{ readonly tilemap: Readonly<{ url?: string | undefined; } & { options: Readonly<{ default?: boolean | undefined; tileSize?: number | undefined; subdomains?: string[] | undefined; errorTileUrl?: string | undefined; tms?: boolean | undefined; reuseTiles?: boolean | undefined; bounds?: number[] | undefined; } & { attribution: string; minZoom: number; maxZoom: number; }>; }>; readonly includeElasticMapsService: boolean; readonly emsUrl: string; readonly emsFileApiUrl: string; readonly emsTileApiUrl: string; readonly emsLandingPageUrl: string; readonly emsFontLibraryUrl: string; readonly emsTileLayerId: Readonly<{} & { dark: string; bright: string; desaturated: string; }>; }" ], "path": "src/plugins/maps_ems/server/index.ts", "deprecated": false diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index 4fa7850a0e9c9..5fc5483e9d64c 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github summary: API docs for the mapsEms plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] warning: 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. --- diff --git a/api_docs/metrics_entities.devdocs.json b/api_docs/metrics_entities.devdocs.json deleted file mode 100644 index 8a4c69fc1e824..0000000000000 --- a/api_docs/metrics_entities.devdocs.json +++ /dev/null @@ -1,1282 +0,0 @@ -{ - "id": "metricsEntities", - "client": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "server": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [], - "setup": { - "parentPluginId": "metricsEntities", - "id": "def-server.MetricsEntitiesPluginSetup", - "type": "Interface", - "tags": [], - "label": "MetricsEntitiesPluginSetup", - "description": [], - "path": "x-pack/plugins/metrics_entities/server/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "metricsEntities", - "id": "def-server.MetricsEntitiesPluginSetup.getMetricsEntitiesClient", - "type": "Function", - "tags": [], - "label": "getMetricsEntitiesClient", - "description": [], - "signature": [ - "(esClient: ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.ElasticsearchClient", - "text": "ElasticsearchClient" - }, - ") => ", - "MetricsEntitiesClient" - ], - "path": "x-pack/plugins/metrics_entities/server/types.ts", - "deprecated": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "metricsEntities", - "id": "def-server.MetricsEntitiesPluginSetup.getMetricsEntitiesClient.$1", - "type": "Object", - "tags": [], - "label": "esClient", - "description": [], - "signature": [ - "{ eql: ", - "default", - "; search: { >(this: That, params?: ", - "SearchRequest", - " | ", - "SearchRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "SearchResponse", - ">; >(this: That, params?: ", - "SearchRequest", - " | ", - "SearchRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "SearchResponse", - ", unknown>>; >(this: That, params?: ", - "SearchRequest", - " | ", - "SearchRequest", - " | undefined, options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "SearchResponse", - ">; }; create: { (this: That, params: ", - "CreateRequest", - " | ", - "CreateRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "WriteResponseBase", - ">; (this: That, params: ", - "CreateRequest", - " | ", - "CreateRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "WriteResponseBase", - ", unknown>>; (this: That, params: ", - "CreateRequest", - " | ", - "CreateRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "WriteResponseBase", - ">; }; monitoring: ", - "default", - "; security: ", - "default", - "; name: string | symbol; index: { (this: That, params: ", - "IndexRequest", - " | ", - "IndexRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "WriteResponseBase", - ">; (this: That, params: ", - "IndexRequest", - " | ", - "IndexRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "WriteResponseBase", - ", unknown>>; (this: That, params: ", - "IndexRequest", - " | ", - "IndexRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "WriteResponseBase", - ">; }; delete: { (this: That, params: ", - "DeleteRequest", - " | ", - "DeleteRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "WriteResponseBase", - ">; (this: That, params: ", - "DeleteRequest", - " | ", - "DeleteRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "WriteResponseBase", - ", unknown>>; (this: That, params: ", - "DeleteRequest", - " | ", - "DeleteRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "WriteResponseBase", - ">; }; get: { (this: That, params: ", - "GetRequest", - " | ", - "GetRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "GetResponse", - ">; (this: That, params: ", - "GetRequest", - " | ", - "GetRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "GetResponse", - ", unknown>>; (this: That, params: ", - "GetRequest", - " | ", - "GetRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "GetResponse", - ">; }; update: { (this: That, params: ", - "UpdateRequest", - " | ", - "UpdateRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "UpdateResponse", - ">; (this: That, params: ", - "UpdateRequest", - " | ", - "UpdateRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "UpdateResponse", - ", unknown>>; (this: That, params: ", - "UpdateRequest", - " | ", - "UpdateRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "UpdateResponse", - ">; }; closePointInTime: { (this: That, params: ", - "ClosePointInTimeRequest", - " | ", - "ClosePointInTimeRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "ClosePointInTimeResponse", - ">; (this: That, params: ", - "ClosePointInTimeRequest", - " | ", - "ClosePointInTimeRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "ClosePointInTimeResponse", - ", unknown>>; (this: That, params: ", - "ClosePointInTimeRequest", - " | ", - "ClosePointInTimeRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "ClosePointInTimeResponse", - ">; }; helpers: ", - "default", - "; [kInternal]: symbol | null; [kAsyncSearch]: symbol | null; [kAutoscaling]: symbol | null; [kCat]: symbol | null; [kCcr]: symbol | null; [kCluster]: symbol | null; [kDanglingIndices]: symbol | null; [kEnrich]: symbol | null; [kEql]: symbol | null; [kFeatures]: symbol | null; [kFleet]: symbol | null; [kGraph]: symbol | null; [kIlm]: symbol | null; [kIndices]: symbol | null; [kIngest]: symbol | null; [kLicense]: symbol | null; [kLogstash]: symbol | null; [kMigration]: symbol | null; [kMl]: symbol | null; [kMonitoring]: symbol | null; [kNodes]: symbol | null; [kRollup]: symbol | null; [kSearchableSnapshots]: symbol | null; [kSecurity]: symbol | null; [kShutdown]: symbol | null; [kSlm]: symbol | null; [kSnapshot]: symbol | null; [kSql]: symbol | null; [kSsl]: symbol | null; [kTasks]: symbol | null; [kTextStructure]: symbol | null; [kTransform]: symbol | null; [kWatcher]: symbol | null; [kXpack]: symbol | null; transport: ", - "default", - "; child: (opts: ", - "ClientOptions", - ") => ", - "default", - "; Internal: ", - "default", - "; asyncSearch: ", - "default", - "; autoscaling: ", - "default", - "; bulk: { (this: That, params: ", - "BulkRequest", - " | ", - "BulkRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "BulkResponse", - ">; (this: That, params: ", - "BulkRequest", - " | ", - "BulkRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "BulkResponse", - ", unknown>>; (this: That, params: ", - "BulkRequest", - " | ", - "BulkRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "BulkResponse", - ">; }; cat: ", - "default", - "; ccr: ", - "default", - "; clearScroll: { (this: That, params?: ", - "ClearScrollRequest", - " | ", - "ClearScrollRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "ClearScrollResponse", - ">; (this: That, params?: ", - "ClearScrollRequest", - " | ", - "ClearScrollRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "ClearScrollResponse", - ", unknown>>; (this: That, params?: ", - "ClearScrollRequest", - " | ", - "ClearScrollRequest", - " | undefined, options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "ClearScrollResponse", - ">; }; cluster: ", - "default", - "; count: { (this: That, params?: ", - "CountRequest", - " | ", - "CountRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "CountResponse", - ">; (this: That, params?: ", - "CountRequest", - " | ", - "CountRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "CountResponse", - ", unknown>>; (this: That, params?: ", - "CountRequest", - " | ", - "CountRequest", - " | undefined, options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "CountResponse", - ">; }; danglingIndices: ", - "default", - "; deleteByQuery: { (this: That, params: ", - "DeleteByQueryRequest", - " | ", - "DeleteByQueryRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "DeleteByQueryResponse", - ">; (this: That, params: ", - "DeleteByQueryRequest", - " | ", - "DeleteByQueryRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "DeleteByQueryResponse", - ", unknown>>; (this: That, params: ", - "DeleteByQueryRequest", - " | ", - "DeleteByQueryRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "DeleteByQueryResponse", - ">; }; deleteByQueryRethrottle: { (this: That, params: ", - "DeleteByQueryRethrottleRequest", - " | ", - "DeleteByQueryRethrottleRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "TasksTaskListResponseBase", - ">; (this: That, params: ", - "DeleteByQueryRethrottleRequest", - " | ", - "DeleteByQueryRethrottleRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "TasksTaskListResponseBase", - ", unknown>>; (this: That, params: ", - "DeleteByQueryRethrottleRequest", - " | ", - "DeleteByQueryRethrottleRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "TasksTaskListResponseBase", - ">; }; deleteScript: { (this: That, params: ", - "DeleteScriptRequest", - " | ", - "DeleteScriptRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "AcknowledgedResponseBase", - ">; (this: That, params: ", - "DeleteScriptRequest", - " | ", - "DeleteScriptRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "AcknowledgedResponseBase", - ", unknown>>; (this: That, params: ", - "DeleteScriptRequest", - " | ", - "DeleteScriptRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "AcknowledgedResponseBase", - ">; }; enrich: ", - "default", - "; exists: { (this: That, params: ", - "ExistsRequest", - " | ", - "ExistsRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise; (this: That, params: ", - "ExistsRequest", - " | ", - "ExistsRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - ">; (this: That, params: ", - "ExistsRequest", - " | ", - "ExistsRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise; }; existsSource: { (this: That, params: ", - "ExistsSourceRequest", - " | ", - "ExistsSourceRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise; (this: That, params: ", - "ExistsSourceRequest", - " | ", - "ExistsSourceRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - ">; (this: That, params: ", - "ExistsSourceRequest", - " | ", - "ExistsSourceRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise; }; explain: { (this: That, params: ", - "ExplainRequest", - " | ", - "ExplainRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "ExplainResponse", - ">; (this: That, params: ", - "ExplainRequest", - " | ", - "ExplainRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "ExplainResponse", - ", unknown>>; (this: That, params: ", - "ExplainRequest", - " | ", - "ExplainRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "ExplainResponse", - ">; }; features: ", - "default", - "; fieldCaps: { (this: That, params: ", - "FieldCapsRequest", - " | ", - "FieldCapsRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "FieldCapsResponse", - ">; (this: That, params: ", - "FieldCapsRequest", - " | ", - "FieldCapsRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "FieldCapsResponse", - ", unknown>>; (this: That, params: ", - "FieldCapsRequest", - " | ", - "FieldCapsRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "FieldCapsResponse", - ">; }; fleet: ", - "default", - "; getScript: { (this: That, params: ", - "GetScriptRequest", - " | ", - "GetScriptRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "GetScriptResponse", - ">; (this: That, params: ", - "GetScriptRequest", - " | ", - "GetScriptRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "GetScriptResponse", - ", unknown>>; (this: That, params: ", - "GetScriptRequest", - " | ", - "GetScriptRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "GetScriptResponse", - ">; }; getScriptContext: { (this: That, params?: ", - "GetScriptContextRequest", - " | ", - "GetScriptContextRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "GetScriptContextResponse", - ">; (this: That, params?: ", - "GetScriptContextRequest", - " | ", - "GetScriptContextRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "GetScriptContextResponse", - ", unknown>>; (this: That, params?: ", - "GetScriptContextRequest", - " | ", - "GetScriptContextRequest", - " | undefined, options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "GetScriptContextResponse", - ">; }; getScriptLanguages: { (this: That, params?: ", - "GetScriptLanguagesRequest", - " | ", - "GetScriptLanguagesRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "GetScriptLanguagesResponse", - ">; (this: That, params?: ", - "GetScriptLanguagesRequest", - " | ", - "GetScriptLanguagesRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "GetScriptLanguagesResponse", - ", unknown>>; (this: That, params?: ", - "GetScriptLanguagesRequest", - " | ", - "GetScriptLanguagesRequest", - " | undefined, options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "GetScriptLanguagesResponse", - ">; }; getSource: { (this: That, params: ", - "GetSourceRequest", - " | ", - "GetSourceRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise; (this: That, params: ", - "GetSourceRequest", - " | ", - "GetSourceRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - ">; (this: That, params: ", - "GetSourceRequest", - " | ", - "GetSourceRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise; }; graph: ", - "default", - "; ilm: ", - "default", - "; indices: ", - "default", - "; info: { (this: That, params?: ", - "InfoRequest", - " | ", - "InfoRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "InfoResponse", - ">; (this: That, params?: ", - "InfoRequest", - " | ", - "InfoRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "InfoResponse", - ", unknown>>; (this: That, params?: ", - "InfoRequest", - " | ", - "InfoRequest", - " | undefined, options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "InfoResponse", - ">; }; ingest: ", - "default", - "; knnSearch: { (this: That, params: ", - "KnnSearchRequest", - " | ", - "KnnSearchRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "KnnSearchResponse", - ">; (this: That, params: ", - "KnnSearchRequest", - " | ", - "KnnSearchRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "KnnSearchResponse", - ", unknown>>; (this: That, params: ", - "KnnSearchRequest", - " | ", - "KnnSearchRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "KnnSearchResponse", - ">; }; license: ", - "default", - "; logstash: ", - "default", - "; mget: { (this: That, params?: ", - "MgetRequest", - " | ", - "MgetRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "MgetResponse", - ">; (this: That, params?: ", - "MgetRequest", - " | ", - "MgetRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "MgetResponse", - ", unknown>>; (this: That, params?: ", - "MgetRequest", - " | ", - "MgetRequest", - " | undefined, options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "MgetResponse", - ">; }; migration: ", - "default", - "; ml: ", - "default", - "; msearch: { >(this: That, params: ", - "MsearchRequest", - " | ", - "MsearchRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "MsearchResponse", - ">; >(this: That, params: ", - "MsearchRequest", - " | ", - "MsearchRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "MsearchResponse", - ", unknown>>; >(this: That, params: ", - "MsearchRequest", - " | ", - "MsearchRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "MsearchResponse", - ">; }; msearchTemplate: { >(this: That, params: ", - "MsearchTemplateRequest", - " | ", - "MsearchTemplateRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "MsearchTemplateResponse", - ">; >(this: That, params: ", - "MsearchTemplateRequest", - " | ", - "MsearchTemplateRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "MsearchTemplateResponse", - ", unknown>>; >(this: That, params: ", - "MsearchTemplateRequest", - " | ", - "MsearchTemplateRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "MsearchTemplateResponse", - ">; }; mtermvectors: { (this: That, params?: ", - "MtermvectorsRequest", - " | ", - "MtermvectorsRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "MtermvectorsResponse", - ">; (this: That, params?: ", - "MtermvectorsRequest", - " | ", - "MtermvectorsRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "MtermvectorsResponse", - ", unknown>>; (this: That, params?: ", - "MtermvectorsRequest", - " | ", - "MtermvectorsRequest", - " | undefined, options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "MtermvectorsResponse", - ">; }; nodes: ", - "default", - "; openPointInTime: { (this: That, params: ", - "OpenPointInTimeRequest", - " | ", - "OpenPointInTimeRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "OpenPointInTimeResponse", - ">; (this: That, params: ", - "OpenPointInTimeRequest", - " | ", - "OpenPointInTimeRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "OpenPointInTimeResponse", - ", unknown>>; (this: That, params: ", - "OpenPointInTimeRequest", - " | ", - "OpenPointInTimeRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "OpenPointInTimeResponse", - ">; }; ping: { (this: That, params?: ", - "PingRequest", - " | ", - "PingRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise; (this: That, params?: ", - "PingRequest", - " | ", - "PingRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - ">; (this: That, params?: ", - "PingRequest", - " | ", - "PingRequest", - " | undefined, options?: ", - "TransportRequestOptions", - " | undefined): Promise; }; putScript: { (this: That, params: ", - "PutScriptRequest", - " | ", - "PutScriptRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "AcknowledgedResponseBase", - ">; (this: That, params: ", - "PutScriptRequest", - " | ", - "PutScriptRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "AcknowledgedResponseBase", - ", unknown>>; (this: That, params: ", - "PutScriptRequest", - " | ", - "PutScriptRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "AcknowledgedResponseBase", - ">; }; rankEval: { (this: That, params: ", - "RankEvalRequest", - " | ", - "RankEvalRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "RankEvalResponse", - ">; (this: That, params: ", - "RankEvalRequest", - " | ", - "RankEvalRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "RankEvalResponse", - ", unknown>>; (this: That, params: ", - "RankEvalRequest", - " | ", - "RankEvalRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "RankEvalResponse", - ">; }; reindex: { (this: That, params: ", - "ReindexRequest", - " | ", - "ReindexRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "ReindexResponse", - ">; (this: That, params: ", - "ReindexRequest", - " | ", - "ReindexRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "ReindexResponse", - ", unknown>>; (this: That, params: ", - "ReindexRequest", - " | ", - "ReindexRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "ReindexResponse", - ">; }; reindexRethrottle: { (this: That, params: ", - "ReindexRethrottleRequest", - " | ", - "ReindexRethrottleRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "ReindexRethrottleResponse", - ">; (this: That, params: ", - "ReindexRethrottleRequest", - " | ", - "ReindexRethrottleRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "ReindexRethrottleResponse", - ", unknown>>; (this: That, params: ", - "ReindexRethrottleRequest", - " | ", - "ReindexRethrottleRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "ReindexRethrottleResponse", - ">; }; renderSearchTemplate: { (this: That, params?: ", - "RenderSearchTemplateRequest", - " | ", - "RenderSearchTemplateRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "RenderSearchTemplateResponse", - ">; (this: That, params?: ", - "RenderSearchTemplateRequest", - " | ", - "RenderSearchTemplateRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "RenderSearchTemplateResponse", - ", unknown>>; (this: That, params?: ", - "RenderSearchTemplateRequest", - " | ", - "RenderSearchTemplateRequest", - " | undefined, options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "RenderSearchTemplateResponse", - ">; }; rollup: ", - "default", - "; scriptsPainlessExecute: { (this: That, params?: ", - "ScriptsPainlessExecuteRequest", - " | ", - "ScriptsPainlessExecuteRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "ScriptsPainlessExecuteResponse", - ">; (this: That, params?: ", - "ScriptsPainlessExecuteRequest", - " | ", - "ScriptsPainlessExecuteRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "ScriptsPainlessExecuteResponse", - ", unknown>>; (this: That, params?: ", - "ScriptsPainlessExecuteRequest", - " | ", - "ScriptsPainlessExecuteRequest", - " | undefined, options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "ScriptsPainlessExecuteResponse", - ">; }; scroll: { >(this: That, params: ", - "ScrollRequest", - " | ", - "ScrollRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "ScrollResponse", - ">; >(this: That, params: ", - "ScrollRequest", - " | ", - "ScrollRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "ScrollResponse", - ", unknown>>; >(this: That, params: ", - "ScrollRequest", - " | ", - "ScrollRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "ScrollResponse", - ">; }; searchMvt: { (this: That, params: ", - "SearchMvtRequest", - " | ", - "SearchMvtRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise; (this: That, params: ", - "SearchMvtRequest", - " | ", - "SearchMvtRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - ">; (this: That, params: ", - "SearchMvtRequest", - " | ", - "SearchMvtRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise; }; searchShards: { (this: That, params?: ", - "SearchShardsRequest", - " | ", - "SearchShardsRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "SearchShardsResponse", - ">; (this: That, params?: ", - "SearchShardsRequest", - " | ", - "SearchShardsRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "SearchShardsResponse", - ", unknown>>; (this: That, params?: ", - "SearchShardsRequest", - " | ", - "SearchShardsRequest", - " | undefined, options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "SearchShardsResponse", - ">; }; searchTemplate: { (this: That, params?: ", - "SearchTemplateRequest", - " | ", - "SearchTemplateRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "SearchTemplateResponse", - ">; (this: That, params?: ", - "SearchTemplateRequest", - " | ", - "SearchTemplateRequest", - " | undefined, options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "SearchTemplateResponse", - ", unknown>>; (this: That, params?: ", - "SearchTemplateRequest", - " | ", - "SearchTemplateRequest", - " | undefined, options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "SearchTemplateResponse", - ">; }; searchableSnapshots: ", - "default", - "; shutdown: ", - "default", - "; slm: ", - "default", - "; snapshot: ", - "default", - "; sql: ", - "default", - "; ssl: ", - "default", - "; tasks: ", - "default", - "; termsEnum: { (this: That, params: ", - "TermsEnumRequest", - " | ", - "TermsEnumRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "TermsEnumResponse", - ">; (this: That, params: ", - "TermsEnumRequest", - " | ", - "TermsEnumRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "TermsEnumResponse", - ", unknown>>; (this: That, params: ", - "TermsEnumRequest", - " | ", - "TermsEnumRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "TermsEnumResponse", - ">; }; termvectors: { (this: That, params: ", - "TermvectorsRequest", - " | ", - "TermvectorsRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "TermvectorsResponse", - ">; (this: That, params: ", - "TermvectorsRequest", - " | ", - "TermvectorsRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "TermvectorsResponse", - ", unknown>>; (this: That, params: ", - "TermvectorsRequest", - " | ", - "TermvectorsRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "TermvectorsResponse", - ">; }; textStructure: ", - "default", - "; transform: ", - "default", - "; updateByQuery: { (this: That, params: ", - "UpdateByQueryRequest", - " | ", - "UpdateByQueryRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "UpdateByQueryResponse", - ">; (this: That, params: ", - "UpdateByQueryRequest", - " | ", - "UpdateByQueryRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "UpdateByQueryResponse", - ", unknown>>; (this: That, params: ", - "UpdateByQueryRequest", - " | ", - "UpdateByQueryRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "UpdateByQueryResponse", - ">; }; updateByQueryRethrottle: { (this: That, params: ", - "UpdateByQueryRethrottleRequest", - " | ", - "UpdateByQueryRethrottleRequest", - ", options?: ", - "TransportRequestOptionsWithOutMeta", - " | undefined): Promise<", - "UpdateByQueryRethrottleResponse", - ">; (this: That, params: ", - "UpdateByQueryRethrottleRequest", - " | ", - "UpdateByQueryRethrottleRequest", - ", options?: ", - "TransportRequestOptionsWithMeta", - " | undefined): Promise<", - "TransportResult", - "<", - "UpdateByQueryRethrottleResponse", - ", unknown>>; (this: That, params: ", - "UpdateByQueryRethrottleRequest", - " | ", - "UpdateByQueryRethrottleRequest", - ", options?: ", - "TransportRequestOptions", - " | undefined): Promise<", - "UpdateByQueryRethrottleResponse", - ">; }; watcher: ", - "default", - "; xpack: ", - "default", - "; }" - ], - "path": "x-pack/plugins/metrics_entities/server/types.ts", - "deprecated": false - } - ] - } - ], - "lifecycle": "setup", - "initialIsOpen": true - }, - "start": { - "parentPluginId": "metricsEntities", - "id": "def-server.MetricsEntitiesPluginStart", - "type": "Type", - "tags": [], - "label": "MetricsEntitiesPluginStart", - "description": [], - "signature": [ - "void" - ], - "path": "x-pack/plugins/metrics_entities/server/types.ts", - "deprecated": false, - "lifecycle": "start", - "initialIsOpen": true - } - }, - "common": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - } -} \ No newline at end of file diff --git a/api_docs/metrics_entities.mdx b/api_docs/metrics_entities.mdx deleted file mode 100644 index cbe21f8f4da51..0000000000000 --- a/api_docs/metrics_entities.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -id: kibMetricsEntitiesPluginApi -slug: /kibana-dev-docs/api/metricsEntities -title: "metricsEntities" -image: https://source.unsplash.com/400x175/?github -summary: API docs for the metricsEntities plugin -date: 2022-04-05 -tags: ['contributor', 'dev', 'apidocs', 'kibana', 'metricsEntities'] -warning: 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. ---- -import metricsEntitiesObj from './metrics_entities.devdocs.json'; - - - -Contact [Security solution](https://github.com/orgs/elastic/teams/security-solution) for questions regarding this plugin. - -**Code health stats** - -| Public API count | Any count | Items lacking comments | Missing exports | -|-------------------|-----------|------------------------|-----------------| -| 4 | 0 | 4 | 1 | - -## Server - -### Setup - - -### Start - - diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index f78b0773ab8b7..c53f258b23ce2 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github summary: API docs for the ml plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] warning: 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. --- diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index 9d4656621ad0b..f4ccaa0afb22f 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github summary: API docs for the monitoring plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring'] warning: 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. --- diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx index fd085531e49d8..6862c66127338 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github summary: API docs for the monitoringCollection plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] warning: 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. --- diff --git a/api_docs/navigation.devdocs.json b/api_docs/navigation.devdocs.json index 7512f5d4cd86e..8c183c4b321ad 100644 --- a/api_docs/navigation.devdocs.json +++ b/api_docs/navigation.devdocs.json @@ -143,7 +143,7 @@ "label": "start", "description": [], "signature": [ - "({ i18n }: ", + "(core: ", { "pluginId": "core", "scope": "public", @@ -170,7 +170,7 @@ "id": "def-public.NavigationPublicPlugin.start.$1", "type": "Object", "tags": [], - "label": "{ i18n }", + "label": "core", "description": [], "signature": [ { @@ -410,9 +410,9 @@ "DisambiguateSet", "<(", "DisambiguateSet", - " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"onClick\" | \"color\" | \"href\">) | (", + " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\">) | (", "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"onClick\" | \"color\">), WithSpanProps> & WithSpanProps & { iconType?: ", + " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\">), WithSpanProps> & WithSpanProps & { iconType?: ", "IconType", " | undefined; label: React.ReactNode; tooltipContent?: React.ReactNode; tooltipPosition?: ", "ToolTipPositions", @@ -426,9 +426,9 @@ "DisambiguateSet", "<(", "DisambiguateSet", - " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"onClick\" | \"color\" | \"href\">) | (", + " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\">) | (", "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"onClick\" | \"color\">), WithSpanProps> & WithSpanProps & { iconType?: ", + " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\">), WithSpanProps> & WithSpanProps & { iconType?: ", "IconType", " | undefined; label: React.ReactNode; tooltipContent?: React.ReactNode; tooltipPosition?: ", "ToolTipPositions", @@ -444,11 +444,11 @@ "DisambiguateSet", " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"onClick\" | \"color\" | \"href\">) | (", + " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\">) | (", "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"onClick\" | \"color\">)> & ", + " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\">)> & ", "DisambiguateSet", - " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"onClick\" | \"color\" | \"href\"> & { iconType?: ", + " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\"> & { iconType?: ", "IconType", " | undefined; label: React.ReactNode; tooltipContent?: React.ReactNode; tooltipPosition?: ", "ToolTipPositions", @@ -462,11 +462,11 @@ "DisambiguateSet", " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"onClick\" | \"color\" | \"href\">) | (", + " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\">) | (", "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"onClick\" | \"color\">)> & ", + " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\">)> & ", "DisambiguateSet", - " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"onClick\" | \"color\" | \"href\"> & { iconType?: ", + " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\"> & { iconType?: ", "IconType", " | undefined; label: React.ReactNode; tooltipContent?: React.ReactNode; tooltipPosition?: ", "ToolTipPositions", @@ -482,11 +482,11 @@ "DisambiguateSet", " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"onClick\" | \"color\" | \"href\">) | (", + " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\">) | (", "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"onClick\" | \"color\">)> & ", + " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\">)> & ", "DisambiguateSet", - " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"onClick\" | \"color\" | \"href\"> & { iconType?: ", + " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\"> & { iconType?: ", "IconType", " | undefined; label: React.ReactNode; tooltipContent?: React.ReactNode; tooltipPosition?: ", "ToolTipPositions", @@ -502,11 +502,11 @@ "DisambiguateSet", " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"onClick\" | \"color\" | \"href\">) | (", + " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\">) | (", "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"onClick\" | \"color\">)> & ", + " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\">)> & ", "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"onClick\" | \"color\"> & { iconType?: ", + " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\"> & { iconType?: ", "IconType", " | undefined; label: React.ReactNode; tooltipContent?: React.ReactNode; tooltipPosition?: ", "ToolTipPositions", @@ -520,11 +520,11 @@ "DisambiguateSet", " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"onClick\" | \"color\" | \"href\">) | (", + " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\">) | (", "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"onClick\" | \"color\">)> & ", + " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\">)> & ", "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"onClick\" | \"color\"> & { iconType?: ", + " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\"> & { iconType?: ", "IconType", " | undefined; label: React.ReactNode; tooltipContent?: React.ReactNode; tooltipPosition?: ", "ToolTipPositions", @@ -540,11 +540,11 @@ "DisambiguateSet", " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"onClick\" | \"color\" | \"href\">) | (", + " & { href: string; target?: string | undefined; rel?: string | undefined; } & Omit, \"color\" | \"onClick\" | \"href\">) | (", "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"onClick\" | \"color\">)> & ", + " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\">)> & ", "DisambiguateSet", - " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"onClick\" | \"color\"> & { iconType?: ", + " & { onClick?: React.MouseEventHandler | undefined; onClickAriaLabel?: string | undefined; } & Omit, \"color\" | \"onClick\"> & { iconType?: ", "IconType", " | undefined; label: React.ReactNode; tooltipContent?: React.ReactNode; tooltipPosition?: ", "ToolTipPositions", diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index 5c0088964dbc6..a7aee0a91e834 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github summary: API docs for the navigation plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] warning: 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. --- diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index 1e3cfb7a6fa96..9dc6262d29612 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github summary: API docs for the newsfeed plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] warning: 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. --- diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index 6dde3e0820d9a..1167181e14f56 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -678,9 +678,9 @@ }, " | undefined; }; selectedAlertId?: string | undefined; } & ", "CommonProps", - " & { as?: \"div\" | undefined; } & _EuiFlyoutProps & Omit, HTMLDivElement>, keyof _EuiFlyoutProps> & Omit, HTMLDivElement>, \"key\" | \"css\" | keyof React.HTMLAttributes> & { ref?: React.RefObject | ((instance: HTMLDivElement | null) => void) | null | undefined; }, \"children\" | \"onClick\" | \"onChange\" | \"color\" | \"onKeyDown\" | \"title\" | \"id\" | \"security\" | \"defaultValue\" | \"hidden\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"as\" | keyof ", + " & { as?: \"div\" | undefined; } & _EuiFlyoutProps & Omit, HTMLDivElement>, keyof _EuiFlyoutProps> & Omit, HTMLDivElement>, \"key\" | \"css\" | keyof React.HTMLAttributes> & { ref?: React.RefObject | ((instance: HTMLDivElement | null) => void) | null | undefined; }, \"children\" | \"color\" | \"title\" | \"onChange\" | \"onKeyDown\" | \"onClick\" | \"id\" | \"security\" | \"defaultValue\" | \"hidden\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"as\" | keyof ", "CommonProps", - " | keyof React.ClassAttributes | keyof _EuiFlyoutProps>, \"children\" | \"onClick\" | \"onChange\" | \"color\" | \"onKeyDown\" | \"alert\" | \"key\" | \"title\" | \"id\" | \"css\" | \"security\" | \"defaultValue\" | \"hidden\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"as\" | keyof ", + " | keyof React.ClassAttributes | keyof _EuiFlyoutProps>, \"children\" | \"alert\" | \"color\" | \"title\" | \"onChange\" | \"onKeyDown\" | \"onClick\" | \"key\" | \"id\" | \"css\" | \"security\" | \"defaultValue\" | \"hidden\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"as\" | keyof ", "CommonProps", " | \"alerts\" | keyof _EuiFlyoutProps | \"isInApp\" | \"observabilityRuleTypeRegistry\" | \"selectedAlertId\"> & { ref?: React.RefObject | ((instance: HTMLDivElement | null) => void) | null | undefined; }> & { readonly _result: ({ alert, alerts, isInApp, observabilityRuleTypeRegistry, onClose, selectedAlertId, }: AlertsFlyoutProps) => JSX.Element | null; }" ], @@ -3683,13 +3683,7 @@ "label": "palette", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<{ [key: string]: unknown; }> | undefined" ], "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts", @@ -4798,7 +4792,7 @@ "DisambiguateSet", ", Omit, \"href\">> & Omit, \"href\">) | (", "DisambiguateSet", - ", \"href\">, React.ButtonHTMLAttributes> & React.ButtonHTMLAttributes))), \"onClick\" | \"color\" | \"rel\" | \"target\"> & { size?: ItemSize | undefined; color?: Color | undefined; label: React.ReactNode; isActive?: boolean | undefined; isDisabled?: boolean | undefined; href?: string | undefined; target?: string | undefined; rel?: string | undefined; iconType?: ", + ", \"href\">, React.ButtonHTMLAttributes> & React.ButtonHTMLAttributes))), \"color\" | \"onClick\" | \"rel\" | \"target\"> & { size?: ItemSize | undefined; color?: Color | undefined; label: React.ReactNode; isActive?: boolean | undefined; isDisabled?: boolean | undefined; href?: string | undefined; target?: string | undefined; rel?: string | undefined; iconType?: ", "IconType", " | undefined; iconProps?: Omit<", "EuiIconProps", @@ -6859,12 +6853,35 @@ { "parentPluginId": "observability", "id": "def-server.ObservabilityRouteHandlerResources.context", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "context", "description": [], "signature": [ - "ObservabilityRequestHandlerContext" + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " & { licensing: Promise<", + { + "pluginId": "licensing", + "scope": "server", + "docId": "kibLicensingPluginApi", + "section": "def-server.LicensingApiRequestHandlerContext", + "text": "LicensingApiRequestHandlerContext" + }, + ">; alerting: Promise<", + { + "pluginId": "alerting", + "scope": "server", + "docId": "kibAlertingPluginApi", + "section": "def-server.AlertingApiRequestHandlerContext", + "text": "AlertingApiRequestHandlerContext" + }, + ">; }" ], "path": "x-pack/plugins/observability/server/routes/types.ts", "deprecated": false @@ -7077,7 +7094,7 @@ "section": "def-server.RequestHandlerContext", "text": "RequestHandlerContext" }, - " & { licensing: ", + " & { licensing: Promise<", { "pluginId": "licensing", "scope": "server", @@ -7085,7 +7102,7 @@ "section": "def-server.LicensingApiRequestHandlerContext", "text": "LicensingApiRequestHandlerContext" }, - "; }, request: ", + ">; }, request: ", { "pluginId": "core", "scope": "server", @@ -7168,11 +7185,82 @@ ], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "observability", + "id": "def-common.getProbabilityFromProgressiveLoadingQuality", + "type": "Function", + "tags": [], + "label": "getProbabilityFromProgressiveLoadingQuality", + "description": [], + "signature": [ + "(quality: ", + { + "pluginId": "observability", + "scope": "common", + "docId": "kibObservabilityPluginApi", + "section": "def-common.ProgressiveLoadingQuality", + "text": "ProgressiveLoadingQuality" + }, + ") => number" + ], + "path": "x-pack/plugins/observability/common/progressive_loading.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "observability", + "id": "def-common.getProbabilityFromProgressiveLoadingQuality.$1", + "type": "Enum", + "tags": [], + "label": "quality", + "description": [], + "signature": [ + { + "pluginId": "observability", + "scope": "common", + "docId": "kibObservabilityPluginApi", + "section": "def-common.ProgressiveLoadingQuality", + "text": "ProgressiveLoadingQuality" + } + ], + "path": "x-pack/plugins/observability/common/progressive_loading.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [], - "enums": [], + "enums": [ + { + "parentPluginId": "observability", + "id": "def-common.ProgressiveLoadingQuality", + "type": "Enum", + "tags": [], + "label": "ProgressiveLoadingQuality", + "description": [], + "path": "x-pack/plugins/observability/common/progressive_loading.ts", + "deprecated": false, + "initialIsOpen": false + } + ], "misc": [ + { + "parentPluginId": "observability", + "id": "def-common.apmProgressiveLoading", + "type": "string", + "tags": [], + "label": "apmProgressiveLoading", + "description": [], + "signature": [ + "\"observability:apmProgressiveLoading\"" + ], + "path": "x-pack/plugins/observability/common/ui_settings_keys.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "observability", "id": "def-common.apmServiceInventoryOptimizedSorting", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 9675c6927423c..958f5d5f0f061 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github summary: API docs for the observability plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Observability UI](https://github.com/orgs/elastic/teams/observability-u | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 369 | 2 | 366 | 31 | +| 373 | 2 | 370 | 30 | ## Client @@ -62,6 +62,9 @@ Contact [Observability UI](https://github.com/orgs/elastic/teams/observability-u ### Functions +### Enums + + ### Consts, variables and types diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 7c84da2e6ca7a..e5785b58293ea 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github summary: API docs for the osquery plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] warning: 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. --- diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 84e92c68c5944..f43ac68f5f298 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -3,7 +3,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory summary: Directory of public APIs available through plugins or packages. -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana'] warning: 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. --- @@ -12,37 +12,37 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 231 | 188 | 35 | +| 247 | 201 | 35 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 25613 | 171 | 19521 | 1128 | +| 25958 | 170 | 19687 | 1153 | ## Plugin Directory | Plugin name           | Maintaining team | Description | API Cnt | Any Cnt | Missing
comments | Missing
exports | |--------------|----------------|-----------|--------------|----------|---------------|--------| -| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 127 | 0 | 127 | 10 | +| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 195 | 0 | 191 | 11 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 23 | 0 | 19 | 1 | -| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 334 | 0 | 325 | 20 | +| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 325 | 0 | 316 | 19 | | | [APM UI](https://github.com/orgs/elastic/teams/apm-ui) | The user interface for Elastic APM | 40 | 0 | 40 | 50 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 9 | 0 | 9 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 78 | 1 | 69 | 2 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Canvas application to Kibana | 9 | 0 | 8 | 3 | -| | [ResponseOps](https://github.com/orgs/elastic/teams/response-ops) | The Case management system in Kibana | 71 | 0 | 58 | 19 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 322 | 2 | 289 | 4 | +| | [ResponseOps](https://github.com/orgs/elastic/teams/response-ops) | The Case management system in Kibana | 71 | 0 | 57 | 19 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 272 | 2 | 253 | 9 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 28 | 0 | 23 | 0 | | | [Cloud Security Posture](https://github.com/orgs/elastic/teams/cloud-posture-security) | The cloud security posture plugin | 14 | 0 | 14 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 13 | 0 | 13 | 1 | -| | [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 | 188 | 0 | 182 | 4 | -| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2497 | 15 | 971 | 33 | +| | [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 | 203 | 0 | 197 | 6 | +| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2524 | 15 | 977 | 33 | | crossClusterReplication | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | | [Fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 98 | 0 | 79 | 1 | -| | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 151 | 0 | 149 | 14 | +| | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 142 | 0 | 140 | 12 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 52 | 0 | 51 | 0 | -| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | 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. | 3426 | 40 | 2816 | 20 | +| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | 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. | 3414 | 38 | 2802 | 18 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Enhanced data plugin. (See src/plugins/data.) Enhances the main data plugin with a search session management UI. Includes a reusable search session indicator component to use in other applications. Exposes routes for managing search sessions. Includes a service that monitors, updates, and cleans up search session saved objects. | 16 | 0 | 16 | 2 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | This plugin provides the ability to create data views via a modal flyout from any kibana app | 13 | 0 | 7 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Reusable data view field editor across Kibana | 42 | 0 | 37 | 3 | @@ -50,7 +50,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | 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. | 862 | 3 | 710 | 15 | | | [Machine Learning 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. | 23 | 2 | 19 | 1 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 10 | 0 | 8 | 2 | -| | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the Discover application and the saved search embeddable. | 77 | 0 | 61 | 7 | +| | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the Discover application and the saved search embeddable. | 76 | 0 | 60 | 7 | | | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 37 | 0 | 35 | 2 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds embeddables service to Kibana | 476 | 0 | 386 | 4 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Extends embeddable plugin with more functionality | 14 | 0 | 14 | 0 | @@ -60,8 +60,8 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | The Event Annotation service contains expressions for event annotations | 49 | 0 | 49 | 3 | | | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 91 | 0 | 91 | 9 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'error' renderer to expressions | 17 | 0 | 15 | 2 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Expression Gauge plugin adds a `gauge` renderer and function to the expression plugin. The renderer will display the `gauge` chart. | 76 | 0 | 76 | 2 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Expression Heatmap plugin adds a `heatmap` renderer and function to the expression plugin. The renderer will display the `heatmap` chart. | 119 | 0 | 115 | 3 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Expression Gauge plugin adds a `gauge` renderer and function to the expression plugin. The renderer will display the `gauge` chart. | 61 | 0 | 61 | 2 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Expression Heatmap plugin adds a `heatmap` renderer and function to the expression plugin. The renderer will display the `heatmap` chart. | 104 | 0 | 100 | 3 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'image' function and renderer to expressions | 26 | 0 | 26 | 0 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'metric' function and renderer to expressions | 32 | 0 | 27 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Expression MetricVis plugin adds a `metric` renderer and function to the expression plugin. The renderer will display the `metric` chart. | 46 | 0 | 46 | 1 | @@ -71,11 +71,11 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'shape' function and renderer to expressions | 148 | 0 | 146 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Expression Tagcloud plugin adds a `tagcloud` renderer and function to the expression plugin. The renderer will display the `Wordcloud` chart. | 7 | 0 | 7 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Expression XY plugin adds a `xy` renderer and function to the expression plugin. The renderer will display the `xy` chart. | 473 | 0 | 463 | 0 | -| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds expression runtime to Kibana | 2145 | 17 | 1701 | 6 | +| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds expression runtime to Kibana | 2158 | 17 | 1713 | 5 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 222 | 0 | 95 | 2 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Index pattern fields and ambiguous values formatters | 286 | 6 | 247 | 3 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | 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. | 62 | 0 | 62 | 2 | -| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1379 | 8 | 1262 | 9 | +| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1385 | 8 | 1268 | 10 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 68 | 0 | 14 | 5 | | globalSearchBar | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | globalSearchProviders | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | @@ -90,25 +90,24 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 123 | 2 | 96 | 4 | | | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides UI and APIs for the interactive setup mode. | 28 | 0 | 18 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 6 | 0 | -| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 239 | 0 | 203 | 5 | +| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 240 | 0 | 204 | 5 | | kibanaUsageCollection | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 0 | 0 | 0 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 615 | 3 | 420 | 9 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 542 | 0 | 467 | 29 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 527 | 0 | 452 | 30 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 8 | 0 | 8 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 3 | 0 | 3 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 10 | -| | [Security detections response](https://github.com/orgs/elastic/teams/security-detections-response) | - | 198 | 0 | 162 | 49 | +| | [Security detections response](https://github.com/orgs/elastic/teams/security-detections-response) | - | 198 | 0 | 90 | 49 | | logstash | [Logstash](https://github.com/orgs/elastic/teams/logstash) | - | 0 | 0 | 0 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 41 | 0 | 41 | 6 | -| | [GIS](https://github.com/orgs/elastic/teams/kibana-gis) | - | 223 | 0 | 222 | 27 | +| | [GIS](https://github.com/orgs/elastic/teams/kibana-gis) | - | 224 | 0 | 223 | 25 | | | [GIS](https://github.com/orgs/elastic/teams/kibana-gis) | - | 67 | 0 | 67 | 0 | -| | [Security solution](https://github.com/orgs/elastic/teams/security-solution) | - | 4 | 0 | 4 | 1 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the machine learning features provided by Elastic. | 196 | 8 | 79 | 30 | | | [Stack Monitoring](https://github.com/orgs/elastic/teams/stack-monitoring-ui) | - | 11 | 0 | 9 | 1 | | | [Stack Monitoring](https://github.com/orgs/elastic/teams/stack-monitoring-ui) | - | 9 | 0 | 9 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 34 | 0 | 34 | 2 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | -| | [Observability UI](https://github.com/orgs/elastic/teams/observability-ui) | - | 369 | 2 | 366 | 31 | +| | [Observability UI](https://github.com/orgs/elastic/teams/observability-ui) | - | 373 | 2 | 370 | 30 | | | [Security asset management](https://github.com/orgs/elastic/teams/security-asset-management) | - | 13 | 0 | 13 | 0 | | painlessLab | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Presentation Utility Plugin is a set of common, shared components and toolkits for solutions within the Presentation space, (e.g. Dashboards, Canvas). | 228 | 2 | 177 | 11 | @@ -121,31 +120,31 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 110 | 0 | 97 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 54 | 0 | 50 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 90 | 0 | 45 | 0 | -| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 33 | 0 | 14 | 0 | -| | [Kibana Reporting Services](https://github.com/orgs/elastic/teams/kibana-reporting-services) | Kibana Screenshotting Plugin | 24 | 0 | 12 | 5 | +| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 32 | 0 | 13 | 0 | +| | [Kibana Reporting Services](https://github.com/orgs/elastic/teams/kibana-reporting-services) | Kibana Screenshotting Plugin | 27 | 0 | 7 | 4 | | searchprofiler | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | | [Platform 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. | 183 | 0 | 103 | 0 | -| | [Security solution](https://github.com/orgs/elastic/teams/security-solution) | - | 45 | 0 | 45 | 18 | +| | [Security solution](https://github.com/orgs/elastic/teams/security-solution) | - | 46 | 0 | 46 | 19 | | | [Security Team](https://github.com/orgs/elastic/teams/security-team) | - | 3 | 0 | 3 | 1 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds URL Service and sharing capabilities to Kibana | 113 | 0 | 54 | 10 | | | [Shared UX](https://github.com/orgs/elastic/teams/shared-ux) | A plugin providing components and services for shared user experiences in Kibana. | 4 | 0 | 0 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 21 | 1 | 21 | 1 | | | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides the Spaces feature, which allows saved objects to be organized into meaningful categories. | 260 | 0 | 64 | 0 | | | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 4 | 0 | 4 | 0 | +| synthetics | [Uptime](https://github.com/orgs/elastic/teams/uptime) | This plugin visualizes data from Synthetics and Heartbeat, and integrates with other Observability solutions. | 0 | 0 | 0 | 0 | | | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 77 | 0 | 39 | 7 | -| | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 43 | 0 | 1 | 0 | +| | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 44 | 0 | 1 | 0 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 32 | 0 | 32 | 6 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 1 | 0 | 1 | 0 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 11 | 0 | 10 | 0 | -| | [Security solution](https://github.com/orgs/elastic/teams/security-solution) | - | 435 | 1 | 331 | 35 | +| | [Security solution](https://github.com/orgs/elastic/teams/security-solution) | - | 434 | 1 | 330 | 35 | | | [Machine Learning 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 | [Kibana Localization](https://github.com/orgs/elastic/teams/kibana-localization) | - | 0 | 0 | 0 | 0 | -| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 304 | 0 | 290 | 23 | +| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 321 | 0 | 307 | 25 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds UI Actions service to Kibana | 130 | 0 | 91 | 11 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Extends UI Actions plugin with more functionality | 203 | 0 | 141 | 9 | -| | [Unified Search](https://github.com/orgs/elastic/teams/kibana-app-services) | Contains all the key functionality of Kibana's unified search experience.Contains all the key functionality of Kibana's unified search experience. | 48 | 1 | 45 | 6 | +| | [Unified Search](https://github.com/orgs/elastic/teams/kibana-app-services) | Contains all the key functionality of Kibana's unified search experience.Contains all the key functionality of Kibana's unified search experience. | 79 | 2 | 75 | 11 | | upgradeAssistant | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | -| uptime | [Uptime](https://github.com/orgs/elastic/teams/uptime) | This plugin visualizes data from Synthetics and Heartbeat, and integrates with other Observability solutions. | 0 | 0 | 0 | 0 | | urlDrilldown | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds drilldown implementations to Kibana | 0 | 0 | 0 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 12 | 0 | 12 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 58 | 0 | 15 | 1 | @@ -163,47 +162,57 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Registers the vega visualization. Is the elastic version of vega and vega-lite libraries. | 2 | 0 | 2 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the vislib visualizations. These are the classical area/line/bar, pie, gauge/goal and heatmap charts. We want to replace them with elastic-charts. | 26 | 0 | 25 | 1 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the new xy-axis chart using the elastic-charts library, which will eventually replace the vislib xy-axis charts including bar, area, and line. | 57 | 0 | 51 | 5 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the shared architecture among all the legacy visualizations, e.g. the visualization type registry or the visualization embeddable. | 363 | 12 | 342 | 14 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the shared architecture among all the legacy visualizations, e.g. the visualization type registry or the visualization embeddable. | 365 | 12 | 344 | 14 | | watcher | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | ## Package Directory | Package name           | Maintaining team | Description | API Cnt | Any Cnt | Missing
comments | Missing
exports | |--------------|----------------|-----------|--------------|----------|---------------|--------| -| | [Owner missing] | - | 82 | 1 | 11 | 0 | -| | [Owner missing] | Elastic APM trace data generator | 62 | 0 | 62 | 9 | -| | [Owner missing] | elasticsearch datemath parser, used in kibana | 44 | 0 | 43 | 0 | +| | [Owner missing] | Elastic APM trace data generator | 70 | 0 | 70 | 10 | | | [Owner missing] | - | 11 | 5 | 11 | 0 | | | [Owner missing] | Alerts components and hooks | 9 | 1 | 9 | 0 | | | Ahmad Bamieh ahmadbamieh@gmail.com | Kibana Analytics tool | 69 | 0 | 69 | 2 | +| | [Owner missing] | - | 88 | 1 | 10 | 0 | +| | [Owner missing] | - | 18 | 0 | 13 | 0 | | | [Owner missing] | - | 16 | 0 | 16 | 0 | | | [Owner missing] | - | 11 | 0 | 11 | 0 | | | [Owner missing] | - | 10 | 0 | 10 | 0 | -| | [Owner missing] | - | 12 | 0 | 5 | 1 | +| | [Owner missing] | - | 18 | 0 | 9 | 1 | +| | [Owner missing] | - | 4 | 0 | 4 | 0 | +| | [Owner missing] | - | 8 | 0 | 7 | 0 | +| | [Owner missing] | - | 7 | 0 | 2 | 0 | +| | [Owner missing] | - | 59 | 0 | 15 | 0 | | | [Owner missing] | - | 2 | 0 | 2 | 0 | -| | [Owner missing] | - | 66 | 0 | 46 | 2 | -| | [Owner missing] | - | 125 | 3 | 123 | 17 | +| | [Owner missing] | - | 106 | 0 | 80 | 1 | +| | [Owner missing] | - | 64 | 0 | 44 | 2 | +| | [Owner missing] | - | 129 | 3 | 127 | 17 | | | [Owner missing] | - | 13 | 0 | 7 | 0 | -| | [Owner missing] | - | 277 | 3 | 199 | 1 | -| | [Owner missing] | - | 63 | 0 | 63 | 2 | +| | [Owner missing] | elasticsearch datemath parser, used in kibana | 44 | 0 | 43 | 0 | +| | [Owner missing] | - | 120 | 3 | 109 | 0 | +| | [Owner missing] | - | 64 | 0 | 64 | 2 | | | [Owner missing] | - | 1 | 0 | 1 | 0 | | | [Owner missing] | - | 27 | 0 | 14 | 1 | | | [Owner missing] | - | 213 | 1 | 159 | 11 | -| | [Owner missing] | - | 13 | 0 | 6 | 0 | +| | [Owner missing] | - | 2 | 0 | 1 | 0 | | | [Owner missing] | - | 20 | 0 | 16 | 0 | +| | [Owner missing] | - | 2 | 0 | 0 | 0 | | | [Owner missing] | - | 1 | 0 | 0 | 0 | | | [Owner missing] | - | 51 | 0 | 48 | 0 | +| | [Owner missing] | - | 43 | 0 | 36 | 0 | | | App Services | - | 35 | 4 | 35 | 0 | | | [Owner missing] | - | 20 | 0 | 20 | 2 | +| | [Owner missing] | - | 13 | 0 | 13 | 0 | +| | [Owner missing] | - | 69 | 0 | 69 | 0 | | | [Owner missing] | - | 30 | 0 | 5 | 36 | | | [Owner missing] | - | 8 | 0 | 8 | 0 | -| | [Owner missing] | - | 466 | 1 | 1 | 0 | +| | [Owner missing] | - | 494 | 1 | 1 | 0 | | | [Owner missing] | - | 55 | 0 | 55 | 2 | | | [Owner missing] | - | 45 | 0 | 45 | 10 | -| | [Owner missing] | - | 28 | 0 | 27 | 0 | +| | [Owner missing] | - | 30 | 0 | 29 | 0 | | | [Owner missing] | - | 1 | 0 | 1 | 0 | | | [Owner missing] | Just some helpers for kibana plugin devs. | 1 | 0 | 1 | 0 | -| | [Owner missing] | - | 63 | 0 | 49 | 5 | +| | [Owner missing] | - | 47 | 0 | 35 | 5 | | | [Owner missing] | - | 21 | 0 | 10 | 0 | | | [Owner missing] | - | 74 | 0 | 71 | 0 | | | [Owner missing] | Security Solution auto complete | 50 | 1 | 35 | 0 | @@ -222,18 +231,22 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Owner missing] | security solution utilities to use across plugins such lists, security_solution, cases, etc... | 31 | 0 | 29 | 0 | | | [Owner missing] | - | 53 | 0 | 50 | 1 | | | [Owner missing] | - | 25 | 0 | 24 | 1 | -| | [Owner missing] | - | 32 | 0 | 4 | 4 | +| | [Owner missing] | - | 12 | 0 | 2 | 3 | +| | [Owner missing] | - | 34 | 0 | 6 | 6 | | | [Owner missing] | - | 67 | 0 | 43 | 0 | | | [Owner missing] | - | 10 | 0 | 2 | 0 | | | [Owner missing] | - | 9 | 0 | 3 | 0 | -| | [Owner missing] | - | 96 | 1 | 63 | 2 | +| | [Owner missing] | - | 2 | 0 | 2 | 0 | +| | [Owner missing] | - | 92 | 1 | 59 | 1 | +| | [Owner missing] | - | 4 | 0 | 2 | 0 | | | Operations | - | 22 | 2 | 21 | 0 | | | [Owner missing] | - | 2 | 0 | 2 | 0 | | | Operations | - | 252 | 6 | 214 | 9 | | | [Owner missing] | - | 132 | 8 | 103 | 2 | +| | [Owner missing] | - | 72 | 0 | 55 | 0 | | | [Owner missing] | - | 29 | 0 | 2 | 0 | | | [Owner missing] | - | 83 | 0 | 83 | 1 | | | [Owner missing] | - | 7 | 0 | 6 | 0 | -| | [Owner missing] | - | 27 | 0 | 10 | 1 | +| | [Owner missing] | - | 28 | 0 | 10 | 1 | | | [Owner missing] | - | 31 | 1 | 21 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index ad2ac1542b0e2..edceb320dace9 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github summary: API docs for the presentationUtil plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] warning: 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. --- diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index b4d4cc4459eb6..a05a7407d5658 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github summary: API docs for the remoteClusters plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] warning: 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. --- diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index 976c1fc50da8d..185a48b5d4218 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github summary: API docs for the reporting plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] warning: 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. --- diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index febdb7ef3b9de..b9e0c8be22113 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github summary: API docs for the rollup plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] warning: 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. --- diff --git a/api_docs/rule_registry.devdocs.json b/api_docs/rule_registry.devdocs.json index 7bf4b15d0ef30..194d13b78917a 100644 --- a/api_docs/rule_registry.devdocs.json +++ b/api_docs/rule_registry.devdocs.json @@ -1376,9 +1376,7 @@ }, ">) => Promise; id: string; name: string; validate?: { params?: ", "RuleTypeParamsValidator", - " | undefined; } | undefined; cancelAlertsOnRuleTimeout?: boolean | undefined; config?: ", - "RuleTypeConfig", - " | undefined; actionGroups: ", + " | undefined; } | undefined; cancelAlertsOnRuleTimeout?: boolean | undefined; actionGroups: ", { "pluginId": "alerting", "scope": "common", diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 854a6a476ec5a..e9a4921bf6d5a 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github summary: API docs for the ruleRegistry plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] warning: 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. --- diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index 8a045ddd337d6..f582632ea2d18 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github summary: API docs for the runtimeFields plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] warning: 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. --- diff --git a/api_docs/saved_objects.devdocs.json b/api_docs/saved_objects.devdocs.json index c8dbdfc152759..fe5794ec8c9ef 100644 --- a/api_docs/saved_objects.devdocs.json +++ b/api_docs/saved_objects.devdocs.json @@ -587,14 +587,6 @@ "plugin": "embeddable", "path": "src/plugins/embeddable/public/lib/attribute_service/attribute_service.tsx" }, - { - "plugin": "presentationUtil", - "path": "src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx" - }, - { - "plugin": "presentationUtil", - "path": "src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx" - }, { "plugin": "discover", "path": "src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx" @@ -603,6 +595,14 @@ "plugin": "discover", "path": "src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx" }, + { + "plugin": "presentationUtil", + "path": "src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx" + }, + { + "plugin": "presentationUtil", + "path": "src/plugins/presentation_util/public/components/saved_object_save_modal_dashboard.tsx" + }, { "plugin": "dashboard", "path": "src/plugins/dashboard/public/services/saved_objects.ts" @@ -1607,22 +1607,6 @@ "plugin": "savedObjectsTaggingOss", "path": "src/plugins/saved_objects_tagging_oss/public/api.ts" }, - { - "plugin": "visualizations", - "path": "src/plugins/visualizations/public/utils/saved_objects_utils/display_duplicate_title_confirm_modal.ts" - }, - { - "plugin": "visualizations", - "path": "src/plugins/visualizations/public/utils/saved_objects_utils/display_duplicate_title_confirm_modal.ts" - }, - { - "plugin": "visualizations", - "path": "src/plugins/visualizations/public/utils/saved_objects_utils/check_for_duplicate_title.ts" - }, - { - "plugin": "visualizations", - "path": "src/plugins/visualizations/public/utils/saved_objects_utils/check_for_duplicate_title.ts" - }, { "plugin": "savedObjectsTaggingOss", "path": "src/plugins/saved_objects_tagging_oss/public/decorator/types.ts" @@ -1639,6 +1623,22 @@ "plugin": "savedObjectsTaggingOss", "path": "src/plugins/saved_objects_tagging_oss/public/api.ts" }, + { + "plugin": "visualizations", + "path": "src/plugins/visualizations/public/utils/saved_objects_utils/display_duplicate_title_confirm_modal.ts" + }, + { + "plugin": "visualizations", + "path": "src/plugins/visualizations/public/utils/saved_objects_utils/display_duplicate_title_confirm_modal.ts" + }, + { + "plugin": "visualizations", + "path": "src/plugins/visualizations/public/utils/saved_objects_utils/check_for_duplicate_title.ts" + }, + { + "plugin": "visualizations", + "path": "src/plugins/visualizations/public/utils/saved_objects_utils/check_for_duplicate_title.ts" + }, { "plugin": "dashboard", "path": "src/plugins/dashboard/public/services/saved_object_loader.ts" @@ -3191,44 +3191,7 @@ "path": "src/plugins/saved_objects/public/plugin.ts", "deprecated": true, "removeBy": "8.8.0", - "references": [ - { - "plugin": "visualizations", - "path": "src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx" - }, - { - "plugin": "visualizations", - "path": "src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx" - }, - { - "plugin": "dashboard", - "path": "src/plugins/dashboard/public/application/listing/dashboard_listing.tsx" - }, - { - "plugin": "dashboard", - "path": "src/plugins/dashboard/public/application/listing/dashboard_listing.tsx" - }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx" - }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx" - }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx" - }, - { - "plugin": "graph", - "path": "x-pack/plugins/graph/public/apps/listing_route.tsx" - }, - { - "plugin": "graph", - "path": "x-pack/plugins/graph/public/apps/listing_route.tsx" - } - ] + "references": [] } ], "lifecycle": "start", diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index 977cd0b83c007..204b0f19f5d68 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github summary: API docs for the savedObjects plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] warning: 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. --- diff --git a/api_docs/saved_objects_management.devdocs.json b/api_docs/saved_objects_management.devdocs.json index a7fe1019e0858..baa484f82f10d 100644 --- a/api_docs/saved_objects_management.devdocs.json +++ b/api_docs/saved_objects_management.devdocs.json @@ -278,7 +278,7 @@ "label": "euiColumn", "description": [], "signature": [ - "{ children?: React.ReactNode; onClick?: React.MouseEventHandler | undefined; onChange?: React.FormEventHandler | undefined; color?: string | undefined; onKeyDown?: React.KeyboardEventHandler | undefined; className?: string | undefined; title?: string | undefined; id?: string | undefined; description?: string | undefined; security?: string | undefined; name: React.ReactNode; field: (string & {}) | keyof ", + "{ children?: React.ReactNode; color?: string | undefined; className?: string | undefined; title?: string | undefined; onChange?: React.FormEventHandler | undefined; onKeyDown?: React.KeyboardEventHandler | undefined; onClick?: React.MouseEventHandler | undefined; id?: string | undefined; description?: string | undefined; security?: string | undefined; name: React.ReactNode; field: (string & {}) | keyof ", { "pluginId": "savedObjectsManagement", "scope": "public", @@ -501,7 +501,7 @@ "label": "obj", "description": [], "signature": [ - "{ type: string; id: string; meta: { title?: string | undefined; icon?: string | undefined; }; overwrite?: boolean | undefined; }" + "{ id: string; type: string; meta: { title?: string | undefined; icon?: string | undefined; }; overwrite?: boolean | undefined; }" ], "path": "src/plugins/saved_objects_management/public/lib/process_import_response.ts", "deprecated": false diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index 19d9efeb08286..0f610adb83a73 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github summary: API docs for the savedObjectsManagement plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] warning: 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. --- diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index 10b3bdaa00b3f..e8b5d1d6a8d6b 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github summary: API docs for the savedObjectsTagging plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] warning: 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. --- diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index 9d5079950e171..ed29900a22d7a 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github summary: API docs for the savedObjectsTaggingOss plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] warning: 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. --- diff --git a/api_docs/screenshot_mode.devdocs.json b/api_docs/screenshot_mode.devdocs.json index 65ac948800747..fead68be2cb62 100644 --- a/api_docs/screenshot_mode.devdocs.json +++ b/api_docs/screenshot_mode.devdocs.json @@ -96,51 +96,7 @@ "initialIsOpen": false } ], - "interfaces": [ - { - "parentPluginId": "screenshotMode", - "id": "def-server.ScreenshotModeRequestHandlerContext", - "type": "Interface", - "tags": [], - "label": "ScreenshotModeRequestHandlerContext", - "description": [], - "signature": [ - { - "pluginId": "screenshotMode", - "scope": "server", - "docId": "kibScreenshotModePluginApi", - "section": "def-server.ScreenshotModeRequestHandlerContext", - "text": "ScreenshotModeRequestHandlerContext" - }, - " extends ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - } - ], - "path": "src/plugins/screenshot_mode/server/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "screenshotMode", - "id": "def-server.ScreenshotModeRequestHandlerContext.screenshotMode", - "type": "Object", - "tags": [], - "label": "screenshotMode", - "description": [], - "signature": [ - "{ isScreenshot: boolean; }" - ], - "path": "src/plugins/screenshot_mode/server/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - } - ], + "interfaces": [], "enums": [], "misc": [ { @@ -167,6 +123,27 @@ "path": "src/plugins/screenshot_mode/common/constants.ts", "deprecated": false, "initialIsOpen": false + }, + { + "parentPluginId": "screenshotMode", + "id": "def-server.ScreenshotModeRequestHandlerContext", + "type": "Type", + "tags": [], + "label": "ScreenshotModeRequestHandlerContext", + "description": [], + "signature": [ + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.RequestHandlerContext", + "text": "RequestHandlerContext" + }, + " & { screenshotMode: Promise<{ isScreenshot: boolean; }>; }" + ], + "path": "src/plugins/screenshot_mode/server/types.ts", + "deprecated": false, + "initialIsOpen": false } ], "objects": [], diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index ad29af1bd007a..5782e32098b7f 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github summary: API docs for the screenshotMode plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 33 | 0 | 14 | 0 | +| 32 | 0 | 13 | 0 | ## Client @@ -42,9 +42,6 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services ### Functions -### Interfaces - - ### Consts, variables and types diff --git a/api_docs/screenshotting.devdocs.json b/api_docs/screenshotting.devdocs.json index fe79cd45375c2..7b1a506b3accb 100644 --- a/api_docs/screenshotting.devdocs.json +++ b/api_docs/screenshotting.devdocs.json @@ -4,8 +4,47 @@ "classes": [], "functions": [], "interfaces": [], - "enums": [], - "misc": [], + "enums": [ + { + "parentPluginId": "screenshotting", + "id": "def-public.LayoutTypes", + "type": "Enum", + "tags": [], + "label": "LayoutTypes", + "description": [ + "\nSupported layout types." + ], + "path": "x-pack/plugins/screenshotting/common/layout.ts", + "deprecated": false, + "initialIsOpen": false + } + ], + "misc": [ + { + "parentPluginId": "screenshotting", + "id": "def-public.LayoutParams", + "type": "Type", + "tags": [], + "label": "LayoutParams", + "description": [ + "\nScreenshot layout parameters." + ], + "signature": [ + "{ id?: Id | undefined; dimensions?: { width: number; height: number; } | undefined; selectors?: Partial<", + "LayoutSelectorDictionary", + "> | undefined; zoom?: number | undefined; } extends ", + "SerializableRecord", + " ? ", + "SerializableRecord", + " & { id?: Id | undefined; dimensions?: { width: number; height: number; } | undefined; selectors?: Partial<", + "LayoutSelectorDictionary", + "> | undefined; zoom?: number | undefined; } : never" + ], + "path": "x-pack/plugins/screenshotting/common/layout.ts", + "deprecated": false, + "initialIsOpen": false + } + ], "objects": [] }, "server": { @@ -30,19 +69,35 @@ "text": "PdfScreenshotOptions" }, " extends ", - "ScreenshotOptions", - "<\"pdf\">" + "CaptureOptions" ], "path": "x-pack/plugins/screenshotting/server/formats/pdf/index.ts", "deprecated": false, "children": [ + { + "parentPluginId": "screenshotting", + "id": "def-server.PdfScreenshotOptions.format", + "type": "string", + "tags": [], + "label": "format", + "description": [ + "\nWhether to format the output as a PDF." + ], + "signature": [ + "\"pdf\"" + ], + "path": "x-pack/plugins/screenshotting/server/formats/pdf/index.ts", + "deprecated": false + }, { "parentPluginId": "screenshotting", "id": "def-server.PdfScreenshotOptions.title", "type": "string", "tags": [], "label": "title", - "description": [], + "description": [ + "\nDocument title." + ], "signature": [ "string | undefined" ], @@ -55,7 +110,9 @@ "type": "string", "tags": [], "label": "logo", - "description": [], + "description": [ + "\nLogo at the footer." + ], "signature": [ "string | undefined" ], @@ -72,9 +129,17 @@ "\nWe default to the \"print\" layout if no ID is specified for the layout" ], "signature": [ - "{ id?: \"canvas\" | \"print\" | \"preserve_layout\" | undefined; dimensions?: { width: number; height: number; } | undefined; selectors?: Partial<", + "{ id?: ", + { + "pluginId": "screenshotting", + "scope": "common", + "docId": "kibScreenshottingPluginApi", + "section": "def-common.LayoutTypes", + "text": "LayoutTypes" + }, + " | undefined; dimensions?: { width: number; height: number; } | undefined; selectors?: Partial<", "LayoutSelectorDictionary", - "> | undefined; zoom?: number | undefined; }" + "> | undefined; zoom?: number | undefined; } | undefined" ], "path": "x-pack/plugins/screenshotting/server/formats/pdf/index.ts", "deprecated": false @@ -91,50 +156,65 @@ "description": [ "\nFinal, formatted PDF result" ], - "signature": [ - { - "pluginId": "screenshotting", - "scope": "server", - "docId": "kibScreenshottingPluginApi", - "section": "def-server.PdfScreenshotResult", - "text": "PdfScreenshotResult" - }, - " extends Omit<", - { - "pluginId": "screenshotting", - "scope": "server", - "docId": "kibScreenshottingPluginApi", - "section": "def-server.FormattedScreenshotResult", - "text": "FormattedScreenshotResult" - }, - ", \"results\">" - ], "path": "x-pack/plugins/screenshotting/server/formats/pdf/index.ts", "deprecated": false, "children": [ { "parentPluginId": "screenshotting", "id": "def-server.PdfScreenshotResult.metrics", - "type": "CompoundType", + "type": "Object", "tags": [], "label": "metrics", - "description": [], + "description": [ + "\nCollected performance metrics during the screenshotting session along with the PDF generation ones." + ], "signature": [ - "PerformanceMetrics", - " & { pageCount: number; }" + "PdfScreenshotMetrics" ], "path": "x-pack/plugins/screenshotting/server/formats/pdf/index.ts", "deprecated": false }, { "parentPluginId": "screenshotting", - "id": "def-server.PdfScreenshotResult.result", + "id": "def-server.PdfScreenshotResult.data", "type": "Object", "tags": [], - "label": "result", - "description": [], + "label": "data", + "description": [ + "\nPDF document data buffer." + ], "signature": [ - "{ data: Buffer; errors: Error[]; renderErrors: string[]; }" + "Buffer" + ], + "path": "x-pack/plugins/screenshotting/server/formats/pdf/index.ts", + "deprecated": false + }, + { + "parentPluginId": "screenshotting", + "id": "def-server.PdfScreenshotResult.errors", + "type": "Array", + "tags": [], + "label": "errors", + "description": [ + "\nAny errors that were encountered while create the PDF and navigating between pages" + ], + "signature": [ + "Error[]" + ], + "path": "x-pack/plugins/screenshotting/server/formats/pdf/index.ts", + "deprecated": false + }, + { + "parentPluginId": "screenshotting", + "id": "def-server.PdfScreenshotResult.renderErrors", + "type": "Array", + "tags": [], + "label": "renderErrors", + "description": [ + "\nAny render errors that could mean some visualizations are missing from the end result." + ], + "signature": [ + "string[]" ], "path": "x-pack/plugins/screenshotting/server/formats/pdf/index.ts", "deprecated": false @@ -160,68 +240,47 @@ "text": "PngScreenshotOptions" }, " extends ", - "ScreenshotOptions", - "<\"png\">" + "CaptureOptions" ], "path": "x-pack/plugins/screenshotting/server/formats/png.ts", "deprecated": false, "children": [ { "parentPluginId": "screenshotting", - "id": "def-server.PngScreenshotOptions.layout", - "type": "Object", - "tags": [], - "label": "layout", - "description": [], + "id": "def-server.PngScreenshotOptions.format", + "type": "string", + "tags": [ + "default" + ], + "label": "format", + "description": [ + "\nWhether to format the output as a PNG." + ], "signature": [ - "{ id?: \"preserve_layout\" | undefined; dimensions?: { width: number; height: number; } | undefined; selectors?: Partial<", - "LayoutSelectorDictionary", - "> | undefined; zoom?: number | undefined; }" + "\"png\" | undefined" ], "path": "x-pack/plugins/screenshotting/server/formats/png.ts", "deprecated": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "screenshotting", - "id": "def-server.PngScreenshotResult", - "type": "Interface", - "tags": [], - "label": "PngScreenshotResult", - "description": [ - "\nThe final output of a PNG screenshot" - ], - "signature": [ - { - "pluginId": "screenshotting", - "scope": "server", - "docId": "kibScreenshottingPluginApi", - "section": "def-server.PngScreenshotResult", - "text": "PngScreenshotResult" }, - " extends ", - { - "pluginId": "screenshotting", - "scope": "server", - "docId": "kibScreenshottingPluginApi", - "section": "def-server.FormattedScreenshotResult", - "text": "FormattedScreenshotResult" - } - ], - "path": "x-pack/plugins/screenshotting/server/formats/png.ts", - "deprecated": false, - "children": [ { "parentPluginId": "screenshotting", - "id": "def-server.PngScreenshotResult.metadata", - "type": "Uncategorized", + "id": "def-server.PngScreenshotOptions.layout", + "type": "Object", "tags": [], - "label": "metadata", + "label": "layout", "description": [], "signature": [ - "undefined" + "{ id?: ", + { + "pluginId": "screenshotting", + "scope": "common", + "docId": "kibScreenshottingPluginApi", + "section": "def-common.LayoutTypes", + "text": "LayoutTypes" + }, + ".PRESERVE_LAYOUT | undefined; dimensions?: { width: number; height: number; } | undefined; selectors?: Partial<", + "LayoutSelectorDictionary", + "> | undefined; zoom?: number | undefined; } | undefined" ], "path": "x-pack/plugins/screenshotting/server/formats/png.ts", "deprecated": false @@ -234,50 +293,67 @@ "misc": [ { "parentPluginId": "screenshotting", - "id": "def-server.FormattedScreenshotResult", + "id": "def-server.PngScreenshotResult", "type": "Type", "tags": [], - "label": "FormattedScreenshotResult", + "label": "PngScreenshotResult", "description": [ - "\nA general, overridable type of screenshot result\n\nPDF or PNG screenshots should extend this and convert the output to a type\nthat best suits their use cases.\n\nThis type documents what might appear on any given output type" + "\nThe final output of a PNG screenshot" ], "signature": [ - "{ metrics?: ", - "PerformanceMetrics", - " | undefined; results: ", - "ScreenshotObservableResult", - "[]; }" + "CaptureResult" ], - "path": "x-pack/plugins/screenshotting/server/formats/types.ts", + "path": "x-pack/plugins/screenshotting/server/formats/png.ts", "deprecated": false, "initialIsOpen": false }, { "parentPluginId": "screenshotting", - "id": "def-server.Layout", + "id": "def-server.ScreenshotOptions", "type": "Type", "tags": [], - "label": "Layout", + "label": "ScreenshotOptions", "description": [], "signature": [ - "BaseLayout", - " & LayoutSelectors & Partial<{ width: number; height: number; }>" + { + "pluginId": "screenshotting", + "scope": "server", + "docId": "kibScreenshottingPluginApi", + "section": "def-server.PdfScreenshotOptions", + "text": "PdfScreenshotOptions" + }, + " | ", + { + "pluginId": "screenshotting", + "scope": "server", + "docId": "kibScreenshottingPluginApi", + "section": "def-server.PngScreenshotOptions", + "text": "PngScreenshotOptions" + } ], - "path": "x-pack/plugins/screenshotting/server/layouts/index.ts", + "path": "x-pack/plugins/screenshotting/server/screenshots/index.ts", "deprecated": false, "initialIsOpen": false }, { "parentPluginId": "screenshotting", - "id": "def-server.UrlOrUrlWithContext", + "id": "def-server.ScreenshotResult", "type": "Type", "tags": [], - "label": "UrlOrUrlWithContext", + "label": "ScreenshotResult", "description": [], "signature": [ - "string | UrlWithContext" + { + "pluginId": "screenshotting", + "scope": "server", + "docId": "kibScreenshottingPluginApi", + "section": "def-server.PdfScreenshotResult", + "text": "PdfScreenshotResult" + }, + " | ", + "CaptureResult" ], - "path": "x-pack/plugins/screenshotting/server/screenshots/observable.ts", + "path": "x-pack/plugins/screenshotting/server/screenshots/index.ts", "deprecated": false, "initialIsOpen": false } @@ -340,25 +416,29 @@ "\nTakes screenshots of multiple pages." ], "signature": [ - "; (options: ", { "pluginId": "screenshotting", "scope": "server", "docId": "kibScreenshottingPluginApi", - "section": "def-server.PngScreenshotOptions", - "text": "PngScreenshotOptions" + "section": "def-server.PdfScreenshotOptions", + "text": "PdfScreenshotOptions" }, - ">(options: O) => ", + "): ", "Observable", - "; (options: ", { "pluginId": "screenshotting", "scope": "server", "docId": "kibScreenshottingPluginApi", - "section": "def-server.PngScreenshotResult", - "text": "PngScreenshotResult" + "section": "def-server.ScreenshotOptions", + "text": "ScreenshotOptions" }, - ">" - ], - "path": "x-pack/plugins/screenshotting/server/plugin.ts", - "deprecated": false, - "children": [ + "): ", + "Observable", + "<", { - "parentPluginId": "screenshotting", - "id": "def-server.ScreenshottingStart.getScreenshots.$1", - "type": "Uncategorized", - "tags": [], - "label": "options", - "description": [ - "Screenshots session options." - ], - "signature": [ - "O" - ], - "path": "x-pack/plugins/screenshotting/server/plugin.ts", - "deprecated": false, - "isRequired": true - } + "pluginId": "screenshotting", + "scope": "server", + "docId": "kibScreenshottingPluginApi", + "section": "def-server.ScreenshotResult", + "text": "ScreenshotResult" + }, + ">; }" ], - "returnComment": [ - "Observable with screenshotting results." - ] + "path": "x-pack/plugins/screenshotting/server/plugin.ts", + "deprecated": false } ], "lifecycle": "start", @@ -409,7 +478,21 @@ "classes": [], "functions": [], "interfaces": [], - "enums": [], + "enums": [ + { + "parentPluginId": "screenshotting", + "id": "def-common.LayoutTypes", + "type": "Enum", + "tags": [], + "label": "LayoutTypes", + "description": [ + "\nSupported layout types." + ], + "path": "x-pack/plugins/screenshotting/common/layout.ts", + "deprecated": false, + "initialIsOpen": false + } + ], "misc": [ { "parentPluginId": "screenshotting", @@ -421,76 +504,63 @@ "\nScreenshot layout parameters." ], "signature": [ - "{ id?: ID | undefined; dimensions?: { width: number; height: number; } | undefined; selectors?: Partial<", + "{ id?: Id | undefined; dimensions?: { width: number; height: number; } | undefined; selectors?: Partial<", "LayoutSelectorDictionary", "> | undefined; zoom?: number | undefined; } extends ", "SerializableRecord", " ? ", "SerializableRecord", - " & { id?: ID | undefined; dimensions?: { width: number; height: number; } | undefined; selectors?: Partial<", + " & { id?: Id | undefined; dimensions?: { width: number; height: number; } | undefined; selectors?: Partial<", "LayoutSelectorDictionary", "> | undefined; zoom?: number | undefined; } : never" ], "path": "x-pack/plugins/screenshotting/common/layout.ts", "deprecated": false, "initialIsOpen": false - } - ], - "objects": [ + }, { "parentPluginId": "screenshotting", - "id": "def-common.LayoutTypes", - "type": "Object", + "id": "def-common.SCREENSHOTTING_APP_ID", + "type": "string", "tags": [], - "label": "LayoutTypes", - "description": [ - "\nSupported layout types." + "label": "SCREENSHOTTING_APP_ID", + "description": [], + "signature": [ + "\"screenshotting\"" ], - "path": "x-pack/plugins/screenshotting/common/layout.ts", + "path": "x-pack/plugins/screenshotting/common/expression.ts", "deprecated": false, - "children": [ - { - "parentPluginId": "screenshotting", - "id": "def-common.LayoutTypes.PRESERVE_LAYOUT", - "type": "string", - "tags": [], - "label": "PRESERVE_LAYOUT", - "description": [], - "signature": [ - "\"preserve_layout\"" - ], - "path": "x-pack/plugins/screenshotting/common/layout.ts", - "deprecated": false - }, - { - "parentPluginId": "screenshotting", - "id": "def-common.LayoutTypes.PRINT", - "type": "string", - "tags": [], - "label": "PRINT", - "description": [], - "signature": [ - "\"print\"" - ], - "path": "x-pack/plugins/screenshotting/common/layout.ts", - "deprecated": false - }, - { - "parentPluginId": "screenshotting", - "id": "def-common.LayoutTypes.CANVAS", - "type": "string", - "tags": [], - "label": "CANVAS", - "description": [], - "signature": [ - "\"canvas\"" - ], - "path": "x-pack/plugins/screenshotting/common/layout.ts", - "deprecated": false - } + "initialIsOpen": false + }, + { + "parentPluginId": "screenshotting", + "id": "def-common.SCREENSHOTTING_EXPRESSION", + "type": "string", + "tags": [], + "label": "SCREENSHOTTING_EXPRESSION", + "description": [], + "signature": [ + "\"expression\"" + ], + "path": "x-pack/plugins/screenshotting/common/expression.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "screenshotting", + "id": "def-common.SCREENSHOTTING_EXPRESSION_INPUT", + "type": "string", + "tags": [], + "label": "SCREENSHOTTING_EXPRESSION_INPUT", + "description": [], + "signature": [ + "\"input\"" ], + "path": "x-pack/plugins/screenshotting/common/expression.ts", + "deprecated": false, "initialIsOpen": false } - ] + ], + "objects": [] } } \ No newline at end of file diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index fb6bac64aa793..c61700bd608f0 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github summary: API docs for the screenshotting plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] warning: 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. --- @@ -18,7 +18,15 @@ Contact [Kibana Reporting Services](https://github.com/orgs/elastic/teams/kibana | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 24 | 0 | 12 | 5 | +| 27 | 0 | 7 | 4 | + +## Client + +### Enums + + +### Consts, variables and types + ## Server @@ -33,8 +41,8 @@ Contact [Kibana Reporting Services](https://github.com/orgs/elastic/teams/kibana ## Common -### Objects - +### Enums + ### Consts, variables and types diff --git a/api_docs/security.mdx b/api_docs/security.mdx index c2ca4891fb327..bcb2ef6b82133 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github summary: API docs for the security plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] warning: 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. --- diff --git a/api_docs/security_solution.devdocs.json b/api_docs/security_solution.devdocs.json index b8e6e83318a0b..07770d1883ef0 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -62,7 +62,7 @@ "label": "experimentalFeatures", "description": [], "signature": [ - "{ readonly metricsEntitiesEnabled: boolean; readonly ruleRegistryEnabled: boolean; readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly usersEnabled: boolean; readonly detectionResponseEnabled: boolean; readonly disableIsolationUIPendingStatuses: boolean; readonly riskyHostsEnabled: boolean; readonly riskyUsersEnabled: boolean; readonly securityRulesCancelEnabled: boolean; readonly pendingActionResponsesWithAck: boolean; readonly policyListEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly responseActionsConsoleEnabled: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly usersEnabled: boolean; readonly detectionResponseEnabled: boolean; readonly disableIsolationUIPendingStatuses: boolean; readonly riskyHostsEnabled: boolean; readonly riskyUsersEnabled: boolean; readonly pendingActionResponsesWithAck: boolean; readonly policyListEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly responseActionsConsoleEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/public/plugin.tsx", "deprecated": false @@ -302,7 +302,9 @@ "PinnedEvent", ">; resolveTimelineConfig?: ", "ResolveTimelineConfig", - " | undefined; showSaveModal?: boolean | undefined; savedQueryId?: string | null | undefined; sessionViewId: string | null; show: boolean; status: ", + " | undefined; showSaveModal?: boolean | undefined; savedQueryId?: string | null | undefined; sessionViewConfig: ", + "SessionViewConfig", + " | null; show: boolean; status: ", "TimelineStatus", "; updated?: number | undefined; updatedBy?: string | null | undefined; isSaving: boolean; version: string | null; initialized?: boolean | undefined; }" ], @@ -704,26 +706,28 @@ "tags": [], "label": "SecuritySolutionApiRequestHandlerContext", "description": [], - "signature": [ - { - "pluginId": "securitySolution", - "scope": "server", - "docId": "kibSecuritySolutionPluginApi", - "section": "def-server.SecuritySolutionApiRequestHandlerContext", - "text": "SecuritySolutionApiRequestHandlerContext" - }, - " extends ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.RequestHandlerContext", - "text": "RequestHandlerContext" - } - ], "path": "x-pack/plugins/security_solution/server/types.ts", "deprecated": false, "children": [ + { + "parentPluginId": "securitySolution", + "id": "def-server.SecuritySolutionApiRequestHandlerContext.core", + "type": "Object", + "tags": [], + "label": "core", + "description": [], + "signature": [ + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.CoreRequestHandlerContext", + "text": "CoreRequestHandlerContext" + } + ], + "path": "x-pack/plugins/security_solution/server/types.ts", + "deprecated": false + }, { "parentPluginId": "securitySolution", "id": "def-server.SecuritySolutionApiRequestHandlerContext.endpointAuthz", @@ -887,7 +891,7 @@ "label": "ConfigType", "description": [], "signature": [ - "Readonly<{} & { signalsIndex: string; maxRuleImportExportSize: number; maxRuleImportPayloadBytes: number; maxTimelineImportExportSize: number; maxTimelineImportPayloadBytes: number; alertMergeStrategy: \"allFields\" | \"missingFields\" | \"noFields\"; alertIgnoreFields: string[]; enableExperimental: string[]; packagerTaskInterval: string; prebuiltRulesFromFileSystem: boolean; prebuiltRulesFromSavedObjects: boolean; }> & { experimentalFeatures: Readonly<{ metricsEntitiesEnabled: boolean; ruleRegistryEnabled: boolean; tGridEnabled: boolean; tGridEventRenderedViewEnabled: boolean; excludePoliciesInFilterEnabled: boolean; usersEnabled: boolean; detectionResponseEnabled: boolean; disableIsolationUIPendingStatuses: boolean; riskyHostsEnabled: boolean; riskyUsersEnabled: boolean; securityRulesCancelEnabled: boolean; pendingActionResponsesWithAck: boolean; policyListEnabled: boolean; previewTelemetryUrlEnabled: boolean; responseActionsConsoleEnabled: boolean; }>; }" + "Readonly<{} & { signalsIndex: string; maxRuleImportExportSize: number; maxRuleImportPayloadBytes: number; maxTimelineImportExportSize: number; maxTimelineImportPayloadBytes: number; alertMergeStrategy: \"allFields\" | \"missingFields\" | \"noFields\"; alertIgnoreFields: string[]; enableExperimental: string[]; packagerTaskInterval: string; prebuiltRulesFromFileSystem: boolean; prebuiltRulesFromSavedObjects: boolean; }> & { experimentalFeatures: Readonly<{ tGridEnabled: boolean; tGridEventRenderedViewEnabled: boolean; excludePoliciesInFilterEnabled: boolean; usersEnabled: boolean; detectionResponseEnabled: boolean; disableIsolationUIPendingStatuses: boolean; riskyHostsEnabled: boolean; riskyUsersEnabled: boolean; pendingActionResponsesWithAck: boolean; policyListEnabled: boolean; previewTelemetryUrlEnabled: boolean; responseActionsConsoleEnabled: boolean; }>; }" ], "path": "x-pack/plugins/security_solution/server/config.ts", "deprecated": false, diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index 52564d9c3f7ad..12032a091582a 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github summary: API docs for the securitySolution plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Security solution](https://github.com/orgs/elastic/teams/security-solut | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 45 | 0 | 45 | 18 | +| 46 | 0 | 46 | 19 | ## Client diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 8d8ff594c127f..86ade737ae827 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github summary: API docs for the sessionView plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] warning: 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. --- diff --git a/api_docs/share.mdx b/api_docs/share.mdx index 04dfc6ca76d26..571fa5d590783 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github summary: API docs for the share plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] warning: 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. --- diff --git a/api_docs/shared_u_x.mdx b/api_docs/shared_u_x.mdx index 555c0c3a3c255..d255d87e8bd8e 100644 --- a/api_docs/shared_u_x.mdx +++ b/api_docs/shared_u_x.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/sharedUX title: "sharedUX" image: https://source.unsplash.com/400x175/?github summary: API docs for the sharedUX plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sharedUX'] warning: 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. --- diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index de02f4b4b2387..ee9d6cb524f39 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github summary: API docs for the snapshotRestore plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] warning: 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. --- diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index cd2fa16dd5876..6c7d81d1ad99c 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github summary: API docs for the spaces plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] warning: 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. --- diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index c48f110748511..dedfaf95ecd53 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github summary: API docs for the stackAlerts plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] warning: 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. --- diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index cade7c0b9b88e..87ff82d41875d 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github summary: API docs for the taskManager plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] warning: 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. --- diff --git a/api_docs/telemetry.devdocs.json b/api_docs/telemetry.devdocs.json index c95f1325348d3..c1b9f1acae9fa 100644 --- a/api_docs/telemetry.devdocs.json +++ b/api_docs/telemetry.devdocs.json @@ -158,6 +158,21 @@ ], "path": "src/plugins/telemetry/public/plugin.ts", "deprecated": false + }, + { + "parentPluginId": "telemetry", + "id": "def-public.TelemetryPluginConfig.hidePrivacyStatement", + "type": "CompoundType", + "tags": [], + "label": "hidePrivacyStatement", + "description": [ + "Should we hide the privacy statement notice? Useful on some environments, e.g. Cloud" + ], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/telemetry/public/plugin.ts", + "deprecated": false } ], "initialIsOpen": false diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index 94652e3525420..29c538adcdd73 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github summary: API docs for the telemetry plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetry'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetr | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 43 | 0 | 1 | 0 | +| 44 | 0 | 1 | 0 | ## Client diff --git a/api_docs/telemetry_collection_manager.mdx b/api_docs/telemetry_collection_manager.mdx index af2ea6cc6e530..2b4bb32e9071a 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github summary: API docs for the telemetryCollectionManager plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionManager'] warning: 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. --- diff --git a/api_docs/telemetry_collection_xpack.mdx b/api_docs/telemetry_collection_xpack.mdx index 1034c582fe31a..dae291cc27ca0 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github summary: API docs for the telemetryCollectionXpack plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionXpack'] warning: 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. --- diff --git a/api_docs/telemetry_management_section.mdx b/api_docs/telemetry_management_section.mdx index 7067c7e1d3ae6..0d8f8313d1651 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github summary: API docs for the telemetryManagementSection plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] warning: 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. --- diff --git a/api_docs/timelines.devdocs.json b/api_docs/timelines.devdocs.json index 4853c844a07fe..aa0ca4894966c 100644 --- a/api_docs/timelines.devdocs.json +++ b/api_docs/timelines.devdocs.json @@ -2594,7 +2594,7 @@ "label": "TGridModelForTimeline", "description": [], "signature": [ - "{ columns: (Pick<", + "{ title: string; columns: (Pick<", "EuiDataGridColumn", ", \"id\" | \"display\" | \"displayAsText\" | \"initialWidth\"> & Pick<", "EuiDataGridColumn", @@ -2610,7 +2610,7 @@ }, "; description?: string | null | undefined; example?: string | number | null | undefined; format?: string | undefined; linkField?: string | undefined; placeholder?: string | undefined; subType?: ", "IFieldSubType", - " | undefined; type?: string | undefined; })[]; title: string; id: string; filters?: ", + " | undefined; type?: string | undefined; })[]; id: string; filters?: ", "Filter", "[] | undefined; dataViewId: string | null; sort: ", "SortColumnTimeline", @@ -4927,15 +4927,7 @@ "label": "renderRow", "description": [], "signature": [ - "({ browserFields, data, isDraggable, timelineId, }: { browserFields: Readonly>>; data: ", + "({ data, isDraggable, timelineId, }: { data: ", "Ecs", "; isDraggable: boolean; timelineId: string; }) => React.ReactNode" ], @@ -4947,32 +4939,11 @@ "id": "def-common.RowRenderer.renderRow.$1", "type": "Object", "tags": [], - "label": "{\n browserFields,\n data,\n isDraggable,\n timelineId,\n }", + "label": "{\n data,\n isDraggable,\n timelineId,\n }", "description": [], "path": "x-pack/plugins/timelines/common/types/timeline/rows/index.ts", "deprecated": false, "children": [ - { - "parentPluginId": "timelines", - "id": "def-common.RowRenderer.renderRow.$1.browserFields", - "type": "Object", - "tags": [], - "label": "browserFields", - "description": [], - "signature": [ - "{ readonly [x: string]: Partial<", - { - "pluginId": "timelines", - "scope": "common", - "docId": "kibTimelinesPluginApi", - "section": "def-common.BrowserField", - "text": "BrowserField" - }, - ">; }" - ], - "path": "x-pack/plugins/timelines/common/types/timeline/rows/index.ts", - "deprecated": false - }, { "parentPluginId": "timelines", "id": "def-common.RowRenderer.renderRow.$1.data", @@ -6439,7 +6410,7 @@ "label": "DataProvidersAnd", "description": [], "signature": [ - "{ type?: ", + "{ id: string; type?: ", { "pluginId": "timelines", "scope": "common", @@ -6447,7 +6418,7 @@ "section": "def-common.DataProviderType", "text": "DataProviderType" }, - " | undefined; id: string; name: string; enabled: boolean; excluded: boolean; kqlQuery: string; queryMatch: ", + " | 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 1c5a341eabe9a..c0002d87729aa 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github summary: API docs for the timelines plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Security solution](https://github.com/orgs/elastic/teams/security-solut | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 435 | 1 | 331 | 35 | +| 434 | 1 | 330 | 35 | ## Client diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index ea19c16ba59fe..fe76107b5c7af 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github summary: API docs for the transform plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] warning: 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. --- diff --git a/api_docs/triggers_actions_ui.devdocs.json b/api_docs/triggers_actions_ui.devdocs.json index 16508005f0572..5c5963cb8c7bd 100644 --- a/api_docs/triggers_actions_ui.devdocs.json +++ b/api_docs/triggers_actions_ui.devdocs.json @@ -54,7 +54,7 @@ "label": "experimentalFeatures", "description": [], "signature": [ - "{ readonly rulesListDatagrid: boolean; readonly internalAlertsTable: boolean; readonly rulesDetailLogs: boolean; }" + "{ readonly rulesListDatagrid: boolean; readonly internalAlertsTable: boolean; readonly internalShareableComponentsSandbox: boolean; readonly rulesDetailLogs: boolean; }" ], "path": "x-pack/plugins/triggers_actions_ui/public/plugin.ts", "deprecated": false @@ -1323,6 +1323,85 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.snoozeRule", + "type": "Function", + "tags": [], + "label": "snoozeRule", + "description": [], + "signature": [ + "({\n id,\n snoozeEndTime,\n http,\n}: { id: string; snoozeEndTime: string | -1; http: ", + { + "pluginId": "core", + "scope": "public", + "docId": "kibCoreHttpPluginApi", + "section": "def-public.HttpSetup", + "text": "HttpSetup" + }, + "; }) => Promise" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/snooze.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.snoozeRule.$1", + "type": "Object", + "tags": [], + "label": "{\n id,\n snoozeEndTime,\n http,\n}", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/snooze.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.snoozeRule.$1.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/snooze.ts", + "deprecated": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.snoozeRule.$1.snoozeEndTime", + "type": "CompoundType", + "tags": [], + "label": "snoozeEndTime", + "description": [], + "signature": [ + "string | -1" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/snooze.ts", + "deprecated": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.snoozeRule.$1.http", + "type": "Object", + "tags": [], + "label": "http", + "description": [], + "signature": [ + { + "pluginId": "core", + "scope": "public", + "docId": "kibCoreHttpPluginApi", + "section": "def-public.HttpSetup", + "text": "HttpSetup" + } + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/snooze.ts", + "deprecated": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.ThresholdExpression", @@ -1421,6 +1500,72 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.unsnoozeRule", + "type": "Function", + "tags": [], + "label": "unsnoozeRule", + "description": [], + "signature": [ + "({ id, http }: { id: string; http: ", + { + "pluginId": "core", + "scope": "public", + "docId": "kibCoreHttpPluginApi", + "section": "def-public.HttpSetup", + "text": "HttpSetup" + }, + "; }) => Promise" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/unsnooze.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.unsnoozeRule.$1", + "type": "Object", + "tags": [], + "label": "{ id, http }", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/unsnooze.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.unsnoozeRule.$1.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/unsnooze.ts", + "deprecated": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.unsnoozeRule.$1.http", + "type": "Object", + "tags": [], + "label": "http", + "description": [], + "signature": [ + { + "pluginId": "core", + "scope": "public", + "docId": "kibCoreHttpPluginApi", + "section": "def-public.HttpSetup", + "text": "HttpSetup" + } + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/unsnooze.ts", + "deprecated": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.useLoadRuleTypes", @@ -2487,6 +2632,19 @@ "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.RuleTypeParamsExpressionProps.dataViews", + "type": "Object", + "tags": [], + "label": "dataViews", + "description": [], + "signature": [ + "DataViewsServicePublic" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.RuleTypeParamsExpressionProps.unifiedSearch", @@ -2555,6 +2713,19 @@ "path": "x-pack/plugins/triggers_actions_ui/public/application/app.tsx", "deprecated": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.TriggersAndActionsUiServices.dataViews", + "type": "Object", + "tags": [], + "label": "dataViews", + "description": [], + "signature": [ + "DataViewsServicePublic" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/app.tsx", + "deprecated": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.TriggersAndActionsUiServices.charts", @@ -2760,6 +2931,25 @@ "path": "x-pack/plugins/triggers_actions_ui/public/application/app.tsx", "deprecated": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.TriggersAndActionsUiServices.alertsTableConfigurationRegistry", + "type": "Object", + "tags": [], + "label": "alertsTableConfigurationRegistry", + "description": [], + "signature": [ + "{ list: () => ", + "AlertsTableConfigurationRegistry", + "[]; get: (id: string) => ", + "AlertsTableConfigurationRegistry", + "; register: (objectType: ", + "AlertsTableConfigurationRegistry", + ") => void; has: (id: string) => boolean; }" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/app.tsx", + "deprecated": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.TriggersAndActionsUiServices.history", @@ -3031,6 +3221,20 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.PLUGIN_ID", + "type": "string", + "tags": [], + "label": "PLUGIN_ID", + "description": [], + "signature": [ + "\"triggersActions\"" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/common/constants/index.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.Rule", @@ -3993,6 +4197,22 @@ ], "path": "x-pack/plugins/triggers_actions_ui/public/plugin.ts", "deprecated": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.TriggersAndActionsUIPublicPluginSetup.alertsTableConfigurationRegistry", + "type": "Object", + "tags": [], + "label": "alertsTableConfigurationRegistry", + "description": [], + "signature": [ + "TypeRegistry", + "<", + "AlertsTableConfigurationRegistry", + ">" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/plugin.ts", + "deprecated": false } ], "lifecycle": "setup", @@ -4046,6 +4266,22 @@ "path": "x-pack/plugins/triggers_actions_ui/public/plugin.ts", "deprecated": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.TriggersAndActionsUIPublicPluginStart.alertsTableConfigurationRegistry", + "type": "Object", + "tags": [], + "label": "alertsTableConfigurationRegistry", + "description": [], + "signature": [ + "TypeRegistry", + "<", + "AlertsTableConfigurationRegistry", + ">" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/plugin.ts", + "deprecated": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.TriggersAndActionsUIPublicPluginStart.getAddConnectorFlyout", @@ -4223,6 +4459,40 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.TriggersAndActionsUIPublicPluginStart.getRuleStatusDropdown", + "type": "Function", + "tags": [], + "label": "getRuleStatusDropdown", + "description": [], + "signature": [ + "(props: ", + "ComponentOpts", + ") => React.ReactElement<", + "ComponentOpts", + ", string | React.JSXElementConstructor>" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/plugin.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.TriggersAndActionsUIPublicPluginStart.getRuleStatusDropdown.$1", + "type": "Object", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "ComponentOpts" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/plugin.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] } ], "lifecycle": "start", @@ -4625,10 +4895,10 @@ ], "label": "parseExperimentalConfigValue", "description": [ - "\nParses the string value used in `xpack.triggersActionsUi.enableExperimental` kibana configuration,\nwhich should be a string of values delimited by a comma (`,`)\n" + "\nParses the string value used in `xpack.trigger_actions_ui.enableExperimental` kibana configuration,\nwhich should be a string of values delimited by a comma (`,`)\n" ], "signature": [ - "(configValue: string[]) => Readonly<{ rulesListDatagrid: boolean; internalAlertsTable: boolean; rulesDetailLogs: boolean; }>" + "(configValue: string[]) => Readonly<{ rulesListDatagrid: boolean; internalAlertsTable: boolean; internalShareableComponentsSandbox: boolean; rulesDetailLogs: boolean; }>" ], "path": "x-pack/plugins/triggers_actions_ui/common/experimental_features.ts", "deprecated": false, @@ -4785,7 +5055,7 @@ "label": "ExperimentalFeatures", "description": [], "signature": [ - "{ readonly rulesListDatagrid: boolean; readonly internalAlertsTable: boolean; readonly rulesDetailLogs: boolean; }" + "{ readonly rulesListDatagrid: boolean; readonly internalAlertsTable: boolean; readonly internalShareableComponentsSandbox: boolean; readonly rulesDetailLogs: boolean; }" ], "path": "x-pack/plugins/triggers_actions_ui/common/experimental_features.ts", "deprecated": false, @@ -4814,10 +5084,10 @@ "tags": [], "label": "allowedExperimentalValues", "description": [ - "\nA list of allowed values that can be used in `xpack.triggersActionsUi.enableExperimental`.\nThis object is then used to validate and parse the value entered." + "\nA list of allowed values that can be used in `xpack.trigger_actions_ui.enableExperimental`.\nThis object is then used to validate and parse the value entered." ], "signature": [ - "{ readonly rulesListDatagrid: boolean; readonly internalAlertsTable: boolean; readonly rulesDetailLogs: boolean; }" + "{ readonly rulesListDatagrid: boolean; readonly internalAlertsTable: boolean; readonly internalShareableComponentsSandbox: boolean; readonly rulesDetailLogs: boolean; }" ], "path": "x-pack/plugins/triggers_actions_ui/common/experimental_features.ts", "deprecated": false, diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index 88c76bf1729fd..20c5152aa8df3 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github summary: API docs for the triggersActionsUi plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Response Ops](https://github.com/orgs/elastic/teams/response-ops) for q | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 304 | 0 | 290 | 23 | +| 321 | 0 | 307 | 25 | ## Client diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 4bd49250ad905..5038ea2fa6daa 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github summary: API docs for the uiActions plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] warning: 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. --- diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index 51e13512c54de..7aeea3d866aad 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github summary: API docs for the uiActionsEnhanced plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] warning: 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. --- diff --git a/api_docs/unified_search.devdocs.json b/api_docs/unified_search.devdocs.json index 5d2e50e061270..28d29d8e5f2db 100644 --- a/api_docs/unified_search.devdocs.json +++ b/api_docs/unified_search.devdocs.json @@ -205,8 +205,8 @@ "pluginId": "dataViews", "scope": "common", "docId": "kibDataViewsPluginApi", - "section": "def-common.IIndexPattern", - "text": "IIndexPattern" + "section": "def-common.DataView", + "text": "DataView" }, ")[]" ], @@ -547,7 +547,7 @@ "label": "nonKqlMode", "description": [], "signature": [ - "\"lucene\" | \"text\" | undefined" + "\"text\" | \"lucene\" | undefined" ], "path": "src/plugins/unified_search/public/query_string_input/query_string_input.tsx", "deprecated": false @@ -714,6 +714,31 @@ "path": "src/plugins/unified_search/public/types.ts", "deprecated": false, "children": [ + { + "parentPluginId": "unifiedSearch", + "id": "def-public.UnifiedSearchPublicPluginStart.autocomplete", + "type": "Object", + "tags": [], + "label": "autocomplete", + "description": [ + "\nautocomplete service\n{@link AutocompleteStart}" + ], + "signature": [ + "{ getQuerySuggestions: ", + { + "pluginId": "unifiedSearch", + "scope": "public", + "docId": "kibUnifiedSearchAutocompletePluginApi", + "section": "def-public.QuerySuggestionGetFn", + "text": "QuerySuggestionGetFn" + }, + "; hasQuerySuggestions: (language: string) => boolean; getValueSuggestions: ", + "ValueSuggestionsGetFn", + "; }" + ], + "path": "src/plugins/unified_search/public/types.ts", + "deprecated": false + }, { "parentPluginId": "unifiedSearch", "id": "def-public.UnifiedSearchPublicPluginStart.ui", @@ -732,15 +757,323 @@ ], "lifecycle": "start", "initialIsOpen": true + }, + "setup": { + "parentPluginId": "unifiedSearch", + "id": "def-public.UnifiedSearchPluginSetup", + "type": "Interface", + "tags": [], + "label": "UnifiedSearchPluginSetup", + "description": [], + "path": "src/plugins/unified_search/public/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "unifiedSearch", + "id": "def-public.UnifiedSearchPluginSetup.autocomplete", + "type": "Object", + "tags": [], + "label": "autocomplete", + "description": [], + "signature": [ + "{ getQuerySuggestions: ", + { + "pluginId": "unifiedSearch", + "scope": "public", + "docId": "kibUnifiedSearchAutocompletePluginApi", + "section": "def-public.QuerySuggestionGetFn", + "text": "QuerySuggestionGetFn" + }, + "; getAutocompleteSettings: () => { terminateAfter: number; timeout: number; }; }" + ], + "path": "src/plugins/unified_search/public/types.ts", + "deprecated": false + } + ], + "lifecycle": "setup", + "initialIsOpen": true } }, "server": { - "classes": [], + "classes": [ + { + "parentPluginId": "unifiedSearch", + "id": "def-server.UnifiedSearchServerPlugin", + "type": "Class", + "tags": [], + "label": "UnifiedSearchServerPlugin", + "description": [], + "signature": [ + { + "pluginId": "unifiedSearch", + "scope": "server", + "docId": "kibUnifiedSearchPluginApi", + "section": "def-server.UnifiedSearchServerPlugin", + "text": "UnifiedSearchServerPlugin" + }, + " implements ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.Plugin", + "text": "Plugin" + }, + "<", + { + "pluginId": "unifiedSearch", + "scope": "server", + "docId": "kibUnifiedSearchPluginApi", + "section": "def-server.UnifiedSearchServerPluginSetup", + "text": "UnifiedSearchServerPluginSetup" + }, + ", void, object, object>" + ], + "path": "src/plugins/unified_search/server/plugin.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "unifiedSearch", + "id": "def-server.UnifiedSearchServerPlugin.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "src/plugins/unified_search/server/plugin.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "unifiedSearch", + "id": "def-server.UnifiedSearchServerPlugin.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "initializerContext", + "description": [], + "signature": [ + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.PluginInitializerContext", + "text": "PluginInitializerContext" + }, + "; valueSuggestions: Readonly<{} & { timeout: moment.Duration; enabled: boolean; tiers: string[]; terminateAfter: moment.Duration; }>; }>; }>>" + ], + "path": "src/plugins/unified_search/server/plugin.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "unifiedSearch", + "id": "def-server.UnifiedSearchServerPlugin.setup", + "type": "Function", + "tags": [], + "label": "setup", + "description": [], + "signature": [ + "(core: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.CoreSetup", + "text": "CoreSetup" + }, + "<", + "UnifiedSearchServerPluginStartDependencies", + ", ", + { + "pluginId": "unifiedSearch", + "scope": "server", + "docId": "kibUnifiedSearchPluginApi", + "section": "def-server.UnifiedSearchServerPluginStart", + "text": "UnifiedSearchServerPluginStart" + }, + ">, {}: ", + "UnifiedSearchServerPluginSetupDependencies", + ") => { autocomplete: { getAutocompleteSettings: () => { terminateAfter: number; timeout: number; }; }; }" + ], + "path": "src/plugins/unified_search/server/plugin.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "unifiedSearch", + "id": "def-server.UnifiedSearchServerPlugin.setup.$1", + "type": "Object", + "tags": [], + "label": "core", + "description": [], + "signature": [ + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.CoreSetup", + "text": "CoreSetup" + }, + "<", + "UnifiedSearchServerPluginStartDependencies", + ", ", + { + "pluginId": "unifiedSearch", + "scope": "server", + "docId": "kibUnifiedSearchPluginApi", + "section": "def-server.UnifiedSearchServerPluginStart", + "text": "UnifiedSearchServerPluginStart" + }, + ">" + ], + "path": "src/plugins/unified_search/server/plugin.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "unifiedSearch", + "id": "def-server.UnifiedSearchServerPlugin.setup.$2", + "type": "Object", + "tags": [], + "label": "{}", + "description": [], + "signature": [ + "UnifiedSearchServerPluginSetupDependencies" + ], + "path": "src/plugins/unified_search/server/plugin.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "unifiedSearch", + "id": "def-server.UnifiedSearchServerPlugin.start", + "type": "Function", + "tags": [], + "label": "start", + "description": [], + "signature": [ + "(core: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.CoreStart", + "text": "CoreStart" + }, + ", {}: ", + "UnifiedSearchServerPluginStartDependencies", + ") => {}" + ], + "path": "src/plugins/unified_search/server/plugin.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "unifiedSearch", + "id": "def-server.UnifiedSearchServerPlugin.start.$1", + "type": "Object", + "tags": [], + "label": "core", + "description": [], + "signature": [ + { + "pluginId": "core", + "scope": "server", + "docId": "kibCorePluginApi", + "section": "def-server.CoreStart", + "text": "CoreStart" + } + ], + "path": "src/plugins/unified_search/server/plugin.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "unifiedSearch", + "id": "def-server.UnifiedSearchServerPlugin.start.$2", + "type": "Object", + "tags": [], + "label": "{}", + "description": [], + "signature": [ + "UnifiedSearchServerPluginStartDependencies" + ], + "path": "src/plugins/unified_search/server/plugin.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "unifiedSearch", + "id": "def-server.UnifiedSearchServerPlugin.stop", + "type": "Function", + "tags": [], + "label": "stop", + "description": [], + "signature": [ + "() => void" + ], + "path": "src/plugins/unified_search/server/plugin.ts", + "deprecated": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], "functions": [], - "interfaces": [], + "interfaces": [ + { + "parentPluginId": "unifiedSearch", + "id": "def-server.UnifiedSearchServerPluginStart", + "type": "Interface", + "tags": [], + "label": "UnifiedSearchServerPluginStart", + "description": [], + "path": "src/plugins/unified_search/server/plugin.ts", + "deprecated": false, + "children": [], + "initialIsOpen": false + } + ], "enums": [], "misc": [], - "objects": [] + "objects": [], + "setup": { + "parentPluginId": "unifiedSearch", + "id": "def-server.UnifiedSearchServerPluginSetup", + "type": "Interface", + "tags": [], + "label": "UnifiedSearchServerPluginSetup", + "description": [], + "path": "src/plugins/unified_search/server/plugin.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "unifiedSearch", + "id": "def-server.UnifiedSearchServerPluginSetup.autocomplete", + "type": "Object", + "tags": [], + "label": "autocomplete", + "description": [], + "signature": [ + "{ getAutocompleteSettings: () => { terminateAfter: number; timeout: number; }; }" + ], + "path": "src/plugins/unified_search/server/plugin.ts", + "deprecated": false + } + ], + "lifecycle": "setup", + "initialIsOpen": true + } }, "common": { "classes": [], diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 5090a1f15bee8..285d17efcba81 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github summary: API docs for the unifiedSearch plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] warning: 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. --- @@ -18,10 +18,13 @@ Contact [Unified Search](https://github.com/orgs/elastic/teams/kibana-app-servic | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 48 | 1 | 45 | 6 | +| 79 | 2 | 75 | 11 | ## Client +### Setup + + ### Start @@ -34,3 +37,14 @@ Contact [Unified Search](https://github.com/orgs/elastic/teams/kibana-app-servic ### Consts, variables and types +## Server + +### Setup + + +### Classes + + +### Interfaces + + diff --git a/api_docs/unified_search_autocomplete.devdocs.json b/api_docs/unified_search_autocomplete.devdocs.json new file mode 100644 index 0000000000000..c3f2543a9c9cb --- /dev/null +++ b/api_docs/unified_search_autocomplete.devdocs.json @@ -0,0 +1,265 @@ +{ + "id": "unifiedSearch.autocomplete", + "client": { + "classes": [], + "functions": [], + "interfaces": [ + { + "parentPluginId": "unifiedSearch", + "id": "def-public.QuerySuggestionGetFnArgs", + "type": "Interface", + "tags": [], + "label": "QuerySuggestionGetFnArgs", + "description": [], + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "unifiedSearch", + "id": "def-public.QuerySuggestionGetFnArgs.language", + "type": "string", + "tags": [], + "label": "language", + "description": [], + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts", + "deprecated": false + }, + { + "parentPluginId": "unifiedSearch", + "id": "def-public.QuerySuggestionGetFnArgs.indexPatterns", + "type": "Array", + "tags": [], + "label": "indexPatterns", + "description": [], + "signature": [ + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.IIndexPattern", + "text": "IIndexPattern" + }, + "[]" + ], + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts", + "deprecated": false + }, + { + "parentPluginId": "unifiedSearch", + "id": "def-public.QuerySuggestionGetFnArgs.query", + "type": "string", + "tags": [], + "label": "query", + "description": [], + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts", + "deprecated": false + }, + { + "parentPluginId": "unifiedSearch", + "id": "def-public.QuerySuggestionGetFnArgs.selectionStart", + "type": "number", + "tags": [], + "label": "selectionStart", + "description": [], + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts", + "deprecated": false + }, + { + "parentPluginId": "unifiedSearch", + "id": "def-public.QuerySuggestionGetFnArgs.selectionEnd", + "type": "number", + "tags": [], + "label": "selectionEnd", + "description": [], + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts", + "deprecated": false + }, + { + "parentPluginId": "unifiedSearch", + "id": "def-public.QuerySuggestionGetFnArgs.signal", + "type": "Object", + "tags": [], + "label": "signal", + "description": [], + "signature": [ + "AbortSignal | undefined" + ], + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts", + "deprecated": false + }, + { + "parentPluginId": "unifiedSearch", + "id": "def-public.QuerySuggestionGetFnArgs.useTimeRange", + "type": "CompoundType", + "tags": [], + "label": "useTimeRange", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts", + "deprecated": false + }, + { + "parentPluginId": "unifiedSearch", + "id": "def-public.QuerySuggestionGetFnArgs.boolFilter", + "type": "Any", + "tags": [], + "label": "boolFilter", + "description": [], + "signature": [ + "any" + ], + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts", + "deprecated": false + }, + { + "parentPluginId": "unifiedSearch", + "id": "def-public.QuerySuggestionGetFnArgs.method", + "type": "CompoundType", + "tags": [], + "label": "method", + "description": [], + "signature": [ + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataPluginApi", + "section": "def-common.ValueSuggestionsMethod", + "text": "ValueSuggestionsMethod" + }, + " | undefined" + ], + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ], + "enums": [ + { + "parentPluginId": "unifiedSearch", + "id": "def-public.QuerySuggestionTypes", + "type": "Enum", + "tags": [], + "label": "QuerySuggestionTypes", + "description": [], + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts", + "deprecated": false, + "initialIsOpen": false + } + ], + "misc": [ + { + "parentPluginId": "unifiedSearch", + "id": "def-public.AutocompleteStart", + "type": "Type", + "tags": [], + "label": "AutocompleteStart", + "description": [], + "signature": [ + "{ getQuerySuggestions: ", + { + "pluginId": "unifiedSearch", + "scope": "public", + "docId": "kibUnifiedSearchAutocompletePluginApi", + "section": "def-public.QuerySuggestionGetFn", + "text": "QuerySuggestionGetFn" + }, + "; hasQuerySuggestions: (language: string) => boolean; getValueSuggestions: ", + "ValueSuggestionsGetFn", + "; }" + ], + "path": "src/plugins/unified_search/public/autocomplete/autocomplete_service.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "unifiedSearch", + "id": "def-public.QuerySuggestion", + "type": "Type", + "tags": [], + "label": "QuerySuggestion", + "description": [], + "signature": [ + "QuerySuggestionBasic", + " | ", + "QuerySuggestionField" + ], + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "unifiedSearch", + "id": "def-public.QuerySuggestionGetFn", + "type": "Type", + "tags": [], + "label": "QuerySuggestionGetFn", + "description": [], + "signature": [ + "(args: ", + { + "pluginId": "unifiedSearch", + "scope": "public", + "docId": "kibUnifiedSearchAutocompletePluginApi", + "section": "def-public.QuerySuggestionGetFnArgs", + "text": "QuerySuggestionGetFnArgs" + }, + ") => Promise<", + { + "pluginId": "unifiedSearch", + "scope": "public", + "docId": "kibUnifiedSearchAutocompletePluginApi", + "section": "def-public.QuerySuggestion", + "text": "QuerySuggestion" + }, + "[]> | undefined" + ], + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "unifiedSearch", + "id": "def-public.QuerySuggestionGetFn.$1", + "type": "Object", + "tags": [], + "label": "args", + "description": [], + "signature": [ + { + "pluginId": "unifiedSearch", + "scope": "public", + "docId": "kibUnifiedSearchAutocompletePluginApi", + "section": "def-public.QuerySuggestionGetFnArgs", + "text": "QuerySuggestionGetFnArgs" + } + ], + "path": "src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx new file mode 100644 index 0000000000000..f6c59fdec600f --- /dev/null +++ b/api_docs/unified_search_autocomplete.mdx @@ -0,0 +1,33 @@ +--- +id: kibUnifiedSearchAutocompletePluginApi +slug: /kibana-dev-docs/api/unifiedSearch-autocomplete +title: "unifiedSearch.autocomplete" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the unifiedSearch.autocomplete plugin +date: 2022-04-26 +tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] +warning: 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. +--- +import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; + +Contains all the key functionality of Kibana's unified search experience.Contains all the key functionality of Kibana's unified search experience. + +Contact [Unified Search](https://github.com/orgs/elastic/teams/kibana-app-services) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 79 | 2 | 75 | 11 | + +## Client + +### Interfaces + + +### Enums + + +### Consts, variables and types + + diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 49e61cb0452fe..3ba684cb1b7fe 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github summary: API docs for the urlForwarding plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] warning: 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. --- diff --git a/api_docs/usage_collection.devdocs.json b/api_docs/usage_collection.devdocs.json index 46559368d5450..8978c71dc262e 100644 --- a/api_docs/usage_collection.devdocs.json +++ b/api_docs/usage_collection.devdocs.json @@ -2190,9 +2190,9 @@ "\r\nPossible type values in the schema" ], "signature": [ - "\"boolean\" | \"keyword\" | \"date\" | \"text\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"double\"" + "\"boolean\" | \"keyword\" | \"text\" | \"date\" | \"integer\" | \"long\" | \"short\" | \"byte\" | \"float\" | \"double\"" ], - "path": "node_modules/@types/elastic__analytics/index.d.ts", + "path": "node_modules/@types/kbn__analytics-client/index.d.ts", "deprecated": false, "initialIsOpen": false }, diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index 362ddd4e39eb8..121a877b20b16 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github summary: API docs for the usageCollection plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] warning: 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. --- diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index 6ccc23ba38612..ef94504e31dc9 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github summary: API docs for the ux plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] warning: 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. --- diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index c335a368d7b71..42f51f6f9501c 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github summary: API docs for the visDefaultEditor plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] warning: 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. --- diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index f1504dbc739ba..15770aeb62c3d 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeGauge plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] warning: 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. --- diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index e36cb3c375d3c..6460ab30cde48 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeHeatmap plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] warning: 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. --- diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index 6fd8b8f633d78..1e97d6977d0df 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypePie plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] warning: 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. --- diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index 9fb56a55bd2d9..0bdbae2ed0092 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeTable plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] warning: 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. --- diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index 91e5141ff9cfd..962bbd0c90c3f 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeTimelion plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] warning: 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. --- diff --git a/api_docs/vis_type_timeseries.devdocs.json b/api_docs/vis_type_timeseries.devdocs.json index 77a7ac1e67ce5..afd54c07290ff 100644 --- a/api_docs/vis_type_timeseries.devdocs.json +++ b/api_docs/vis_type_timeseries.devdocs.json @@ -145,7 +145,7 @@ { "parentPluginId": "visTypeTimeseries", "id": "def-server.VisTypeTimeseriesSetup.getVisData.$1", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "requestContext", "description": [], diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index ba5ac71899536..b26a90a93f8f0 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeTimeseries plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] warning: 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. --- diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index c978eed560309..e65deb3cea8a9 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeVega plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] warning: 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. --- diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index 9638ab6b1db54..aba082b0dfb43 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeVislib plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] warning: 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. --- diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index 6728cabe0e78c..8b80289230ed5 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeXy plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] warning: 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. --- diff --git a/api_docs/visualizations.devdocs.json b/api_docs/visualizations.devdocs.json index 9cd950856997f..500393546b819 100644 --- a/api_docs/visualizations.devdocs.json +++ b/api_docs/visualizations.devdocs.json @@ -3162,9 +3162,9 @@ "Observable", "; getTimeUpdate$: () => ", "Observable", - "; getRefreshIntervalUpdate$: () => ", + "; getRefreshIntervalUpdate$: () => ", "Observable", - "; getAutoRefreshFetch$: () => ", + "; getAutoRefreshFetch$: () => ", "Observable", "<", { @@ -3176,7 +3176,7 @@ }, ">; getFetch$: () => ", "Observable", - "; getTime: () => ", + "; getTime: () => ", { "pluginId": "data", "scope": "common", @@ -4496,13 +4496,7 @@ "label": "palette", "description": [], "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.PaletteOutput", - "text": "PaletteOutput" - }, + "PaletteOutput", "<{ [key: string]: unknown; }> | undefined" ], "path": "src/plugins/visualizations/public/vis_types/types.ts", @@ -4850,6 +4844,34 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "visualizations", + "id": "def-public.SAVED_OBJECTS_LIMIT_SETTING", + "type": "string", + "tags": [], + "label": "SAVED_OBJECTS_LIMIT_SETTING", + "description": [], + "signature": [ + "\"savedObjects:listingLimit\"" + ], + "path": "src/plugins/visualizations/common/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "visualizations", + "id": "def-public.SAVED_OBJECTS_PER_PAGE_SETTING", + "type": "string", + "tags": [], + "label": "SAVED_OBJECTS_PER_PAGE_SETTING", + "description": [], + "signature": [ + "\"savedObjects:perPage\"" + ], + "path": "src/plugins/visualizations/common/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "visualizations", "id": "def-public.SavedVisState", @@ -5018,7 +5040,7 @@ "label": "VisualizeEmbeddableContract", "description": [], "signature": [ - "{ readonly type: \"visualization\"; readonly id: string; getExplicitInput: () => ", + "{ readonly id: string; readonly type: \"visualization\"; getExplicitInput: () => ", { "pluginId": "visualizations", "scope": "public", @@ -5234,7 +5256,7 @@ "label": "VisualizeEmbeddableFactoryContract", "description": [], "signature": [ - "{ readonly type: \"visualization\"; create: (input: ", + "{ create: (input: ", { "pluginId": "visualizations", "scope": "public", @@ -5294,7 +5316,7 @@ "VisualizeEmbeddable", " | ", "DisabledLabEmbeddable", - " | undefined>; isEditable: () => Promise; getDisplayName: () => string; createFromSavedObject: (savedObjectId: string, input: Partial<", + " | undefined>; readonly type: \"visualization\"; isEditable: () => Promise; getDisplayName: () => string; createFromSavedObject: (savedObjectId: string, input: Partial<", { "pluginId": "visualizations", "scope": "public", diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index abd99851677e0..992f0475d6f4f 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github summary: API docs for the visualizations plugin -date: 2022-04-05 +date: 2022-04-26 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] warning: 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. --- @@ -18,7 +18,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 363 | 12 | 342 | 14 | +| 365 | 12 | 344 | 14 | ## Client diff --git a/dev_docs/getting_started/hello_world_plugin.mdx b/dev_docs/getting_started/hello_world_plugin.mdx index be4fcf5671a42..8fa4ea4316129 100644 --- a/dev_docs/getting_started/hello_world_plugin.mdx +++ b/dev_docs/getting_started/hello_world_plugin.mdx @@ -40,6 +40,10 @@ and add the following: "id": "helloWorld", "version": "1.0.0", "kibanaVersion": "kibana", + "owner": { + "name": "Kibana Core", + "githubTeam": "kibana-core" + }, "ui": true } ``` @@ -77,6 +81,7 @@ And add the following to it: ``` $ mkdir public +$ cd public $ touch plugin.tsx ``` diff --git a/dev_docs/getting_started/setting_up_a_development_env.mdx b/dev_docs/getting_started/setting_up_a_development_env.mdx index ae994d6a018de..570dcb57a56b8 100644 --- a/dev_docs/getting_started/setting_up_a_development_env.mdx +++ b/dev_docs/getting_started/setting_up_a_development_env.mdx @@ -72,7 +72,7 @@ In another terminal tab/window you can start Kibana. yarn start ``` -If you include the `--run-examples` flag then all of the [developer examples](https://github.com/elastic/kibana/tree/{branch}/examples). Read more about the advanced options for [Running Kibana](https://www.elastic.co/guide/en/kibana/current/running-kibana-advanced.html). +Include developer examples](https://github.com/elastic/kibana/tree/main/examples) by adding an optional `--run-examples` flag. Read more about the advanced options for [Running Kibana](https://www.elastic.co/guide/en/kibana/current/running-kibana-advanced.html). ## Code away! diff --git a/dev_docs/tutorials/data/search.mdx b/dev_docs/tutorials/data/search.mdx index 0787c44b632ec..ab5c3f29ea1be 100644 --- a/dev_docs/tutorials/data/search.mdx +++ b/dev_docs/tutorials/data/search.mdx @@ -259,7 +259,7 @@ export const myEnhancedSearchStrategyProvider = ( await ese.cancel(id, options, deps); }, extend: async (id, keepAlive, options, deps) => { - // async search results are not stored indefinitely. By default, they expire after 7 days (or as defined by xpack.data_enhanced.search.sessions.defaultExpiration setting in kibana.yml). + // async search results are not stored indefinitely. By default, they expire after 7 days (or as defined by data.search.sessions.defaultExpiration setting in kibana.yml). // call the extend method of the async strategy you are using or implement your own extend function. await ese.extend(id, options, deps); }, diff --git a/docs/api/actions-and-connectors/legacy/index.asciidoc b/docs/api/actions-and-connectors/legacy/index.asciidoc index 859dd652de984..66ecb2ed31119 100644 --- a/docs/api/actions-and-connectors/legacy/index.asciidoc +++ b/docs/api/actions-and-connectors/legacy/index.asciidoc @@ -1,4 +1,4 @@ [[actions-and-connectors-legacy-apis]] === Deprecated 7.x APIs -These APIs are deprecated and will be removed as of 8.0. +These APIs are deprecated and will be removed in a future release. diff --git a/docs/api/alerting/legacy/index.asciidoc b/docs/api/alerting/legacy/index.asciidoc index cce2c378bdb58..48f37c06ff543 100644 --- a/docs/api/alerting/legacy/index.asciidoc +++ b/docs/api/alerting/legacy/index.asciidoc @@ -1,7 +1,7 @@ [[alerts-api]] === Deprecated 7.x APIs -These APIs are deprecated and will be removed as of 8.0. +These APIs are deprecated and will be removed in a future release. include::create.asciidoc[leveloffset=+1] include::delete.asciidoc[leveloffset=+1] diff --git a/docs/api/machine-learning/sync.asciidoc b/docs/api/machine-learning/sync.asciidoc index ba7e98a12dde6..d210a7826d166 100644 --- a/docs/api/machine-learning/sync.asciidoc +++ b/docs/api/machine-learning/sync.asciidoc @@ -47,9 +47,9 @@ it is added. This list contains the {dfeed} identifiers and indicates whether the synchronization was successful. `datafeedsRemoved`:: -(array) If saved objects exist for {dfeeds} that no longer exist, they are -deleted. This list contains the {dfeed} identifiers and indicates whether the -synchronization was successful. +(array) If a saved object for an anomaly detection job references a datafeed +that no longer exists, it is deleted. This list contains the {dfeed} identifiers +and indicates whether the synchronization was successful. `savedObjectsCreated`:: (array) If saved objects are missing for {ml} jobs or trained models, they are diff --git a/docs/developer/contributing/development-functional-tests.asciidoc b/docs/developer/contributing/development-functional-tests.asciidoc index af88754b316fa..b544add73b3b1 100644 --- a/docs/developer/contributing/development-functional-tests.asciidoc +++ b/docs/developer/contributing/development-functional-tests.asciidoc @@ -6,7 +6,7 @@ We use functional tests to make sure the {kib} UI works as expected. It replaces [discrete] === Running functional tests -The `FunctionalTestRunner` is very bare bones and gets most of its functionality from its config file, located at {blob}test/functional/config.js[test/functional/config.js] or {blob}x-pack/test/functional/config.js[x-pack/test/functional/config.js]. If you’re writing a plugin outside the {kib} repo, you will have your own config file. +The `FunctionalTestRunner` (FTR) is very bare bones and gets most of its functionality from its config file. The {kib} repo contains many FTR config files which use slightly different configurations for the {kib} server or {es}, have different test files, and potentially other config differences. You can find a manifest of all the FTR config files in `.buildkite/ftr_configs.yml`. If you’re writing a plugin outside the {kib} repo, you will have your own config file. See <> for more info. There are three ways to run the tests depending on your goals: @@ -96,7 +96,8 @@ node scripts/functional_test_runner --exclude-tag skipCloud When run without any arguments the `FunctionalTestRunner` automatically loads the configuration in the standard location, but you can override that behavior with the `--config` flag. List configs with multiple --config arguments. -* `--config test/functional/config.js` starts {es} and {kib} servers with the WebDriver tests configured to run in Chrome. +* `--config test/functional/apps/app-name/config.js` starts {es} and {kib} servers with the WebDriver tests configured to run in Chrome for a specific app. For example, +`--config test/functional/apps/home/config.js` starts {es} and {kib} servers with the WebDriver tests configured to run in Chrome for the home app. * `--config test/functional/config.firefox.js` starts {es} and {kib} servers with the WebDriver tests configured to run in Firefox. * `--config test/api_integration/config.js` starts {es} and {kib} servers with the api integration tests configuration. * `--config test/accessibility/config.ts` starts {es} and {kib} servers with the WebDriver tests configured to run an accessibility audit using https://www.deque.com/axe/[axe]. @@ -416,7 +417,7 @@ export function SomethingUsefulProvider({ getService }) { ----------- + * Re-export your provider from `services/index.js` -* Import it into `src/functional/config.js` and add it to the services config: +* Import it into `src/functional/config.base.js` and add it to the services config: + ["source","js"] ----------- diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 90bf3d3c29b41..a91776fde65ba 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -222,7 +222,7 @@ It also provides a stateful version of it on the start contract. |{kib-repo}blob/{branch}/src/plugins/newsfeed/README.md[newsfeed] |The newsfeed plugin adds a NewsfeedNavButton to the top navigation bar and renders the content in the flyout. -Content is fetched from the remote (https://feeds.elastic.co and https://feeds-staging.elastic.co in dev mode) once a day, with periodic checks if the content needs to be refreshed. All newsfeed content is hosted remotely. +Content is fetched from the remote (https://feeds.elastic.co) once a day, with periodic checks if the content needs to be refreshed. All newsfeed content is hosted remotely. |{kib-repo}blob/{branch}/src/plugins/presentation_util/README.mdx[presentationUtil] @@ -412,10 +412,6 @@ The plugin exposes the static DefaultEditorController class to consume. |Adds drilldown capabilities to dashboard. Owned by the Kibana App team. -|{kib-repo}blob/{branch}/x-pack/plugins/data_enhanced/README.md[dataEnhanced] -|The data_enhanced plugin is the x-pack counterpart to the src/plguins/data plugin. - - |{kib-repo}blob/{branch}/x-pack/plugins/data_visualizer/README.md[dataVisualizer] |The data_visualizer plugin enables you to explore the fields in your data. diff --git a/docs/developer/plugin/external-plugin-functional-tests.asciidoc b/docs/developer/plugin/external-plugin-functional-tests.asciidoc index 55b311794f9dc..4a98ffcc5d08c 100644 --- a/docs/developer/plugin/external-plugin-functional-tests.asciidoc +++ b/docs/developer/plugin/external-plugin-functional-tests.asciidoc @@ -24,7 +24,7 @@ export default async function ({ readConfigFile }) { // read the {kib} config file so that we can utilize some of // its services and PageObjects - const kibanaConfig = await readConfigFile(resolve(REPO_ROOT, 'test/functional/config.js')); + const kibanaConfig = await readConfigFile(resolve(REPO_ROOT, 'test/functional/config.base.js')); return { // list paths to the files that contain your plugins tests diff --git a/docs/development/core/public/kibana-plugin-core-public.analyticsservicesetup.md b/docs/development/core/public/kibana-plugin-core-public.analyticsservicesetup.md index b1d059fe556b4..8cee67f0110dd 100644 --- a/docs/development/core/public/kibana-plugin-core-public.analyticsservicesetup.md +++ b/docs/development/core/public/kibana-plugin-core-public.analyticsservicesetup.md @@ -9,5 +9,5 @@ Exposes the public APIs of the AnalyticsClient during the setup phase. Signature: ```typescript -export declare type AnalyticsServiceSetup = AnalyticsClient; +export declare type AnalyticsServiceSetup = Omit; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.analyticsservicepreboot.md b/docs/development/core/server/kibana-plugin-core-server.analyticsservicepreboot.md index d648455dde18d..e1129181dbb49 100644 --- a/docs/development/core/server/kibana-plugin-core-server.analyticsservicepreboot.md +++ b/docs/development/core/server/kibana-plugin-core-server.analyticsservicepreboot.md @@ -9,5 +9,5 @@ Exposes the public APIs of the AnalyticsClient during the preboot phase Signature: ```typescript -export declare type AnalyticsServicePreboot = AnalyticsClient; +export declare type AnalyticsServicePreboot = Omit; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.analyticsservicesetup.md b/docs/development/core/server/kibana-plugin-core-server.analyticsservicesetup.md index aa84919848f24..5dc90eb0c1f03 100644 --- a/docs/development/core/server/kibana-plugin-core-server.analyticsservicesetup.md +++ b/docs/development/core/server/kibana-plugin-core-server.analyticsservicesetup.md @@ -9,5 +9,5 @@ Exposes the public APIs of the AnalyticsClient during the setup phase. Signature: ```typescript -export declare type AnalyticsServiceSetup = AnalyticsClient; +export declare type AnalyticsServiceSetup = Omit; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporter.import.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporter.import.md index 1ca6058e7d742..f30ddeddba92d 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporter.import.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporter.import.md @@ -9,14 +9,14 @@ Import saved objects from given stream. See the [options](./kibana-plugin-core-s Signature: ```typescript -import({ readStream, createNewCopies, namespace, overwrite, }: SavedObjectsImportOptions): Promise; +import({ readStream, createNewCopies, namespace, overwrite, refresh, }: SavedObjectsImportOptions): Promise; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| { readStream, createNewCopies, namespace, overwrite, } | SavedObjectsImportOptions | | +| { readStream, createNewCopies, namespace, overwrite, refresh, } | SavedObjectsImportOptions | | Returns: diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporter.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporter.md index 18ce27ca2c0dc..b1035bc247ad1 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporter.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporter.md @@ -21,6 +21,6 @@ export declare class SavedObjectsImporter | Method | Modifiers | Description | | --- | --- | --- | -| [import({ readStream, createNewCopies, namespace, overwrite, })](./kibana-plugin-core-server.savedobjectsimporter.import.md) | | Import saved objects from given stream. See the [options](./kibana-plugin-core-server.savedobjectsimportoptions.md) for more detailed information. | +| [import({ readStream, createNewCopies, namespace, overwrite, refresh, })](./kibana-plugin-core-server.savedobjectsimporter.import.md) | | Import saved objects from given stream. See the [options](./kibana-plugin-core-server.savedobjectsimportoptions.md) for more detailed information. | | [resolveImportErrors({ readStream, createNewCopies, namespace, retries, })](./kibana-plugin-core-server.savedobjectsimporter.resolveimporterrors.md) | | Resolve and return saved object import errors. See the [options](./kibana-plugin-core-server.savedobjectsresolveimporterrorsoptions.md) for more detailed information. | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportoptions.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportoptions.md index 58d0f4bf982c3..775f3a4c9acb3 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportoptions.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportoptions.md @@ -20,4 +20,5 @@ export interface SavedObjectsImportOptions | [namespace?](./kibana-plugin-core-server.savedobjectsimportoptions.namespace.md) | string | (Optional) if specified, will import in given namespace, else will import as global object | | [overwrite](./kibana-plugin-core-server.savedobjectsimportoptions.overwrite.md) | boolean | If true, will override existing object if present. Note: this has no effect when used with the createNewCopies option. | | [readStream](./kibana-plugin-core-server.savedobjectsimportoptions.readstream.md) | Readable | The stream of [saved objects](./kibana-plugin-core-server.savedobject.md) to import | +| [refresh?](./kibana-plugin-core-server.savedobjectsimportoptions.refresh.md) | boolean \| 'wait\_for' | (Optional) Refresh setting, defaults to wait_for | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportoptions.refresh.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportoptions.refresh.md new file mode 100644 index 0000000000000..cc7e36354647a --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportoptions.refresh.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsImportOptions](./kibana-plugin-core-server.savedobjectsimportoptions.md) > [refresh](./kibana-plugin-core-server.savedobjectsimportoptions.refresh.md) + +## SavedObjectsImportOptions.refresh property + +Refresh setting, defaults to `wait_for` + +Signature: + +```typescript +refresh?: boolean | 'wait_for'; +``` diff --git a/docs/index.asciidoc b/docs/index.asciidoc index 668a6edcad3db..41321f991c1b0 100644 --- a/docs/index.asciidoc +++ b/docs/index.asciidoc @@ -11,7 +11,6 @@ include::{docs-root}/shared/versions/stack/{source_branch}.asciidoc[] :docker-image: {docker-repo}:{version} :es-docker-repo: docker.elastic.co/elasticsearch/elasticsearch :es-docker-image: {es-docker-repo}:{version} -:blob: {kib-repo}blob/{branch}/ :security-ref: https://www.elastic.co/community/security/ :Data-source: Data view :data-source: data view @@ -20,6 +19,8 @@ include::{docs-root}/shared/versions/stack/{source_branch}.asciidoc[] include::{docs-root}/shared/attributes.asciidoc[] +:blob: {kib-repo}blob/{branch}/ + include::user/index.asciidoc[] include::accessibility.asciidoc[] diff --git a/docs/management/connectors/action-types/email.asciidoc b/docs/management/connectors/action-types/email.asciidoc index af408fd36a23c..3571185d0d510 100644 --- a/docs/management/connectors/action-types/email.asciidoc +++ b/docs/management/connectors/action-types/email.asciidoc @@ -7,9 +7,11 @@ The email connector uses the SMTP protocol to send mail messages, using an integration of https://nodemailer.com/[Nodemailer]. An exception is Microsoft Exchange, which uses HTTP protocol for sending emails, https://docs.microsoft.com/en-us/graph/api/user-sendmail[Send mail]. Email message text is sent as both plain text and html text. -NOTE: For emails to have a footer with a link back to {kib}, set the <> configuration setting. - -NOTE: When the <> configuration setting is used, the email addresses used for all of the Sender (from), To, CC, and BCC properties must have email domains specified in the configuration setting. +[NOTE] +==== +* For emails to have a footer with a link back to {kib}, set the <> configuration setting. +* When the <> configuration setting is used, the email addresses used for all of the Sender (from), To, CC, and BCC properties must have email domains specified in the configuration setting. +==== [float] [[email-connector-configuration]] diff --git a/docs/maps/asset-tracking-tutorial.asciidoc b/docs/maps/asset-tracking-tutorial.asciidoc index ff62f5c019b74..c53adf90ec3a2 100644 --- a/docs/maps/asset-tracking-tutorial.asciidoc +++ b/docs/maps/asset-tracking-tutorial.asciidoc @@ -112,7 +112,7 @@ filter { "type" => "%{[resultSet][vehicle][type]}" "vehicle_id" => "%{[resultSet][vehicle][vehicleID]}" } - remove_field => [ "resultSet", "@version", "@timestamp" ] + remove_field => [ "resultSet", "@version", "@timestamp", "[event][original]" ] } mutate { diff --git a/docs/maps/import-geospatial-data.asciidoc b/docs/maps/import-geospatial-data.asciidoc index f58077fa38f92..8ee54e5dba638 100644 --- a/docs/maps/import-geospatial-data.asciidoc +++ b/docs/maps/import-geospatial-data.asciidoc @@ -96,12 +96,6 @@ To open an existing index for drawing: . Click *Add layer*. -. Set *Scaling* to *Limit results to 10,000*. - -. In **Filtering**: -** Clear the *Apply global search to layer data* checkbox. -** If your data view contains a default time field, clear the *Apply global time to layer data* checkbox. - . Click *Save & close*. . In the legend, click the layer name and select *Edit features*. diff --git a/docs/osquery/advanced-osquery.asciidoc b/docs/osquery/advanced-osquery.asciidoc deleted file mode 100644 index 4f03a6d5fe5eb..0000000000000 --- a/docs/osquery/advanced-osquery.asciidoc +++ /dev/null @@ -1,103 +0,0 @@ -[[advanced-osquery]] -== Advanced Osquery - -[float] -[[osquery-map-fields]] -=== Map result fields to ECS - -When you save queries or add queries to a pack, you can optionally map Osquery results or static values to fields in -the {ecs-ref}/ecs-reference.html[Elastic Common Schema] (ECS). -This standardizes your Osquery data for use across detections, machine learning, -and any other areas that rely on ECS-compliant data. -When the query is run, the results include the original `osquery.` -and the mapped ECS fields. For example, if you update a query to map `osquery.name` to `user.name`, the query results include both fields. - -. Edit saved queries or queries in a pack to map fields: - -* For *Saved queries*: Open the *Saved queries* tab, and then click the edit icon for the query that you want to map. - -* For *packs*: Open the *Packs* tab, edit a pack, and then click the edit icon for the query that you want to map. - -. In the **ECS mapping** section, select an **ECS field** to map. - -. In the **Value** column, use the dropdown on the left to choose what type of value to map to the ECS field: - -** **Osquery value**: Select an Osquery field. The fields available are based on the SQL query entered, and only include fields that the query returns. When the query runs, the ECS field is set dynamically to the value of the Osquery field selected. - -** **Static value**: Enter a static value. When the query runs, the ECS field is set to the value entered. For example, static fields can be used to apply `tags` or your preferred `event.category` to the query results. - -. Map more fields, as needed. To remove any mapped rows, click the delete icon. - -. Save your changes. - -[NOTE] -========================= - -* Some ECS fields are restricted and cannot be mapped. These are not available in the ECS dropdown. - -* Some ECS fields are restricted to a set of allowed values, like {ecs-ref}/ecs-event.html#field-event-category[event.category]. Use the {ecs-ref}/ecs-field-reference.html[ECS Field Reference] for help when mapping fields. - -* Osquery date fields have a variety of data types (including integer, text, or bigint). When mapping an Osquery date field to an ECS date field, you might need to use SQL operators in the query to get an {es}-compatible -{ref}/date.html[date] type. -========================= - - -[float] -[[osquery-extended-tables]] -=== Extended tables for Kubernetes queries -In addition to the Osquery schema, the Elastic-provided version of Osquery also includes the following tables to support Kubernetes containers. These can be queried with live or scheduled queries. - -* `host_users` - -* `host_groups` - -* `host_processes` - -When querying these tables, the expectation is that the `/etc/passwd`, `/etc/group`, and `/proc` are available in the container under `/hostfs` as: -`/hostfs/etc/passwd`, `/hostfs/etc/group`, and `/hostfs/proc`. For information about the fields available in these tables, see the -https://docs.elastic.co/en/integrations/osquery_manager#exported-fields[exported fields] reference. - -[float] -[[osquery-status]] -=== Osquery status - -A query can have the following status: - -[cols="2*<"] -|=== -| Successful | The query successfully completed. -| Failed | The query encountered a problem, such as an issue with the query or the agent was disconnected, and might have failed. -| Not yet responded | The query has not been sent to the agent. -| Expired | The action request timed out. The agent may be offline. -|=== - -NOTE: If an agent is offline, the request status remains **pending** as {kib} retries the request. -By default, a query request times out after five minutes. The time out applies to the time it takes -to deliver the action request to an agent to run a query. If the action completes after the timeout period, -the results are still returned. - - -[float] -[[osquery-results]] -=== Osquery results -When you run live or scheduled queries, the results are automatically -stored in an {es} index, so that you can search, analyze, and visualize this data in {kib}. -For a list of the Osquery fields that can be returned in query results, -refer to https://docs.elastic.co/en/integrations/osquery_manager#exported-fields[exported fields]. -Query results can also include ECS fields, if the query has a defined ECS mapping. - -Osquery responses include the following information: - -* Everything prefaced with `osquery.` is part of the query response. These fields are not mapped to ECS by default. - -* Results include some ECS fields by default, such as `host.*` and `agent.*`, which provide information about the host that was queried. - -* For live queries, the `action_data.query` is the query that was sent. - -* For scheduled queries in a pack, the `action_id` has the format `pack__`. You can use this information to look up the query that was run. - -* By default, all query results are https://osquery.readthedocs.io/en/stable/deployment/logging/#snapshot-logs[snapshot logs] -that represent a point in time with a set of results, with no -https://osquery.readthedocs.io/en/stable/deployment/logging/#differential-logs[differentials]. - -* Osquery data is stored in the `logs-osquery_manager.result-` datastream, and the result row data is under the `osquery` property in the document. diff --git a/docs/osquery/exported-fields-reference.asciidoc b/docs/osquery/exported-fields-reference.asciidoc index d359c484e4439..600751620c6d9 100644 --- a/docs/osquery/exported-fields-reference.asciidoc +++ b/docs/osquery/exported-fields-reference.asciidoc @@ -1,4 +1,6453 @@ [[exported-fields-osquery]] == Exported fields reference -_Content coming soon._ +The following fields can be returned in osquery results. Note the following about osquery fields: + +* Some fields list multiple descriptions because the one that applies depends on which table was queried. For example, a result stored in the `osquery.autoupdate` field may represent a response from the `firefox_addons` table or the `windows_security_center` table. +* In the cases where a field name is associated with more than one osquery table, we have made a best guess at what the data `type` should be. In the cases where it is unknown, the data type is set as a `keyword` object. + +For more information about osquery tables, see the https://osquery.io/schema[osquery schema documentation]. + +[float] +[[osquery-fields]] +=== Fields + +*UUID* - keyword, text.text + +* _system_extensions.UUID_ - Extension unique id + +*abi* - keyword, text.text + +* _elf_info.abi_ - Section type + +*action* - keyword, text.text + +* _disk_events.action_ - Appear or disappear +* _example.action_ - Action performed in generation +* _file_events.action_ - Change action (UPDATE, REMOVE, etc) +* _hardware_events.action_ - Remove, insert, change properties, etc +* _ntfs_journal_events.action_ - Change action (Write, Delete, etc) +* _scheduled_tasks.action_ - Actions executed by the scheduled task +* _socket_events.action_ - The socket action (bind, listen, close) +* _yara_events.action_ - Change action (UPDATE, REMOVE, etc) + +*activated* - keyword, number.long + +* _tpm_info.activated_ - TPM is activated + +*active* - keyword, number.long + +* _firefox_addons.active_ - 1 If the addon is active else 0 +* _memory_info.active_ - The total amount of buffer or page cache memory, in bytes, that is in active use +* _osquery_events.active_ - 1 if the publisher or subscriber is active else 0 +* _osquery_packs.active_ - Whether this pack is active (the version, platform and discovery queries match) yes=1, no=0. +* _osquery_registry.active_ - 1 If this plugin is active else 0 +* _virtual_memory_info.active_ - Total number of active pages. + +*active_disks* - keyword, number.long + +* _md_devices.active_disks_ - Number of active disks in array + +*active_state* - keyword, text.text + +* _systemd_units.active_state_ - The high-level unit activation state, i.e. generalization of SUB + +*actual* - keyword, number.long + +* _fan_speed_sensors.actual_ - Actual speed + +*additional_product_id* - keyword, text.text + +* _smart_drive_info.additional_product_id_ - An additional drive identifier if any + +*addr* - keyword, number.long + +* _elf_symbols.addr_ - Symbol address (value) + +*address* - keyword, text.text + +* _arp_cache.address_ - IPv4 address target +* _dns_resolvers.address_ - Resolver IP/IPv6 address +* _etc_hosts.address_ - IP address mapping +* _fbsd_kmods.address_ - Kernel module address +* _interface_addresses.address_ - Specific address for interface +* _kernel_modules.address_ - Kernel module address +* _listening_ports.address_ - Specific address for bind +* _platform_info.address_ - Relative address of firmware mapping +* _user_events.address_ - The Internet protocol address or family ID + +*address_width* - keyword, text.text + +* _cpu_info.address_width_ - The width of the CPU address bus. + +*algorithm* - keyword, text.text + +* _authorized_keys.algorithm_ - algorithm of key + +*alias* - keyword, text.text + +* _etc_protocols.alias_ - Protocol alias +* _time_machine_destinations.alias_ - Human readable name of drive + +*aliases* - keyword, text.text + +* _etc_services.aliases_ - Optional space separated list of other names for a service +* _lxd_images.aliases_ - Comma-separated list of image aliases + +*align* - keyword, number.long + +* _elf_sections.align_ - Segment alignment +* _elf_segments.align_ - Segment alignment + +*allow_maximum* - keyword, number.long + +* _shared_resources.allow_maximum_ - Number of concurrent users for this resource has been limited. If True, the value in the MaximumAllowed property is ignored. + +*allow_root* - keyword, text.text + +* _authorizations.allow_root_ - Label top-level key + +*allow_signed_enabled* - keyword, number.long + +* _alf.allow_signed_enabled_ - 1 If allow signed mode is enabled else 0 + +*ami_id* - keyword, text.text + +* _ec2_instance_metadata.ami_id_ - AMI ID used to launch this EC2 instance + +*amperage* - keyword, number.long + +* _battery.amperage_ - The battery's current amperage in mA + +*anonymous* - keyword, number.long + +* _virtual_memory_info.anonymous_ - Total number of anonymous pages. + +*antispyware* - keyword, text.text + +* _windows_security_center.antispyware_ - The health of the monitored Antispyware solution (see windows_security_products) + +*antivirus* - keyword, text.text + +* _windows_security_center.antivirus_ - The health of the monitored Antivirus solution (see windows_security_products) + +*api_version* - keyword, text.text + +* _docker_version.api_version_ - API version + +*apparmor* - keyword, text.text + +* _apparmor_events.apparmor_ - Apparmor Status like ALLOWED, DENIED etc. + +*applescript_enabled* - keyword, text.text + +* _apps.applescript_enabled_ - Info properties NSAppleScriptEnabled label + +*application* - keyword, text.text + +* _office_mru.application_ - Associated Office application + +*arch* - keyword, text.text + +* _deb_packages.arch_ - Package architecture +* _docker_version.arch_ - Hardware architecture +* _os_version.arch_ - OS Architecture +* _pkg_packages.arch_ - Architecture(s) supported +* _rpm_packages.arch_ - Architecture(s) supported +* _seccomp_events.arch_ - Information about the CPU architecture +* _signature.arch_ - If applicable, the arch of the signed code + +*architecture* - keyword, text.text + +* _docker_info.architecture_ - Hardware architecture +* _ec2_instance_metadata.architecture_ - Hardware architecture of this EC2 instance +* _lxd_images.architecture_ - Target architecture for the image +* _lxd_instances.architecture_ - Instance architecture + +*architectures* - keyword, text.text + +* _apt_sources.architectures_ - Repository architectures + +*args* - keyword, text.text + +* _startup_items.args_ - Arguments provided to startup executable + +*arguments* - keyword, text.text + +* _kernel_info.arguments_ - Kernel arguments + +*array_handle* - keyword, text.text + +* _memory_devices.array_handle_ - The memory array that the device is attached to + +*assessments_enabled* - keyword, number.long + +* _gatekeeper.assessments_enabled_ - 1 If a Gatekeeper is enabled else 0 + +*asset_tag* - keyword, text.text + +* _memory_devices.asset_tag_ - Manufacturer specific asset tag of memory device + +*ata_version* - keyword, text.text + +* _smart_drive_info.ata_version_ - ATA version of drive + +*atime* - keyword, number.long + +* _device_file.atime_ - Last access time +* _file.atime_ - Last access time +* _file_events.atime_ - Last access time +* _process_events.atime_ - File last access in UNIX time +* _shared_memory.atime_ - Attached time + +*attach* - keyword, text.text + +* _apparmor_profiles.attach_ - Which executable(s) a profile will attach to. + +*attached* - keyword, number.long + +* _shared_memory.attached_ - Number of attached processes + +*attributes* - keyword, text.text + +* _file.attributes_ - File attrib string. See: https://ss64.com/nt/attrib.html + +*audible_alarm* - keyword, text.text + +* _chassis_info.audible_alarm_ - If TRUE, the frame is equipped with an audible alarm. + +*auid* - keyword + +* _process_events.auid_ - Audit User ID at process start +* _process_file_events.auid_ - Audit user ID of the process using the file +* _seccomp_events.auid_ - Audit user ID (loginuid) of the user who started the analyzed process +* _socket_events.auid_ - Audit User ID +* _user_events.auid_ - Audit User ID + +*authenticate_user* - keyword, text.text + +* _authorizations.authenticate_user_ - Label top-level key + +*authentication_package* - keyword, text.text + +* _logon_sessions.authentication_package_ - The authentication package used to authenticate the owner of the logon session. + +*author* - keyword, text.text + +* _chocolatey_packages.author_ - Optional package author +* _chrome_extensions.author_ - Optional extension author +* _npm_packages.author_ - Package author name +* _python_packages.author_ - Optional package author +* _safari_extensions.author_ - Optional extension author + +*authority* - keyword, text.text + +* _signature.authority_ - Certificate Common Name + +*authority_key_id* - keyword, text.text + +* _certificates.authority_key_id_ - AKID an optionally included SHA1 + +*authority_key_identifier* - keyword, text.text + +* _curl_certificate.authority_key_identifier_ - Authority Key Identifier + +*authorizations* - keyword, text.text + +* _keychain_acls.authorizations_ - A space delimited set of authorization attributes + +*auto_login* - keyword, number.long + +* _wifi_networks.auto_login_ - 1 if auto login is enabled, 0 otherwise + +*auto_update* - keyword, number.long + +* _lxd_images.auto_update_ - Whether the image auto-updates (1) or not (0) + +*autoupdate* - keyword + +* _firefox_addons.autoupdate_ - 1 If the addon applies background updates else 0 +* _windows_security_center.autoupdate_ - The health of the Windows Autoupdate feature + +*availability* - keyword, text.text + +* _cpu_info.availability_ - The availability and status of the CPU. + +*availability_zone* - keyword, text.text + +* _ec2_instance_metadata.availability_zone_ - Availability zone in which this instance launched + +*average* - keyword, text.text + +* _load_average.average_ - Load average over the specified period. + +*average_memory* - keyword, number.long + +* _osquery_schedule.average_memory_ - Average private memory left after executing + +*avg_disk_bytes_per_read* - keyword, number.long + +* _physical_disk_performance.avg_disk_bytes_per_read_ - Average number of bytes transferred from the disk during read operations + +*avg_disk_bytes_per_write* - keyword, number.long + +* _physical_disk_performance.avg_disk_bytes_per_write_ - Average number of bytes transferred to the disk during write operations + +*avg_disk_read_queue_length* - keyword, number.long + +* _physical_disk_performance.avg_disk_read_queue_length_ - Average number of read requests that were queued for the selected disk during the sample interval + +*avg_disk_sec_per_read* - keyword, number.long + +* _physical_disk_performance.avg_disk_sec_per_read_ - Average time, in seconds, of a read operation of data from the disk + +*avg_disk_sec_per_write* - keyword, number.long + +* _physical_disk_performance.avg_disk_sec_per_write_ - Average time, in seconds, of a write operation of data to the disk + +*avg_disk_write_queue_length* - keyword, number.long + +* _physical_disk_performance.avg_disk_write_queue_length_ - Average number of write requests that were queued for the selected disk during the sample interval + +*backup_date* - keyword, number.long + +* _time_machine_backups.backup_date_ - Backup Date + +*bank_locator* - keyword, text.text + +* _memory_devices.bank_locator_ - String number of the string that identifies the physically-labeled bank where the memory device is located + +*base64* - keyword, number.long + +* _extended_attributes.base64_ - 1 if the value is base64 encoded else 0 + +*base_image* - keyword, text.text + +* _lxd_instances.base_image_ - ID of image used to launch this instance + +*base_uri* - keyword, text.text + +* _apt_sources.base_uri_ - Repository base URI + +*baseurl* - keyword, text.text + +* _yum_sources.baseurl_ - Repository base URL + +*basic_constraint* - keyword, text.text + +* _curl_certificate.basic_constraint_ - Basic Constraints + +*binary_queue* - keyword, number.long + +* _carbon_black_info.binary_queue_ - Size in bytes of binaries waiting to be sent to Carbon Black server + +*binding* - keyword, text.text + +* _elf_symbols.binding_ - Binding type + +*bitmap_chunk_size* - keyword, text.text + +* _md_devices.bitmap_chunk_size_ - Bitmap chunk size + +*bitmap_external_file* - keyword, text.text + +* _md_devices.bitmap_external_file_ - External referenced bitmap file + +*bitmap_on_mem* - keyword, text.text + +* _md_devices.bitmap_on_mem_ - Pages allocated in in-memory bitmap, if enabled + +*block* - keyword, text.text + +* _ssh_configs.block_ - The host or match block + +*block_size* - keyword, number.long + +* _block_devices.block_size_ - Block size in bytes +* _device_file.block_size_ - Block size of filesystem +* _file.block_size_ - Block size of filesystem + +*blocks* - keyword, number.long + +* _device_partitions.blocks_ - Number of blocks +* _mounts.blocks_ - Mounted device used blocks + +*blocks_available* - keyword, number.long + +* _mounts.blocks_available_ - Mounted device available blocks + +*blocks_free* - keyword, number.long + +* _mounts.blocks_free_ - Mounted device free blocks + +*blocks_size* - keyword, number.long + +* _device_partitions.blocks_size_ - Byte size of each block +* _mounts.blocks_size_ - Block size in bytes + +*bluetooth_sharing* - keyword, number.long + +* _sharing_preferences.bluetooth_sharing_ - 1 If bluetooth sharing is enabled for any user else 0 + +*board_model* - keyword, text.text + +* _system_info.board_model_ - Board model + +*board_serial* - keyword, text.text + +* _system_info.board_serial_ - Board serial number + +*board_vendor* - keyword, text.text + +* _system_info.board_vendor_ - Board vendor + +*board_version* - keyword, text.text + +* _system_info.board_version_ - Board version + +*boot_partition* - keyword, number.long + +* _logical_drives.boot_partition_ - True if Windows booted from this drive. + +*boot_uuid* - keyword, text.text + +* _ibridge_info.boot_uuid_ - Boot UUID of the iBridge controller + +*bp_microcode_disabled* - keyword, number.long + +* _kva_speculative_info.bp_microcode_disabled_ - Branch Predictions are disabled due to lack of microcode update. + +*bp_mitigations* - keyword, number.long + +* _kva_speculative_info.bp_mitigations_ - Branch Prediction mitigations are enabled. + +*bp_system_pol_disabled* - keyword, number.long + +* _kva_speculative_info.bp_system_pol_disabled_ - Branch Predictions are disabled via system policy. + +*breach_description* - keyword, text.text + +* _chassis_info.breach_description_ - If provided, gives a more detailed description of a detected security breach. + +*bridge_nf_ip6tables* - keyword, number.long + +* _docker_info.bridge_nf_ip6tables_ - 1 if bridge netfilter ip6tables is enabled. 0 otherwise + +*bridge_nf_iptables* - keyword, number.long + +* _docker_info.bridge_nf_iptables_ - 1 if bridge netfilter iptables is enabled. 0 otherwise + +*broadcast* - keyword, text.text + +* _interface_addresses.broadcast_ - Broadcast address for the interface + +*browser_type* - keyword, text.text + +* _chrome_extension_content_scripts.browser_type_ - The browser type (Valid values: chrome, chromium, opera, yandex, brave) +* _chrome_extensions.browser_type_ - The browser type (Valid values: chrome, chromium, opera, yandex, brave, edge, edge_beta) + +*bsd_flags* - keyword, text.text + +* _file.bsd_flags_ - The BSD file flags (chflags). Possible values: NODUMP, UF_IMMUTABLE, UF_APPEND, OPAQUE, HIDDEN, ARCHIVED, SF_IMMUTABLE, SF_APPEND + +*bssid* - keyword, text.text + +* _wifi_status.bssid_ - The current basic service set identifier +* _wifi_survey.bssid_ - The current basic service set identifier + +*btime* - keyword, number.long + +* _file.btime_ - (B)irth or (cr)eate time +* _process_events.btime_ - File creation in UNIX time + +*buffers* - keyword, number.long + +* _memory_info.buffers_ - The amount of physical RAM, in bytes, used for file buffers + +*build* - keyword, text.text + +* _os_version.build_ - Optional build-specific or variant string + +*build_distro* - keyword, text.text + +* _osquery_info.build_distro_ - osquery toolkit platform distribution name (os version) + +*build_id* - keyword, text.text + +* _sandboxes.build_id_ - Sandbox-specific identifier + +*build_number* - keyword, number.long + +* _windows_crashes.build_number_ - Windows build number of the crashing machine + +*build_platform* - keyword, text.text + +* _osquery_info.build_platform_ - osquery toolkit build platform + +*build_time* - keyword, text.text + +* _docker_version.build_time_ - Build time +* _portage_packages.build_time_ - Unix time when package was built + +*bundle_executable* - keyword, text.text + +* _apps.bundle_executable_ - Info properties CFBundleExecutable label + +*bundle_identifier* - keyword, text.text + +* _apps.bundle_identifier_ - Info properties CFBundleIdentifier label +* _running_apps.bundle_identifier_ - The bundle identifier of the application + +*bundle_name* - keyword, text.text + +* _apps.bundle_name_ - Info properties CFBundleName label + +*bundle_package_type* - keyword, text.text + +* _apps.bundle_package_type_ - Info properties CFBundlePackageType label + +*bundle_path* - keyword, text.text + +* _sandboxes.bundle_path_ - Application bundle used by the sandbox +* _system_extensions.bundle_path_ - System extension bundle path + +*bundle_short_version* - keyword, text.text + +* _apps.bundle_short_version_ - Info properties CFBundleShortVersionString label + +*bundle_version* - keyword, text.text + +* _apps.bundle_version_ - Info properties CFBundleVersion label + +*busy_state* - keyword, number.long + +* _iokit_devicetree.busy_state_ - 1 if the device is in a busy state else 0 +* _iokit_registry.busy_state_ - 1 if the node is in a busy state else 0 + +*bytes* - keyword, number.long + +* _curl.bytes_ - Number of bytes in the response +* _iptables.bytes_ - Number of matching bytes for this rule. + +*bytes_available* - keyword, number.long + +* _time_machine_destinations.bytes_available_ - Bytes available on volume + +*bytes_received* - keyword, number.long + +* _lxd_networks.bytes_received_ - Number of bytes received on this network + +*bytes_sent* - keyword, number.long + +* _lxd_networks.bytes_sent_ - Number of bytes sent on this network + +*bytes_used* - keyword, number.long + +* _time_machine_destinations.bytes_used_ - Bytes used on volume + +*ca* - keyword, number.long + +* _certificates.ca_ - 1 if CA: true (certificate is an authority) else 0 + +*cache_path* - keyword, text.text + +* _quicklook_cache.cache_path_ - Path to cache data + +*cached* - keyword, number.long + +* _lxd_images.cached_ - Whether image is cached (1) or not (0) +* _memory_info.cached_ - The amount of physical RAM, in bytes, used as cache memory + +*capability* - keyword, number.long + +* _apparmor_events.capability_ - Capability number + +*capname* - keyword, text.text + +* _apparmor_events.capname_ - Capability requested by the process + +*caption* - keyword, text.text + +* _patches.caption_ - Short description of the patch. +* _windows_optional_features.caption_ - Caption of feature in settings UI + +*captive_portal* - keyword, number.long + +* _wifi_networks.captive_portal_ - 1 if this network has a captive portal, 0 otherwise + +*carve* - keyword, number.long + +* _carves.carve_ - Set this value to '1' to start a file carve + +*carve_guid* - keyword, text.text + +* _carves.carve_guid_ - Identifying value of the carve session + +*category* - keyword, text.text + +* _apps.category_ - The UTI that categorizes the app for the App Store +* _file_events.category_ - The category of the file defined in the config +* _ntfs_journal_events.category_ - The category that the event originated from +* _power_sensors.category_ - The sensor category: currents, voltage, wattage +* _system_extensions.category_ - System extension category +* _yara_events.category_ - The category of the file + +*cdhash* - keyword, text.text + +* _es_process_events.cdhash_ - Codesigning hash of the process +* _signature.cdhash_ - Hash of the application Code Directory + +*celsius* - keyword, number.double + +* _temperature_sensors.celsius_ - Temperature in Celsius + +*certificate* - keyword, text.text + +* _lxd_certificates.certificate_ - Certificate content + +*cgroup_driver* - keyword, text.text + +* _docker_info.cgroup_driver_ - Control groups driver + +*cgroup_namespace* - keyword, text.text + +* _docker_containers.cgroup_namespace_ - cgroup namespace +* _process_namespaces.cgroup_namespace_ - cgroup namespace inode + +*chain* - keyword, text.text + +* _iptables.chain_ - Size of module content. + +*change_type* - keyword, text.text + +* _docker_container_fs_changes.change_type_ - Type of change: C:Modified, A:Added, D:Deleted + +*channel* - keyword + +* _wifi_status.channel_ - Channel number +* _wifi_survey.channel_ - Channel number +* _windows_eventlog.channel_ - Source or channel of the event + +*channel_band* - keyword, number.long + +* _wifi_status.channel_band_ - Channel band +* _wifi_survey.channel_band_ - Channel band + +*channel_width* - keyword, number.long + +* _wifi_status.channel_width_ - Channel width +* _wifi_survey.channel_width_ - Channel width + +*charged* - keyword, number.long + +* _battery.charged_ - 1 if the battery is currently completely charged. 0 otherwise + +*charging* - keyword, number.long + +* _battery.charging_ - 1 if the battery is currently being charged by a power source. 0 otherwise + +*chassis_bridge_capability_available* - keyword, number.long + +* _lldp_neighbors.chassis_bridge_capability_available_ - Chassis bridge capability availability + +*chassis_bridge_capability_enabled* - keyword, number.long + +* _lldp_neighbors.chassis_bridge_capability_enabled_ - Is chassis bridge capability enabled. + +*chassis_docsis_capability_available* - keyword, number.long + +* _lldp_neighbors.chassis_docsis_capability_available_ - Chassis DOCSIS capability availability + +*chassis_docsis_capability_enabled* - keyword, number.long + +* _lldp_neighbors.chassis_docsis_capability_enabled_ - Chassis DOCSIS capability enabled + +*chassis_id* - keyword, text.text + +* _lldp_neighbors.chassis_id_ - Neighbor chassis ID value + +*chassis_id_type* - keyword, text.text + +* _lldp_neighbors.chassis_id_type_ - Neighbor chassis ID type + +*chassis_mgmt_ips* - keyword, text.text + +* _lldp_neighbors.chassis_mgmt_ips_ - Comma delimited list of chassis management IPS + +*chassis_other_capability_available* - keyword, number.long + +* _lldp_neighbors.chassis_other_capability_available_ - Chassis other capability availability + +*chassis_other_capability_enabled* - keyword, number.long + +* _lldp_neighbors.chassis_other_capability_enabled_ - Chassis other capability enabled + +*chassis_repeater_capability_available* - keyword, number.long + +* _lldp_neighbors.chassis_repeater_capability_available_ - Chassis repeater capability availability + +*chassis_repeater_capability_enabled* - keyword, number.long + +* _lldp_neighbors.chassis_repeater_capability_enabled_ - Chassis repeater capability enabled + +*chassis_router_capability_available* - keyword, number.long + +* _lldp_neighbors.chassis_router_capability_available_ - Chassis router capability availability + +*chassis_router_capability_enabled* - keyword, number.long + +* _lldp_neighbors.chassis_router_capability_enabled_ - Chassis router capability enabled + +*chassis_station_capability_available* - keyword, number.long + +* _lldp_neighbors.chassis_station_capability_available_ - Chassis station capability availability + +*chassis_station_capability_enabled* - keyword, number.long + +* _lldp_neighbors.chassis_station_capability_enabled_ - Chassis station capability enabled + +*chassis_sys_description* - keyword, number.long + +* _lldp_neighbors.chassis_sys_description_ - Max number of CPU physical cores + +*chassis_sysname* - keyword, text.text + +* _lldp_neighbors.chassis_sysname_ - CPU brand string, contains vendor and model + +*chassis_tel_capability_available* - keyword, number.long + +* _lldp_neighbors.chassis_tel_capability_available_ - Chassis telephone capability availability + +*chassis_tel_capability_enabled* - keyword, number.long + +* _lldp_neighbors.chassis_tel_capability_enabled_ - Chassis telephone capability enabled + +*chassis_types* - keyword, text.text + +* _chassis_info.chassis_types_ - A comma-separated list of chassis types, such as Desktop or Laptop. + +*chassis_wlan_capability_available* - keyword, number.long + +* _lldp_neighbors.chassis_wlan_capability_available_ - Chassis wlan capability availability + +*chassis_wlan_capability_enabled* - keyword, number.long + +* _lldp_neighbors.chassis_wlan_capability_enabled_ - Chassis wlan capability enabled + +*check_array_finish* - keyword, text.text + +* _md_devices.check_array_finish_ - Estimated duration of the check array activity + +*check_array_progress* - keyword, text.text + +* _md_devices.check_array_progress_ - Progress of the check array activity + +*check_array_speed* - keyword, text.text + +* _md_devices.check_array_speed_ - Speed of the check array activity + +*checksum* - keyword, text.text + +* _disk_events.checksum_ - UDIF Master checksum if available (CRC32) + +*child_pid* - keyword, number.long + +* _es_process_events.child_pid_ - Process ID of a child process in case of a fork event + +*chunk_size* - keyword, number.long + +* _md_devices.chunk_size_ - chunk size in bytes + +*cid* - keyword, number.long + +* _bpf_process_events.cid_ - Cgroup ID +* _bpf_socket_events.cid_ - Cgroup ID + +*class* - keyword, text.text + +* _authorizations.class_ - Label top-level key +* _drivers.class_ - Device/driver class name +* _elf_dynamic.class_ - Class (32 or 64) +* _elf_info.class_ - Class type, 32 or 64bit +* _iokit_devicetree.class_ - Best matching device class (most-specific category) +* _iokit_registry.class_ - Best matching device class (most-specific category) +* _usb_devices.class_ - USB Device class +* _wmi_cli_event_consumers.class_ - The name of the class. +* _wmi_event_filters.class_ - The name of the class. +* _wmi_filter_consumer_binding.class_ - The name of the class. +* _wmi_script_event_consumers.class_ - The name of the class. + +*client_site_name* - keyword, text.text + +* _ntdomains.client_site_name_ - The name of the site where the domain controller is configured. + +*cmdline* - keyword, text.text + +* _bpf_process_events.cmdline_ - Command line arguments +* _docker_container_processes.cmdline_ - Complete argv +* _es_process_events.cmdline_ - Command line arguments (argv) +* _process_events.cmdline_ - Command line arguments (argv) +* _processes.cmdline_ - Complete argv + +*cmdline_count* - keyword, number.long + +* _es_process_events.cmdline_count_ - Number of command line arguments + +*cmdline_size* - keyword, number.long + +* _process_events.cmdline_size_ - Actual size (bytes) of command line arguments + +*code* - keyword, text.text + +* _seccomp_events.code_ - The seccomp action + +*code_integrity_policy_enforcement_status* - keyword, text.text + +* _hvci_status.code_integrity_policy_enforcement_status_ - The status of the code integrity policy enforcement settings. Returns UNKNOWN if an error is encountered. + +*codename* - keyword, text.text + +* _os_version.codename_ - OS version codename + +*collect_cross_processes* - keyword, number.long + +* _carbon_black_info.collect_cross_processes_ - If the sensor is configured to cross process events + +*collect_data_file_writes* - keyword, number.long + +* _carbon_black_info.collect_data_file_writes_ - If the sensor is configured to collect non binary file writes + +*collect_emet_events* - keyword, number.long + +* _carbon_black_info.collect_emet_events_ - If the sensor is configured to EMET events + +*collect_file_mods* - keyword, number.long + +* _carbon_black_info.collect_file_mods_ - If the sensor is configured to collect file modification events + +*collect_module_info* - keyword, number.long + +* _carbon_black_info.collect_module_info_ - If the sensor is configured to collect metadata of binaries + +*collect_module_loads* - keyword, number.long + +* _carbon_black_info.collect_module_loads_ - If the sensor is configured to capture module loads + +*collect_net_conns* - keyword, number.long + +* _carbon_black_info.collect_net_conns_ - If the sensor is configured to collect network connections + +*collect_process_user_context* - keyword, number.long + +* _carbon_black_info.collect_process_user_context_ - If the sensor is configured to collect the user running a process + +*collect_processes* - keyword, number.long + +* _carbon_black_info.collect_processes_ - If the sensor is configured to process events + +*collect_reg_mods* - keyword, number.long + +* _carbon_black_info.collect_reg_mods_ - If the sensor is configured to collect registry modification events + +*collect_sensor_operations* - keyword, number.long + +* _carbon_black_info.collect_sensor_operations_ - Unknown + +*collect_store_files* - keyword, number.long + +* _carbon_black_info.collect_store_files_ - If the sensor is configured to send back binaries to the Carbon Black server + +*collisions* - keyword, number.long + +* _interface_details.collisions_ - Packet Collisions detected + +*color_depth* - keyword, number.long + +* _video_info.color_depth_ - The amount of bits per pixel to represent color. + +*comm* - keyword, text.text + +* _apparmor_events.comm_ - Command-line name of the command that was used to invoke the analyzed process +* _seccomp_events.comm_ - Command-line name of the command that was used to invoke the analyzed process + +*command* - keyword, text.text + +* _crontab.command_ - Raw command string +* _docker_containers.command_ - Command with arguments +* _shell_history.command_ - Unparsed date/line/command history line + +*command_args* - keyword, text.text + +* _shortcut_files.command_args_ - Command args passed to lnk file. + +*command_line* - keyword, text.text + +* _windows_crashes.command_line_ - Command-line string passed to the crashed process + +*command_line_template* - keyword, text.text + +* _wmi_cli_event_consumers.command_line_template_ - Standard string template that specifies the process to be started. This property can be NULL, and the ExecutablePath property is used as the command line. + +*comment* - keyword, text.text + +* _authorizations.comment_ - Label top-level key +* _docker_image_history.comment_ - Instruction comment +* _etc_protocols.comment_ - Comment with protocol description +* _etc_services.comment_ - Optional comment for a service. +* _groups.comment_ - Remarks or comments associated with the group +* _keychain_items.comment_ - Optional keychain comment + +*common_name* - keyword, text.text + +* _certificates.common_name_ - Certificate CommonName +* _curl_certificate.common_name_ - Common name of company issued to + +*common_path* - keyword, text.text + +* _shortcut_files.common_path_ - Common system path to target file. + +*compat* - keyword, number.long + +* _seccomp_events.compat_ - Is system call in compatibility mode + +*compiler* - keyword, text.text + +* _apps.compiler_ - Info properties DTCompiler label + +*completed_time* - keyword, number.long + +* _cups_jobs.completed_time_ - When the job completed printing + +*components* - keyword, text.text + +* _apt_sources.components_ - Repository components + +*compressed* - keyword, number.long + +* _virtual_memory_info.compressed_ - The total number of pages that have been compressed by the VM compressor. + +*compressor* - keyword, number.long + +* _virtual_memory_info.compressor_ - The number of pages used to store compressed VM pages. + +*computer_name* - keyword, text.text + +* _system_info.computer_name_ - Friendly computer name (optional) +* _windows_eventlog.computer_name_ - Hostname of system where event was generated +* _windows_events.computer_name_ - Hostname of system where event was generated + +*condition* - keyword, text.text + +* _battery.condition_ - One of the following: "Normal" indicates the condition of the battery is within normal tolerances, "Service Needed" indicates that the battery should be checked out by a licensed Mac repair service, "Permanent Failure" indicates the battery needs replacement + +*config_entrypoint* - keyword, text.text + +* _docker_containers.config_entrypoint_ - Container entrypoint(s) + +*config_flag* - keyword, text.text + +* _sip_config.config_flag_ - The System Integrity Protection config flag + +*config_hash* - keyword, text.text + +* _osquery_info.config_hash_ - Hash of the working configuration state + +*config_name* - keyword, text.text + +* _carbon_black_info.config_name_ - Sensor group + +*config_valid* - keyword, number.long + +* _osquery_info.config_valid_ - 1 if the config was loaded and considered valid, else 0 + +*config_value* - keyword, text.text + +* _system_controls.config_value_ - The MIB value set in /etc/sysctl.conf + +*configured_clock_speed* - keyword, number.long + +* _memory_devices.configured_clock_speed_ - Configured speed of memory device in megatransfers per second (MT/s) + +*configured_voltage* - keyword, number.long + +* _memory_devices.configured_voltage_ - Configured operating voltage of device in millivolts + +*connection_id* - keyword, text.text + +* _interface_details.connection_id_ - Name of the network connection as it appears in the Network Connections Control Panel program. + +*connection_status* - keyword, text.text + +* _interface_details.connection_status_ - State of the network adapter connection to the network. + +*consistency_scan_date* - keyword, number.long + +* _time_machine_destinations.consistency_scan_date_ - Consistency scan date + +*consumer* - keyword, text.text + +* _wmi_filter_consumer_binding.consumer_ - Reference to an instance of __EventConsumer that represents the object path to a logical consumer, the recipient of an event. + +*containers* - keyword, number.long + +* _docker_info.containers_ - Total number of containers + +*containers_paused* - keyword, number.long + +* _docker_info.containers_paused_ - Number of containers in paused state + +*containers_running* - keyword, number.long + +* _docker_info.containers_running_ - Number of containers currently running + +*containers_stopped* - keyword, number.long + +* _docker_info.containers_stopped_ - Number of containers in stopped state + +*content* - keyword, text.text + +* _disk_events.content_ - Disk event content + +*content_caching* - keyword, number.long + +* _sharing_preferences.content_caching_ - 1 If content caching is enabled else 0 + +*content_type* - keyword, text.text + +* _package_install_history.content_type_ - Package content_type (optional) + +*conversion_status* - keyword, number.long + +* _bitlocker_info.conversion_status_ - The bitlocker conversion status of the drive. + +*coprocessor_version* - keyword, text.text + +* _ibridge_info.coprocessor_version_ - The manufacturer and chip version + +*copy* - keyword, number.long + +* _virtual_memory_info.copy_ - Total number of copy-on-write pages. + +*copyright* - keyword, text.text + +* _apps.copyright_ - Info properties NSHumanReadableCopyright label + +*core* - keyword, number.long + +* _cpu_time.core_ - Name of the cpu (core) + +*cosine_similarity* - keyword, number.double + +* _powershell_events.cosine_similarity_ - How similar the Powershell script is to a provided 'normal' character frequency + +*count* - keyword, number.long + +* _userassist.count_ - Number of times the application has been executed. +* _yara.count_ - Number of YARA matches +* _yara_events.count_ - Number of YARA matches + +*country_code* - keyword, text.text + +* _wifi_status.country_code_ - The country code (ISO/IEC 3166-1:1997) for the network +* _wifi_survey.country_code_ - The country code (ISO/IEC 3166-1:1997) for the network + +*cpu* - keyword, number.double + +* _docker_container_processes.cpu_ - CPU utilization as percentage + +*cpu_brand* - keyword, text.text + +* _system_info.cpu_brand_ - CPU brand string, contains vendor and model + +*cpu_cfs_period* - keyword, number.long + +* _docker_info.cpu_cfs_period_ - 1 if CPU Completely Fair Scheduler (CFS) period support is enabled. 0 otherwise + +*cpu_cfs_quota* - keyword, number.long + +* _docker_info.cpu_cfs_quota_ - 1 if CPU Completely Fair Scheduler (CFS) quota support is enabled. 0 otherwise + +*cpu_kernelmode_usage* - keyword, number.long + +* _docker_container_stats.cpu_kernelmode_usage_ - CPU kernel mode usage + +*cpu_logical_cores* - keyword, number.long + +* _system_info.cpu_logical_cores_ - Number of logical CPU cores available to the system + +*cpu_microcode* - keyword, text.text + +* _system_info.cpu_microcode_ - Microcode version + +*cpu_physical_cores* - keyword, number.long + +* _system_info.cpu_physical_cores_ - Number of physical CPU cores in to the system + +*cpu_pred_cmd_supported* - keyword, number.long + +* _kva_speculative_info.cpu_pred_cmd_supported_ - PRED_CMD MSR supported by CPU Microcode. + +*cpu_set* - keyword, number.long + +* _docker_info.cpu_set_ - 1 if CPU set selection support is enabled. 0 otherwise + +*cpu_shares* - keyword, number.long + +* _docker_info.cpu_shares_ - 1 if CPU share weighting support is enabled. 0 otherwise + +*cpu_spec_ctrl_supported* - keyword, number.long + +* _kva_speculative_info.cpu_spec_ctrl_supported_ - SPEC_CTRL MSR supported by CPU Microcode. + +*cpu_status* - keyword, number.long + +* _cpu_info.cpu_status_ - The current operating status of the CPU. + +*cpu_subtype* - keyword + +* _processes.cpu_subtype_ - Indicates the specific processor on which an entry may be used. +* _system_info.cpu_subtype_ - CPU subtype + +*cpu_total_usage* - keyword, number.long + +* _docker_container_stats.cpu_total_usage_ - Total CPU usage + +*cpu_type* - keyword + +* _processes.cpu_type_ - Indicates the specific processor designed for installation. +* _system_info.cpu_type_ - CPU type + +*cpu_usermode_usage* - keyword, number.long + +* _docker_container_stats.cpu_usermode_usage_ - CPU user mode usage + +*cpus* - keyword, number.long + +* _docker_info.cpus_ - Number of CPUs + +*crash_path* - keyword, text.text + +* _crashes.crash_path_ - Location of log file +* _windows_crashes.crash_path_ - Path of the log file + +*crashed_thread* - keyword, number.long + +* _crashes.crashed_thread_ - Thread ID which crashed + +*created* - keyword, text.text + +* _authorizations.created_ - Label top-level key +* _docker_containers.created_ - Time of creation as UNIX time +* _docker_image_history.created_ - Time of creation as UNIX time +* _docker_images.created_ - Time of creation as UNIX time +* _docker_networks.created_ - Time of creation as UNIX time +* _keychain_items.created_ - Data item was created + +*created_at* - keyword, text.text + +* _lxd_images.created_at_ - ISO time of image creation +* _lxd_instances.created_at_ - ISO time of creation + +*created_by* - keyword, text.text + +* _docker_image_history.created_by_ - Created by instruction + +*created_time* - keyword, number.long + +* _shellbags.created_time_ - Directory Created time. + +*creation_time* - keyword + +* _account_policy_data.creation_time_ - When the account was first created +* _cups_jobs.creation_time_ - When the print request was initiated + +*creator* - keyword, text.text + +* _firefox_addons.creator_ - Addon-supported creator string + +*creator_pid* - keyword, number.long + +* _shared_memory.creator_pid_ - Process ID that created the segment + +*creator_uid* - keyword, number.long + +* _shared_memory.creator_uid_ - User ID of creator process + +*csname* - keyword, text.text + +* _patches.csname_ - The name of the host the patch is installed on. + +*ctime* - keyword + +* _device_file.ctime_ - Creation time +* _file.ctime_ - Last status change time +* _file_events.ctime_ - Last status change time +* _gatekeeper_approved_apps.ctime_ - Last change time +* _process_events.ctime_ - File last metadata change in UNIX time +* _shared_memory.ctime_ - Changed time + +*current_capacity* - keyword, number.long + +* _battery.current_capacity_ - The battery's current charged capacity in mAh + +*current_clock_speed* - keyword, number.long + +* _cpu_info.current_clock_speed_ - The current frequency of the CPU. + +*current_directory* - keyword, text.text + +* _windows_crashes.current_directory_ - Current working directory of the crashed process + +*current_disk_queue_length* - keyword, number.long + +* _physical_disk_performance.current_disk_queue_length_ - Number of requests outstanding on the disk at the time the performance data is collected + +*current_locale* - keyword, text.text + +* _chrome_extensions.current_locale_ - Current locale supported by extension + +*current_value* - keyword, text.text + +* _system_controls.current_value_ - Value of setting + +*cwd* - keyword, text.text + +* _bpf_process_events.cwd_ - Current working directory +* _es_process_events.cwd_ - The process current working directory +* _process_events.cwd_ - The process current working directory +* _process_file_events.cwd_ - The current working directory of the process +* _processes.cwd_ - Process current working directory + +*cycle_count* - keyword, number.long + +* _battery.cycle_count_ - The number of charge/discharge cycles + +*data* - keyword, text.text + +* _magic.data_ - Magic number data from libmagic +* _registry.data_ - Data content of registry value +* _windows_eventlog.data_ - Data associated with the event +* _windows_events.data_ - Data associated with the event + +*data_width* - keyword, number.long + +* _memory_devices.data_width_ - Data width, in bits, of this memory device + +*database* - keyword, number.long + +* _lxd_cluster_members.database_ - Whether the server is a database node (1) or not (0) + +*date* - keyword + +* _drivers.date_ - Driver date +* _platform_info.date_ - Self-reported platform code update date + +*datetime* - keyword, text.text + +* _crashes.datetime_ - Date/Time at which the crash occurred +* _powershell_events.datetime_ - System time at which the Powershell script event occurred +* _syslog_events.datetime_ - Time known to syslog +* _time.datetime_ - Current date and time (ISO format) in the system +* _windows_crashes.datetime_ - Timestamp (log format) of the crash +* _windows_eventlog.datetime_ - System time at which the event occurred +* _windows_events.datetime_ - System time at which the event occurred + +*day* - keyword, number.long + +* _time.day_ - Current day in the system + +*day_of_month* - keyword, text.text + +* _crontab.day_of_month_ - The day of the month for the job + +*day_of_week* - keyword, text.text + +* _crontab.day_of_week_ - The day of the week for the job + +*days* - keyword, number.long + +* _uptime.days_ - Days of uptime + +*dc_site_name* - keyword, text.text + +* _ntdomains.dc_site_name_ - The name of the site where the domain controller is located. + +*decompressed* - keyword, number.long + +* _virtual_memory_info.decompressed_ - The total number of pages that have been decompressed by the VM compressor. + +*default_locale* - keyword, text.text + +* _chrome_extensions.default_locale_ - Default locale supported by extension + +*default_value* - keyword, text.text + +* _osquery_flags.default_value_ - Flag default value + +*denied_mask* - keyword, text.text + +* _apparmor_events.denied_mask_ - Denied permissions for the process + +*denylisted* - keyword, number.long + +* _osquery_schedule.denylisted_ - 1 if the query is denylisted else 0 + +*dependencies* - keyword, text.text + +* _kernel_panics.dependencies_ - Module dependencies existing in crashed module's backtrace + +*depth* - keyword, number.long + +* _iokit_devicetree.depth_ - Device nested depth +* _iokit_registry.depth_ - Node nested depth + +*description* - keyword, text.text + +* _appcompat_shims.description_ - Description of the SDB. +* _atom_packages.description_ - Package supplied description +* _browser_plugins.description_ - Plugin description text +* _chassis_info.description_ - An extended description of the chassis if available. +* _chrome_extensions.description_ - Extension-optional description +* _disk_info.description_ - The OS's description of the disk. +* _drivers.description_ - Driver description +* _firefox_addons.description_ - Addon-supplied description string +* _interface_details.description_ - Short description of the object a one-line string. +* _keychain_acls.description_ - The description included with the ACL entry +* _keychain_items.description_ - Optional item description +* _logical_drives.description_ - The canonical description of the drive, e.g. 'Logical Fixed Disk', 'CD-ROM Disk'. +* _lxd_images.description_ - Image description +* _lxd_instances.description_ - Instance description +* _npm_packages.description_ - Package supplied description +* _osquery_flags.description_ - Flag description +* _patches.description_ - Fuller description of the patch. +* _safari_extensions.description_ - Optional extension description text +* _services.description_ - Service Description +* _shared_resources.description_ - A textual description of the object +* _shortcut_files.description_ - Lnk file description. +* _smbios_tables.description_ - Table entry description +* _systemd_units.description_ - Unit description +* _users.description_ - Optional user description +* _ycloud_instance_metadata.description_ - Description of the VM + +*designed_capacity* - keyword, number.long + +* _battery.designed_capacity_ - The battery's designed capacity in mAh + +*dest_path* - keyword, text.text + +* _process_file_events.dest_path_ - The canonical path associated with the event + +*destination* - keyword, text.text + +* _cups_jobs.destination_ - The printer the job was sent to +* _docker_container_mounts.destination_ - Destination path inside container +* _routes.destination_ - Destination IP address + +*destination_id* - keyword, text.text + +* _time_machine_backups.destination_id_ - Time Machine destination ID +* _time_machine_destinations.destination_id_ - Time Machine destination ID + +*dev_id_enabled* - keyword, number.long + +* _gatekeeper.dev_id_enabled_ - 1 If a Gatekeeper allows execution from identified developers else 0 + +*developer_id* - keyword, text.text + +* _safari_extensions.developer_id_ - Optional developer identifier +* _xprotect_meta.developer_id_ - Developer identity (SHA1) of extension + +*development_region* - keyword, text.text + +* _apps.development_region_ - Info properties CFBundleDevelopmentRegion label +* _browser_plugins.development_region_ - Plugin language-localization + +*device* - keyword, text.text + +* _device_file.device_ - Absolute file path to device node +* _device_firmware.device_ - The device name +* _device_hash.device_ - Absolute file path to device node +* _device_partitions.device_ - Absolute file path to device node +* _disk_events.device_ - Disk event BSD name +* _file.device_ - Device ID (optional) +* _kernel_info.device_ - Kernel device identifier +* _lxd_instance_devices.device_ - Name of the device +* _mounts.device_ - Mounted device +* _process_memory_map.device_ - MA:MI Major/minor device ID + +*device_alias* - keyword, text.text + +* _mounts.device_alias_ - Mounted device alias + +*device_error_address* - keyword, text.text + +* _memory_error_info.device_error_address_ - 32 bit physical address of the error relative to the start of the failing memory address, in bytes + +*device_id* - keyword, text.text + +* _bitlocker_info.device_id_ - ID of the encrypted drive. +* _cpu_info.device_id_ - The DeviceID of the CPU. +* _drivers.device_id_ - Device ID +* _logical_drives.device_id_ - The drive id, usually the drive name, e.g., 'C:'. + +*device_locator* - keyword, text.text + +* _memory_devices.device_locator_ - String number of the string that identifies the physically-labeled socket or board position where the memory device is located + +*device_model* - keyword, text.text + +* _smart_drive_info.device_model_ - Device Model + +*device_name* - keyword, text.text + +* _drivers.device_name_ - Device name +* _md_devices.device_name_ - md device name +* _smart_drive_info.device_name_ - Name of block device + +*device_path* - keyword, text.text + +* _iokit_devicetree.device_path_ - Device tree path + +*device_type* - keyword, text.text + +* _lxd_instance_devices.device_type_ - Device type +* _shortcut_files.device_type_ - Device containing the target file. + +*dhcp_enabled* - keyword, number.long + +* _interface_details.dhcp_enabled_ - If TRUE, the dynamic host configuration protocol (DHCP) server automatically assigns an IP address to the computer system when establishing a network connection. + +*dhcp_lease_expires* - keyword, text.text + +* _interface_details.dhcp_lease_expires_ - Expiration date and time for a leased IP address that was assigned to the computer by the dynamic host configuration protocol (DHCP) server. + +*dhcp_lease_obtained* - keyword, text.text + +* _interface_details.dhcp_lease_obtained_ - Date and time the lease was obtained for the IP address assigned to the computer by the dynamic host configuration protocol (DHCP) server. + +*dhcp_server* - keyword, text.text + +* _interface_details.dhcp_server_ - IP address of the dynamic host configuration protocol (DHCP) server. + +*directory* - keyword, text.text + +* _extended_attributes.directory_ - Directory of file(s) +* _file.directory_ - Directory of file(s) +* _hash.directory_ - Must provide a path or directory +* _npm_packages.directory_ - Node module's directory where this package is located +* _python_packages.directory_ - Directory where Python modules are located +* _users.directory_ - User's home directory + +*disabled* - keyword + +* _browser_plugins.disabled_ - Is the plugin disabled. 1 = Disabled +* _firefox_addons.disabled_ - 1 If the addon is application-disabled else 0 +* _launchd.disabled_ - Skip loading this daemon or agent on boot +* _wifi_networks.disabled_ - 1 if this network is disabled, 0 otherwise + +*disc_sharing* - keyword, number.long + +* _sharing_preferences.disc_sharing_ - 1 If CD or DVD sharing is enabled else 0 + +*disconnected* - keyword, number.long + +* _connectivity.disconnected_ - True if the all interfaces are not connected to any network + +*discovery_cache_hits* - keyword, number.long + +* _osquery_packs.discovery_cache_hits_ - The number of times that the discovery query used cached values since the last time the config was reloaded + +*discovery_executions* - keyword, number.long + +* _osquery_packs.discovery_executions_ - The number of times that the discovery queries have been executed since the last time the config was reloaded + +*disk_bytes_read* - keyword, number.long + +* _processes.disk_bytes_read_ - Bytes read from disk + +*disk_bytes_written* - keyword, number.long + +* _processes.disk_bytes_written_ - Bytes written to disk + +*disk_id* - keyword, number.long + +* _smart_drive_info.disk_id_ - Physical slot number of device, only exists when hardware storage controller exists + +*disk_index* - keyword, number.long + +* _disk_info.disk_index_ - Physical drive number of the disk. + +*disk_read* - keyword, number.long + +* _docker_container_stats.disk_read_ - Total disk read bytes + +*disk_size* - keyword, number.long + +* _disk_info.disk_size_ - Size of the disk. + +*disk_write* - keyword, number.long + +* _docker_container_stats.disk_write_ - Total disk write bytes + +*display_name* - keyword, text.text + +* _apps.display_name_ - Info properties CFBundleDisplayName label +* _services.display_name_ - Service Display name + +*dns_domain* - keyword, text.text + +* _interface_details.dns_domain_ - Organization name followed by a period and an extension that indicates the type of organization, such as 'microsoft.com'. + +*dns_domain_name* - keyword, text.text + +* _logon_sessions.dns_domain_name_ - The DNS name for the owner of the logon session. + +*dns_domain_suffix_search_order* - keyword, text.text + +* _interface_details.dns_domain_suffix_search_order_ - Array of DNS domain suffixes to be appended to the end of host names during name resolution. + +*dns_forest_name* - keyword, text.text + +* _ntdomains.dns_forest_name_ - The name of the root of the DNS tree. + +*dns_host_name* - keyword, text.text + +* _interface_details.dns_host_name_ - Host name used to identify the local computer for authentication by some utilities. + +*dns_server_search_order* - keyword, text.text + +* _interface_details.dns_server_search_order_ - Array of server IP addresses to be used in querying for DNS servers. + +*domain* - keyword, text.text + +* _ad_config.domain_ - Active Directory trust domain +* _managed_policies.domain_ - System or manager-chosen domain key +* _preferences.domain_ - Application ID usually in com.name.product format + +*domain_controller_address* - keyword, text.text + +* _ntdomains.domain_controller_address_ - The IP Address of the discovered domain controller.. + +*domain_controller_name* - keyword, text.text + +* _ntdomains.domain_controller_name_ - The name of the discovered domain controller. + +*domain_name* - keyword, text.text + +* _ntdomains.domain_name_ - The name of the domain. + +*drive_letter* - keyword, text.text + +* _bitlocker_info.drive_letter_ - Drive letter of the encrypted drive. +* _ntfs_journal_events.drive_letter_ - The drive letter identifying the source journal + +*drive_name* - keyword, text.text + +* _md_drives.drive_name_ - Drive device name + +*driver* - keyword, text.text + +* _docker_container_mounts.driver_ - Driver providing the mount +* _docker_networks.driver_ - Network driver +* _docker_volumes.driver_ - Volume driver +* _hardware_events.driver_ - Driver claiming the device +* _lxd_storage_pools.driver_ - Storage driver +* _pci_devices.driver_ - PCI Device used driver +* _video_info.driver_ - The driver of the device. + +*driver_date* - keyword, number.long + +* _video_info.driver_date_ - The date listed on the installed driver. + +*driver_key* - keyword, text.text + +* _drivers.driver_key_ - Driver key + +*driver_type* - keyword, text.text + +* _smart_drive_info.driver_type_ - The explicit device type used to retrieve the SMART information + +*driver_version* - keyword, text.text + +* _video_info.driver_version_ - The version of the installed driver. + +*dst_ip* - keyword, text.text + +* _iptables.dst_ip_ - Destination IP address. + +*dst_mask* - keyword, text.text + +* _iptables.dst_mask_ - Destination IP address mask. + +*dst_port* - keyword, text.text + +* _iptables.dst_port_ - Protocol destination port(s). + +*dtime* - keyword, number.long + +* _shared_memory.dtime_ - Detached time + +*dump_certificate* - keyword, number.long + +* _curl_certificate.dump_certificate_ - Set this value to '1' to dump certificate + +*duration* - keyword, number.long + +* _bpf_process_events.duration_ - How much time was spent inside the syscall (nsecs) +* _bpf_socket_events.duration_ - How much time was spent inside the syscall (nsecs) + +*eapi* - keyword, number.long + +* _portage_packages.eapi_ - The eapi for the ebuild + +*egid* - keyword + +* _docker_container_processes.egid_ - Effective group ID +* _es_process_events.egid_ - Effective Group ID of the process +* _process_events.egid_ - Effective group ID at process start +* _process_file_events.egid_ - Effective group ID of the process using the file +* _processes.egid_ - Unsigned effective group ID + +*eid* - keyword, text.text + +* _apparmor_events.eid_ - Event ID +* _bpf_process_events.eid_ - Event ID +* _bpf_socket_events.eid_ - Event ID +* _disk_events.eid_ - Event ID +* _es_process_events.eid_ - Event ID +* _file_events.eid_ - Event ID +* _hardware_events.eid_ - Event ID +* _ntfs_journal_events.eid_ - Event ID +* _process_events.eid_ - Event ID +* _process_file_events.eid_ - Event ID +* _selinux_events.eid_ - Event ID +* _socket_events.eid_ - Event ID +* _syslog_events.eid_ - Event ID +* _user_events.eid_ - Event ID +* _windows_events.eid_ - Event ID +* _yara_events.eid_ - Event ID + +*ejectable* - keyword, number.long + +* _disk_events.ejectable_ - 1 if ejectable, 0 if not + +*elapsed_time* - keyword, number.long + +* _processes.elapsed_time_ - Elapsed time in seconds this process has been running. + +*element* - keyword, text.text + +* _apps.element_ - Does the app identify as a background agent + +*elevated_token* - keyword, number.long + +* _processes.elevated_token_ - Process uses elevated token yes=1, no=0 + +*enable_ipv6* - keyword, number.long + +* _docker_networks.enable_ipv6_ - 1 if IPv6 is enabled on this network. 0 otherwise + +*enabled* - keyword + +* _app_schemes.enabled_ - 1 if this handler is the OS default, else 0 +* _event_taps.enabled_ - Is the Event Tap enabled +* _interface_details.enabled_ - Indicates whether the adapter is enabled or not. +* _location_services.enabled_ - 1 if Location Services are enabled, else 0 +* _lxd_cluster.enabled_ - Whether clustering enabled (1) or not (0) on this node +* _sandboxes.enabled_ - Application sandboxings enabled on container +* _scheduled_tasks.enabled_ - Whether or not the scheduled task is enabled +* _screenlock.enabled_ - 1 If a password is required after sleep or the screensaver begins; else 0 +* _sip_config.enabled_ - 1 if this configuration is enabled, otherwise 0 +* _tpm_info.enabled_ - TPM is enabled +* _yum_sources.enabled_ - Whether the repository is used + +*enabled_nvram* - keyword, number.long + +* _sip_config.enabled_nvram_ - 1 if this configuration is enabled, otherwise 0 + +*encrypted* - keyword, number.long + +* _disk_encryption.encrypted_ - 1 If encrypted: true (disk is encrypted), else 0 +* _user_ssh_keys.encrypted_ - 1 if key is encrypted, 0 otherwise + +*encryption* - keyword, text.text + +* _time_machine_destinations.encryption_ - Last known encrypted state + +*encryption_method* - keyword, text.text + +* _bitlocker_info.encryption_method_ - The encryption type of the device. + +*encryption_status* - keyword, text.text + +* _disk_encryption.encryption_status_ - Disk encryption status with one of following values: encrypted | not encrypted | undefined + +*end* - keyword, text.text + +* _memory_map.end_ - End address of memory region +* _process_memory_map.end_ - Virtual end address (hex) + +*ending_address* - keyword, text.text + +* _memory_array_mapped_addresses.ending_address_ - Physical ending address of last kilobyte of a range of memory mapped to physical memory array +* _memory_device_mapped_addresses.ending_address_ - Physical ending address of last kilobyte of a range of memory mapped to physical memory array + +*endpoint_id* - keyword, text.text + +* _docker_container_networks.endpoint_id_ - Endpoint ID + +*entry* - keyword, text.text + +* _authorization_mechanisms.entry_ - The whole string entry +* _elf_info.entry_ - Entry point address +* _shimcache.entry_ - Execution order. + +*env* - keyword, text.text + +* _es_process_events.env_ - Environment variables delimited by spaces +* _process_events.env_ - Environment variables delimited by spaces + +*env_count* - keyword, number.long + +* _es_process_events.env_count_ - Number of environment variables +* _process_events.env_count_ - Number of environment variables + +*env_size* - keyword, number.long + +* _process_events.env_size_ - Actual size (bytes) of environment list + +*env_variables* - keyword, text.text + +* _docker_containers.env_variables_ - Container environmental variables + +*environment* - keyword, text.text + +* _apps.environment_ - Application-set environment variables + +*ephemeral* - keyword, number.long + +* _lxd_instances.ephemeral_ - Whether the instance is ephemeral(1) or not(0) + +*epoch* - keyword, number.long + +* _rpm_packages.epoch_ - Package epoch value + +*error* - keyword, text.text + +* _apparmor_events.error_ - Error information + +*error_granularity* - keyword, text.text + +* _memory_error_info.error_granularity_ - Granularity to which the error can be resolved + +*error_operation* - keyword, text.text + +* _memory_error_info.error_operation_ - Memory access operation that caused the error + +*error_resolution* - keyword, text.text + +* _memory_error_info.error_resolution_ - Range, in bytes, within which this error can be determined, when an error address is given + +*error_type* - keyword, text.text + +* _memory_error_info.error_type_ - type of error associated with current error status for array or device + +*euid* - keyword + +* _docker_container_processes.euid_ - Effective user ID +* _es_process_events.euid_ - Effective User ID of the process +* _process_events.euid_ - Effective user ID at process start +* _process_file_events.euid_ - Effective user ID of the process using the file +* _processes.euid_ - Unsigned effective user ID + +*event* - keyword, text.text + +* _crontab.event_ - The job @event name (rare) + +*event_queue* - keyword, number.long + +* _carbon_black_info.event_queue_ - Size in bytes of Carbon Black event files on disk + +*event_tap_id* - keyword, number.long + +* _event_taps.event_tap_id_ - Unique ID for the Tap + +*event_tapped* - keyword, text.text + +* _event_taps.event_tapped_ - The mask that identifies the set of events to be observed. + +*event_type* - keyword, text.text + +* _es_process_events.event_type_ - Type of EndpointSecurity event + +*eventid* - keyword, number.long + +* _windows_eventlog.eventid_ - Event ID of the event +* _windows_events.eventid_ - Event ID of the event + +*events* - keyword, number.long + +* _osquery_events.events_ - Number of events emitted or received since osquery started + +*exception_address* - keyword, text.text + +* _windows_crashes.exception_address_ - Address (in hex) where the exception occurred + +*exception_code* - keyword, text.text + +* _windows_crashes.exception_code_ - The Windows exception code + +*exception_codes* - keyword, text.text + +* _crashes.exception_codes_ - Exception codes from the crash + +*exception_message* - keyword, text.text + +* _windows_crashes.exception_message_ - The NTSTATUS error message associated with the exception code + +*exception_notes* - keyword, text.text + +* _crashes.exception_notes_ - Exception notes from the crash + +*exception_type* - keyword, text.text + +* _crashes.exception_type_ - Exception type of the crash + +*exe* - keyword, text.text + +* _seccomp_events.exe_ - The path to the executable that was used to invoke the analyzed process + +*executable* - keyword, text.text + +* _appcompat_shims.executable_ - Name of the executable that is being shimmed. This is pulled from the registry. +* _process_file_events.executable_ - The executable path + +*executable_path* - keyword, text.text + +* _wmi_cli_event_consumers.executable_path_ - Module to execute. The string can specify the full path and file name of the module to execute, or it can specify a partial name. If a partial name is specified, the current drive and current directory are assumed. + +*execution_flag* - keyword, number.long + +* _shimcache.execution_flag_ - Boolean Execution flag, 1 for execution, 0 for no execution, -1 for missing (this flag does not exist on Windows 10 and higher). + +*executions* - keyword, number.long + +* _osquery_schedule.executions_ - Number of times the query was executed + +*exit_code* - keyword, text.text + +* _bpf_process_events.exit_code_ - Exit code of the system call +* _bpf_socket_events.exit_code_ - Exit code of the system call +* _es_process_events.exit_code_ - Exit code of a process in case of an exit event + +*expand* - keyword, number.long + +* _default_environment.expand_ - 1 if the variable needs expanding, 0 otherwise + +*expire* - keyword, number.long + +* _shadow.expire_ - Number of days since UNIX epoch date until account is disabled + +*expires_at* - keyword, text.text + +* _lxd_images.expires_at_ - ISO time of image expiration + +*extended_key_usage* - keyword, text.text + +* _curl_certificate.extended_key_usage_ - Extended usage of key in certificate + +*extensions* - keyword, text.text + +* _osquery_info.extensions_ - osquery extensions status + +*external* - keyword, number.long + +* _app_schemes.external_ - 1 if this handler does NOT exist on OS X by default, else 0 + +*extra* - keyword, text.text + +* _asl.extra_ - Extra columns, in JSON format. Queries against this column are performed entirely in SQLite, so do not benefit from efficient querying via asl.h. +* _platform_info.extra_ - Platform-specific additional information + +*facility* - keyword, text.text + +* _asl.facility_ - Sender's facility. Default is 'user'. +* _syslog_events.facility_ - Syslog facility + +*fahrenheit* - keyword, number.double + +* _temperature_sensors.fahrenheit_ - Temperature in Fahrenheit + +*failed_disks* - keyword, number.long + +* _md_devices.failed_disks_ - Number of failed disks in array + +*failed_login_count* - keyword, number.long + +* _account_policy_data.failed_login_count_ - The number of failed login attempts using an incorrect password. Count resets after a correct password is entered. + +*failed_login_timestamp* - keyword, number.double + +* _account_policy_data.failed_login_timestamp_ - The time of the last failed login attempt. Resets after a correct password is entered + +*family* - keyword, number.long + +* _bpf_socket_events.family_ - The Internet protocol family ID +* _listening_ports.family_ - Network protocol (IPv4, IPv6) +* _process_open_sockets.family_ - Network protocol (IPv4, IPv6) +* _socket_events.family_ - The Internet protocol family ID + +*fan* - keyword, text.text + +* _fan_speed_sensors.fan_ - Fan number + +*faults* - keyword, number.long + +* _virtual_memory_info.faults_ - Total number of calls to vm_faults. + +*fd* - keyword, text.text + +* _bpf_socket_events.fd_ - The file description for the process socket +* _listening_ports.fd_ - Socket file descriptor number +* _process_open_files.fd_ - Process-specific file descriptor number +* _process_open_pipes.fd_ - File descriptor +* _process_open_sockets.fd_ - Socket file descriptor number +* _socket_events.fd_ - The file description for the process socket + +*feature* - keyword, text.text + +* _cpuid.feature_ - Present feature flags + +*feature_control* - keyword, number.long + +* _msr.feature_control_ - Bitfield controlling enabled features. + +*field_name* - keyword, text.text + +* _system_controls.field_name_ - Specific attribute of opaque type + +*file_attributes* - keyword, text.text + +* _ntfs_journal_events.file_attributes_ - File attributes + +*file_backed* - keyword, number.long + +* _virtual_memory_info.file_backed_ - Total number of file backed pages. + +*file_id* - keyword, text.text + +* _file.file_id_ - file ID + +*file_sharing* - keyword, number.long + +* _sharing_preferences.file_sharing_ - 1 If file sharing is enabled else 0 + +*file_system* - keyword, text.text + +* _logical_drives.file_system_ - The file system of the drive. + +*file_version* - keyword, text.text + +* _file.file_version_ - File version + +*filename* - keyword, text.text + +* _device_file.filename_ - Name portion of file path +* _file.filename_ - Name portion of file path +* _lxd_images.filename_ - Filename of the image file +* _prefetch.filename_ - Executable filename. +* _xprotect_entries.filename_ - Use this file name to match + +*filepath* - keyword, text.text + +* _package_bom.filepath_ - Package file or directory + +*filesystem* - keyword, text.text + +* _disk_events.filesystem_ - Filesystem if available + +*filetype* - keyword, text.text + +* _xprotect_entries.filetype_ - Use this file type to match + +*filevault_status* - keyword, text.text + +* _disk_encryption.filevault_status_ - FileVault status with one of following values: on | off | unknown + +*filter* - keyword, text.text + +* _wmi_filter_consumer_binding.filter_ - Reference to an instance of __EventFilter that represents the object path to an event filter which is a query that specifies the type of event to be received. + +*filter_name* - keyword, text.text + +* _iptables.filter_name_ - Packet matching filter table name. + +*fingerprint* - keyword, text.text + +* _lxd_certificates.fingerprint_ - SHA256 hash of the certificate + +*finished_at* - keyword, text.text + +* _docker_containers.finished_at_ - Container finish time as string + +*firewall* - keyword, text.text + +* _windows_security_center.firewall_ - The health of the monitored Firewall (see windows_security_products) + +*firewall_unload* - keyword, number.long + +* _alf.firewall_unload_ - 1 If firewall unloading enabled else 0 + +*firmware_version* - keyword, text.text + +* _ibridge_info.firmware_version_ - The build version of the firmware +* _smart_drive_info.firmware_version_ - Drive firmware version + +*fix_comments* - keyword, text.text + +* _patches.fix_comments_ - Additional comments about the patch. + +*flag* - keyword, number.long + +* _shadow.flag_ - Reserved + +*flags* - keyword + +* _device_partitions.flags_ - +* _dns_cache.flags_ - DNS record flags +* _elf_info.flags_ - ELF header flags +* _elf_sections.flags_ - Section attributes +* _elf_segments.flags_ - Segment attributes +* _interface_details.flags_ - Flags (netdevice) for the device +* _mounts.flags_ - Mounted device flags +* _pipes.flags_ - The flags indicating whether this pipe connection is a server or client end, and if the pipe for sending messages or bytes +* _routes.flags_ - Flags to describe route + +*flatsize* - keyword, number.long + +* _pkg_packages.flatsize_ - Package size in bytes + +*folder_id* - keyword, text.text + +* _ycloud_instance_metadata.folder_id_ - Folder identifier for the VM + +*following* - keyword, text.text + +* _systemd_units.following_ - The name of another unit that this unit follows in state + +*forced* - keyword, number.long + +* _preferences.forced_ - 1 if the value is forced/managed, else 0 + +*form_factor* - keyword, text.text + +* _memory_devices.form_factor_ - Implementation form factor for this memory device +* _smart_drive_info.form_factor_ - Form factor if reported + +*format* - keyword, text.text + +* _cups_jobs.format_ - The format of the print job + +*forwarding_enabled* - keyword, number.long + +* _interface_ipv6.forwarding_enabled_ - Enable IP forwarding + +*fragment_path* - keyword, text.text + +* _systemd_units.fragment_path_ - The unit file path this unit was read from, if there is any + +*frame_backtrace* - keyword, text.text + +* _kernel_panics.frame_backtrace_ - Backtrace of the crashed module + +*free* - keyword, number.long + +* _virtual_memory_info.free_ - Total number of free pages. + +*free_space* - keyword, number.long + +* _logical_drives.free_space_ - The amount of free space, in bytes, of the drive (-1 on failure). + +*friendly_name* - keyword, text.text + +* _interface_addresses.friendly_name_ - The friendly display name of the interface. +* _interface_details.friendly_name_ - The friendly display name of the interface. + +*from_webstore* - keyword, text.text + +* _chrome_extensions.from_webstore_ - True if this extension was installed from the web store + +*fs_id* - keyword, text.text + +* _quicklook_cache.fs_id_ - Quicklook file fs_id key + +*fsgid* - keyword + +* _process_events.fsgid_ - Filesystem group ID at process start +* _process_file_events.fsgid_ - Filesystem group ID of the process using the file + +*fsuid* - keyword + +* _apparmor_events.fsuid_ - Filesystem user ID +* _process_events.fsuid_ - Filesystem user ID at process start +* _process_file_events.fsuid_ - Filesystem user ID of the process using the file + +*gateway* - keyword, text.text + +* _docker_container_networks.gateway_ - Gateway +* _docker_networks.gateway_ - Network gateway +* _routes.gateway_ - Route gateway + +*gid* - keyword + +* _asl.gid_ - GID that sent the log message (set by the server). +* _bpf_process_events.gid_ - Group ID +* _bpf_socket_events.gid_ - Group ID +* _device_file.gid_ - Owning group ID +* _docker_container_processes.gid_ - Group ID +* _es_process_events.gid_ - Group ID of the process +* _file.gid_ - Owning group ID +* _file_events.gid_ - Owning group ID +* _groups.gid_ - Unsigned int64 group ID +* _package_bom.gid_ - Expected group of file or directory +* _process_events.gid_ - Group ID at process start +* _process_file_events.gid_ - The gid of the process performing the action +* _processes.gid_ - Unsigned group ID +* _seccomp_events.gid_ - Group ID of the user who started the analyzed process +* _user_groups.gid_ - Group ID +* _users.gid_ - Group ID (unsigned) + +*gid_signed* - keyword, number.long + +* _groups.gid_signed_ - A signed int64 version of gid +* _users.gid_signed_ - Default group ID as int64 signed (Apple) + +*git_commit* - keyword, text.text + +* _docker_version.git_commit_ - Docker build git commit + +*global_seq_num* - keyword, number.long + +* _es_process_events.global_seq_num_ - Global sequence number + +*global_state* - keyword, number.long + +* _alf.global_state_ - 1 If the firewall is enabled with exceptions, 2 if the firewall is configured to block all incoming connections, else 0 + +*go_version* - keyword, text.text + +* _docker_version.go_version_ - Go version + +*gpgcheck* - keyword, text.text + +* _yum_sources.gpgcheck_ - Whether packages are GPG checked + +*gpgkey* - keyword, text.text + +* _yum_sources.gpgkey_ - URL to GPG key + +*grace_period* - keyword, number.long + +* _screenlock.grace_period_ - The amount of time in seconds the screen must be asleep or the screensaver on before a password is required on-wake. 0 = immediately; -1 = no password is required on-wake + +*group_sid* - keyword, text.text + +* _groups.group_sid_ - Unique group ID + +*groupname* - keyword, text.text + +* _groups.groupname_ - Canonical local group name +* _launchd.groupname_ - Run this daemon or agent as this group +* _rpm_package_files.groupname_ - File default groupname from info DB +* _suid_bin.groupname_ - Binary owner group + +*guest* - keyword, number.long + +* _cpu_time.guest_ - Time spent running a virtual CPU for a guest OS under the control of the Linux kernel + +*guest_nice* - keyword, number.long + +* _cpu_time.guest_nice_ - Time spent running a niced guest + +*handle* - keyword, text.text + +* _memory_array_mapped_addresses.handle_ - Handle, or instance number, associated with the structure +* _memory_arrays.handle_ - Handle, or instance number, associated with the array +* _memory_device_mapped_addresses.handle_ - Handle, or instance number, associated with the structure +* _memory_devices.handle_ - Handle, or instance number, associated with the structure in SMBIOS +* _memory_error_info.handle_ - Handle, or instance number, associated with the structure +* _oem_strings.handle_ - Handle, or instance number, associated with the Type 11 structure +* _smbios_tables.handle_ - Table entry handle + +*handle_count* - keyword, number.long + +* _processes.handle_count_ - Total number of handles that the process has open. This number is the sum of the handles currently opened by each thread in the process. + +*handler* - keyword, text.text + +* _app_schemes.handler_ - Application label for the handler + +*hard_limit* - keyword, text.text + +* _ulimit_info.hard_limit_ - Maximum limit value + +*hard_links* - keyword, number.long + +* _device_file.hard_links_ - Number of hard links +* _file.hard_links_ - Number of hard links + +*hardware_model* - keyword, text.text + +* _disk_info.hardware_model_ - Hard drive model. +* _system_info.hardware_model_ - Hardware model + +*hardware_serial* - keyword, text.text + +* _system_info.hardware_serial_ - Device serial number + +*hardware_vendor* - keyword, text.text + +* _system_info.hardware_vendor_ - Hardware vendor + +*hardware_version* - keyword, text.text + +* _system_info.hardware_version_ - Hardware version + +*has_expired* - keyword, number.long + +* _curl_certificate.has_expired_ - 1 if the certificate has expired, 0 otherwise + +*hash* - keyword, text.text + +* _prefetch.hash_ - Prefetch CRC hash. + +*hash_alg* - keyword, text.text + +* _shadow.hash_alg_ - Password hashing algorithm + +*hash_resources* - keyword, number.long + +* _signature.hash_resources_ - Set to 1 to also hash resources, or 0 otherwise. Default is 1 + +*hashed* - keyword, number.long + +* _file_events.hashed_ - 1 if the file was hashed, 0 if not, -1 if hashing failed + +*header* - keyword, text.text + +* _sudoers.header_ - Symbol for given rule + +*header_size* - keyword, number.long + +* _smbios_tables.header_size_ - Header size in bytes + +*health* - keyword, text.text + +* _battery.health_ - One of the following: "Good" describes a well-performing battery, "Fair" describes a functional battery with limited capacity, or "Poor" describes a battery that's not capable of providing power + +*hidden* - keyword, number.long + +* _scheduled_tasks.hidden_ - Whether or not the task is visible in the UI +* _smc_keys.hidden_ - 1 if this key is normally hidden, otherwise 0 + +*history_file* - keyword, text.text + +* _shell_history.history_file_ - Path to the .*_history for this user + +*hit_count* - keyword, text.text + +* _quicklook_cache.hit_count_ - Number of cache hits on thumbnail + +*home_directory* - keyword, text.text + +* _logon_sessions.home_directory_ - The home directory for the logon session. + +*home_directory_drive* - keyword, text.text + +* _logon_sessions.home_directory_drive_ - The drive location of the home directory of the logon session. + +*homepage* - keyword, text.text + +* _atom_packages.homepage_ - Package supplied homepage + +*hop_limit* - keyword, number.long + +* _interface_ipv6.hop_limit_ - Current Hop Limit + +*hopcount* - keyword, number.long + +* _routes.hopcount_ - Max hops expected + +*host* - keyword, text.text + +* _asl.host_ - Sender's address (set by the server). +* _last.host_ - Entry hostname +* _logged_in_users.host_ - Remote hostname +* _preferences.host_ - 'current' or 'any' host, where 'current' takes precedence +* _syslog_events.host_ - Hostname configured for syslog + +*host_ip* - keyword, text.text + +* _docker_container_ports.host_ip_ - Host IP address on which public port is listening + +*host_port* - keyword, number.long + +* _docker_container_ports.host_port_ - Host port + +*hostname* - keyword, text.text + +* _curl_certificate.hostname_ - Hostname (domain[:port]) to CURL +* _shortcut_files.hostname_ - Optional hostname of the target file. +* _system_info.hostname_ - Network hostname including domain +* _ycloud_instance_metadata.hostname_ - Hostname of the VM + +*hostnames* - keyword, text.text + +* _etc_hosts.hostnames_ - Raw hosts mapping + +*hotfix_id* - keyword, text.text + +* _patches.hotfix_id_ - The KB ID of the patch. + +*hour* - keyword, text.text + +* _crontab.hour_ - The hour of the day for the job +* _time.hour_ - Current hour in the system + +*hours* - keyword, number.long + +* _uptime.hours_ - Hours of uptime + +*http_proxy* - keyword, text.text + +* _docker_info.http_proxy_ - HTTP proxy + +*https_proxy* - keyword, text.text + +* _docker_info.https_proxy_ - HTTPS proxy + +*hwaddr* - keyword, text.text + +* _lxd_networks.hwaddr_ - Hardware address for this network + +*iam_arn* - keyword, text.text + +* _ec2_instance_metadata.iam_arn_ - If there is an IAM role associated with the instance, contains instance profile ARN + +*ibrs_support_enabled* - keyword, number.long + +* _kva_speculative_info.ibrs_support_enabled_ - Windows uses IBRS. + +*ibytes* - keyword, number.long + +* _interface_details.ibytes_ - Input bytes + +*icon_mode* - keyword, number.long + +* _quicklook_cache.icon_mode_ - Thumbnail icon mode + +*icon_path* - keyword, text.text + +* _shortcut_files.icon_path_ - Lnk file icon location. + +*id* - keyword, text.text + +* _disk_info.id_ - The unique identifier of the drive on the system. +* _dns_resolvers.id_ - Address type index or order +* _docker_container_fs_changes.id_ - Container ID +* _docker_container_labels.id_ - Container ID +* _docker_container_mounts.id_ - Container ID +* _docker_container_networks.id_ - Container ID +* _docker_container_ports.id_ - Container ID +* _docker_container_processes.id_ - Container ID +* _docker_container_stats.id_ - Container ID +* _docker_containers.id_ - Container ID +* _docker_image_history.id_ - Image ID +* _docker_image_labels.id_ - Image ID +* _docker_image_layers.id_ - Image ID +* _docker_images.id_ - Image ID +* _docker_info.id_ - Docker system ID +* _docker_network_labels.id_ - Network ID +* _docker_networks.id_ - Network ID +* _example.id_ - An index of some sort +* _iokit_devicetree.id_ - IOKit internal registry ID +* _iokit_registry.id_ - IOKit internal registry ID +* _lxd_images.id_ - Image ID +* _systemd_units.id_ - Unique unit identifier + +*identifier* - keyword, text.text + +* _browser_plugins.identifier_ - Plugin identifier +* _chrome_extension_content_scripts.identifier_ - Extension identifier +* _chrome_extensions.identifier_ - Extension identifier, computed from its manifest. Empty in case of error. +* _crashes.identifier_ - Identifier of the crashed process +* _firefox_addons.identifier_ - Addon identifier +* _safari_extensions.identifier_ - Extension identifier +* _signature.identifier_ - The signing identifier sealed into the signature +* _system_extensions.identifier_ - Identifier name +* _xprotect_meta.identifier_ - Browser plugin or extension identifier + +*identifying_number* - keyword, text.text + +* _programs.identifying_number_ - Product identification such as a serial number on software, or a die number on a hardware chip. + +*identity* - keyword, text.text + +* _xprotect_entries.identity_ - XProtect identity (SHA1) of content + +*idle* - keyword, number.long + +* _cpu_time.idle_ - Time spent in the idle task + +*idrops* - keyword, number.long + +* _interface_details.idrops_ - Input drops + +*idx* - keyword, number.long + +* _kernel_extensions.idx_ - Extension load tag or index + +*ierrors* - keyword, number.long + +* _interface_details.ierrors_ - Input errors + +*image* - keyword, text.text + +* _docker_containers.image_ - Docker image (name) used to launch this container +* _drivers.image_ - Path to driver image file + +*image_id* - keyword, text.text + +* _docker_containers.image_id_ - Docker image ID + +*images* - keyword, number.long + +* _docker_info.images_ - Number of images + +*in_smartctl_db* - keyword, number.long + +* _smart_drive_info.in_smartctl_db_ - Boolean value for if drive is recognized + +*inactive* - keyword, number.long + +* _memory_info.inactive_ - The total amount of buffer or page cache memory, in bytes, that are free and available +* _shadow.inactive_ - Number of days after password expires until account is blocked +* _virtual_memory_info.inactive_ - Total number of inactive pages. + +*inetd_compatibility* - keyword, text.text + +* _launchd.inetd_compatibility_ - Run this daemon or agent as it was launched from inetd + +*inf* - keyword, text.text + +* _drivers.inf_ - Associated inf file + +*info* - keyword, text.text + +* _apparmor_events.info_ - Additional information + +*info_access* - keyword, text.text + +* _curl_certificate.info_access_ - Authority Information Access + +*info_string* - keyword, text.text + +* _apps.info_string_ - Info properties CFBundleGetInfoString label + +*inherited_from* - keyword, text.text + +* _ntfs_acl_permissions.inherited_from_ - The inheritance policy of the ACE. + +*iniface* - keyword, text.text + +* _iptables.iniface_ - Input interface for the rule. + +*iniface_mask* - keyword, text.text + +* _iptables.iniface_mask_ - Input interface mask for the rule. + +*inode* - keyword, number.long + +* _device_file.inode_ - Filesystem inode number +* _device_hash.inode_ - Filesystem inode number +* _file.inode_ - Filesystem inode number +* _file_events.inode_ - Filesystem inode number +* _process_memory_map.inode_ - Mapped path inode, 0 means uninitialized (BSS) +* _process_open_pipes.inode_ - Pipe inode number +* _quicklook_cache.inode_ - Parsed file ID (inode) from fs_id + +*inodes* - keyword, number.long + +* _device_partitions.inodes_ - Number of meta nodes +* _mounts.inodes_ - Mounted device used inodes + +*inodes_free* - keyword, number.long + +* _mounts.inodes_free_ - Mounted device free inodes + +*inodes_total* - keyword, number.long + +* _lxd_storage_pools.inodes_total_ - Total number of inodes available in this storage pool + +*inodes_used* - keyword, number.long + +* _lxd_storage_pools.inodes_used_ - Number of inodes used + +*input_eax* - keyword, text.text + +* _cpuid.input_eax_ - Value of EAX used + +*install_date* - keyword + +* _os_version.install_date_ - The install date of the OS. +* _patches.install_date_ - Indicates when the patch was installed. Lack of a value does not indicate that the patch was not installed. +* _programs.install_date_ - Date that this product was installed on the system. +* _shared_resources.install_date_ - Indicates when the object was installed. Lack of a value does not indicate that the object is not installed. + +*install_location* - keyword, text.text + +* _programs.install_location_ - The installation location directory of the product. + +*install_source* - keyword, text.text + +* _programs.install_source_ - The installation source of the product. + +*install_time* - keyword + +* _appcompat_shims.install_time_ - Install time of the SDB +* _chrome_extensions.install_time_ - Extension install time, in its original Webkit format +* _package_receipts.install_time_ - Timestamp of install time +* _rpm_packages.install_time_ - When the package was installed + +*install_timestamp* - keyword, number.long + +* _chrome_extensions.install_timestamp_ - Extension install time, converted to unix time + +*installed_by* - keyword, text.text + +* _patches.installed_by_ - The system context in which the patch as installed. + +*installed_on* - keyword, text.text + +* _patches.installed_on_ - The date when the patch was installed. + +*installer_name* - keyword, text.text + +* _package_receipts.installer_name_ - Name of installer process + +*instance_id* - keyword, text.text + +* _ec2_instance_metadata.instance_id_ - EC2 instance ID +* _ec2_instance_tags.instance_id_ - EC2 instance ID +* _osquery_info.instance_id_ - Unique, long-lived ID per instance of osquery +* _ycloud_instance_metadata.instance_id_ - Unique identifier for the VM + +*instance_identifier* - keyword, text.text + +* _hvci_status.instance_identifier_ - The instance ID of Device Guard. + +*instance_type* - keyword, text.text + +* _ec2_instance_metadata.instance_type_ - EC2 instance type + +*instances* - keyword, number.long + +* _pipes.instances_ - Number of instances of the named pipe + +*interface* - keyword, text.text + +* _arp_cache.interface_ - Interface of the network for the MAC +* _interface_addresses.interface_ - Interface name +* _interface_details.interface_ - Interface name +* _interface_ipv6.interface_ - Interface name +* _lldp_neighbors.interface_ - Interface name +* _routes.interface_ - Route local interface +* _wifi_status.interface_ - Name of the interface +* _wifi_survey.interface_ - Name of the interface + +*interleave_data_depth* - keyword, number.long + +* _memory_device_mapped_addresses.interleave_data_depth_ - The max number of consecutive rows from memory device that are accessed in a single interleave transfer; 0 indicates device is non-interleave + +*interleave_position* - keyword, number.long + +* _memory_device_mapped_addresses.interleave_position_ - The position of the device in a interleave, i.e. 0 indicates non-interleave, 1 indicates 1st interleave, 2 indicates 2nd interleave, etc. + +*internal* - keyword, number.long + +* _osquery_registry.internal_ - 1 If the plugin is internal else 0 + +*internet_settings* - keyword, text.text + +* _windows_security_center.internet_settings_ - The health of the Internet Settings + +*internet_sharing* - keyword, number.long + +* _sharing_preferences.internet_sharing_ - 1 If internet sharing is enabled else 0 + +*interval* - keyword, number.long + +* _docker_container_stats.interval_ - Difference between read and preread in nano-seconds +* _osquery_schedule.interval_ - The interval in seconds to run this query, not an exact interval + +*iowait* - keyword, number.long + +* _cpu_time.iowait_ - Time spent waiting for I/O to complete + +*ip* - keyword, text.text + +* _seccomp_events.ip_ - Instruction pointer value + +*ip_address* - keyword, text.text + +* _docker_container_networks.ip_address_ - IP address + +*ip_prefix_len* - keyword, number.long + +* _docker_container_networks.ip_prefix_len_ - IP subnet prefix length + +*ipackets* - keyword, number.long + +* _interface_details.ipackets_ - Input packets + +*ipc_namespace* - keyword, text.text + +* _docker_containers.ipc_namespace_ - IPC namespace +* _process_namespaces.ipc_namespace_ - ipc namespace inode + +*ipv4_address* - keyword, text.text + +* _lxd_networks.ipv4_address_ - IPv4 address + +*ipv4_forwarding* - keyword, number.long + +* _docker_info.ipv4_forwarding_ - 1 if IPv4 forwarding is enabled. 0 otherwise + +*ipv4_internet* - keyword, number.long + +* _connectivity.ipv4_internet_ - True if any interface is connected to the Internet via IPv4 + +*ipv4_local_network* - keyword, number.long + +* _connectivity.ipv4_local_network_ - True if any interface is connected to a routed network via IPv4 + +*ipv4_no_traffic* - keyword, number.long + +* _connectivity.ipv4_no_traffic_ - True if any interface is connected via IPv4, but has seen no traffic + +*ipv4_subnet* - keyword, number.long + +* _connectivity.ipv4_subnet_ - True if any interface is connected to the local subnet via IPv4 + +*ipv6_address* - keyword, text.text + +* _docker_container_networks.ipv6_address_ - IPv6 address +* _lxd_networks.ipv6_address_ - IPv6 address + +*ipv6_gateway* - keyword, text.text + +* _docker_container_networks.ipv6_gateway_ - IPv6 gateway + +*ipv6_internet* - keyword, number.long + +* _connectivity.ipv6_internet_ - True if any interface is connected to the Internet via IPv6 + +*ipv6_local_network* - keyword, number.long + +* _connectivity.ipv6_local_network_ - True if any interface is connected to a routed network via IPv6 + +*ipv6_no_traffic* - keyword, number.long + +* _connectivity.ipv6_no_traffic_ - True if any interface is connected via IPv6, but has seen no traffic + +*ipv6_prefix_len* - keyword, number.long + +* _docker_container_networks.ipv6_prefix_len_ - IPv6 subnet prefix length + +*ipv6_subnet* - keyword, number.long + +* _connectivity.ipv6_subnet_ - True if any interface is connected to the local subnet via IPv6 + +*irq* - keyword, number.long + +* _cpu_time.irq_ - Time spent servicing interrupts + +*is_active* - keyword, number.long + +* _running_apps.is_active_ - 1 if the application is in focus, 0 otherwise + +*is_hidden* - keyword, number.long + +* _groups.is_hidden_ - IsHidden attribute set in OpenDirectory +* _users.is_hidden_ - IsHidden attribute set in OpenDirectory + +*iso_8601* - keyword, text.text + +* _time.iso_8601_ - Current time (ISO format) in the system + +*issuer* - keyword, text.text + +* _certificates.issuer_ - Certificate issuer distinguished name + +*issuer_alternative_names* - keyword, text.text + +* _curl_certificate.issuer_alternative_names_ - Issuer Alternative Name + +*issuer_common_name* - keyword, text.text + +* _curl_certificate.issuer_common_name_ - Issuer common name + +*issuer_name* - keyword, text.text + +* _authenticode.issuer_name_ - The certificate issuer name + +*issuer_organization* - keyword, text.text + +* _curl_certificate.issuer_organization_ - Issuer organization + +*issuer_organization_unit* - keyword, text.text + +* _curl_certificate.issuer_organization_unit_ - Issuer organization unit + +*job_id* - keyword, number.long + +* _systemd_units.job_id_ - Next queued job id + +*job_path* - keyword, text.text + +* _systemd_units.job_path_ - The object path for the job + +*job_type* - keyword, text.text + +* _systemd_units.job_type_ - Job type + +*json_cmdline* - keyword, text.text + +* _bpf_process_events.json_cmdline_ - Command line arguments, in JSON format + +*keep_alive* - keyword, text.text + +* _launchd.keep_alive_ - Should the process be restarted if killed + +*kernel_memory* - keyword, number.long + +* _docker_info.kernel_memory_ - 1 if kernel memory limit support is enabled. 0 otherwise + +*kernel_version* - keyword, text.text + +* _docker_info.kernel_version_ - Kernel version +* _docker_version.kernel_version_ - Kernel version +* _kernel_panics.kernel_version_ - Version of the system kernel + +*key* - keyword, text.text + +* _authorized_keys.key_ - parsed authorized keys line +* _azure_instance_tags.key_ - The tag key +* _chrome_extensions.key_ - The extension key, from the manifest file +* _docker_container_labels.key_ - Label key +* _docker_image_labels.key_ - Label key +* _docker_network_labels.key_ - Label key +* _docker_volume_labels.key_ - Label key +* _ec2_instance_tags.key_ - Tag key +* _extended_attributes.key_ - Name of the value generated from the extended attribute +* _known_hosts.key_ - parsed authorized keys line +* _launchd_overrides.key_ - Name of the override key +* _lxd_instance_config.key_ - Configuration parameter name +* _lxd_instance_devices.key_ - Device info param name +* _mdls.key_ - Name of the metadata key +* _plist.key_ - Preference top-level key +* _power_sensors.key_ - The SMC key on OS X +* _preferences.key_ - Preference top-level key +* _process_envs.key_ - Environment variable name +* _registry.key_ - Name of the key to search for +* _selinux_settings.key_ - Key or class name. +* _smc_keys.key_ - 4-character key +* _temperature_sensors.key_ - The SMC key on OS X + +*key_algorithm* - keyword, text.text + +* _certificates.key_algorithm_ - Key algorithm used + +*key_file* - keyword, text.text + +* _authorized_keys.key_file_ - Path to the authorized_keys file +* _known_hosts.key_file_ - Path to known_hosts file + +*key_strength* - keyword, text.text + +* _certificates.key_strength_ - Key size used for RSA/DSA, or curve name + +*key_type* - keyword, text.text + +* _user_ssh_keys.key_type_ - The type of the private key. One of [rsa, dsa, dh, ec, hmac, cmac], or the empty string. + +*key_usage* - keyword, text.text + +* _certificates.key_usage_ - Certificate key usage and extended key usage +* _curl_certificate.key_usage_ - Usage of key in certificate + +*keychain_path* - keyword, text.text + +* _keychain_acls.keychain_path_ - The path of the keychain + +*keyword* - keyword, text.text + +* _portage_keywords.keyword_ - The keyword applied to the package + +*keywords* - keyword, text.text + +* _windows_eventlog.keywords_ - A bitmask of the keywords defined in the event +* _windows_events.keywords_ - A bitmask of the keywords defined in the event + +*kva_shadow_enabled* - keyword, number.long + +* _kva_speculative_info.kva_shadow_enabled_ - Kernel Virtual Address shadowing is enabled. + +*kva_shadow_inv_pcid* - keyword, number.long + +* _kva_speculative_info.kva_shadow_inv_pcid_ - Kernel VA INVPCID is enabled. + +*kva_shadow_pcid* - keyword, number.long + +* _kva_speculative_info.kva_shadow_pcid_ - Kernel VA PCID flushing optimization is enabled. + +*kva_shadow_user_global* - keyword, number.long + +* _kva_speculative_info.kva_shadow_user_global_ - User pages are marked as global. + +*label* - keyword, text.text + +* _apparmor_events.label_ - AppArmor label +* _augeas.label_ - The label of the configuration item +* _authorization_mechanisms.label_ - Label of the authorization right +* _authorizations.label_ - Item name, usually in reverse domain format +* _block_devices.label_ - Block device label string +* _device_partitions.label_ - +* _keychain_acls.label_ - An optional label tag that may be included with the keychain entry +* _keychain_items.label_ - Generic item name +* _launchd.label_ - Daemon or agent service name +* _launchd_overrides.label_ - Daemon or agent service name +* _quicklook_cache.label_ - Parsed version 'gen' field +* _sandboxes.label_ - UTI-format bundle or label ID + +*language* - keyword, text.text + +* _programs.language_ - The language of the product. + +*last_change* - keyword, number.long + +* _interface_details.last_change_ - Time of last device modification (optional) +* _shadow.last_change_ - Date of last password change (starting from UNIX epoch date) + +*last_connected* - keyword, number.long + +* _wifi_networks.last_connected_ - Last time this netword was connected to as a unix_time + +*last_executed* - keyword, number.long + +* _osquery_schedule.last_executed_ - UNIX time stamp in seconds of the last completed execution + +*last_execution_time* - keyword, number.long + +* _background_activities_moderator.last_execution_time_ - Most recent time application was executed. +* _userassist.last_execution_time_ - Most recent time application was executed. + +*last_hit_date* - keyword, number.long + +* _quicklook_cache.last_hit_date_ - Apple date format for last thumbnail cache hit + +*last_loaded* - keyword, text.text + +* _kernel_panics.last_loaded_ - Last loaded module before panic + +*last_opened_time* - keyword + +* _apps.last_opened_time_ - The time that the app was last used +* _office_mru.last_opened_time_ - Most recent opened time file was opened + +*last_run_code* - keyword, text.text + +* _scheduled_tasks.last_run_code_ - Exit status code of the last task run + +*last_run_message* - keyword, text.text + +* _scheduled_tasks.last_run_message_ - Exit status message of the last task run + +*last_run_time* - keyword, number.long + +* _prefetch.last_run_time_ - Most recent time application was run. +* _scheduled_tasks.last_run_time_ - Timestamp the task last ran + +*last_unloaded* - keyword, text.text + +* _kernel_panics.last_unloaded_ - Last unloaded module before panic + +*last_used_at* - keyword, text.text + +* _lxd_images.last_used_at_ - ISO time for the most recent use of this image in terms of container spawn + +*launch_type* - keyword, text.text + +* _xprotect_entries.launch_type_ - Launch services content type + +*layer_id* - keyword, text.text + +* _docker_image_layers.layer_id_ - Layer ID + +*layer_order* - keyword, number.long + +* _docker_image_layers.layer_order_ - Layer Order (1 = base layer) + +*level* - keyword, number.long + +* _asl.level_ - Log level number. See levels in asl.h. +* _windows_eventlog.level_ - Severity level associated with the event +* _windows_events.level_ - The severity level associated with the event + +*license* - keyword, text.text + +* _atom_packages.license_ - License for package +* _chocolatey_packages.license_ - License under which package is launched +* _npm_packages.license_ - License for package +* _python_packages.license_ - License under which package is launched + +*link* - keyword, text.text + +* _elf_sections.link_ - Link to other section + +*link_speed* - keyword, number.long + +* _interface_details.link_speed_ - Interface speed in Mb/s + +*linked_against* - keyword, text.text + +* _kernel_extensions.linked_against_ - Indexes of extensions this extension is linked against + +*load_state* - keyword, text.text + +* _systemd_units.load_state_ - Reflects whether the unit definition was properly loaded + +*local_address* - keyword, text.text + +* _bpf_socket_events.local_address_ - Local address associated with socket +* _process_open_sockets.local_address_ - Socket local address +* _socket_events.local_address_ - Local address associated with socket + +*local_hostname* - keyword, text.text + +* _ec2_instance_metadata.local_hostname_ - Private IPv4 DNS hostname of the first interface of this instance +* _system_info.local_hostname_ - Local hostname (optional) + +*local_ipv4* - keyword, text.text + +* _ec2_instance_metadata.local_ipv4_ - Private IPv4 address of the first interface of this instance + +*local_path* - keyword, text.text + +* _shortcut_files.local_path_ - Local system path to target file. + +*local_port* - keyword, number.long + +* _bpf_socket_events.local_port_ - Local network protocol port number +* _process_open_sockets.local_port_ - Socket local port +* _socket_events.local_port_ - Local network protocol port number + +*local_time* - keyword, number.long + +* _time.local_time_ - Current local UNIX time in the system + +*local_timezone* - keyword, text.text + +* _time.local_timezone_ - Current local timezone in the system + +*location* - keyword, text.text + +* _azure_instance_metadata.location_ - Azure Region the VM is running in +* _firefox_addons.location_ - Global, profile location +* _memory_arrays.location_ - Physical location of the memory array +* _package_receipts.location_ - Optional relative install path on volume + +*lock* - keyword, text.text + +* _chassis_info.lock_ - If TRUE, the frame is equipped with a lock. + +*lock_status* - keyword, number.long + +* _bitlocker_info.lock_status_ - The accessibility status of the drive from Windows. + +*locked* - keyword, number.long + +* _shared_memory.locked_ - 1 if segment is locked else 0 + +*log_file_disk_quota_mb* - keyword, number.long + +* _carbon_black_info.log_file_disk_quota_mb_ - Event file disk quota in MB + +*log_file_disk_quota_percentage* - keyword, number.long + +* _carbon_black_info.log_file_disk_quota_percentage_ - Event file disk quota in a percentage + +*logging_driver* - keyword, text.text + +* _docker_info.logging_driver_ - Logging driver + +*logging_enabled* - keyword, number.long + +* _alf.logging_enabled_ - 1 If logging mode is enabled else 0 + +*logging_option* - keyword, number.long + +* _alf.logging_option_ - Firewall logging option + +*logical_processors* - keyword, number.long + +* _cpu_info.logical_processors_ - The number of logical processors of the CPU. + +*logon_domain* - keyword, text.text + +* _logon_sessions.logon_domain_ - The name of the domain used to authenticate the owner of the logon session. + +*logon_id* - keyword, number.long + +* _logon_sessions.logon_id_ - A locally unique identifier (LUID) that identifies a logon session. + +*logon_script* - keyword, text.text + +* _logon_sessions.logon_script_ - The script used for logging on. + +*logon_server* - keyword, text.text + +* _logon_sessions.logon_server_ - The name of the server used to authenticate the owner of the logon session. + +*logon_sid* - keyword, text.text + +* _logon_sessions.logon_sid_ - The user's security identifier (SID). + +*logon_time* - keyword, number.long + +* _logon_sessions.logon_time_ - The time the session owner logged on. + +*logon_type* - keyword, text.text + +* _logon_sessions.logon_type_ - The logon method. + +*lu_wwn_device_id* - keyword, text.text + +* _smart_drive_info.lu_wwn_device_id_ - Device Identifier + +*mac* - keyword, text.text + +* _arp_cache.mac_ - MAC address of broadcasted address +* _ec2_instance_metadata.mac_ - MAC address for the first network interface of this EC2 instance +* _interface_details.mac_ - MAC of interface (optional) + +*mac_address* - keyword, text.text + +* _docker_container_networks.mac_address_ - MAC address + +*machine* - keyword, number.long + +* _elf_info.machine_ - Machine type + +*machine_name* - keyword, text.text + +* _windows_crashes.machine_name_ - Name of the machine where the crash happened + +*magic_db_files* - keyword, text.text + +* _magic.magic_db_files_ - Colon(:) separated list of files where the magic db file can be found. By default one of the following is used: /usr/share/file/magic/magic, /usr/share/misc/magic or /usr/share/misc/magic.mgc + +*maintainer* - keyword, text.text + +* _apt_sources.maintainer_ - Repository maintainer +* _deb_packages.maintainer_ - Package maintainer + +*major* - keyword, number.long + +* _os_version.major_ - Major release version + +*major_version* - keyword, number.long + +* _windows_crashes.major_version_ - Windows major version of the machine + +*managed* - keyword, number.long + +* _lxd_networks.managed_ - 1 if network created by LXD, 0 otherwise + +*manifest_hash* - keyword, text.text + +* _chrome_extensions.manifest_hash_ - The SHA256 hash of the manifest.json file + +*manifest_json* - keyword, text.text + +* _chrome_extensions.manifest_json_ - The manifest file of the extension + +*manual* - keyword, number.long + +* _managed_policies.manual_ - 1 if policy was loaded manually, otherwise 0 + +*manufacture_date* - keyword, number.long + +* _battery.manufacture_date_ - The date the battery was manufactured UNIX Epoch + +*manufacturer* - keyword, text.text + +* _battery.manufacturer_ - The battery manufacturer's name +* _chassis_info.manufacturer_ - The manufacturer of the chassis. +* _cpu_info.manufacturer_ - The manufacturer of the CPU. +* _disk_info.manufacturer_ - The manufacturer of the disk. +* _drivers.manufacturer_ - Device manufacturer +* _interface_details.manufacturer_ - Name of the network adapter's manufacturer. +* _memory_devices.manufacturer_ - Manufacturer ID string +* _video_info.manufacturer_ - The manufacturer of the gpu. + +*manufacturer_id* - keyword, number.long + +* _tpm_info.manufacturer_id_ - TPM manufacturers ID + +*manufacturer_name* - keyword, text.text + +* _tpm_info.manufacturer_name_ - TPM manufacturers name + +*manufacturer_version* - keyword, text.text + +* _tpm_info.manufacturer_version_ - TPM version + +*mask* - keyword, text.text + +* _interface_addresses.mask_ - Interface netmask +* _portage_keywords.mask_ - If the package is masked + +*match* - keyword, text.text + +* _chrome_extension_content_scripts.match_ - The pattern that the script is matched against +* _iptables.match_ - Matching rule that applies. + +*matches* - keyword, text.text + +* _yara.matches_ - List of YARA matches +* _yara_events.matches_ - List of YARA matches + +*max* - keyword, number.long + +* _fan_speed_sensors.max_ - Maximum speed +* _shadow.max_ - Maximum number of days between password changes + +*max_capacity* - keyword, number.long + +* _battery.max_capacity_ - The battery's actual capacity when it is fully charged in mAh +* _memory_arrays.max_capacity_ - Maximum capacity of array in gigabytes + +*max_clock_speed* - keyword, number.long + +* _cpu_info.max_clock_speed_ - The maximum possible frequency of the CPU. + +*max_instances* - keyword, number.long + +* _pipes.max_instances_ - The maximum number of instances creatable for this pipe + +*max_speed* - keyword, number.long + +* _memory_devices.max_speed_ - Max speed of memory device in megatransfers per second (MT/s) + +*max_voltage* - keyword, number.long + +* _memory_devices.max_voltage_ - Maximum operating voltage of device in millivolts + +*maximum_allowed* - keyword, number.long + +* _shared_resources.maximum_allowed_ - Limit on the maximum number of users allowed to use this resource concurrently. The value is only valid if the AllowMaximum property is set to FALSE. + +*md5* - keyword, text.text + +* _acpi_tables.md5_ - MD5 hash of table content +* _device_hash.md5_ - MD5 hash of provided inode data +* _file_events.md5_ - The MD5 of the file after change +* _hash.md5_ - MD5 hash of provided filesystem data +* _smbios_tables.md5_ - MD5 hash of table entry + +*md_device_name* - keyword, text.text + +* _md_drives.md_device_name_ - md device name + +*mdm_managed* - keyword, number.long + +* _system_extensions.mdm_managed_ - 1 if managed by MDM system extension payload configuration, 0 otherwise + +*mechanism* - keyword, text.text + +* _authorization_mechanisms.mechanism_ - Name of the mechanism that will be called + +*med_capability_capabilities* - keyword, number.long + +* _lldp_neighbors.med_capability_capabilities_ - Is MED capabilities enabled + +*med_capability_inventory* - keyword, number.long + +* _lldp_neighbors.med_capability_inventory_ - Is MED inventory capability enabled + +*med_capability_location* - keyword, number.long + +* _lldp_neighbors.med_capability_location_ - Is MED location capability enabled + +*med_capability_mdi_pd* - keyword, number.long + +* _lldp_neighbors.med_capability_mdi_pd_ - Is MED MDI PD capability enabled + +*med_capability_mdi_pse* - keyword, number.long + +* _lldp_neighbors.med_capability_mdi_pse_ - Is MED MDI PSE capability enabled + +*med_capability_policy* - keyword, number.long + +* _lldp_neighbors.med_capability_policy_ - Is MED policy capability enabled + +*med_device_type* - keyword, text.text + +* _lldp_neighbors.med_device_type_ - Chassis MED type + +*med_policies* - keyword, text.text + +* _lldp_neighbors.med_policies_ - Comma delimited list of MED policies + +*media_name* - keyword, text.text + +* _disk_events.media_name_ - Disk event media name string + +*mem* - keyword, number.double + +* _docker_container_processes.mem_ - Memory utilization as percentage + +*member_config_description* - keyword, text.text + +* _lxd_cluster.member_config_description_ - Config description + +*member_config_entity* - keyword, text.text + +* _lxd_cluster.member_config_entity_ - Type of configuration parameter for this node + +*member_config_key* - keyword, text.text + +* _lxd_cluster.member_config_key_ - Config key + +*member_config_name* - keyword, text.text + +* _lxd_cluster.member_config_name_ - Name of configuration parameter + +*member_config_value* - keyword, text.text + +* _lxd_cluster.member_config_value_ - Config value + +*memory* - keyword, number.long + +* _docker_info.memory_ - Total memory + +*memory_array_error_address* - keyword, text.text + +* _memory_error_info.memory_array_error_address_ - 32 bit physical address of the error based on the addressing of the bus to which the memory array is connected + +*memory_array_handle* - keyword, text.text + +* _memory_array_mapped_addresses.memory_array_handle_ - Handle of the memory array associated with this structure + +*memory_array_mapped_address_handle* - keyword, text.text + +* _memory_device_mapped_addresses.memory_array_mapped_address_handle_ - Handle of the memory array mapped address to which this device range is mapped to + +*memory_device_handle* - keyword, text.text + +* _memory_device_mapped_addresses.memory_device_handle_ - Handle of the memory device structure associated with this structure + +*memory_error_correction* - keyword, text.text + +* _memory_arrays.memory_error_correction_ - Primary hardware error correction or detection method supported + +*memory_error_info_handle* - keyword, text.text + +* _memory_arrays.memory_error_info_handle_ - Handle, or instance number, associated with any error that was detected for the array + +*memory_free* - keyword, number.long + +* _memory_info.memory_free_ - The amount of physical RAM, in bytes, left unused by the system + +*memory_limit* - keyword, number.long + +* _docker_container_stats.memory_limit_ - Memory limit +* _docker_info.memory_limit_ - 1 if memory limit support is enabled. 0 otherwise + +*memory_max_usage* - keyword, number.long + +* _docker_container_stats.memory_max_usage_ - Memory maximum usage + +*memory_total* - keyword, number.long + +* _memory_info.memory_total_ - Total amount of physical RAM, in bytes + +*memory_type* - keyword, text.text + +* _memory_devices.memory_type_ - Type of memory used + +*memory_type_details* - keyword, text.text + +* _memory_devices.memory_type_details_ - Additional details for memory device + +*memory_usage* - keyword, number.long + +* _docker_container_stats.memory_usage_ - Memory usage + +*message* - keyword, text.text + +* _apparmor_events.message_ - Raw audit message +* _asl.message_ - Message text. +* _lxd_cluster_members.message_ - Message from the node (Online/Offline) +* _selinux_events.message_ - Message +* _syslog_events.message_ - The syslog message +* _user_events.message_ - Message from the event + +*metadata_endpoint* - keyword, text.text + +* _ycloud_instance_metadata.metadata_endpoint_ - Endpoint used to fetch VM metadata + +*method* - keyword, text.text + +* _curl.method_ - The HTTP method for the request + +*metric* - keyword, number.long + +* _interface_details.metric_ - Metric based on the speed of the interface +* _routes.metric_ - Cost of route. Lowest is preferred + +*metric_name* - keyword, text.text + +* _prometheus_metrics.metric_name_ - Name of collected Prometheus metric + +*metric_value* - keyword, number.double + +* _prometheus_metrics.metric_value_ - Value of collected Prometheus metric + +*mft_entry* - keyword, number.long + +* _shellbags.mft_entry_ - Directory master file table entry. +* _shortcut_files.mft_entry_ - Target mft entry. + +*mft_sequence* - keyword, number.long + +* _shellbags.mft_sequence_ - Directory master file table sequence. +* _shortcut_files.mft_sequence_ - Target mft sequence. + +*mime_encoding* - keyword, text.text + +* _magic.mime_encoding_ - MIME encoding data from libmagic + +*mime_type* - keyword, text.text + +* _magic.mime_type_ - MIME type data from libmagic + +*min* - keyword, number.long + +* _fan_speed_sensors.min_ - Minimum speed +* _shadow.min_ - Minimal number of days between password changes + +*min_api_version* - keyword, text.text + +* _docker_version.min_api_version_ - Minimum API version supported + +*min_version* - keyword, text.text + +* _xprotect_meta.min_version_ - The minimum allowed plugin version. + +*min_voltage* - keyword, number.long + +* _memory_devices.min_voltage_ - Minimum operating voltage of device in millivolts + +*minimum_system_version* - keyword, text.text + +* _apps.minimum_system_version_ - Minimum version of OS X required for the app to run + +*minor* - keyword, number.long + +* _os_version.minor_ - Minor release version + +*minor_version* - keyword, number.long + +* _windows_crashes.minor_version_ - Windows minor version of the machine + +*minute* - keyword, text.text + +* _crontab.minute_ - The exact minute for the job + +*minutes* - keyword, number.long + +* _time.minutes_ - Current minutes in the system +* _uptime.minutes_ - Minutes of uptime + +*minutes_to_full_charge* - keyword, number.long + +* _battery.minutes_to_full_charge_ - The number of minutes until the battery is fully charged. This value is -1 if this time is still being calculated + +*minutes_until_empty* - keyword, number.long + +* _battery.minutes_until_empty_ - The number of minutes until the battery is fully depleted. This value is -1 if this time is still being calculated + +*mnt_namespace* - keyword, text.text + +* _docker_containers.mnt_namespace_ - Mount namespace +* _process_namespaces.mnt_namespace_ - mnt namespace inode + +*mode* - keyword, text.text + +* _apparmor_profiles.mode_ - How the policy is applied. +* _device_file.mode_ - Permission bits +* _docker_container_mounts.mode_ - Mount options (rw, ro) +* _file.mode_ - Permission bits +* _file_events.mode_ - Permission bits +* _package_bom.mode_ - Expected permissions +* _process_events.mode_ - File mode permissions +* _process_open_pipes.mode_ - Pipe open mode (r/w) +* _rpm_package_files.mode_ - File permissions mode from info DB +* _wifi_status.mode_ - The current operating mode for the Wi-Fi interface + +*model* - keyword, text.text + +* _battery.model_ - The battery's model number +* _block_devices.model_ - Block device model string identifier +* _chassis_info.model_ - The model of the chassis. +* _cpu_info.model_ - The model of the CPU. +* _hardware_events.model_ - Hardware device model +* _pci_devices.model_ - PCI Device model +* _usb_devices.model_ - USB Device model string +* _video_info.model_ - The model of the gpu. + +*model_family* - keyword, text.text + +* _smart_drive_info.model_family_ - Drive model family + +*model_id* - keyword, text.text + +* _hardware_events.model_id_ - Hex encoded Hardware model identifier +* _pci_devices.model_id_ - Hex encoded PCI Device model identifier +* _usb_devices.model_id_ - Hex encoded USB Device model identifier + +*modified* - keyword, text.text + +* _authorizations.modified_ - Label top-level key +* _keychain_items.modified_ - Date of last modification + +*modified_time* - keyword, number.long + +* _package_bom.modified_time_ - Timestamp the file was installed +* _shellbags.modified_time_ - Directory Modified time. +* _shimcache.modified_time_ - File Modified time. + +*module* - keyword, text.text + +* _windows_crashes.module_ - Path of the crashed module within the process + +*module_backtrace* - keyword, text.text + +* _kernel_panics.module_backtrace_ - Modules appearing in the crashed module's backtrace + +*module_path* - keyword, text.text + +* _services.module_path_ - Path to ServiceDll + +*month* - keyword, text.text + +* _crontab.month_ - The month of the year for the job +* _time.month_ - Current month in the system + +*mount_namespace_id* - keyword, text.text + +* _deb_packages.mount_namespace_id_ - Mount namespace id +* _file.mount_namespace_id_ - Mount namespace id +* _hash.mount_namespace_id_ - Mount namespace id +* _npm_packages.mount_namespace_id_ - Mount namespace id +* _os_version.mount_namespace_id_ - Mount namespace id +* _rpm_packages.mount_namespace_id_ - Mount namespace id + +*mount_point* - keyword, text.text + +* _docker_volumes.mount_point_ - Mount point + +*mountable* - keyword, number.long + +* _disk_events.mountable_ - 1 if mountable, 0 if not + +*msize* - keyword, number.long + +* _elf_segments.msize_ - Segment offset in memory + +*mtime* - keyword + +* _device_file.mtime_ - Last modification time +* _file.mtime_ - Last modification time +* _file_events.mtime_ - Last modification time +* _gatekeeper_approved_apps.mtime_ - Last modification time +* _process_events.mtime_ - File modification in UNIX time +* _quicklook_cache.mtime_ - Parsed version date field +* _registry.mtime_ - timestamp of the most recent registry write + +*mtu* - keyword, number.long + +* _interface_details.mtu_ - Network MTU +* _lxd_networks.mtu_ - MTU size +* _routes.mtu_ - Maximum Transmission Unit for the route + +*name* - keyword, text.text + +* _acpi_tables.name_ - ACPI table name +* _ad_config.name_ - The OS X-specific configuration name +* _apparmor_events.name_ - Process name +* _apparmor_profiles.name_ - Policy name. +* _apps.name_ - Name of the Name.app folder +* _apt_sources.name_ - Repository name +* _atom_packages.name_ - Package display name +* _autoexec.name_ - Name of the program +* _azure_instance_metadata.name_ - Name of the VM +* _block_devices.name_ - Block device name +* _browser_plugins.name_ - Plugin display name +* _chocolatey_packages.name_ - Package display name +* _chrome_extensions.name_ - Extension display name +* _cups_destinations.name_ - Name of the printer +* _deb_packages.name_ - Package name +* _disk_encryption.name_ - Disk name +* _disk_events.name_ - Disk event name +* _disk_info.name_ - The label of the disk object. +* _dns_cache.name_ - DNS record name +* _docker_container_mounts.name_ - Optional mount name +* _docker_container_networks.name_ - Network name +* _docker_container_processes.name_ - The process path or shorthand argv[0] +* _docker_container_stats.name_ - Container name +* _docker_containers.name_ - Container name +* _docker_info.name_ - Name of the docker host +* _docker_networks.name_ - Network name +* _docker_volume_labels.name_ - Volume name +* _docker_volumes.name_ - Volume name +* _elf_sections.name_ - Section name +* _elf_segments.name_ - Segment type/name +* _elf_symbols.name_ - Symbol name +* _etc_protocols.name_ - Protocol name +* _etc_services.name_ - Service name +* _example.name_ - Description for name column +* _fan_speed_sensors.name_ - Fan name +* _fbsd_kmods.name_ - Module name +* _firefox_addons.name_ - Addon display name +* _homebrew_packages.name_ - Package name +* _ie_extensions.name_ - Extension display name +* _iokit_devicetree.name_ - Device node name +* _iokit_registry.name_ - Default name of the node +* _kernel_extensions.name_ - Extension label +* _kernel_modules.name_ - Module name +* _kernel_panics.name_ - Process name corresponding to crashed thread +* _launchd.name_ - File name of plist (used by launchd) +* _lxd_certificates.name_ - Name of the certificate +* _lxd_instance_config.name_ - Instance name +* _lxd_instance_devices.name_ - Instance name +* _lxd_instances.name_ - Instance name +* _lxd_networks.name_ - Name of the network +* _lxd_storage_pools.name_ - Name of the storage pool +* _managed_policies.name_ - Policy key name +* _md_personalities.name_ - Name of personality supported by kernel +* _memory_map.name_ - Region name +* _npm_packages.name_ - Package display name +* _ntdomains.name_ - The label by which the object is known. +* _nvram.name_ - Variable name +* _os_version.name_ - Distribution or product name +* _osquery_events.name_ - Event publisher or subscriber name +* _osquery_extensions.name_ - Extension's name +* _osquery_flags.name_ - Flag name +* _osquery_packs.name_ - The given name for this query pack +* _osquery_registry.name_ - Name of the plugin item +* _osquery_schedule.name_ - The given name for this query +* _package_install_history.name_ - Package display name +* _physical_disk_performance.name_ - Name of the physical disk +* _pipes.name_ - Name of the pipe +* _pkg_packages.name_ - Package name +* _power_sensors.name_ - Name of power source +* _processes.name_ - The process path or shorthand argv[0] +* _programs.name_ - Commonly used product name. +* _python_packages.name_ - Package display name +* _registry.name_ - Name of the registry value entry +* _rpm_packages.name_ - RPM package name +* _safari_extensions.name_ - Extension display name +* _scheduled_tasks.name_ - Name of the scheduled task +* _services.name_ - Service name +* _shared_folders.name_ - The shared name of the folder as it appears to other users +* _shared_resources.name_ - Alias given to a path set up as a share on a computer system running Windows. +* _startup_items.name_ - Name of startup item +* _system_controls.name_ - Full sysctl MIB name +* _temperature_sensors.name_ - Name of temperature source +* _windows_optional_features.name_ - Name of the feature +* _windows_security_products.name_ - Name of product +* _wmi_bios_info.name_ - Name of the Bios setting +* _wmi_cli_event_consumers.name_ - Unique name of a consumer. +* _wmi_event_filters.name_ - Unique identifier of an event filter. +* _wmi_script_event_consumers.name_ - Unique identifier for the event consumer. +* _xprotect_entries.name_ - Description of XProtected malware +* _xprotect_reports.name_ - Description of XProtected malware +* _ycloud_instance_metadata.name_ - Name of the VM +* _yum_sources.name_ - Repository name + +*name_constraints* - keyword, text.text + +* _curl_certificate.name_constraints_ - Name Constraints + +*namespace* - keyword, text.text + +* _apparmor_events.namespace_ - AppArmor namespace + +*native* - keyword, number.long + +* _browser_plugins.native_ - Plugin requires native execution +* _firefox_addons.native_ - 1 If the addon includes binary components else 0 + +*net_namespace* - keyword, text.text + +* _docker_containers.net_namespace_ - Network namespace +* _listening_ports.net_namespace_ - The inode number of the network namespace +* _process_namespaces.net_namespace_ - net namespace inode +* _process_open_sockets.net_namespace_ - The inode number of the network namespace + +*netmask* - keyword, text.text + +* _dns_resolvers.netmask_ - Address (sortlist) netmask length +* _routes.netmask_ - Netmask length + +*network_id* - keyword, text.text + +* _docker_container_networks.network_id_ - Network ID + +*network_name* - keyword, text.text + +* _wifi_networks.network_name_ - Name of the network +* _wifi_status.network_name_ - Name of the network +* _wifi_survey.network_name_ - Name of the network + +*network_rx_bytes* - keyword, number.long + +* _docker_container_stats.network_rx_bytes_ - Total network bytes read + +*network_tx_bytes* - keyword, number.long + +* _docker_container_stats.network_tx_bytes_ - Total network bytes transmitted + +*next_run_time* - keyword, number.long + +* _scheduled_tasks.next_run_time_ - Timestamp the task is scheduled to run next + +*nice* - keyword, number.long + +* _cpu_time.nice_ - Time spent in user mode with low priority (nice) +* _docker_container_processes.nice_ - Process nice level (-20 to 20, default 0) +* _processes.nice_ - Process nice level (-20 to 20, default 0) + +*no_proxy* - keyword, text.text + +* _docker_info.no_proxy_ - Comma-separated list of domain extensions proxy should not be used for + +*node* - keyword, text.text + +* _augeas.node_ - The node path of the configuration item + +*node_ref_number* - keyword, text.text + +* _ntfs_journal_events.node_ref_number_ - The ordinal that associates a journal record with a filename + +*noise* - keyword, number.long + +* _wifi_status.noise_ - The current noise measurement (dBm) +* _wifi_survey.noise_ - The current noise measurement (dBm) + +*not_valid_after* - keyword, text.text + +* _certificates.not_valid_after_ - Certificate expiration data + +*not_valid_before* - keyword, text.text + +* _certificates.not_valid_before_ - Lower bound of valid date + +*nr_raid_disks* - keyword, number.long + +* _md_devices.nr_raid_disks_ - Number of partitions or disk devices to comprise the array + +*ntime* - keyword, text.text + +* _bpf_process_events.ntime_ - The nsecs uptime timestamp as obtained from BPF +* _bpf_socket_events.ntime_ - The nsecs uptime timestamp as obtained from BPF + +*num_procs* - keyword, number.long + +* _docker_container_stats.num_procs_ - Number of processors + +*number* - keyword, number.long + +* _etc_protocols.number_ - Protocol number +* _oem_strings.number_ - The string index of the structure +* _smbios_tables.number_ - Table entry number + +*number_memory_devices* - keyword, number.long + +* _memory_arrays.number_memory_devices_ - Number of memory devices on array + +*number_of_cores* - keyword, text.text + +* _cpu_info.number_of_cores_ - The number of cores of the CPU. + +*object_name* - keyword, text.text + +* _winbaseobj.object_name_ - Object Name + +*object_path* - keyword, text.text + +* _systemd_units.object_path_ - The object path for this unit + +*object_type* - keyword, text.text + +* _winbaseobj.object_type_ - Object Type + +*obytes* - keyword, number.long + +* _interface_details.obytes_ - Output bytes + +*odrops* - keyword, number.long + +* _interface_details.odrops_ - Output drops + +*oerrors* - keyword, number.long + +* _interface_details.oerrors_ - Output errors + +*offer* - keyword, text.text + +* _azure_instance_metadata.offer_ - Offer information for the VM image (Azure image gallery VMs only) + +*offset* - keyword, number.long + +* _device_partitions.offset_ - +* _elf_sections.offset_ - Offset of section in file +* _elf_segments.offset_ - Segment offset in file +* _elf_symbols.offset_ - Section table index +* _process_memory_map.offset_ - Offset into mapped path + +*oid* - keyword, text.text + +* _system_controls.oid_ - Control MIB + +*old_path* - keyword, text.text + +* _ntfs_journal_events.old_path_ - Old path (renames only) + +*on_demand* - keyword, text.text + +* _launchd.on_demand_ - Deprecated key, replaced by keep_alive + +*on_disk* - keyword, number.long + +* _processes.on_disk_ - The process path exists yes=1, no=0, unknown=-1 + +*online_cpus* - keyword, number.long + +* _docker_container_stats.online_cpus_ - Online CPUs + +*oom_kill_disable* - keyword, number.long + +* _docker_info.oom_kill_disable_ - 1 if Out-of-memory kill is disabled. 0 otherwise + +*opackets* - keyword, number.long + +* _interface_details.opackets_ - Output packets + +*opaque_version* - keyword, text.text + +* _gatekeeper.opaque_version_ - Version of Gatekeeper's gkopaque.bundle + +*operation* - keyword, text.text + +* _apparmor_events.operation_ - Permission requested by the process +* _process_file_events.operation_ - Operation type + +*option* - keyword, text.text + +* _ad_config.option_ - Canonical name of option +* _ssh_configs.option_ - The option and value + +*option_name* - keyword, text.text + +* _cups_destinations.option_name_ - Option name + +*option_value* - keyword, text.text + +* _cups_destinations.option_value_ - Option value + +*optional* - keyword, number.long + +* _xprotect_entries.optional_ - Match any of the identities/patterns for this XProtect name + +*optional_permissions* - keyword, text.text + +* _chrome_extensions.optional_permissions_ - The permissions optionally required by the extensions + +*optional_permissions_json* - keyword, text.text + +* _chrome_extensions.optional_permissions_json_ - The JSON-encoded permissions optionally required by the extensions + +*options* - keyword + +* _dns_resolvers.options_ - Resolver options +* _nfs_shares.options_ - Options string set on the export share + +*organization* - keyword, text.text + +* _curl_certificate.organization_ - Organization issued to + +*organization_unit* - keyword, text.text + +* _curl_certificate.organization_unit_ - Organization unit issued to + +*original_parent* - keyword, number.long + +* _es_process_events.original_parent_ - Original parent process ID in case of reparenting + +*original_program_name* - keyword, text.text + +* _authenticode.original_program_name_ - The original program name that the publisher has signed + +*os* - keyword, text.text + +* _docker_info.os_ - Operating system +* _docker_version.os_ - Operating system +* _lxd_images.os_ - OS on which image is based +* _lxd_instances.os_ - The OS of this instance + +*os_type* - keyword, text.text + +* _azure_instance_metadata.os_type_ - Linux or Windows +* _docker_info.os_type_ - Operating system type + +*os_version* - keyword, text.text + +* _kernel_panics.os_version_ - Version of the operating system + +*other* - keyword, text.text + +* _md_devices.other_ - Other information associated with array from /proc/mdstat + +*other_run_times* - keyword, text.text + +* _prefetch.other_run_times_ - Other execution times in prefetch file. + +*ouid* - keyword, number.long + +* _apparmor_events.ouid_ - Object owner's user ID + +*outiface* - keyword, text.text + +* _iptables.outiface_ - Output interface for the rule. + +*outiface_mask* - keyword, text.text + +* _iptables.outiface_mask_ - Output interface mask for the rule. + +*output_bit* - keyword, number.long + +* _cpuid.output_bit_ - Bit in register value for feature value + +*output_register* - keyword, text.text + +* _cpuid.output_register_ - Register used to for feature value + +*output_size* - keyword, number.long + +* _osquery_schedule.output_size_ - Total number of bytes generated by the query + +*overflows* - keyword, text.text + +* _process_events.overflows_ - List of structures that overflowed + +*owned* - keyword, number.long + +* _tpm_info.owned_ - TPM is ownned + +*owner_gid* - keyword, number.long + +* _process_events.owner_gid_ - File owner group ID + +*owner_uid* - keyword, number.long + +* _process_events.owner_uid_ - File owner user ID +* _shared_memory.owner_uid_ - User ID of owning process + +*owner_uuid* - keyword, number.long + +* _osquery_registry.owner_uuid_ - Extension route UUID (0 for core) + +*package* - keyword, text.text + +* _portage_keywords.package_ - Package name +* _portage_packages.package_ - Package name +* _portage_use.package_ - Package name +* _rpm_package_files.package_ - RPM package name + +*package_filename* - keyword, text.text + +* _package_receipts.package_filename_ - Filename of original .pkg file + +*package_group* - keyword, text.text + +* _rpm_packages.package_group_ - Package group + +*package_id* - keyword, text.text + +* _package_install_history.package_id_ - Label packageIdentifiers +* _package_receipts.package_id_ - Package domain identifier + +*packet_device_type* - keyword, text.text + +* _smart_drive_info.packet_device_type_ - Packet device type + +*packets* - keyword, number.long + +* _iptables.packets_ - Number of matching packets for this rule. + +*packets_received* - keyword, number.long + +* _lxd_networks.packets_received_ - Number of packets received on this network + +*packets_sent* - keyword, number.long + +* _lxd_networks.packets_sent_ - Number of packets sent on this network + +*page_ins* - keyword, number.long + +* _virtual_memory_info.page_ins_ - The total number of requests for pages from a pager. + +*page_outs* - keyword, number.long + +* _virtual_memory_info.page_outs_ - Total number of pages paged out. + +*parent* - keyword + +* _apparmor_events.parent_ - Parent process PID +* _block_devices.parent_ - Block device parent name +* _bpf_process_events.parent_ - Parent process ID +* _bpf_socket_events.parent_ - Parent process ID +* _crashes.parent_ - Parent PID of the crashed process +* _docker_container_processes.parent_ - Process parent's PID +* _es_process_events.parent_ - Parent process ID +* _iokit_devicetree.parent_ - Parent device registry ID +* _iokit_registry.parent_ - Parent registry ID +* _process_events.parent_ - Process parent's PID, or -1 if cannot be determined. +* _processes.parent_ - Process parent's PID + +*parent_ref_number* - keyword, text.text + +* _ntfs_journal_events.parent_ref_number_ - The ordinal that associates a journal record with a filename's parent directory + +*part_number* - keyword, text.text + +* _memory_devices.part_number_ - Manufacturer specific serial number of memory device + +*partial* - keyword + +* _ntfs_journal_events.partial_ - Set to 1 if either path or old_path only contains the file or folder name +* _process_file_events.partial_ - True if this is a partial event (i.e.: this process existed before we started osquery) + +*partition* - keyword, text.text + +* _device_file.partition_ - A partition number +* _device_hash.partition_ - A partition number +* _device_partitions.partition_ - A partition number or description + +*partition_row_position* - keyword, number.long + +* _memory_device_mapped_addresses.partition_row_position_ - Identifies the position of the referenced memory device in a row of the address partition + +*partition_width* - keyword, number.long + +* _memory_array_mapped_addresses.partition_width_ - Number of memory devices that form a single row of memory for the address partition of this structure + +*partitions* - keyword, number.long + +* _disk_info.partitions_ - Number of detected partitions on disk. + +*partner_fd* - keyword, number.long + +* _process_open_pipes.partner_fd_ - File descriptor of shared pipe at partner's end + +*partner_mode* - keyword, text.text + +* _process_open_pipes.partner_mode_ - Mode of shared pipe at partner's end + +*partner_pid* - keyword, number.long + +* _process_open_pipes.partner_pid_ - Process ID of partner process sharing a particular pipe + +*passpoint* - keyword, number.long + +* _wifi_networks.passpoint_ - 1 if Passpoint is supported, 0 otherwise + +*password_last_set_time* - keyword, number.double + +* _account_policy_data.password_last_set_time_ - The time the password was last changed + +*password_status* - keyword, text.text + +* _shadow.password_status_ - Password status + +*patch* - keyword, number.long + +* _os_version.patch_ - Optional patch release + +*path* - keyword, text.text + +* _alf_exceptions.path_ - Path to the executable that is excepted +* _apparmor_profiles.path_ - Unique, aa-status compatible, policy identifier. +* _appcompat_shims.path_ - This is the path to the SDB database. +* _apps.path_ - Absolute and full Name.app path +* _atom_packages.path_ - Package's package.json path +* _augeas.path_ - The path to the configuration file +* _authenticode.path_ - Must provide a path or directory +* _autoexec.path_ - Path to the executable +* _background_activities_moderator.path_ - Application file path. +* _bpf_process_events.path_ - Binary path +* _bpf_socket_events.path_ - Path of executed file +* _browser_plugins.path_ - Path to plugin bundle +* _carves.path_ - The path of the requested carve +* _certificates.path_ - Path to Keychain or PEM bundle +* _chocolatey_packages.path_ - Path at which this package resides +* _chrome_extension_content_scripts.path_ - Path to extension folder +* _chrome_extensions.path_ - Path to extension folder +* _crashes.path_ - Path to the crashed process +* _crontab.path_ - File parsed +* _device_file.path_ - A logical path within the device node +* _disk_events.path_ - Path of the DMG file accessed +* _docker_container_fs_changes.path_ - FIle or directory path relative to rootfs +* _docker_containers.path_ - Container path +* _elf_dynamic.path_ - Path to ELF file +* _elf_info.path_ - Path to ELF file +* _elf_sections.path_ - Path to ELF file +* _elf_segments.path_ - Path to ELF file +* _elf_symbols.path_ - Path to ELF file +* _es_process_events.path_ - Path of executed file +* _example.path_ - Path of example +* _extended_attributes.path_ - Absolute file path +* _file.path_ - Absolute file path +* _firefox_addons.path_ - Path to plugin bundle +* _gatekeeper_approved_apps.path_ - Path of executable allowed to run +* _hardware_events.path_ - Local device path assigned (optional) +* _hash.path_ - Must provide a path or directory +* _homebrew_packages.path_ - Package install path +* _ie_extensions.path_ - Path to executable +* _kernel_extensions.path_ - Optional path to extension bundle +* _kernel_info.path_ - Kernel path +* _kernel_panics.path_ - Location of log file +* _keychain_acls.path_ - The path of the authorized application +* _keychain_items.path_ - Path to keychain containing item +* _launchd.path_ - Path to daemon or agent plist +* _launchd_overrides.path_ - Path to daemon or agent plist +* _listening_ports.path_ - Path for UNIX domain sockets +* _magic.path_ - Absolute path to target file +* _mdfind.path_ - Path of the file returned from spotlight +* _mdls.path_ - Path of the file +* _mounts.path_ - Mounted device path +* _npm_packages.path_ - Module's package.json path +* _ntfs_acl_permissions.path_ - Path to the file or directory. +* _ntfs_journal_events.path_ - Path +* _office_mru.path_ - File path +* _osquery_extensions.path_ - Path of the extension's Thrift connection or library path +* _package_bom.path_ - Path of package bom +* _package_receipts.path_ - Path of receipt plist +* _plist.path_ - (required) read preferences from a plist +* _prefetch.path_ - Prefetch file path. +* _process_events.path_ - Path of executed file +* _process_file_events.path_ - The path associated with the event +* _process_memory_map.path_ - Path to mapped file or mapped type +* _process_open_files.path_ - Filesystem path of descriptor +* _process_open_sockets.path_ - For UNIX sockets (family=AF_UNIX), the domain path +* _processes.path_ - Path to executed binary +* _python_packages.path_ - Path at which this module resides +* _quicklook_cache.path_ - Path of file +* _registry.path_ - Full path to the value +* _rpm_package_files.path_ - File path within the package +* _safari_extensions.path_ - Path to extension XAR bundle +* _sandboxes.path_ - Path to sandbox container directory +* _scheduled_tasks.path_ - Path to the executable to be run +* _services.path_ - Path to Service Executable +* _shared_folders.path_ - Absolute path of shared folder on the local system +* _shared_resources.path_ - Local path of the Windows share. +* _shellbags.path_ - Directory name. +* _shimcache.path_ - This is the path to the executed file. +* _shortcut_files.path_ - Directory name. +* _signature.path_ - Must provide a path or directory +* _socket_events.path_ - Path of executed file +* _startup_items.path_ - Path of startup item +* _suid_bin.path_ - Binary path +* _system_extensions.path_ - Original path of system extension +* _user_events.path_ - Supplied path from event +* _user_ssh_keys.path_ - Path to key file +* _userassist.path_ - Application file path. +* _windows_crashes.path_ - Path of the executable file for the crashed process +* _yara.path_ - The path scanned + +*pci_class* - keyword, text.text + +* _pci_devices.pci_class_ - PCI Device class + +*pci_class_id* - keyword, text.text + +* _pci_devices.pci_class_id_ - PCI Device class ID in hex format + +*pci_slot* - keyword, text.text + +* _interface_details.pci_slot_ - PCI slot number +* _pci_devices.pci_slot_ - PCI Device used slot + +*pci_subclass* - keyword, text.text + +* _pci_devices.pci_subclass_ - PCI Device subclass + +*pci_subclass_id* - keyword, text.text + +* _pci_devices.pci_subclass_id_ - PCI Device subclass in hex format + +*pem* - keyword, text.text + +* _curl_certificate.pem_ - Certificate PEM format + +*percent_disk_read_time* - keyword, number.long + +* _physical_disk_performance.percent_disk_read_time_ - Percentage of elapsed time that the selected disk drive is busy servicing read requests + +*percent_disk_time* - keyword, number.long + +* _physical_disk_performance.percent_disk_time_ - Percentage of elapsed time that the selected disk drive is busy servicing read or write requests + +*percent_disk_write_time* - keyword, number.long + +* _physical_disk_performance.percent_disk_write_time_ - Percentage of elapsed time that the selected disk drive is busy servicing write requests + +*percent_idle_time* - keyword, number.long + +* _physical_disk_performance.percent_idle_time_ - Percentage of time during the sample interval that the disk was idle + +*percent_processor_time* - keyword, number.long + +* _processes.percent_processor_time_ - Returns elapsed time that all of the threads of this process used the processor to execute instructions in 100 nanoseconds ticks. + +*percent_remaining* - keyword, number.long + +* _battery.percent_remaining_ - The percentage of battery remaining before it is drained + +*percentage_encrypted* - keyword, number.long + +* _bitlocker_info.percentage_encrypted_ - The percentage of the drive that is encrypted. + +*perf_ctl* - keyword, number.long + +* _msr.perf_ctl_ - Performance setting for the processor. + +*perf_status* - keyword, number.long + +* _msr.perf_status_ - Performance status for the processor. + +*period* - keyword, text.text + +* _load_average.period_ - Period over which the average is calculated. + +*permanent* - keyword, text.text + +* _arp_cache.permanent_ - 1 for true, 0 for false + +*permissions* - keyword, text.text + +* _chrome_extensions.permissions_ - The permissions required by the extension +* _process_memory_map.permissions_ - r=read, w=write, x=execute, p=private (cow) +* _shared_memory.permissions_ - Memory segment permissions +* _suid_bin.permissions_ - Binary permissions + +*permissions_json* - keyword, text.text + +* _chrome_extensions.permissions_json_ - The JSON-encoded permissions required by the extension + +*persistent* - keyword, number.long + +* _chrome_extensions.persistent_ - 1 If extension is persistent across all tabs else 0 + +*persistent_volume_id* - keyword, text.text + +* _bitlocker_info.persistent_volume_id_ - Persistent ID of the drive. + +*pgroup* - keyword, number.long + +* _docker_container_processes.pgroup_ - Process group +* _processes.pgroup_ - Process group + +*physical_adapter* - keyword, number.long + +* _interface_details.physical_adapter_ - Indicates whether the adapter is a physical or a logical adapter. + +*physical_memory* - keyword, number.long + +* _system_info.physical_memory_ - Total physical memory in bytes + +*physical_presence_version* - keyword, text.text + +* _tpm_info.physical_presence_version_ - Version of the Physical Presence Interface + +*pid* - keyword, number.long + +* _apparmor_events.pid_ - Process ID +* _asl.pid_ - Sending process ID encoded as a string. Set automatically. +* _bpf_process_events.pid_ - Process ID +* _bpf_socket_events.pid_ - Process ID +* _crashes.pid_ - Process (or thread) ID of the crashed process +* _docker_container_processes.pid_ - Process ID +* _docker_containers.pid_ - Identifier of the initial process +* _es_process_events.pid_ - Process (or thread) ID +* _last.pid_ - Process (or thread) ID +* _listening_ports.pid_ - Process (or thread) ID +* _logged_in_users.pid_ - Process (or thread) ID +* _lxd_instances.pid_ - Instance's process ID +* _osquery_info.pid_ - Process (or thread/handle) ID +* _pipes.pid_ - Process ID of the process to which the pipe belongs +* _process_envs.pid_ - Process (or thread) ID +* _process_events.pid_ - Process (or thread) ID +* _process_file_events.pid_ - Process ID +* _process_memory_map.pid_ - Process (or thread) ID +* _process_namespaces.pid_ - Process (or thread) ID +* _process_open_files.pid_ - Process (or thread) ID +* _process_open_pipes.pid_ - Process ID +* _process_open_sockets.pid_ - Process (or thread) ID +* _processes.pid_ - Process (or thread) ID +* _running_apps.pid_ - The pid of the application +* _seccomp_events.pid_ - Process ID +* _services.pid_ - the Process ID of the service +* _shared_memory.pid_ - Process ID to last use the segment +* _socket_events.pid_ - Process (or thread) ID +* _user_events.pid_ - Process (or thread) ID +* _windows_crashes.pid_ - Process ID of the crashed process +* _windows_eventlog.pid_ - Process ID which emitted the event record + +*pid_namespace* - keyword, text.text + +* _docker_containers.pid_namespace_ - PID namespace +* _process_namespaces.pid_namespace_ - pid namespace inode + +*pid_with_namespace* - keyword, number.long + +* _apt_sources.pid_with_namespace_ - Pids that contain a namespace +* _authorized_keys.pid_with_namespace_ - Pids that contain a namespace +* _crontab.pid_with_namespace_ - Pids that contain a namespace +* _deb_packages.pid_with_namespace_ - Pids that contain a namespace +* _dns_resolvers.pid_with_namespace_ - Pids that contain a namespace +* _etc_hosts.pid_with_namespace_ - Pids that contain a namespace +* _file.pid_with_namespace_ - Pids that contain a namespace +* _groups.pid_with_namespace_ - Pids that contain a namespace +* _hash.pid_with_namespace_ - Pids that contain a namespace +* _npm_packages.pid_with_namespace_ - Pids that contain a namespace +* _os_version.pid_with_namespace_ - Pids that contain a namespace +* _python_packages.pid_with_namespace_ - Pids that contain a namespace +* _rpm_packages.pid_with_namespace_ - Pids that contain a namespace +* _suid_bin.pid_with_namespace_ - Pids that contain a namespace +* _user_ssh_keys.pid_with_namespace_ - Pids that contain a namespace +* _users.pid_with_namespace_ - Pids that contain a namespace +* _yum_sources.pid_with_namespace_ - Pids that contain a namespace + +*pids* - keyword + +* _docker_container_stats.pids_ - Number of processes +* _lldp_neighbors.pids_ - Comma delimited list of PIDs + +*placement_group_id* - keyword, text.text + +* _azure_instance_metadata.placement_group_id_ - Placement group for the VM scale set + +*platform* - keyword, text.text + +* _os_version.platform_ - OS Platform or ID +* _osquery_packs.platform_ - Platforms this query is supported on + +*platform_binary* - keyword, number.long + +* _es_process_events.platform_binary_ - Indicates if the binary is Apple signed binary (1) or not (0) + +*platform_fault_domain* - keyword, text.text + +* _azure_instance_metadata.platform_fault_domain_ - Fault domain the VM is running in + +*platform_info* - keyword, number.long + +* _msr.platform_info_ - Platform information. + +*platform_like* - keyword, text.text + +* _os_version.platform_like_ - Closely related platforms + +*platform_mask* - keyword, number.long + +* _osquery_info.platform_mask_ - The osquery platform bitmask + +*platform_update_domain* - keyword, text.text + +* _azure_instance_metadata.platform_update_domain_ - Update domain the VM is running in + +*plugin* - keyword, text.text + +* _authorization_mechanisms.plugin_ - Authorization plugin name + +*pnp_device_id* - keyword, text.text + +* _disk_info.pnp_device_id_ - The unique identifier of the drive on the system. + +*point_to_point* - keyword, text.text + +* _interface_addresses.point_to_point_ - PtP address for the interface + +*points* - keyword, number.long + +* _example.points_ - This is a signed SQLite int column + +*policies* - keyword, text.text + +* _curl_certificate.policies_ - Certificate Policies + +*policy* - keyword, text.text + +* _iptables.policy_ - Policy that applies for this rule. + +*policy_constraints* - keyword, text.text + +* _curl_certificate.policy_constraints_ - Policy Constraints + +*policy_mappings* - keyword, text.text + +* _curl_certificate.policy_mappings_ - Policy Mappings + +*port* - keyword, number.long + +* _docker_container_ports.port_ - Port inside the container +* _etc_services.port_ - Service port number +* _listening_ports.port_ - Transport layer port + +*port_aggregation_id* - keyword, text.text + +* _lldp_neighbors.port_aggregation_id_ - Port aggregation ID + +*port_autoneg_1000baset_fd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_1000baset_fd_enabled_ - 1000Base-T FD auto negotiation enabled + +*port_autoneg_1000baset_hd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_1000baset_hd_enabled_ - 1000Base-T HD auto negotiation enabled + +*port_autoneg_1000basex_fd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_1000basex_fd_enabled_ - 1000Base-X FD auto negotiation enabled + +*port_autoneg_1000basex_hd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_1000basex_hd_enabled_ - 1000Base-X HD auto negotiation enabled + +*port_autoneg_100baset2_fd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_100baset2_fd_enabled_ - 100Base-T2 FD auto negotiation enabled + +*port_autoneg_100baset2_hd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_100baset2_hd_enabled_ - 100Base-T2 HD auto negotiation enabled + +*port_autoneg_100baset4_fd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_100baset4_fd_enabled_ - 100Base-T4 FD auto negotiation enabled + +*port_autoneg_100baset4_hd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_100baset4_hd_enabled_ - 100Base-T4 HD auto negotiation enabled + +*port_autoneg_100basetx_fd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_100basetx_fd_enabled_ - 100Base-TX FD auto negotiation enabled + +*port_autoneg_100basetx_hd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_100basetx_hd_enabled_ - 100Base-TX HD auto negotiation enabled + +*port_autoneg_10baset_fd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_10baset_fd_enabled_ - 10Base-T FD auto negotiation enabled + +*port_autoneg_10baset_hd_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_10baset_hd_enabled_ - 10Base-T HD auto negotiation enabled + +*port_autoneg_enabled* - keyword, number.long + +* _lldp_neighbors.port_autoneg_enabled_ - Is auto negotiation enabled + +*port_autoneg_supported* - keyword, number.long + +* _lldp_neighbors.port_autoneg_supported_ - Auto negotiation supported + +*port_description* - keyword, text.text + +* _lldp_neighbors.port_description_ - Port description + +*port_id* - keyword, text.text + +* _lldp_neighbors.port_id_ - Port ID value + +*port_id_type* - keyword, text.text + +* _lldp_neighbors.port_id_type_ - Port ID type + +*port_mau_type* - keyword, text.text + +* _lldp_neighbors.port_mau_type_ - MAU type + +*port_mfs* - keyword, number.long + +* _lldp_neighbors.port_mfs_ - Port max frame size + +*port_ttl* - keyword, number.long + +* _lldp_neighbors.port_ttl_ - Age of neighbor port + +*possibly_hidden* - keyword, number.long + +* _wifi_networks.possibly_hidden_ - 1 if network is possibly a hidden network, 0 otherwise + +*power_8023at_enabled* - keyword, number.long + +* _lldp_neighbors.power_8023at_enabled_ - Is 802.3at enabled + +*power_8023at_power_allocated* - keyword, text.text + +* _lldp_neighbors.power_8023at_power_allocated_ - 802.3at power allocated + +*power_8023at_power_priority* - keyword, text.text + +* _lldp_neighbors.power_8023at_power_priority_ - 802.3at power priority + +*power_8023at_power_requested* - keyword, text.text + +* _lldp_neighbors.power_8023at_power_requested_ - 802.3at power requested + +*power_8023at_power_source* - keyword, text.text + +* _lldp_neighbors.power_8023at_power_source_ - 802.3at power source + +*power_8023at_power_type* - keyword, text.text + +* _lldp_neighbors.power_8023at_power_type_ - 802.3at power type + +*power_class* - keyword, text.text + +* _lldp_neighbors.power_class_ - Power class + +*power_device_type* - keyword, text.text + +* _lldp_neighbors.power_device_type_ - Dot3 power device type + +*power_mdi_enabled* - keyword, number.long + +* _lldp_neighbors.power_mdi_enabled_ - Is MDI power enabled + +*power_mdi_supported* - keyword, number.long + +* _lldp_neighbors.power_mdi_supported_ - MDI power supported + +*power_mode* - keyword, text.text + +* _smart_drive_info.power_mode_ - Device power mode + +*power_paircontrol_enabled* - keyword, number.long + +* _lldp_neighbors.power_paircontrol_enabled_ - Is power pair control enabled + +*power_pairs* - keyword, text.text + +* _lldp_neighbors.power_pairs_ - Dot3 power pairs + +*ppid* - keyword, number.long + +* _process_file_events.ppid_ - Parent process ID + +*ppvids_enabled* - keyword, text.text + +* _lldp_neighbors.ppvids_enabled_ - Comma delimited list of enabled PPVIDs + +*ppvids_supported* - keyword, text.text + +* _lldp_neighbors.ppvids_supported_ - Comma delimited list of supported PPVIDs + +*pre_cpu_kernelmode_usage* - keyword, number.long + +* _docker_container_stats.pre_cpu_kernelmode_usage_ - Last read CPU kernel mode usage + +*pre_cpu_total_usage* - keyword, number.long + +* _docker_container_stats.pre_cpu_total_usage_ - Last read total CPU usage + +*pre_cpu_usermode_usage* - keyword, number.long + +* _docker_container_stats.pre_cpu_usermode_usage_ - Last read CPU user mode usage + +*pre_online_cpus* - keyword, number.long + +* _docker_container_stats.pre_online_cpus_ - Last read online CPUs + +*pre_system_cpu_usage* - keyword, number.long + +* _docker_container_stats.pre_system_cpu_usage_ - Last read CPU system usage + +*prefix* - keyword, text.text + +* _homebrew_packages.prefix_ - Homebrew install prefix + +*preread* - keyword, number.long + +* _docker_container_stats.preread_ - UNIX time when stats were last read + +*principal* - keyword, text.text + +* _ntfs_acl_permissions.principal_ - User or group to which the ACE applies. + +*printer_sharing* - keyword, number.long + +* _sharing_preferences.printer_sharing_ - 1 If printer sharing is enabled else 0 + +*priority* - keyword, text.text + +* _deb_packages.priority_ - Package priority + +*privileged* - keyword, text.text + +* _authorization_mechanisms.privileged_ - If privileged it will run as root, else as an anonymous user +* _docker_containers.privileged_ - Is the container privileged + +*probe_error* - keyword, number.long + +* _bpf_process_events.probe_error_ - Set to 1 if one or more buffers could not be captured +* _bpf_socket_events.probe_error_ - Set to 1 if one or more buffers could not be captured + +*process* - keyword, text.text + +* _alf_explicit_auths.process_ - Process name explicitly allowed + +*process_being_tapped* - keyword, number.long + +* _event_taps.process_being_tapped_ - The process ID of the target application + +*process_type* - keyword, text.text + +* _launchd.process_type_ - Key describes the intended purpose of the job + +*process_uptime* - keyword, number.long + +* _windows_crashes.process_uptime_ - Uptime of the process in seconds + +*processes* - keyword, number.long + +* _lxd_instances.processes_ - Number of processes running inside this instance + +*processing_time* - keyword, number.long + +* _cups_jobs.processing_time_ - How long the job took to process + +*processor_number* - keyword, number.long + +* _msr.processor_number_ - The processor number as reported in /proc/cpuinfo + +*processor_type* - keyword, text.text + +* _cpu_info.processor_type_ - The processor type, such as Central, Math, or Video. + +*product_name* - keyword, text.text + +* _tpm_info.product_name_ - Product name of the TPM + +*product_version* - keyword, text.text + +* _file.product_version_ - File product version + +*profile* - keyword, text.text + +* _apparmor_events.profile_ - Apparmor profile name +* _chrome_extensions.profile_ - The name of the Chrome profile that contains this extension + +*profile_path* - keyword, text.text + +* _chrome_extension_content_scripts.profile_path_ - The profile path +* _chrome_extensions.profile_path_ - The profile path +* _logon_sessions.profile_path_ - The home directory for the logon session. + +*program* - keyword, text.text + +* _launchd.program_ - Path to target program + +*program_arguments* - keyword, text.text + +* _launchd.program_arguments_ - Command line arguments passed to program + +*propagation* - keyword, text.text + +* _docker_container_mounts.propagation_ - Mount propagation + +*protected* - keyword, number.long + +* _app_schemes.protected_ - 1 if this handler is protected (reserved) by OS X, else 0 + +*protection_disabled* - keyword, number.long + +* _carbon_black_info.protection_disabled_ - If the sensor is configured to report tamper events + +*protection_status* - keyword, number.long + +* _bitlocker_info.protection_status_ - The bitlocker protection status of the drive. + +*protection_type* - keyword, text.text + +* _processes.protection_type_ - The protection type of the process + +*protocol* - keyword + +* _bpf_socket_events.protocol_ - The network protocol ID +* _etc_services.protocol_ - Transport protocol (TCP/UDP) +* _iptables.protocol_ - Protocol number identification. +* _listening_ports.protocol_ - Transport protocol (TCP/UDP) +* _process_open_sockets.protocol_ - Transport protocol (TCP/UDP) +* _socket_events.protocol_ - The network protocol ID +* _usb_devices.protocol_ - USB Device protocol + +*provider* - keyword, text.text + +* _drivers.provider_ - Driver provider + +*provider_guid* - keyword, text.text + +* _windows_eventlog.provider_guid_ - Provider guid of the event +* _windows_events.provider_guid_ - Provider guid of the event + +*provider_name* - keyword, text.text + +* _windows_eventlog.provider_name_ - Provider name of the event +* _windows_events.provider_name_ - Provider name of the event + +*pseudo* - keyword, number.long + +* _process_memory_map.pseudo_ - 1 If path is a pseudo path, else 0 + +*psize* - keyword, number.long + +* _elf_segments.psize_ - Size of segment in file + +*public* - keyword, number.long + +* _lxd_images.public_ - Whether image is public (1) or not (0) + +*publisher* - keyword, text.text + +* _azure_instance_metadata.publisher_ - Publisher of the VM image +* _osquery_events.publisher_ - Name of the associated publisher +* _programs.publisher_ - Name of the product supplier. + +*purgeable* - keyword, number.long + +* _virtual_memory_info.purgeable_ - Total number of purgeable pages. + +*purged* - keyword, number.long + +* _virtual_memory_info.purged_ - Total number of purged pages. + +*pvid* - keyword, text.text + +* _lldp_neighbors.pvid_ - Primary VLAN id + +*query* - keyword, text.text + +* _mdfind.query_ - The query that was run to find the file +* _osquery_schedule.query_ - The exact query to run +* _wmi_event_filters.query_ - Windows Management Instrumentation Query Language (WQL) event query that specifies the set of events for consumer notification, and the specific conditions for notification. + +*query_language* - keyword, text.text + +* _wmi_event_filters.query_language_ - Query language that the query is written in. + +*queue_directories* - keyword, text.text + +* _launchd.queue_directories_ - Similar to watch_paths but only with non-empty directories + +*raid_disks* - keyword, number.long + +* _md_devices.raid_disks_ - Number of configured RAID disks in array + +*raid_level* - keyword, number.long + +* _md_devices.raid_level_ - Current raid level of the array + +*rapl_energy_status* - keyword, number.long + +* _msr.rapl_energy_status_ - Run Time Average Power Limiting energy status. + +*rapl_power_limit* - keyword, number.long + +* _msr.rapl_power_limit_ - Run Time Average Power Limiting power limit. + +*rapl_power_units* - keyword, number.long + +* _msr.rapl_power_units_ - Run Time Average Power Limiting power units. + +*reactivated* - keyword, number.long + +* _virtual_memory_info.reactivated_ - Total number of reactivated pages. + +*read* - keyword, number.long + +* _docker_container_stats.read_ - UNIX time when stats were read + +*read_device_identity_failure* - keyword, text.text + +* _smart_drive_info.read_device_identity_failure_ - Error string for device id read, if any + +*readonly* - keyword, number.long + +* _nfs_shares.readonly_ - 1 if the share is exported readonly else 0 + +*readonly_rootfs* - keyword, number.long + +* _docker_containers.readonly_rootfs_ - Is the root filesystem mounted as read only + +*record_timestamp* - keyword, text.text + +* _ntfs_journal_events.record_timestamp_ - Journal record timestamp + +*record_usn* - keyword, text.text + +* _ntfs_journal_events.record_usn_ - The update sequence number that identifies the journal record + +*recovery_finish* - keyword, text.text + +* _md_devices.recovery_finish_ - Estimated duration of recovery activity + +*recovery_progress* - keyword, text.text + +* _md_devices.recovery_progress_ - Progress of the recovery activity + +*recovery_speed* - keyword, text.text + +* _md_devices.recovery_speed_ - Speed of recovery activity + +*redirect_accept* - keyword, number.long + +* _interface_ipv6.redirect_accept_ - Accept ICMP redirect messages + +*ref_pid* - keyword, number.long + +* _asl.ref_pid_ - Reference PID for messages proxied by launchd + +*ref_proc* - keyword, text.text + +* _asl.ref_proc_ - Reference process for messages proxied by launchd + +*referenced* - keyword, number.long + +* _chrome_extension_content_scripts.referenced_ - 1 if this extension is referenced by the Preferences file of the profile +* _chrome_extensions.referenced_ - 1 if this extension is referenced by the Preferences file of the profile + +*referenced_identifier* - keyword, text.text + +* _chrome_extensions.referenced_identifier_ - Extension identifier, as specified by the preferences file. Empty if the extension is not in the profile. + +*refreshes* - keyword, number.long + +* _osquery_events.refreshes_ - Publisher only: number of runloop restarts + +*refs* - keyword, number.long + +* _fbsd_kmods.refs_ - Module reverse dependencies +* _kernel_extensions.refs_ - Reference count + +*region* - keyword, text.text + +* _ec2_instance_metadata.region_ - AWS region in which this instance launched + +*registers* - keyword, text.text + +* _crashes.registers_ - The value of the system registers +* _kernel_panics.registers_ - A space delimited line of register:value pairs +* _windows_crashes.registers_ - The values of the system registers + +*registry* - keyword, text.text + +* _osquery_registry.registry_ - Name of the osquery registry + +*registry_hive* - keyword, text.text + +* _logged_in_users.registry_hive_ - HKEY_USERS registry hive + +*registry_path* - keyword, text.text + +* _ie_extensions.registry_path_ - Extension identifier + +*relative_path* - keyword, text.text + +* _shortcut_files.relative_path_ - Relative path to target file from lnk file. +* _wmi_cli_event_consumers.relative_path_ - Relative path to the class or instance. +* _wmi_event_filters.relative_path_ - Relative path to the class or instance. +* _wmi_filter_consumer_binding.relative_path_ - Relative path to the class or instance. +* _wmi_script_event_consumers.relative_path_ - Relative path to the class or instance. + +*release* - keyword, text.text + +* _apt_sources.release_ - Release name +* _lxd_images.release_ - OS release version on which the image is based +* _rpm_packages.release_ - Package release + +*remediation_path* - keyword, text.text + +* _windows_security_products.remediation_path_ - Remediation path + +*remote_address* - keyword, text.text + +* _bpf_socket_events.remote_address_ - Remote address associated with socket +* _process_open_sockets.remote_address_ - Socket remote address +* _socket_events.remote_address_ - Remote address associated with socket + +*remote_apple_events* - keyword, number.long + +* _sharing_preferences.remote_apple_events_ - 1 If remote apple events are enabled else 0 + +*remote_login* - keyword, number.long + +* _sharing_preferences.remote_login_ - 1 If remote login is enabled else 0 + +*remote_management* - keyword, number.long + +* _sharing_preferences.remote_management_ - 1 If remote management is enabled else 0 + +*remote_port* - keyword, number.long + +* _bpf_socket_events.remote_port_ - Remote network protocol port number +* _process_open_sockets.remote_port_ - Socket remote port +* _socket_events.remote_port_ - Remote network protocol port number + +*removable* - keyword, number.long + +* _usb_devices.removable_ - 1 If USB device is removable else 0 + +*repository* - keyword, text.text + +* _portage_packages.repository_ - From which repository the ebuild was used + +*request_id* - keyword, text.text + +* _carves.request_id_ - Identifying value of the carve request (e.g., scheduled query name, distributed request, etc) + +*requested_mask* - keyword, text.text + +* _apparmor_events.requested_mask_ - Requested access mask + +*requirement* - keyword, text.text + +* _gatekeeper_approved_apps.requirement_ - Code signing requirement language + +*reservation_id* - keyword, text.text + +* _ec2_instance_metadata.reservation_id_ - ID of the reservation + +*reshape_finish* - keyword, text.text + +* _md_devices.reshape_finish_ - Estimated duration of reshape activity + +*reshape_progress* - keyword, text.text + +* _md_devices.reshape_progress_ - Progress of the reshape activity + +*reshape_speed* - keyword, text.text + +* _md_devices.reshape_speed_ - Speed of reshape activity + +*resident_size* - keyword, number.long + +* _docker_container_processes.resident_size_ - Bytes of private memory used by process +* _processes.resident_size_ - Bytes of private memory used by process + +*resource_group_name* - keyword, text.text + +* _azure_instance_metadata.resource_group_name_ - Resource group for the VM + +*response_code* - keyword, number.long + +* _curl.response_code_ - The HTTP status code for the response + +*responsible* - keyword, text.text + +* _crashes.responsible_ - Process responsible for the crashed process + +*result* - keyword, text.text + +* _authenticode.result_ - The signature check result +* _curl.result_ - The HTTP response body + +*resync_finish* - keyword, text.text + +* _md_devices.resync_finish_ - Estimated duration of resync activity + +*resync_progress* - keyword, text.text + +* _md_devices.resync_progress_ - Progress of the resync activity + +*resync_speed* - keyword, text.text + +* _md_devices.resync_speed_ - Speed of resync activity + +*retain_count* - keyword, number.long + +* _iokit_devicetree.retain_count_ - The device reference count +* _iokit_registry.retain_count_ - The node reference count + +*revision* - keyword, text.text + +* _deb_packages.revision_ - Package revision +* _hardware_events.revision_ - Device revision (optional) +* _platform_info.revision_ - BIOS major and minor revision + +*rid* - keyword, number.long + +* _lldp_neighbors.rid_ - Neighbor chassis index + +*roaming* - keyword, number.long + +* _wifi_networks.roaming_ - 1 if roaming is supported, 0 otherwise + +*roaming_profile* - keyword, text.text + +* _wifi_networks.roaming_profile_ - Describe the roaming profile, usually one of Single, Dual or Multi + +*root* - keyword, text.text + +* _processes.root_ - Process virtual root directory + +*root_dir* - keyword, text.text + +* _docker_info.root_dir_ - Docker root directory + +*root_directory* - keyword, text.text + +* _launchd.root_directory_ - Key used to specify a directory to chroot to before launch + +*root_volume_uuid* - keyword, text.text + +* _time_machine_destinations.root_volume_uuid_ - Root UUID of backup volume + +*rotation_rate* - keyword, text.text + +* _smart_drive_info.rotation_rate_ - Drive RPM + +*round_trip_time* - keyword, number.long + +* _curl.round_trip_time_ - Time taken to complete the request + +*rowid* - keyword, number.long + +* _quicklook_cache.rowid_ - Quicklook file rowid key + +*rssi* - keyword, number.long + +* _wifi_status.rssi_ - The current received signal strength indication (dbm) +* _wifi_survey.rssi_ - The current received signal strength indication (dbm) + +*rtadv_accept* - keyword, number.long + +* _interface_ipv6.rtadv_accept_ - Accept ICMP Router Advertisement + +*rule_details* - keyword, text.text + +* _sudoers.rule_details_ - Rule definition + +*run_at_load* - keyword, text.text + +* _launchd.run_at_load_ - Should the program run on launch load + +*run_count* - keyword, number.long + +* _prefetch.run_count_ - Number of times the application has been run. + +*rw* - keyword, number.long + +* _docker_container_mounts.rw_ - 1 if read/write. 0 otherwise + +*sata_version* - keyword, text.text + +* _smart_drive_info.sata_version_ - SATA version, if any + +*scheme* - keyword, text.text + +* _app_schemes.scheme_ - Name of the scheme/protocol + +*scope* - keyword, text.text + +* _selinux_settings.scope_ - Where the key is located inside the SELinuxFS mount point. + +*screen_sharing* - keyword, number.long + +* _sharing_preferences.screen_sharing_ - 1 If screen sharing is enabled else 0 + +*script* - keyword, text.text + +* _chrome_extension_content_scripts.script_ - The content script used by the extension + +*script_block_count* - keyword, number.long + +* _powershell_events.script_block_count_ - The total number of script blocks for this script + +*script_block_id* - keyword, text.text + +* _powershell_events.script_block_id_ - The unique GUID of the powershell script to which this block belongs + +*script_file_name* - keyword, text.text + +* _wmi_script_event_consumers.script_file_name_ - Name of the file from which the script text is read, intended as an alternative to specifying the text of the script in the ScriptText property. + +*script_name* - keyword, text.text + +* _powershell_events.script_name_ - The name of the Powershell script + +*script_path* - keyword, text.text + +* _powershell_events.script_path_ - The path for the Powershell script + +*script_text* - keyword, text.text + +* _powershell_events.script_text_ - The text content of the Powershell script +* _wmi_script_event_consumers.script_text_ - Text of the script that is expressed in a language known to the scripting engine. This property must be NULL if the ScriptFileName property is not NULL. + +*scripting_engine* - keyword, text.text + +* _wmi_script_event_consumers.scripting_engine_ - Name of the scripting engine to use, for example, 'VBScript'. This property cannot be NULL. + +*sdb_id* - keyword, text.text + +* _appcompat_shims.sdb_id_ - Unique GUID of the SDB. + +*sdk* - keyword, text.text + +* _browser_plugins.sdk_ - Build SDK used to compile plugin +* _safari_extensions.sdk_ - Bundle SDK used to compile extension + +*sdk_version* - keyword, text.text + +* _osquery_extensions.sdk_version_ - osquery SDK version used to build the extension + +*seconds* - keyword, number.long + +* _time.seconds_ - Current seconds in the system +* _uptime.seconds_ - Seconds of uptime + +*section* - keyword, text.text + +* _deb_packages.section_ - Package section + +*sector_sizes* - keyword, text.text + +* _smart_drive_info.sector_sizes_ - Bytes of drive sector sizes + +*secure_boot* - keyword, number.long + +* _secureboot.secure_boot_ - Whether secure boot is enabled + +*secure_process* - keyword, number.long + +* _processes.secure_process_ - Process is secure (IUM) yes=1, no=0 + +*security_breach* - keyword, text.text + +* _chassis_info.security_breach_ - The physical status of the chassis such as Breach Successful, Breach Attempted, etc. + +*security_groups* - keyword, text.text + +* _ec2_instance_metadata.security_groups_ - Comma separated list of security group names + +*security_options* - keyword, text.text + +* _docker_containers.security_options_ - List of container security options + +*security_type* - keyword, text.text + +* _wifi_networks.security_type_ - Type of security on this network +* _wifi_status.security_type_ - Type of security on this network + +*self_signed* - keyword, number.long + +* _certificates.self_signed_ - 1 if self-signed, else 0 + +*sender* - keyword, text.text + +* _asl.sender_ - Sender's identification string. Default is process name. + +*sensor_backend_server* - keyword, text.text + +* _carbon_black_info.sensor_backend_server_ - Carbon Black server + +*sensor_id* - keyword, number.long + +* _carbon_black_info.sensor_id_ - Sensor ID of the Carbon Black sensor + +*sensor_ip_addr* - keyword, text.text + +* _carbon_black_info.sensor_ip_addr_ - IP address of the sensor + +*seq_num* - keyword, number.long + +* _es_process_events.seq_num_ - Per event sequence number + +*serial* - keyword, text.text + +* _certificates.serial_ - Certificate serial number +* _chassis_info.serial_ - The serial number of the chassis. +* _disk_info.serial_ - The serial number of the disk. +* _hardware_events.serial_ - Device serial (optional) +* _usb_devices.serial_ - USB Device serial connection + +*serial_number* - keyword, text.text + +* _authenticode.serial_number_ - The certificate serial number +* _battery.serial_number_ - The battery's unique serial number +* _curl_certificate.serial_number_ - Certificate serial number +* _memory_devices.serial_number_ - Serial number of memory device +* _smart_drive_info.serial_number_ - Device serial number + +*serial_port_enabled* - keyword, text.text + +* _ycloud_instance_metadata.serial_port_enabled_ - Indicates if serial port is enabled for the VM + +*series* - keyword, text.text + +* _video_info.series_ - The series of the gpu. + +*server_name* - keyword, text.text + +* _lxd_cluster.server_name_ - Name of the LXD server node +* _lxd_cluster_members.server_name_ - Name of the LXD server node + +*server_version* - keyword, text.text + +* _docker_info.server_version_ - Server version + +*service* - keyword, text.text + +* _drivers.service_ - Driver service name, if one exists +* _interface_details.service_ - The name of the service the network adapter uses. +* _iokit_devicetree.service_ - 1 if the device conforms to IOService else 0 + +*service_exit_code* - keyword, number.long + +* _services.service_exit_code_ - The service-specific error code that the service returns when an error occurs while the service is starting or stopping + +*service_key* - keyword, text.text + +* _drivers.service_key_ - Driver service registry key + +*service_type* - keyword, text.text + +* _services.service_type_ - Service Type: OWN_PROCESS, SHARE_PROCESS and maybe Interactive (can interact with the desktop) + +*ses* - keyword, number.long + +* _seccomp_events.ses_ - Session ID of the session from which the analyzed process was invoked + +*session_id* - keyword, number.long + +* _logon_sessions.session_id_ - The Terminal Services session identifier. +* _winbaseobj.session_id_ - Terminal Services Session Id + +*session_owner* - keyword, text.text + +* _authorizations.session_owner_ - Label top-level key + +*set* - keyword, number.long + +* _memory_devices.set_ - Identifies if memory device is one of a set of devices. A value of 0 indicates no set affiliation. + +*setup_mode* - keyword, number.long + +* _secureboot.setup_mode_ - Whether setup mode is enabled + +*severity* - keyword, number.long + +* _syslog_events.severity_ - Syslog severity + +*sgid* - keyword + +* _docker_container_processes.sgid_ - Saved group ID +* _process_events.sgid_ - Saved group ID at process start +* _process_file_events.sgid_ - Saved group ID of the process using the file +* _processes.sgid_ - Unsigned saved group ID + +*sha1* - keyword, text.text + +* _apparmor_profiles.sha1_ - A unique hash that identifies this policy. +* _certificates.sha1_ - SHA1 hash of the raw certificate contents +* _device_hash.sha1_ - SHA1 hash of provided inode data +* _file_events.sha1_ - The SHA1 of the file after change +* _hash.sha1_ - SHA1 hash of provided filesystem data +* _rpm_packages.sha1_ - SHA1 hash of the package contents + +*sha1_fingerprint* - keyword, text.text + +* _curl_certificate.sha1_fingerprint_ - SHA1 fingerprint + +*sha256* - keyword, text.text + +* _carves.sha256_ - A SHA256 sum of the carved archive +* _device_hash.sha256_ - SHA256 hash of provided inode data +* _file_events.sha256_ - The SHA256 of the file after change +* _hash.sha256_ - SHA256 hash of provided filesystem data +* _rpm_package_files.sha256_ - SHA256 file digest from RPM info DB + +*sha256_fingerprint* - keyword, text.text + +* _curl_certificate.sha256_fingerprint_ - SHA-256 fingerprint + +*shard* - keyword, number.long + +* _osquery_packs.shard_ - Shard restriction limit, 1-100, 0 meaning no restriction + +*share* - keyword, text.text + +* _nfs_shares.share_ - Filesystem path to the share + +*share_name* - keyword, text.text + +* _shortcut_files.share_name_ - Share name of the target file. + +*shared* - keyword, text.text + +* _authorizations.shared_ - Label top-level key + +*shell* - keyword, text.text + +* _users.shell_ - User's configured default shell + +*shell_only* - keyword, number.long + +* _osquery_flags.shell_only_ - Is the flag shell only? + +*shmid* - keyword, number.long + +* _shared_memory.shmid_ - Shared memory segment ID + +*sid* - keyword, text.text + +* _background_activities_moderator.sid_ - User SID. +* _certificates.sid_ - SID +* _logged_in_users.sid_ - The user's unique security identifier +* _office_mru.sid_ - User SID +* _shellbags.sid_ - User SID +* _userassist.sid_ - User SID. + +*sig* - keyword, number.long + +* _seccomp_events.sig_ - Signal value sent to process by seccomp + +*sig_group* - keyword, text.text + +* _yara.sig_group_ - Signature group used + +*sigfile* - keyword, text.text + +* _yara.sigfile_ - Signature file used + +*signature* - keyword, text.text + +* _curl_certificate.signature_ - Signature + +*signature_algorithm* - keyword, text.text + +* _curl_certificate.signature_algorithm_ - Signature Algorithm + +*signatures_up_to_date* - keyword, number.long + +* _windows_security_products.signatures_up_to_date_ - 1 if product signatures are up to date, else 0 + +*signed* - keyword, number.long + +* _drivers.signed_ - Whether the driver is signed or not +* _signature.signed_ - 1 If the file is signed else 0 + +*signing_algorithm* - keyword, text.text + +* _certificates.signing_algorithm_ - Signing algorithm used + +*signing_id* - keyword, text.text + +* _es_process_events.signing_id_ - Signature identifier of the process + +*sigrule* - keyword, text.text + +* _yara.sigrule_ - Signature strings used + +*sigurl* - keyword, text.text + +* _yara.sigurl_ - Signature url + +*size* - keyword + +* _acpi_tables.size_ - Size of compiled table data +* _block_devices.size_ - Block device size in blocks +* _carves.size_ - Size of the carved archive +* _cups_jobs.size_ - The size of the print job +* _deb_packages.size_ - Package size in bytes +* _device_file.size_ - Size of file in bytes +* _disk_events.size_ - Size of partition in bytes +* _docker_image_history.size_ - Size of instruction in bytes +* _elf_sections.size_ - Size of section +* _elf_symbols.size_ - Size of object +* _example.size_ - This is a signed SQLite bigint column +* _fbsd_kmods.size_ - Size of module content +* _file.size_ - Size of file in bytes +* _file_events.size_ - Size of file in bytes +* _kernel_extensions.size_ - Bytes of wired memory used by extension +* _kernel_modules.size_ - Size of module content +* _logical_drives.size_ - The total amount of space, in bytes, of the drive (-1 on failure). +* _lxd_images.size_ - Size of image in bytes +* _lxd_storage_pools.size_ - Size of the storage pool +* _md_devices.size_ - size of the array in blocks +* _memory_devices.size_ - Size of memory device in Megabyte +* _package_bom.size_ - Expected file size +* _platform_info.size_ - Size in bytes of firmware +* _portage_packages.size_ - The size of the package +* _prefetch.size_ - Application file size. +* _quicklook_cache.size_ - Parsed version size field +* _rpm_package_files.size_ - Expected file size in bytes from RPM info DB +* _rpm_packages.size_ - Package size in bytes +* _shared_memory.size_ - Size in bytes +* _smbios_tables.size_ - Table entry size in bytes +* _smc_keys.size_ - Reported size of data in bytes + +*size_bytes* - keyword, number.long + +* _docker_images.size_bytes_ - Size of image in bytes + +*sku* - keyword, text.text + +* _azure_instance_metadata.sku_ - SKU for the VM image +* _chassis_info.sku_ - The Stock Keeping Unit number if available. + +*slot* - keyword + +* _md_drives.slot_ - Slot position of disk +* _portage_packages.slot_ - The slot used by package + +*smart_enabled* - keyword, text.text + +* _smart_drive_info.smart_enabled_ - SMART enabled status + +*smart_supported* - keyword, text.text + +* _smart_drive_info.smart_supported_ - SMART support status + +*smbios_tag* - keyword, text.text + +* _chassis_info.smbios_tag_ - The assigned asset tag number of the chassis. + +*socket* - keyword + +* _listening_ports.socket_ - Socket handle or inode number +* _process_open_sockets.socket_ - Socket handle or inode number +* _socket_events.socket_ - The local path (UNIX domain socket only) + +*socket_designation* - keyword, text.text + +* _cpu_info.socket_designation_ - The assigned socket on the board for the given CPU. + +*soft_limit* - keyword, text.text + +* _ulimit_info.soft_limit_ - Current limit value + +*softirq* - keyword, number.long + +* _cpu_time.softirq_ - Time spent servicing softirqs + +*source* - keyword, text.text + +* _apt_sources.source_ - Source file +* _autoexec.source_ - Source table of the autoexec item +* _deb_packages.source_ - Package source +* _docker_container_mounts.source_ - Source path on host +* _lxd_storage_pools.source_ - Storage pool source +* _package_install_history.source_ - Install source: usually the installer process name +* _routes.source_ - Route source +* _rpm_packages.source_ - Source RPM package name (optional) +* _shellbags.source_ - Shellbags source Registry file +* _startup_items.source_ - Directory or plist containing startup item +* _sudoers.source_ - Source file containing the given rule +* _windows_events.source_ - Source or channel of the event + +*source_path* - keyword, text.text + +* _systemd_units.source_path_ - Path to the (possibly generated) unit configuration file + +*source_url* - keyword, text.text + +* _firefox_addons.source_url_ - URL that installed the addon + +*space_total* - keyword, number.long + +* _lxd_storage_pools.space_total_ - Total available storage space in bytes for this storage pool + +*space_used* - keyword, number.long + +* _lxd_storage_pools.space_used_ - Storage space used in bytes + +*spare_disks* - keyword, number.long + +* _md_devices.spare_disks_ - Number of idle disks in array + +*spec_version* - keyword, text.text + +* _tpm_info.spec_version_ - Trusted Computing Group specification that the TPM supports + +*speculative* - keyword, number.long + +* _virtual_memory_info.speculative_ - Total number of speculative pages. + +*speed* - keyword, number.long + +* _interface_details.speed_ - Estimate of the current bandwidth in bits per second. + +*src_ip* - keyword, text.text + +* _iptables.src_ip_ - Source IP address. + +*src_mask* - keyword, text.text + +* _iptables.src_mask_ - Source IP address mask. + +*src_port* - keyword, text.text + +* _iptables.src_port_ - Protocol source port(s). + +*ssdeep* - keyword, text.text + +* _hash.ssdeep_ - ssdeep hash of provided filesystem data + +*ssh_config_file* - keyword, text.text + +* _ssh_configs.ssh_config_file_ - Path to the ssh_config file + +*ssh_public_key* - keyword, text.text + +* _ec2_instance_metadata.ssh_public_key_ - SSH public key. Only available if supplied at instance launch time +* _ycloud_instance_metadata.ssh_public_key_ - SSH public key. Only available if supplied at instance launch time + +*ssid* - keyword, text.text + +* _wifi_networks.ssid_ - SSID octets of the network +* _wifi_status.ssid_ - SSID octets of the network +* _wifi_survey.ssid_ - SSID octets of the network + +*stack_trace* - keyword, text.text + +* _crashes.stack_trace_ - Most recent frame from the stack trace +* _windows_crashes.stack_trace_ - Multiple stack frames from the stack trace + +*start* - keyword, text.text + +* _memory_map.start_ - Start address of memory region +* _process_memory_map.start_ - Virtual start address (hex) + +*start_interval* - keyword, text.text + +* _launchd.start_interval_ - Frequency to run in seconds + +*start_on_mount* - keyword, text.text + +* _launchd.start_on_mount_ - Run daemon or agent every time a filesystem is mounted + +*start_time* - keyword, number.long + +* _docker_container_processes.start_time_ - Process start in seconds since boot (non-sleeping) +* _osquery_info.start_time_ - UNIX time in seconds when the process started +* _processes.start_time_ - Process start time in seconds since Epoch, in case of error -1 + +*start_type* - keyword, text.text + +* _services.start_type_ - Service start type: BOOT_START, SYSTEM_START, AUTO_START, DEMAND_START, DISABLED + +*started_at* - keyword, text.text + +* _docker_containers.started_at_ - Container start time as string + +*starting_address* - keyword, text.text + +* _memory_array_mapped_addresses.starting_address_ - Physical stating address, in kilobytes, of a range of memory mapped to physical memory array +* _memory_device_mapped_addresses.starting_address_ - Physical stating address, in kilobytes, of a range of memory mapped to physical memory array + +*state* - keyword + +* _alf_exceptions.state_ - Firewall exception state +* _battery.state_ - One of the following: "AC Power" indicates the battery is connected to an external power source, "Battery Power" indicates that the battery is drawing internal power, "Off Line" indicates the battery is off-line or no longer connected +* _chrome_extensions.state_ - 1 if this extension is enabled +* _docker_container_processes.state_ - Process state +* _docker_containers.state_ - Container state (created, restarting, running, removing, paused, exited, dead) +* _lxd_networks.state_ - Network status +* _md_drives.state_ - State of the drive +* _process_open_sockets.state_ - TCP socket state +* _processes.state_ - Process state +* _scheduled_tasks.state_ - State of the scheduled task +* _system_extensions.state_ - System extension state +* _windows_optional_features.state_ - Installation state value. 1 == Enabled, 2 == Disabled, 3 == Absent +* _windows_security_products.state_ - State of protection + +*state_timestamp* - keyword, text.text + +* _windows_security_products.state_timestamp_ - Timestamp for the product state + +*stateful* - keyword, number.long + +* _lxd_instances.stateful_ - Whether the instance is stateful(1) or not(0) + +*statename* - keyword, text.text + +* _windows_optional_features.statename_ - Installation state name. 'Enabled','Disabled','Absent' + +*status* - keyword, text.text + +* _carves.status_ - Status of the carve, can be STARTING, PENDING, SUCCESS, or FAILED +* _chassis_info.status_ - If available, gives various operational or nonoperational statuses such as OK, Degraded, and Pred Fail. +* _deb_packages.status_ - Package status +* _docker_containers.status_ - Container status information +* _kernel_modules.status_ - Kernel module status +* _lxd_cluster_members.status_ - Status of the node (Online/Offline) +* _lxd_instances.status_ - Instance state (running, stopped, etc.) +* _md_devices.status_ - Current state of the array +* _ntdomains.status_ - The current status of the domain object. +* _process_events.status_ - OpenBSM Attribute: Status of the process +* _services.status_ - Service Current status: STOPPED, START_PENDING, STOP_PENDING, RUNNING, CONTINUE_PENDING, PAUSE_PENDING, PAUSED +* _shared_memory.status_ - Destination/attach status +* _shared_resources.status_ - String that indicates the current status of the object. +* _socket_events.status_ - Either 'succeeded', 'failed', 'in_progress' (connect() on non-blocking socket) or 'no_client' (null accept() on non-blocking socket) +* _startup_items.status_ - Startup status; either enabled or disabled + +*stderr_path* - keyword, text.text + +* _launchd.stderr_path_ - Pipe stderr to a target path + +*stdout_path* - keyword, text.text + +* _launchd.stdout_path_ - Pipe stdout to a target path + +*steal* - keyword, number.long + +* _cpu_time.steal_ - Time spent in other operating systems when running in a virtualized environment + +*stealth_enabled* - keyword, number.long + +* _alf.stealth_enabled_ - 1 If stealth mode is enabled else 0 + +*stibp_support_enabled* - keyword, number.long + +* _kva_speculative_info.stibp_support_enabled_ - Windows uses STIBP. + +*storage_driver* - keyword, text.text + +* _docker_info.storage_driver_ - Storage driver + +*store* - keyword, text.text + +* _certificates.store_ - Certificate system store + +*store_id* - keyword, text.text + +* _certificates.store_id_ - Exists for service/user stores. Contains raw store id provided by WinAPI. + +*store_location* - keyword, text.text + +* _certificates.store_location_ - Certificate system store location + +*strings* - keyword, text.text + +* _yara.strings_ - Matching strings +* _yara_events.strings_ - Matching strings + +*sub_state* - keyword, text.text + +* _systemd_units.sub_state_ - The low-level unit activation state, values depend on unit type + +*subclass* - keyword, text.text + +* _usb_devices.subclass_ - USB Device subclass + +*subject* - keyword, text.text + +* _certificates.subject_ - Certificate distinguished name + +*subject_alternative_names* - keyword, text.text + +* _curl_certificate.subject_alternative_names_ - Subject Alternative Name + +*subject_info_access* - keyword, text.text + +* _curl_certificate.subject_info_access_ - Subject Information Access + +*subject_key_id* - keyword, text.text + +* _certificates.subject_key_id_ - SKID an optionally included SHA1 + +*subject_key_identifier* - keyword, text.text + +* _curl_certificate.subject_key_identifier_ - Subject Key Identifier + +*subject_name* - keyword, text.text + +* _authenticode.subject_name_ - The certificate subject name + +*subkey* - keyword, text.text + +* _plist.subkey_ - Intermediate key path, includes lists/dicts +* _preferences.subkey_ - Intemediate key path, includes lists/dicts + +*subnet* - keyword, text.text + +* _docker_networks.subnet_ - Network subnet + +*subscription_id* - keyword, text.text + +* _azure_instance_metadata.subscription_id_ - Azure subscription for the VM + +*subscriptions* - keyword, number.long + +* _osquery_events.subscriptions_ - Number of subscriptions the publisher received or subscriber used + +*subsystem* - keyword, text.text + +* _system_controls.subsystem_ - Subsystem ID, control type + +*subsystem_model* - keyword, text.text + +* _pci_devices.subsystem_model_ - Device description of PCI device subsystem + +*subsystem_model_id* - keyword, text.text + +* _pci_devices.subsystem_model_id_ - Model ID of PCI device subsystem + +*subsystem_vendor* - keyword, text.text + +* _pci_devices.subsystem_vendor_ - Vendor of PCI device subsystem + +*subsystem_vendor_id* - keyword, text.text + +* _pci_devices.subsystem_vendor_id_ - Vendor ID of PCI device subsystem + +*success* - keyword, number.long + +* _socket_events.success_ - Deprecated. Use the 'status' column instead + +*suid* - keyword + +* _docker_container_processes.suid_ - Saved user ID +* _process_events.suid_ - Saved user ID at process start +* _process_file_events.suid_ - Saved user ID of the process using the file +* _processes.suid_ - Unsigned saved user ID + +*summary* - keyword, text.text + +* _chocolatey_packages.summary_ - Package-supplied summary +* _python_packages.summary_ - Package-supplied summary + +*superblock_state* - keyword, text.text + +* _md_devices.superblock_state_ - State of the superblock + +*superblock_update_time* - keyword, number.long + +* _md_devices.superblock_update_time_ - Unix timestamp of last update + +*superblock_version* - keyword, text.text + +* _md_devices.superblock_version_ - Version of the superblock + +*swap_cached* - keyword, number.long + +* _memory_info.swap_cached_ - The amount of swap, in bytes, used as cache memory + +*swap_free* - keyword, number.long + +* _memory_info.swap_free_ - The total amount of swap free, in bytes + +*swap_ins* - keyword, number.long + +* _virtual_memory_info.swap_ins_ - The total number of compressed pages that have been swapped out to disk. + +*swap_limit* - keyword, number.long + +* _docker_info.swap_limit_ - 1 if swap limit support is enabled. 0 otherwise + +*swap_outs* - keyword, number.long + +* _virtual_memory_info.swap_outs_ - The total number of compressed pages that have been swapped back in from disk. + +*swap_total* - keyword, number.long + +* _memory_info.swap_total_ - The total amount of swap available, in bytes + +*symlink* - keyword, number.long + +* _file.symlink_ - 1 if the path is a symlink, otherwise 0 + +*syscall* - keyword, text.text + +* _bpf_process_events.syscall_ - System call name +* _bpf_socket_events.syscall_ - System call name +* _process_events.syscall_ - Syscall name: fork, vfork, clone, execve, execveat +* _seccomp_events.syscall_ - Type of the system call + +*system* - keyword, number.long + +* _cpu_time.system_ - Time spent in system mode + +*system_cpu_usage* - keyword, number.long + +* _docker_container_stats.system_cpu_usage_ - CPU system usage + +*system_model* - keyword, text.text + +* _kernel_panics.system_model_ - Physical system model, for example 'MacBookPro12,1 (Mac-E43C1C25D4880AD6)' + +*system_time* - keyword, number.long + +* _osquery_schedule.system_time_ - Total system time spent executing +* _processes.system_time_ - CPU time in milliseconds spent in kernel space + +*table* - keyword, text.text + +* _elf_symbols.table_ - Table name containing symbol + +*tag* - keyword + +* _elf_dynamic.tag_ - Tag ID +* _syslog_events.tag_ - The syslog tag + +*tags* - keyword, text.text + +* _docker_image_history.tags_ - Comma-separated list of tags +* _docker_images.tags_ - Comma-separated list of repository tags +* _yara.tags_ - Matching tags +* _yara_events.tags_ - Matching tags + +*tapping_process* - keyword, number.long + +* _event_taps.tapping_process_ - The process ID of the application that created the event tap. + +*target* - keyword + +* _fan_speed_sensors.target_ - Target speed +* _iptables.target_ - Target that applies for this rule. + +*target_accessed* - keyword, number.long + +* _shortcut_files.target_accessed_ - Target Accessed time. + +*target_created* - keyword, number.long + +* _shortcut_files.target_created_ - Target Created time. + +*target_modified* - keyword, number.long + +* _shortcut_files.target_modified_ - Target Modified time. + +*target_name* - keyword, text.text + +* _prometheus_metrics.target_name_ - Address of prometheus target + +*target_path* - keyword, text.text + +* _file_events.target_path_ - The path associated with the event +* _shortcut_files.target_path_ - Target file path +* _yara_events.target_path_ - The path scanned + +*target_size* - keyword, number.long + +* _shortcut_files.target_size_ - Size of target file. + +*task* - keyword, number.long + +* _windows_eventlog.task_ - Task value associated with the event +* _windows_events.task_ - Task value associated with the event + +*team* - keyword, text.text + +* _system_extensions.team_ - Signing team ID + +*team_id* - keyword, text.text + +* _es_process_events.team_id_ - Team identifier of thd process + +*team_identifier* - keyword, text.text + +* _signature.team_identifier_ - The team signing identifier sealed into the signature + +*temporarily_disabled* - keyword, number.long + +* _wifi_networks.temporarily_disabled_ - 1 if this network is temporarily disabled, 0 otherwise + +*terminal* - keyword, text.text + +* _user_events.terminal_ - The network protocol ID + +*threads* - keyword, number.long + +* _docker_container_processes.threads_ - Number of threads used by process +* _processes.threads_ - Number of threads used by process + +*throttled* - keyword, number.long + +* _virtual_memory_info.throttled_ - Total number of throttled pages. + +*tid* - keyword, number.long + +* _bpf_process_events.tid_ - Thread ID +* _bpf_socket_events.tid_ - Thread ID +* _windows_crashes.tid_ - Thread ID of the crashed thread +* _windows_eventlog.tid_ - Thread ID which emitted the event record + +*time* - keyword + +* _apparmor_events.time_ - Time of execution in UNIX time +* _asl.time_ - Unix timestamp. Set automatically +* _bpf_process_events.time_ - Time of execution in UNIX time +* _bpf_socket_events.time_ - Time of execution in UNIX time +* _carves.time_ - Time at which the carve was kicked off +* _disk_events.time_ - Time of appearance/disappearance in UNIX time +* _docker_container_processes.time_ - Cumulative CPU time. [DD-]HH:MM:SS format +* _es_process_events.time_ - Time of execution in UNIX time +* _file_events.time_ - Time of file event +* _hardware_events.time_ - Time of hardware event +* _kernel_panics.time_ - Formatted time of the event +* _last.time_ - Entry timestamp +* _logged_in_users.time_ - Time entry was made +* _ntfs_journal_events.time_ - Time of file event +* _package_install_history.time_ - Label date as UNIX timestamp +* _powershell_events.time_ - Timestamp the event was received by the osquery event publisher +* _process_events.time_ - Time of execution in UNIX time +* _process_file_events.time_ - Time of execution in UNIX time +* _seccomp_events.time_ - Time of execution in UNIX time +* _selinux_events.time_ - Time of execution in UNIX time +* _shell_history.time_ - Entry timestamp. It could be absent, default value is 0. +* _socket_events.time_ - Time of execution in UNIX time +* _syslog_events.time_ - Current unix epoch time +* _user_events.time_ - Time of execution in UNIX time +* _user_interaction_events.time_ - Time +* _windows_events.time_ - Timestamp the event was received +* _xprotect_reports.time_ - Quarantine alert time +* _yara_events.time_ - Time of the scan + +*time_nano_sec* - keyword, number.long + +* _asl.time_nano_sec_ - Nanosecond time. + +*time_range* - keyword, text.text + +* _windows_eventlog.time_range_ - System time to selectively filter the events + +*timeout* - keyword, text.text + +* _authorizations.timeout_ - Label top-level key +* _curl_certificate.timeout_ - Set this value to the timeout in seconds to complete the TLS handshake (default 4s, use 0 for no timeout) + +*timestamp* - keyword, text.text + +* _time.timestamp_ - Current timestamp (log format) in the system +* _windows_eventlog.timestamp_ - Timestamp to selectively filter the events + +*timestamp_ms* - keyword, number.long + +* _prometheus_metrics.timestamp_ms_ - Unix timestamp of collected data in MS + +*timezone* - keyword, text.text + +* _time.timezone_ - Current timezone in the system + +*title* - keyword, text.text + +* _cups_jobs.title_ - Title of the printed job + +*total_seconds* - keyword, number.long + +* _uptime.total_seconds_ - Total uptime seconds + +*total_size* - keyword, number.long + +* _docker_container_processes.total_size_ - Total virtual memory size +* _processes.total_size_ - Total virtual memory size + +*total_width* - keyword, number.long + +* _memory_devices.total_width_ - Total width, in bits, of this memory device, including any check or error-correction bits + +*transaction_id* - keyword, number.long + +* _file_events.transaction_id_ - ID used during bulk update +* _yara_events.transaction_id_ - ID used during bulk update + +*transmit_rate* - keyword, text.text + +* _wifi_status.transmit_rate_ - The current transmit rate + +*transport_type* - keyword, text.text + +* _smart_drive_info.transport_type_ - Drive transport type + +*tries* - keyword, text.text + +* _authorizations.tries_ - Label top-level key + +*tty* - keyword, text.text + +* _last.tty_ - Entry terminal +* _logged_in_users.tty_ - Device name + +*turbo_disabled* - keyword, number.long + +* _msr.turbo_disabled_ - Whether the turbo feature is disabled. + +*turbo_ratio_limit* - keyword, number.long + +* _msr.turbo_ratio_limit_ - The turbo feature ratio limit. + +*type* - keyword, text.text + +* _apparmor_events.type_ - Event type +* _appcompat_shims.type_ - Type of the SDB database. +* _block_devices.type_ - Block device type string +* _bpf_socket_events.type_ - The socket type +* _crashes.type_ - Type of crash log +* _device_file.type_ - File status +* _device_firmware.type_ - Type of device +* _device_partitions.type_ - +* _disk_encryption.type_ - Description of cipher type and mode if available +* _disk_info.type_ - The interface type of the disk. +* _dns_cache.type_ - DNS record type +* _dns_resolvers.type_ - Address type: sortlist, nameserver, search +* _docker_container_mounts.type_ - Type of mount (bind, volume) +* _docker_container_ports.type_ - Protocol (tcp, udp) +* _docker_volumes.type_ - Volume type +* _elf_info.type_ - Offset of section in file +* _elf_sections.type_ - Section type +* _elf_symbols.type_ - Symbol type +* _file.type_ - File status +* _firefox_addons.type_ - Extension, addon, webapp +* _hardware_events.type_ - Type of hardware and hardware event +* _interface_addresses.type_ - Type of address. One of dhcp, manual, auto, other, unknown +* _interface_details.type_ - Interface type (includes virtual) +* _keychain_items.type_ - Keychain item type (class) +* _last.type_ - Entry type, according to ut_type types (utmp.h) +* _logged_in_users.type_ - Login type +* _logical_drives.type_ - Deprecated (always 'Unknown'). +* _lxd_certificates.type_ - Type of the certificate +* _lxd_networks.type_ - Type of network +* _mounts.type_ - Mounted device type +* _ntfs_acl_permissions.type_ - Type of access mode for the access control entry. +* _nvram.type_ - Data type (CFData, CFString, etc) +* _osquery_events.type_ - Either publisher or subscriber +* _osquery_extensions.type_ - SDK extension type: extension or module +* _osquery_flags.type_ - Flag type +* _process_open_pipes.type_ - Pipe Type: named vs unnamed/anonymous +* _registry.type_ - Type of the registry value, or 'subkey' if item is a subkey +* _routes.type_ - Type of route +* _selinux_events.type_ - Event type +* _shared_resources.type_ - Type of resource being shared. Types include: disk drives, print queues, interprocess communications (IPC), and general devices. +* _smbios_tables.type_ - Table entry type +* _smc_keys.type_ - SMC-reported type literal type +* _startup_items.type_ - Startup Item or Login Item +* _system_controls.type_ - Data type +* _ulimit_info.type_ - System resource to be limited +* _user_events.type_ - The file description for the process socket +* _users.type_ - Whether the account is roaming (domain), local, or a system profile +* _windows_crashes.type_ - Type of crash log +* _windows_security_products.type_ - Type of security product +* _xprotect_meta.type_ - Either plugin or extension + +*type_name* - keyword, text.text + +* _last.type_name_ - Entry type name, according to ut_type types (utmp.h) + +*uid* - keyword + +* _account_policy_data.uid_ - User ID +* _asl.uid_ - UID that sent the log message (set by the server). +* _atom_packages.uid_ - The local user that owns the plugin +* _authorized_keys.uid_ - The local owner of authorized_keys file +* _bpf_process_events.uid_ - User ID +* _bpf_socket_events.uid_ - User ID +* _browser_plugins.uid_ - The local user that owns the plugin +* _chrome_extension_content_scripts.uid_ - The local user that owns the extension +* _chrome_extensions.uid_ - The local user that owns the extension +* _crashes.uid_ - User ID of the crashed process +* _device_file.uid_ - Owning user ID +* _disk_encryption.uid_ - Currently authenticated user if available +* _docker_container_processes.uid_ - User ID +* _es_process_events.uid_ - User ID of the process +* _file.uid_ - Owning user ID +* _file_events.uid_ - Owning user ID +* _firefox_addons.uid_ - The local user that owns the addon +* _known_hosts.uid_ - The local user that owns the known_hosts file +* _launchd_overrides.uid_ - User ID applied to the override, 0 applies to all +* _package_bom.uid_ - Expected user of file or directory +* _process_events.uid_ - User ID at process start +* _process_file_events.uid_ - The uid of the process performing the action +* _processes.uid_ - Unsigned user ID +* _safari_extensions.uid_ - The local user that owns the extension +* _seccomp_events.uid_ - User ID of the user who started the analyzed process +* _shell_history.uid_ - Shell history owner +* _ssh_configs.uid_ - The local owner of the ssh_config file +* _user_events.uid_ - User ID +* _user_groups.uid_ - User ID +* _user_ssh_keys.uid_ - The local user that owns the key file +* _users.uid_ - User ID + +*uid_signed* - keyword, number.long + +* _users.uid_signed_ - User ID as int64 signed (Apple) + +*umci_policy_status* - keyword, text.text + +* _hvci_status.umci_policy_status_ - The status of the User Mode Code Integrity security settings. Returns UNKNOWN if an error is encountered. + +*uncompressed* - keyword, number.long + +* _virtual_memory_info.uncompressed_ - Total number of uncompressed pages. + +*uninstall_string* - keyword, text.text + +* _programs.uninstall_string_ - Path and filename of the uninstaller. + +*unique_chip_id* - keyword, text.text + +* _ibridge_info.unique_chip_id_ - Unique id of the iBridge controller + +*unix_time* - keyword, number.long + +* _time.unix_time_ - Current UNIX time in the system, converted to UTC if --utc enabled + +*unmask* - keyword, number.long + +* _portage_keywords.unmask_ - If the package is unmasked + +*unused_devices* - keyword, text.text + +* _md_devices.unused_devices_ - Unused devices + +*update_source_alias* - keyword, text.text + +* _lxd_images.update_source_alias_ - Alias of image at update source server + +*update_source_certificate* - keyword, text.text + +* _lxd_images.update_source_certificate_ - Certificate for update source server + +*update_source_protocol* - keyword, text.text + +* _lxd_images.update_source_protocol_ - Protocol used for image information update and image import from source server + +*update_source_server* - keyword, text.text + +* _lxd_images.update_source_server_ - Server for image update + +*update_url* - keyword, text.text + +* _chrome_extensions.update_url_ - Extension-supplied update URI +* _safari_extensions.update_url_ - Extension-supplied update URI + +*upid* - keyword, number.long + +* _processes.upid_ - A 64bit pid that is never reused. Returns -1 if we couldn't gather them from the system. + +*uploaded_at* - keyword, text.text + +* _lxd_images.uploaded_at_ - ISO time of image upload + +*upn* - keyword, text.text + +* _logon_sessions.upn_ - The user principal name (UPN) for the owner of the logon session. + +*uppid* - keyword, number.long + +* _processes.uppid_ - The 64bit parent pid that is never reused. Returns -1 if we couldn't gather them from the system. + +*uptime* - keyword, number.long + +* _apparmor_events.uptime_ - Time of execution in system uptime +* _kernel_panics.uptime_ - System uptime at kernel panic in nanoseconds +* _process_events.uptime_ - Time of execution in system uptime +* _process_file_events.uptime_ - Time of execution in system uptime +* _seccomp_events.uptime_ - Time of execution in system uptime +* _selinux_events.uptime_ - Time of execution in system uptime +* _socket_events.uptime_ - Time of execution in system uptime +* _user_events.uptime_ - Time of execution in system uptime + +*url* - keyword, text.text + +* _curl.url_ - The url for the request +* _lxd_cluster_members.url_ - URL of the node + +*usb_address* - keyword, number.long + +* _usb_devices.usb_address_ - USB Device used address + +*usb_port* - keyword, number.long + +* _usb_devices.usb_port_ - USB Device used port + +*use* - keyword, text.text + +* _memory_arrays.use_ - Function for which the array is used +* _portage_use.use_ - USE flag which has been enabled for package + +*used_by* - keyword, text.text + +* _kernel_modules.used_by_ - Module reverse dependencies +* _lxd_networks.used_by_ - URLs for containers using this network + +*user* - keyword + +* _cpu_time.user_ - Time spent in user mode +* _cups_jobs.user_ - The user who printed the job +* _docker_container_processes.user_ - User name +* _logged_in_users.user_ - User login name +* _logon_sessions.user_ - The account name of the security principal that owns the logon session. +* _sandboxes.user_ - Sandbox owner +* _systemd_units.user_ - The configured user, if any + +*user_account* - keyword, text.text + +* _services.user_account_ - The name of the account that the service process will be logged on as when it runs. This name can be of the form Domain\UserName. If the account belongs to the built-in domain, the name can be of the form .\UserName. + +*user_account_control* - keyword, text.text + +* _windows_security_center.user_account_control_ - The health of the User Account Control (UAC) capability in Windows + +*user_action* - keyword, text.text + +* _xprotect_reports.user_action_ - Action taken by user after prompted + +*user_agent* - keyword, text.text + +* _curl.user_agent_ - The user-agent string to use for the request + +*user_capacity* - keyword, text.text + +* _smart_drive_info.user_capacity_ - Bytes of drive capacity + +*user_namespace* - keyword, text.text + +* _docker_containers.user_namespace_ - User namespace +* _process_namespaces.user_namespace_ - user namespace inode + +*user_time* - keyword, number.long + +* _osquery_schedule.user_time_ - Total user time spent executing +* _processes.user_time_ - CPU time in milliseconds spent in user space + +*user_uuid* - keyword, text.text + +* _disk_encryption.user_uuid_ - UUID of authenticated user if available + +*username* - keyword, text.text + +* _certificates.username_ - Username +* _es_process_events.username_ - Username +* _last.username_ - Entry username +* _launchd.username_ - Run this daemon or agent as this username +* _managed_policies.username_ - Policy applies only this user +* _preferences.username_ - (optional) read preferences for a specific user +* _rpm_package_files.username_ - File default username from info DB +* _shadow.username_ - Username +* _startup_items.username_ - The user associated with the startup item +* _suid_bin.username_ - Binary owner username +* _users.username_ - Username +* _windows_crashes.username_ - Username of the user who ran the crashed process + +*uses_pattern* - keyword, number.long + +* _xprotect_entries.uses_pattern_ - Uses a match pattern instead of identity + +*uts_namespace* - keyword, text.text + +* _docker_containers.uts_namespace_ - UTS namespace +* _process_namespaces.uts_namespace_ - uts namespace inode + +*uuid* - keyword, text.text + +* _block_devices.uuid_ - Block device Universally Unique Identifier +* _disk_encryption.uuid_ - Disk Universally Unique Identifier +* _disk_events.uuid_ - UUID of the volume inside DMG if available +* _managed_policies.uuid_ - Optional UUID assigned to policy set +* _osquery_extensions.uuid_ - The transient ID assigned for communication +* _osquery_info.uuid_ - Unique ID provided by the system +* _system_info.uuid_ - Unique ID provided by the system +* _users.uuid_ - User's UUID (Apple) or SID (Windows) + +*vaddr* - keyword, number.long + +* _elf_sections.vaddr_ - Section virtual address in memory +* _elf_segments.vaddr_ - Segment virtual address in memory + +*valid_from* - keyword, text.text + +* _curl_certificate.valid_from_ - Period of validity start date + +*valid_to* - keyword, text.text + +* _curl_certificate.valid_to_ - Period of validity end date + +*value* - keyword, text.text + +* _ad_config.value_ - Variable typed option value +* _augeas.value_ - The value of the configuration item +* _azure_instance_tags.value_ - The tag value +* _cpuid.value_ - Bit value or string +* _default_environment.value_ - Value of the environment variable +* _docker_container_labels.value_ - Optional label value +* _docker_image_labels.value_ - Optional label value +* _docker_network_labels.value_ - Optional label value +* _docker_volume_labels.value_ - Optional label value +* _ec2_instance_tags.value_ - Tag value +* _elf_dynamic.value_ - Tag value +* _extended_attributes.value_ - The parsed information from the attribute +* _launchd_overrides.value_ - Overridden value +* _lxd_instance_config.value_ - Configuration parameter value +* _lxd_instance_devices.value_ - Device info param value +* _managed_policies.value_ - Policy value +* _mdls.value_ - Value stored in the metadata key +* _nvram.value_ - Raw variable data +* _oem_strings.value_ - The value of the OEM string +* _osquery_flags.value_ - Flag value +* _plist.value_ - String value of most CF types +* _power_sensors.value_ - Power in Watts +* _preferences.value_ - String value of most CF types +* _process_envs.value_ - Environment variable value +* _selinux_settings.value_ - Active value. +* _smc_keys.value_ - A type-encoded representation of the key value +* _wmi_bios_info.value_ - Value of the Bios setting + +*valuetype* - keyword, text.text + +* _mdls.valuetype_ - CoreFoundation type of data stored in value + +*variable* - keyword, text.text + +* _default_environment.variable_ - Name of the environment variable + +*vbs_status* - keyword, text.text + +* _hvci_status.vbs_status_ - The status of the virtualization based security settings. Returns UNKNOWN if an error is encountered. + +*vendor* - keyword, text.text + +* _block_devices.vendor_ - Block device vendor string +* _disk_events.vendor_ - Disk event vendor string +* _hardware_events.vendor_ - Hardware device vendor +* _pci_devices.vendor_ - PCI Device vendor +* _platform_info.vendor_ - Platform code vendor +* _rpm_packages.vendor_ - Package vendor +* _usb_devices.vendor_ - USB Device vendor string + +*vendor_id* - keyword, text.text + +* _hardware_events.vendor_id_ - Hex encoded Hardware vendor identifier +* _pci_devices.vendor_id_ - Hex encoded PCI Device vendor identifier +* _usb_devices.vendor_id_ - Hex encoded USB Device vendor identifier + +*vendor_syndrome* - keyword, text.text + +* _memory_error_info.vendor_syndrome_ - Vendor specific ECC syndrome or CRC data associated with the erroneous access + +*version* - keyword, text.text + +* _alf.version_ - Application Layer Firewall version +* _apt_sources.version_ - Repository source version +* _atom_packages.version_ - Package supplied version +* _authorizations.version_ - Label top-level key +* _azure_instance_metadata.version_ - Version of the VM image +* _bitlocker_info.version_ - The FVE metadata version of the drive. +* _browser_plugins.version_ - Plugin short version +* _chocolatey_packages.version_ - Package-supplied version +* _chrome_extension_content_scripts.version_ - Extension-supplied version +* _chrome_extensions.version_ - Extension-supplied version +* _crashes.version_ - Version info of the crashed process +* _curl_certificate.version_ - Version Number +* _deb_packages.version_ - Package version +* _device_firmware.version_ - Firmware version +* _docker_version.version_ - Docker version +* _drivers.version_ - Driver version +* _elf_info.version_ - Object file version +* _es_process_events.version_ - Version of EndpointSecurity event +* _firefox_addons.version_ - Addon-supplied version string +* _gatekeeper.version_ - Version of Gatekeeper's gke.bundle +* _homebrew_packages.version_ - Current 'linked' version +* _hvci_status.version_ - The version number of the Device Guard build. +* _ie_extensions.version_ - Version of the executable +* _intel_me_info.version_ - Intel ME version +* _kernel_extensions.version_ - Extension version +* _kernel_info.version_ - Kernel version +* _npm_packages.version_ - Package supplied version +* _office_mru.version_ - Office application version number +* _os_version.version_ - Pretty, suitable for presentation, OS version +* _osquery_extensions.version_ - Extension's version +* _osquery_info.version_ - osquery toolkit version +* _osquery_packs.version_ - Minimum osquery version that this query will run on +* _package_install_history.version_ - Package display version +* _package_receipts.version_ - Installed package version +* _pkg_packages.version_ - Package version +* _platform_info.version_ - Platform code version +* _portage_keywords.version_ - The version which are affected by the use flags, empty means all +* _portage_packages.version_ - The version which are affected by the use flags, empty means all +* _portage_use.version_ - The version of the installed package +* _programs.version_ - Product version information. +* _python_packages.version_ - Package-supplied version +* _rpm_packages.version_ - Package version +* _safari_extensions.version_ - Extension long version +* _system_extensions.version_ - System extension version +* _usb_devices.version_ - USB Device version number +* _windows_crashes.version_ - File version info of the crashed process + +*video_mode* - keyword, text.text + +* _video_info.video_mode_ - The current resolution of the display. + +*virtual_process* - keyword, number.long + +* _processes.virtual_process_ - Process is virtual (e.g. System, Registry, vmmem) yes=1, no=0 + +*visible* - keyword, number.long + +* _firefox_addons.visible_ - 1 If the addon is shown in browser else 0 + +*visible_alarm* - keyword, text.text + +* _chassis_info.visible_alarm_ - If TRUE, the frame is equipped with a visual alarm. + +*vlans* - keyword, text.text + +* _lldp_neighbors.vlans_ - Comma delimited list of vlan ids + +*vm_id* - keyword, text.text + +* _azure_instance_metadata.vm_id_ - Unique identifier for the VM +* _azure_instance_tags.vm_id_ - Unique identifier for the VM + +*vm_scale_set_name* - keyword, text.text + +* _azure_instance_metadata.vm_scale_set_name_ - VM scale set name + +*vm_size* - keyword, text.text + +* _azure_instance_metadata.vm_size_ - VM size + +*voltage* - keyword, number.long + +* _battery.voltage_ - The battery's current voltage in mV + +*volume_creation* - keyword, text.text + +* _prefetch.volume_creation_ - Volume creation time. + +*volume_id* - keyword, number.long + +* _quicklook_cache.volume_id_ - Parsed volume ID from fs_id + +*volume_serial* - keyword, text.text + +* _file.volume_serial_ - Volume serial number +* _prefetch.volume_serial_ - Volume serial number. +* _shortcut_files.volume_serial_ - Volume serial number. + +*volume_size* - keyword, number.long + +* _platform_info.volume_size_ - (Optional) size of firmware volume + +*wall_time* - keyword, number.long + +* _osquery_schedule.wall_time_ - Total wall time spent executing + +*warning* - keyword, number.long + +* _shadow.warning_ - Number of days before password expires to warn user about it + +*warnings* - keyword, text.text + +* _smart_drive_info.warnings_ - Warning messages from SMART controller + +*watch_paths* - keyword, text.text + +* _launchd.watch_paths_ - Key that launches daemon or agent if path is modified + +*watcher* - keyword, number.long + +* _osquery_info.watcher_ - Process (or thread/handle) ID of optional watcher process + +*weekday* - keyword, text.text + +* _time.weekday_ - Current weekday in the system + +*win32_exit_code* - keyword, number.long + +* _services.win32_exit_code_ - The error code that the service uses to report an error that occurs when it is starting or stopping + +*win_timestamp* - keyword, number.long + +* _time.win_timestamp_ - Timestamp value in 100 nanosecond units. + +*windows_security_center_service* - keyword, text.text + +* _windows_security_center.windows_security_center_service_ - The health of the Windows Security Center Service + +*wired* - keyword, number.long + +* _virtual_memory_info.wired_ - Total number of wired down pages. + +*wired_size* - keyword, number.long + +* _docker_container_processes.wired_size_ - Bytes of unpageable memory used by process +* _processes.wired_size_ - Bytes of unpageable memory used by process + +*working_directory* - keyword, text.text + +* _launchd.working_directory_ - Key used to specify a directory to chdir to before launch + +*working_disks* - keyword, number.long + +* _md_devices.working_disks_ - Number of working disks in array + +*working_path* - keyword, text.text + +* _shortcut_files.working_path_ - Target file directory. + +*world* - keyword, number.long + +* _portage_packages.world_ - If package is in the world file + +*writable* - keyword, number.long + +* _disk_events.writable_ - 1 if writable, 0 if not + +*xpath* - keyword, text.text + +* _windows_eventlog.xpath_ - The custom query to filter events + +*year* - keyword, number.long + +* _time.year_ - Current year in the system + +*zero_fill* - keyword, number.long + +* _virtual_memory_info.zero_fill_ - Total number of zero filled pages. + +*zone* - keyword, text.text + +* _azure_instance_metadata.zone_ - Availability zone of the VM +* _ycloud_instance_metadata.zone_ - Availability zone of the VM + diff --git a/docs/osquery/osquery.asciidoc b/docs/osquery/osquery.asciidoc index ab8058027934e..9e384d79a4d6b 100644 --- a/docs/osquery/osquery.asciidoc +++ b/docs/osquery/osquery.asciidoc @@ -16,7 +16,7 @@ With Osquery in {kib}, you can: * View a history of past queries and their results * Save queries and build a library of queries for specific use cases -Osquery is powered by the *Osquery Manager* integration. +Osquery in {kib} is powered by the *Osquery Manager* integration. For information on how to set up *Osquery Manager*, refer to <>. [float] @@ -119,6 +119,53 @@ image::images/scheduled-pack.png[Shows queries in the pack and details about eac . View scheduled query results in <> or the drag-and-drop <> editor. +[float] +[[osquery-prebuilt-packs]] +== Prebuilt Elastic packs + +The Osquery Manager integration includes a set of prebuilt Osquery packs that you can optionally load. Once added, you can then activate and schedule the packs. + +You can modify the scheduled agent policies for a prebuilt pack, but you cannot edit queries in the pack. To edit the queries, you must first create a copy of the pack. + +For information about the prebuilt packs that are available, refer to <>. + +[float] +[[load-prebuilt-packs]] +=== Load and activate prebuilt Elastic packs + +. Go to *Packs*, and then click *Load Elastic prebuilt packs*. ++ +NOTE: This option is only available if new or updated prebuilt packs are available. + +. For each pack that you want to schedule: + +* Enable the option to make the pack *Active*. + +* Click the pack name, then *Edit*. + +* Update the *Scheduled agent policies* to specify the policies where this pack should run. + +. Click *Update pack*. + +[float] +[[copy-prebuilt-packs]] +=== Copy prebuilt Elastic packs + +To modify queries in prebuilt packs, you must first make a copy of the pack. + +. Go to *Stack Management* -> *Saved Objects*. + +. Search for the Osquery packs you want to modify by name. + +. Select the checkboxes of the packs to export. + +. Click *Export x objects*. + +. Click *Import*. + +. Select the import option *Create new objects with random IDs*, then click *Import* to import the pack. This creates a copy of the pack that you can edit. + + [float] [[osquery-manage-query]] == Save queries @@ -157,10 +204,113 @@ To add or edit saved queries from the *Saved queries* tab: . Click *Save* or *Update*. --- -include::advanced-osquery.asciidoc[] +[float] +[[osquery-map-fields]] +== Map result fields to ECS + +When you save queries or add queries to a pack, you can optionally map Osquery results or static values to fields in +the {ecs-ref}/ecs-reference.html[Elastic Common Schema] (ECS). +This standardizes your Osquery data for use across detections, machine learning, +and any other areas that rely on ECS-compliant data. +When the query is run, the results include the original `osquery.` +and the mapped ECS fields. For example, if you update a query to map `osquery.name` to `user.name`, the query results include both fields. + +. Edit saved queries or queries in a pack to map fields: + +* For *Saved queries*: Open the *Saved queries* tab, and then click the edit icon for the query that you want to map. + +* For *packs*: Open the *Packs* tab, edit a pack, and then click the edit icon for the query that you want to map. + +. In the **ECS mapping** section, select an **ECS field** to map. + +. In the **Value** column, use the dropdown on the left to choose what type of value to map to the ECS field: + +** **Osquery value**: Select an Osquery field. The fields available are based on the SQL query entered, and only include fields that the query returns. When the query runs, the ECS field is set dynamically to the value of the Osquery field selected. + +** **Static value**: Enter a static value. When the query runs, the ECS field is set to the value entered. For example, static fields can be used to apply `tags` or your preferred `event.category` to the query results. + +. Map more fields, as needed. To remove any mapped rows, click the delete icon. + +. Save your changes. + +[NOTE] +========================= + +* Some ECS fields are restricted and cannot be mapped. These are not available in the ECS dropdown. + +* Some ECS fields are restricted to a set of allowed values, like {ecs-ref}/ecs-event.html#field-event-category[event.category]. Use the {ecs-ref}/ecs-field-reference.html[ECS Field Reference] for help when mapping fields. + +* Osquery date fields have a variety of data types (including integer, text, or bigint). When mapping an Osquery date field to an ECS date field, you might need to use SQL operators in the query to get an {es}-compatible +{ref}/date.html[date] type. +========================= + + +[float] +[[osquery-extended-tables]] +== Extended tables for Kubernetes queries +In addition to the Osquery schema, the Elastic-provided version of Osquery also includes the following tables to support Kubernetes containers. These can be queried with live or scheduled queries. + +* `host_users` + +* `host_groups` + +* `host_processes` + +When querying these tables, the expectation is that the `/etc/passwd`, `/etc/group`, and `/proc` are available in the container under `/hostfs` as: +`/hostfs/etc/passwd`, `/hostfs/etc/group`, and `/hostfs/proc`. For information about the fields available in these tables, see the +https://docs.elastic.co/en/integrations/osquery_manager#exported-fields[exported fields] reference. + +[float] +[[osquery-status]] +== Osquery status + +A query can have the following status: + +[cols="2*<"] +|=== +| Successful | The query successfully completed. +| Failed | The query encountered a problem, such as an issue with the query or the agent was disconnected, and might have failed. +| Not yet responded | The query has not been sent to the agent. +| Expired | The action request timed out. The agent may be offline. +|=== + +NOTE: If an agent is offline, the request status remains **pending** as {kib} retries the request. +By default, a query request times out after five minutes. The time out applies to the time it takes +to deliver the action request to an agent to run a query. If the action completes after the timeout period, +the results are still returned. + + +[float] +[[osquery-results]] +== Osquery results +When you run live or scheduled queries, the results are automatically +stored in an {es} index, so that you can search, analyze, and visualize this data in {kib}. +For a list of the Osquery fields that can be returned in query results, +refer to https://docs.elastic.co/en/integrations/osquery_manager#exported-fields[exported fields]. +Query results can also include ECS fields, if the query has a defined ECS mapping. + +Osquery responses include the following information: + +* Everything prefaced with `osquery.` is part of the query response. These fields are not mapped to ECS by default. + +* Results include some ECS fields by default, such as `host.*` and `agent.*`, which provide information about the host that was queried. + +* For live queries, the `action_data.query` is the query that was sent. + +* For scheduled queries in a pack, the `action_id` has the format `pack__`. You can use this information to look up the query that was run. + +* By default, all query results are https://osquery.readthedocs.io/en/stable/deployment/logging/#snapshot-logs[snapshot logs] +that represent a point in time with a set of results, with no +https://osquery.readthedocs.io/en/stable/deployment/logging/#differential-logs[differentials]. + +* Osquery data is stored in the `logs-osquery_manager.result-` datastream, and the result row data is under the `osquery` property in the document. + + +-- include::manage-integration.asciidoc[] include::exported-fields-reference.asciidoc[] + +include::prebuilt-packs.asciidoc[] diff --git a/docs/osquery/prebuilt-packs.asciidoc b/docs/osquery/prebuilt-packs.asciidoc new file mode 100644 index 0000000000000..776a1937b7a69 --- /dev/null +++ b/docs/osquery/prebuilt-packs.asciidoc @@ -0,0 +1,63 @@ +[[prebuilt-packs]] +== Prebuilt packs reference + +This section lists all prebuilt packs available for Osquery Manager. +Each pack is also available as a saved object, with the name `Pack: `. + +For more information, refer to <>. + + +|=== +|Name |Description |Source |Added + +|`hardware-monitoring` +|Monitor for hardware changes. +|https://github.com/osquery/osquery/tree/master/packs[Osquery] +|8.2 + +|`incident-response` +|Detect and respond to breaches. +|https://github.com/osquery/osquery/tree/master/packs[Osquery] +|8.2 + +|`it-compliance` +a|Identify outdated and vulnerable software. + +Dashboard: `[Osquery Manager] Compliance pack` + +|https://github.com/osquery/osquery/tree/master/packs[Osquery] +|8.2 + +|`osquery-monitoring` +|Monitor Osquery info and performance. +|https://github.com/osquery/osquery/tree/master/packs[Osquery] +|8.2 + +|`ossec-rootkit` +a|Run rootkit detection queries to monitor for compromise. + +Dashboard: `[Osquery Manager] OSSEC rootkit pack` + +|https://github.com/osquery/osquery/tree/master/packs[Osquery] +|8.2 + +|`osx-attacks` +|Identify compromised macOS systems. +|https://github.com/osquery/osquery/tree/master/packs[Osquery] +|8.2 + +|`unwanted-chrome-extensions` +|Monitor for malicious Chrome extensions. +|https://github.com/osquery/osquery/tree/master/packs[Osquery] +|8.2 + +|`vuln-management` +|Identify system vulnerabilities. +|https://github.com/osquery/osquery/tree/master/packs[Osquery] +|8.2 + +|`windows-attacks` +|Monitor for evidence of Windows attacks. +|https://github.com/osquery/osquery/tree/master/packs[Osquery] +|8.2 +|=== diff --git a/docs/redirects.asciidoc b/docs/redirects.asciidoc index 9a96eec55db32..934ebfc3e4b08 100644 --- a/docs/redirects.asciidoc +++ b/docs/redirects.asciidoc @@ -410,3 +410,8 @@ This page has been deleted. Refer to <>. == View a document This page has been deleted. Refer to <>. + +[role="exclude",id="advanced-osquery"] +== Advanced Osquery + +This page has been deleted. Refer to <>. diff --git a/docs/settings/alert-action-settings.asciidoc b/docs/settings/alert-action-settings.asciidoc index 7579ec207c835..95003a08b7b09 100644 --- a/docs/settings/alert-action-settings.asciidoc +++ b/docs/settings/alert-action-settings.asciidoc @@ -92,7 +92,7 @@ URLs can use both the `ssl` and `smtp` options. + No other URL values should be part of this URL, including paths, query strings, and authentication information. When an http or smtp request -is made as part of executing an action, only the protocol, hostname, and +is made as part of running an action, only the protocol, hostname, and port of the URL for that request are used to look up these configuration values. @@ -126,9 +126,9 @@ into a single string. This configuration can be used for environments where the files cannot be made available. [[action-config-email-domain-allowlist]] `xpack.actions.email.domain_allowlist` {ess-icon}:: -A list of allowed email domains which can be used with the email connector. When this setting is not used, all email domains are allowed. When this setting is used, if any email is attempted to be sent that includes an addressee with an email domain that is not in the allowlist, or the from address domain is not in the allowlist, the run of the connector will fail with a message indicating the emails not allowed. +A list of allowed email domains which can be used with the email connector. When this setting is not used, all email domains are allowed. When this setting is used, if any email is attempted to be sent that (a) includes an addressee with an email domain that is not in the allowlist, or (b) includes a from address domain that is not in the allowlist, it will fail with a message indicating the email is not allowed. -WARNING: This feature is available in {kib} 7.17.4 and 8.3.0 onwards but is not supported in {kib} 8.0, 8.1 or 8.2. As such this settings should be removed before upgrading from 7.17 to 8.0, 8.1 or 8.2. It is possible to configure the settings in 7.17.4 and then upgrade to 8.3.0 directly. +WARNING: This feature is available in {kib} 7.17.4 and 8.3.0 onwards but is not supported in {kib} 8.0, 8.1 or 8.2. As such, this setting should be removed before upgrading from 7.17 to 8.0, 8.1 or 8.2. It is possible to configure the settings in 7.17.4 and then upgrade to 8.3.0 directly. `xpack.actions.enabledActionTypes` {ess-icon}:: A list of action types that are enabled. It defaults to `[*]`, enabling all types. The names for built-in {kib} action types are prefixed with a `.` and include: `.email`, `.index`, `.jira`, `.pagerduty`, `.resilient`, `.server-log`, `.servicenow`, .`servicenow-itom`, `.servicenow-sir`, `.slack`, `.swimlane`, `.teams`, `.xmatters`, and `.webhook`. An empty list `[]` will disable all action types. @@ -187,33 +187,37 @@ For example, `20m`, `24h`, `7d`, `1w`. Default: `60s`. [[alert-settings]] ==== Alerting settings -`xpack.alerting.maxEphemeralActionsPerAlert`:: -Sets the number of actions that will be executed ephemerally. To use this, enable ephemeral tasks in task manager first with <> +`xpack.alerting.maxEphemeralActionsPerAlert` {ess-icon}:: +Sets the number of actions that will run ephemerally. To use this, enable +ephemeral tasks in task manager first with +<> -`xpack.alerting.cancelAlertsOnRuleTimeout`:: -Specifies whether to skip writing alerts and scheduling actions if rule execution is cancelled due to timeout. Default: `true`. This setting can be overridden by individual rule types. +`xpack.alerting.cancelAlertsOnRuleTimeout` {ess-icon}:: +Specifies whether to skip writing alerts and scheduling actions if rule +processing was cancelled due to a timeout. Default: `true`. This setting can be +overridden by individual rule types. -`xpack.alerting.rules.minimumScheduleInterval.value`:: +`xpack.alerting.rules.minimumScheduleInterval.value` {ess-icon}:: Specifies the minimum schedule interval for rules. This minimum is applied to all rules created or updated after you set this value. The time is formatted as: + `[s,m,h,d]` + -For example, `20m`, `24h`, `7d`. Default: `1m`. +For example, `20m`, `24h`, `7d`. This duration cannot exceed `1d`. Default: `1m`. -`xpack.alerting.rules.minimumScheduleInterval.enforce`:: +`xpack.alerting.rules.minimumScheduleInterval.enforce` {ess-icon}:: Specifies the behavior when a new or changed rule has a schedule interval less than the value defined in `xpack.alerting.rules.minimumScheduleInterval.value`. If `false`, rules with schedules less than the interval will be created but warnings will be logged. If `true`, rules with schedules less than the interval cannot be created. Default: `false`. -`xpack.alerting.rules.run.actions.max`:: -Specifies the maximum number of actions that a rule can trigger each time detection checks run. +`xpack.alerting.rules.run.actions.max` {ess-icon}:: +Specifies the maximum number of actions that a rule can generate each time detection checks run. -`xpack.alerting.rules.run.timeout`:: +`xpack.alerting.rules.run.timeout` {ess-icon}:: Specifies the default timeout for tasks associated with all types of rules. The time is formatted as: + `[ms,s,m,h,d,w,M,Y]` + For example, `20m`, `24h`, `7d`, `1w`. Default: `5m`. -`xpack.alerting.rules.run.ruleTypeOverrides`:: +`xpack.alerting.rules.run.ruleTypeOverrides` {ess-icon}:: Overrides the configs under `xpack.alerting.rules.run` for the rule type with the given ID. List the rule identifier and its settings in an array of objects. + For example: @@ -226,7 +230,7 @@ xpack.alerting.rules.run: timeout: '15m' -- -`xpack.alerting.rules.run.actions.connectorTypeOverrides`:: +`xpack.alerting.rules.run.actions.connectorTypeOverrides` {ess-icon}:: Overrides the configs under `xpack.alerting.rules.run.actions` for the connector type with the given ID. List the connector type identifier and its settings in an array of objects. + For example: diff --git a/docs/settings/apm-settings.asciidoc b/docs/settings/apm-settings.asciidoc index 7441621f441f9..2cfd3169b45a3 100644 --- a/docs/settings/apm-settings.asciidoc +++ b/docs/settings/apm-settings.asciidoc @@ -38,71 +38,69 @@ If you'd like to change any of the default values, copy and paste the relevant settings into your `kibana.yml` configuration file. Changing these settings may disable features of the APM App. -[cols="2*<"] -|=== -| `xpack.apm.maxServiceEnvironments` {ess-icon} - | Maximum number of unique service environments recognized by the UI. Defaults to `100`. +`xpack.apm.maxServiceEnvironments` {ess-icon}:: +Maximum number of unique service environments recognized by the UI. Defaults to `100`. -| `xpack.apm.serviceMapFingerprintBucketSize` {ess-icon} - | Maximum number of unique transaction combinations sampled for generating service map focused on a specific service. Defaults to `100`. +`xpack.apm.serviceMapFingerprintBucketSize` {ess-icon}:: +Maximum number of unique transaction combinations sampled for generating service map focused on a specific service. Defaults to `100`. -| `xpack.apm.serviceMapFingerprintGlobalBucketSize` {ess-icon} - | Maximum number of unique transaction combinations sampled for generating the global service map. Defaults to `100`. +`xpack.apm.serviceMapFingerprintGlobalBucketSize` {ess-icon}:: +Maximum number of unique transaction combinations sampled for generating the global service map. Defaults to `100`. -| `xpack.apm.serviceMapEnabled` {ess-icon} - | Set to `false` to disable service maps. Defaults to `true`. +`xpack.apm.serviceMapEnabled` {ess-icon}:: +Set to `false` to disable service maps. Defaults to `true`. -| `xpack.apm.serviceMapTraceIdBucketSize` {ess-icon} - | Maximum number of trace IDs sampled for generating service map focused on a specific service. Defaults to `65`. +`xpack.apm.serviceMapTraceIdBucketSize` {ess-icon}:: +Maximum number of trace IDs sampled for generating service map focused on a specific service. Defaults to `65`. -| `xpack.apm.serviceMapTraceIdGlobalBucketSize` {ess-icon} - | Maximum number of trace IDs sampled for generating the global service map. Defaults to `6`. +`xpack.apm.serviceMapTraceIdGlobalBucketSize` {ess-icon}:: +Maximum number of trace IDs sampled for generating the global service map. Defaults to `6`. -| `xpack.apm.serviceMapMaxTracesPerRequest` {ess-icon} - | Maximum number of traces per request for generating the global service map. Defaults to `50`. +`xpack.apm.serviceMapMaxTracesPerRequest` {ess-icon}:: +Maximum number of traces per request for generating the global service map. Defaults to `50`. -| `xpack.apm.ui.enabled` {ess-icon} - | Set to `false` to hide the APM app from the main menu. Defaults to `true`. +`xpack.apm.ui.enabled` {ess-icon}:: +Set to `false` to hide the APM app from the main menu. Defaults to `true`. -| `xpack.apm.ui.transactionGroupBucketSize` {ess-icon} - | Number of top transaction groups displayed in the APM app. Defaults to `1000`. +`xpack.apm.ui.transactionGroupBucketSize` {ess-icon}:: +Number of top transaction groups displayed in the APM app. Defaults to `1000`. -| `xpack.apm.ui.maxTraceItems` {ess-icon} - | Maximum number of child items displayed when viewing trace details. Defaults to `1000`. +`xpack.apm.ui.maxTraceItems` {ess-icon}:: +Maximum number of child items displayed when viewing trace details. Defaults to `1000`. -| `xpack.observability.annotations.index` {ess-icon} - | Index name where Observability annotations are stored. Defaults to `observability-annotations`. +`xpack.observability.annotations.index` {ess-icon}:: +Index name where Observability annotations are stored. Defaults to `observability-annotations`. -| `xpack.apm.searchAggregatedTransactions` {ess-icon} - | Enables Transaction histogram metrics. Defaults to `auto` so the UI will use metric indices over transaction indices for transactions if aggregated transactions are found. When set to `always`, additional configuration in APM Server is required. When set to `never` and aggregated transactions are not used. - See {apm-guide-ref}/transaction-metrics.html[Configure transaction metrics] for more information. +`xpack.apm.searchAggregatedTransactions` {ess-icon}:: +Enables Transaction histogram metrics. Defaults to `auto` so the UI will use metric indices over transaction indices for transactions if aggregated transactions are found. When set to `always`, additional configuration in APM Server is required. When set to `never` and aggregated transactions are not used. ++ +See {apm-guide-ref}/transaction-metrics.html[Configure transaction metrics] for more information. -| `xpack.apm.metricsInterval` {ess-icon} - | Sets a `fixed_interval` for date histograms in metrics aggregations. Defaults to `30`. +`xpack.apm.metricsInterval` {ess-icon}:: +Sets a `fixed_interval` for date histograms in metrics aggregations. Defaults to `30`. -| `xpack.apm.agent.migrations.enabled` {ess-icon} - | Set to `false` to disable cloud APM migrations. Defaults to `true`. +`xpack.apm.agent.migrations.enabled` {ess-icon}:: +Set to `false` to disable cloud APM migrations. Defaults to `true`. -| `xpack.apm.indices.error` {ess-icon} - | Matcher for all error indices. Defaults to `logs-apm*,apm-*`. +`xpack.apm.indices.error` {ess-icon}:: +Matcher for all error indices. Defaults to `logs-apm*,apm-*`. -| `xpack.apm.indices.onboarding` {ess-icon} - | Matcher for all onboarding indices. Defaults to `apm-*`. +`xpack.apm.indices.onboarding` {ess-icon}:: +Matcher for all onboarding indices. Defaults to `apm-*`. -| `xpack.apm.indices.span` {ess-icon} - | Matcher for all span indices. Defaults to `traces-apm*,apm-*`. +`xpack.apm.indices.span` {ess-icon}:: +Matcher for all span indices. Defaults to `traces-apm*,apm-*`. -| `xpack.apm.indices.transaction` {ess-icon} - | Matcher for all transaction indices. Defaults to `traces-apm*,apm-*`. +`xpack.apm.indices.transaction` {ess-icon}:: +Matcher for all transaction indices. Defaults to `traces-apm*,apm-*`. -| `xpack.apm.indices.metric` {ess-icon} - | Matcher for all metrics indices. Defaults to `metrics-apm*,apm-*`. +`xpack.apm.indices.metric` {ess-icon}:: +Matcher for all metrics indices. Defaults to `metrics-apm*,apm-*`. -| `xpack.apm.indices.sourcemap` {ess-icon} - | Matcher for all source map indices. Defaults to `apm-*`. +`xpack.apm.indices.sourcemap` {ess-icon}:: +Matcher for all source map indices. Defaults to `apm-*`. -| `xpack.apm.autoCreateApmDataView` {ess-icon} - | Set to `false` to disable the automatic creation of the APM data view when the APM app is opened. Defaults to `true`. -|=== +`xpack.apm.autoCreateApmDataView` {ess-icon}:: +Set to `false` to disable the automatic creation of the APM data view when the APM app is opened. Defaults to `true`. // end::general-apm-settings[] diff --git a/docs/settings/fleet-settings.asciidoc b/docs/settings/fleet-settings.asciidoc index 5ddf45887a530..ddce9feb3e640 100644 --- a/docs/settings/fleet-settings.asciidoc +++ b/docs/settings/fleet-settings.asciidoc @@ -18,104 +18,140 @@ See the {fleet-guide}/index.html[{fleet}] docs for more information. [[general-fleet-settings-kb]] ==== General {fleet} settings -[cols="2*<"] -|=== -| `xpack.fleet.agents.enabled` {ess-icon} - | Set to `true` (default) to enable {fleet}. -|=== +`xpack.fleet.agents.enabled` {ess-icon}:: +Set to `true` (default) to enable {fleet}. + [[fleet-data-visualizer-settings]] ==== {package-manager} settings -[cols="2*<"] -|=== -| `xpack.fleet.registryUrl` - | The address to use to reach the {package-manager} registry. -| `xpack.fleet.registryProxyUrl` - | The proxy address to use to reach the {package-manager} registry if an internet connection is not directly available. - Refer to {fleet-guide}/air-gapped.html[Air-gapped environments] for details. +`xpack.fleet.registryUrl`:: +The address to use to reach the {package-manager} registry. + +`xpack.fleet.registryProxyUrl`:: +The proxy address to use to reach the {package-manager} registry if an internet connection is not directly available. +Refer to {fleet-guide}/air-gapped.html[Air-gapped environments] for details. -|=== ==== {fleet} settings -[cols="2*<"] -|=== -| `xpack.fleet.agents.fleet_server.hosts` - | Hostnames used by {agent} for accessing {fleet-server}. -| `xpack.fleet.agents.elasticsearch.hosts` - | Hostnames used by {agent} for accessing {es}. -| `xpack.fleet.agents.elasticsearch.ca_sha256` - | Hash pin used for certificate verification. The pin is a base64-encoded - string of the SHA-256 fingerprint. -|=== +`xpack.fleet.agents.fleet_server.hosts`:: +Hostnames used by {agent} for accessing {fleet-server}. + +`xpack.fleet.agents.elasticsearch.hosts`:: +Hostnames used by {agent} for accessing {es}. +`xpack.fleet.agents.elasticsearch.ca_sha256`:: +Hash pin used for certificate verification. The pin is a base64-encoded string of the SHA-256 fingerprint. + +[role="child_attributes"] ==== Preconfiguration settings (for advanced use cases) Use these settings to pre-define integrations and agent policies that you want {fleet} to load up by default. -[cols="2*>. Defaults to `false` for all operating systems except Debian and Red Hat Linux, which use `true`. -`xpack.reporting.capture.browser.chromium.proxy.enabled`:: -Enables the proxy for Chromium to use. When set to `true`, you must also specify the `xpack.reporting.capture.browser.chromium.proxy.server` setting. Defaults to `false`. +`xpack.screenshotting.browser.chromium.proxy.enabled`:: +Enables the proxy for Chromium to use. When set to `true`, you must also specify the `xpack.screenshotting.browser.chromium.proxy.server` setting. Defaults to `false`. -`xpack.reporting.capture.browser.chromium.proxy.server`:: +`xpack.screenshotting.browser.chromium.proxy.server`:: The uri for the proxy server. Providing the username and password for the proxy server via the uri is not supported. -`xpack.reporting.capture.browser.chromium.proxy.bypass`:: +`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] @@ -136,13 +136,13 @@ If the Chromium browser is asked to send a request that violates the network pol 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. -`xpack.reporting.capture.networkPolicy`:: +`xpack.screenshotting.networkPolicy`:: 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.reporting.capture.networkPolicy.enabled`:: +`xpack.screenshotting.networkPolicy.enabled`:: When `false`, disables the *Reporting* network policy. Defaults to `true`. -`xpack.reporting.capture.networkPolicy.rules`:: +`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. The rule objects are evaluated sequentially from the beginning to the end of the array, and continue until there is a matching rule. If no rules allow a request, the request is denied. @@ -150,14 +150,14 @@ The rule objects are evaluated sequentially from the beginning to the end of the [source,yaml] ------------------------------------------------------- # Only allow requests to placeholder.com -xpack.reporting.capture.networkPolicy: +xpack.screenshotting.networkPolicy: rules: [ { allow: true, host: "placeholder.com" } ] ------------------------------------------------------- [source,yaml] ------------------------------------------------------- # Only allow requests to https://placeholder.com -xpack.reporting.capture.networkPolicy: +xpack.screenshotting.networkPolicy: rules: [ { allow: true, host: "placeholder.com", protocol: "https:" } ] ------------------------------------------------------- @@ -166,7 +166,7 @@ A final `allow` rule with no host or protocol allows all requests that are not e [source,yaml] ------------------------------------------------------- # Denies requests from http://placeholder.com, but anything else is allowed. -xpack.reporting.capture.networkPolicy: +xpack.screenshotting.networkPolicy: rules: [{ allow: false, host: "placeholder.com", protocol: "http:" }, { allow: true }]; ------------------------------------------------------- @@ -175,7 +175,7 @@ A network policy can be composed of multiple rules: [source,yaml] ------------------------------------------------------- # Allow any request to http://placeholder.com but for any other host, https is required -xpack.reporting.capture.networkPolicy +xpack.screenshotting.networkPolicy rules: [ { allow: true, host: "placeholder.com", protocol: "http:" }, { allow: true, protocol: "https:" }, diff --git a/docs/settings/search-sessions-settings.asciidoc b/docs/settings/search-sessions-settings.asciidoc index 7b03cd23a9023..7628ecfb397e1 100644 --- a/docs/settings/search-sessions-settings.asciidoc +++ b/docs/settings/search-sessions-settings.asciidoc @@ -7,26 +7,26 @@ Configure the search session settings in your `kibana.yml` configuration file. -`xpack.data_enhanced.search.sessions.enabled` {ess-icon}:: +`data.search.sessions.enabled` {ess-icon}:: Set to `true` (default) to enable search sessions. -`xpack.data_enhanced.search.sessions.trackingInterval` {ess-icon}:: +`data.search.sessions.trackingInterval` {ess-icon}:: The frequency for updating the state of a search session. The default is `10s`. -`xpack.data_enhanced.search.sessions.pageSize` {ess-icon}:: +`data.search.sessions.pageSize` {ess-icon}:: How many search sessions {kib} processes at once while monitoring session progress. The default is `100`. -`xpack.data_enhanced.search.sessions.notTouchedTimeout` {ess-icon}:: +`data.search.sessions.notTouchedTimeout` {ess-icon}:: How long {kib} stores search results from unsaved sessions, after the last search in the session completes. The default is `5m`. -`xpack.data_enhanced.search.sessions.notTouchedInProgressTimeout` {ess-icon}:: +`data.search.sessions.notTouchedInProgressTimeout` {ess-icon}:: How long a search session can run after a user navigates away without saving a session. The default is `1m`. -`xpack.data_enhanced.search.sessions.maxUpdateRetries` {ess-icon}:: +`data.search.sessions.maxUpdateRetries` {ess-icon}:: How many retries {kib} can perform while attempting to save a search session. The default is `3`. -`xpack.data_enhanced.search.sessions.defaultExpiration` {ess-icon}:: +`data.search.sessions.defaultExpiration` {ess-icon}:: How long search session results are stored before they are deleted. Extending a search session resets the expiration by the same value. The default is `7d`. diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 2d52abc1f0138..79421e1ac90b5 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -500,7 +500,7 @@ send on all responses to the client from the {kib} server. *Default: `{}`* |[[server-host]] `server.host:` | This setting specifies the host of the -back end server. To allow remote users to connect, set the value to the IP address or DNS name of the {kib} server. *Default: `"localhost"`* +back end server. To allow remote users to connect, set the value to the IP address or DNS name of the {kib} server. Use `0.0.0.0` to make Kibana listen on all IPs (public and private). *Default: `"localhost"`* | `server.keepaliveTimeout:` | The number of milliseconds to wait for additional data before restarting @@ -543,10 +543,6 @@ inactive socket. *Default: `"120000"`* | Paths to a PEM-encoded X.509 server certificate and its corresponding private key. These are used by {kib} to establish trust when receiving inbound SSL/TLS connections from users. -|[[server-uuid]] `server.uuid:` - | The unique identifier for this {kib} instance. - - |=== [NOTE] @@ -627,6 +623,9 @@ all http requests to https over the port configured as <> for more information. +The intervals of rule checks in {kib} are approximate. Their timing is affected by factors such as the frequency at which tasks are claimed and the task load on the system. Refer to <> for more information. ============================================== [float] @@ -82,7 +82,7 @@ The result is a template: all the parameters needed to invoke a service are supp 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`. -When the rule detects the condition, it creates an <> containing the details of the condition, renders the template with these details such as server name, and executes the action on the {kib} server by invoking the `email` connector type. +When the rule detects the condition, it creates an <> containing the details of the condition, renders the template with these details such as server name, and runs the action on the {kib} server by invoking the `email` connector type. image::images/what-is-an-action.svg[Actions are like templates that are rendered when an alert detects a condition] diff --git a/docs/user/alerting/alerting-setup.asciidoc b/docs/user/alerting/alerting-setup.asciidoc index 6f8caedde3e18..2b92e8caa7ef9 100644 --- a/docs/user/alerting/alerting-setup.asciidoc +++ b/docs/user/alerting/alerting-setup.asciidoc @@ -62,7 +62,7 @@ Rules and connectors are isolated to the {kib} space in which they were created. [[alerting-authorization]] === Authorization -Rules are authorized using an <> associated with the last user to edit the rule. This API key captures a snapshot of the user's privileges at the time of edit and is subsequently used to run all background tasks associated with the rule, including condition checks, like {es} queries, and action executions. The following rule actions will re-generate the API key: +Rules are authorized using an <> associated with the last user to edit the rule. This API key captures a snapshot of the user's privileges at the time of edit and is subsequently used to run all background tasks associated with the rule, including condition checks like {es} queries and triggered actions. The following rule actions will re-generate the API key: * Creating a rule * Enabling a disabled rule diff --git a/docs/user/alerting/alerting-troubleshooting.asciidoc b/docs/user/alerting/alerting-troubleshooting.asciidoc index 5f3c566e82d42..32c77d7fa57a7 100644 --- a/docs/user/alerting/alerting-troubleshooting.asciidoc +++ b/docs/user/alerting/alerting-troubleshooting.asciidoc @@ -1,11 +1,7 @@ -[role="xpack"] [[alerting-troubleshooting]] -== Troubleshooting -++++ -Troubleshooting -++++ +== Troubleshooting and limitations -Alerting provides many options for diagnosing problems with Rules and Connectors. +Alerting provides many options for diagnosing problems with rules and connectors. [float] [[alerting-kibana-log]] @@ -56,7 +52,7 @@ Diagnosing these may be difficult - but there may be log messages for error cond === Use the REST APIs There is a rich set of HTTP endpoints to introspect and manage rules and connectors. -One of the http endpoints available for actions is the POST <>. You can use this to “test” an action. For instance, if you have a server log action created, you can execute it via curling the endpoint: +One of the http endpoints available for actions is the POST <>. You can use this to “test” an action. For instance, if you have a server log action created, you can run it via curling the endpoint: [source, txt] -------------------------------------------------- curl -X POST -k \ @@ -79,13 +75,13 @@ The same REST POST _execute API command will be: kbn-action execute a692dc89-15b9-4a3c-9e47-9fb6872e49ce ‘{"params":{"subject":"hallo","message":"hallo!","to":["me@example.com"]}}’ -------------------------------------------------- -The result of this http request (and printed to stdout by https://github.com/pmuellr/kbn-action[kbn-action]) will be data returned by the action execution, along with error messages if errors were encountered. +The result of this http request (and printed to stdout by https://github.com/pmuellr/kbn-action[kbn-action]) will be data returned by the action, along with error messages if errors were encountered. [float] [[alerting-error-banners]] === Look for error banners -The Rule Management and Rule Details pages contain an error banner, which helps to identify the errors for the rules: +The *Rule Management* and *Rule Details* pages contain an error banner, which helps to identify the errors for the rules: [role="screenshot"] image::images/rules-management-health.png[Rule management page with the errors banner] @@ -96,14 +92,14 @@ image::images/rules-details-health.png[Rule details page with the errors banner] [[task-manager-diagnostics]] === Task Manager diagnostics -Under the hood, Rules and Connectors uses a plugin called Task Manager, which handles the scheduling, execution, and error handling of the tasks. -This means that failure cases in Rules or Connectors will, at times, be revealed by the Task Manager mechanism, rather than the Rules mechanism. +Under the hood, {rules-ui} uses a plugin called Task Manager, which handles the scheduling, running, and error handling of the tasks. +This means that failure cases in {rules-ui} will, at times, be revealed by the Task Manager mechanism, rather than the Rules mechanism. Task Manager provides a visible status which can be used to diagnose issues and is very well documented <> and <>. Task Manager uses the `.kibana_task_manager` index, an internal index that contains all the saved objects that represent the tasks in the system. [float] -==== Getting from a Rule to its Task +==== Getting from a rule to its task When a rule is created, a task is created, scheduled to run at the interval specified. For example, when a rule is created and configured to check every 5 minutes, then the underlying task will be expected to run every 5 minutes. In practice, after each time the rule runs, the task is scheduled to run again in 5 minutes, rather than being scheduled to run every 5 minutes indefinitely. If you use the <> to fetch the underlying rule, you’ll get an object like so: @@ -190,12 +186,28 @@ When diagnosing the health state of the task, you will most likely be interested Investigating the underlying task can help you gauge whether the problem you’re seeing is rooted in the rule not running at all, whether it’s running and failing, or whether it is running, but exhibiting behavior that is different than what was expected (at which point you should focus on the rule itself, rather than the task). -In addition to the above methods, broadly used the next approaches and common issues: +In addition to the above methods, refer to the following approaches and common issues: * <> * <> * <> +[discrete] +[[alerting-limitations]] +=== Limitations + +The following limitations and known problems apply to the {version} release of +the {kib} {alert-features}. + +[discrete] +==== Alert visibility + +If you create a rule in the {observability} or {security-app}, its alerts are +not visible in *{stack-manage-app} > {rules-ui}*. You can view them only in the +{kib} app where you created the rule. If you use the +<>, the visibility of the alerts is related to +the `consumer` property. + include::troubleshooting/alerting-common-issues.asciidoc[] include::troubleshooting/event-log-index.asciidoc[] include::troubleshooting/testing-connectors.asciidoc[] diff --git a/docs/user/alerting/create-and-manage-rules.asciidoc b/docs/user/alerting/create-and-manage-rules.asciidoc index ba1629abd9c86..52db2ed51217e 100644 --- a/docs/user/alerting/create-and-manage-rules.asciidoc +++ b/docs/user/alerting/create-and-manage-rules.asciidoc @@ -44,7 +44,7 @@ Notify:: This value limits how often actions are repeated when an alert rem [[alerting-concepts-suppressing-duplicate-notifications]] [NOTE] ============================================== -Since actions are executed per alert, a rule can end up generating a large number of actions. Take the following example where a rule is monitoring three servers every minute for CPU usage > 0.9, and the rule is set to notify **Every time alert is active**: +Since actions are triggered per alert, a rule can end up generating a large number of actions. Take the following example where a rule is monitoring three servers every minute for CPU usage > 0.9, and the rule is set to notify **Every time alert is active**: * Minute 1: server X123 > 0.9. *One email* is sent for server X123. * Minute 2: X123 and Y456 > 0.9. *Two emails* are sent, one for X123 and one for Y456. @@ -163,8 +163,8 @@ A rule can have one of the following statuses: `active`:: The conditions for the rule have been met, and the associated actions should be invoked. `ok`:: The conditions for the rule have not been met, and the associated actions are not invoked. -`error`:: An error was encountered during rule execution. -`pending`:: The rule has not yet executed. The rule was either just created, or enabled after being disabled. +`error`:: An error was encountered by the rule. +`pending`:: The rule has not yet run. The rule was either just created, or enabled after being disabled. `unknown`:: A problem occurred when calculating the status. Most likely, something went wrong with the alerting code. [float] diff --git a/docs/user/alerting/rule-types.asciidoc b/docs/user/alerting/rule-types.asciidoc index 324347f3b2648..120c580330b9f 100644 --- a/docs/user/alerting/rule-types.asciidoc +++ b/docs/user/alerting/rule-types.asciidoc @@ -41,6 +41,12 @@ see {subscriptions}[the subscription page]. Observability rules are categorized into APM and User Experience, Logs, Metrics, Stack Monitoring, and Uptime. +[NOTE] +============================================== +If you create a rule in the {observability} app, its alerts are not visible in +*{stack-manage-app} > {rules-ui}*. They are visible only in the {observability} app. +============================================== + [cols="2*<"] |=== @@ -72,7 +78,13 @@ beta:[] {ml-docs}/ml-configuring-alerts.html[{ml-cap} rules] run scheduled check [[security-rules]] === Security rules -Security rules detect suspicious source events with pre-built or custom rules and create alerts when a rule’s conditions are met. For more information, refer to {security-guide}/prebuilt-rules.html[Security rules]. +Security rules detect suspicious source events with pre-built or custom rules and create alerts when a rule's conditions are met. For more information, refer to {security-guide}/prebuilt-rules.html[Security rules]. + +[NOTE] +============================================== +Alerts associated with security rules are visible only in the {security-app}; +they are not visible in *{stack-manage-app} > {rules-ui}*. +============================================== include::rule-types/index-threshold.asciidoc[] include::rule-types/es-query.asciidoc[] diff --git a/docs/user/alerting/rule-types/es-query.asciidoc b/docs/user/alerting/rule-types/es-query.asciidoc index dba8a4878cb26..c8f98808ca552 100644 --- a/docs/user/alerting/rule-types/es-query.asciidoc +++ b/docs/user/alerting/rule-types/es-query.asciidoc @@ -26,7 +26,7 @@ Index:: Specifies an *index or data view* and a *time field* that is used for the *time window*. Size:: Specifies the number of documents to pass to the configured actions when the threshold condition is met. -{es} query:: Specifies the ES DSL query to execute. The number of documents that +{es} query:: Specifies the ES DSL query. The number of documents that match this query is evaluated against the threshold condition. Only the `query` field is used, other DSL fields are not considered. Threshold:: Defines a threshold value and a comparison operator (`is above`, @@ -81,7 +81,7 @@ image::images/rule-types-es-query-example-action-variable.png[Iterate over hits Use the *Test query* feature to verify that your query DSL is valid. -* Valid queries are executed against the configured *index* using the configured +* Valid queries are run against the configured *index* using the configured *time window*. The number of documents that match the query is displayed. + [role="screenshot"] @@ -95,16 +95,14 @@ image::user/alerting/images/rule-types-es-query-invalid.png[Test {es} query show [float] ==== Handling multiple matches of the same document -This rule type checks for duplication of document matches across rule -executions. If you configure the rule with a schedule interval smaller than the -time window, and a document matches a query in multiple rule executions, it is -alerted on only once. +This rule type checks for duplication of document matches across multiple runs. +If you configure the rule with a schedule interval smaller than the time window, +and a document matches a query in multiple runs, it is alerted on only once. The rule uses the timestamp of the matches to avoid alerting on the same match multiple times. The timestamp of the latest match is used for evaluating the -rule conditions when the rule is executed. Only matches between the latest -timestamp from the previous execution and the actual rule execution are -considered. +rule conditions when the rule runs. Only matches between the latest timestamp +from the previous run and the current run are considered. Suppose you have a rule configured to run every minute. The rule uses a time window of 1 hour and checks if there are more than 99 matches for the query. The @@ -112,16 +110,16 @@ window of 1 hour and checks if there are more than 99 matches for the query. The [cols="3*<"] |=== -| `Execution 1 (0:00)` +| `Run 1 (0:00)` | Rule finds 113 matches in the last hour: `113 > 99` | Rule is active and user is alerted. -| `Execution 2 (0:01)` +| `Run 2 (0:01)` | Rule finds 127 matches in the last hour. 105 of the matches are duplicates that were already alerted on previously, so you actually have 22 matches: `22 !> 99` | No alert. -| `Execution 3 (0:02)` +| `Run 3 (0:02)` | Rule finds 159 matches in the last hour. 88 of the matches are duplicates that were already alerted on previously, so you actually have 71 matches: `71 !> 99` | No alert. -| `Execution 4 (0:03)` +| `Run 4 (0:03)` | Rule finds 190 matches in the last hour. 71 of them are duplicates that were already alerted on previously, so you actually have 119 matches: `119 > 99` | Rule is active and user is alerted. |=== \ No newline at end of file diff --git a/docs/user/alerting/rule-types/index-threshold.asciidoc b/docs/user/alerting/rule-types/index-threshold.asciidoc index c65b0f66b1b63..03f855a861022 100644 --- a/docs/user/alerting/rule-types/index-threshold.asciidoc +++ b/docs/user/alerting/rule-types/index-threshold.asciidoc @@ -52,7 +52,7 @@ In this example, you will use the {kib} < Rules and Connectors**. -. Create a new rule that is checked every four hours and executes actions when the rule status changes. +. Create a new rule that is checked every four hours and triggers actions when the rule status changes. + [role="screenshot"] image::user/alerting/images/rule-types-index-threshold-select.png[Choosing an index threshold rule type] diff --git a/docs/user/alerting/troubleshooting/alerting-common-issues.asciidoc b/docs/user/alerting/troubleshooting/alerting-common-issues.asciidoc index 7ab34dacacd98..75a158e6d364f 100644 --- a/docs/user/alerting/troubleshooting/alerting-common-issues.asciidoc +++ b/docs/user/alerting/troubleshooting/alerting-common-issues.asciidoc @@ -40,35 +40,34 @@ When diagnosing issues related to alerting, focus on the tasks that begin with ` Alerting tasks always begin with `alerting:`. For example, the `alerting:.index-threshold` tasks back the <>. Action tasks always begin with `actions:`. For example, the `actions:.index` tasks back the <>. -For more details on monitoring and diagnosing task execution in Task Manager, see <>. +For more details on monitoring and diagnosing tasks in Task Manager, refer to <>. [float] [[connector-tls-settings]] -==== Connectors have TLS errors when executing actions +==== Connectors have TLS errors when running actions *Problem* -When executing actions, a connector gets a TLS socket error when connecting to -the server. +A connector gets a TLS socket error when connecting to the server to run an action. *Solution* Configuration options are available to specialize connections to TLS servers, -including ignoring server certificate validation, and providing certificate -authority data to verify servers using custom certificates. For more details, -see <>. +including ignoring server certificate validation and providing certificate +authority data to verify servers using custom certificates. For more details, +see <>. [float] -[[rules-long-execution-time]] +[[rules-long-run-time]] ==== Rules take a long time to run *Problem* -Rules are taking a long time to execute and are impacting the overall health of your deployment. +Rules are taking a long time to run and are impacting the overall health of your deployment. [IMPORTANT] ============================================== -By default, only users with a `superuser` role can query the experimental[] {kib} event log because it is a system index. To enable additional users to execute this query, assign `read` privileges to the `.kibana-event-log*` index. +By default, only users with a `superuser` role can query the experimental[] {kib} event log because it is a system index. To enable additional users to run this query, assign `read` privileges to the `.kibana-event-log*` index. ============================================== *Solution* @@ -87,9 +86,9 @@ image::images/rule-details-timeout-error.png[Rule details page with timeout erro If you want your rules to run longer, update the `xpack.alerting.rules.run.timeout` configuration in your <>. You can also target a specific rule type by using `xpack.alerting.rules.run.ruleTypeOverrides`. -Rules that consistently run longer than their <> may produce unexpected results. If the average run duration, visible on the <>, is greater than the check interval, consider increasing the check interval. +Rules that consistently run longer than their <> may produce unexpected results. If the average run duration, visible on the <>, is greater than the check interval, consider increasing the check interval. -To get all long-running rules, you can query for a list of rule ids, bucketed by their execution times: +To get all long-running rules, you can query for a list of rule ids, bucketed by their run times: [source,console] -------------------------------------------------- @@ -160,9 +159,9 @@ GET /.kibana-event-log*/_search -------------------------------------------------- // TEST -<1> This queries for rules executed in the last day. Update the values of `lte` and `gte` to query over a different time range. -<2> Use `event.provider: actions` to query for long-running action executions. -<3> Execution durations are stored as nanoseconds. This adds a runtime field to convert that duration into seconds. +<1> This queries for rules run in the last day. Update the values of `lte` and `gte` to query over a different time range. +<2> Use `event.provider: actions` to query for long-running actions. +<3> Run durations are stored as nanoseconds. This adds a runtime field to convert that duration into seconds. <4> This interval buckets the `event.duration_in_seconds` runtime field into 1 second intervals. Update this value to change the granularity of the buckets. If you are unable to use runtime fields, make sure this aggregation targets `event.duration` and use nanoseconds for the interval. <5> This retrieves the top 10 rule ids for this duration interval. Update this value to retrieve more rule ids. @@ -237,10 +236,10 @@ This query returns the following: } } -------------------------------------------------- -<1> Most rule execution durations fall within the first bucket (0 - 1 seconds). -<2> A single rule with id `41893910-6bca-11eb-9e0d-85d233e3ee35` took between 30 and 31 seconds to execute. +<1> Most run durations fall within the first bucket (0 - 1 seconds). +<2> A single rule with id `41893910-6bca-11eb-9e0d-85d233e3ee35` took between 30 and 31 seconds to run. -Use the <> to retrieve additional information about rules that take a long time to execute. +Use the <> to retrieve additional information about rules that take a long time to run. [float] [[rule-cannot-decrypt-api-key]] @@ -248,11 +247,11 @@ Use the <> to retrieve additional information about r *Problem*: -The rule fails to execute and has an `Unable to decrypt attribute "apiKey"` error. +The rule fails to run and has an `Unable to decrypt attribute "apiKey"` error. *Solution*: -This error happens when the `xpack.encryptedSavedObjects.encryptionKey` value used to create the rule does not match the value used during rule execution. Depending on the scenario, there are different ways to solve this problem: +This error happens when the `xpack.encryptedSavedObjects.encryptionKey` value used to create the rule does not match the value used when the rule runs. Depending on the scenario, there are different ways to solve this problem: [cols="2*<"] |=== diff --git a/docs/user/alerting/troubleshooting/event-log-index.asciidoc b/docs/user/alerting/troubleshooting/event-log-index.asciidoc index 5016b6d6f19c9..a0e6cd11ed184 100644 --- a/docs/user/alerting/troubleshooting/event-log-index.asciidoc +++ b/docs/user/alerting/troubleshooting/event-log-index.asciidoc @@ -6,15 +6,16 @@ experimental[] Use the event log index to determine: -* Whether a rule successfully ran but its associated actions did not +* Whether a rule ran successfully but its associated actions did not * Whether a rule was ever activated -* Additional information about rule execution errors -* Duration times for rule and action executions +* Additional information about errors when the rule ran +* Run durations for the rules and actions [float] -==== Example Event Log Queries +==== Example event log queries + +The following event log query looks at all events related to a specific rule id: -Event log query to look at all event related to a specific rule id: [source, txt] -------------------------------------------------- GET /.kibana-event-log*/_search @@ -77,7 +78,9 @@ GET /.kibana-event-log*/_search } -------------------------------------------------- -Event log query to look at all events related to executing a rule or action. These events include duration. +The following event log query looks at all events related to running a rule or +action. These events include duration: + [source, txt] -------------------------------------------------- GET /.kibana-event-log*/_search @@ -124,8 +127,10 @@ GET /.kibana-event-log*/_search } -------------------------------------------------- -Event log query to look at the errors. -You should see an `error.message` property in that event, with a message from the action executor that might provide more detail on why the action encountered an error: +The following event log query looks at the errors. You should see an +`error.message` property in that event, with a message that might provide more +details about why the action encountered an error: + [source, txt] -------------------------------------------------- { @@ -150,7 +155,9 @@ You should see an `error.message` property in that event, with a message from th } -------------------------------------------------- -And see the errors for the rules you might provide the next search query: +You might also see the errors for the rules, which can use in the next search +query. For example: + [source, txt] -------------------------------------------------- { diff --git a/docs/user/alerting/troubleshooting/testing-connectors.asciidoc b/docs/user/alerting/troubleshooting/testing-connectors.asciidoc index 64ba106655321..fd5a897dfd4c3 100644 --- a/docs/user/alerting/troubleshooting/testing-connectors.asciidoc +++ b/docs/user/alerting/troubleshooting/testing-connectors.asciidoc @@ -15,9 +15,10 @@ image::user/alerting/images/email-connector-test.png[Rule management page with t image::user/alerting/images/teams-connector-test.png[Five clauses define the condition to detect] [float] -==== experimental[] Troubleshooting Connectors with `kbn-action` tool +==== experimental[] Troubleshooting connectors with the `kbn-action` tool -Executing an Email action via https://github.com/pmuellr/kbn-action[kbn-action]. In this example, is using a cloud deployment of the stack: +You can run an email action via https://github.com/pmuellr/kbn-action[kbn-action]. +In this example, it is a Cloud deployment of the {stack}: [source, txt] -------------------------------------------------- @@ -44,7 +45,8 @@ $ kbn-action ls } ] -------------------------------------------------- -and then execute this: + +You can then run the following test: [source, txt] -------------------------------------------------- diff --git a/docs/user/dashboard/vega-reference.asciidoc b/docs/user/dashboard/vega-reference.asciidoc index b593eacf703fb..95b1ffb27b476 100644 --- a/docs/user/dashboard/vega-reference.asciidoc +++ b/docs/user/dashboard/vega-reference.asciidoc @@ -242,12 +242,17 @@ experimental[] Access the Elastic Map Service files via the same mechanism: url: { // "type" defaults to "elasticsearch" otherwise %type%: emsfile - // Name of the file, exactly as in the Region map visualization + // Name of the file, exactly as in https://maps.elastic.co name: World Countries } -// The result is a geojson file, get its features to use -// this data source with the "shape" marks +// The result is either a topojson file or a geojson file. +// Refer to the Default format for the file at https://maps.elastic.co +// Get its features to use this data source with the "shape" marks // https://vega.github.io/vega/docs/marks/shape/ +// For a topojson file use +format: {type: "topojson", feature: "data"} + +// For a geojson file use format: {property: "features"} ---- @@ -310,16 +315,28 @@ experimental[] You can use the *Vega* https://vega.github.io/vega/docs/data/[dat [source,yaml] ---- -url: { - // "type" defaults to "elasticsearch" otherwise - %type%: emsfile - // Name of the file, exactly as in the Region map visualization - name: World Countries -} -// The result is a geojson file, get its features to use -// this data source with the "shape" marks -// https://vega.github.io/vega/docs/marks/shape/ -format: {property: "features"} + "data": [ + { + "name": "countries", + "url": { + // "type" defaults to "elasticsearch" otherwise + %type%: emsfile + // Name of the file, exactly as in the Region map visualization + name: World Countries + }, + // The result is a topojson file, get its features to use + // this data source with the "shape" marks + // https://vega.github.io/vega/docs/marks/shape/ + "format": {"type": "topojson", "feature": "data"}, + } + ], + "marks": [ + { + "type": "shape", + "from": {"data": "countries"}, + "transform": [{"type": "geoshape", "projection": "projection"}] + } + ] ---- [float] diff --git a/docs/user/production-considerations/alerting-production-considerations.asciidoc b/docs/user/production-considerations/alerting-production-considerations.asciidoc index 42f9a17cc6f88..f94ec1c3d04bb 100644 --- a/docs/user/production-considerations/alerting-production-considerations.asciidoc +++ b/docs/user/production-considerations/alerting-production-considerations.asciidoc @@ -64,3 +64,50 @@ Because {kib} uses the documents to display historic data, you should set the de For more information on index lifecycle management, see: {ref}/index-lifecycle-management.html[Index Lifecycle Policies]. + +[float] +[[alerting-circuit-breakers]] +=== Circuit breakers + +There are several scenarios where running alerting rules and actions can start to negatively impact the overall health of a {kib} instance either by clogging up Task Manager throughput or by consuming so much CPU/memory that other operations cannot complete in a reasonable amount of time. There are several <> circuit breakers to help minimize these effects. + +[float] +==== Rules with very short intervals + +Running large numbers of rules at very short intervals can quickly clog up Task Manager throughput, leading to higher schedule drift. Use `xpack.alerting.rules.minimumScheduleInterval.value` to set a minimum schedule interval for rules. The default (and recommended) value for this configuration is `1m`. Use `xpack.alerting.rules.minimumScheduleInterval.enforce` to specify whether to strictly enforce this minimum. While the default value for this setting is `false` to maintain backwards compatibility with existing rules, set this to `true` to prevent new and updated rules from running at an interval below the minimum. + +[float] +==== Rules that run for a long time + +Rules that run for a long time typically do so because they are issuing resource-intensive {es} queries or performing CPU-intensive processing. This can block the event loop, making {kib} inaccessible while the rule runs. By default, rule processing is cancelled after `5m` but this can be overriden using the `xpack.alerting.rules.run.timeout` configuration. This value can also be configured per rule type using `xpack.alerting.rules.run.ruleTypeOverrides`. For example, the following configuration sets the global timeout value to `1m` while allowing *Index Threshold* rules to run for `10m` before being cancelled. + +[source,yaml] +-- +xpack.alerting.rules.run: + timeout: '1m' + ruleTypeOverrides: + - id: '.index-threshold' + timeout: '10m' +-- + +When a rule run is cancelled, any alerts and actions that were generated during the run are discarded. This behavior is controlled by the `xpack.alerting.cancelAlertsOnRuleTimeout` configuration, which defaults to `true`. Set this to `false` to receive alerts and actions after the timeout, although be aware that these may be incomplete and possibly inaccurate. + +[float] +==== Rules that spawn too many actions + +Rules that spawn too many actions can quickly clog up Task Manager throughput. This can occur if: + +* A rule configured with a single action generates many alerts. For example, if a rule configured to run a single email action generates 100,000 alerts, then 100,000 actions will be scheduled during a run. +* A rule configured with multiple actions generates alerts. For example, if a rule configured to run an email action, a server log action and a webhook action generates 30,000 alerts, then 90,000 actions will be scheduled during a run. + +Use `xpack.alerting.rules.run.actions.max` to limit the maximum number of actions a rule can generate per run. This value can also be configured by connector type using `xpack.alerting.rules.run.actions.connectorTypeOverrides`. For example, the following config sets the global maximum number of actions to 100 while allowing rules with *Email* actions to generate up to 200 actions. + +[source,yaml] +-- +xpack.alerting.rules.run: + actions: + max: 100 + connectorTypeOverrides: + - id: '.email' + max: 200 +-- \ No newline at end of file diff --git a/docs/user/production-considerations/reporting-production-considerations.asciidoc b/docs/user/production-considerations/reporting-production-considerations.asciidoc index f673c4538f443..20ea82be9c6e4 100644 --- a/docs/user/production-considerations/reporting-production-considerations.asciidoc +++ b/docs/user/production-considerations/reporting-production-considerations.asciidoc @@ -31,10 +31,10 @@ distributions don't have user namespaces enabled by default, or they require the automatically disable the sandbox when it is running on Debian because additional steps are required to enable unprivileged usernamespaces. In these situations, you'll see the following message in your {kib} startup logs: `Chromium sandbox provides an additional layer of protection, but is not supported for your OS. -Automatically setting 'xpack.reporting.capture.browser.chromium.disableSandbox: true'.` +Automatically setting 'xpack.screenshotting.browser.chromium.disableSandbox: true'.` Reporting automatically enables the Chromium sandbox at startup when a supported OS is detected. However, if your kernel is 3.8 or newer, it's -recommended to set `xpack.reporting.capture.browser.chromium.disableSandbox: false` in your `kibana.yml` to explicitly enable usernamespaces. +recommended to set `xpack.screenshotting.browser.chromium.disableSandbox: false` in your `kibana.yml` to explicitly enable usernamespaces. [float] [[reporting-docker-sandbox]] diff --git a/nav-kibana-dev.docnav.json b/nav-kibana-dev.docnav.json index 517d42c8f72d0..4a1fd848a928e 100644 --- a/nav-kibana-dev.docnav.json +++ b/nav-kibana-dev.docnav.json @@ -69,6 +69,7 @@ { "label": "Contributors Newsletters", "items": [ + { "id": "kibApril2022ContributorNewsletter" }, { "id": "kibMarch2022ContributorNewsletter" }, { "id": "kibFebruary2022ContributorNewsletter" }, { "id": "kibJanuary2022ContributorNewsletter" }, @@ -99,7 +100,6 @@ { "id": "kibCoreSavedObjectsPluginApi" }, { "id": "kibFieldFormatsPluginApi" }, { "id": "kibDataPluginApi" }, - { "id": "kibDataAutocompletePluginApi" }, { "id": "kibDataEnhancedPluginApi" }, { "id": "kibDataViewsPluginApi" }, { "id": "kibDataQueryPluginApi" }, diff --git a/package.json b/package.json index 4c7db193368a6..577feb8f59ba4 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.2.0-canary.2", "@elastic/ems-client": "8.3.0", - "@elastic/eui": "54.0.0", + "@elastic/eui": "55.0.1", "@elastic/filesaver": "1.1.2", "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.1", @@ -134,6 +134,9 @@ "@kbn/ambient-ui-types": "link:bazel-bin/packages/kbn-ambient-ui-types", "@kbn/analytics": "link:bazel-bin/packages/kbn-analytics", "@kbn/analytics-client": "link:bazel-bin/packages/analytics/client", + "@kbn/analytics-shippers-elastic-v3-browser": "link:bazel-bin/packages/analytics/shippers/elastic_v3/browser", + "@kbn/analytics-shippers-elastic-v3-common": "link:bazel-bin/packages/analytics/shippers/elastic_v3/common", + "@kbn/analytics-shippers-elastic-v3-server": "link:bazel-bin/packages/analytics/shippers/elastic_v3/server", "@kbn/analytics-shippers-fullstory": "link:bazel-bin/packages/analytics/shippers/fullstory", "@kbn/apm-config-loader": "link:bazel-bin/packages/kbn-apm-config-loader", "@kbn/apm-utils": "link:bazel-bin/packages/kbn-apm-utils", @@ -175,8 +178,11 @@ "@kbn/securitysolution-utils": "link:bazel-bin/packages/kbn-securitysolution-utils", "@kbn/server-http-tools": "link:bazel-bin/packages/kbn-server-http-tools", "@kbn/server-route-repository": "link:bazel-bin/packages/kbn-server-route-repository", + "@kbn/shared-ux-avatar-solution": "link:bazel-bin/packages/shared-ux/avatar/solution", "@kbn/shared-ux-button-exit-full-screen": "link:bazel-bin/packages/shared-ux/button/exit_full_screen", "@kbn/shared-ux-components": "link:bazel-bin/packages/kbn-shared-ux-components", + "@kbn/shared-ux-link-redirect-app": "link:bazel-bin/packages/shared-ux/link/redirect_app", + "@kbn/shared-ux-page-analytics-no-data": "link:bazel-bin/packages/shared-ux/page/analytics_no_data", "@kbn/shared-ux-services": "link:bazel-bin/packages/kbn-shared-ux-services", "@kbn/shared-ux-storybook": "link:bazel-bin/packages/kbn-shared-ux-storybook", "@kbn/shared-ux-utility": "link:bazel-bin/packages/kbn-shared-ux-utility", @@ -221,7 +227,7 @@ "base64-js": "^1.3.1", "bitmap-sdf": "^1.0.3", "brace": "0.11.1", - "broadcast-channel": "^4.11.0", + "broadcast-channel": "4.10.0", "canvg": "^3.0.9", "chalk": "^4.1.0", "cheerio": "^1.0.0-rc.10", @@ -234,7 +240,7 @@ "constate": "^1.3.2", "content-disposition": "0.5.3", "copy-to-clipboard": "^3.0.8", - "core-js": "^3.21.1", + "core-js": "^3.22.4", "cronstrue": "^1.51.0", "cytoscape": "^3.10.0", "cytoscape-dagre": "^2.2.2", @@ -249,7 +255,7 @@ "deep-freeze-strict": "^1.1.1", "deepmerge": "^4.2.2", "del": "^5.1.0", - "elastic-apm-node": "^3.31.0", + "elastic-apm-node": "^3.33.0", "email-addresses": "^5.0.0", "execa": "^4.0.2", "exit-hook": "^2.2.0", @@ -271,15 +277,12 @@ "handlebars": "4.7.7", "he": "^1.2.0", "history": "^4.9.0", - "history-extra": "^5.0.1", "hjson": "3.2.1", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^5.0.0", "i18n-iso-countries": "^4.3.1", "icalendar": "0.7.1", - "idx": "^2.5.6", "immer": "^9.0.6", - "inline-style": "^2.0.0", "inquirer": "^7.3.3", "intl": "^1.2.5", "intl-format-cache": "^2.1.0", @@ -336,6 +339,7 @@ "p-retry": "^4.2.0", "papaparse": "^5.2.0", "pbf": "3.2.1", + "pdfjs-dist": "^2.13.216", "pdfmake": "^0.2.4", "peggy": "^1.2.0", "pluralize": "3.1.0", @@ -413,7 +417,6 @@ "styled-components": "^5.1.0", "suricata-sid-db": "^1.0.2", "symbol-observable": "^1.2.0", - "tabbable": "1.1.3", "tar": "^6.1.11", "tinycolor2": "1.4.1", "tinygradient": "0.4.3", @@ -436,7 +439,6 @@ "vega-tooltip": "^0.28.0", "venn.js": "0.2.20", "vinyl": "^2.2.0", - "vt-pbf": "^3.1.1", "whatwg-fetch": "^3.0.0", "xml2js": "^0.4.22", "yauzl": "^2.10.0" @@ -484,7 +486,6 @@ "@kbn/babel-preset": "link:bazel-bin/packages/kbn-babel-preset", "@kbn/bazel-packages": "link:bazel-bin/packages/kbn-bazel-packages", "@kbn/bazel-runner": "link:bazel-bin/packages/kbn-bazel-runner", - "@kbn/ci-stats-client": "link:bazel-bin/packages/kbn-ci-stats-client", "@kbn/ci-stats-core": "link:bazel-bin/packages/kbn-ci-stats-core", "@kbn/ci-stats-reporter": "link:bazel-bin/packages/kbn-ci-stats-reporter", "@kbn/cli-dev-mode": "link:bazel-bin/packages/kbn-cli-dev-mode", @@ -499,6 +500,7 @@ "@kbn/import-resolver": "link:bazel-bin/packages/kbn-import-resolver", "@kbn/jest-serializers": "link:bazel-bin/packages/kbn-jest-serializers", "@kbn/optimizer": "link:bazel-bin/packages/kbn-optimizer", + "@kbn/performance-testing-dataset-extractor": "link:bazel-bin/packages/kbn-performance-testing-dataset-extractor", "@kbn/plugin-generator": "link:bazel-bin/packages/kbn-plugin-generator", "@kbn/plugin-helpers": "link:bazel-bin/packages/kbn-plugin-helpers", "@kbn/pm": "link:packages/kbn-pm", @@ -607,13 +609,15 @@ "@types/kbn__alerts": "link:bazel-bin/packages/kbn-alerts/npm_module_types", "@types/kbn__analytics": "link:bazel-bin/packages/kbn-analytics/npm_module_types", "@types/kbn__analytics-client": "link:bazel-bin/packages/analytics/client/npm_module_types", + "@types/kbn__analytics-shippers-elastic-v3-browser": "link:bazel-bin/packages/analytics/shippers/elastic_v3/browser/npm_module_types", + "@types/kbn__analytics-shippers-elastic-v3-common": "link:bazel-bin/packages/analytics/shippers/elastic_v3/common/npm_module_types", + "@types/kbn__analytics-shippers-elastic-v3-server": "link:bazel-bin/packages/analytics/shippers/elastic_v3/server/npm_module_types", "@types/kbn__analytics-shippers-fullstory": "link:bazel-bin/packages/analytics/shippers/fullstory/npm_module_types", "@types/kbn__apm-config-loader": "link:bazel-bin/packages/kbn-apm-config-loader/npm_module_types", "@types/kbn__apm-utils": "link:bazel-bin/packages/kbn-apm-utils/npm_module_types", "@types/kbn__axe-config": "link:bazel-bin/packages/kbn-axe-config/npm_module_types", "@types/kbn__bazel-packages": "link:bazel-bin/packages/kbn-bazel-packages/npm_module_types", "@types/kbn__bazel-runner": "link:bazel-bin/packages/kbn-bazel-runner/npm_module_types", - "@types/kbn__ci-stats-client": "link:bazel-bin/packages/kbn-ci-stats-client/npm_module_types", "@types/kbn__ci-stats-core": "link:bazel-bin/packages/kbn-ci-stats-core/npm_module_types", "@types/kbn__ci-stats-reporter": "link:bazel-bin/packages/kbn-ci-stats-reporter/npm_module_types", "@types/kbn__cli-dev-mode": "link:bazel-bin/packages/kbn-cli-dev-mode/npm_module_types", @@ -643,6 +647,7 @@ "@types/kbn__mapbox-gl": "link:bazel-bin/packages/kbn-mapbox-gl/npm_module_types", "@types/kbn__monaco": "link:bazel-bin/packages/kbn-monaco/npm_module_types", "@types/kbn__optimizer": "link:bazel-bin/packages/kbn-optimizer/npm_module_types", + "@types/kbn__performance-testing-dataset-extractor": "link:bazel-bin/packages/kbn-performance-testing-dataset-extractor/npm_module_types", "@types/kbn__plugin-discovery": "link:bazel-bin/packages/kbn-plugin-discovery/npm_module_types", "@types/kbn__plugin-generator": "link:bazel-bin/packages/kbn-plugin-generator/npm_module_types", "@types/kbn__plugin-helpers": "link:bazel-bin/packages/kbn-plugin-helpers/npm_module_types", @@ -664,8 +669,11 @@ "@types/kbn__securitysolution-utils": "link:bazel-bin/packages/kbn-securitysolution-utils/npm_module_types", "@types/kbn__server-http-tools": "link:bazel-bin/packages/kbn-server-http-tools/npm_module_types", "@types/kbn__server-route-repository": "link:bazel-bin/packages/kbn-server-route-repository/npm_module_types", + "@types/kbn__shared-ux-avatar-solution": "link:bazel-bin/packages/shared-ux/avatar/solution/npm_module_types", "@types/kbn__shared-ux-button-exit-full-screen": "link:bazel-bin/packages/shared-ux/button/exit_full_screen/npm_module_types", "@types/kbn__shared-ux-components": "link:bazel-bin/packages/kbn-shared-ux-components/npm_module_types", + "@types/kbn__shared-ux-link-redirect-app": "link:bazel-bin/packages/shared-ux/link/redirect_app/npm_module_types", + "@types/kbn__shared-ux-page-analytics-no-data": "link:bazel-bin/packages/shared-ux/page/analytics_no_data/npm_module_types", "@types/kbn__shared-ux-services": "link:bazel-bin/packages/kbn-shared-ux-services/npm_module_types", "@types/kbn__shared-ux-storybook": "link:bazel-bin/packages/kbn-shared-ux-storybook/npm_module_types", "@types/kbn__shared-ux-utility": "link:bazel-bin/packages/kbn-shared-ux-utility/npm_module_types", @@ -710,7 +718,6 @@ "@types/opn": "^5.1.0", "@types/ora": "^1.3.5", "@types/papaparse": "^5.0.3", - "@types/parse-link-header": "^1.0.0", "@types/pbf": "3.0.2", "@types/pdfmake": "^0.1.19", "@types/pegjs": "^0.10.1", @@ -740,7 +747,7 @@ "@types/redux-logger": "^3.0.8", "@types/resolve": "^1.20.1", "@types/seedrandom": ">=2.0.0 <4.0.0", - "@types/selenium-webdriver": "^4.0.18", + "@types/selenium-webdriver": "^4.0.19", "@types/semver": "^7", "@types/set-value": "^2.0.0", "@types/sinon": "^7.0.13", @@ -752,7 +759,6 @@ "@types/supertest": "^2.0.5", "@types/tapable": "^1.0.6", "@types/tar": "^4.0.5", - "@types/tar-fs": "^1.16.1", "@types/tempy": "^0.2.0", "@types/testing-library__jest-dom": "^5.14.3", "@types/tinycolor2": "^1.4.1", @@ -849,7 +855,6 @@ "file-loader": "^4.2.0", "form-data": "^4.0.0", "geckodriver": "^3.0.1", - "glob-watcher": "5.0.3", "gulp": "4.0.2", "gulp-babel": "^8.0.0", "gulp-brotli": "^3.0.0", @@ -889,13 +894,11 @@ "marge": "^1.0.1", "micromatch": "3.1.10", "minimist": "^1.2.6", - "mkdirp": "0.5.1", "mocha": "^9.1.0", "mocha-junit-reporter": "^2.0.2", "mochawesome": "^7.0.1", "mochawesome-merge": "^4.2.1", "mock-fs": "^5.1.2", - "mock-http-server": "1.3.0", "ms-chromium-edge-driver": "^0.5.1", "multimatch": "^4.0.0", "mutation-observer": "^1.0.3", @@ -905,10 +908,8 @@ "nyc": "^15.1.0", "oboe": "^2.1.4", "openapi-types": "^10.0.0", - "parse-link-header": "^1.0.1", "pbf": "3.2.1", "pirates": "^4.0.1", - "pixelmatch": "^5.1.0", "playwright": "^1.17.1", "postcss": "^7.0.32", "postcss-loader": "^3.0.0", @@ -922,9 +923,7 @@ "resolve": "^1.22.0", "rxjs-marbles": "^5.0.6", "sass-loader": "^10.2.0", - "sass-resources-loader": "^2.0.1", "selenium-webdriver": "^4.1.1", - "serve-static": "1.14.1", "shelljs": "^0.8.4", "simple-git": "1.116.0", "sinon": "^7.4.2", @@ -940,7 +939,6 @@ "supertest": "^3.1.0", "supports-color": "^7.0.0", "tape": "^5.0.1", - "tar-fs": "^2.1.0", "tempy": "^0.3.0", "terser": "^5.7.1", "terser-webpack-plugin": "^4.2.3", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 567e5dd65d664..5f435f583a36a 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -10,6 +10,9 @@ filegroup( name = "build_pkg_code", srcs = [ "//packages/analytics/client:build", + "//packages/analytics/shippers/elastic_v3/browser:build", + "//packages/analytics/shippers/elastic_v3/common:build", + "//packages/analytics/shippers/elastic_v3/server:build", "//packages/analytics/shippers/fullstory:build", "//packages/elastic-apm-synthtrace:build", "//packages/elastic-eslint-config-kibana:build", @@ -26,7 +29,6 @@ filegroup( "//packages/kbn-babel-preset:build", "//packages/kbn-bazel-packages:build", "//packages/kbn-bazel-runner:build", - "//packages/kbn-ci-stats-client:build", "//packages/kbn-ci-stats-core:build", "//packages/kbn-ci-stats-reporter:build", "//packages/kbn-cli-dev-mode:build", @@ -60,6 +62,7 @@ filegroup( "//packages/kbn-mapbox-gl:build", "//packages/kbn-monaco:build", "//packages/kbn-optimizer:build", + "//packages/kbn-performance-testing-dataset-extractor:build", "//packages/kbn-plugin-discovery:build", "//packages/kbn-plugin-generator:build", "//packages/kbn-plugin-helpers:build", @@ -106,7 +109,10 @@ filegroup( "//packages/kbn-ui-theme:build", "//packages/kbn-utility-types:build", "//packages/kbn-utils:build", + "//packages/shared-ux/avatar/solution:build", "//packages/shared-ux/button/exit_full_screen:build", + "//packages/shared-ux/link/redirect_app:build", + "//packages/shared-ux/page/analytics_no_data:build", ], ) @@ -115,6 +121,9 @@ filegroup( name = "build_pkg_types", srcs = [ "//packages/analytics/client:build_types", + "//packages/analytics/shippers/elastic_v3/browser:build_types", + "//packages/analytics/shippers/elastic_v3/common:build_types", + "//packages/analytics/shippers/elastic_v3/server:build_types", "//packages/analytics/shippers/fullstory:build_types", "//packages/elastic-apm-synthtrace:build_types", "//packages/elastic-safer-lodash-set:build_types", @@ -126,7 +135,6 @@ filegroup( "//packages/kbn-axe-config:build_types", "//packages/kbn-bazel-packages:build_types", "//packages/kbn-bazel-runner:build_types", - "//packages/kbn-ci-stats-client:build_types", "//packages/kbn-ci-stats-core:build_types", "//packages/kbn-ci-stats-reporter:build_types", "//packages/kbn-cli-dev-mode:build_types", @@ -156,6 +164,7 @@ filegroup( "//packages/kbn-mapbox-gl:build_types", "//packages/kbn-monaco:build_types", "//packages/kbn-optimizer:build_types", + "//packages/kbn-performance-testing-dataset-extractor:build_types", "//packages/kbn-plugin-discovery:build_types", "//packages/kbn-plugin-generator:build_types", "//packages/kbn-plugin-helpers:build_types", @@ -196,7 +205,10 @@ filegroup( "//packages/kbn-ui-theme:build_types", "//packages/kbn-utility-types:build_types", "//packages/kbn-utils:build_types", + "//packages/shared-ux/avatar/solution:build_types", "//packages/shared-ux/button/exit_full_screen:build_types", + "//packages/shared-ux/link/redirect_app:build_types", + "//packages/shared-ux/page/analytics_no_data:build_types", ], ) diff --git a/packages/analytics/client/BUILD.bazel b/packages/analytics/client/BUILD.bazel index af79d33ccc692..0b6a811adc22c 100644 --- a/packages/analytics/client/BUILD.bazel +++ b/packages/analytics/client/BUILD.bazel @@ -62,6 +62,13 @@ jsts_transpiler( build_pkg_name = package_name(), ) +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + ts_config( name = "tsconfig", src = "tsconfig.json", @@ -86,7 +93,7 @@ ts_project( js_library( name = PKG_DIRNAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node"], + deps = RUNTIME_DEPS + [":target_node", ":target_web"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/analytics/client/package.json b/packages/analytics/client/package.json index 82ab42bc22a31..9f2c648183a41 100644 --- a/packages/analytics/client/package.json +++ b/packages/analytics/client/package.json @@ -2,6 +2,7 @@ "name": "@kbn/analytics-client", "private": true, "version": "1.0.0", + "browser": "./target_web/index.js", "main": "./target_node/index.js", "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/analytics/client/src/analytics_client/analytics_client.test.ts b/packages/analytics/client/src/analytics_client/analytics_client.test.ts index 08515ecae8d1e..5348ffa1979fa 100644 --- a/packages/analytics/client/src/analytics_client/analytics_client.test.ts +++ b/packages/analytics/client/src/analytics_client/analytics_client.test.ts @@ -9,6 +9,7 @@ // eslint-disable-next-line max-classes-per-file import type { Observable } from 'rxjs'; import { BehaviorSubject, firstValueFrom, lastValueFrom, Subject } from 'rxjs'; +import { fakeSchedulers } from 'rxjs-marbles/jest'; import type { MockedLogger } from '@kbn/logging-mocks'; import { loggerMock } from '@kbn/logging-mocks'; import { AnalyticsClient } from './analytics_client'; @@ -17,13 +18,12 @@ import { shippersMock } from '../shippers/mocks'; import type { EventContext, TelemetryCounter } from '../events'; import { TelemetryCounterType } from '../events'; -const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - describe('AnalyticsClient', () => { let analyticsClient: AnalyticsClient; let logger: MockedLogger; beforeEach(() => { + jest.useFakeTimers(); logger = loggerMock.create(); analyticsClient = new AnalyticsClient({ logger, @@ -32,6 +32,11 @@ describe('AnalyticsClient', () => { }); }); + afterEach(() => { + analyticsClient.shutdown(); + jest.useRealTimers(); + }); + describe('registerEventType', () => { test('successfully registers a event type', () => { analyticsClient.registerEventType({ @@ -281,13 +286,16 @@ describe('AnalyticsClient', () => { constructor({ optInMock, extendContextMock, + shutdownMock, }: { optInMock?: jest.Mock; extendContextMock?: jest.Mock; + shutdownMock?: jest.Mock; }) { super(); if (optInMock) this.optIn = optInMock; if (extendContextMock) this.extendContext = extendContextMock; + if (shutdownMock) this.shutdown = shutdownMock; } } @@ -310,54 +318,76 @@ describe('AnalyticsClient', () => { expect(optIn).toHaveBeenCalledWith(true); }); - test('Spreads the context updates to the shipper (only after opt-in)', async () => { - const extendContextMock = jest.fn(); - analyticsClient.registerShipper(MockedShipper, { extendContextMock }); - expect(extendContextMock).toHaveBeenCalledTimes(0); // Not until we have opt-in - analyticsClient.optIn({ global: { enabled: true } }); - await delay(10); - expect(extendContextMock).toHaveBeenCalledWith({}); // The initial context - - const context$ = new Subject<{ a_field: boolean }>(); - analyticsClient.registerContextProvider({ - name: 'contextProviderA', - schema: { - a_field: { - type: 'boolean', - _meta: { - description: 'a_field description', + test( + 'Spreads the context updates to the shipper (only after opt-in)', + fakeSchedulers((advance) => { + const extendContextMock = jest.fn(); + analyticsClient.registerShipper(MockedShipper, { extendContextMock }); + expect(extendContextMock).toHaveBeenCalledTimes(0); // Not until we have opt-in + analyticsClient.optIn({ global: { enabled: true } }); + advance(10); + expect(extendContextMock).toHaveBeenCalledWith({}); // The initial context + + const context$ = new Subject<{ a_field: boolean }>(); + analyticsClient.registerContextProvider({ + name: 'contextProviderA', + schema: { + a_field: { + type: 'boolean', + _meta: { + description: 'a_field description', + }, }, }, - }, - context$, - }); - - context$.next({ a_field: true }); - expect(extendContextMock).toHaveBeenCalledWith({ a_field: true }); // After update - }); - - test('Does not spread the context if opt-in === false', async () => { - const extendContextMock = jest.fn(); - analyticsClient.registerShipper(MockedShipper, { extendContextMock }); - expect(extendContextMock).toHaveBeenCalledTimes(0); // Not until we have opt-in - analyticsClient.optIn({ global: { enabled: false } }); - await delay(10); - expect(extendContextMock).toHaveBeenCalledTimes(0); // Not until we have opt-in - }); + context$, + }); - test('Handles errors in the shipper', async () => { - const extendContextMock = jest.fn().mockImplementation(() => { - throw new Error('Something went terribly wrong'); - }); - analyticsClient.registerShipper(MockedShipper, { extendContextMock }); - analyticsClient.optIn({ global: { enabled: true } }); - await delay(10); - expect(extendContextMock).toHaveBeenCalledWith({}); // The initial context - expect(logger.warn).toHaveBeenCalledWith( - `Shipper "${MockedShipper.shipperName}" failed to extend the context`, - expect.any(Error) - ); - }); + context$.next({ a_field: true }); + expect(extendContextMock).toHaveBeenCalledWith({ a_field: true }); // After update + }) + ); + + test( + 'Does not spread the context if opt-in === false', + fakeSchedulers((advance) => { + const extendContextMock = jest.fn(); + analyticsClient.registerShipper(MockedShipper, { extendContextMock }); + expect(extendContextMock).toHaveBeenCalledTimes(0); // Not until we have opt-in + analyticsClient.optIn({ global: { enabled: false } }); + advance(10); + expect(extendContextMock).toHaveBeenCalledTimes(0); // Not until we have opt-in + }) + ); + + test( + 'Handles errors in the shipper', + fakeSchedulers((advance) => { + const optInMock = jest.fn().mockImplementation(() => { + throw new Error('Something went terribly wrong'); + }); + const extendContextMock = jest.fn().mockImplementation(() => { + throw new Error('Something went terribly wrong'); + }); + const shutdownMock = jest.fn().mockImplementation(() => { + throw new Error('Something went terribly wrong'); + }); + analyticsClient.registerShipper(MockedShipper, { + optInMock, + extendContextMock, + shutdownMock, + }); + expect(() => analyticsClient.optIn({ global: { enabled: true } })).not.toThrow(); + advance(10); + expect(optInMock).toHaveBeenCalledWith(true); + expect(extendContextMock).toHaveBeenCalledWith({}); // The initial context + expect(logger.warn).toHaveBeenCalledWith( + `Shipper "${MockedShipper.shipperName}" failed to extend the context`, + expect.any(Error) + ); + expect(() => analyticsClient.shutdown()).not.toThrow(); + expect(shutdownMock).toHaveBeenCalled(); + }) + ); }); describe('registerContextProvider', () => { @@ -718,6 +748,7 @@ describe('AnalyticsClient', () => { expect(optInMock1).toHaveBeenCalledWith(false); // Using global and shipper-specific expect(optInMock2).toHaveBeenCalledWith(false); // Using only global }); + test('Catches error in the shipper.optIn method', () => { optInMock1.mockImplementation(() => { throw new Error('Something went terribly wrong'); @@ -818,86 +849,89 @@ describe('AnalyticsClient', () => { ]); }); - test('Sends events from the internal queue when there are shippers and an opt-in response is true', async () => { - const telemetryCounterPromise = lastValueFrom( - analyticsClient.telemetryCounter$.pipe(take(3 + 2), toArray()) // Waiting for 3 enqueued + 2 batch-shipped events - ); - - // Send multiple events of 1 type to test the grouping logic as well - analyticsClient.reportEvent('event-type-a', { a_field: 'a' }); - analyticsClient.reportEvent('event-type-b', { b_field: 100 }); - analyticsClient.reportEvent('event-type-a', { a_field: 'b' }); - - // As proven in the previous test, the events are still enqueued. - // Let's register a shipper and opt-in to test the dequeue logic. - const reportEventsMock = jest.fn(); - analyticsClient.registerShipper(MockedShipper1, { reportEventsMock }); - analyticsClient.optIn({ global: { enabled: true } }); - await delay(10); - - expect(reportEventsMock).toHaveBeenCalledTimes(2); - expect(reportEventsMock).toHaveBeenNthCalledWith(1, [ - { - context: {}, - event_type: 'event-type-a', - properties: { a_field: 'a' }, - timestamp: expect.any(String), - }, - { - context: {}, - event_type: 'event-type-a', - properties: { a_field: 'b' }, - timestamp: expect.any(String), - }, - ]); - expect(reportEventsMock).toHaveBeenNthCalledWith(2, [ - { - context: {}, - event_type: 'event-type-b', - properties: { b_field: 100 }, - timestamp: expect.any(String), - }, - ]); - - // Expect 3 enqueued events, and 2 sent_to_shipper batched requests - await expect(telemetryCounterPromise).resolves.toEqual([ - { - type: 'enqueued', - source: 'client', - event_type: 'event-type-a', - code: 'enqueued', - count: 1, - }, - { - type: 'enqueued', - source: 'client', - event_type: 'event-type-b', - code: 'enqueued', - count: 1, - }, - { - type: 'enqueued', - source: 'client', - event_type: 'event-type-a', - code: 'enqueued', - count: 1, - }, - { - type: 'sent_to_shipper', - source: 'client', - event_type: 'event-type-a', - code: 'OK', - count: 2, - }, - { - type: 'sent_to_shipper', - source: 'client', - event_type: 'event-type-b', - code: 'OK', - count: 1, - }, - ]); - }); + test( + 'Sends events from the internal queue when there are shippers and an opt-in response is true', + fakeSchedulers(async (advance) => { + const telemetryCounterPromise = lastValueFrom( + analyticsClient.telemetryCounter$.pipe(take(3 + 2), toArray()) // Waiting for 3 enqueued + 2 batch-shipped events + ); + + // Send multiple events of 1 type to test the grouping logic as well + analyticsClient.reportEvent('event-type-a', { a_field: 'a' }); + analyticsClient.reportEvent('event-type-b', { b_field: 100 }); + analyticsClient.reportEvent('event-type-a', { a_field: 'b' }); + + // As proven in the previous test, the events are still enqueued. + // Let's register a shipper and opt-in to test the dequeue logic. + const reportEventsMock = jest.fn(); + analyticsClient.registerShipper(MockedShipper1, { reportEventsMock }); + analyticsClient.optIn({ global: { enabled: true } }); + advance(10); + + expect(reportEventsMock).toHaveBeenCalledTimes(2); + expect(reportEventsMock).toHaveBeenNthCalledWith(1, [ + { + context: {}, + event_type: 'event-type-a', + properties: { a_field: 'a' }, + timestamp: expect.any(String), + }, + { + context: {}, + event_type: 'event-type-a', + properties: { a_field: 'b' }, + timestamp: expect.any(String), + }, + ]); + expect(reportEventsMock).toHaveBeenNthCalledWith(2, [ + { + context: {}, + event_type: 'event-type-b', + properties: { b_field: 100 }, + timestamp: expect.any(String), + }, + ]); + + // Expect 3 enqueued events, and 2 sent_to_shipper batched requests + await expect(telemetryCounterPromise).resolves.toEqual([ + { + type: 'enqueued', + source: 'client', + event_type: 'event-type-a', + code: 'enqueued', + count: 1, + }, + { + type: 'enqueued', + source: 'client', + event_type: 'event-type-b', + code: 'enqueued', + count: 1, + }, + { + type: 'enqueued', + source: 'client', + event_type: 'event-type-a', + code: 'enqueued', + count: 1, + }, + { + type: 'sent_to_shipper', + source: 'client', + event_type: 'event-type-a', + code: 'OK', + count: 2, + }, + { + type: 'sent_to_shipper', + source: 'client', + event_type: 'event-type-b', + code: 'OK', + count: 1, + }, + ]); + }) + ); test('Discards events from the internal queue when there are shippers and an opt-in response is false', async () => { const telemetryCounterPromise = lastValueFrom( @@ -941,250 +975,259 @@ describe('AnalyticsClient', () => { ]); }); - test('Discards only one type of the enqueued events based on event_type config', async () => { - const telemetryCounterPromise = lastValueFrom( - analyticsClient.telemetryCounter$.pipe(take(3 + 1), toArray()) // Waiting for 3 enqueued + 1 batch-shipped events - ); - - // Send multiple events of 1 type to test the grouping logic as well - analyticsClient.reportEvent('event-type-a', { a_field: 'a' }); - analyticsClient.reportEvent('event-type-b', { b_field: 100 }); - analyticsClient.reportEvent('event-type-a', { a_field: 'b' }); - - const reportEventsMock = jest.fn(); - analyticsClient.registerShipper(MockedShipper1, { reportEventsMock }); - analyticsClient.optIn({ - global: { enabled: true }, - event_types: { ['event-type-a']: { enabled: false } }, - }); - await delay(10); - - expect(reportEventsMock).toHaveBeenCalledTimes(1); - expect(reportEventsMock).toHaveBeenNthCalledWith(1, [ - { - context: {}, - event_type: 'event-type-b', - properties: { b_field: 100 }, - timestamp: expect.any(String), - }, - ]); - - // Expect 3 enqueued events, and 1 sent_to_shipper batched request - await expect(telemetryCounterPromise).resolves.toEqual([ - { - type: 'enqueued', - source: 'client', - event_type: 'event-type-a', - code: 'enqueued', - count: 1, - }, - { - type: 'enqueued', - source: 'client', - event_type: 'event-type-b', - code: 'enqueued', - count: 1, - }, - { - type: 'enqueued', - source: 'client', - event_type: 'event-type-a', - code: 'enqueued', - count: 1, - }, - { - type: 'sent_to_shipper', - source: 'client', - event_type: 'event-type-b', - code: 'OK', - count: 1, - }, - ]); - }); - - test('Discards the event at the shipper level (for a specific event)', async () => { - const telemetryCounterPromise = lastValueFrom( - analyticsClient.telemetryCounter$.pipe(take(3 + 2), toArray()) // Waiting for 3 enqueued + 2 batch-shipped events - ); - - // Send multiple events of 1 type to test the grouping logic as well - analyticsClient.reportEvent('event-type-a', { a_field: 'a' }); - analyticsClient.reportEvent('event-type-b', { b_field: 100 }); - analyticsClient.reportEvent('event-type-a', { a_field: 'b' }); - - // Register 2 shippers and set 1 of them as disabled for event-type-a - const reportEventsMock1 = jest.fn(); - const reportEventsMock2 = jest.fn(); - analyticsClient.registerShipper(MockedShipper1, { reportEventsMock: reportEventsMock1 }); - analyticsClient.registerShipper(MockedShipper2, { reportEventsMock: reportEventsMock2 }); - analyticsClient.optIn({ - global: { enabled: true }, - event_types: { - ['event-type-a']: { enabled: true, shippers: { [MockedShipper2.shipperName]: false } }, - }, - }); - await delay(10); - - expect(reportEventsMock1).toHaveBeenCalledTimes(2); - expect(reportEventsMock1).toHaveBeenNthCalledWith(1, [ - { - context: {}, - event_type: 'event-type-a', - properties: { a_field: 'a' }, - timestamp: expect.any(String), - }, - { - context: {}, - event_type: 'event-type-a', - properties: { a_field: 'b' }, - timestamp: expect.any(String), - }, - ]); - expect(reportEventsMock1).toHaveBeenNthCalledWith(2, [ - { - context: {}, - event_type: 'event-type-b', - properties: { b_field: 100 }, - timestamp: expect.any(String), - }, - ]); - expect(reportEventsMock2).toHaveBeenCalledTimes(1); - expect(reportEventsMock2).toHaveBeenNthCalledWith(1, [ - { - context: {}, - event_type: 'event-type-b', - properties: { b_field: 100 }, - timestamp: expect.any(String), - }, - ]); - - // Expect 3 enqueued events, and 2 sent_to_shipper batched requests - await expect(telemetryCounterPromise).resolves.toEqual([ - { - type: 'enqueued', - source: 'client', - event_type: 'event-type-a', - code: 'enqueued', - count: 1, - }, - { - type: 'enqueued', - source: 'client', - event_type: 'event-type-b', - code: 'enqueued', - count: 1, - }, - { - type: 'enqueued', - source: 'client', - event_type: 'event-type-a', - code: 'enqueued', - count: 1, - }, - { - type: 'sent_to_shipper', - source: 'client', - event_type: 'event-type-a', - code: 'OK', - count: 2, - }, - { - type: 'sent_to_shipper', - source: 'client', - event_type: 'event-type-b', - code: 'OK', - count: 1, - }, - ]); - }); - - test('Discards all the events at the shipper level (globally disabled)', async () => { - const telemetryCounterPromise = lastValueFrom( - analyticsClient.telemetryCounter$.pipe(take(3 + 2), toArray()) // Waiting for 3 enqueued + 2 batch-shipped events - ); - - // Send multiple events of 1 type to test the grouping logic as well - analyticsClient.reportEvent('event-type-a', { a_field: 'a' }); - analyticsClient.reportEvent('event-type-b', { b_field: 100 }); - analyticsClient.reportEvent('event-type-a', { a_field: 'b' }); - - // Register 2 shippers and set 1 of them as globally disabled - const reportEventsMock1 = jest.fn(); - const reportEventsMock2 = jest.fn(); - analyticsClient.registerShipper(MockedShipper1, { reportEventsMock: reportEventsMock1 }); - analyticsClient.registerShipper(MockedShipper2, { reportEventsMock: reportEventsMock2 }); - analyticsClient.optIn({ - global: { enabled: true, shippers: { [MockedShipper2.shipperName]: false } }, - event_types: { - ['event-type-a']: { enabled: true }, - }, - }); - await delay(10); - - expect(reportEventsMock1).toHaveBeenCalledTimes(2); - expect(reportEventsMock1).toHaveBeenNthCalledWith(1, [ - { - context: {}, - event_type: 'event-type-a', - properties: { a_field: 'a' }, - timestamp: expect.any(String), - }, - { - context: {}, - event_type: 'event-type-a', - properties: { a_field: 'b' }, - timestamp: expect.any(String), - }, - ]); - expect(reportEventsMock1).toHaveBeenNthCalledWith(2, [ - { - context: {}, - event_type: 'event-type-b', - properties: { b_field: 100 }, - timestamp: expect.any(String), - }, - ]); - expect(reportEventsMock2).toHaveBeenCalledTimes(0); - - // Expect 3 enqueued events, and 2 sent_to_shipper batched requests - await expect(telemetryCounterPromise).resolves.toEqual([ - { - type: 'enqueued', - source: 'client', - event_type: 'event-type-a', - code: 'enqueued', - count: 1, - }, - { - type: 'enqueued', - source: 'client', - event_type: 'event-type-b', - code: 'enqueued', - count: 1, - }, - { - type: 'enqueued', - source: 'client', - event_type: 'event-type-a', - code: 'enqueued', - count: 1, - }, - { - type: 'sent_to_shipper', - source: 'client', - event_type: 'event-type-a', - code: 'OK', - count: 2, - }, - { - type: 'sent_to_shipper', - source: 'client', - event_type: 'event-type-b', - code: 'OK', - count: 1, - }, - ]); - }); + test( + 'Discards only one type of the enqueued events based on event_type config', + fakeSchedulers(async (advance) => { + const telemetryCounterPromise = lastValueFrom( + analyticsClient.telemetryCounter$.pipe(take(3 + 1), toArray()) // Waiting for 3 enqueued + 1 batch-shipped events + ); + + // Send multiple events of 1 type to test the grouping logic as well + analyticsClient.reportEvent('event-type-a', { a_field: 'a' }); + analyticsClient.reportEvent('event-type-b', { b_field: 100 }); + analyticsClient.reportEvent('event-type-a', { a_field: 'b' }); + + const reportEventsMock = jest.fn(); + analyticsClient.registerShipper(MockedShipper1, { reportEventsMock }); + analyticsClient.optIn({ + global: { enabled: true }, + event_types: { ['event-type-a']: { enabled: false } }, + }); + advance(10); + + expect(reportEventsMock).toHaveBeenCalledTimes(1); + expect(reportEventsMock).toHaveBeenNthCalledWith(1, [ + { + context: {}, + event_type: 'event-type-b', + properties: { b_field: 100 }, + timestamp: expect.any(String), + }, + ]); + + // Expect 3 enqueued events, and 1 sent_to_shipper batched request + await expect(telemetryCounterPromise).resolves.toEqual([ + { + type: 'enqueued', + source: 'client', + event_type: 'event-type-a', + code: 'enqueued', + count: 1, + }, + { + type: 'enqueued', + source: 'client', + event_type: 'event-type-b', + code: 'enqueued', + count: 1, + }, + { + type: 'enqueued', + source: 'client', + event_type: 'event-type-a', + code: 'enqueued', + count: 1, + }, + { + type: 'sent_to_shipper', + source: 'client', + event_type: 'event-type-b', + code: 'OK', + count: 1, + }, + ]); + }) + ); + + test( + 'Discards the event at the shipper level (for a specific event)', + fakeSchedulers(async (advance) => { + const telemetryCounterPromise = lastValueFrom( + analyticsClient.telemetryCounter$.pipe(take(3 + 2), toArray()) // Waiting for 3 enqueued + 2 batch-shipped events + ); + + // Send multiple events of 1 type to test the grouping logic as well + analyticsClient.reportEvent('event-type-a', { a_field: 'a' }); + analyticsClient.reportEvent('event-type-b', { b_field: 100 }); + analyticsClient.reportEvent('event-type-a', { a_field: 'b' }); + + // Register 2 shippers and set 1 of them as disabled for event-type-a + const reportEventsMock1 = jest.fn(); + const reportEventsMock2 = jest.fn(); + analyticsClient.registerShipper(MockedShipper1, { reportEventsMock: reportEventsMock1 }); + analyticsClient.registerShipper(MockedShipper2, { reportEventsMock: reportEventsMock2 }); + analyticsClient.optIn({ + global: { enabled: true }, + event_types: { + ['event-type-a']: { enabled: true, shippers: { [MockedShipper2.shipperName]: false } }, + }, + }); + advance(10); + + expect(reportEventsMock1).toHaveBeenCalledTimes(2); + expect(reportEventsMock1).toHaveBeenNthCalledWith(1, [ + { + context: {}, + event_type: 'event-type-a', + properties: { a_field: 'a' }, + timestamp: expect.any(String), + }, + { + context: {}, + event_type: 'event-type-a', + properties: { a_field: 'b' }, + timestamp: expect.any(String), + }, + ]); + expect(reportEventsMock1).toHaveBeenNthCalledWith(2, [ + { + context: {}, + event_type: 'event-type-b', + properties: { b_field: 100 }, + timestamp: expect.any(String), + }, + ]); + expect(reportEventsMock2).toHaveBeenCalledTimes(1); + expect(reportEventsMock2).toHaveBeenNthCalledWith(1, [ + { + context: {}, + event_type: 'event-type-b', + properties: { b_field: 100 }, + timestamp: expect.any(String), + }, + ]); + + // Expect 3 enqueued events, and 2 sent_to_shipper batched requests + await expect(telemetryCounterPromise).resolves.toEqual([ + { + type: 'enqueued', + source: 'client', + event_type: 'event-type-a', + code: 'enqueued', + count: 1, + }, + { + type: 'enqueued', + source: 'client', + event_type: 'event-type-b', + code: 'enqueued', + count: 1, + }, + { + type: 'enqueued', + source: 'client', + event_type: 'event-type-a', + code: 'enqueued', + count: 1, + }, + { + type: 'sent_to_shipper', + source: 'client', + event_type: 'event-type-a', + code: 'OK', + count: 2, + }, + { + type: 'sent_to_shipper', + source: 'client', + event_type: 'event-type-b', + code: 'OK', + count: 1, + }, + ]); + }) + ); + + test( + 'Discards all the events at the shipper level (globally disabled)', + fakeSchedulers(async (advance) => { + const telemetryCounterPromise = lastValueFrom( + analyticsClient.telemetryCounter$.pipe(take(3 + 2), toArray()) // Waiting for 3 enqueued + 2 batch-shipped events + ); + + // Send multiple events of 1 type to test the grouping logic as well + analyticsClient.reportEvent('event-type-a', { a_field: 'a' }); + analyticsClient.reportEvent('event-type-b', { b_field: 100 }); + analyticsClient.reportEvent('event-type-a', { a_field: 'b' }); + + // Register 2 shippers and set 1 of them as globally disabled + const reportEventsMock1 = jest.fn(); + const reportEventsMock2 = jest.fn(); + analyticsClient.registerShipper(MockedShipper1, { reportEventsMock: reportEventsMock1 }); + analyticsClient.registerShipper(MockedShipper2, { reportEventsMock: reportEventsMock2 }); + analyticsClient.optIn({ + global: { enabled: true, shippers: { [MockedShipper2.shipperName]: false } }, + event_types: { + ['event-type-a']: { enabled: true }, + }, + }); + advance(10); + + expect(reportEventsMock1).toHaveBeenCalledTimes(2); + expect(reportEventsMock1).toHaveBeenNthCalledWith(1, [ + { + context: {}, + event_type: 'event-type-a', + properties: { a_field: 'a' }, + timestamp: expect.any(String), + }, + { + context: {}, + event_type: 'event-type-a', + properties: { a_field: 'b' }, + timestamp: expect.any(String), + }, + ]); + expect(reportEventsMock1).toHaveBeenNthCalledWith(2, [ + { + context: {}, + event_type: 'event-type-b', + properties: { b_field: 100 }, + timestamp: expect.any(String), + }, + ]); + expect(reportEventsMock2).toHaveBeenCalledTimes(0); + + // Expect 3 enqueued events, and 2 sent_to_shipper batched requests + await expect(telemetryCounterPromise).resolves.toEqual([ + { + type: 'enqueued', + source: 'client', + event_type: 'event-type-a', + code: 'enqueued', + count: 1, + }, + { + type: 'enqueued', + source: 'client', + event_type: 'event-type-b', + code: 'enqueued', + count: 1, + }, + { + type: 'enqueued', + source: 'client', + event_type: 'event-type-a', + code: 'enqueued', + count: 1, + }, + { + type: 'sent_to_shipper', + source: 'client', + event_type: 'event-type-a', + code: 'OK', + count: 2, + }, + { + type: 'sent_to_shipper', + source: 'client', + event_type: 'event-type-b', + code: 'OK', + count: 1, + }, + ]); + }) + ); test('Discards incoming events when opt-in response is false', async () => { // Set OptIn and shipper first to test the "once-set up" scenario diff --git a/packages/analytics/client/src/analytics_client/analytics_client.ts b/packages/analytics/client/src/analytics_client/analytics_client.ts index 1c7f261aaf4b4..e38f975fee5a9 100644 --- a/packages/analytics/client/src/analytics_client/analytics_client.ts +++ b/packages/analytics/client/src/analytics_client/analytics_client.ts @@ -208,6 +208,21 @@ export class AnalyticsClient implements IAnalyticsClient { this.shipperRegistered$.next(); }; + public shutdown = () => { + this.shippersRegistry.allShippers.forEach((shipper, shipperName) => { + try { + shipper.shutdown(); + } catch (err) { + this.initContext.logger.warn(`Failed to shutdown shipper "${shipperName}"`, err); + } + }); + this.internalEventQueue$.complete(); + this.internalTelemetryCounter$.complete(); + this.shipperRegistered$.complete(); + this.optInConfig$.complete(); + this.context$.complete(); + }; + /** * Forwards the `events` to the registered shippers, bearing in mind if the shipper is opted-in for that eventType. * @param eventType The event type's name diff --git a/packages/analytics/client/src/analytics_client/mocks.ts b/packages/analytics/client/src/analytics_client/mocks.ts index da707178a756e..221ca0ff3872f 100644 --- a/packages/analytics/client/src/analytics_client/mocks.ts +++ b/packages/analytics/client/src/analytics_client/mocks.ts @@ -18,6 +18,7 @@ function createMockedAnalyticsClient(): jest.Mocked { removeContextProvider: jest.fn(), registerShipper: jest.fn(), telemetryCounter$: new Subject(), + shutdown: jest.fn(), }; } diff --git a/packages/analytics/client/src/analytics_client/types.ts b/packages/analytics/client/src/analytics_client/types.ts index 782d6b16fa594..88b60dc100e89 100644 --- a/packages/analytics/client/src/analytics_client/types.ts +++ b/packages/analytics/client/src/analytics_client/types.ts @@ -176,7 +176,7 @@ export interface IAnalyticsClient { ) => void; /** * Registers the event type that will be emitted via the reportEvent API. - * @param eventTypeOps + * @param eventTypeOps The definition of the event type {@link EventTypeOpts}. */ registerEventType: (eventTypeOps: EventTypeOpts) => void; @@ -211,4 +211,8 @@ export interface IAnalyticsClient { * Observable to emit the stats of the processed events. */ readonly telemetryCounter$: Observable; + /** + * Stops the client. + */ + shutdown: () => void; } diff --git a/packages/analytics/client/src/events/types.ts b/packages/analytics/client/src/events/types.ts index bc193a2788db1..6d31f88621364 100644 --- a/packages/analytics/client/src/events/types.ts +++ b/packages/analytics/client/src/events/types.ts @@ -12,6 +12,18 @@ import type { ShipperName } from '../analytics_client'; * Definition of the context that can be appended to the events through the {@link IAnalyticsClient.registerContextProvider}. */ export interface EventContext { + /** + * The UUID of the cluster + */ + cluster_uuid?: string; + /** + * The name of the cluster. + */ + cluster_name?: string; + /** + * The license ID. + */ + license_id?: string; /** * The unique user ID. */ @@ -36,7 +48,10 @@ export interface EventContext { * The current entity ID (dashboard ID, visualization ID, etc.). */ entityId?: string; - // TODO: Extend with known keys + + /** + * Additional keys are allowed. + */ [key: string]: unknown; } diff --git a/packages/analytics/client/src/schema/types.test.ts b/packages/analytics/client/src/schema/types.test.ts index 3ed25e46d6dc9..05eccf2bb19c7 100644 --- a/packages/analytics/client/src/schema/types.test.ts +++ b/packages/analytics/client/src/schema/types.test.ts @@ -421,6 +421,48 @@ describe('schema types', () => { }; expect(valueType).not.toBeUndefined(); // <-- Only to stop the var-not-used complain }); + + test('it should expect support readonly arrays', () => { + let valueType: SchemaValue> = { + type: 'array', + items: { + properties: { + a_value: { + type: 'keyword', + _meta: { + description: 'Some description', + }, + }, + }, + }, + }; + + valueType = { + type: 'array', + items: { + properties: { + a_value: { + type: 'keyword', + _meta: { + description: 'Some description', + optional: false, + }, + }, + }, + _meta: { + description: 'Description at the object level', + }, + }, + }; + + // @ts-expect-error because it's missing the items definition + valueType = { type: 'array' }; + // @ts-expect-error because it's missing the items definition + valueType = { type: 'array', items: {} }; + // @ts-expect-error because it's missing the items' properties definition + valueType = { type: 'array', items: { properties: {} } }; + expect(valueType).not.toBeUndefined(); // <-- Only to stop the var-not-used complain + }); }); }); diff --git a/packages/analytics/client/src/schema/types.ts b/packages/analytics/client/src/schema/types.ts index 8bac1ceaad620..5043c46e73fd4 100644 --- a/packages/analytics/client/src/schema/types.ts +++ b/packages/analytics/client/src/schema/types.ts @@ -64,7 +64,7 @@ export type SchemaValue = ? // If the Value is unknown (TS can't infer the type), allow any type of schema SchemaArray | SchemaObject | SchemaChildValue : // Otherwise, try to infer the type and enforce the schema - NonNullable extends Array + NonNullable extends Array | ReadonlyArray ? SchemaArray : NonNullable extends object ? SchemaObject diff --git a/packages/analytics/client/src/shippers/mocks.ts b/packages/analytics/client/src/shippers/mocks.ts index 6f80d91675c97..4660ae9d27e72 100644 --- a/packages/analytics/client/src/shippers/mocks.ts +++ b/packages/analytics/client/src/shippers/mocks.ts @@ -23,6 +23,7 @@ class MockedShipper implements IShipper { public reportEvents = jest.fn(); public extendContext = jest.fn(); public telemetryCounter$ = new Subject(); + public shutdown = jest.fn(); } export const shippersMock = { diff --git a/packages/analytics/client/src/shippers/types.ts b/packages/analytics/client/src/shippers/types.ts index 945ea08dae58a..67fe2c54bd77e 100644 --- a/packages/analytics/client/src/shippers/types.ts +++ b/packages/analytics/client/src/shippers/types.ts @@ -25,11 +25,15 @@ export interface IShipper { optIn: (isOptedIn: boolean) => void; /** * Perform any necessary calls to the persisting/analytics solution to set the event's context. - * @param newContext + * @param newContext The full new context to set {@link EventContext} */ extendContext?: (newContext: EventContext) => void; /** * Observable to emit the stats of the processed events. */ telemetryCounter$?: Observable; + /** + * Shutdown the shipper. + */ + shutdown: () => void; } diff --git a/packages/analytics/shippers/README.md b/packages/analytics/shippers/README.md index 8fc04b9f9ecbc..5ab85d08ff2ca 100644 --- a/packages/analytics/shippers/README.md +++ b/packages/analytics/shippers/README.md @@ -3,3 +3,5 @@ This directory holds the implementation of the _built-in_ shippers provided by the Analytics client. At the moment, the shippers are: * [FullStory](./fullstory/README.md) +* [Elastic V3 (Browser shipper)](./elastic_v3/browser/README.md) +* [Elastic V3 (Server-side shipper)](./elastic_v3/server/README.md) diff --git a/packages/analytics/shippers/elastic_v3/browser/BUILD.bazel b/packages/analytics/shippers/elastic_v3/browser/BUILD.bazel new file mode 100644 index 0000000000000..63332da454feb --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/browser/BUILD.bazel @@ -0,0 +1,128 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "browser" +PKG_REQUIRE_NAME = "@kbn/analytics-shippers-elastic-v3-browser" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ + "@npm//rxjs", + "//packages/analytics/client", + "//packages/analytics/shippers/elastic_v3/common", +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//rxjs", + "//packages/analytics/client:npm_module_types", + "//packages/analytics/shippers/elastic_v3/common:npm_module_types", + "//packages/kbn-logging-mocks:npm_module_types", +] + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/analytics/shippers/elastic_v3/browser/README.md b/packages/analytics/shippers/elastic_v3/browser/README.md new file mode 100644 index 0000000000000..fcdd5e8a3ca2e --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/browser/README.md @@ -0,0 +1,25 @@ +# @kbn/analytics-shippers-elastic-v3-browser + +UI-side implementation of the Elastic V3 shipper for the `@kbn/analytics-client`. + +## How to use it + +This module is intended to be used **on the browser only**. Due to the nature of the UI events, they are usually more scattered in time, and we can assume a much lower load than the server. For that reason, it doesn't apply the necessary backpressure mechanisms to prevent the server from getting overloaded with too many events neither identifies if the server sits behind a firewall to discard any incoming events. Refer to `@kbn/analytics-shippers-elastic-v3-server` for the server-side implementation. + +```typescript +import { ElasticV3BrowserShipper } from "@kbn/analytics-shippers-elastic-v3-browser"; + +analytics.registerShipper(ElasticV3BrowserShipper, { channelName: 'myChannel', version: '1.0.0' }); +``` + +## Configuration + +| Name | Description | +|:-------------:|:-------------------------------------------------------------------------------------------| +| `channelName` | The name of the channel to send the events. | +| `version` | The version of the application generating the events. | +| `debug` | When `true`, it logs the responses from the remote Telemetry Service. Defaults to `false`. | + +## Transmission protocol + +This shipper sends the events to the Elastic Internal Telemetry Service. The incoming events are buffered for up to 1 second to attempt to send them in a single request. diff --git a/packages/analytics/shippers/elastic_v3/browser/jest.config.js b/packages/analytics/shippers/elastic_v3/browser/jest.config.js new file mode 100644 index 0000000000000..20067863aad97 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/browser/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../../../', + roots: ['/packages/analytics/shippers/elastic_v3/browser'], +}; diff --git a/packages/analytics/shippers/elastic_v3/browser/package.json b/packages/analytics/shippers/elastic_v3/browser/package.json new file mode 100644 index 0000000000000..418887000521a --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/browser/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/analytics-shippers-elastic-v3-browser", + "private": true, + "version": "1.0.0", + "browser": "./target_web/index.js", + "main": "./target_node/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/analytics/shippers/elastic_v3/browser/src/browser_shipper.test.ts b/packages/analytics/shippers/elastic_v3/browser/src/browser_shipper.test.ts new file mode 100644 index 0000000000000..6fbe8fc166586 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/browser/src/browser_shipper.test.ts @@ -0,0 +1,291 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { loggerMock } from '@kbn/logging-mocks'; +import type { AnalyticsClientInitContext, Event } from '@kbn/analytics-client'; +import { fakeSchedulers } from 'rxjs-marbles/jest'; +import { firstValueFrom } from 'rxjs'; +import { ElasticV3BrowserShipper } from './browser_shipper'; + +describe('ElasticV3BrowserShipper', () => { + const events: Event[] = [ + { + timestamp: '2020-01-01T00:00:00.000Z', + event_type: 'test-event-type', + context: {}, + properties: {}, + }, + ]; + + const initContext: AnalyticsClientInitContext = { + sendTo: 'staging', + isDev: true, + logger: loggerMock.create(), + }; + + let shipper: ElasticV3BrowserShipper; + + let fetchMock: jest.Mock; + + beforeEach(() => { + jest.useFakeTimers(); + + fetchMock = jest.fn().mockResolvedValue({ + status: 200, + ok: true, + text: () => Promise.resolve('{"status": "ok"}'), + }); + + Object.defineProperty(global, 'fetch', { + value: fetchMock, + writable: true, + }); + + shipper = new ElasticV3BrowserShipper( + { version: '1.2.3', channelName: 'test-channel', debug: true }, + initContext + ); + }); + + afterEach(() => { + shipper.shutdown(); + jest.useRealTimers(); + }); + + test("custom sendTo overrides Analytics client's", () => { + const prodShipper = new ElasticV3BrowserShipper( + { version: '1.2.3', channelName: 'test-channel', debug: true, sendTo: 'production' }, + initContext + ); + + // eslint-disable-next-line dot-notation + expect(prodShipper['url']).not.toEqual(shipper['url']); + }); + + test('set optIn should update the isOptedIn$ observable', () => { + // eslint-disable-next-line dot-notation + const internalOptIn$ = shipper['isOptedIn$']; + + // Initially undefined + expect(internalOptIn$.value).toBeUndefined(); + + shipper.optIn(true); + expect(internalOptIn$.value).toBe(true); + + shipper.optIn(false); + expect(internalOptIn$.value).toBe(false); + }); + + test('set extendContext should store local values: clusterUuid and licenseId', () => { + // eslint-disable-next-line dot-notation + const getInternalClusterUuid = () => shipper['clusterUuid']; + // eslint-disable-next-line dot-notation + const getInternalLicenseId = () => shipper['licenseId']; + + // Initial values + expect(getInternalClusterUuid()).toBe('UNKNOWN'); + expect(getInternalLicenseId()).toBeUndefined(); + + shipper.extendContext({ cluster_uuid: 'test-cluster-uuid' }); + expect(getInternalClusterUuid()).toBe('test-cluster-uuid'); + expect(getInternalLicenseId()).toBeUndefined(); + + shipper.extendContext({ license_id: 'test-license-id' }); + expect(getInternalClusterUuid()).toBe('test-cluster-uuid'); + expect(getInternalLicenseId()).toBe('test-license-id'); + + shipper.extendContext({ cluster_uuid: 'test-cluster-uuid-2', license_id: 'test-license-id-2' }); + expect(getInternalClusterUuid()).toBe('test-cluster-uuid-2'); + expect(getInternalLicenseId()).toBe('test-license-id-2'); + }); + + test('calls to reportEvents do not call `fetch` straight away (buffer of 1s)', () => { + shipper.reportEvents(events); + expect(fetchMock).not.toHaveBeenCalled(); + }); + + test( + 'calls to reportEvents do not call `fetch` after 1s because no optIn value is set yet', + fakeSchedulers((advance) => { + shipper.reportEvents(events); + advance(1000); + expect(fetchMock).not.toHaveBeenCalled(); + }) + ); + + test( + 'calls to reportEvents call `fetch` after 1s when optIn value is set to true', + fakeSchedulers(async (advance) => { + shipper.reportEvents(events); + shipper.optIn(true); + const counter = firstValueFrom(shipper.telemetryCounter$); + advance(1000); + expect(fetchMock).toHaveBeenCalledWith( + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { + body: '{"timestamp":"2020-01-01T00:00:00.000Z","event_type":"test-event-type","context":{},"properties":{}}\n', + headers: { + 'content-type': 'application/x-njson', + 'x-elastic-cluster-id': 'UNKNOWN', + 'x-elastic-stack-version': '1.2.3', + }, + keepalive: true, + method: 'POST', + query: { debug: true }, + } + ); + await expect(counter).resolves.toMatchInlineSnapshot(` + Object { + "code": "200", + "count": 1, + "event_type": "test-event-type", + "source": "elastic_v3_browser", + "type": "succeeded", + } + `); + }) + ); + + test( + 'calls to reportEvents do not call `fetch` after 1s when optIn value is set to false', + fakeSchedulers((advance) => { + shipper.reportEvents(events); + shipper.optIn(false); + advance(1000); + expect(fetchMock).not.toHaveBeenCalled(); + }) + ); + + test('calls to reportEvents call `fetch` when shutting down if optIn value is set to true', async () => { + shipper.reportEvents(events); + shipper.optIn(true); + const counter = firstValueFrom(shipper.telemetryCounter$); + shipper.shutdown(); + expect(fetchMock).toHaveBeenCalledWith( + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { + body: '{"timestamp":"2020-01-01T00:00:00.000Z","event_type":"test-event-type","context":{},"properties":{}}\n', + headers: { + 'content-type': 'application/x-njson', + 'x-elastic-cluster-id': 'UNKNOWN', + 'x-elastic-stack-version': '1.2.3', + }, + keepalive: true, + method: 'POST', + query: { debug: true }, + } + ); + await expect(counter).resolves.toMatchInlineSnapshot(` + Object { + "code": "200", + "count": 1, + "event_type": "test-event-type", + "source": "elastic_v3_browser", + "type": "succeeded", + } + `); + }); + + test( + 'does not add the query.debug: true property to the request if the shipper is not set with the debug flag', + fakeSchedulers((advance) => { + shipper = new ElasticV3BrowserShipper( + { version: '1.2.3', channelName: 'test-channel' }, + initContext + ); + shipper.reportEvents(events); + shipper.optIn(true); + advance(1000); + expect(fetchMock).toHaveBeenCalledWith( + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { + body: '{"timestamp":"2020-01-01T00:00:00.000Z","event_type":"test-event-type","context":{},"properties":{}}\n', + headers: { + 'content-type': 'application/x-njson', + 'x-elastic-cluster-id': 'UNKNOWN', + 'x-elastic-stack-version': '1.2.3', + }, + keepalive: true, + method: 'POST', + } + ); + }) + ); + + test( + 'handles when the fetch request fails', + fakeSchedulers(async (advance) => { + fetchMock.mockRejectedValueOnce(new Error('Failed to fetch')); + shipper.reportEvents(events); + shipper.optIn(true); + const counter = firstValueFrom(shipper.telemetryCounter$); + advance(1000); + expect(fetchMock).toHaveBeenCalledWith( + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { + body: '{"timestamp":"2020-01-01T00:00:00.000Z","event_type":"test-event-type","context":{},"properties":{}}\n', + headers: { + 'content-type': 'application/x-njson', + 'x-elastic-cluster-id': 'UNKNOWN', + 'x-elastic-stack-version': '1.2.3', + }, + keepalive: true, + method: 'POST', + query: { debug: true }, + } + ); + await expect(counter).resolves.toMatchInlineSnapshot(` + Object { + "code": "Failed to fetch", + "count": 1, + "event_type": "test-event-type", + "source": "elastic_v3_browser", + "type": "failed", + } + `); + }) + ); + + test( + 'handles when the fetch request fails (request completes but not OK response)', + fakeSchedulers(async (advance) => { + fetchMock.mockResolvedValue({ + ok: false, + status: 400, + text: () => Promise.resolve('{"status": "not ok"}'), + }); + shipper.reportEvents(events); + shipper.optIn(true); + const counter = firstValueFrom(shipper.telemetryCounter$); + advance(1000); + expect(fetchMock).toHaveBeenCalledWith( + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { + body: '{"timestamp":"2020-01-01T00:00:00.000Z","event_type":"test-event-type","context":{},"properties":{}}\n', + headers: { + 'content-type': 'application/x-njson', + 'x-elastic-cluster-id': 'UNKNOWN', + 'x-elastic-stack-version': '1.2.3', + }, + keepalive: true, + method: 'POST', + query: { debug: true }, + } + ); + await expect(counter).resolves.toMatchInlineSnapshot(` + Object { + "code": "400", + "count": 1, + "event_type": "test-event-type", + "source": "elastic_v3_browser", + "type": "failed", + } + `); + }) + ); +}); diff --git a/packages/analytics/shippers/elastic_v3/browser/src/browser_shipper.ts b/packages/analytics/shippers/elastic_v3/browser/src/browser_shipper.ts new file mode 100644 index 0000000000000..5607d3d79a13d --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/browser/src/browser_shipper.ts @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { BehaviorSubject, interval, Subject, bufferWhen, concatMap, filter, skipWhile } from 'rxjs'; +import type { + AnalyticsClientInitContext, + Event, + EventContext, + IShipper, + TelemetryCounter, +} from '@kbn/analytics-client'; +import { ElasticV3ShipperOptions, ErrorWithCode } from '@kbn/analytics-shippers-elastic-v3-common'; +import { + buildHeaders, + buildUrl, + createTelemetryCounterHelper, + eventsToNDJSON, +} from '@kbn/analytics-shippers-elastic-v3-common'; + +export class ElasticV3BrowserShipper implements IShipper { + public static shipperName = 'elastic_v3_browser'; + public readonly telemetryCounter$ = new Subject(); + + private readonly reportTelemetryCounters = createTelemetryCounterHelper( + this.telemetryCounter$, + ElasticV3BrowserShipper.shipperName + ); + private readonly url: string; + + private readonly internalQueue$ = new Subject(); + + private readonly isOptedIn$ = new BehaviorSubject(undefined); + private clusterUuid: string = 'UNKNOWN'; + private licenseId: string | undefined; + + constructor( + private readonly options: ElasticV3ShipperOptions, + private readonly initContext: AnalyticsClientInitContext + ) { + this.setUpInternalQueueSubscriber(); + this.url = buildUrl({ + sendTo: options.sendTo ?? initContext.sendTo, + channelName: options.channelName, + }); + } + + public extendContext(newContext: EventContext) { + if (newContext.cluster_uuid) { + this.clusterUuid = newContext.cluster_uuid; + } + if (newContext.license_id) { + this.licenseId = newContext.license_id; + } + } + + public optIn(isOptedIn: boolean) { + this.isOptedIn$.next(isOptedIn); + } + + public reportEvents(events: Event[]) { + events.forEach((event) => { + this.internalQueue$.next(event); + }); + } + + public shutdown() { + this.internalQueue$.complete(); // NOTE: When completing the observable, the buffer logic does not wait and releases any buffered events. + } + + private setUpInternalQueueSubscriber() { + this.internalQueue$ + .pipe( + // Buffer events for 1 second or until we have an optIn value + bufferWhen(() => interval(1000).pipe(skipWhile(() => this.isOptedIn$.value === undefined))), + // Discard any events if we are not opted in + skipWhile(() => this.isOptedIn$.value === false), + // Skip empty buffers + filter((events) => events.length > 0), + // Send events + concatMap(async (events) => this.sendEvents(events)) + ) + .subscribe(); + } + + private async sendEvents(events: Event[]) { + try { + const code = await this.makeRequest(events); + this.reportTelemetryCounters(events, { code }); + } catch (error) { + this.reportTelemetryCounters(events, { code: error.code, error }); + } + } + + private async makeRequest(events: Event[]): Promise { + const { status, text, ok } = await fetch(this.url, { + method: 'POST', + body: eventsToNDJSON(events), + headers: buildHeaders(this.clusterUuid, this.options.version, this.licenseId), + ...(this.options.debug && { query: { debug: true } }), + // Allow the request to outlive the page in case the tab is closed + keepalive: true, + }); + + if (this.options.debug) { + this.initContext.logger.debug( + `[${ElasticV3BrowserShipper.shipperName}]: ${status} - ${await text()}` + ); + } + + if (!ok) { + throw new ErrorWithCode(`${status} - ${await text()}`, `${status}`); + } + + return `${status}`; + } +} diff --git a/packages/analytics/shippers/elastic_v3/browser/src/index.ts b/packages/analytics/shippers/elastic_v3/browser/src/index.ts new file mode 100644 index 0000000000000..9eb6668994037 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/browser/src/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type { ElasticV3ShipperOptions } from '@kbn/analytics-shippers-elastic-v3-common'; +export { ElasticV3BrowserShipper } from './browser_shipper'; diff --git a/packages/analytics/shippers/elastic_v3/browser/tsconfig.json b/packages/analytics/shippers/elastic_v3/browser/tsconfig.json new file mode 100644 index 0000000000000..b81a9a0217634 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/browser/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/analytics/shippers/elastic_v3/common/BUILD.bazel b/packages/analytics/shippers/elastic_v3/common/BUILD.bazel new file mode 100644 index 0000000000000..a09ee9019f93d --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/BUILD.bazel @@ -0,0 +1,125 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "common" +PKG_REQUIRE_NAME = "@kbn/analytics-shippers-elastic-v3-common" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ + "@npm//rxjs", + "//packages/analytics/client", +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//rxjs", + "//packages/analytics/client:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/analytics/shippers/elastic_v3/common/README.md b/packages/analytics/shippers/elastic_v3/common/README.md new file mode 100644 index 0000000000000..6684027b1ed7f --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/README.md @@ -0,0 +1,10 @@ +# @kbn/analytics-shippers-elastic-v3-common + +This package holds the common code for the Elastic V3 shippers: + +- Types defining the Shipper configuration `ElasticV3ShipperOptions` +- `buildUrl` utility helps decide which URL to use depending on whether the shipper is configured to send to production or staging. +- `eventsToNdjson` utility converts any array of events to NDJSON format. +- `reportTelemetryCounters` helps with building the TelemetryCounter to emit after processing an event. + +It should be considered an internal package and should not be used other than by the shipper implementations: `@kbn/analytics-shippers-elastic-v3-browser` and `@kbn/analytics-shippers-elastic-v3-server` diff --git a/packages/analytics/shippers/elastic_v3/common/jest.config.js b/packages/analytics/shippers/elastic_v3/common/jest.config.js new file mode 100644 index 0000000000000..1336211347fb7 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../../../', + roots: ['/packages/analytics/shippers/elastic_v3/common'], +}; diff --git a/packages/analytics/shippers/elastic_v3/common/package.json b/packages/analytics/shippers/elastic_v3/common/package.json new file mode 100644 index 0000000000000..ce8825e6bdc1f --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/analytics-shippers-elastic-v3-common", + "private": true, + "version": "1.0.0", + "browser": "./target_web/index.js", + "main": "./target_node/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/analytics/shippers/elastic_v3/common/src/build_headers.test.ts b/packages/analytics/shippers/elastic_v3/common/src/build_headers.test.ts new file mode 100644 index 0000000000000..468824916ec48 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/src/build_headers.test.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { buildHeaders } from './build_headers'; + +describe('buildHeaders', () => { + test('builds the headers as expected in the V3 endpoints', () => { + expect(buildHeaders('test-cluster', '1.2.3', 'test-license')).toMatchInlineSnapshot(` + Object { + "content-type": "application/x-njson", + "x-elastic-cluster-id": "test-cluster", + "x-elastic-license-id": "test-license", + "x-elastic-stack-version": "1.2.3", + } + `); + }); + + test('if license is not provided, it skips the license header', () => { + expect(buildHeaders('test-cluster', '1.2.3')).toMatchInlineSnapshot(` + Object { + "content-type": "application/x-njson", + "x-elastic-cluster-id": "test-cluster", + "x-elastic-stack-version": "1.2.3", + } + `); + }); +}); diff --git a/packages/analytics/shippers/elastic_v3/common/src/build_headers.ts b/packages/analytics/shippers/elastic_v3/common/src/build_headers.ts new file mode 100644 index 0000000000000..43126cf9d5629 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/src/build_headers.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export function buildHeaders(clusterUuid: string, version: string, licenseId?: string) { + return { + 'content-type': 'application/x-njson', + 'x-elastic-cluster-id': clusterUuid, + 'x-elastic-stack-version': version, + ...(licenseId && { 'x-elastic-license-id': licenseId }), + }; +} diff --git a/packages/analytics/shippers/elastic_v3/common/src/build_url.test.ts b/packages/analytics/shippers/elastic_v3/common/src/build_url.test.ts new file mode 100644 index 0000000000000..171491757ea56 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/src/build_url.test.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { buildUrl } from './build_url'; + +describe('buildUrl', () => { + test('returns production URL', () => { + expect(buildUrl({ sendTo: 'production', channelName: 'test-channel' })).toBe( + 'https://telemetry.elastic.co/v3/send/test-channel' + ); + }); + + test('returns staging URL', () => { + expect(buildUrl({ sendTo: 'staging', channelName: 'test-channel' })).toBe( + 'https://telemetry-staging.elastic.co/v3/send/test-channel' + ); + }); +}); diff --git a/packages/analytics/shippers/elastic_v3/common/src/build_url.ts b/packages/analytics/shippers/elastic_v3/common/src/build_url.ts new file mode 100644 index 0000000000000..f8d88b6804d08 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/src/build_url.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** + * Builds the URL for the V3 API. + * @param sendTo Whether to send it to production or staging. + * @param channelName The name of the channel to send the data to. + */ +export function buildUrl({ + sendTo, + channelName, +}: { + sendTo: 'production' | 'staging'; + channelName: string; +}): string { + const baseUrl = + sendTo === 'production' + ? 'https://telemetry.elastic.co' + : 'https://telemetry-staging.elastic.co'; + return `${baseUrl}/v3/send/${channelName}`; +} diff --git a/packages/analytics/shippers/elastic_v3/common/src/error_with_code.test.ts b/packages/analytics/shippers/elastic_v3/common/src/error_with_code.test.ts new file mode 100644 index 0000000000000..9be5d5bdc762c --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/src/error_with_code.test.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ErrorWithCode } from './error_with_code'; + +describe('ErrorWithCode', () => { + const error = new ErrorWithCode('test', 'test_code'); + test('message and code properties are publicly accessible', () => { + expect(error.message).toBe('test'); + expect(error.code).toBe('test_code'); + }); + test('extends error', () => { + expect(error).toBeInstanceOf(Error); + }); +}); diff --git a/packages/analytics/shippers/elastic_v3/common/src/error_with_code.ts b/packages/analytics/shippers/elastic_v3/common/src/error_with_code.ts new file mode 100644 index 0000000000000..09346575a2df1 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/src/error_with_code.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export class ErrorWithCode extends Error { + constructor(message: string, public readonly code: string) { + super(message); + } +} diff --git a/packages/analytics/shippers/elastic_v3/common/src/events_to_ndjson.test.ts b/packages/analytics/shippers/elastic_v3/common/src/events_to_ndjson.test.ts new file mode 100644 index 0000000000000..4033bcb862967 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/src/events_to_ndjson.test.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { Event } from '@kbn/analytics-client'; +import { eventsToNDJSON } from './events_to_ndjson'; + +describe('eventsToNDJSON', () => { + test('works with one event', () => { + const event: Event = { + timestamp: '2020-01-01T00:00:00.000Z', + event_type: 'event_type', + context: {}, + properties: {}, + }; + + // Mind the extra line at the bottom + expect(eventsToNDJSON([event])).toMatchInlineSnapshot(` + "{\\"timestamp\\":\\"2020-01-01T00:00:00.000Z\\",\\"event_type\\":\\"event_type\\",\\"context\\":{},\\"properties\\":{}} + " + `); + }); + + test('works with many events', () => { + const events: Event[] = [ + { + timestamp: '2020-01-01T00:00:00.000Z', + event_type: 'event_type', + context: {}, + properties: {}, + }, + { + timestamp: '2020-01-02T00:00:00.000Z', + event_type: 'event_type', + context: {}, + properties: {}, + }, + ]; + + expect(eventsToNDJSON(events)).toMatchInlineSnapshot(` + "{\\"timestamp\\":\\"2020-01-01T00:00:00.000Z\\",\\"event_type\\":\\"event_type\\",\\"context\\":{},\\"properties\\":{}} + {\\"timestamp\\":\\"2020-01-02T00:00:00.000Z\\",\\"event_type\\":\\"event_type\\",\\"context\\":{},\\"properties\\":{}} + " + `); + }); +}); diff --git a/packages/analytics/shippers/elastic_v3/common/src/events_to_ndjson.ts b/packages/analytics/shippers/elastic_v3/common/src/events_to_ndjson.ts new file mode 100644 index 0000000000000..af768770a0d5f --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/src/events_to_ndjson.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { Event } from '@kbn/analytics-client'; + +export function eventsToNDJSON(events: Event[]): string { + return `${events.map((event) => JSON.stringify(event)).join('\n')}\n`; +} diff --git a/packages/analytics/shippers/elastic_v3/common/src/index.ts b/packages/analytics/shippers/elastic_v3/common/src/index.ts new file mode 100644 index 0000000000000..043be5c33c6fe --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/src/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 and the Server Side Public License, v 1; you may 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 { buildHeaders } from './build_headers'; +export { buildUrl } from './build_url'; +export { ErrorWithCode } from './error_with_code'; +export { eventsToNDJSON } from './events_to_ndjson'; +export { createTelemetryCounterHelper } from './report_telemetry_counters'; +export type { ElasticV3ShipperOptions } from './types'; diff --git a/packages/analytics/shippers/elastic_v3/common/src/report_telemetry_counters.test.ts b/packages/analytics/shippers/elastic_v3/common/src/report_telemetry_counters.test.ts new file mode 100644 index 0000000000000..13a59eb600db5 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/src/report_telemetry_counters.test.ts @@ -0,0 +1,203 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { firstValueFrom, Subject, take, toArray } from 'rxjs'; +import type { Event, TelemetryCounter } from '@kbn/analytics-client'; +import { TelemetryCounterType } from '@kbn/analytics-client'; +import { createTelemetryCounterHelper } from './report_telemetry_counters'; + +describe('reportTelemetryCounters', () => { + let reportTelemetryCounters: ReturnType; + let telemetryCounter$: Subject; + + beforeEach(() => { + telemetryCounter$ = new Subject(); + reportTelemetryCounters = createTelemetryCounterHelper(telemetryCounter$, 'my_shipper'); + }); + + test('emits a success counter for one event', async () => { + const events: Event[] = [ + { + timestamp: '2020-01-01T00:00:00.000Z', + event_type: 'event_type_a', + context: {}, + properties: {}, + }, + ]; + + const counters = firstValueFrom(telemetryCounter$); + + reportTelemetryCounters(events); + + await expect(counters).resolves.toMatchInlineSnapshot(` + Object { + "code": "OK", + "count": 1, + "event_type": "event_type_a", + "source": "my_shipper", + "type": "succeeded", + } + `); + }); + + test('emits a success counter for one event with custom code', async () => { + const events: Event[] = [ + { + timestamp: '2020-01-01T00:00:00.000Z', + event_type: 'event_type_a', + context: {}, + properties: {}, + }, + ]; + + const counters = firstValueFrom(telemetryCounter$); + + reportTelemetryCounters(events, { code: 'my_code' }); + + await expect(counters).resolves.toMatchInlineSnapshot(` + Object { + "code": "my_code", + "count": 1, + "event_type": "event_type_a", + "source": "my_shipper", + "type": "succeeded", + } + `); + }); + + test('emits a counter with custom type', async () => { + const events: Event[] = [ + { + timestamp: '2020-01-01T00:00:00.000Z', + event_type: 'event_type_a', + context: {}, + properties: {}, + }, + ]; + + const counters = firstValueFrom(telemetryCounter$); + + reportTelemetryCounters(events, { + type: TelemetryCounterType.dropped, + code: 'my_code', + }); + + await expect(counters).resolves.toMatchInlineSnapshot(` + Object { + "code": "my_code", + "count": 1, + "event_type": "event_type_a", + "source": "my_shipper", + "type": "dropped", + } + `); + }); + + test('emits a failure counter for one event with error message as a code', async () => { + const events: Event[] = [ + { + timestamp: '2020-01-01T00:00:00.000Z', + event_type: 'event_type_a', + context: {}, + properties: {}, + }, + ]; + + const counters = firstValueFrom(telemetryCounter$); + + reportTelemetryCounters(events, { + error: new Error('Something went terribly wrong'), + }); + + await expect(counters).resolves.toMatchInlineSnapshot(` + Object { + "code": "Something went terribly wrong", + "count": 1, + "event_type": "event_type_a", + "source": "my_shipper", + "type": "failed", + } + `); + }); + + test('emits a failure counter for one event with custom code', async () => { + const events: Event[] = [ + { + timestamp: '2020-01-01T00:00:00.000Z', + event_type: 'event_type_a', + context: {}, + properties: {}, + }, + ]; + + const counters = firstValueFrom(telemetryCounter$); + + reportTelemetryCounters(events, { + code: 'my_code', + error: new Error('Something went terribly wrong'), + }); + + await expect(counters).resolves.toMatchInlineSnapshot(` + Object { + "code": "my_code", + "count": 1, + "event_type": "event_type_a", + "source": "my_shipper", + "type": "failed", + } + `); + }); + + test('emits a success counter for multiple events of different types', async () => { + const events: Event[] = [ + // 2 types a + { + timestamp: '2020-01-01T00:00:00.000Z', + event_type: 'event_type_a', + context: {}, + properties: {}, + }, + { + timestamp: '2020-01-01T00:00:00.000Z', + event_type: 'event_type_a', + context: {}, + properties: {}, + }, + // 1 type b + { + timestamp: '2020-01-01T00:00:00.000Z', + event_type: 'event_type_b', + context: {}, + properties: {}, + }, + ]; + + const counters = firstValueFrom(telemetryCounter$.pipe(take(2), toArray())); + + reportTelemetryCounters(events); + + await expect(counters).resolves.toMatchInlineSnapshot(` + Array [ + Object { + "code": "OK", + "count": 2, + "event_type": "event_type_a", + "source": "my_shipper", + "type": "succeeded", + }, + Object { + "code": "OK", + "count": 1, + "event_type": "event_type_b", + "source": "my_shipper", + "type": "succeeded", + }, + ] + `); + }); +}); diff --git a/packages/analytics/shippers/elastic_v3/common/src/report_telemetry_counters.ts b/packages/analytics/shippers/elastic_v3/common/src/report_telemetry_counters.ts new file mode 100644 index 0000000000000..e4b63bcf2710f --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/src/report_telemetry_counters.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { Subject } from 'rxjs'; +import type { Event, TelemetryCounter } from '@kbn/analytics-client'; +import { TelemetryCounterType } from '@kbn/analytics-client'; + +/** + * Creates a telemetry counter helper to make it easier to generate them + * @param telemetryCounter$ The observable that will be used to emit the telemetry counters + * @param source The name of the shipper that is sending the events. + */ +export function createTelemetryCounterHelper( + telemetryCounter$: Subject, + source: string +) { + /** + * Triggers a telemetry counter for each event type. + * @param events The events to trigger the telemetry counter for. + * @param type The type of telemetry counter to trigger. + * @param code The success or error code for additional detail about the result. + * @param error The error that occurred, if any. + */ + return ( + events: Event[], + { type, code, error }: { type?: TelemetryCounterType; code?: string; error?: Error } = {} + ) => { + const eventTypeCounts = countEventTypes(events); + Object.entries(eventTypeCounts).forEach(([eventType, count]) => { + telemetryCounter$.next({ + source, + type: type ?? (error ? TelemetryCounterType.failed : TelemetryCounterType.succeeded), + code: code ?? error?.message ?? 'OK', + count, + event_type: eventType, + }); + }); + }; +} + +function countEventTypes(events: Event[]) { + return events.reduce((acc, event) => { + if (acc[event.event_type]) { + acc[event.event_type] += 1; + } else { + acc[event.event_type] = 1; + } + return acc; + }, {} as Record); +} diff --git a/packages/analytics/shippers/elastic_v3/common/src/types.ts b/packages/analytics/shippers/elastic_v3/common/src/types.ts new file mode 100644 index 0000000000000..c27aa448119fe --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/src/types.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** + * Options for the Elastic V3 shipper + */ +export interface ElasticV3ShipperOptions { + /** + * The name of the channel to stream all the events to. + */ + channelName: string; + /** + * The product's version. + */ + version: string; + /** + * Provide it to override the Analytics client's default configuration. + */ + sendTo?: 'staging' | 'production'; + /** + * Should show debug information about the requests it makes to the V3 API. + */ + debug?: boolean; +} diff --git a/packages/analytics/shippers/elastic_v3/common/tsconfig.json b/packages/analytics/shippers/elastic_v3/common/tsconfig.json new file mode 100644 index 0000000000000..b81a9a0217634 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/common/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/analytics/shippers/elastic_v3/server/BUILD.bazel b/packages/analytics/shippers/elastic_v3/server/BUILD.bazel new file mode 100644 index 0000000000000..c05164d1f2b7e --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/server/BUILD.bazel @@ -0,0 +1,123 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "server" +PKG_REQUIRE_NAME = "@kbn/analytics-shippers-elastic-v3-server" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ + "@npm//node-fetch", + "@npm//rxjs", + "//packages/analytics/client", + "//packages/analytics/shippers/elastic_v3/common", +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/node-fetch", + "@npm//@types/jest", + "@npm//rxjs", + "//packages/analytics/client:npm_module_types", + "//packages/analytics/shippers/elastic_v3/common:npm_module_types", + "//packages/kbn-logging-mocks:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/analytics/shippers/elastic_v3/server/README.md b/packages/analytics/shippers/elastic_v3/server/README.md new file mode 100644 index 0000000000000..ccdcf6a66cbe2 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/server/README.md @@ -0,0 +1,25 @@ +# @kbn/analytics-shippers-elastic-v3-server + +Server-side implementation of the Elastic V3 shipper for the `@kbn/analytics-client`. + +## How to use it + +This module is intended to be used **on the server-side only**. It is specially designed to apply the necessary backpressure mechanisms to prevent the server from getting overloaded with too many events and identify if the server sits behind a firewall to discard any incoming events. Refer to `@kbn/analytics-shippers-elastic-v3-browser` for the browser-side implementation. + +```typescript +import { ElasticV3ServerShipper } from "@kbn/analytics-shippers-elastic-v3-server"; + +analytics.registerShipper(ElasticV3ServerShipper, { channelName: 'myChannel', version: '1.0.0' }); +``` + +## Configuration + +| Name | Description | +|:-------------:|:-------------------------------------------------------------------------------------------| +| `channelName` | The name of the channel to send the events. | +| `version` | The version of the application generating the events. | +| `debug` | When `true`, it logs the responses from the remote Telemetry Service. Defaults to `false`. | + +## Transmission protocol + +This shipper sends the events to the Elastic Internal Telemetry Service. It holds up to 1000 events in a shared queue. Any additional incoming events once it's full will be dropped. It sends the events from the queue in batches of 10kB every 10 seconds. If not enough events are available in the queue for longer than 10 minutes, it will send any remaining events. When shutting down, it'll send all the remaining events in the queue. diff --git a/packages/analytics/shippers/elastic_v3/server/jest.config.js b/packages/analytics/shippers/elastic_v3/server/jest.config.js new file mode 100644 index 0000000000000..4397eb3e50c5a --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/server/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../../../', + roots: ['/packages/analytics/shippers/elastic_v3/server'], +}; diff --git a/packages/analytics/shippers/elastic_v3/server/package.json b/packages/analytics/shippers/elastic_v3/server/package.json new file mode 100644 index 0000000000000..5ab24844eb1ba --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/server/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/analytics-shippers-elastic-v3-server", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/analytics/shippers/elastic_v3/server/src/index.ts b/packages/analytics/shippers/elastic_v3/server/src/index.ts new file mode 100644 index 0000000000000..50faf3c72ab29 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/server/src/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type { ElasticV3ShipperOptions } from '@kbn/analytics-shippers-elastic-v3-common'; +export { ElasticV3ServerShipper } from './server_shipper'; diff --git a/packages/analytics/shippers/elastic_v3/server/src/server_shipper.test.mocks.ts b/packages/analytics/shippers/elastic_v3/server/src/server_shipper.test.mocks.ts new file mode 100644 index 0000000000000..8da36242d48b6 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/server/src/server_shipper.test.mocks.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 and the Server Side Public License, v 1; you may 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 fetchMock = jest.fn().mockResolvedValue({ + status: 200, + ok: true, + text: () => Promise.resolve('{"status": "ok"}'), +}); + +jest.doMock('node-fetch', () => fetchMock); diff --git a/packages/analytics/shippers/elastic_v3/server/src/server_shipper.test.ts b/packages/analytics/shippers/elastic_v3/server/src/server_shipper.test.ts new file mode 100644 index 0000000000000..ffdfd797437a9 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/server/src/server_shipper.test.ts @@ -0,0 +1,438 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { loggerMock } from '@kbn/logging-mocks'; +import { firstValueFrom } from 'rxjs'; +import { fakeSchedulers } from 'rxjs-marbles/jest'; +import type { AnalyticsClientInitContext, Event } from '@kbn/analytics-client'; +import { fetchMock } from './server_shipper.test.mocks'; +import { ElasticV3ServerShipper } from './server_shipper'; + +const SECONDS = 1000; +const MINUTES = 60 * SECONDS; + +describe('ElasticV3ServerShipper', () => { + const events: Event[] = [ + { + timestamp: '2020-01-01T00:00:00.000Z', + event_type: 'test-event-type', + context: {}, + properties: {}, + }, + ]; + + const nextTick = () => new Promise((resolve) => setImmediate(resolve)); + + const initContext: AnalyticsClientInitContext = { + sendTo: 'staging', + isDev: true, + logger: loggerMock.create(), + }; + + let shipper: ElasticV3ServerShipper; + + // eslint-disable-next-line dot-notation + const setLastBatchSent = (ms: number) => (shipper['lastBatchSent'] = ms); + + beforeEach(() => { + jest.useFakeTimers(); + + shipper = new ElasticV3ServerShipper( + { version: '1.2.3', channelName: 'test-channel', debug: true }, + initContext + ); + // eslint-disable-next-line dot-notation + shipper['firstTimeOffline'] = null; + }); + + afterEach(() => { + shipper.shutdown(); + jest.clearAllMocks(); + }); + + test('set optIn should update the isOptedIn$ observable', () => { + // eslint-disable-next-line dot-notation + const getInternalOptIn = () => shipper['isOptedIn']; + + // Initially undefined + expect(getInternalOptIn()).toBeUndefined(); + + shipper.optIn(true); + expect(getInternalOptIn()).toBe(true); + + shipper.optIn(false); + expect(getInternalOptIn()).toBe(false); + }); + + test('clears the queue after optIn: false', () => { + shipper.reportEvents(events); + // eslint-disable-next-line dot-notation + expect(shipper['internalQueue'].length).toBe(1); + + shipper.optIn(false); + // eslint-disable-next-line dot-notation + expect(shipper['internalQueue'].length).toBe(0); + }); + + test('set extendContext should store local values: clusterUuid and licenseId', () => { + // eslint-disable-next-line dot-notation + const getInternalClusterUuid = () => shipper['clusterUuid']; + // eslint-disable-next-line dot-notation + const getInternalLicenseId = () => shipper['licenseId']; + + // Initial values + expect(getInternalClusterUuid()).toBe('UNKNOWN'); + expect(getInternalLicenseId()).toBeUndefined(); + + shipper.extendContext({ cluster_uuid: 'test-cluster-uuid' }); + expect(getInternalClusterUuid()).toBe('test-cluster-uuid'); + expect(getInternalLicenseId()).toBeUndefined(); + + shipper.extendContext({ license_id: 'test-license-id' }); + expect(getInternalClusterUuid()).toBe('test-cluster-uuid'); + expect(getInternalLicenseId()).toBe('test-license-id'); + + shipper.extendContext({ cluster_uuid: 'test-cluster-uuid-2', license_id: 'test-license-id-2' }); + expect(getInternalClusterUuid()).toBe('test-cluster-uuid-2'); + expect(getInternalLicenseId()).toBe('test-license-id-2'); + }); + + test('calls to reportEvents do not call `fetch` straight away', () => { + shipper.reportEvents(events); + expect(fetchMock).not.toHaveBeenCalled(); + }); + + test( + 'calls to reportEvents do not call `fetch` after 10 minutes because no optIn value is set yet', + fakeSchedulers((advance) => { + shipper.reportEvents(events); + advance(10 * MINUTES); + expect(fetchMock).not.toHaveBeenCalled(); + }) + ); + + test( + 'calls to reportEvents call `fetch` after 10 minutes when optIn value is set to true', + fakeSchedulers(async (advance) => { + shipper.reportEvents(events); + shipper.optIn(true); + const counter = firstValueFrom(shipper.telemetryCounter$); + setLastBatchSent(Date.now() - 10 * MINUTES); + advance(10 * MINUTES); + expect(fetchMock).toHaveBeenCalledWith( + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { + body: '{"timestamp":"2020-01-01T00:00:00.000Z","event_type":"test-event-type","context":{},"properties":{}}\n', + headers: { + 'content-type': 'application/x-njson', + 'x-elastic-cluster-id': 'UNKNOWN', + 'x-elastic-stack-version': '1.2.3', + }, + method: 'POST', + query: { debug: true }, + } + ); + await expect(counter).resolves.toMatchInlineSnapshot(` + Object { + "code": "200", + "count": 1, + "event_type": "test-event-type", + "source": "elastic_v3_server", + "type": "succeeded", + } + `); + }) + ); + + test( + 'calls to reportEvents do not call `fetch` after 10 minutes when optIn value is set to false', + fakeSchedulers((advance) => { + shipper.reportEvents(events); + shipper.optIn(false); + setLastBatchSent(Date.now() - 10 * MINUTES); + advance(10 * MINUTES); + expect(fetchMock).not.toHaveBeenCalled(); + }) + ); + + test('calls to reportEvents call `fetch` when shutting down if optIn value is set to true', async () => { + shipper.reportEvents(events); + shipper.optIn(true); + const counter = firstValueFrom(shipper.telemetryCounter$); + shipper.shutdown(); + await nextTick(); // We are handling the shutdown in a promise, so we need to wait for the next tick. + expect(fetchMock).toHaveBeenCalledWith( + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { + body: '{"timestamp":"2020-01-01T00:00:00.000Z","event_type":"test-event-type","context":{},"properties":{}}\n', + headers: { + 'content-type': 'application/x-njson', + 'x-elastic-cluster-id': 'UNKNOWN', + 'x-elastic-stack-version': '1.2.3', + }, + method: 'POST', + query: { debug: true }, + } + ); + await expect(counter).resolves.toMatchInlineSnapshot(` + Object { + "code": "200", + "count": 1, + "event_type": "test-event-type", + "source": "elastic_v3_server", + "type": "succeeded", + } + `); + }); + + test( + 'does not add the query.debug: true property to the request if the shipper is not set with the debug flag', + fakeSchedulers((advance) => { + shipper = new ElasticV3ServerShipper( + { version: '1.2.3', channelName: 'test-channel' }, + initContext + ); + // eslint-disable-next-line dot-notation + shipper['firstTimeOffline'] = null; + shipper.reportEvents(events); + shipper.optIn(true); + setLastBatchSent(Date.now() - 10 * MINUTES); + advance(10 * MINUTES); + expect(fetchMock).toHaveBeenCalledWith( + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { + body: '{"timestamp":"2020-01-01T00:00:00.000Z","event_type":"test-event-type","context":{},"properties":{}}\n', + headers: { + 'content-type': 'application/x-njson', + 'x-elastic-cluster-id': 'UNKNOWN', + 'x-elastic-stack-version': '1.2.3', + }, + method: 'POST', + } + ); + }) + ); + + test( + 'sends when the queue overflows the 10kB leaky bucket one batch every 10s', + fakeSchedulers(async (advance) => { + expect.assertions(2 * 9 + 2); + + shipper.reportEvents(new Array(1000).fill(events[0])); + shipper.optIn(true); + + // Due to the size of the test events, it matches 9 rounds. + for (let i = 0; i < 9; i++) { + const counter = firstValueFrom(shipper.telemetryCounter$); + setLastBatchSent(Date.now() - 10 * SECONDS); + advance(10 * SECONDS); + expect(fetchMock).toHaveBeenNthCalledWith( + i + 1, + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { + body: new Array(103) + .fill( + '{"timestamp":"2020-01-01T00:00:00.000Z","event_type":"test-event-type","context":{},"properties":{}}\n' + ) + .join(''), + headers: { + 'content-type': 'application/x-njson', + 'x-elastic-cluster-id': 'UNKNOWN', + 'x-elastic-stack-version': '1.2.3', + }, + method: 'POST', + query: { debug: true }, + } + ); + await expect(counter).resolves.toMatchInlineSnapshot(` + Object { + "code": "200", + "count": 103, + "event_type": "test-event-type", + "source": "elastic_v3_server", + "type": "succeeded", + } + `); + await nextTick(); + } + // eslint-disable-next-line dot-notation + expect(shipper['internalQueue'].length).toBe(1000 - 9 * 103); // 73 + + // If we call it again, it should not enqueue all the events (only the ones to fill the queue): + shipper.reportEvents(new Array(1000).fill(events[0])); + // eslint-disable-next-line dot-notation + expect(shipper['internalQueue'].length).toBe(1000); + }) + ); + + test( + 'handles when the fetch request fails', + fakeSchedulers(async (advance) => { + fetchMock.mockRejectedValueOnce(new Error('Failed to fetch')); + shipper.reportEvents(events); + shipper.optIn(true); + const counter = firstValueFrom(shipper.telemetryCounter$); + setLastBatchSent(Date.now() - 10 * MINUTES); + advance(10 * MINUTES); + expect(fetchMock).toHaveBeenCalledWith( + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { + body: '{"timestamp":"2020-01-01T00:00:00.000Z","event_type":"test-event-type","context":{},"properties":{}}\n', + headers: { + 'content-type': 'application/x-njson', + 'x-elastic-cluster-id': 'UNKNOWN', + 'x-elastic-stack-version': '1.2.3', + }, + method: 'POST', + query: { debug: true }, + } + ); + await expect(counter).resolves.toMatchInlineSnapshot(` + Object { + "code": "Failed to fetch", + "count": 1, + "event_type": "test-event-type", + "source": "elastic_v3_server", + "type": "failed", + } + `); + }) + ); + + test( + 'handles when the fetch request fails (request completes but not OK response)', + fakeSchedulers(async (advance) => { + fetchMock.mockResolvedValueOnce({ + ok: false, + status: 400, + text: () => Promise.resolve('{"status": "not ok"}'), + }); + shipper.reportEvents(events); + shipper.optIn(true); + const counter = firstValueFrom(shipper.telemetryCounter$); + setLastBatchSent(Date.now() - 10 * MINUTES); + advance(10 * MINUTES); + expect(fetchMock).toHaveBeenCalledWith( + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { + body: '{"timestamp":"2020-01-01T00:00:00.000Z","event_type":"test-event-type","context":{},"properties":{}}\n', + headers: { + 'content-type': 'application/x-njson', + 'x-elastic-cluster-id': 'UNKNOWN', + 'x-elastic-stack-version': '1.2.3', + }, + method: 'POST', + query: { debug: true }, + } + ); + await expect(counter).resolves.toMatchInlineSnapshot(` + Object { + "code": "400", + "count": 1, + "event_type": "test-event-type", + "source": "elastic_v3_server", + "type": "failed", + } + `); + }) + ); + + test( + 'connectivity check is run after report failure', + fakeSchedulers(async (advance) => { + fetchMock.mockRejectedValueOnce(new Error('Failed to fetch')); + shipper.reportEvents(events); + shipper.optIn(true); + const counter = firstValueFrom(shipper.telemetryCounter$); + setLastBatchSent(Date.now() - 10 * MINUTES); + advance(10 * MINUTES); + expect(fetchMock).toHaveBeenNthCalledWith( + 1, + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { + body: '{"timestamp":"2020-01-01T00:00:00.000Z","event_type":"test-event-type","context":{},"properties":{}}\n', + headers: { + 'content-type': 'application/x-njson', + 'x-elastic-cluster-id': 'UNKNOWN', + 'x-elastic-stack-version': '1.2.3', + }, + method: 'POST', + query: { debug: true }, + } + ); + await expect(counter).resolves.toMatchInlineSnapshot(` + Object { + "code": "Failed to fetch", + "count": 1, + "event_type": "test-event-type", + "source": "elastic_v3_server", + "type": "failed", + } + `); + fetchMock.mockRejectedValueOnce(new Error('Failed to fetch')); + advance(1 * MINUTES); + await nextTick(); + expect(fetchMock).toHaveBeenNthCalledWith( + 2, + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { method: 'OPTIONS' } + ); + fetchMock.mockResolvedValueOnce({ ok: false }); + advance(2 * MINUTES); + await nextTick(); + expect(fetchMock).toHaveBeenNthCalledWith( + 3, + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { method: 'OPTIONS' } + ); + + // let's see the effect of after 24 hours: + shipper.reportEvents(events); + // eslint-disable-next-line dot-notation + expect(shipper['internalQueue'].length).toBe(1); + // eslint-disable-next-line dot-notation + shipper['firstTimeOffline'] = 100; + + fetchMock.mockResolvedValueOnce({ ok: false }); + advance(4 * MINUTES); + await nextTick(); + expect(fetchMock).toHaveBeenNthCalledWith( + 4, + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { method: 'OPTIONS' } + ); + // eslint-disable-next-line dot-notation + expect(shipper['internalQueue'].length).toBe(0); + + // New events are not added to the queue because it's been offline for 24 hours. + shipper.reportEvents(events); + // eslint-disable-next-line dot-notation + expect(shipper['internalQueue'].length).toBe(0); + + // Regains connection + fetchMock.mockResolvedValueOnce({ ok: true }); + advance(8 * MINUTES); + await nextTick(); + expect(fetchMock).toHaveBeenNthCalledWith( + 5, + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { method: 'OPTIONS' } + ); + // eslint-disable-next-line dot-notation + expect(shipper['firstTimeOffline']).toBe(null); + + advance(16 * MINUTES); + await nextTick(); + expect(fetchMock).not.toHaveBeenNthCalledWith( + 6, + 'https://telemetry-staging.elastic.co/v3/send/test-channel', + { method: 'OPTIONS' } + ); + }) + ); +}); diff --git a/packages/analytics/shippers/elastic_v3/server/src/server_shipper.ts b/packages/analytics/shippers/elastic_v3/server/src/server_shipper.ts new file mode 100644 index 0000000000000..1c1277f7d7b4b --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/server/src/server_shipper.ts @@ -0,0 +1,290 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 fetch from 'node-fetch'; +import { + filter, + Subject, + interval, + concatMap, + merge, + from, + firstValueFrom, + timer, + retryWhen, + tap, + delayWhen, + takeWhile, +} from 'rxjs'; +import type { + AnalyticsClientInitContext, + Event, + EventContext, + IShipper, + TelemetryCounter, +} from '@kbn/analytics-client'; +import { TelemetryCounterType } from '@kbn/analytics-client'; +import type { ElasticV3ShipperOptions } from '@kbn/analytics-shippers-elastic-v3-common'; +import { + buildHeaders, + buildUrl, + createTelemetryCounterHelper, + eventsToNDJSON, + ErrorWithCode, +} from '@kbn/analytics-shippers-elastic-v3-common'; + +const SECOND = 1000; +const MINUTE = 60 * SECOND; +const HOUR = 60 * MINUTE; +const KIB = 1024; +const MAX_NUMBER_OF_EVENTS_IN_INTERNAL_QUEUE = 1000; + +export class ElasticV3ServerShipper implements IShipper { + public static shipperName = 'elastic_v3_server'; + public readonly telemetryCounter$ = new Subject(); + + private readonly reportTelemetryCounters = createTelemetryCounterHelper( + this.telemetryCounter$, + ElasticV3ServerShipper.shipperName + ); + + private readonly internalQueue: Event[] = []; + private readonly shutdown$ = new Subject(); + + private readonly url: string; + + private lastBatchSent = Date.now(); + + private clusterUuid: string = 'UNKNOWN'; + private licenseId?: string; + private isOptedIn?: boolean; + + /** + * Specifies when it went offline: + * - `undefined` means it doesn't know yet whether it is online or offline + * - `null` means it's online + * - `number` means it's offline since that time + * @private + */ + private firstTimeOffline?: number | null; + + constructor( + private readonly options: ElasticV3ShipperOptions, + private readonly initContext: AnalyticsClientInitContext + ) { + this.url = buildUrl({ + sendTo: options.sendTo ?? initContext.sendTo, + channelName: options.channelName, + }); + this.setInternalSubscriber(); + this.checkConnectivity(); + } + + public extendContext(newContext: EventContext) { + if (newContext.cluster_uuid) { + this.clusterUuid = newContext.cluster_uuid; + } + if (newContext.license_id) { + this.licenseId = newContext.license_id; + } + } + + public optIn(isOptedIn: boolean) { + this.isOptedIn = isOptedIn; + + if (isOptedIn === false) { + this.internalQueue.length = 0; + } + } + + public reportEvents(events: Event[]) { + if ( + this.isOptedIn === false || + (this.firstTimeOffline && Date.now() - this.firstTimeOffline > 24 * HOUR) + ) { + return; + } + + const freeSpace = MAX_NUMBER_OF_EVENTS_IN_INTERNAL_QUEUE - this.internalQueue.length; + + // As per design, we only want store up-to 1000 events at a time. Drop anything that goes beyond that limit + if (freeSpace < events.length) { + const toDrop = events.length - freeSpace; + const droppedEvents = events.splice(-toDrop, toDrop); + this.reportTelemetryCounters(droppedEvents, { + type: TelemetryCounterType.dropped, + code: 'queue_full', + }); + } + + this.internalQueue.push(...events); + } + + public shutdown() { + this.shutdown$.complete(); + } + + /** + * Checks the server has connectivity to the remote endpoint. + * The frequency of the connectivity tests will back off, starting with 1 minute, and multiplying by 2 + * until it reaches 1 hour. Then, it’ll keep the 1h frequency until it reaches 24h without connectivity. + * At that point, it clears the queue and stops accepting events in the queue. + * The connectivity checks will continue to happen every 1 hour just in case it regains it at any point. + * @private + */ + private checkConnectivity() { + let backoff = 1 * MINUTE; + timer(0, 1 * MINUTE) + .pipe( + takeWhile(() => this.shutdown$.isStopped === false), + filter(() => this.isOptedIn === true && this.firstTimeOffline !== null), + concatMap(async () => { + const { ok } = await fetch(this.url, { + method: 'OPTIONS', + }); + + if (!ok) { + throw new Error(`Failed to connect to ${this.url}`); + } + + this.firstTimeOffline = null; + backoff = 1 * MINUTE; + }), + retryWhen((errors) => + errors.pipe( + takeWhile(() => this.shutdown$.isStopped === false), + tap(() => { + if (!this.firstTimeOffline) { + this.firstTimeOffline = Date.now(); + } else if (Date.now() - this.firstTimeOffline > 24 * HOUR) { + this.internalQueue.length = 0; + } + backoff = backoff * 2; + if (backoff > 1 * HOUR) { + backoff = 1 * HOUR; + } + }), + delayWhen(() => timer(backoff)) + ) + ) + ) + .subscribe(); + } + + private setInternalSubscriber() { + // Check the status of the queues every 1 second. + merge( + interval(1000), + // Using a promise because complete does not emit through the pipe. + from(firstValueFrom(this.shutdown$, { defaultValue: true })) + ) + .pipe( + // Only move ahead if it's opted-in and online. + filter(() => this.isOptedIn === true && this.firstTimeOffline === null), + + // Send the events now if (validations sorted from cheapest to most CPU expensive): + // - We are shutting down. + // - There are some events in the queue, and we didn't send anything in the last 10 minutes. + // - The last time we sent was more than 10 seconds ago and: + // - We reached the minimum batch size of 10kB per request in our leaky bucket. + // - The queue is full (meaning we'll never get to 10kB because the events are very small). + filter( + () => + (this.internalQueue.length > 0 && + (this.shutdown$.isStopped || Date.now() - this.lastBatchSent >= 10 * MINUTE)) || + (Date.now() - this.lastBatchSent >= 10 * SECOND && + (this.internalQueue.length === 1000 || + this.getQueueByteSize(this.internalQueue) >= 10 * KIB)) + ), + + // Send the events + concatMap(async () => { + this.lastBatchSent = Date.now(); + const eventsToSend = this.getEventsToSend(); + await this.sendEvents(eventsToSend); + }), + + // Stop the subscriber if we are shutting down. + takeWhile(() => !this.shutdown$.isStopped) + ) + .subscribe(); + } + + /** + * Calculates the size of the queue in bytes. + * @returns The number of bytes held in the queue. + * @private + */ + private getQueueByteSize(queue: Event[]) { + return queue.reduce((acc, event) => { + return acc + this.getEventSize(event); + }, 0); + } + + /** + * Calculates the size of the event in bytes. + * @param event The event to calculate the size of. + * @returns The number of bytes held in the event. + * @private + */ + private getEventSize(event: Event) { + return Buffer.from(JSON.stringify(event)).length; + } + + /** + * Returns a queue of events of up-to 10kB. + * @remarks It mutates the internal queue by removing from it the events returned by this method. + * @private + */ + private getEventsToSend(): Event[] { + // If the internal queue is already smaller than the minimum batch size, do a direct assignment. + if (this.getQueueByteSize(this.internalQueue) < 10 * KIB) { + return this.internalQueue.splice(0, this.internalQueue.length); + } + // Otherwise, we'll feed the events to the leaky bucket queue until we reach 10kB. + const queue: Event[] = []; + let queueByteSize = 0; + while (queueByteSize < 10 * KIB) { + const event = this.internalQueue.shift()!; + queueByteSize += this.getEventSize(event); + queue.push(event); + } + return queue; + } + + private async sendEvents(events: Event[]) { + try { + const code = await this.makeRequest(events); + this.reportTelemetryCounters(events, { code }); + } catch (error) { + this.reportTelemetryCounters(events, { code: error.code, error }); + this.firstTimeOffline = undefined; + } + } + + private async makeRequest(events: Event[]): Promise { + const { status, text, ok } = await fetch(this.url, { + method: 'POST', + body: eventsToNDJSON(events), + headers: buildHeaders(this.clusterUuid, this.options.version, this.licenseId), + ...(this.options.debug && { query: { debug: true } }), + }); + + if (this.options.debug) { + this.initContext.logger.debug( + `[${ElasticV3ServerShipper.shipperName}]: ${status} - ${await text()}` + ); + } + + if (!ok) { + throw new ErrorWithCode(`${status} - ${await text()}`, `${status}`); + } + + return `${status}`; + } +} diff --git a/packages/analytics/shippers/elastic_v3/server/tsconfig.json b/packages/analytics/shippers/elastic_v3/server/tsconfig.json new file mode 100644 index 0000000000000..b81a9a0217634 --- /dev/null +++ b/packages/analytics/shippers/elastic_v3/server/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/analytics/shippers/fullstory/BUILD.bazel b/packages/analytics/shippers/fullstory/BUILD.bazel index 2825e3fd733ea..c7da842e3cd93 100644 --- a/packages/analytics/shippers/fullstory/BUILD.bazel +++ b/packages/analytics/shippers/fullstory/BUILD.bazel @@ -62,6 +62,13 @@ jsts_transpiler( build_pkg_name = package_name(), ) +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + ts_config( name = "tsconfig", src = "tsconfig.json", @@ -86,7 +93,7 @@ ts_project( js_library( name = PKG_DIRNAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node"], + deps = RUNTIME_DEPS + [":target_node", ":target_web"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/analytics/shippers/fullstory/README.md b/packages/analytics/shippers/fullstory/README.md index 60c4b641e300b..6d1c443db3d22 100644 --- a/packages/analytics/shippers/fullstory/README.md +++ b/packages/analytics/shippers/fullstory/README.md @@ -4,7 +4,7 @@ FullStory implementation as a shipper for the `@kbn/analytics-client`. ## How to use it -This module is intended to be used on the UI only. It does not support server-side events. +This module is intended to be used **on the browser only**. It does not support server-side events. ```typescript import { FullStoryShipper } from "@kbn/analytics-shippers-fullstory"; @@ -21,3 +21,11 @@ analytics.registerShipper(FullStoryShipper, { fullStoryOrgId: '12345' }) | `scriptUrl` | The URL to load the FullStory client from. Falls back to `edge.fullstory.com/s/fs.js` if not specified. | | `debug` | Whether the debug logs should be printed to the console. Defaults to `false`. | | `namespace` | The name of the variable where the API is stored: `window[namespace]`. Defaults to `FS`. | + +## FullStory Custom Events Rate Limits + +FullStory limits the number of custom events that can be sent per second ([docs](https://help.fullstory.com/hc/en-us/articles/360020623234#custom-property-rate-limiting)). In order to comply with that limit, this shipper will only emit the event types registered in the allow-list defined in the constant [CUSTOM_EVENT_TYPES_ALLOWLIST](./src/fullstory_shipper.ts). We may change this behaviour in the future to a remotely-controlled list of events or rely on the opt-in _cherry-pick_ config mechanism of the Analytics Client. + +## Transmission protocol + +This shipper relies on FullStory official snippet. The internals about how it transfers the data are not documented. diff --git a/packages/analytics/shippers/fullstory/package.json b/packages/analytics/shippers/fullstory/package.json index 5d8fa7b7df976..ab5ac56b35641 100644 --- a/packages/analytics/shippers/fullstory/package.json +++ b/packages/analytics/shippers/fullstory/package.json @@ -2,6 +2,7 @@ "name": "@kbn/analytics-shippers-fullstory", "private": true, "version": "1.0.0", + "browser": "./target_web/index.js", "main": "./target_node/index.js", "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/analytics/shippers/fullstory/src/fullstory_shipper.test.ts b/packages/analytics/shippers/fullstory/src/fullstory_shipper.test.ts index 4b60ba661fe14..10f24ba5b5e14 100644 --- a/packages/analytics/shippers/fullstory/src/fullstory_shipper.test.ts +++ b/packages/analytics/shippers/fullstory/src/fullstory_shipper.test.ts @@ -119,7 +119,7 @@ describe('FullStoryShipper', () => { { event_type: 'test-event-2', timestamp: '2020-01-01T00:00:00.000Z', - properties: { test: 'test-2' }, + properties: { other_property: 'test-2' }, context: { pageName: 'test-page-1' }, }, ]); @@ -129,6 +129,49 @@ describe('FullStoryShipper', () => { test_str: 'test-1', }); expect(fullStoryApiMock.event).toHaveBeenCalledWith('test-event-2', { + other_property_str: 'test-2', + }); + }); + + test('filters the events by the allow-list', () => { + fullstoryShipper = new FullStoryShipper( + { + eventTypesAllowlist: ['valid-event-1', 'valid-event-2'], + debug: true, + fullStoryOrgId: 'test-org-id', + }, + { + logger: loggerMock.create(), + sendTo: 'staging', + isDev: true, + } + ); + fullstoryShipper.reportEvents([ + { + event_type: 'test-event-1', // Should be filtered out. + timestamp: '2020-01-01T00:00:00.000Z', + properties: { test: 'test-1' }, + context: { pageName: 'test-page-1' }, + }, + { + event_type: 'valid-event-1', + timestamp: '2020-01-01T00:00:00.000Z', + properties: { test: 'test-1' }, + context: { pageName: 'test-page-1' }, + }, + { + event_type: 'valid-event-2', + timestamp: '2020-01-01T00:00:00.000Z', + properties: { test: 'test-2' }, + context: { pageName: 'test-page-1' }, + }, + ]); + + expect(fullStoryApiMock.event).toHaveBeenCalledTimes(2); + expect(fullStoryApiMock.event).toHaveBeenCalledWith('valid-event-1', { + test_str: 'test-1', + }); + expect(fullStoryApiMock.event).toHaveBeenCalledWith('valid-event-2', { test_str: 'test-2', }); }); diff --git a/packages/analytics/shippers/fullstory/src/fullstory_shipper.ts b/packages/analytics/shippers/fullstory/src/fullstory_shipper.ts index ef9d5d662813d..48e8d7e6fa246 100644 --- a/packages/analytics/shippers/fullstory/src/fullstory_shipper.ts +++ b/packages/analytics/shippers/fullstory/src/fullstory_shipper.ts @@ -18,20 +18,46 @@ import { getParsedVersion } from './get_parsed_version'; import { formatPayload } from './format_payload'; import { loadSnippet } from './load_snippet'; -export type FullStoryShipperConfig = FullStorySnippetConfig; +/** + * FullStory shipper configuration. + */ +export interface FullStoryShipperConfig extends FullStorySnippetConfig { + /** + * FullStory's custom events rate limit is very aggressive. + * If this setting is provided, it'll only send the event types specified in this list. + */ + eventTypesAllowlist?: string[]; +} +/** + * FullStory shipper. + */ export class FullStoryShipper implements IShipper { + /** Shipper's unique name */ public static shipperName = 'FullStory'; + private readonly fullStoryApi: FullStoryApi; private lastUserId: string | undefined; + private readonly eventTypesAllowlist?: string[]; + /** + * Creates a new instance of the FullStoryShipper. + * @param config {@link FullStoryShipperConfig} + * @param initContext {@link AnalyticsClientInitContext} + */ constructor( config: FullStoryShipperConfig, private readonly initContext: AnalyticsClientInitContext ) { - this.fullStoryApi = loadSnippet(config); + const { eventTypesAllowlist, ...snippetConfig } = config; + this.fullStoryApi = loadSnippet(snippetConfig); + this.eventTypesAllowlist = eventTypesAllowlist; } + /** + * {@inheritDoc IShipper.extendContext} + * @param newContext {@inheritDoc IShipper.extendContext.newContext} + */ public extendContext(newContext: EventContext): void { this.initContext.logger.debug(`Received context ${JSON.stringify(newContext)}`); @@ -70,6 +96,10 @@ export class FullStoryShipper implements IShipper { } } + /** + * {@inheritDoc IShipper.optIn} + * @param isOptedIn {@inheritDoc IShipper.optIn.isOptedIn} + */ public optIn(isOptedIn: boolean): void { this.initContext.logger.debug(`Setting FS to optIn ${isOptedIn}`); // FullStory uses 2 different opt-in methods: @@ -85,11 +115,21 @@ export class FullStoryShipper implements IShipper { } } + /** + * {@inheritDoc IShipper.reportEvents} + * @param events {@inheritDoc IShipper.reportEvents.events} + */ public reportEvents(events: Event[]): void { this.initContext.logger.debug(`Reporting ${events.length} events to FS`); - events.forEach((event) => { - // We only read event.properties and discard the rest because the context is already sent in the other APIs. - this.fullStoryApi.event(event.event_type, formatPayload(event.properties)); - }); + events + .filter((event) => this.eventTypesAllowlist?.includes(event.event_type) ?? true) + .forEach((event) => { + // We only read event.properties and discard the rest because the context is already sent in the other APIs. + this.fullStoryApi.event(event.event_type, formatPayload(event.properties)); + }); + } + + public shutdown() { + // No need to do anything here for now. } } diff --git a/packages/analytics/shippers/fullstory/src/load_snippet.ts b/packages/analytics/shippers/fullstory/src/load_snippet.ts index 471152f033b5a..b06530111d672 100644 --- a/packages/analytics/shippers/fullstory/src/load_snippet.ts +++ b/packages/analytics/shippers/fullstory/src/load_snippet.ts @@ -8,6 +8,9 @@ import type { FullStoryApi } from './types'; +/** + * FullStory basic configuration. + */ export interface FullStorySnippetConfig { /** * The FullStory account id. diff --git a/packages/elastic-apm-synthtrace/src/lib/apm/aggregators/service_latency_aggregator.ts b/packages/elastic-apm-synthtrace/src/lib/apm/aggregators/service_latency_aggregator.ts new file mode 100644 index 0000000000000..e28ba234b2a49 --- /dev/null +++ b/packages/elastic-apm-synthtrace/src/lib/apm/aggregators/service_latency_aggregator.ts @@ -0,0 +1,183 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { random } from 'lodash'; +import { Client } from '@elastic/elasticsearch'; +import { ApmFields } from '../apm_fields'; +import { Fields } from '../../entity'; +import { StreamAggregator } from '../../stream_aggregator'; + +type LatencyState = { + count: number; + min: number; + max: number; + sum: number; + timestamp: number; +} & Pick; + +export type ServiceFields = Fields & + Pick< + ApmFields, + | 'timestamp.us' + | 'ecs.version' + | 'metricset.name' + | 'observer' + | 'processor.event' + | 'processor.name' + | 'service.name' + | 'service.version' + | 'service.environment' + | 'transaction.type' + > & + Partial<{ + 'transaction.duration.aggregate': { + min: number; + max: number; + sum: number; + value_count: number; + }; + }>; + +export class ServiceLatencyAggregator implements StreamAggregator { + public readonly name; + + constructor() { + this.name = 'service-latency'; + } + + getDataStreamName(): string { + return 'metrics-apm.service'; + } + + getMappings(): Record { + return { + properties: { + '@timestamp': { + type: 'date', + format: 'date_optional_time||epoch_millis', + }, + transaction: { + type: 'object', + properties: { + type: { type: 'keyword', time_series_dimension: true }, + duration: { + type: 'object', + properties: { + aggregate: { + type: 'aggregate_metric_double', + metrics: ['min', 'max', 'sum', 'value_count'], + default_metric: 'sum', + time_series_metric: 'gauge', + }, + }, + }, + }, + }, + service: { + type: 'object', + properties: { + name: { type: 'keyword', time_series_dimension: true }, + environment: { type: 'keyword', time_series_dimension: true }, + }, + }, + }, + }; + } + + getDimensions(): string[] { + return ['service.name', 'service.environment', 'transaction.type']; + } + + getWriteTarget(document: Record): string | null { + const eventType = document.metricset?.name; + if (eventType === 'service') return 'metrics-apm.service-default'; + return null; + } + + private state: Record = {}; + + private processedComponent: number = 0; + + process(event: ApmFields): Fields[] | null { + if (!event['@timestamp']) return null; + const service = event['service.name']!; + const environment = event['service.environment'] ?? 'production'; + const transactionType = event['transaction.type'] ?? 'request'; + const key = `${service}-${environment}-${transactionType}`; + const addToState = (timestamp: number) => { + if (!this.state[key]) { + this.state[key] = { + timestamp, + count: 0, + min: 0, + max: 0, + sum: 0, + 'service.name': service, + 'service.environment': environment, + 'transaction.type': transactionType, + }; + } + const duration = Number(event['transaction.duration.us']); + if (duration >= 0) { + const state = this.state[key]; + + state.count++; + state.sum += duration; + if (duration > state.max) state.max = duration; + if (duration < state.min) state.min = Math.min(0, duration); + } + }; + + // ensure we flush current state first if event falls out of the current max window age + if (this.state[key]) { + const diff = Math.abs(event['@timestamp'] - this.state[key].timestamp); + if (diff >= 1000 * 60) { + const fields = this.createServiceFields(key); + delete this.state[key]; + addToState(event['@timestamp']); + return [fields]; + } + } + + addToState(event['@timestamp']); + // if cardinality is too high force emit of current state + if (Object.keys(this.state).length === 1000) { + return this.flush(); + } + + return null; + } + + flush(): Fields[] { + const fields = Object.keys(this.state).map((key) => this.createServiceFields(key)); + this.state = {}; + return fields; + } + + private createServiceFields(key: string): ServiceFields { + this.processedComponent = ++this.processedComponent % 1000; + const component = Date.now() % 100; + const state = this.state[key]; + return { + '@timestamp': state.timestamp + random(0, 100) + component + this.processedComponent, + 'metricset.name': 'service', + 'processor.event': 'metric', + 'service.name': state['service.name'], + 'service.environment': state['service.environment'], + 'transaction.type': state['transaction.type'], + 'transaction.duration.aggregate': { + min: state.min, + max: state.max, + sum: state.sum, + value_count: state.count, + }, + }; + } + + async bootstrapElasticsearch(esClient: Client): Promise {} +} diff --git a/packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts b/packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts index 6e7fb5ffdb1bc..91bec0ba49c52 100644 --- a/packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts +++ b/packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts @@ -7,6 +7,7 @@ */ import { Client } from '@elastic/elasticsearch'; +import { IndicesIndexSettings } from '@elastic/elasticsearch/lib/api/types'; import { cleanWriteTargets } from '../../utils/clean_write_targets'; import { getApmWriteTargets } from '../utils/get_apm_write_targets'; import { Logger } from '../../utils/create_logger'; @@ -15,6 +16,7 @@ import { EntityIterable } from '../../entity_iterable'; import { StreamProcessor } from '../../stream_processor'; import { EntityStreams } from '../../entity_streams'; import { Fields } from '../../entity'; +import { StreamAggregator } from '../../stream_aggregator'; export interface StreamToBulkOptions { concurrency?: number; @@ -57,7 +59,7 @@ export class ApmSynthtraceEsClient { return info.version.number; } - async clean() { + async clean(dataStreams?: string[]) { return this.getWriteTargets().then(async (writeTargets) => { const indices = Object.values(writeTargets); this.logger.info(`Attempting to clean: ${indices}`); @@ -68,7 +70,7 @@ export class ApmSynthtraceEsClient { logger: this.logger, }); } - for (const name of indices) { + for (const name of indices.concat(dataStreams ?? [])) { const dataStream = await this.client.indices.getDataStream({ name }, { ignore: [404] }); if (dataStream.data_streams && dataStream.data_streams.length > 0) { this.logger.debug(`Deleting datastream: ${name}`); @@ -149,7 +151,6 @@ export class ApmSynthtraceEsClient { streamProcessor?: StreamProcessor ) { const dataStream = Array.isArray(events) ? new EntityStreams(events) : events; - const sp = streamProcessor != null ? streamProcessor @@ -165,7 +166,7 @@ export class ApmSynthtraceEsClient { await this.logger.perf('enumerate_scenario', async () => { // @ts-ignore // We just want to enumerate - for await (item of sp.streamToDocumentAsync(sp.toDocument, dataStream)) { + for await (item of sp.streamToDocumentAsync((e) => sp.toDocument(e), dataStream)) { if (yielded === 0) { options.itemStartStopCallback?.apply(this, [item, false]); yielded++; @@ -185,7 +186,7 @@ export class ApmSynthtraceEsClient { flushBytes: 500000, // TODO https://github.com/elastic/elasticsearch-js/issues/1610 // having to map here is awkward, it'd be better to map just before serialization. - datasource: sp.streamToDocumentAsync(sp.toDocument, dataStream), + datasource: sp.streamToDocumentAsync((e) => sp.toDocument(e), dataStream), onDrop: (doc) => { this.logger.info(JSON.stringify(doc, null, 2)); }, @@ -197,11 +198,12 @@ export class ApmSynthtraceEsClient { options?.itemStartStopCallback?.apply(this, [item, false]); yielded++; } - const index = options?.mapToIndex - ? options?.mapToIndex(item) - : !this.forceLegacyIndices - ? StreamProcessor.getDataStreamForEvent(item, writeTargets) - : StreamProcessor.getIndexForEvent(item, writeTargets); + let index = options?.mapToIndex ? options?.mapToIndex(item) : null; + if (!index) { + index = !this.forceLegacyIndices + ? sp.getDataStreamForEvent(item, writeTargets) + : StreamProcessor.getIndexForEvent(item, writeTargets); + } return { create: { _index: index } }; }, }); @@ -211,4 +213,53 @@ export class ApmSynthtraceEsClient { await this.refresh(); } } + + async createDataStream(aggregator: StreamAggregator) { + const datastreamName = aggregator.getDataStreamName(); + const mappings = aggregator.getMappings(); + const dimensions = aggregator.getDimensions(); + + const indexSettings: IndicesIndexSettings = { lifecycle: { name: 'metrics' } }; + if (dimensions.length > 0) { + indexSettings.mode = 'time_series'; + indexSettings.routing_path = dimensions; + } + + await this.client.cluster.putComponentTemplate({ + name: `${datastreamName}-mappings`, + template: { + mappings, + }, + _meta: { + description: `Mappings for ${datastreamName}-*`, + }, + }); + this.logger.info(`Created mapping component template for ${datastreamName}-*`); + + await this.client.cluster.putComponentTemplate({ + name: `${datastreamName}-settings`, + template: { + settings: { + index: indexSettings, + }, + }, + _meta: { + description: `Settings for ${datastreamName}-*`, + }, + }); + this.logger.info(`Created settings component template for ${datastreamName}-*`); + + await this.client.indices.putIndexTemplate({ + name: `${datastreamName}-index_template`, + index_patterns: [`${datastreamName}-*`], + data_stream: {}, + composed_of: [`${datastreamName}-mappings`, `${datastreamName}-settings`], + priority: 500, + }); + this.logger.info(`Created index template for ${datastreamName}-*`); + + await this.client.indices.createDataStream({ name: datastreamName + '-default' }); + + await aggregator.bootstrapElasticsearch(this.client); + } } diff --git a/packages/elastic-apm-synthtrace/src/lib/stream_aggregator.ts b/packages/elastic-apm-synthtrace/src/lib/stream_aggregator.ts new file mode 100644 index 0000000000000..3076b105a10fd --- /dev/null +++ b/packages/elastic-apm-synthtrace/src/lib/stream_aggregator.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { Client } from '@elastic/elasticsearch'; +import { ApmFields, Fields } from '..'; + +export interface StreamAggregator { + name: string; + + getWriteTarget(document: Record): string | null; + + process(event: TFields): Fields[] | null; + + flush(): Fields[]; + + bootstrapElasticsearch(esClient: Client): Promise; + + getDataStreamName(): string; + + getDimensions(): string[]; + + getMappings(): Record; +} diff --git a/packages/elastic-apm-synthtrace/src/lib/stream_processor.ts b/packages/elastic-apm-synthtrace/src/lib/stream_processor.ts index 17ced20f5d7ed..e1cb332996e23 100644 --- a/packages/elastic-apm-synthtrace/src/lib/stream_processor.ts +++ b/packages/elastic-apm-synthtrace/src/lib/stream_processor.ts @@ -17,10 +17,12 @@ import { dedot } from './utils/dedot'; import { ApmElasticsearchOutputWriteTargets } from './apm/utils/get_apm_write_targets'; import { Logger } from './utils/create_logger'; import { Fields } from './entity'; +import { StreamAggregator } from './stream_aggregator'; export interface StreamProcessorOptions { version?: string; - processors: Array<(events: TFields[]) => TFields[]>; + processors?: Array<(events: TFields[]) => TFields[]>; + streamAggregators?: Array>; flushInterval?: string; // defaults to 10k maxBufferSize?: number; @@ -39,6 +41,8 @@ export class StreamProcessor { getBreakdownMetrics, ]; public static defaultFlushInterval: number = 10000; + private readonly processors: Array<(events: TFields[]) => TFields[]>; + private readonly streamAggregators: Array>; constructor(private readonly options: StreamProcessorOptions) { [this.intervalAmount, this.intervalUnit] = this.options.flushInterval @@ -47,6 +51,8 @@ export class StreamProcessor { this.name = this.options?.name ?? 'StreamProcessor'; this.version = this.options.version ?? '8.0.0'; this.versionMajor = Number.parseInt(this.version.split('.')[0], 10); + this.processors = options.processors ?? []; + this.streamAggregators = options.streamAggregators ?? []; } private readonly intervalAmount: number; private readonly intervalUnit: any; @@ -73,6 +79,15 @@ export class StreamProcessor { yield StreamProcessor.enrich(event, this.version, this.versionMajor); sourceEventsYielded++; + for (const aggregator of this.streamAggregators) { + const aggregatedEvents = aggregator.process(event); + if (aggregatedEvents) { + yield* aggregatedEvents.map((d) => + StreamProcessor.enrich(d, this.version, this.versionMajor) + ); + } + } + if (sourceEventsYielded % maxBufferSize === 0) { if (this.options?.processedCallback) { this.options.processedCallback(maxBufferSize); @@ -96,7 +111,7 @@ export class StreamProcessor { this.options.logger?.debug( `${this.name} flush ${localBuffer.length} documents ${order}: ${e} => ${f}` ); - for (const processor of this.options.processors) { + for (const processor of this.processors) { yield* processor(localBuffer).map((d) => StreamProcessor.enrich(d, this.version, this.versionMajor) ); @@ -116,13 +131,16 @@ export class StreamProcessor { this.options.logger?.info( `${this.name} processing remaining buffer: ${localBuffer.length} items left` ); - for (const processor of this.options.processors) { + for (const processor of this.processors) { yield* processor(localBuffer).map((d) => StreamProcessor.enrich(d, this.version, this.versionMajor) ); } this.options.processedCallback?.apply(this, [localBuffer.length]); } + for (const aggregator of this.streamAggregators) { + yield* aggregator.flush(); + } } private calculateFlushAfter(eventDate: number | null, order: 'asc' | 'desc') { @@ -186,10 +204,7 @@ export class StreamProcessor { return newDoc; } - static getDataStreamForEvent( - d: Record, - writeTargets: ApmElasticsearchOutputWriteTargets - ) { + getDataStreamForEvent(d: Record, writeTargets: ApmElasticsearchOutputWriteTargets) { if (!d.processor?.event) { throw Error("'processor.event' is not set on document, can not determine target index"); } @@ -204,6 +219,13 @@ export class StreamProcessor { } } } + for (const aggregator of this.streamAggregators) { + const target = aggregator.getWriteTarget(d); + if (target) { + dataStream = target; + break; + } + } return dataStream; } diff --git a/packages/elastic-apm-synthtrace/src/scripts/run_synthtrace.ts b/packages/elastic-apm-synthtrace/src/scripts/run_synthtrace.ts index 7ba3251def983..5e007d9adeac4 100644 --- a/packages/elastic-apm-synthtrace/src/scripts/run_synthtrace.ts +++ b/packages/elastic-apm-synthtrace/src/scripts/run_synthtrace.ts @@ -14,6 +14,8 @@ import { startLiveDataUpload } from './utils/start_live_data_upload'; import { parseRunCliFlags } from './utils/parse_run_cli_flags'; import { getCommonServices } from './utils/get_common_services'; import { ApmSynthtraceKibanaClient } from '../lib/apm/client/apm_synthtrace_kibana_client'; +import { StreamAggregator } from '../lib/stream_aggregator'; +import { ServiceLatencyAggregator } from '../lib/apm/aggregators/service_latency_aggregator'; function options(y: Argv) { return y @@ -186,8 +188,9 @@ yargs(process.argv.slice(2)) await apmEsClient.updateComponentTemplates(runOptions.numShards); } + const aggregators: StreamAggregator[] = [new ServiceLatencyAggregator()]; if (argv.clean) { - await apmEsClient.clean(); + await apmEsClient.clean(aggregators.map((a) => a.getDataStreamName() + '-*')); } if (runOptions.gcpRepository) { await apmEsClient.registerGcpRepository(runOptions.gcpRepository); @@ -205,6 +208,8 @@ yargs(process.argv.slice(2)) )}` ); + for (const aggregator of aggregators) await apmEsClient.createDataStream(aggregator); + if (runOptions.maxDocs !== 0) await startHistoricalDataUpload(apmEsClient, logger, runOptions, from, to, version); diff --git a/packages/elastic-apm-synthtrace/src/scripts/utils/synthtrace_worker.ts b/packages/elastic-apm-synthtrace/src/scripts/utils/synthtrace_worker.ts new file mode 100644 index 0000000000000..76b6e2ce6b6d8 --- /dev/null +++ b/packages/elastic-apm-synthtrace/src/scripts/utils/synthtrace_worker.ts @@ -0,0 +1,100 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 pLimit from 'p-limit'; +import { workerData, parentPort } from 'worker_threads'; +import { RunOptions } from './parse_run_cli_flags'; +import { getScenario } from './get_scenario'; +import { StreamToBulkOptions } from '../../lib/apm/client/apm_synthtrace_es_client'; +import { getCommonServices } from './get_common_services'; +import { LogLevel } from '../../lib/utils/create_logger'; +import { StreamProcessor } from '../../lib/stream_processor'; +import { Scenario } from '../scenario'; +import { EntityIterable, Fields } from '../..'; +import { StreamAggregator } from '../../lib/stream_aggregator'; +import { ServiceLatencyAggregator } from '../../lib/apm/aggregators/service_latency_aggregator'; + +// logging proxy to main thread, ensures we see real time logging +const l = { + perf: (name: string, cb: () => T): T => { + return cb(); + }, + debug: (...args: any[]) => parentPort?.postMessage({ log: LogLevel.debug, args }), + info: (...args: any[]) => parentPort?.postMessage({ log: LogLevel.info, args }), + error: (...args: any[]) => parentPort?.postMessage({ log: LogLevel.error, args }), +}; + +export interface WorkerData { + bucketFrom: Date; + bucketTo: Date; + runOptions: RunOptions; + workerIndex: number; + version: string; +} + +const { bucketFrom, bucketTo, runOptions, workerIndex, version } = workerData as WorkerData; + +const { logger, apmEsClient } = getCommonServices(runOptions, l); +const file = runOptions.file; +let scenario: Scenario; +let events: EntityIterable; +let streamToBulkOptions: StreamToBulkOptions; +let streamProcessor: StreamProcessor; + +async function setup() { + scenario = await logger.perf('get_scenario', () => getScenario({ file, logger })); + const { generate, mapToIndex } = await scenario(runOptions); + + events = logger.perf('generate_scenario', () => generate({ from: bucketFrom, to: bucketTo })); + streamToBulkOptions = { + maxDocs: runOptions.maxDocs, + mapToIndex, + dryRun: !!runOptions.dryRun, + }; + streamToBulkOptions.itemStartStopCallback = (item, done) => { + if (!item) return; + if (!done) { + parentPort?.postMessage({ workerIndex, firstTimestamp: item['@timestamp'] }); + } else { + parentPort?.postMessage({ workerIndex, lastTimestamp: item['@timestamp'] }); + } + }; + const aggregators: StreamAggregator[] = [new ServiceLatencyAggregator()]; + streamProcessor = new StreamProcessor({ + version, + processors: StreamProcessor.apmProcessors, + streamAggregators: aggregators, + maxSourceEvents: runOptions.maxDocs, + logger: l, + processedCallback: (processedDocuments) => { + parentPort!.postMessage({ workerIndex, processedDocuments }); + }, + name: `Worker ${workerIndex}`, + }); +} + +async function doWork() { + await logger.perf( + 'index_scenario', + async () => await apmEsClient.index(events, streamToBulkOptions, streamProcessor) + ); +} + +parentPort!.on('message', async (message) => { + if (message === 'setup') { + await setup(); + } + if (message === 'start') { + try { + await doWork(); + process.exit(0); + } catch (error) { + l.info(error); + process.exit(2); + } + } +}); diff --git a/packages/elastic-apm-synthtrace/src/scripts/utils/worker.js b/packages/elastic-apm-synthtrace/src/scripts/utils/worker.js index 84887f4d9285b..734a9323e5516 100644 --- a/packages/elastic-apm-synthtrace/src/scripts/utils/worker.js +++ b/packages/elastic-apm-synthtrace/src/scripts/utils/worker.js @@ -12,4 +12,4 @@ require('@babel/register')({ presets: [['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-typescript'], }); -require('./worker'); +require('./synthtrace_worker'); diff --git a/packages/elastic-apm-synthtrace/src/scripts/utils/worker.ts b/packages/elastic-apm-synthtrace/src/scripts/utils/worker.ts deleted file mode 100644 index 4e4ef8a02ff71..0000000000000 --- a/packages/elastic-apm-synthtrace/src/scripts/utils/worker.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 pLimit from 'p-limit'; -import { workerData, parentPort } from 'worker_threads'; -import { RunOptions } from './parse_run_cli_flags'; -import { getScenario } from './get_scenario'; -import { StreamToBulkOptions } from '../../lib/apm/client/apm_synthtrace_es_client'; -import { getCommonServices } from './get_common_services'; -import { LogLevel } from '../../lib/utils/create_logger'; -import { StreamProcessor } from '../../lib/stream_processor'; -import { Scenario } from '../scenario'; -import { EntityIterable, Fields } from '../..'; - -// logging proxy to main thread, ensures we see real time logging -const l = { - perf: (name: string, cb: () => T): T => { - return cb(); - }, - debug: (...args: any[]) => parentPort?.postMessage({ log: LogLevel.debug, args }), - info: (...args: any[]) => parentPort?.postMessage({ log: LogLevel.info, args }), - error: (...args: any[]) => parentPort?.postMessage({ log: LogLevel.error, args }), -}; - -export interface WorkerData { - bucketFrom: Date; - bucketTo: Date; - runOptions: RunOptions; - workerIndex: number; - version: string; -} - -const { bucketFrom, bucketTo, runOptions, workerIndex, version } = workerData as WorkerData; - -const { logger, apmEsClient } = getCommonServices(runOptions, l); -const file = runOptions.file; -let scenario: Scenario; -let events: EntityIterable; -let streamToBulkOptions: StreamToBulkOptions; -let streamProcessor: StreamProcessor; - -async function setup() { - scenario = await logger.perf('get_scenario', () => getScenario({ file, logger })); - const { generate, mapToIndex } = await scenario(runOptions); - - events = logger.perf('generate_scenario', () => generate({ from: bucketFrom, to: bucketTo })); - streamToBulkOptions = { - maxDocs: runOptions.maxDocs, - mapToIndex, - dryRun: !!runOptions.dryRun, - }; - streamToBulkOptions.itemStartStopCallback = (item, done) => { - if (!item) return; - if (!done) { - parentPort?.postMessage({ workerIndex, firstTimestamp: item['@timestamp'] }); - } else { - parentPort?.postMessage({ workerIndex, lastTimestamp: item['@timestamp'] }); - } - }; - streamProcessor = new StreamProcessor({ - version, - processors: StreamProcessor.apmProcessors, - maxSourceEvents: runOptions.maxDocs, - logger: l, - processedCallback: (processedDocuments) => { - parentPort!.postMessage({ workerIndex, processedDocuments }); - }, - name: `Worker ${workerIndex}`, - }); -} - -async function doWork() { - await logger.perf( - 'index_scenario', - async () => await apmEsClient.index(events, streamToBulkOptions, streamProcessor) - ); -} - -parentPort!.on('message', async (message) => { - if (message === 'setup') { - await setup(); - } - if (message === 'start') { - try { - await doWork(); - process.exit(0); - } catch (error) { - l.info(error); - process.exit(2); - } - } -}); diff --git a/packages/kbn-ace/BUILD.bazel b/packages/kbn-ace/BUILD.bazel index 630a636e99b9b..8db4f8c4a4a21 100644 --- a/packages/kbn-ace/BUILD.bazel +++ b/packages/kbn-ace/BUILD.bazel @@ -52,6 +52,19 @@ jsts_transpiler( build_pkg_name = package_name(), ) +jsts_transpiler( + name = "target_web", + srcs = SRCS, + additional_args = [ + "--copy-files", + "--ignore", + "**/*/src/ace/modes/x_json/worker/x_json.ace.worker.js", + "--quiet" + ], + build_pkg_name = package_name(), + web = True, +) + ts_config( name = "tsconfig", src = "tsconfig.json", @@ -77,7 +90,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node"], + deps = RUNTIME_DEPS + [":target_node", ":target_web"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-ace/package.json b/packages/kbn-ace/package.json index f5775c3b34596..ce8e83e0686ef 100644 --- a/packages/kbn-ace/package.json +++ b/packages/kbn-ace/package.json @@ -2,6 +2,7 @@ "name": "@kbn/ace", "version": "1.0.0", "private": true, + "browser": "./target_web/index.js", "main": "./target_node/index.js", "license": "SSPL-1.0 OR Elastic License 2.0" } diff --git a/packages/kbn-babel-preset/styled_components_files.js b/packages/kbn-babel-preset/styled_components_files.js index a8b1234a406fd..53052809b6b2f 100644 --- a/packages/kbn-babel-preset/styled_components_files.js +++ b/packages/kbn-babel-preset/styled_components_files.js @@ -13,7 +13,7 @@ module.exports = { */ USES_STYLED_COMPONENTS: [ /packages[\/\\]kbn-ui-shared-deps-(npm|src)[\/\\]/, - /src[\/\\]plugins[\/\\](unified_search|kibana_react)[\/\\]/, + /src[\/\\]plugins[\/\\](kibana_react)[\/\\]/, /x-pack[\/\\]plugins[\/\\](apm|beats_management|cases|fleet|infra|lists|observability|osquery|security_solution|timelines|synthetics|ux)[\/\\]/, /x-pack[\/\\]test[\/\\]plugin_functional[\/\\]plugins[\/\\]resolver_test[\/\\]/, ], diff --git a/packages/kbn-bazel-packages/src/bazel_package_dirs.ts b/packages/kbn-bazel-packages/src/bazel_package_dirs.ts index f09883994feca..80248646f1e6f 100644 --- a/packages/kbn-bazel-packages/src/bazel_package_dirs.ts +++ b/packages/kbn-bazel-packages/src/bazel_package_dirs.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ +import globby from 'globby'; import Path from 'path'; -import globby from 'globby'; import { REPO_ROOT } from '@kbn/utils'; /** @@ -24,6 +24,7 @@ export const BAZEL_PACKAGE_DIRS = [ 'packages/shared-ux/*', 'packages/analytics', 'packages/analytics/shippers', + 'packages/analytics/shippers/elastic_v3', ]; /** diff --git a/packages/kbn-bazel-packages/src/discover_packages.ts b/packages/kbn-bazel-packages/src/discover_packages.ts index db31b54beec75..8b78e4e293118 100644 --- a/packages/kbn-bazel-packages/src/discover_packages.ts +++ b/packages/kbn-bazel-packages/src/discover_packages.ts @@ -9,6 +9,7 @@ import Path from 'path'; import globby from 'globby'; +import normalizePath from 'normalize-path'; import { REPO_ROOT } from '@kbn/utils'; import { asyncMapWithLimit } from '@kbn/std'; @@ -16,7 +17,7 @@ import { BazelPackage } from './bazel_package'; import { BAZEL_PACKAGE_DIRS } from './bazel_package_dirs'; export function discoverBazelPackageLocations(repoRoot: string) { - return globby + const packagesWithPackageJson = globby .sync( BAZEL_PACKAGE_DIRS.map((dir) => `${dir}/*/package.json`), { @@ -24,8 +25,26 @@ export function discoverBazelPackageLocations(repoRoot: string) { absolute: true, } ) + // NOTE: removing x-pack by default for now to prevent a situation where a BUILD.bazel file + // needs to be added at the root of the folder which will make x-pack to be wrongly recognized + // as a Bazel package in that case + .filter((path) => !normalizePath(path).includes('x-pack/package.json')) .sort((a, b) => a.localeCompare(b)) .map((path) => Path.dirname(path)); + + const packagesWithBuildBazel = globby + .sync( + BAZEL_PACKAGE_DIRS.map((dir) => `${dir}/*/BUILD.bazel`), + { + cwd: repoRoot, + absolute: true, + } + ) + .map((path) => Path.dirname(path)); + + // NOTE: only return as discovered packages the ones with a package.json + BUILD.bazel file. + // In the future we should change this to only discover the ones declaring kibana.json. + return packagesWithPackageJson.filter((pkg) => packagesWithBuildBazel.includes(pkg)); } export async function discoverBazelPackages(repoRoot: string = REPO_ROOT) { diff --git a/packages/kbn-bazel-runner/jest.config.js b/packages/kbn-bazel-runner/jest.config.js deleted file mode 100644 index 4d4f77a8f43f3..0000000000000 --- a/packages/kbn-bazel-runner/jest.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-bazel-runner'], -}; diff --git a/packages/kbn-ci-stats-client/BUILD.bazel b/packages/kbn-ci-stats-client/BUILD.bazel deleted file mode 100644 index 7017adc604416..0000000000000 --- a/packages/kbn-ci-stats-client/BUILD.bazel +++ /dev/null @@ -1,120 +0,0 @@ -load("@npm//@bazel/typescript:index.bzl", "ts_config") -load("@build_bazel_rules_nodejs//:index.bzl", "js_library") -load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") - -PKG_DIRNAME = "kbn-ci-stats-client" -PKG_REQUIRE_NAME = "@kbn/ci-stats-client" - -SOURCE_FILES = glob( - [ - "src/**/*.ts", - ], - exclude = [ - "**/*.test.*", - ], -) - -SRCS = SOURCE_FILES - -filegroup( - name = "srcs", - srcs = SRCS, -) - -NPM_MODULE_EXTRA_FILES = [ - "package.json", -] - -# In this array place runtime dependencies, including other packages and NPM packages -# which must be available for this code to run. -# -# To reference other packages use: -# "//repo/relative/path/to/package" -# eg. "//packages/kbn-utils" -# -# To reference a NPM package use: -# "@npm//name-of-package" -# eg. "@npm//lodash" -RUNTIME_DEPS = [ - "@npm//axios", - "//packages/kbn-ci-stats-core", - "//packages/kbn-tooling-log", -] - -# In this array place dependencies necessary to build the types, which will include the -# :npm_module_types target of other packages and packages from NPM, including @types/* -# packages. -# -# To reference the types for another package use: -# "//repo/relative/path/to/package:npm_module_types" -# eg. "//packages/kbn-utils:npm_module_types" -# -# References to NPM packages work the same as RUNTIME_DEPS -TYPES_DEPS = [ - "@npm//@types/node", - "@npm//@types/jest", - "@npm//axios", - "//packages/kbn-ci-stats-core:npm_module_types", - "//packages/kbn-tooling-log:npm_module_types", -] - -jsts_transpiler( - name = "target_node", - srcs = SRCS, - build_pkg_name = package_name(), -) - -ts_config( - name = "tsconfig", - src = "tsconfig.json", - deps = [ - "//:tsconfig.base.json", - "//:tsconfig.bazel.json", - ], -) - -ts_project( - name = "tsc_types", - args = ['--pretty'], - srcs = SRCS, - deps = TYPES_DEPS, - declaration = True, - emit_declaration_only = True, - out_dir = "target_types", - root_dir = "src", - tsconfig = ":tsconfig", -) - -js_library( - name = PKG_DIRNAME, - srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node"], - package_name = PKG_REQUIRE_NAME, - visibility = ["//visibility:public"], -) - -pkg_npm( - name = "npm_module", - deps = [":" + PKG_DIRNAME], -) - -filegroup( - name = "build", - srcs = [":npm_module"], - visibility = ["//visibility:public"], -) - -pkg_npm_types( - name = "npm_module_types", - srcs = SRCS, - deps = [":tsc_types"], - package_name = PKG_REQUIRE_NAME, - tsconfig = ":tsconfig", - visibility = ["//visibility:public"], -) - -filegroup( - name = "build_types", - srcs = [":npm_module_types"], - visibility = ["//visibility:public"], -) diff --git a/packages/kbn-ci-stats-client/README.md b/packages/kbn-ci-stats-client/README.md deleted file mode 100644 index d1f6c59e978c9..0000000000000 --- a/packages/kbn-ci-stats-client/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @kbn/ci-stats-client - -Client for reading data stored at https://ci-stats.kibana.dev \ No newline at end of file diff --git a/packages/kbn-ci-stats-client/jest.config.js b/packages/kbn-ci-stats-client/jest.config.js deleted file mode 100644 index d855d7886d0d7..0000000000000 --- a/packages/kbn-ci-stats-client/jest.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-ci-stats-client'], -}; diff --git a/packages/kbn-ci-stats-client/package.json b/packages/kbn-ci-stats-client/package.json deleted file mode 100644 index 709f6a3454d59..0000000000000 --- a/packages/kbn-ci-stats-client/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "@kbn/ci-stats-client", - "private": true, - "version": "1.0.0", - "main": "./target_node/index.js", - "license": "SSPL-1.0 OR Elastic License 2.0", - "kibana": { - "devOnly": true - } -} diff --git a/packages/kbn-ci-stats-client/src/ci_stats_client.ts b/packages/kbn-ci-stats-client/src/ci_stats_client.ts deleted file mode 100644 index a7ab6f1cc4cb8..0000000000000 --- a/packages/kbn-ci-stats-client/src/ci_stats_client.ts +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 Axios from 'axios'; -import { ToolingLog } from '@kbn/tooling-log'; - -import { parseConfig, Config, CiStatsMetadata } from '@kbn/ci-stats-core'; - -interface LatestTestGroupStatsOptions { - /** The Kibana branch to get stats for, eg "main" */ - branch: string; - /** The CI job names to filter builds by, eg "kibana-hourly" */ - ciJobNames: string[]; - /** Filter test groups by group type */ - testGroupType?: string; -} - -interface CompleteSuccessBuildSource { - jobName: string; - jobRunner: string; - completedAt: string; - commit: string; - startedAt: string; - branch: string; - result: 'SUCCESS'; - jobId: string; - targetBranch: string | null; - fromKibanaCiProduction: boolean; - requiresValidMetrics: boolean | null; - jobUrl: string; - mergeBase: string | null; -} - -interface TestGroupSource { - '@timestamp': string; - buildId: string; - name: string; - type: string; - startTime: string; - durationMs: number; - meta: CiStatsMetadata; -} - -interface LatestTestGroupStatsResp { - build: CompleteSuccessBuildSource & { id: string }; - testGroups: Array; -} - -export class CiStatsClient { - /** - * Create a CiStatsReporter by inspecting the ENV for the necessary config - */ - static fromEnv(log: ToolingLog) { - return new CiStatsClient(parseConfig(log)); - } - - constructor(private readonly config?: Config) {} - - isEnabled() { - return !!this.config?.apiToken; - } - - async getLatestTestGroupStats(options: LatestTestGroupStatsOptions) { - if (!this.config || !this.config.apiToken) { - throw new Error('No ciStats config available, call `isEnabled()` before using the client'); - } - - const resp = await Axios.request({ - baseURL: 'https://ci-stats.kibana.dev', - url: '/v1/test_group_stats', - params: { - branch: options.branch, - ci_job_name: options.ciJobNames.join(','), - test_group_type: options.testGroupType, - }, - headers: { - Authentication: `token ${this.config.apiToken}`, - }, - }); - - return resp.data; - } -} diff --git a/packages/kbn-ci-stats-client/src/index.ts b/packages/kbn-ci-stats-client/src/index.ts deleted file mode 100644 index ac32c69b9f7b7..0000000000000 --- a/packages/kbn-ci-stats-client/src/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { CiStatsClient } from './ci_stats_client'; diff --git a/packages/kbn-ci-stats-core/README.md b/packages/kbn-ci-stats-core/README.md index b2e34a492b745..741728394aa83 100644 --- a/packages/kbn-ci-stats-core/README.md +++ b/packages/kbn-ci-stats-core/README.md @@ -1,3 +1,3 @@ # @kbn/ci-stats-core -Config and types used by `@kbn/ci-stats-client` and `@kbn/ci-stats-reporter`. \ No newline at end of file +Config and types used by `@kbn/ci-stats-reporter`. \ No newline at end of file diff --git a/packages/kbn-ci-stats-core/jest.config.js b/packages/kbn-ci-stats-core/jest.config.js deleted file mode 100644 index 0feb7b4e1b872..0000000000000 --- a/packages/kbn-ci-stats-core/jest.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-ci-stats-core'], -}; diff --git a/packages/kbn-ci-stats-reporter/jest.config.js b/packages/kbn-ci-stats-reporter/jest.config.js deleted file mode 100644 index bf58324f440a3..0000000000000 --- a/packages/kbn-ci-stats-reporter/jest.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-ci-stats-reporter'], -}; diff --git a/packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts b/packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts index d709927787b0e..5d3f17a76f687 100644 --- a/packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts +++ b/packages/kbn-ci-stats-reporter/src/ci_stats_reporter.ts @@ -22,6 +22,20 @@ import { parseConfig, Config, CiStatsMetadata } from '@kbn/ci-stats-core'; import type { CiStatsTestGroupInfo, CiStatsTestRun } from './ci_stats_test_group_types'; const BASE_URL = 'https://ci-stats.kibana.dev'; +const SECOND = 1000; +const MINUTE = 60 * SECOND; + +function limitMetaStrings(meta: CiStatsMetadata) { + return Object.fromEntries( + Object.entries(meta).map(([key, value]) => { + if (typeof value === 'string' && value.length > 2000) { + return [key, value.slice(0, 2000)]; + } + + return [key, value]; + }) + ); +} /** A ci-stats metric record */ export interface CiStatsMetric { @@ -84,10 +98,8 @@ export interface CiStatsReportTestsOptions { } /* @internal */ -interface ReportTestsResponse { - buildId: string; +interface ReportTestGroupResponse { groupId: string; - testRunCount: number; } /* @internal */ @@ -97,6 +109,7 @@ interface ReqOptions { body: any; bodyDesc: string; query?: AxiosRequestConfig['params']; + timeout?: number; } /** Object that helps report data to the ci-stats service */ @@ -138,7 +151,14 @@ export class CiStatsReporter { } const buildId = this.config?.buildId; - const timings = options.timings; + const timings = options.timings.map((timing) => + timing.meta + ? { + ...timing, + meta: limitMetaStrings(timing.meta), + } + : timing + ); const upstreamBranch = options.upstreamBranch ?? this.getUpstreamBranch(); const kibanaUuid = options.kibanaUuid === undefined ? this.getKibanaUuid() : options.kibanaUuid; let email; @@ -238,18 +258,52 @@ export class CiStatsReporter { ); } - return await this.req({ + const groupResp = await this.req({ auth: true, - path: '/v1/test_group', + path: '/v2/test_group', query: { buildId: this.config?.buildId, }, - bodyDesc: `[${group.name}/${group.type}] test groups with ${testRuns.length} tests`, - body: [ - JSON.stringify({ group }), - ...testRuns.map((testRun) => JSON.stringify({ testRun })), - ].join('\n'), + bodyDesc: `[${group.name}/${group.type}] test group`, + body: group, }); + + if (!groupResp) { + return; + } + + let bufferBytes = 0; + const buffer: string[] = []; + const flushBuffer = async () => { + await this.req<{ testRunCount: number }>({ + auth: true, + path: '/v2/test_runs', + query: { + buildId: this.config?.buildId, + groupId: groupResp.groupId, + groupType: group.type, + }, + bodyDesc: `[${group.name}/${group.type}] Chunk of ${bufferBytes} bytes`, + body: buffer.join('\n'), + timeout: 5 * MINUTE, + }); + buffer.length = 0; + bufferBytes = 0; + }; + + // send test runs in chunks of ~500kb + for (const testRun of testRuns) { + const json = JSON.stringify(testRun); + bufferBytes += json.length; + buffer.push(json); + if (bufferBytes >= 450000) { + await flushBuffer(); + } + } + + if (bufferBytes) { + await flushBuffer(); + } } /** @@ -286,7 +340,7 @@ export class CiStatsReporter { } } - private async req({ auth, body, bodyDesc, path, query }: ReqOptions) { + private async req({ auth, body, bodyDesc, path, query, timeout = 60 * SECOND }: ReqOptions) { let attempt = 0; const maxAttempts = 5; @@ -315,6 +369,7 @@ export class CiStatsReporter { // if it can be serialized into a string, send it maxBodyLength: Infinity, maxContentLength: Infinity, + timeout, }); return resp.data; diff --git a/packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts b/packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts index 223273ca82cd3..298b46498aff4 100644 --- a/packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts +++ b/packages/kbn-ci-stats-reporter/src/ci_stats_test_group_types.ts @@ -83,6 +83,10 @@ export interface CiStatsTestGroupInfo { * The name of this specific group (within the "type") */ name: string; + /** + * Overall result of this test group + */ + result: 'fail' | 'pass' | 'skip'; /** * Arbitrary metadata associated with this group. We currently look for a ciGroup metadata property for highlighting that when appropriate */ diff --git a/packages/kbn-dev-utils/src/proc_runner/proc.ts b/packages/kbn-dev-utils/src/proc_runner/proc.ts index 0402feec99d47..323c1fb674317 100644 --- a/packages/kbn-dev-utils/src/proc_runner/proc.ts +++ b/packages/kbn-dev-utils/src/proc_runner/proc.ts @@ -37,19 +37,7 @@ async function withTimeout( ms: number, onTimeout: () => Promise ) { - const TIMEOUT = Symbol('timeout'); - try { - await Promise.race([ - attempt(), - new Promise((_, reject) => setTimeout(() => reject(TIMEOUT), ms)), - ]); - } catch (error) { - if (error === TIMEOUT) { - await onTimeout(); - } else { - throw error; - } - } + await Rx.lastValueFrom(Rx.race(Rx.defer(attempt), Rx.timer(ms).pipe(Rx.mergeMap(onTimeout)))); } export type Proc = ReturnType; @@ -168,5 +156,8 @@ export function startProc(name: string, options: ProcOptions, log: ToolingLog) { outcome$, outcomePromise, stop, + stopWasCalled() { + return stopCalled; + }, }; } diff --git a/packages/kbn-dev-utils/src/proc_runner/proc_runner.ts b/packages/kbn-dev-utils/src/proc_runner/proc_runner.ts index 3b7cf008df9a6..654a9d1080135 100644 --- a/packages/kbn-dev-utils/src/proc_runner/proc_runner.ts +++ b/packages/kbn-dev-utils/src/proc_runner/proc_runner.ts @@ -7,7 +7,6 @@ */ import * as Rx from 'rxjs'; -import { filter, first, catchError, map } from 'rxjs/operators'; import exitHook from 'exit-hook'; import { ToolingLog } from '@kbn/tooling-log'; @@ -22,6 +21,7 @@ const noop = () => {}; interface RunOptions extends ProcOptions { wait: true | RegExp; waitTimeout?: number | false; + onEarlyExit?: (msg: string) => void; } /** @@ -48,16 +48,6 @@ export class ProcRunner { /** * Start a process, tracking it by `name` - * @param {String} name - * @param {Object} options - * @property {String} options.cmd executable to run - * @property {Array?} options.args arguments to provide the executable - * @property {String?} options.cwd current working directory for the process - * @property {RegExp|Boolean} options.wait Should start() wait for some time? Use - * `true` will wait until the proc exits, - * a `RegExp` will wait until that log line - * is found - * @return {Promise} */ async run(name: string, options: RunOptions) { const { @@ -67,6 +57,7 @@ export class ProcRunner { wait = false, waitTimeout = 15 * MINUTE, env = process.env, + onEarlyExit, } = options; const cmd = options.cmd === 'node' ? process.execPath : options.cmd; @@ -90,32 +81,52 @@ export class ProcRunner { stdin, }); + if (onEarlyExit) { + proc.outcomePromise + .then( + (code) => { + if (!proc.stopWasCalled()) { + onEarlyExit(`[${name}] exitted early with ${code}`); + } + }, + (error) => { + if (!proc.stopWasCalled()) { + onEarlyExit(`[${name}] exitted early: ${error.message}`); + } + } + ) + .catch((error) => { + throw new Error(`Error handling early exit: ${error.stack}`); + }); + } + try { if (wait instanceof RegExp) { // wait for process to log matching line - await Rx.race( - proc.lines$.pipe( - filter((line) => wait.test(line)), - first(), - catchError((err) => { - if (err.name !== 'EmptyError') { - throw createFailError(`[${name}] exited without matching pattern: ${wait}`); - } else { - throw err; - } - }) - ), - waitTimeout === false - ? Rx.NEVER - : Rx.timer(waitTimeout).pipe( - map(() => { - const sec = waitTimeout / SECOND; - throw createFailError( - `[${name}] failed to match pattern within ${sec} seconds [pattern=${wait}]` - ); - }) - ) - ).toPromise(); + await Rx.lastValueFrom( + Rx.race( + proc.lines$.pipe( + Rx.filter((line) => wait.test(line)), + Rx.take(1), + Rx.defaultIfEmpty(undefined), + Rx.map((line) => { + if (line === undefined) { + throw createFailError(`[${name}] exited without matching pattern: ${wait}`); + } + }) + ), + waitTimeout === false + ? Rx.NEVER + : Rx.timer(waitTimeout).pipe( + Rx.map(() => { + const sec = waitTimeout / SECOND; + throw createFailError( + `[${name}] failed to match pattern within ${sec} seconds [pattern=${wait}]` + ); + }) + ) + ) + ); } if (wait === true) { diff --git a/packages/kbn-doc-links/BUILD.bazel b/packages/kbn-doc-links/BUILD.bazel index 13b68935c4326..25f376afba96b 100644 --- a/packages/kbn-doc-links/BUILD.bazel +++ b/packages/kbn-doc-links/BUILD.bazel @@ -45,6 +45,13 @@ jsts_transpiler( build_pkg_name = package_name(), ) +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + ts_config( name = "tsconfig", src = "tsconfig.json", @@ -69,7 +76,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node"], + deps = RUNTIME_DEPS + [":target_node", ":target_web"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-doc-links/package.json b/packages/kbn-doc-links/package.json index 8ddbe564c356c..e4212ed989d9a 100644 --- a/packages/kbn-doc-links/package.json +++ b/packages/kbn-doc-links/package.json @@ -1,5 +1,6 @@ { "name": "@kbn/doc-links", + "browser": "./target_web/index.js", "main": "./target_node/index.js", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index a661707bfa568..818825096ffc1 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -47,6 +47,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { metaData: `${APM_DOCS}guide/${DOC_LINK_VERSION}/data-model-metadata.html`, overview: `${APM_DOCS}guide/${DOC_LINK_VERSION}/apm-overview.html`, tailSamplingPolicies: `${APM_DOCS}guide/${DOC_LINK_VERSION}/configure-tail-based-sampling.html`, + elasticAgent: `${APM_DOCS}guide/${DOC_LINK_VERSION}/upgrade-to-apm-integration.html`, }, canvas: { guide: `${KIBANA_DOCS}canvas.html`, @@ -459,6 +460,9 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { monitorLogstash: `${ELASTIC_WEBSITE_URL}guide/en/logstash/${DOC_LINK_VERSION}/monitoring-with-metricbeat.html`, troubleshootKibana: `${KIBANA_DOCS}monitor-troubleshooting.html`, }, + reporting: { + cloudMinimumRequirements: `${KIBANA_DOCS}reporting-getting-started.html#reporting-on-cloud-resource-requirements`, + }, security: { apiKeyServiceSettings: `${ELASTICSEARCH_DOCS}security-settings.html#api-key-service-settings`, clusterPrivileges: `${ELASTICSEARCH_DOCS}security-privileges.html#privileges-list-cluster`, @@ -599,7 +603,6 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { installElasticAgent: `${FLEET_DOCS}install-fleet-managed-elastic-agent.html`, installElasticAgentStandalone: `${FLEET_DOCS}install-standalone-elastic-agent.html`, upgradeElasticAgent: `${FLEET_DOCS}upgrade-elastic-agent.html`, - upgradeElasticAgent712lower: `${FLEET_DOCS}upgrade-elastic-agent.html#upgrade-7.12-lower`, learnMoreBlog: `${ELASTIC_WEBSITE_URL}blog/elastic-agent-and-fleet-make-it-easier-to-integrate-your-systems-with-elastic`, apiKeysLearnMore: `${KIBANA_DOCS}api-keys.html`, onPremRegistry: `${FLEET_DOCS}air-gapped.html`, @@ -645,6 +648,8 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { }, kibanaUpgradeSavedObjects: { resolveMigrationFailures: `${KIBANA_DOCS}resolve-migrations-failures.html`, + repeatedTimeoutRequests: `${KIBANA_DOCS}resolve-migrations-failures.html#_repeated_time_out_requests_that_eventually_fail`, + routingAllocationDisabled: `${KIBANA_DOCS}resolve-migrations-failures.html#routing-allocation-disabled`, }, }); }; diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index a2d05fbbe699c..2e14fccaccd29 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -33,6 +33,7 @@ export interface DocLinks { readonly metaData: string; readonly overview: string; readonly tailSamplingPolicies: string; + readonly elasticAgent: string; }; readonly canvas: { readonly guide: string; @@ -310,6 +311,9 @@ export interface DocLinks { gdalTutorial: string; }>; readonly monitoring: Record; + readonly reporting: Readonly<{ + cloudMinimumRequirements: string; + }>; readonly security: Readonly<{ apiKeyServiceSettings: string; clusterPrivileges: string; @@ -355,7 +359,6 @@ export interface DocLinks { installElasticAgent: string; installElasticAgentStandalone: string; upgradeElasticAgent: string; - upgradeElasticAgent712lower: string; learnMoreBlog: string; apiKeysLearnMore: string; onPremRegistry: string; @@ -400,5 +403,7 @@ export interface DocLinks { }; readonly kibanaUpgradeSavedObjects: { readonly resolveMigrationFailures: string; + readonly repeatedTimeoutRequests: string; + readonly routingAllocationDisabled: string; }; } diff --git a/packages/kbn-docs-utils/src/api_docs/build_api_declarations/buid_api_declaration.test.ts b/packages/kbn-docs-utils/src/api_docs/build_api_declarations/buid_api_declaration.test.ts index 9b86db445c225..527ce59011a8b 100644 --- a/packages/kbn-docs-utils/src/api_docs/build_api_declarations/buid_api_declaration.test.ts +++ b/packages/kbn-docs-utils/src/api_docs/build_api_declarations/buid_api_declaration.test.ts @@ -53,6 +53,24 @@ it('Test number primitive doc def', () => { expect(def.type).toBe(TypeKind.NumberKind); }); +it('Test a constructor type declaration inside an interface', () => { + const node = nodes.find((n) => getNodeName(n) === 'ClassConstructorWithStaticProperties'); + expect(node).toBeDefined(); + const def = buildApiDeclarationTopNode(node!, { + plugins, + log, + currentPluginId: plugins[0].manifest.id, + scope: ApiScope.CLIENT, + captureReferences: false, + }); + + expect(def.type).toBe(TypeKind.InterfaceKind); + expect(def.children).toHaveLength(2); + expect(def.children![1].type).toBe(TypeKind.FunctionKind); + expect(def.children![1].label).toBe('new'); + expect(def.children![1].id).toBe('def-public.ClassConstructorWithStaticProperties.new'); +}); + it('Function type is exported as type with signature', () => { const node = nodes.find((n) => getNodeName(n) === 'FnWithGeneric'); expect(node).toBeDefined(); diff --git a/packages/kbn-docs-utils/src/api_docs/build_api_declarations/build_api_declaration.ts b/packages/kbn-docs-utils/src/api_docs/build_api_declarations/build_api_declaration.ts index 809097ee73818..2e167c7a0a783 100644 --- a/packages/kbn-docs-utils/src/api_docs/build_api_declarations/build_api_declaration.ts +++ b/packages/kbn-docs-utils/src/api_docs/build_api_declarations/build_api_declaration.ts @@ -65,12 +65,17 @@ export function buildApiDeclaration(node: Node, opts: BuildApiDecOpts): ApiDecla Node.isMethodSignature(node) || Node.isFunctionDeclaration(node) || Node.isMethodDeclaration(node) || + Node.isConstructSignatureDeclaration(node) || Node.isConstructorDeclaration(node) ) { return buildFunctionDec(node, { ...opts, // Use "Constructor" if applicable, instead of the default "Unnamed" - name: Node.isConstructorDeclaration(node) ? 'Constructor' : node.getName() || 'Unnamed', + name: Node.isConstructSignatureDeclaration(node) + ? 'new' + : Node.isConstructorDeclaration(node) + ? 'Constructor' + : node.getName() || 'Unnamed', }); } else if ( Node.isPropertySignature(node) || diff --git a/packages/kbn-docs-utils/src/api_docs/build_api_declarations/build_function_dec.ts b/packages/kbn-docs-utils/src/api_docs/build_api_declarations/build_function_dec.ts index 3ba688f1ee284..020ffd402366a 100644 --- a/packages/kbn-docs-utils/src/api_docs/build_api_declarations/build_function_dec.ts +++ b/packages/kbn-docs-utils/src/api_docs/build_api_declarations/build_function_dec.ts @@ -11,6 +11,7 @@ import { MethodDeclaration, ConstructorDeclaration, MethodSignature, + ConstructSignatureDeclaration, } from 'ts-morph'; import { buildApiDecsForParameters } from './build_parameter_decs'; @@ -23,7 +24,12 @@ import { BuildApiDecOpts } from './types'; * Takes the various function-like node declaration types and converts them into an ApiDeclaration. */ export function buildFunctionDec( - node: FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature, + node: + | ConstructSignatureDeclaration + | FunctionDeclaration + | MethodDeclaration + | ConstructorDeclaration + | MethodSignature, opts: BuildApiDecOpts ): ApiDeclaration { const fn = { diff --git a/packages/kbn-docs-utils/src/api_docs/build_api_declarations/utils.ts b/packages/kbn-docs-utils/src/api_docs/build_api_declarations/utils.ts index a57b1790b27a8..76328a314b066 100644 --- a/packages/kbn-docs-utils/src/api_docs/build_api_declarations/utils.ts +++ b/packages/kbn-docs-utils/src/api_docs/build_api_declarations/utils.ts @@ -46,7 +46,11 @@ export function buildParentApiId(parentName: string, parentsParentApiId?: string } export function getOptsForChild(node: Node, parentOpts: BuildApiDecOpts): BuildApiDecOpts { - const name = isNamedNode(node) ? node.getName() : 'Unnamed'; + const name = Node.isConstructSignatureDeclaration(node) + ? 'new' + : isNamedNode(node) + ? node.getName() + : 'Unnamed'; return getOptsForChildWithName(name, parentOpts); } diff --git a/packages/kbn-docs-utils/src/api_docs/build_api_docs_cli.ts b/packages/kbn-docs-utils/src/api_docs/build_api_docs_cli.ts index 0617e35a88615..41bb6400b92ab 100644 --- a/packages/kbn-docs-utils/src/api_docs/build_api_docs_cli.ts +++ b/packages/kbn-docs-utils/src/api_docs/build_api_docs_cli.ts @@ -65,11 +65,16 @@ export function runBuildApiDocsCli() { // Delete all files except the README that warns about the auto-generated nature of // the folder. const files = Fs.readdirSync(outputFolder); - files.forEach((file) => { - if (file.indexOf('README.md') < 0) { - Fs.rmSync(Path.resolve(outputFolder, file)); - } - }); + await Promise.all( + files + .filter((file) => file.indexOf('README.md') < 0) + .map( + (file) => + new Promise((resolve, reject) => + Fs.rm(Path.resolve(outputFolder, file), (err) => (err ? reject(err) : resolve())) + ) + ) + ); } const collectReferences = flags.references as boolean; diff --git a/packages/kbn-docs-utils/src/api_docs/mdx/split_apis_by_folder.test.ts b/packages/kbn-docs-utils/src/api_docs/mdx/split_apis_by_folder.test.ts index b1862f9bc2165..1a1ecb8ec3e67 100644 --- a/packages/kbn-docs-utils/src/api_docs/mdx/split_apis_by_folder.test.ts +++ b/packages/kbn-docs-utils/src/api_docs/mdx/split_apis_by_folder.test.ts @@ -38,7 +38,7 @@ beforeAll(() => { }); test('foo service has all exports', () => { - expect(doc?.client.length).toBe(37); + expect(doc?.client.length).toBe(38); const split = splitApisByFolder(doc); expect(split.length).toBe(2); @@ -47,5 +47,5 @@ test('foo service has all exports', () => { expect(fooDoc?.common.length).toBe(1); expect(fooDoc?.client.length).toBe(2); - expect(mainDoc?.client.length).toBe(35); + expect(mainDoc?.client.length).toBe(36); }); diff --git a/packages/kbn-docs-utils/src/api_docs/tests/__fixtures__/src/plugin_a/public/index.ts b/packages/kbn-docs-utils/src/api_docs/tests/__fixtures__/src/plugin_a/public/index.ts index 345e85bc044b7..ad3d1204aeda9 100644 --- a/packages/kbn-docs-utils/src/api_docs/tests/__fixtures__/src/plugin_a/public/index.ts +++ b/packages/kbn-docs-utils/src/api_docs/tests/__fixtures__/src/plugin_a/public/index.ts @@ -24,6 +24,11 @@ export interface InterfaceWithIndexSignature { [key: string]: { foo: string }; } +export interface ClassConstructorWithStaticProperties { + staticProperty1: string; + new (config: { foo: string }): InterfaceWithIndexSignature; +} + export function plugin() { return new PluginA(); } diff --git a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.devdocs.json b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.devdocs.json index ff977517cb5a7..88e4043442e88 100644 --- a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.devdocs.json +++ b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.devdocs.json @@ -712,6 +712,67 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "pluginA", + "id": "def-public.ClassConstructorWithStaticProperties", + "type": "Interface", + "tags": [], + "label": "ClassConstructorWithStaticProperties", + "description": [], + "path": "packages/kbn-docs-utils/src/api_docs/tests/__fixtures__/src/plugin_a/public/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "pluginA", + "id": "def-public.ClassConstructorWithStaticProperties.staticProperty1", + "type": "string", + "tags": [], + "label": "staticProperty1", + "description": [], + "path": "packages/kbn-docs-utils/src/api_docs/tests/__fixtures__/src/plugin_a/public/index.ts", + "deprecated": false + }, + { + "parentPluginId": "pluginA", + "id": "def-public.ClassConstructorWithStaticProperties.new", + "type": "Function", + "tags": [], + "label": "new", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-docs-utils/src/api_docs/tests/__fixtures__/src/plugin_a/public/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "pluginA", + "id": "def-public.ClassConstructorWithStaticProperties.new.$1", + "type": "Object", + "tags": [], + "label": "config", + "description": [], + "path": "packages/kbn-docs-utils/src/api_docs/tests/__fixtures__/src/plugin_a/public/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "pluginA", + "id": "def-public.ClassConstructorWithStaticProperties.new.$1.foo", + "type": "string", + "tags": [], + "label": "foo", + "description": [], + "path": "packages/kbn-docs-utils/src/api_docs/tests/__fixtures__/src/plugin_a/public/index.ts", + "deprecated": false + } + ] + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "pluginA", "id": "def-public.ExampleInterface", diff --git a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.mdx b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.mdx index ab80f1f02d0ac..6a66fa74f7c01 100644 --- a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.mdx +++ b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/pluginA title: "pluginA" image: https://source.unsplash.com/400x175/?github summary: API docs for the pluginA plugin -date: 2022-02-14 +date: 2022-04-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'pluginA'] warning: 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. --- @@ -18,7 +18,7 @@ Contact Kibana Core for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 131 | 1 | 71 | 2 | +| 136 | 1 | 76 | 2 | ## Client diff --git a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a_foo.mdx b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a_foo.mdx index e9873f8223017..4082de6306895 100644 --- a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a_foo.mdx +++ b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a_foo.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/pluginA-foo title: "pluginA.foo" image: https://source.unsplash.com/400x175/?github summary: API docs for the pluginA.foo plugin -date: 2022-02-14 +date: 2022-04-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'pluginA.foo'] warning: 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. --- @@ -18,7 +18,7 @@ Contact Kibana Core for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 131 | 1 | 71 | 2 | +| 136 | 1 | 76 | 2 | ## Client diff --git a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_b.mdx b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_b.mdx index 1671cd7a529d3..d69da971f7e3e 100644 --- a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_b.mdx +++ b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_b.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/pluginB title: "pluginB" image: https://source.unsplash.com/400x175/?github summary: API docs for the pluginB plugin -date: 2022-02-14 +date: 2022-04-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'pluginB'] warning: 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. --- diff --git a/packages/kbn-es-archiver/src/cli.ts b/packages/kbn-es-archiver/src/cli.ts index fbb5784afe5ac..cf871abe6f18f 100644 --- a/packages/kbn-es-archiver/src/cli.ts +++ b/packages/kbn-es-archiver/src/cli.ts @@ -24,7 +24,7 @@ import { Client, HttpConnection } from '@elastic/elasticsearch'; import { EsArchiver } from './es_archiver'; const resolveConfigPath = (v: string) => Path.resolve(process.cwd(), v); -const defaultConfigPath = resolveConfigPath('test/functional/config.js'); +const defaultConfigPath = resolveConfigPath('test/functional/config.base.js'); export function runCli() { new RunWithCommands({ @@ -33,7 +33,7 @@ export function runCli() { string: ['es-url', 'kibana-url', 'config', 'es-ca', 'kibana-ca'], help: ` --config path to an FTR config file that sets --es-url and --kibana-url - default: ${defaultConfigPath} + default: ${Path.relative(process.cwd(), defaultConfigPath)} --es-url url for Elasticsearch, prefer the --config flag --kibana-url url for Kibana, prefer the --config flag --kibana-ca if Kibana url points to https://localhost we default to the CA from @kbn/dev-utils, customize the CA with this flag diff --git a/packages/kbn-es/src/cluster.js b/packages/kbn-es/src/cluster.js index a6faffc2cfcd7..eecaef06be453 100644 --- a/packages/kbn-es/src/cluster.js +++ b/packages/kbn-es/src/cluster.js @@ -215,6 +215,25 @@ exports.Cluster = class Cluster { }), ]); }); + + if (options.onEarlyExit) { + this._outcome + .then( + () => { + if (!this._stopCalled) { + options.onEarlyExit(`ES exitted unexpectedly`); + } + }, + (error) => { + if (!this._stopCalled) { + options.onEarlyExit(`ES exitted unexpectedly: ${error.stack}`); + } + } + ) + .catch((error) => { + throw new Error(`failure handling early exit: ${error.stack}`); + }); + } } /** @@ -260,6 +279,24 @@ exports.Cluster = class Cluster { await this._outcome; } + /** + * Stops ES process, it it's running, without waiting for it to shutdown gracefully + */ + async kill() { + if (this._stopCalled) { + return; + } + + this._stopCalled; + + if (!this._process || !this._outcome) { + throw new Error('ES has not been started'); + } + + await treeKillAsync(this._process.pid, 'SIGKILL'); + await this._outcome; + } + /** * Common logic from this.start() and this.run() * diff --git a/packages/kbn-es/src/cluster_exec_options.ts b/packages/kbn-es/src/cluster_exec_options.ts index 8ef3b23cd8c51..da21aaf05b139 100644 --- a/packages/kbn-es/src/cluster_exec_options.ts +++ b/packages/kbn-es/src/cluster_exec_options.ts @@ -15,4 +15,5 @@ export interface EsClusterExecOptions { password?: string; skipReadyCheck?: boolean; readyTimeout?: number; + onEarlyExit?: (msg: string) => void; } diff --git a/packages/kbn-eslint-plugin-imports/jest.integration.config.js b/packages/kbn-eslint-plugin-imports/jest.integration.config.js deleted file mode 100644 index e7d05a6222c88..0000000000000 --- a/packages/kbn-eslint-plugin-imports/jest.integration.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -module.exports = { - preset: '@kbn/test/jest_integration_node', - rootDir: '../..', - roots: ['/packages/kbn-eslint-plugin-imports'], -}; diff --git a/packages/kbn-field-types/BUILD.bazel b/packages/kbn-field-types/BUILD.bazel index 77a4acaedb235..4259b9c14a6ab 100644 --- a/packages/kbn-field-types/BUILD.bazel +++ b/packages/kbn-field-types/BUILD.bazel @@ -43,10 +43,17 @@ TYPES_DEPS = [ ] jsts_transpiler( - name = "target_node", - srcs = SRCS, - build_pkg_name = package_name(), - ) + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) ts_config( name = "tsconfig", @@ -72,7 +79,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node"], + deps = RUNTIME_DEPS + [":target_node", ":target_web"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-field-types/package.json b/packages/kbn-field-types/package.json index 4e6276e508c36..14b842526d9bc 100644 --- a/packages/kbn-field-types/package.json +++ b/packages/kbn-field-types/package.json @@ -3,5 +3,6 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", + "browser": "./target_web/index.js", "main": "./target_node/index.js" } diff --git a/packages/kbn-generate/jest.config.js b/packages/kbn-generate/jest.config.js deleted file mode 100644 index b72f891cfb1a7..0000000000000 --- a/packages/kbn-generate/jest.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-generate'], -}; diff --git a/packages/kbn-import-resolver/src/import_resolver.ts b/packages/kbn-import-resolver/src/import_resolver.ts index 44114ce731e28..05b69f299a798 100644 --- a/packages/kbn-import-resolver/src/import_resolver.ts +++ b/packages/kbn-import-resolver/src/import_resolver.ts @@ -25,9 +25,18 @@ const NODE_MODULE_SEG = Path.sep + 'node_modules' + Path.sep; export class ImportResolver { static create(repoRoot: string) { const pkgMap = new Map(); - for (const dir of discoverBazelPackageLocations(repoRoot)) { - const pkg = JSON.parse(Fs.readFileSync(Path.resolve(dir, 'package.json'), 'utf8')); - pkgMap.set(pkg.name, normalizePath(Path.relative(repoRoot, dir))); + for (const dir of discoverBazelPackageLocations(REPO_ROOT)) { + const relativeBazelPackageDir = Path.relative(REPO_ROOT, dir); + const repoRootBazelPackageDir = Path.resolve(repoRoot, relativeBazelPackageDir); + + if (!Fs.existsSync(Path.resolve(repoRootBazelPackageDir, 'package.json'))) { + continue; + } + + const pkg = JSON.parse( + Fs.readFileSync(Path.resolve(repoRootBazelPackageDir, 'package.json'), 'utf8') + ); + pkgMap.set(pkg.name, normalizePath(relativeBazelPackageDir)); } return new ImportResolver(repoRoot, pkgMap, readPackageMap()); diff --git a/packages/kbn-interpreter/BUILD.bazel b/packages/kbn-interpreter/BUILD.bazel index 469f16296230f..f1c066d4cbd84 100644 --- a/packages/kbn-interpreter/BUILD.bazel +++ b/packages/kbn-interpreter/BUILD.bazel @@ -42,6 +42,13 @@ jsts_transpiler( build_pkg_name = package_name(), ) +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + peggy( name = "grammar", data = [ @@ -82,7 +89,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES + [":grammar"], - deps = RUNTIME_DEPS + [":target_node"], + deps = RUNTIME_DEPS + [":target_node", ":target_web"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-interpreter/package.json b/packages/kbn-interpreter/package.json index 1480e7e808370..ca1f35c02874b 100644 --- a/packages/kbn-interpreter/package.json +++ b/packages/kbn-interpreter/package.json @@ -1,6 +1,7 @@ { "name": "@kbn/interpreter", "author": "App Services", + "browser": "./target_web/index.js", "main": "./target_node/index.js", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", diff --git a/packages/kbn-io-ts-utils/BUILD.bazel b/packages/kbn-io-ts-utils/BUILD.bazel index aa0116b81efe6..15917e75a5285 100644 --- a/packages/kbn-io-ts-utils/BUILD.bazel +++ b/packages/kbn-io-ts-utils/BUILD.bazel @@ -49,6 +49,13 @@ jsts_transpiler( build_pkg_name = package_name(), ) +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + ts_config( name = "tsconfig", src = "tsconfig.json", @@ -73,7 +80,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node"], + deps = RUNTIME_DEPS + [":target_node", ":target_web"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-io-ts-utils/package.json b/packages/kbn-io-ts-utils/package.json index 2dc3532e05d96..806f3c46cf337 100644 --- a/packages/kbn-io-ts-utils/package.json +++ b/packages/kbn-io-ts-utils/package.json @@ -1,5 +1,6 @@ { "name": "@kbn/io-ts-utils", + "browser": "./target_web/index.js", "main": "./target_node/index.js", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", diff --git a/packages/kbn-jest-serializers/jest.config.js b/packages/kbn-jest-serializers/jest.config.js deleted file mode 100644 index 23fad67c028bf..0000000000000 --- a/packages/kbn-jest-serializers/jest.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-jest-serializers'], -}; diff --git a/packages/kbn-kibana-json-schema/jest.config.js b/packages/kbn-kibana-json-schema/jest.config.js deleted file mode 100644 index 00bc8f55adc57..0000000000000 --- a/packages/kbn-kibana-json-schema/jest.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-kibana-json-schema'], -}; diff --git a/packages/kbn-mapbox-gl/BUILD.bazel b/packages/kbn-mapbox-gl/BUILD.bazel index 89cbeb4c431ae..d4cebd52152f0 100644 --- a/packages/kbn-mapbox-gl/BUILD.bazel +++ b/packages/kbn-mapbox-gl/BUILD.bazel @@ -45,6 +45,13 @@ jsts_transpiler( build_pkg_name = package_name(), ) +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + ts_config( name = "tsconfig", src = "tsconfig.json", @@ -69,7 +76,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node"], + deps = RUNTIME_DEPS + [":target_node", ":target_web"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-mapbox-gl/package.json b/packages/kbn-mapbox-gl/package.json index 493fbb881c80c..f0a5c7eabdfcb 100644 --- a/packages/kbn-mapbox-gl/package.json +++ b/packages/kbn-mapbox-gl/package.json @@ -3,5 +3,6 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", + "browser": "./target_web/index.js", "main": "./target_node/index.js" } diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index dc16c080306ad..0e7d4939cbc29 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -57,7 +57,7 @@ pageLoadAssetSize: telemetry: 51957 telemetryManagementSection: 38586 transform: 41007 - triggersActionsUi: 103400 + triggersActionsUi: 104400 upgradeAssistant: 81241 urlForwarding: 32579 usageCollection: 39762 @@ -105,7 +105,6 @@ pageLoadAssetSize: kibanaReact: 74422 share: 71239 uiActions: 35121 - dataEnhanced: 24980 embeddable: 87309 embeddableEnhanced: 22107 uiActionsEnhanced: 38494 @@ -123,9 +122,10 @@ pageLoadAssetSize: sessionView: 77750 cloudSecurityPosture: 19109 visTypeGauge: 24113 - unifiedSearch: 104869 + unifiedSearch: 71059 data: 454087 - expressionXY: 26500 eventAnnotation: 19334 screenshotting: 22870 synthetics: 40958 + expressionXY: 29000 + kibanaUsageCollection: 16463 diff --git a/packages/kbn-optimizer/src/babel_runtime_helpers/find_babel_runtime_helpers_in_entry_bundles.ts b/packages/kbn-optimizer/src/audit_bundle_dependencies/find_babel_runtime_helpers_in_entry_bundles.ts similarity index 100% rename from packages/kbn-optimizer/src/babel_runtime_helpers/find_babel_runtime_helpers_in_entry_bundles.ts rename to packages/kbn-optimizer/src/audit_bundle_dependencies/find_babel_runtime_helpers_in_entry_bundles.ts diff --git a/packages/kbn-optimizer/src/babel_runtime_helpers/find_node_libs_browser_polyfills_in_entry_bundles.ts b/packages/kbn-optimizer/src/audit_bundle_dependencies/find_node_libs_browser_polyfills_in_entry_bundles.ts similarity index 100% rename from packages/kbn-optimizer/src/babel_runtime_helpers/find_node_libs_browser_polyfills_in_entry_bundles.ts rename to packages/kbn-optimizer/src/audit_bundle_dependencies/find_node_libs_browser_polyfills_in_entry_bundles.ts diff --git a/packages/kbn-optimizer/src/audit_bundle_dependencies/find_target_node_imports.ts b/packages/kbn-optimizer/src/audit_bundle_dependencies/find_target_node_imports.ts new file mode 100644 index 0000000000000..dd1309f838e41 --- /dev/null +++ b/packages/kbn-optimizer/src/audit_bundle_dependencies/find_target_node_imports.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; + +import { run } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; + +import { OptimizerConfig } from '../optimizer'; +import { parseStats } from './parse_stats'; + +/** + * Analyzes the bundle dependencies to find any imports using the `@kbn//target_node` build target. + * + * We should aim for those packages to be imported using the `@kbn//target_web` build because it's optimized + * for browser compatibility. + * + * This utility also helps identify when code that should only run in the server is leaked into the browser. + */ +export async function runFindTargetNodeImportsCli() { + run(async ({ log }) => { + const config = OptimizerConfig.create({ + includeCoreBundle: true, + repoRoot: REPO_ROOT, + }); + + const paths = config.bundles.map((b) => Path.resolve(b.outputDir, 'stats.json')); + + log.info('analyzing', paths.length, 'stats files'); + log.verbose(paths); + + const imports = new Set(); + for (const path of paths) { + const stats = parseStats(path); + + for (const module of stats.modules) { + if (module.name.includes('/target_node/')) { + const [, cleanName] = /\/((?:kbn-|@kbn\/).+)\/target_node/.exec(module.name) ?? []; + imports.add(cleanName || module.name); + } + } + } + + log.success('found', imports.size, '@kbn/*/target_node imports in entry bundles and chunks'); + log.write( + Array.from(imports, (i) => `'${i}',`) + .sort() + .join('\n') + ); + }); +} diff --git a/packages/kbn-optimizer/src/audit_bundle_dependencies/index.ts b/packages/kbn-optimizer/src/audit_bundle_dependencies/index.ts new file mode 100644 index 0000000000000..e6059c4c2c9b5 --- /dev/null +++ b/packages/kbn-optimizer/src/audit_bundle_dependencies/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './find_babel_runtime_helpers_in_entry_bundles'; +export * from './find_node_libs_browser_polyfills_in_entry_bundles'; +export * from './find_target_node_imports'; diff --git a/packages/kbn-optimizer/src/babel_runtime_helpers/parse_stats.ts b/packages/kbn-optimizer/src/audit_bundle_dependencies/parse_stats.ts similarity index 100% rename from packages/kbn-optimizer/src/babel_runtime_helpers/parse_stats.ts rename to packages/kbn-optimizer/src/audit_bundle_dependencies/parse_stats.ts diff --git a/packages/kbn-optimizer/src/babel_runtime_helpers/index.ts b/packages/kbn-optimizer/src/babel_runtime_helpers/index.ts deleted file mode 100644 index 3a7987f867bc5..0000000000000 --- a/packages/kbn-optimizer/src/babel_runtime_helpers/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 './find_babel_runtime_helpers_in_entry_bundles'; -export * from './find_node_libs_browser_polyfills_in_entry_bundles'; diff --git a/packages/kbn-optimizer/src/index.ts b/packages/kbn-optimizer/src/index.ts index d759a4aa02455..48e77c8628905 100644 --- a/packages/kbn-optimizer/src/index.ts +++ b/packages/kbn-optimizer/src/index.ts @@ -14,4 +14,4 @@ export * from './node'; export * from './limits'; export * from './cli'; export * from './report_optimizer_timings'; -export * from './babel_runtime_helpers'; +export * from './audit_bundle_dependencies'; diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_built_paths.ts b/packages/kbn-optimizer/src/optimizer/optimizer_built_paths.ts index 294c3e835a3bd..8421c0846d52a 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_built_paths.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_built_paths.ts @@ -14,7 +14,10 @@ import { ascending } from '../common'; export async function getOptimizerBuiltPaths() { return ( await globby( - ['**/*', '!**/{__fixtures__,__snapshots__,integration_tests,babel_runtime_helpers,node}/**'], + [ + '**/*', + '!**/{__fixtures__,__snapshots__,integration_tests,audit_bundle_dependencies,node}/**', + ], { cwd: Path.resolve(__dirname, '../'), absolute: true, diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 422fe12f11b28..876bc347a21c5 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -94,7 +94,7 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker: module: { // no parse rules for a few known large packages which have no require() statements // or which have require() statements that should be ignored because the file is - // already bundled with all its necessary depedencies + // already bundled with all its necessary dependencies noParse: [ /[\/\\]node_modules[\/\\]lodash[\/\\]index\.js$/, /[\/\\]node_modules[\/\\]vega[\/\\]build[\/\\]vega\.js$/, diff --git a/packages/kbn-performance-testing-dataset-extractor/BUILD.bazel b/packages/kbn-performance-testing-dataset-extractor/BUILD.bazel new file mode 100644 index 0000000000000..b58375165352c --- /dev/null +++ b/packages/kbn-performance-testing-dataset-extractor/BUILD.bazel @@ -0,0 +1,122 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "kbn-performance-testing-dataset-extractor" +PKG_REQUIRE_NAME = "@kbn/performance-testing-dataset-extractor" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ + "//packages/kbn-dev-utils", + "//packages/kbn-utils", + "//packages/kbn-tooling-log", + "@npm//@elastic/elasticsearch", +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "//packages/kbn-dev-utils:npm_module_types", + "//packages/kbn-utils:npm_module_types", + "//packages/kbn-tooling-log:npm_module_types", + "@npm//@elastic/elasticsearch", + "@npm//@types/node", + "@npm//@types/jest", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-performance-testing-dataset-extractor/README.md b/packages/kbn-performance-testing-dataset-extractor/README.md new file mode 100644 index 0000000000000..ef5488a82ff21 --- /dev/null +++ b/packages/kbn-performance-testing-dataset-extractor/README.md @@ -0,0 +1,14 @@ +# @kbn/performance-testing-dataset-extractor + +A library to convert APM traces into JSON format for performance testing. + +## Usage + +``` + node scripts/extract_performance_testing_dataset \ + --journeyName "<_source.labels.journeyName>" \ + --buildId "<_source.labels.testBuildId>" \ + --es-url "" \ + --es-username "" \ + --es-password "" +``` \ No newline at end of file diff --git a/packages/kbn-performance-testing-dataset-extractor/jest.config.js b/packages/kbn-performance-testing-dataset-extractor/jest.config.js new file mode 100644 index 0000000000000..e31a2d7996893 --- /dev/null +++ b/packages/kbn-performance-testing-dataset-extractor/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-performance-testing-dataset-extractor'], +}; diff --git a/packages/kbn-performance-testing-dataset-extractor/package.json b/packages/kbn-performance-testing-dataset-extractor/package.json new file mode 100644 index 0000000000000..4d637728b28de --- /dev/null +++ b/packages/kbn-performance-testing-dataset-extractor/package.json @@ -0,0 +1,11 @@ +{ + "name": "@kbn/performance-testing-dataset-extractor", + "description": "A library to convert APM traces into JSON format for performance testing.", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0", + "kibana": { + "devOnly": true + } +} diff --git a/packages/kbn-performance-testing-dataset-extractor/src/cli.ts b/packages/kbn-performance-testing-dataset-extractor/src/cli.ts new file mode 100644 index 0000000000000..7d16f625e4874 --- /dev/null +++ b/packages/kbn-performance-testing-dataset-extractor/src/cli.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** *********************************************************** + * + * Run `node scripts/extract_performance_testing_dataset --help` for usage information + * + *************************************************************/ + +import { run, createFlagError } from '@kbn/dev-utils'; +import { extractor } from './extractor'; + +export async function runExtractor() { + run( + async ({ log, flags }) => { + const baseURL = flags['es-url']; + if (baseURL && typeof baseURL !== 'string') { + throw createFlagError('--es-url must be a string'); + } + if (!baseURL) { + throw createFlagError('--es-url must be defined'); + } + + const username = flags['es-username']; + if (username && typeof username !== 'string') { + throw createFlagError('--es-username must be a string'); + } + if (!username) { + throw createFlagError('--es-username must be defined'); + } + + const password = flags['es-password']; + if (password && typeof password !== 'string') { + throw createFlagError('--es-password must be a string'); + } + if (!password) { + throw createFlagError('--es-password must be defined'); + } + + const journeyName = flags.journeyName; + if (journeyName && typeof journeyName !== 'string') { + throw createFlagError('--journeyName must be a string'); + } + if (!journeyName) { + throw createFlagError('--journeyName must be defined'); + } + + const buildId = flags.buildId; + if (buildId && typeof buildId !== 'string') { + throw createFlagError('--buildId must be a string'); + } + if (!buildId) { + throw createFlagError('--buildId must be defined'); + } + + return extractor({ + param: { journeyName, buildId }, + client: { baseURL, username, password }, + log, + }); + }, + { + description: `CLI to fetch and normalize APM traces for journey scalability testing`, + flags: { + string: ['journeyName', 'buildId', 'es-url', 'es-username', 'es-password'], + help: ` + --journeyName Single user performance journey name, stored in APM-based document as label: 'labels.journeyName' + --buildId BUILDKITE_JOB_ID or uuid generated locally, stored in APM-based document as label: 'labels.testBuildId' + --es-url url for Elasticsearch (APM cluster) + --es-username username for Elasticsearch (APM cluster) + --es-password password for Elasticsearch (APM cluster) + `, + }, + } + ); +} diff --git a/packages/kbn-performance-testing-dataset-extractor/src/es_client.ts b/packages/kbn-performance-testing-dataset-extractor/src/es_client.ts new file mode 100644 index 0000000000000..53c2e8ba9e8c3 --- /dev/null +++ b/packages/kbn-performance-testing-dataset-extractor/src/es_client.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 { Client } from '@elastic/elasticsearch'; + +interface ClientOptions { + node: string; + username: string; + password: string; +} + +interface Labels { + journeyName: string; + maxUsersCount: string; +} + +interface Request { + method: string; + headers: string; + body?: { original: string }; +} + +interface Response { + status_code: number; +} + +interface Transaction { + id: string; + name: string; + type: string; +} + +export interface Document { + labels: Labels; + character: string; + quote: string; + service: { version: string }; + processor: string; + trace: { id: string }; + '@timestamp': string; + environment: string; + url: { path: string }; + http: { + request: Request; + response: Response; + }; + transaction: Transaction; +} + +export function initClient(options: ClientOptions) { + const client = new Client({ + node: options.node, + auth: { + username: options.username, + password: options.password, + }, + }); + + return { + async getTransactions(buildId: string, journeyName: string) { + const result = await client.search({ + body: { + track_total_hits: true, + sort: [ + { + '@timestamp': { + order: 'desc', + unmapped_type: 'boolean', + }, + }, + ], + size: 10000, + stored_fields: ['*'], + _source: true, + query: { + bool: { + must: [], + filter: [ + { + bool: { + filter: [ + { + bool: { + should: [ + { + match_phrase: { + 'transaction.type': 'request', + }, + }, + ], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [ + { + match_phrase: { + 'processor.event': 'transaction', + }, + }, + ], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [ + { + match_phrase: { + 'labels.testBuildId': buildId, + }, + }, + ], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [ + { + match_phrase: { + 'labels.journeyName': journeyName, + }, + }, + ], + minimum_should_match: 1, + }, + }, + ], + }, + }, + ], + should: [], + must_not: [], + }, + }, + }, + }); + return result?.hits?.hits; + }, + }; +} diff --git a/packages/kbn-performance-testing-dataset-extractor/src/extractor.ts b/packages/kbn-performance-testing-dataset-extractor/src/extractor.ts new file mode 100644 index 0000000000000..cd0dbed9d8e51 --- /dev/null +++ b/packages/kbn-performance-testing-dataset-extractor/src/extractor.ts @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import fs from 'fs/promises'; +import { existsSync } from 'fs'; +import path from 'path'; +import { ToolingLog } from '@kbn/tooling-log'; +import { initClient, Document } from './es_client'; + +interface CLIParams { + param: { + journeyName: string; + buildId: string; + }; + client: { + baseURL: string; + username: string; + password: string; + }; + log: ToolingLog; +} + +export const extractor = async ({ param, client, log }: CLIParams) => { + const authOptions = { + node: client.baseURL, + username: client.username, + password: client.password, + }; + const esClient = initClient(authOptions); + const hits = await esClient.getTransactions(param.buildId, param.journeyName); + if (!hits || hits.length === 0) { + log.warning(` + No transactions found with 'labels.testBuildId=${param.buildId}' and 'labels.journeyName=${param.journeyName}' + \nOutput file won't be generated + `); + return; + } + + const source = hits[0]!._source as Document; + const journeyName = source.labels.journeyName || 'Unknown Journey'; + const kibanaVersion = source.service.version; + const maxUsersCount = source.labels.maxUsersCount || '0'; + + const data = hits + .map((hit) => hit!._source as Document) + .map((hit) => { + return { + processor: hit.processor, + traceId: hit.trace.id, + timestamp: hit['@timestamp'], + environment: hit.environment, + request: { + url: { path: hit.url.path }, + headers: hit.http.request.headers, + method: hit.http.request.method, + body: hit.http.request.body ? JSON.parse(hit.http.request.body.original) : '', + }, + response: { statusCode: hit.http.response.status_code }, + transaction: { + id: hit.transaction.id, + name: hit.transaction.name, + type: hit.transaction.type, + }, + }; + }); + + const output = { + journeyName, + kibanaVersion, + maxUsersCount, + traceItems: data, + }; + + const outputDir = path.resolve('target/scalability_traces'); + const fileName = `${output.journeyName.replace(/ /g, '')}-${param.buildId}.json`; + const filePath = path.resolve(outputDir, fileName); + + log.info(`Found ${hits.length} transactions, output file: ${filePath}`); + if (!existsSync(outputDir)) { + await fs.mkdir(outputDir, { recursive: true }); + } + await fs.writeFile(filePath, JSON.stringify(output, null, 2), 'utf8'); +}; diff --git a/packages/kbn-performance-testing-dataset-extractor/src/index.ts b/packages/kbn-performance-testing-dataset-extractor/src/index.ts new file mode 100644 index 0000000000000..4e739789d65af --- /dev/null +++ b/packages/kbn-performance-testing-dataset-extractor/src/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 { extractor } from './extractor'; +export * from './cli'; diff --git a/packages/kbn-ci-stats-client/tsconfig.json b/packages/kbn-performance-testing-dataset-extractor/tsconfig.json similarity index 100% rename from packages/kbn-ci-stats-client/tsconfig.json rename to packages/kbn-performance-testing-dataset-extractor/tsconfig.json diff --git a/packages/kbn-plugin-discovery/jest.config.js b/packages/kbn-plugin-discovery/jest.config.js deleted file mode 100644 index 37d7a4f2b63a2..0000000000000 --- a/packages/kbn-plugin-discovery/jest.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-plugin-discovery'], -}; diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 32171e030d91a..f2d5a60cd325e 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -1580,8 +1580,21 @@ var _ciStatsCore = __webpack_require__("../../node_modules/@kbn/ci-stats-core/ta */ // @ts-expect-error not "public", but necessary to prevent Jest shimming from breaking things const BASE_URL = 'https://ci-stats.kibana.dev'; +const SECOND = 1000; +const MINUTE = 60 * SECOND; + +function limitMetaStrings(meta) { + return Object.fromEntries(Object.entries(meta).map(([key, value]) => { + if (typeof value === 'string' && value.length > 2000) { + return [key, value.slice(0, 2000)]; + } + + return [key, value]; + })); +} /** A ci-stats metric record */ + /** Object that helps report data to the ci-stats service */ class CiStatsReporter { /** @@ -1631,7 +1644,9 @@ class CiStatsReporter { } const buildId = (_this$config3 = this.config) === null || _this$config3 === void 0 ? void 0 : _this$config3.buildId; - const timings = options.timings; + const timings = options.timings.map(timing => timing.meta ? { ...timing, + meta: limitMetaStrings(timing.meta) + } : timing); const upstreamBranch = (_options$upstreamBran = options.upstreamBranch) !== null && _options$upstreamBran !== void 0 ? _options$upstreamBran : this.getUpstreamBranch(); const kibanaUuid = options.kibanaUuid === undefined ? this.getKibanaUuid() : options.kibanaUuid; let email; @@ -1740,19 +1755,56 @@ class CiStatsReporter { throw new Error('unable to report tests unless buildId is configured and auth config available'); } - return await this.req({ + const groupResp = await this.req({ auth: true, - path: '/v1/test_group', + path: '/v2/test_group', query: { buildId: (_this$config7 = this.config) === null || _this$config7 === void 0 ? void 0 : _this$config7.buildId }, - bodyDesc: `[${group.name}/${group.type}] test groups with ${testRuns.length} tests`, - body: [JSON.stringify({ - group - }), ...testRuns.map(testRun => JSON.stringify({ - testRun - }))].join('\n') + bodyDesc: `[${group.name}/${group.type}] test group`, + body: group }); + + if (!groupResp) { + return; + } + + let bufferBytes = 0; + const buffer = []; + + const flushBuffer = async () => { + var _this$config8; + + await this.req({ + auth: true, + path: '/v2/test_runs', + query: { + buildId: (_this$config8 = this.config) === null || _this$config8 === void 0 ? void 0 : _this$config8.buildId, + groupId: groupResp.groupId, + groupType: group.type + }, + bodyDesc: `[${group.name}/${group.type}] Chunk of ${bufferBytes} bytes`, + body: buffer.join('\n'), + timeout: 5 * MINUTE + }); + buffer.length = 0; + bufferBytes = 0; + }; // send test runs in chunks of ~500kb + + + for (const testRun of testRuns) { + const json = JSON.stringify(testRun); + bufferBytes += json.length; + buffer.push(json); + + if (bufferBytes >= 450000) { + await flushBuffer(); + } + } + + if (bufferBytes) { + await flushBuffer(); + } } /** * In order to allow this code to run before @kbn/utils is built, @kbn/pm will pass @@ -1802,7 +1854,8 @@ class CiStatsReporter { body, bodyDesc, path, - query + query, + timeout = 60 * SECOND }) { let attempt = 0; const maxAttempts = 5; @@ -1830,7 +1883,8 @@ class CiStatsReporter { adapter: _http.default, // if it can be serialized into a string, send it maxBodyLength: Infinity, - maxContentLength: Infinity + maxContentLength: Infinity, + timeout }); return resp.data; } catch (error) { @@ -2216,8 +2270,6 @@ exports.observeLines = observeLines; var Rx = _interopRequireWildcard(__webpack_require__("../../node_modules/rxjs/dist/esm5/index.js")); -var _operators = __webpack_require__("../../node_modules/rxjs/dist/esm5/operators/index.js"); - var _observe_readable = __webpack_require__("../../node_modules/@kbn/stdio-dev-helpers/target_node/observe_readable.js"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } @@ -2243,8 +2295,8 @@ const SEP = /\r?\n/; * @return {Rx.Observable} */ function observeLines(readable) { - const done$ = (0, _observe_readable.observeReadable)(readable).pipe((0, _operators.share)()); - const scan$ = Rx.fromEvent(readable, 'data').pipe((0, _operators.scan)(({ + const done$ = (0, _observe_readable.observeReadable)(readable).pipe(Rx.share()); + const scan$ = Rx.fromEvent(readable, 'data').pipe(Rx.scan(({ buffer }, chunk) => { buffer += chunk; @@ -2268,16 +2320,15 @@ function observeLines(readable) { }, { buffer: '' }), // stop if done completes or errors - (0, _operators.takeUntil)(done$.pipe((0, _operators.materialize)())), (0, _operators.share)()); + Rx.takeUntil(done$.pipe(Rx.materialize())), Rx.share()); return Rx.merge( // use done$ to provide completion/errors done$, // merge in the "lines" from each step - scan$.pipe((0, _operators.mergeMap)(({ + scan$.pipe(Rx.mergeMap(({ lines }) => lines || [])), // inject the "unsplit" data at the end - scan$.pipe((0, _operators.last)(), (0, _operators.mergeMap)(({ + scan$.pipe(Rx.takeLast(1), Rx.mergeMap(({ buffer - }) => buffer ? [buffer] : []), // if there were no lines, last() will error, so catch and complete - (0, _operators.catchError)(() => Rx.empty()))); + }) => buffer ? [buffer] : []))); } /***/ }), @@ -2295,8 +2346,6 @@ exports.observeReadable = observeReadable; var Rx = _interopRequireWildcard(__webpack_require__("../../node_modules/rxjs/dist/esm5/index.js")); -var _operators = __webpack_require__("../../node_modules/rxjs/dist/esm5/operators/index.js"); - function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } @@ -2315,7 +2364,9 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && * - fails on the first "error" event */ function observeReadable(readable) { - return Rx.race(Rx.fromEvent(readable, 'end').pipe((0, _operators.first)(), (0, _operators.ignoreElements)()), Rx.fromEvent(readable, 'error').pipe((0, _operators.first)(), (0, _operators.mergeMap)(err => Rx.throwError(err)))); + return Rx.race(Rx.fromEvent(readable, 'end').pipe(Rx.first(), Rx.ignoreElements()), Rx.fromEvent(readable, 'error').pipe(Rx.first(), Rx.map(err => { + throw err; + }))); } /***/ }), @@ -19254,7 +19305,7 @@ cmdShim.ifExists = cmdShimIfExists var fs = __webpack_require__("../../node_modules/graceful-fs/graceful-fs.js") -var mkdir = __webpack_require__("../../node_modules/cmd-shim/node_modules/mkdirp/index.js") +var mkdir = __webpack_require__("../../node_modules/mkdirp/index.js") , path = __webpack_require__("path") , toBatchSyntax = __webpack_require__("../../node_modules/cmd-shim/lib/to-batch-syntax.js") , shebangExpr = /^#\!\s*(?:\/usr\/bin\/env)?\s*([^ \t]+=[^ \t]+\s+)*\s*([^ \t]+)(.*)$/ @@ -19547,112 +19598,6 @@ function replaceDollarWithPercentPair(value) { -/***/ }), - -/***/ "../../node_modules/cmd-shim/node_modules/mkdirp/index.js": -/***/ (function(module, exports, __webpack_require__) { - -var path = __webpack_require__("path"); -var fs = __webpack_require__("fs"); -var _0777 = parseInt('0777', 8); - -module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; - -function mkdirP (p, opts, f, made) { - if (typeof opts === 'function') { - f = opts; - opts = {}; - } - else if (!opts || typeof opts !== 'object') { - opts = { mode: opts }; - } - - var mode = opts.mode; - var xfs = opts.fs || fs; - - if (mode === undefined) { - mode = _0777 & (~process.umask()); - } - if (!made) made = null; - - var cb = f || function () {}; - p = path.resolve(p); - - xfs.mkdir(p, mode, function (er) { - if (!er) { - made = made || p; - return cb(null, made); - } - switch (er.code) { - case 'ENOENT': - if (path.dirname(p) === p) return cb(er); - mkdirP(path.dirname(p), opts, function (er, made) { - if (er) cb(er, made); - else mkdirP(p, opts, cb, made); - }); - break; - - // In the case of any other error, just see if there's a dir - // there already. If so, then hooray! If not, then something - // is borked. - default: - xfs.stat(p, function (er2, stat) { - // if the stat fails, then that's super weird. - // let the original error be the failure reason. - if (er2 || !stat.isDirectory()) cb(er, made) - else cb(null, made); - }); - break; - } - }); -} - -mkdirP.sync = function sync (p, opts, made) { - if (!opts || typeof opts !== 'object') { - opts = { mode: opts }; - } - - var mode = opts.mode; - var xfs = opts.fs || fs; - - if (mode === undefined) { - mode = _0777 & (~process.umask()); - } - if (!made) made = null; - - p = path.resolve(p); - - try { - xfs.mkdirSync(p, mode); - made = made || p; - } - catch (err0) { - switch (err0.code) { - case 'ENOENT' : - made = sync(path.dirname(p), opts, made); - sync(p, opts, made); - break; - - // In the case of any other error, just see if there's a dir - // there already. If so, then hooray! If not, then something - // is borked. - default: - var stat; - try { - stat = xfs.statSync(p); - } - catch (err1) { - throw err0; - } - if (!stat.isDirectory()) throw err0; - break; - } - } - - return made; -}; - - /***/ }), /***/ "../../node_modules/color-convert/conversions.js": @@ -36253,6 +36198,112 @@ function isConstructorOrProto (obj, key) { } +/***/ }), + +/***/ "../../node_modules/mkdirp/index.js": +/***/ (function(module, exports, __webpack_require__) { + +var path = __webpack_require__("path"); +var fs = __webpack_require__("fs"); +var _0777 = parseInt('0777', 8); + +module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; + +function mkdirP (p, opts, f, made) { + if (typeof opts === 'function') { + f = opts; + opts = {}; + } + else if (!opts || typeof opts !== 'object') { + opts = { mode: opts }; + } + + var mode = opts.mode; + var xfs = opts.fs || fs; + + if (mode === undefined) { + mode = _0777 & (~process.umask()); + } + if (!made) made = null; + + var cb = f || function () {}; + p = path.resolve(p); + + xfs.mkdir(p, mode, function (er) { + if (!er) { + made = made || p; + return cb(null, made); + } + switch (er.code) { + case 'ENOENT': + if (path.dirname(p) === p) return cb(er); + mkdirP(path.dirname(p), opts, function (er, made) { + if (er) cb(er, made); + else mkdirP(p, opts, cb, made); + }); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + xfs.stat(p, function (er2, stat) { + // if the stat fails, then that's super weird. + // let the original error be the failure reason. + if (er2 || !stat.isDirectory()) cb(er, made) + else cb(null, made); + }); + break; + } + }); +} + +mkdirP.sync = function sync (p, opts, made) { + if (!opts || typeof opts !== 'object') { + opts = { mode: opts }; + } + + var mode = opts.mode; + var xfs = opts.fs || fs; + + if (mode === undefined) { + mode = _0777 & (~process.umask()); + } + if (!made) made = null; + + p = path.resolve(p); + + try { + xfs.mkdirSync(p, mode); + made = made || p; + } + catch (err0) { + switch (err0.code) { + case 'ENOENT' : + made = sync(path.dirname(p), opts, made); + sync(p, opts, made); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + var stat; + try { + stat = xfs.statSync(p); + } + catch (err1) { + throw err0; + } + if (!stat.isDirectory()) throw err0; + break; + } + } + + return made; +}; + + /***/ }), /***/ "../../node_modules/multimatch/index.js": @@ -61512,32 +61563,6 @@ class Project { return this.json.name; } - ensureValidProjectDependency(project) { - const relativePathToProject = normalizePath(path__WEBPACK_IMPORTED_MODULE_1___default.a.relative(this.path, project.path)); - const relativePathToProjectIfBazelPkg = normalizePath(path__WEBPACK_IMPORTED_MODULE_1___default.a.relative(this.path, `${__dirname}/../../../bazel-bin/packages/${path__WEBPACK_IMPORTED_MODULE_1___default.a.basename(project.path)}`)); - const versionInPackageJson = this.allDependencies[project.name]; - const expectedVersionInPackageJson = `link:${relativePathToProject}`; - const expectedVersionInPackageJsonIfBazelPkg = `link:${relativePathToProjectIfBazelPkg}`; // TODO: after introduce bazel to build all the packages and completely remove the support for kbn packages - // do not allow child projects to hold dependencies, unless they are meant to be published externally - - if (versionInPackageJson === expectedVersionInPackageJson || versionInPackageJson === expectedVersionInPackageJsonIfBazelPkg) { - return; - } - - const updateMsg = 'Update its package.json to the expected value below.'; - const meta = { - actual: `"${project.name}": "${versionInPackageJson}"`, - expected: `"${project.name}": "${expectedVersionInPackageJson}" or "${project.name}": "${expectedVersionInPackageJsonIfBazelPkg}"`, - package: `${this.name} (${this.packageJsonLocation})` - }; - - if (Object(_package_json__WEBPACK_IMPORTED_MODULE_5__[/* isLinkDependency */ "a"])(versionInPackageJson)) { - throw new _errors__WEBPACK_IMPORTED_MODULE_3__[/* CliError */ "a"](`[${this.name}] depends on [${project.name}] using 'link:', but the path is wrong. ${updateMsg}`, meta); - } - - throw new _errors__WEBPACK_IMPORTED_MODULE_3__[/* CliError */ "a"](`[${this.name}] depends on [${project.name}] but it's not using the local package. ${updateMsg}`, meta); - } - getBuildConfig() { return this.json.kibana && this.json.kibana.build || {}; } @@ -61609,10 +61634,6 @@ class Project { return Object.values(this.allDependencies).every(dep => Object(_package_json__WEBPACK_IMPORTED_MODULE_5__[/* isLinkDependency */ "a"])(dep)); } -} // We normalize all path separators to `/` in generated files - -function normalizePath(path) { - return path.replace(/[\\\/]+/g, '/'); } /***/ }), @@ -61634,7 +61655,8 @@ function normalizePath(path) { /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__("util"); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__("./src/utils/errors.ts"); -/* harmony import */ var _project__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__("./src/utils/project.ts"); +/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__("./src/utils/log.ts"); +/* harmony import */ var _project__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__("./src/utils/project.ts"); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License @@ -61647,6 +61669,7 @@ function normalizePath(path) { + const glob = Object(util__WEBPACK_IMPORTED_MODULE_2__["promisify"])(glob__WEBPACK_IMPORTED_MODULE_0___default.a); /** a Map of project names to Project instances */ @@ -61665,7 +61688,7 @@ async function getProjects(rootPath, projectsPathsPatterns, { for (const filePath of pathsToProcess) { const projectConfigPath = normalize(filePath); const projectDir = path__WEBPACK_IMPORTED_MODULE_1___default.a.dirname(projectConfigPath); - const project = await _project__WEBPACK_IMPORTED_MODULE_4__[/* Project */ "a"].fromPath(projectDir); + const project = await _project__WEBPACK_IMPORTED_MODULE_5__[/* Project */ "a"].fromPath(projectDir); const excludeProject = exclude.includes(project.name) || include.length > 0 && !include.includes(project.name) || bazelOnly && !project.isBazelPackage(); if (excludeProject) { @@ -61739,10 +61762,18 @@ function buildProjectGraph(projects) { const projectDeps = []; const dependencies = project.allDependencies; + if (!project.isSinglePackageJsonProject && Object.keys(dependencies).length > 0) { + _log__WEBPACK_IMPORTED_MODULE_4__[/* log */ "a"].warning(`${project.name} is not allowed to hold local dependencies and they will be discarded. Please declare them at the root package.json`); + } + + if (!project.isSinglePackageJsonProject) { + projectGraph.set(project.name, projectDeps); + continue; + } + for (const depName of Object.keys(dependencies)) { if (projects.has(depName)) { const dep = projects.get(depName); - project.ensureValidProjectDependency(dep); projectDeps.push(dep); } } diff --git a/packages/kbn-pm/src/__snapshots__/run.test.ts.snap b/packages/kbn-pm/src/__snapshots__/run.test.ts.snap index e5efc9a915224..28e1b98e0fcd9 100644 --- a/packages/kbn-pm/src/__snapshots__/run.test.ts.snap +++ b/packages/kbn-pm/src/__snapshots__/run.test.ts.snap @@ -4,14 +4,9 @@ exports[`excludes project if single \`exclude\` filter is specified 1`] = ` Object { "graph": Object { "bar": Array [], - "baz": Array [ - "bar", - ], + "baz": Array [], "kibana": Array [], - "quux": Array [ - "bar", - "baz", - ], + "quux": Array [], "with-additional-projects": Array [], }, "projects": Array [ @@ -42,12 +37,8 @@ Object { exports[`includes only projects specified in multiple \`include\` filters 1`] = ` Object { "graph": Object { - "bar": Array [ - "foo", - ], - "baz": Array [ - "bar", - ], + "bar": Array [], + "baz": Array [], "foo": Array [], }, "projects": Array [ @@ -72,20 +63,13 @@ Object { exports[`passes all found projects to the command if no filter is specified 1`] = ` Object { "graph": Object { - "bar": Array [ - "foo", - ], - "baz": Array [ - "bar", - ], + "bar": Array [], + "baz": Array [], "foo": Array [], "kibana": Array [ "foo", ], - "quux": Array [ - "bar", - "baz", - ], + "quux": Array [], "with-additional-projects": Array [], }, "projects": Array [ diff --git a/packages/kbn-pm/src/utils/__fixtures__/kibana/packages/bar/package.json b/packages/kbn-pm/src/utils/__fixtures__/kibana/packages/bar/package.json index b5eae58393860..06a8b8dcc6aa8 100644 --- a/packages/kbn-pm/src/utils/__fixtures__/kibana/packages/bar/package.json +++ b/packages/kbn-pm/src/utils/__fixtures__/kibana/packages/bar/package.json @@ -1,7 +1,4 @@ { "name": "bar", - "version": "1.0.0", - "dependencies": { - "foo": "link:../foo" - } + "version": "1.0.0" } diff --git a/packages/kbn-pm/src/utils/__fixtures__/plugins/quux/package.json b/packages/kbn-pm/src/utils/__fixtures__/plugins/quux/package.json index b2794986c5b0b..f2a3062454509 100644 --- a/packages/kbn-pm/src/utils/__fixtures__/plugins/quux/package.json +++ b/packages/kbn-pm/src/utils/__fixtures__/plugins/quux/package.json @@ -1,8 +1,4 @@ { "name": "quux", - "version": "1.0.0", - "dependencies": { - "bar": "link:../../kibana/packages/bar", - "baz": "link:../baz" - } + "version": "1.0.0" } diff --git a/packages/kbn-pm/src/utils/__fixtures__/plugins/zorge/package.json b/packages/kbn-pm/src/utils/__fixtures__/plugins/zorge/package.json index 80a27b17661dd..3f22a1845b66a 100644 --- a/packages/kbn-pm/src/utils/__fixtures__/plugins/zorge/package.json +++ b/packages/kbn-pm/src/utils/__fixtures__/plugins/zorge/package.json @@ -1,7 +1,4 @@ { "name": "zorge", - "version": "1.0.0", - "dependencies": { - "foo": "link:../../kibana/packages/foo" - } + "version": "1.0.0" } diff --git a/packages/kbn-pm/src/utils/__snapshots__/project.test.ts.snap b/packages/kbn-pm/src/utils/__snapshots__/project.test.ts.snap index b3bcc402db2a3..d4a4e5ca23452 100644 --- a/packages/kbn-pm/src/utils/__snapshots__/project.test.ts.snap +++ b/packages/kbn-pm/src/utils/__snapshots__/project.test.ts.snap @@ -1,7 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`#ensureValidProjectDependency using link:, but with wrong path 1`] = `"[kibana] depends on [foo] using 'link:', but the path is wrong. Update its package.json to the expected value below."`; - -exports[`#ensureValidProjectDependency using version instead of link: 1`] = `"[kibana] depends on [foo] but it's not using the local package. Update its package.json to the expected value below."`; - exports[`#getExecutables() throws CliError when bin is something strange 1`] = `"[kibana] has an invalid \\"bin\\" field in its package.json, expected an object or a string"`; diff --git a/packages/kbn-pm/src/utils/__snapshots__/projects.test.ts.snap b/packages/kbn-pm/src/utils/__snapshots__/projects.test.ts.snap index 86ba136c50aa1..a716b9fab4e5b 100644 --- a/packages/kbn-pm/src/utils/__snapshots__/projects.test.ts.snap +++ b/packages/kbn-pm/src/utils/__snapshots__/projects.test.ts.snap @@ -2,37 +2,28 @@ exports[`#buildProjectGraph builds full project graph 1`] = ` Object { - "bar": Array [ - "foo", - ], + "bar": Array [], "baz": Array [], "foo": Array [], "kibana": Array [ "foo", ], - "quux": Array [ - "bar", - "baz", - ], - "zorge": Array [ - "foo", - ], + "quux": Array [], + "zorge": Array [], } `; exports[`#topologicallyBatchProjects batches projects topologically based on their project dependencies 1`] = ` Array [ Array [ + "bar", "foo", "baz", - ], - Array [ - "kibana", - "bar", + "quux", "zorge", ], Array [ - "quux", + "kibana", ], ] `; @@ -43,10 +34,8 @@ Array [ "kibana", "bar", "baz", - "zorge", - ], - Array [ "quux", + "zorge", ], ] `; diff --git a/packages/kbn-pm/src/utils/project.test.ts b/packages/kbn-pm/src/utils/project.test.ts index 9be5953880283..389dbf123cd52 100644 --- a/packages/kbn-pm/src/utils/project.test.ts +++ b/packages/kbn-pm/src/utils/project.test.ts @@ -50,65 +50,6 @@ test('fields', async () => { expect(kibana.hasScript('build')).toBe(false); }); -describe('#ensureValidProjectDependency', () => { - test('valid link: version', async () => { - const root = createProjectWith({ - dependencies: { - foo: 'link:packages/foo', - }, - }); - - const foo = createProjectWith( - { - name: 'foo', - }, - 'packages/foo' - ); - - expect(() => root.ensureValidProjectDependency(foo)).not.toThrow(); - }); - - test('using link:, but with wrong path', () => { - const root = createProjectWith( - { - dependencies: { - foo: 'link:wrong/path', - }, - }, - rootPath - ); - - const foo = createProjectWith( - { - name: 'foo', - }, - 'packages/foo' - ); - - expect(() => root.ensureValidProjectDependency(foo)).toThrowErrorMatchingSnapshot(); - }); - - test('using version instead of link:', () => { - const root = createProjectWith( - { - dependencies: { - foo: '1.0.0', - }, - }, - rootPath - ); - - const foo = createProjectWith( - { - name: 'foo', - }, - 'packages/foo' - ); - - expect(() => root.ensureValidProjectDependency(foo)).toThrowErrorMatchingSnapshot(); - }); -}); - describe('#getExecutables()', () => { test('converts bin:string to an object with absolute paths', () => { const project = createProjectWith({ diff --git a/packages/kbn-pm/src/utils/project.ts b/packages/kbn-pm/src/utils/project.ts index 48c606c10da42..842f828543116 100644 --- a/packages/kbn-pm/src/utils/project.ts +++ b/packages/kbn-pm/src/utils/project.ts @@ -88,48 +88,6 @@ export class Project { return this.json.name; } - public ensureValidProjectDependency(project: Project) { - const relativePathToProject = normalizePath(Path.relative(this.path, project.path)); - const relativePathToProjectIfBazelPkg = normalizePath( - Path.relative( - this.path, - `${__dirname}/../../../bazel-bin/packages/${Path.basename(project.path)}` - ) - ); - - const versionInPackageJson = this.allDependencies[project.name]; - const expectedVersionInPackageJson = `link:${relativePathToProject}`; - const expectedVersionInPackageJsonIfBazelPkg = `link:${relativePathToProjectIfBazelPkg}`; - - // TODO: after introduce bazel to build all the packages and completely remove the support for kbn packages - // do not allow child projects to hold dependencies, unless they are meant to be published externally - if ( - versionInPackageJson === expectedVersionInPackageJson || - versionInPackageJson === expectedVersionInPackageJsonIfBazelPkg - ) { - return; - } - - const updateMsg = 'Update its package.json to the expected value below.'; - const meta = { - actual: `"${project.name}": "${versionInPackageJson}"`, - expected: `"${project.name}": "${expectedVersionInPackageJson}" or "${project.name}": "${expectedVersionInPackageJsonIfBazelPkg}"`, - package: `${this.name} (${this.packageJsonLocation})`, - }; - - if (isLinkDependency(versionInPackageJson)) { - throw new CliError( - `[${this.name}] depends on [${project.name}] using 'link:', but the path is wrong. ${updateMsg}`, - meta - ); - } - - throw new CliError( - `[${this.name}] depends on [${project.name}] but it's not using the local package. ${updateMsg}`, - meta - ); - } - public getBuildConfig(): BuildConfig { return (this.json.kibana && this.json.kibana.build) || {}; } @@ -206,8 +164,3 @@ export class Project { return Object.values(this.allDependencies).every((dep) => isLinkDependency(dep)); } } - -// We normalize all path separators to `/` in generated files -function normalizePath(path: string) { - return path.replace(/[\\\/]+/g, '/'); -} diff --git a/packages/kbn-pm/src/utils/projects.test.ts b/packages/kbn-pm/src/utils/projects.test.ts index bf7bb052b254a..c87876642cf0b 100644 --- a/packages/kbn-pm/src/utils/projects.test.ts +++ b/packages/kbn-pm/src/utils/projects.test.ts @@ -249,6 +249,6 @@ describe('#includeTransitiveProjects', () => { const quux = projects.get('quux')!; const withTransitive = includeTransitiveProjects([quux], projects); - expect([...withTransitive.keys()]).toEqual(['quux', 'bar', 'baz', 'foo']); + expect([...withTransitive.keys()]).toEqual(['quux']); }); }); diff --git a/packages/kbn-pm/src/utils/projects.ts b/packages/kbn-pm/src/utils/projects.ts index 28a1fcfec8c36..e30dfc9f4c876 100644 --- a/packages/kbn-pm/src/utils/projects.ts +++ b/packages/kbn-pm/src/utils/projects.ts @@ -11,6 +11,7 @@ import path from 'path'; import { promisify } from 'util'; import { CliError } from './errors'; +import { log } from './log'; import { Project } from './project'; const glob = promisify(globSync); @@ -115,14 +116,23 @@ export function buildProjectGraph(projects: ProjectMap) { const projectGraph: ProjectGraph = new Map(); for (const project of projects.values()) { - const projectDeps = []; + const projectDeps: Project[] = []; const dependencies = project.allDependencies; + if (!project.isSinglePackageJsonProject && Object.keys(dependencies).length > 0) { + log.warning( + `${project.name} is not allowed to hold local dependencies and they will be discarded. Please declare them at the root package.json` + ); + } + + if (!project.isSinglePackageJsonProject) { + projectGraph.set(project.name, projectDeps); + continue; + } + for (const depName of Object.keys(dependencies)) { if (projects.has(depName)) { const dep = projects.get(depName)!; - project.ensureValidProjectDependency(dep); - projectDeps.push(dep); } } diff --git a/packages/kbn-rule-data-utils/BUILD.bazel b/packages/kbn-rule-data-utils/BUILD.bazel index 6477b558db9cb..d4dc6577c02b4 100644 --- a/packages/kbn-rule-data-utils/BUILD.bazel +++ b/packages/kbn-rule-data-utils/BUILD.bazel @@ -44,6 +44,13 @@ jsts_transpiler( build_pkg_name = package_name(), ) +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + ts_config( name = "tsconfig", src = "tsconfig.json", @@ -69,7 +76,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node"], + deps = RUNTIME_DEPS + [":target_node", ":target_web"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-rule-data-utils/package.json b/packages/kbn-rule-data-utils/package.json index 9372d7e70a8d1..d11f65e294a48 100644 --- a/packages/kbn-rule-data-utils/package.json +++ b/packages/kbn-rule-data-utils/package.json @@ -1,5 +1,6 @@ { "name": "@kbn/rule-data-utils", + "browser": "./target_web/index.js", "main": "./target_node/index.js", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", diff --git a/packages/kbn-securitysolution-rules/BUILD.bazel b/packages/kbn-securitysolution-rules/BUILD.bazel index 80a27a426fbb2..31b8fa8679312 100644 --- a/packages/kbn-securitysolution-rules/BUILD.bazel +++ b/packages/kbn-securitysolution-rules/BUILD.bazel @@ -47,6 +47,13 @@ jsts_transpiler( build_pkg_name = package_name(), ) +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + ts_config( name = "tsconfig", src = "tsconfig.json", @@ -71,7 +78,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node"], + deps = RUNTIME_DEPS + [":target_node", ":target_web"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-rules/package.json b/packages/kbn-securitysolution-rules/package.json index 4962576450f59..da061b244e7a0 100644 --- a/packages/kbn-securitysolution-rules/package.json +++ b/packages/kbn-securitysolution-rules/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "description": "security solution rule utilities to use across plugins", "license": "SSPL-1.0 OR Elastic License 2.0", + "browser": "./target_web/index.js", "main": "./target_node/index.js", "private": true } diff --git a/packages/kbn-securitysolution-utils/BUILD.bazel b/packages/kbn-securitysolution-utils/BUILD.bazel index 70ecc2712d4af..1842e5d1a523f 100644 --- a/packages/kbn-securitysolution-utils/BUILD.bazel +++ b/packages/kbn-securitysolution-utils/BUILD.bazel @@ -47,6 +47,13 @@ jsts_transpiler( build_pkg_name = package_name(), ) +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) + ts_config( name = "tsconfig", src = "tsconfig.json", @@ -71,7 +78,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node"], + deps = RUNTIME_DEPS + [":target_node", ":target_web"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-utils/package.json b/packages/kbn-securitysolution-utils/package.json index 8f347972f8316..e43d2570f730f 100644 --- a/packages/kbn-securitysolution-utils/package.json +++ b/packages/kbn-securitysolution-utils/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "description": "security solution utilities to use across plugins such lists, security_solution, cases, etc...", "license": "SSPL-1.0 OR Elastic License 2.0", + "browser": "./target_web/index.js", "main": "./target_node/index.js", "private": true } diff --git a/packages/kbn-server-route-repository/BUILD.bazel b/packages/kbn-server-route-repository/BUILD.bazel index 06c09260e2fa6..b635362ae1521 100644 --- a/packages/kbn-server-route-repository/BUILD.bazel +++ b/packages/kbn-server-route-repository/BUILD.bazel @@ -52,6 +52,17 @@ jsts_transpiler( build_pkg_name = package_name(), ) +jsts_transpiler( + name = "target_web", + srcs = [ + "src/web_index.ts", + "src/format_request.ts", + "src/parse_endpoint.ts", + ], + build_pkg_name = package_name(), + web = True, +) + ts_config( name = "tsconfig", src = "tsconfig.json", @@ -76,7 +87,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = RUNTIME_DEPS + [":target_node"], + deps = RUNTIME_DEPS + [":target_node", ":target_web"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-server-route-repository/README.md b/packages/kbn-server-route-repository/README.md index e22205540ef31..13d7972028cb7 100644 --- a/packages/kbn-server-route-repository/README.md +++ b/packages/kbn-server-route-repository/README.md @@ -5,3 +5,11 @@ Utility functions for creating a typed server route repository, and a typed clie ## Usage TBD + +## Server vs. Browser entry points + +This package exposes utils that can be used on both: the server and the browser. +However, importing the package might bring in server-only code, affecting the bundle size. +To avoid this, the package exposes 2 entry points: [`index.js`](./src/index.ts) and [`web_index.js`](./src/web_index.ts). + +When adding utilities to this package, please make sure to update the entry points accordingly and the [BUILD.bazel](./BUILD.bazel)'s `target_web` target build to include all the necessary files. diff --git a/packages/kbn-server-route-repository/package.json b/packages/kbn-server-route-repository/package.json index 32e59c896db00..1491f24c54dc1 100644 --- a/packages/kbn-server-route-repository/package.json +++ b/packages/kbn-server-route-repository/package.json @@ -1,5 +1,6 @@ { "name": "@kbn/server-route-repository", + "browser": "./target_web/web_index.js", "main": "./target_node/index.js", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", diff --git a/packages/kbn-server-route-repository/src/web_index.ts b/packages/kbn-server-route-repository/src/web_index.ts new file mode 100644 index 0000000000000..3ceeed55236bd --- /dev/null +++ b/packages/kbn-server-route-repository/src/web_index.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. + */ + +export { formatRequest } from './format_request'; +export { parseEndpoint } from './parse_endpoint'; +export type { + RouteRepositoryClient, + ReturnOf, + EndpointOf, + ClientRequestParamsOf, + DecodedRequestParamsOf, + ServerRouteRepository, + ServerRoute, + RouteParamsRT, + RouteState, +} from './typings'; diff --git a/packages/kbn-shared-ux-components/BUILD.bazel b/packages/kbn-shared-ux-components/BUILD.bazel index 8eca4da014493..b1420f5376041 100644 --- a/packages/kbn-shared-ux-components/BUILD.bazel +++ b/packages/kbn-shared-ux-components/BUILD.bazel @@ -40,8 +40,10 @@ NPM_MODULE_EXTRA_FILES = [ # "@npm//name-of-package" # eg. "@npm//lodash" RUNTIME_DEPS = [ - "//packages/kbn-i18n", "//packages/kbn-i18n-react", + "//packages/kbn-i18n", + "//packages/shared-ux/avatar/solution", + "//packages/shared-ux/link/redirect_app", "//packages/kbn-shared-ux-services", "//packages/kbn-shared-ux-storybook", "//packages/kbn-shared-ux-utility", @@ -51,6 +53,7 @@ RUNTIME_DEPS = [ "@npm//classnames", "@npm//react-use", "@npm//react", + "@npm//rxjs", "@npm//url-loader", ] @@ -64,12 +67,14 @@ RUNTIME_DEPS = [ # # References to NPM packages work the same as RUNTIME_DEPS TYPES_DEPS = [ - "//packages/kbn-i18n:npm_module_types", + "//packages/kbn-ambient-ui-types", "//packages/kbn-i18n-react:npm_module_types", + "//packages/kbn-i18n:npm_module_types", + "//packages/shared-ux/avatar/solution:npm_module_types", + "//packages/shared-ux/link/redirect_app:npm_module_types", "//packages/kbn-shared-ux-services:npm_module_types", "//packages/kbn-shared-ux-storybook:npm_module_types", "//packages/kbn-shared-ux-utility:npm_module_types", - "//packages/kbn-ambient-ui-types", "@npm//@types/node", "@npm//@types/jest", "@npm//@types/react", @@ -78,6 +83,7 @@ TYPES_DEPS = [ "@npm//@emotion/css", "@npm//@elastic/eui", "@npm//react-use", + "@npm//rxjs", ] jsts_transpiler( diff --git a/packages/kbn-shared-ux-components/src/empty_state/index.ts b/packages/kbn-shared-ux-components/src/empty_state/index.ts new file mode 100644 index 0000000000000..68defa5269344 --- /dev/null +++ b/packages/kbn-shared-ux-components/src/empty_state/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 { NoDataViews, NoDataViewsComponent } from './no_data_views'; +export { KibanaNoDataPage } from './kibana_no_data_page'; diff --git a/packages/kbn-shared-ux-components/src/empty_state/index.tsx b/packages/kbn-shared-ux-components/src/empty_state/index.tsx deleted file mode 100644 index 902a9cd3614d5..0000000000000 --- a/packages/kbn-shared-ux-components/src/empty_state/index.tsx +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { NoDataViews, NoDataViewsComponent } from './no_data_views'; diff --git a/packages/kbn-shared-ux-components/src/empty_state/kibana_no_data_page.mdx b/packages/kbn-shared-ux-components/src/empty_state/kibana_no_data_page.mdx new file mode 100644 index 0000000000000..c3571d1dd9d82 --- /dev/null +++ b/packages/kbn-shared-ux-components/src/empty_state/kibana_no_data_page.mdx @@ -0,0 +1,23 @@ +--- +id: sharedUX/Components/KibanaNoDataPage +slug: /shared-ux-components/kibana_no_data_page +title: Kibana No Data Page +summary: A page to be displayed when there is no data in Elasticsearch, or no data views +tags: ['shared-ux', 'component'] +date: 2022-04-20 +--- + +## Description + +Many plugins display "no data" page, either when there is no data in Elasticsearch, or there haven't been any data views created yet. This component is meant +to be used in those scenarios. It displays an appropriate message to the user and facilitate addition of integrations and creation of data views. + +## Component: `KibanaNoDataPage` + +- uses `hasUserDataView` and `hasData` API from `HasData` service in `data_views` plugin to check for existence of data / data views +- uses `onDataViewCreated` callback to be called once the data view has been created +- receives (noDataConfig)[https://github.com/elastic/kibana/blob/main/packages/kbn-shared-ux-components/src/page_template/no_data_page/types.ts] as configuration for the page in case of no data +- needs to be wrapped in `ServicesContext` provided by the start contract of the `shared_ux` plugin to be used + +## EUI Promotion Status +This component is not currently considered for promotion to EUI. \ No newline at end of file diff --git a/packages/kbn-shared-ux-components/src/empty_state/kibana_no_data_page.stories.tsx b/packages/kbn-shared-ux-components/src/empty_state/kibana_no_data_page.stories.tsx new file mode 100644 index 0000000000000..6d0c240dd4c05 --- /dev/null +++ b/packages/kbn-shared-ux-components/src/empty_state/kibana_no_data_page.stories.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 and the Server Side Public License, v 1; you may 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 { action } from '@storybook/addon-actions'; +import React from 'react'; +import { servicesFactory, DataServiceFactoryConfig } from '@kbn/shared-ux-storybook'; +import { SharedUxServicesProvider } from '@kbn/shared-ux-services'; +import mdx from './kibana_no_data_page.mdx'; +import { NoDataPageProps } from '../page_template'; +import { KibanaNoDataPage } from './kibana_no_data_page'; + +export default { + title: 'No Data/Kibana No Data Page', + description: 'A component to display when there is no data available', + parameters: { + docs: { + page: mdx, + }, + }, +}; + +const noDataConfig = { + solution: 'Analytics', + logo: 'logoKibana', + action: { + elasticAgent: { + title: 'Add Integrations', + }, + }, + docsLink: 'http://www.docs.com', +}; + +type Params = Pick & DataServiceFactoryConfig; + +export const PureComponent = (params: Params) => { + const { solution, logo, hasESData, hasUserDataView } = params; + const serviceParams = { hasESData, hasUserDataView, hasDataViews: false }; + const services = servicesFactory(serviceParams); + return ( + + + + ); +}; + +PureComponent.argTypes = { + solution: { + control: 'text', + defaultValue: 'Observability', + }, + logo: { + control: { type: 'radio' }, + options: ['logoElastic', 'logoKibana', 'logoCloud', undefined], + defaultValue: undefined, + }, + hasESData: { + control: 'boolean', + defaultValue: false, + }, + hasUserDataView: { + control: 'boolean', + defaultValue: false, + }, +}; diff --git a/packages/kbn-shared-ux-components/src/empty_state/kibana_no_data_page.test.tsx b/packages/kbn-shared-ux-components/src/empty_state/kibana_no_data_page.test.tsx new file mode 100644 index 0000000000000..82fbd222b3640 --- /dev/null +++ b/packages/kbn-shared-ux-components/src/empty_state/kibana_no_data_page.test.tsx @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { act } from 'react-dom/test-utils'; + +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { SharedUxServicesProvider, mockServicesFactory } from '@kbn/shared-ux-services'; + +import { KibanaNoDataPage } from './kibana_no_data_page'; +import { NoDataConfigPage } from '../page_template'; +import { NoDataViews } from './no_data_views'; + +describe('Kibana No Data Page', () => { + const noDataConfig = { + solution: 'Analytics', + pageTitle: 'Analytics', + logo: 'logoKibana', + action: { + elasticAgent: { + title: 'Add Integrations', + }, + }, + docsLink: 'http://www.docs.com', + }; + const onDataViewCreated = jest.fn(); + const config = { + hasESData: false, + hasDataView: false, + hasUserDataView: false, + }; + + afterEach(() => { + jest.resetAllMocks(); + }); + + test('renders NoDataConfigPage', async () => { + const services = mockServicesFactory({ config: { ...config, hasESData: false } }); + const component = mountWithIntl( + + + + ); + + await act(() => new Promise(setImmediate)); + component.update(); + + expect(component.find(NoDataConfigPage).length).toBe(1); + expect(component.find(NoDataViews).length).toBe(0); + }); + + test('renders NoDataViews', async () => { + const services = mockServicesFactory({ config: { ...config, hasESData: true } }); + const component = mountWithIntl( + + + + ); + + await act(() => new Promise(setImmediate)); + component.update(); + + expect(component.find(NoDataViews).length).toBe(1); + expect(component.find(NoDataConfigPage).length).toBe(0); + }); +}); diff --git a/packages/kbn-shared-ux-components/src/empty_state/kibana_no_data_page.tsx b/packages/kbn-shared-ux-components/src/empty_state/kibana_no_data_page.tsx new file mode 100644 index 0000000000000..2e54d0d9f6a67 --- /dev/null +++ b/packages/kbn-shared-ux-components/src/empty_state/kibana_no_data_page.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React, { useEffect, useState } from 'react'; +import { useData } from '@kbn/shared-ux-services'; +import { NoDataConfigPage, NoDataPageProps } from '../page_template'; +import { NoDataViews } from './no_data_views'; + +export interface Props { + onDataViewCreated: (dataView: unknown) => void; + noDataConfig: NoDataPageProps; +} + +export const KibanaNoDataPage = ({ onDataViewCreated, noDataConfig }: Props) => { + const { hasESData, hasUserDataView } = useData(); + const [dataExists, setDataExists] = useState(false); + const [hasUserDataViews, setHasUserDataViews] = useState(false); + + useEffect(() => { + const checkData = async () => { + setDataExists(await hasESData()); + setHasUserDataViews(await hasUserDataView()); + }; + // TODO: add error handling + // https://github.com/elastic/kibana/issues/130913 + checkData().catch(() => {}); + }, [hasESData, hasUserDataView]); + + if (!dataExists) { + return ; + } + + if (!hasUserDataViews) { + return ; + } + + return null; +}; diff --git a/packages/kbn-shared-ux-components/src/empty_state/no_data_views/no_data_views.stories.tsx b/packages/kbn-shared-ux-components/src/empty_state/no_data_views/no_data_views.stories.tsx index f40a04d8d4d2b..bee7c87d2841b 100644 --- a/packages/kbn-shared-ux-components/src/empty_state/no_data_views/no_data_views.stories.tsx +++ b/packages/kbn-shared-ux-components/src/empty_state/no_data_views/no_data_views.stories.tsx @@ -18,7 +18,7 @@ import mdx from './no_data_views.mdx'; const services = servicesFactory({}); export default { - title: 'No Data Views', + title: 'No Data/No Data Views', description: 'A component to display when there are no user-created data views available.', parameters: { docs: { diff --git a/packages/kbn-shared-ux-components/src/index.ts b/packages/kbn-shared-ux-components/src/index.ts index 98bd3ad1ac7e9..77586e8592b6a 100644 --- a/packages/kbn-shared-ux-components/src/index.ts +++ b/packages/kbn-shared-ux-components/src/index.ts @@ -15,8 +15,6 @@ export const LazyToolbarButton = React.lazy(() => })) ); -export const RedirectAppLinks = React.lazy(() => import('./redirect_app_links')); - /** * A `ToolbarButton` component that is wrapped by the `withSuspense` HOC. This component can * be used directly by consumers and will load the `LazyToolbarButton` component lazily with @@ -30,53 +28,36 @@ export const ToolbarButton = withSuspense(LazyToolbarButton); export { AddFromLibraryButton, ToolbarPopover } from './toolbar'; /** - * The Lazily-loaded `NoDataViews` component. Consumers should use `React.Suspense` or the + * The Lazily-loaded `IconButtonGroup` component. Consumers should use `React.Suspense` or the * `withSuspense` HOC to load this component. */ -export const LazyNoDataViews = React.lazy(() => - import('./empty_state/no_data_views').then(({ NoDataViews }) => ({ - default: NoDataViews, +export const LazyIconButtonGroup = React.lazy(() => + import('./toolbar').then(({ IconButtonGroup }) => ({ + default: IconButtonGroup, })) ); /** - * A `NoDataViews` component that is wrapped by the `withSuspense` HOC. This component can - * be used directly by consumers and will load the `LazyNoDataViews` component lazily with - * a predefined fallback and error boundary. + * The IconButtonGroup component that is wrapped by the `withSuspence` HOC. */ -export const NoDataViews = withSuspense(LazyNoDataViews); +export const IconButtonGroup = withSuspense(LazyIconButtonGroup); /** - * A pure `NoDataViews` component, with no services hooks. Consumers should use `React.Suspense` or the + * A `KibanaNoDataPage` component, with service hooks. Consumers should use `React.Suspennse` or the * `withSuspense` HOC to load this component. */ -export const LazyNoDataViewsComponent = React.lazy(() => - import('./empty_state/no_data_views').then(({ NoDataViewsComponent }) => ({ - default: NoDataViewsComponent, +export const KibanaNoDataPageLazy = React.lazy(() => + import('./empty_state').then(({ KibanaNoDataPage }) => ({ + default: KibanaNoDataPage, })) ); /** - * A pure `NoDataViews` component, with no services hooks. The component is wrapped by the `withSuspense` HOC. - * This component can be used directly by consumers and will load the `LazyNoDataViewsComponent` lazily with + * A `KibanaNoDataPage` component. The component is wrapped by the `withSuspense` HOC. + * This component can be used directly by consumers and will load the `KibanaNoDataPageLazy` lazily with * a predefined fallback and error boundary. */ -export const NoDataViewsComponent = withSuspense(LazyNoDataViewsComponent); - -/** - * The Lazily-loaded `IconButtonGroup` component. Consumers should use `React.Suspense` or the - * `withSuspense` HOC to load this component. - */ -export const LazyIconButtonGroup = React.lazy(() => - import('./toolbar').then(({ IconButtonGroup }) => ({ - default: IconButtonGroup, - })) -); - -/** - * The IconButtonGroup component that is wrapped by the `withSuspence` HOC. - */ -export const IconButtonGroup = withSuspense(LazyIconButtonGroup); +export const KibanaNoDataPage = withSuspense(KibanaNoDataPageLazy); /** * The lazily loaded `KibanaPageTemplate` component that is wrapped by the `withSuspense` HOC. Consumers should use @@ -95,6 +76,11 @@ export const KibanaPageTemplateLazy = React.lazy(() => */ export const KibanaPageTemplate = withSuspense(KibanaPageTemplateLazy); +/** + * A `KibanaPageTemplateProps` type. + */ +export type { KibanaPageTemplateProps } from './page_template'; + /** * The lazily loaded `KibanaPageTemplateSolutionNav` component that is wrapped by the `withSuspense` HOC. Consumers should use * `React.Suspense` or `withSuspense` HOC to load this component. @@ -113,18 +99,35 @@ export const KibanaPageTemplateSolutionNavLazy = React.lazy(() => export const KibanaPageTemplateSolutionNav = withSuspense(KibanaPageTemplateSolutionNavLazy); /** - * The Lazily-loaded `KibanaSolutionAvatar` component. Consumers should use `React.Suspense` or - * the withSuspense` HOC to load this component. + * The Lazily-loaded `NoDataViews` component. Consumers should use `React.Suspennse` or the + * `withSuspense` HOC to load this component. + */ +export const NoDataViewsLazy = React.lazy(() => + import('./empty_state/no_data_views').then(({ NoDataViews }) => ({ + default: NoDataViews, + })) +); + +/** + * A `NoDataViews` component that is wrapped by the `withSuspense` HOC. This component can + * be used directly by consumers and will load the `LazyNoDataViews` component lazily with + * a predefined fallback and error boundary. */ -export const KibanaSolutionAvatarLazy = React.lazy(() => - import('./solution_avatar').then(({ KibanaSolutionAvatar }) => ({ - default: KibanaSolutionAvatar, +export const NoDataViews = withSuspense(NoDataViewsLazy); + +/** + * A pure `NoDataViews` component, with no services hooks. Consumers should use `React.Suspennse` or the + * `withSuspense` HOC to load this component. + */ +export const NoDataViewsComponentLazy = React.lazy(() => + import('./empty_state/no_data_views').then(({ NoDataViewsComponent }) => ({ + default: NoDataViewsComponent, })) ); /** - * A `KibanaSolutionAvatar` component that is wrapped by the `withSuspense` HOC. This component can - * be used directly by consumers and will load the `KibanaPageTemplateSolutionNavAvatarLazy` component lazily with + * A pure `NoDataViews` component, with no services hooks. The component is wrapped by the `withSuspense` HOC. + * This component can be used directly by consumers and will load the `LazyNoDataViewsComponent` lazily with * a predefined fallback and error boundary. */ -export const KibanaSolutionAvatar = withSuspense(KibanaSolutionAvatarLazy); +export const NoDataViewsComponent = withSuspense(NoDataViewsComponentLazy); diff --git a/packages/kbn-shared-ux-components/src/page_template/index.ts b/packages/kbn-shared-ux-components/src/page_template/index.ts index caed703e5d656..671f720972fc9 100644 --- a/packages/kbn-shared-ux-components/src/page_template/index.ts +++ b/packages/kbn-shared-ux-components/src/page_template/index.ts @@ -5,8 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - -export { NoDataCard, ElasticAgentCard } from './no_data_page'; -export { NoDataPage } from './no_data_page'; +export { NoDataCard, ElasticAgentCard, NoDataPage, NoDataConfigPage } from './no_data_page'; export { KibanaPageTemplate } from './page_template'; export type { KibanaPageTemplateProps } from './types'; +export type { NoDataPageProps } from './no_data_page'; diff --git a/packages/kbn-shared-ux-components/src/page_template/no_data_page/__snapshots__/no_data_page.test.tsx.snap b/packages/kbn-shared-ux-components/src/page_template/no_data_page/__snapshots__/no_data_page.test.tsx.snap index 66b085b284391..0046e9c3fd3c1 100644 --- a/packages/kbn-shared-ux-components/src/page_template/no_data_page/__snapshots__/no_data_page.test.tsx.snap +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/__snapshots__/no_data_page.test.tsx.snap @@ -7,7 +7,7 @@ exports[`NoDataPage render 1`] = ` - - - + `; exports[`ElasticAgentCardComponent props href 1`] = ` - - - + `; exports[`ElasticAgentCardComponent renders 1`] = ` - - - + `; exports[`ElasticAgentCardComponent renders with canAccessFleet false 1`] = ` - + This integration is not yet enabled. Your administrator has the required permissions to turn it on. + + } + image="test-file-stub" + isDisabled={true} + title={ + + Contact your administrator + } - navigateToUrl={[MockFunction]} -> - - This integration is not yet enabled. Your administrator has the required permissions to turn it on. - - } - image="test-file-stub" - isDisabled={true} - title={ - - Contact your administrator - - } - /> - +/> `; diff --git a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap index cac139bbb9035..b15f254a5274a 100644 --- a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap @@ -4,10 +4,19 @@ exports[`ElasticAgentCard renders 1`] = ` - - -
- + - - Add Elastic Agent - - } - href="/app/integrations/browse" - image="test-file-stub" - paddingSize="l" - title="Add Elastic Agent" +
- - - - , - ], - }, + + + Add Elastic Agent + } - } - /> - -
-
-
- -
-
-
- - - - Add Elastic Agent - - - - + + , + ], + }, + } + } + isStringTag={false} + serialized={ + Object { + "map": undefined, + "name": "1hu4pg0-EuiCard", + "next": undefined, + "styles": "max-width:400px;margin-inline:auto;;label:EuiCard;", + "toString": [Function], + } + } + /> +
-

- Use Elastic Agent for a simple, unified way to collect data from your machines. -

-
-
-
-
- - -
+
+
+ - Add Elastic Agent - + - - - - -
-
- - - - -
- - + + +
+

+ Use Elastic Agent for a simple, unified way to collect data from your machines. +

+
+
+ +
+ + + + + +
+ + + + + + + + + + + `; diff --git a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.component.test.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.component.test.tsx index f25edb069c629..367fcd10b96a9 100644 --- a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.component.test.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.component.test.tsx @@ -10,31 +10,15 @@ import { shallow } from 'enzyme'; import React from 'react'; import { ElasticAgentCardComponent } from './elastic_agent_card.component'; import { NoDataCard } from './no_data_card'; -import { Subject } from 'rxjs'; describe('ElasticAgentCardComponent', () => { - const navigateToUrl = jest.fn(); - const currentAppId$ = new Subject().asObservable(); - test('renders', () => { - const component = shallow( - - ); + const component = shallow(); expect(component).toMatchSnapshot(); }); test('renders with canAccessFleet false', () => { - const component = shallow( - - ); + const component = shallow(); expect(component.find(NoDataCard).props().isDisabled).toBe(true); expect(component).toMatchSnapshot(); }); @@ -42,12 +26,7 @@ describe('ElasticAgentCardComponent', () => { describe('props', () => { test('button', () => { const component = shallow( - + ); expect(component.find(NoDataCard).props().button).toBe('Button'); expect(component).toMatchSnapshot(); @@ -55,12 +34,7 @@ describe('ElasticAgentCardComponent', () => { test('href', () => { const component = shallow( - + ); expect(component.find(NoDataCard).props().href).toBe('some path'); expect(component).toMatchSnapshot(); diff --git a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.component.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.component.tsx index 0bca3929f4c2d..7b046bbe3fe8c 100644 --- a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.component.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.component.tsx @@ -9,16 +9,12 @@ import React, { FunctionComponent } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiTextColor } from '@elastic/eui'; -import { Observable } from 'rxjs'; import { ElasticAgentCardProps } from './types'; import { NoDataCard } from './no_data_card'; import ElasticAgentCardIllustration from './assets/elastic_agent_card.svg'; -import { RedirectAppLinks } from '../../../redirect_app_links'; export type ElasticAgentCardComponentProps = ElasticAgentCardProps & { canAccessFleet: boolean; - navigateToUrl: (url: string) => Promise; - currentAppId$: Observable; }; const noPermissionTitle = i18n.translate( @@ -54,32 +50,19 @@ const elasticAgentCardDescription = i18n.translate( */ export const ElasticAgentCardComponent: FunctionComponent = ({ canAccessFleet, - title, - navigateToUrl, - currentAppId$, + title = elasticAgentCardTitle, ...cardRest }) => { - const noAccessCard = ( - {noPermissionTitle}} - description={{noPermissionDescription}} - isDisabled - {...cardRest} - /> - ); - const card = ( - - ); + const props = canAccessFleet + ? { + title, + description: elasticAgentCardDescription, + } + : { + title: {noPermissionTitle}, + description: {noPermissionDescription}, + isDisabled: true, + }; - return ( - - {canAccessFleet ? card : noAccessCard} - - ); + return ; }; diff --git a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.stories.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.stories.tsx index ea890c265b5cd..84cbfb1c73a94 100644 --- a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.stories.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.stories.tsx @@ -7,29 +7,23 @@ */ import React from 'react'; -import { applicationServiceFactory } from '@kbn/shared-ux-storybook'; import { - ElasticAgentCardComponent, - ElasticAgentCardComponentProps, + ElasticAgentCardComponent as Component, + ElasticAgentCardComponentProps as ComponentProps, } from './elastic_agent_card.component'; +import { ElasticAgentCard } from './elastic_agent_card'; + export default { - title: 'Page Template/No Data Page/Elastic Agent Data Card', + title: 'Page Template/No Data/Elastic Agent Data Card', description: 'A solution-specific wrapper around NoDataCard, to be used on NoData page', }; -type Params = Pick; +type Params = Pick; export const PureComponent = (params: Params) => { - const { currentAppId$, navigateToUrl } = applicationServiceFactory(); - return ( - - ); + return ; }; PureComponent.argTypes = { @@ -38,3 +32,7 @@ PureComponent.argTypes = { defaultValue: true, }, }; + +export const ConnectedComponent = () => { + return ; +}; diff --git a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.tsx index d917514a96ccc..3702dd4a456a7 100644 --- a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/elastic_agent_card.tsx @@ -6,8 +6,10 @@ * Side Public License, v 1. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { useApplication, useHttp, usePermissions } from '@kbn/shared-ux-services'; +import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; +import useObservable from 'react-use/lib/useObservable'; import { ElasticAgentCardProps } from './types'; import { ElasticAgentCardComponent } from './elastic_agent_card.component'; @@ -16,27 +18,28 @@ export const ElasticAgentCard = (props: ElasticAgentCardProps) => { const { canAccessFleet } = usePermissions(); const { addBasePath } = useHttp(); const { navigateToUrl, currentAppId$ } = useApplication(); + const currentAppId = useObservable(currentAppId$); - const createHref = () => { - const { href, category } = props; - if (href) { - return href; + const { href: srcHref, category } = props; + + const href = useMemo(() => { + if (srcHref) { + return srcHref; } + // TODO: get this URL from a locator const prefix = '/app/integrations/browse'; + if (category) { return addBasePath(`${prefix}/${category}`); } - return prefix; - }; + + return addBasePath(prefix); + }, [addBasePath, srcHref, category]); return ( - + + + ); }; diff --git a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/no_data_card.stories.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/no_data_card.stories.tsx index 4cea040af5f3c..9c1b2d0322074 100644 --- a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/no_data_card.stories.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_card/no_data_card.stories.tsx @@ -11,7 +11,7 @@ import { NoDataCard } from './no_data_card'; import type { NoDataCardProps } from './types'; export default { - title: 'Page Template/No Data Page/No Data Card', + title: 'Page Template/No Data/No Data Card', description: 'A wrapper around EuiCard, to be used on NoData page', }; diff --git a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_config_page/index.ts b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_config_page/index.ts new file mode 100644 index 0000000000000..f8c272c8f9875 --- /dev/null +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_config_page/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 and the Server Side Public License, v 1; you may 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 { NoDataConfigPage, NoDataConfigPageWithSolutionNavBar } from './no_data_config_page'; diff --git a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_config_page/index.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_config_page/index.tsx deleted file mode 100644 index 0bdde40021398..0000000000000 --- a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_config_page/index.tsx +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { NoDataConfigPage, NoDataConfigPageWithSolutionNavBar } from './no_data_config_page'; diff --git a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_page.stories.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_page.stories.tsx deleted file mode 100644 index cbb8ef6b0446f..0000000000000 --- a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_page.stories.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { servicesFactory } from '@kbn/shared-ux-storybook'; -import { NoDataPageProps } from './types'; -import { NoDataPage } from './no_data_page'; - -const services = servicesFactory({}); - -export default { - title: 'Page Template/No Data Page/No Data Page', - description: 'No Data Page of PageTemplate', -}; -const action = { - elasticAgent: {}, -}; -type Params = Pick; - -export const PureComponent = (params: Params) => { - return ; -}; - -PureComponent.argTypes = { - solution: { - control: 'text', - defaultValue: 'Observability', - }, - logo: { - control: { type: 'radio' }, - options: ['logoElastic', 'logoKibana', 'logoCloud', undefined], - defaultValue: undefined, - }, -}; diff --git a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_page.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_page.tsx index f16f87039a626..837eb5282507f 100644 --- a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_page.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_page.tsx @@ -7,14 +7,15 @@ */ import React, { useMemo, FunctionComponent } from 'react'; -import { i18n } from '@kbn/i18n'; +import classNames from 'classnames'; import { EuiLink, EuiSpacer, EuiText, EuiTextColor } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import classNames from 'classnames'; +import { KibanaSolutionAvatar } from '@kbn/shared-ux-avatar-solution'; + import { ElasticAgentCard } from './no_data_card'; import { NoDataPageProps } from './types'; -import { KibanaSolutionAvatar } from '../../solution_avatar'; export const NoDataPage: FunctionComponent = ({ solution, diff --git a/packages/kbn-shared-ux-components/src/page_template/solution_nav/__snapshots__/solution_nav.test.tsx.snap b/packages/kbn-shared-ux-components/src/page_template/solution_nav/__snapshots__/solution_nav.test.tsx.snap index fce0e996d99cd..069192708e47b 100644 --- a/packages/kbn-shared-ux-components/src/page_template/solution_nav/__snapshots__/solution_nav.test.tsx.snap +++ b/packages/kbn-shared-ux-components/src/page_template/solution_nav/__snapshots__/solution_nav.test.tsx.snap @@ -178,7 +178,7 @@ exports[`KibanaPageTemplateSolutionNav renders with icon 1`] = ` className="kbnPageTemplateSolutionNav" heading={ - - & { diff --git a/packages/kbn-shared-ux-components/src/redirect_app_links/click_handler.ts b/packages/kbn-shared-ux-components/src/redirect_app_links/click_handler.ts deleted file mode 100644 index db2990726dc93..0000000000000 --- a/packages/kbn-shared-ux-components/src/redirect_app_links/click_handler.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { getClosestLink, hasActiveModifierKey } from '@kbn/shared-ux-utility'; - -interface CreateCrossAppClickHandlerOptions { - navigateToUrl(url: string): Promise; - container?: HTMLElement; -} - -export const createNavigateToUrlClickHandler = ({ - container, - navigateToUrl, -}: CreateCrossAppClickHandlerOptions): React.MouseEventHandler => { - return (e) => { - if (!container) { - return; - } - // see https://github.com/DefinitelyTyped/DefinitelyTyped/pull/12239 - const target = e.target as HTMLElement; - - const link = getClosestLink(target, container); - if (!link) { - return; - } - - const isNotEmptyHref = link.href; - const hasNoTarget = link.target === '' || link.target === '_self'; - const isLeftClickOnly = e.button === 0; - - if ( - isNotEmptyHref && - hasNoTarget && - isLeftClickOnly && - !e.defaultPrevented && - !hasActiveModifierKey(e) - ) { - e.preventDefault(); - navigateToUrl(link.href); - } - }; -}; diff --git a/packages/kbn-shared-ux-components/src/redirect_app_links/index.ts b/packages/kbn-shared-ux-components/src/redirect_app_links/index.ts deleted file mode 100644 index db7462d7cb1bf..0000000000000 --- a/packages/kbn-shared-ux-components/src/redirect_app_links/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ -/* eslint-disable import/no-default-export */ - -import { RedirectAppLinks } from './redirect_app_links'; -export type { RedirectAppLinksProps } from './redirect_app_links'; -export { RedirectAppLinks } from './redirect_app_links'; - -/** - * Exporting the RedirectAppLinks component as a default export so it can be - * loaded by React.lazy. - */ -export default RedirectAppLinks; diff --git a/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.mdx b/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.mdx deleted file mode 100644 index 0023182940ae9..0000000000000 --- a/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: sharedUX/Components/AppLink -slug: /shared-ux/components/redirect-app-link -title: Redirect App Link -summary: The component for redirect links. -tags: ['shared-ux', 'component'] -date: 2022-02-01 ---- - -> This documentation is in progress. - -**This component has been refactored.** Instead of requiring the entire `application`, it instead takes just `navigateToUrl` and `currentAppId$`. This makes the component more lightweight. diff --git a/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.stories.tsx b/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.stories.tsx deleted file mode 100644 index 0ca0e2a8d9978..0000000000000 --- a/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.stories.tsx +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { EuiButton } from '@elastic/eui'; -import React from 'react'; -import { BehaviorSubject } from 'rxjs'; - -import { action } from '@storybook/addon-actions'; -import { RedirectAppLinks } from './redirect_app_links'; -import mdx from './redirect_app_links.mdx'; - -export default { - title: 'Redirect App Links', - description: 'app links component that takes in an application id and navigation url.', - parameters: { - docs: { - page: mdx, - }, - }, -}; - -export const Component = () => { - return ( - Promise.resolve()} - currentAppId$={new BehaviorSubject('test')} - > - - Test link - - - ); -}; diff --git a/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.test.tsx b/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.test.tsx deleted file mode 100644 index d36bace70b7c8..0000000000000 --- a/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.test.tsx +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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, { MouseEvent } from 'react'; -import { mount } from 'enzyme'; -import { BehaviorSubject } from 'rxjs'; - -import { RedirectAppLinks } from './redirect_app_links'; - -export type UnmountCallback = () => void; -export type MountPoint = (element: T) => UnmountCallback; - -const createServiceMock = () => { - const currentAppId$ = new BehaviorSubject('currentApp'); - - return { - currentAppId$: currentAppId$.asObservable(), - navigateToApp: jest.fn(), - navigateToUrl: jest.fn(), - }; -}; - -/* eslint-disable jsx-a11y/click-events-have-key-events */ - -describe('RedirectAppLinks', () => { - let application = createServiceMock(); - - beforeEach(() => { - application = createServiceMock(); - }); - - it('intercept click events on children link elements', () => { - let event: MouseEvent; - const component = mount( -
{ - event = e; - }} - > - -
- content -
-
-
- ); - - component.find('a').simulate('click', { button: 0, defaultPrevented: false }); - expect(application.navigateToUrl).toHaveBeenCalledTimes(1); - expect(event!.defaultPrevented).toBe(true); - }); - - it('intercept click events on children inside link elements', async () => { - let event: MouseEvent; - - const component = mount( -
{ - event = e; - }} - > - - - -
- ); - - component.find('span').simulate('click', { button: 0, defaultPrevented: false }); - - expect(application.navigateToUrl).toHaveBeenCalledTimes(1); - expect(event!.defaultPrevented).toBe(true); - }); - - it('does not intercept click events when the target is not inside a link', () => { - let event: MouseEvent; - - const component = mount( -
{ - event = e; - }} - > - - - content - - -
- ); - - component.find('span').simulate('click', { button: 0, defaultPrevented: false }); - - expect(application.navigateToApp).not.toHaveBeenCalled(); - expect(event!.defaultPrevented).toBe(false); - }); - - it('does not intercept click events when the link is a parent of the container', () => { - let event: MouseEvent; - - const component = mount( -
{ - event = e; - }} - > - - - content - - -
- ); - - component.find('span').simulate('click', { button: 0, defaultPrevented: false }); - - expect(application.navigateToApp).not.toHaveBeenCalled(); - expect(event!.defaultPrevented).toBe(false); - }); - - it('does not intercept click events when the link has an external target', () => { - let event: MouseEvent; - - const component = mount( -
{ - event = e; - }} - > - - - content - - -
- ); - - component.find('a').simulate('click', { button: 0, defaultPrevented: false }); - - expect(application.navigateToApp).not.toHaveBeenCalled(); - expect(event!.defaultPrevented).toBe(false); - }); - - it('does not intercept click events when the event is already defaultPrevented', () => { - let event: MouseEvent; - - const component = mount( -
{ - event = e; - }} - > - - - e.preventDefault()}>content - - -
- ); - - component.find('span').simulate('click', { button: 0, defaultPrevented: false }); - - expect(application.navigateToApp).not.toHaveBeenCalled(); - expect(event!.defaultPrevented).toBe(true); - }); - - it('does not intercept click events when the event propagation is stopped', () => { - let event: MouseEvent; - - const component = mount( -
{ - event = e; - }} - > - - e.stopPropagation()}> - content - - -
- ); - - component.find('a').simulate('click', { button: 0, defaultPrevented: false }); - - expect(application.navigateToApp).not.toHaveBeenCalled(); - expect(event!).toBe(undefined); - }); - - it('does not intercept click events when the event is not triggered from the left button', () => { - let event: MouseEvent; - - const component = mount( -
{ - event = e; - }} - > - -
- content -
-
-
- ); - - component.find('a').simulate('click', { button: 1, defaultPrevented: false }); - - expect(application.navigateToApp).not.toHaveBeenCalled(); - expect(event!.defaultPrevented).toBe(false); - }); - - it('does not intercept click events when the event has a modifier key enabled', () => { - let event: MouseEvent; - - const component = mount( -
{ - event = e; - }} - > - -
- content -
-
-
- ); - - component.find('a').simulate('click', { button: 0, ctrlKey: true, defaultPrevented: false }); - - expect(application.navigateToApp).not.toHaveBeenCalled(); - expect(event!.defaultPrevented).toBe(false); - }); -}); diff --git a/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.tsx b/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.tsx deleted file mode 100644 index e1d0bd4bed653..0000000000000 --- a/packages/kbn-shared-ux-components/src/redirect_app_links/redirect_app_links.tsx +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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, { useRef, useMemo } from 'react'; -import type { HTMLAttributes, DetailedHTMLProps, FC } from 'react'; -import useObservable from 'react-use/lib/useObservable'; -import { Observable } from 'rxjs'; - -import { createNavigateToUrlClickHandler } from './click_handler'; - -type DivProps = DetailedHTMLProps, HTMLDivElement>; -/** - * TODO: this interface recreates props from the `ApplicationStart` interface. - * see: https://github.com/elastic/kibana/issues/127695 - */ -export interface RedirectAppLinksProps extends DivProps { - currentAppId$: Observable; - navigateToUrl(url: string): Promise; -} - -/** - * Utility component that will intercept click events on children anchor (``) elements to call - * `application.navigateToUrl` with the link's href. This will trigger SPA friendly navigation - * when the link points to a valid Kibana app. - * - * @example - * ```tsx - * url} currentAppId$={observableAppId}> - * Go to another-app - * - * ``` - * - * @remarks - * It is recommended to use the component at the highest possible level of the component tree that would - * require to handle the links. A good practice is to consider it as a context provider and to use it - * at the root level of an application or of the page that require the feature. - */ -export const RedirectAppLinks: FC = ({ - navigateToUrl, - currentAppId$, - children, - ...otherProps -}) => { - const currentAppId = useObservable(currentAppId$, undefined); - const containerRef = useRef(null); - const clickHandler = useMemo( - () => - containerRef.current && currentAppId - ? createNavigateToUrlClickHandler({ - container: containerRef.current, - navigateToUrl, - }) - : undefined, - [currentAppId, navigateToUrl] - ); - - return ( - // eslint-disable-next-line jsx-a11y/click-events-have-key-events -
- {children} -
- ); -}; diff --git a/packages/kbn-shared-ux-components/src/solution_avatar/__snapshots__/solution_avatar.test.tsx.snap b/packages/kbn-shared-ux-components/src/solution_avatar/__snapshots__/solution_avatar.test.tsx.snap deleted file mode 100644 index 9817d7cdd8d45..0000000000000 --- a/packages/kbn-shared-ux-components/src/solution_avatar/__snapshots__/solution_avatar.test.tsx.snap +++ /dev/null @@ -1,10 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`KibanaSolutionAvatar renders 1`] = ` - -`; diff --git a/packages/kbn-shared-ux-components/src/solution_avatar/index.tsx b/packages/kbn-shared-ux-components/src/solution_avatar/index.tsx deleted file mode 100644 index efc597cbdcb13..0000000000000 --- a/packages/kbn-shared-ux-components/src/solution_avatar/index.tsx +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { KibanaSolutionAvatar } from './solution_avatar'; -export type { KibanaSolutionAvatarProps } from './solution_avatar'; diff --git a/packages/kbn-shared-ux-components/src/solution_avatar/solution_avatar.stories.tsx b/packages/kbn-shared-ux-components/src/solution_avatar/solution_avatar.stories.tsx deleted file mode 100644 index bc26806016df0..0000000000000 --- a/packages/kbn-shared-ux-components/src/solution_avatar/solution_avatar.stories.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { KibanaSolutionAvatar, KibanaSolutionAvatarProps } from './solution_avatar'; - -export default { - title: 'Solution Avatar', - description: 'A wrapper around EuiAvatar, specifically to stylize Elastic Solutions', -}; - -type Params = Pick; - -export const PureComponent = (params: Params) => { - return ; -}; - -PureComponent.argTypes = { - name: { - control: 'text', - defaultValue: 'Kibana', - }, - size: { - control: 'radio', - options: ['s', 'm', 'l', 'xl', 'xxl'], - defaultValue: 'xxl', - }, -}; diff --git a/packages/kbn-shared-ux-components/src/solution_avatar/solution_avatar.test.tsx b/packages/kbn-shared-ux-components/src/solution_avatar/solution_avatar.test.tsx deleted file mode 100644 index 7a8b20c3f8d64..0000000000000 --- a/packages/kbn-shared-ux-components/src/solution_avatar/solution_avatar.test.tsx +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { shallow } from 'enzyme'; -import { KibanaSolutionAvatar } from './solution_avatar'; - -describe('KibanaSolutionAvatar', () => { - test('renders', () => { - const component = shallow(); - expect(component).toMatchSnapshot(); - }); -}); diff --git a/packages/kbn-shared-ux-components/src/solution_avatar/solution_avatar.tsx b/packages/kbn-shared-ux-components/src/solution_avatar/solution_avatar.tsx deleted file mode 100644 index deb71affc9c1a..0000000000000 --- a/packages/kbn-shared-ux-components/src/solution_avatar/solution_avatar.tsx +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 './solution_avatar.scss'; - -import React from 'react'; - -import { DistributiveOmit, EuiAvatar, EuiAvatarProps } from '@elastic/eui'; -import classNames from 'classnames'; - -export type KibanaSolutionAvatarProps = DistributiveOmit & { - /** - * Any EuiAvatar size available, or `xxl` for custom large, brand-focused version - */ - size?: EuiAvatarProps['size'] | 'xxl'; -}; - -/** - * Applies extra styling to a typical EuiAvatar. - * The `name` value will be appended to 'logo' to configure the `iconType` unless `iconType` is provided. - */ -export const KibanaSolutionAvatar = ({ className, size, ...rest }: KibanaSolutionAvatarProps) => { - return ( - // @ts-ignore Complains about ExclusiveUnion between `iconSize` and `iconType`, but works fine - - ); -}; diff --git a/packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/__snapshots__/icon_button_group.test.tsx.snap b/packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/__snapshots__/icon_button_group.test.tsx.snap index 60a49e521ab07..8091bd222d1a3 100644 --- a/packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/__snapshots__/icon_button_group.test.tsx.snap +++ b/packages/kbn-shared-ux-components/src/toolbar/buttons/icon_button_group/__snapshots__/icon_button_group.test.tsx.snap @@ -4,10 +4,19 @@ exports[` is rendered 1`] = ` is rendered 1`] = ` /packages/kbn-shared-ux-services'], -}; diff --git a/packages/kbn-shared-ux-services/src/context.tsx b/packages/kbn-shared-ux-services/src/context.tsx index 55d05c57a08fb..6131c23e27370 100644 --- a/packages/kbn-shared-ux-services/src/context.tsx +++ b/packages/kbn-shared-ux-services/src/context.tsx @@ -63,3 +63,8 @@ export const useDocLinks = () => useSharedUxServices().docLinks; export const useHttp = () => useSharedUxServices().http; export const useApplication = () => useSharedUxServices().application; + +/** + * React hook for accessing the pre-wired `SharedUxDataService`. + */ +export const useData = () => useSharedUxServices().data; diff --git a/packages/kbn-shared-ux-services/src/index.ts b/packages/kbn-shared-ux-services/src/index.ts index d9ea2c670c9c5..744718a63b42d 100755 --- a/packages/kbn-shared-ux-services/src/index.ts +++ b/packages/kbn-shared-ux-services/src/index.ts @@ -14,6 +14,7 @@ export type { SharedUxHttpService, SharedUxPlatformService, SharedUxUserPermissionsService, + SharedUxDataService, } from './services'; export { @@ -24,6 +25,7 @@ export { useHttp, usePermissions, usePlatformService, + useData, useSharedUxServices, } from './context'; diff --git a/packages/kbn-shared-ux-services/src/services/data.ts b/packages/kbn-shared-ux-services/src/services/data.ts new file mode 100644 index 0000000000000..2750c9bd32085 --- /dev/null +++ b/packages/kbn-shared-ux-services/src/services/data.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** + * A service providing data information. Typically used for handling of empty state.. + */ +export interface SharedUxDataService { + /** True if the cluster contains data, false otherwise. */ + hasESData: () => Promise; + /** True if Kibana instance contains user-created data view, false otherwise. */ + hasUserDataView: () => Promise; + /** True if Kibana instance contains any data view, including system-created ones. */ + hasDataView: () => Promise; +} diff --git a/packages/kbn-shared-ux-services/src/services/index.ts b/packages/kbn-shared-ux-services/src/services/index.ts index 485264353feb2..cf0e211ef492e 100644 --- a/packages/kbn-shared-ux-services/src/services/index.ts +++ b/packages/kbn-shared-ux-services/src/services/index.ts @@ -12,6 +12,7 @@ export type { SharedUxEditorsService } from './editors'; export type { SharedUxHttpService } from './http'; export type { SharedUxUserPermissionsService } from './permissions'; export type { SharedUxPlatformService } from './platform'; +export type { SharedUxDataService } from './data'; export { mockServicesFactory, mockServiceFactories } from './mock'; export { stubServicesFactory, stubServiceFactories } from './stub'; diff --git a/packages/kbn-shared-ux-services/src/services/mock/application.mock.ts b/packages/kbn-shared-ux-services/src/services/mock/application.mock.ts index abb68a1edb642..947113c4a3168 100644 --- a/packages/kbn-shared-ux-services/src/services/mock/application.mock.ts +++ b/packages/kbn-shared-ux-services/src/services/mock/application.mock.ts @@ -17,5 +17,7 @@ export type MockApplicationServiceFactory = ServiceFactory ({ navigateToUrl: () => Promise.resolve(), - currentAppId$: new Observable(), + currentAppId$: new Observable((subscriber) => { + subscriber.next('abc123'); + }), }); diff --git a/packages/kbn-shared-ux-services/src/services/mock/data.mock.ts b/packages/kbn-shared-ux-services/src/services/mock/data.mock.ts new file mode 100644 index 0000000000000..bb9d59643348d --- /dev/null +++ b/packages/kbn-shared-ux-services/src/services/mock/data.mock.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ServiceFactory } from '../../types'; +import { SharedUxDataService } from '../data'; + +/** + * A factory function for creating a Jest-based implementation of `SharedUxDataService`. + */ +export type MockDataServiceFactory = ServiceFactory; + +export interface MockDataServiceFactoryConfig { + hasESData: boolean; + hasDataView: boolean; + hasUserDataView: boolean; +} + +/** + * A factory function for creating a Jest-based implementation of `SharedUxDataService`. + */ +export const dataServiceFactory: (config?: MockDataServiceFactoryConfig) => SharedUxDataService = ( + config?: MockDataServiceFactoryConfig +) => ({ + hasESData: () => Promise.resolve(config?.hasESData || false), + hasDataView: () => Promise.resolve(config?.hasDataView || false), + hasUserDataView: () => Promise.resolve(config?.hasUserDataView || false), +}); diff --git a/packages/kbn-shared-ux-services/src/services/mock/index.ts b/packages/kbn-shared-ux-services/src/services/mock/index.ts index 63256345d1bba..604a8ae677b9d 100644 --- a/packages/kbn-shared-ux-services/src/services/mock/index.ts +++ b/packages/kbn-shared-ux-services/src/services/mock/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { SharedUxServices, ServiceFactory } from '../../types'; +import type { SharedUxServices } from '../../types'; import { applicationServiceFactory } from './application.mock'; import { docLinksServiceFactory } from './doc_links.mock'; @@ -14,6 +14,7 @@ import { editorsServiceFactory } from './editors.mock'; import { httpServiceFactory } from './http.mock'; import { userPermissionsServiceFactory } from './permissions.mock'; import { platformServiceFactory } from './platform.mock'; +import { dataServiceFactory, MockDataServiceFactoryConfig } from './data.mock'; export type { MockApplicationServiceFactory } from './application.mock'; export type { MockDocLinksServiceFactory } from './doc_links.mock'; @@ -28,17 +29,25 @@ export { editorsServiceFactory } from './editors.mock'; export { httpServiceFactory } from './http.mock'; export { userPermissionsServiceFactory } from './permissions.mock'; export { platformServiceFactory } from './platform.mock'; +export { dataServiceFactory } from './data.mock'; + +export interface MockServicesFactoryParams { + config: MockDataServiceFactoryConfig; +} /** * A factory function for creating a Jest-based implementation of `SharedUxServices`. */ -export const mockServicesFactory: ServiceFactory = () => ({ +export const mockServicesFactory: (params?: MockServicesFactoryParams) => SharedUxServices = ( + params?: MockServicesFactoryParams +) => ({ application: applicationServiceFactory(), docLinks: docLinksServiceFactory(), editors: editorsServiceFactory(), http: httpServiceFactory(), permissions: userPermissionsServiceFactory(), platform: platformServiceFactory(), + data: dataServiceFactory(params?.config), }); /** @@ -51,4 +60,5 @@ export const mockServiceFactories = { httpServiceFactory, platformServiceFactory, userPermissionsServiceFactory, + dataServiceFactory, }; diff --git a/packages/kbn-shared-ux-services/src/services/stub/data.ts b/packages/kbn-shared-ux-services/src/services/stub/data.ts new file mode 100644 index 0000000000000..833c64e1f9d8d --- /dev/null +++ b/packages/kbn-shared-ux-services/src/services/stub/data.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ServiceFactory } from '../../types'; +import { SharedUxDataService } from '../data'; + +/** + * A factory function for creating a simple stubbed implementation of `SharedUxDataSevice`. + */ +export type DataServiceFactory = ServiceFactory; + +/** + * A factory function for creating a simple stubbed implementation of `SharedUxDataSevice`. + */ +export const dataServiceFactory: DataServiceFactory = () => ({ + hasESData: () => Promise.resolve(true), + hasDataView: () => Promise.resolve(false), + hasUserDataView: () => Promise.resolve(false), +}); diff --git a/packages/kbn-shared-ux-services/src/services/stub/index.ts b/packages/kbn-shared-ux-services/src/services/stub/index.ts index 10323b7db8762..ab8b0ca3f4d9c 100644 --- a/packages/kbn-shared-ux-services/src/services/stub/index.ts +++ b/packages/kbn-shared-ux-services/src/services/stub/index.ts @@ -14,6 +14,7 @@ import { editorsServiceFactory } from './editors'; import { httpServiceFactory } from './http'; import { platformServiceFactory } from './platform'; import { userPermissionsServiceFactory } from './permissions'; +import { dataServiceFactory } from './data'; /** * A factory function for creating simple stubbed implementations of all `SharedUxServices`. @@ -25,6 +26,7 @@ export const stubServicesFactory: ServiceFactory = () => ({ http: httpServiceFactory(), permissions: userPermissionsServiceFactory(), platform: platformServiceFactory(), + data: dataServiceFactory(), }); /** @@ -37,4 +39,5 @@ export const stubServiceFactories = { httpServiceFactory, platformServiceFactory, userPermissionsServiceFactory, + dataServiceFactory, }; diff --git a/packages/kbn-shared-ux-services/src/types.ts b/packages/kbn-shared-ux-services/src/types.ts index 2bfbafb880e80..a0a4ec32c0e75 100755 --- a/packages/kbn-shared-ux-services/src/types.ts +++ b/packages/kbn-shared-ux-services/src/types.ts @@ -10,6 +10,7 @@ import { FC } from 'react'; import { SharedUxApplicationService, + SharedUxDataService, SharedUxDocLinksService, SharedUxEditorsService, SharedUxHttpService, @@ -32,6 +33,7 @@ export interface SharedUxServices { http: SharedUxHttpService; permissions: SharedUxUserPermissionsService; platform: SharedUxPlatformService; + data: SharedUxDataService; } /** diff --git a/packages/kbn-shared-ux-storybook/jest.config.js b/packages/kbn-shared-ux-storybook/jest.config.js deleted file mode 100644 index 91285e025f069..0000000000000 --- a/packages/kbn-shared-ux-storybook/jest.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['/packages/kbn-shared-ux-storybook'], -}; diff --git a/packages/kbn-shared-ux-storybook/src/index.ts b/packages/kbn-shared-ux-storybook/src/index.ts index 51f1c61165020..6b310673eb00d 100755 --- a/packages/kbn-shared-ux-storybook/src/index.ts +++ b/packages/kbn-shared-ux-storybook/src/index.ts @@ -16,4 +16,7 @@ export { platformServiceFactory, servicesFactory, userPermissionsServiceFactory, + dataServiceFactory, } from './services'; + +export type { DataServiceFactoryConfig } from './services'; diff --git a/packages/kbn-shared-ux-storybook/src/services/application.ts b/packages/kbn-shared-ux-storybook/src/services/application.ts index 2a544445fc474..1b16526bc8be8 100644 --- a/packages/kbn-shared-ux-storybook/src/services/application.ts +++ b/packages/kbn-shared-ux-storybook/src/services/application.ts @@ -16,8 +16,8 @@ export type ApplicationServiceFactory = ServiceFactory ({ - navigateToUrl: () => { - action('NavigateToUrl'); + navigateToUrl: (url) => { + action('navigateToUrl')(url); return Promise.resolve(); }, currentAppId$: new BehaviorSubject('123'), diff --git a/packages/kbn-shared-ux-storybook/src/services/data.ts b/packages/kbn-shared-ux-storybook/src/services/data.ts new file mode 100644 index 0000000000000..dbfd2fceb4210 --- /dev/null +++ b/packages/kbn-shared-ux-storybook/src/services/data.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ServiceFactory, SharedUxDataService } from '@kbn/shared-ux-services'; + +export interface DataServiceFactoryConfig { + hasESData: boolean; + hasDataView: boolean; + hasUserDataView: boolean; +} + +/** + * A factory function for creating a Storybook implementation of `SharedUxDataService`. + */ +export type SharedUxDataServiceFactory = ServiceFactory< + SharedUxDataService, + DataServiceFactoryConfig +>; + +/** + * A factory function for creating a Storybook implementation of `SharedUxDataService`. + */ +export const dataServiceFactory: SharedUxDataServiceFactory = (params) => { + return { + hasESData: () => Promise.resolve(params.hasESData || false), + hasDataView: () => Promise.resolve(params.hasDataView || false), + hasUserDataView: () => Promise.resolve(params.hasUserDataView || false), + }; +}; diff --git a/packages/kbn-shared-ux-storybook/src/services/doc_links.ts b/packages/kbn-shared-ux-storybook/src/services/doc_links.ts index 5ae1dcccc3664..eff942989956f 100644 --- a/packages/kbn-shared-ux-storybook/src/services/doc_links.ts +++ b/packages/kbn-shared-ux-storybook/src/services/doc_links.ts @@ -18,4 +18,5 @@ export type SharedUxDocLinksServiceFactory = ServiceFactory ({ dataViewsDocLink: 'https://www.elastic.co/guide/en/kibana/master/data-views.html', + kibanaGuideDocLink: 'https://www.elastic.co/guide/en/kibana/master/index.html', }); diff --git a/packages/kbn-shared-ux-storybook/src/services/index.ts b/packages/kbn-shared-ux-storybook/src/services/index.ts index 2de34f3392312..ff6ad1f1f2913 100644 --- a/packages/kbn-shared-ux-storybook/src/services/index.ts +++ b/packages/kbn-shared-ux-storybook/src/services/index.ts @@ -14,6 +14,7 @@ import { editorsServiceFactory } from './editors'; import { httpServiceFactory } from './http'; import { platformServiceFactory } from './platform'; import { userPermissionsServiceFactory } from './permissions'; +import { dataServiceFactory, DataServiceFactoryConfig } from './data'; export { applicationServiceFactory } from './application'; export { docLinksServiceFactory } from './doc_links'; @@ -21,6 +22,7 @@ export { editorsServiceFactory } from './editors'; export { httpServiceFactory } from './http'; export { platformServiceFactory } from './platform'; export { userPermissionsServiceFactory } from './permissions'; +export { dataServiceFactory } from './data'; /** * A factory function for creating a Storybook implementation of `SharedUxServices`. @@ -32,4 +34,7 @@ export const servicesFactory: ServiceFactory = (params) => http: httpServiceFactory(params), permissions: userPermissionsServiceFactory(), platform: platformServiceFactory(params), + data: dataServiceFactory(params as DataServiceFactoryConfig), }); + +export type { DataServiceFactoryConfig } from './data'; diff --git a/packages/kbn-sort-package-json/jest.config.js b/packages/kbn-sort-package-json/jest.config.js deleted file mode 100644 index ae0651be19e61..0000000000000 --- a/packages/kbn-sort-package-json/jest.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-sort-package-json'], -}; diff --git a/packages/kbn-stdio-dev-helpers/jest.config.js b/packages/kbn-stdio-dev-helpers/jest.config.js deleted file mode 100644 index 31a8aab16c7e5..0000000000000 --- a/packages/kbn-stdio-dev-helpers/jest.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -module.exports = { - preset: '@kbn/test/jest_node', - rootDir: '../..', - roots: ['/packages/kbn-stdio-dev-helpers'], -}; diff --git a/packages/kbn-stdio-dev-helpers/src/observe_lines.ts b/packages/kbn-stdio-dev-helpers/src/observe_lines.ts index 9b6ba85fea897..4b3a2ac2287da 100644 --- a/packages/kbn-stdio-dev-helpers/src/observe_lines.ts +++ b/packages/kbn-stdio-dev-helpers/src/observe_lines.ts @@ -9,7 +9,6 @@ import { Readable } from 'stream'; import * as Rx from 'rxjs'; -import { scan, takeUntil, share, materialize, mergeMap, last, catchError } from 'rxjs/operators'; const SEP = /\r?\n/; @@ -25,13 +24,13 @@ import { observeReadable } from './observe_readable'; * @return {Rx.Observable} */ export function observeLines(readable: Readable): Rx.Observable { - const done$ = observeReadable(readable).pipe(share()); + const done$ = observeReadable(readable).pipe(Rx.share()); const scan$: Rx.Observable<{ buffer: string; lines?: string[] }> = Rx.fromEvent( readable, 'data' ).pipe( - scan( + Rx.scan( ({ buffer }, chunk) => { buffer += chunk; @@ -53,9 +52,9 @@ export function observeLines(readable: Readable): Rx.Observable { ), // stop if done completes or errors - takeUntil(done$.pipe(materialize())), + Rx.takeUntil(done$.pipe(Rx.materialize())), - share() + Rx.share() ); return Rx.merge( @@ -63,14 +62,12 @@ export function observeLines(readable: Readable): Rx.Observable { done$, // merge in the "lines" from each step - scan$.pipe(mergeMap(({ lines }) => lines || [])), + scan$.pipe(Rx.mergeMap(({ lines }) => lines || [])), // inject the "unsplit" data at the end scan$.pipe( - last(), - mergeMap(({ buffer }) => (buffer ? [buffer] : [])), - // if there were no lines, last() will error, so catch and complete - catchError(() => Rx.empty()) + Rx.takeLast(1), + Rx.mergeMap(({ buffer }) => (buffer ? [buffer] : [])) ) ); } diff --git a/packages/kbn-stdio-dev-helpers/src/observe_readable.ts b/packages/kbn-stdio-dev-helpers/src/observe_readable.ts index 29aa77a538601..fa087c299aa51 100644 --- a/packages/kbn-stdio-dev-helpers/src/observe_readable.ts +++ b/packages/kbn-stdio-dev-helpers/src/observe_readable.ts @@ -9,7 +9,6 @@ import { Readable } from 'stream'; import * as Rx from 'rxjs'; -import { first, ignoreElements, mergeMap } from 'rxjs/operators'; /** * Produces an Observable from a ReadableSteam that: @@ -18,11 +17,12 @@ import { first, ignoreElements, mergeMap } from 'rxjs/operators'; */ export function observeReadable(readable: Readable): Rx.Observable { return Rx.race( - Rx.fromEvent(readable, 'end').pipe(first(), ignoreElements()), - + Rx.fromEvent(readable, 'end').pipe(Rx.first(), Rx.ignoreElements()), Rx.fromEvent(readable, 'error').pipe( - first(), - mergeMap((err) => Rx.throwError(err)) + Rx.first(), + Rx.map((err) => { + throw err; + }) ) ); } diff --git a/packages/kbn-storybook/src/lib/default_config.ts b/packages/kbn-storybook/src/lib/default_config.ts index 3caf879c48cbb..0f0b8070ff8b0 100644 --- a/packages/kbn-storybook/src/lib/default_config.ts +++ b/packages/kbn-storybook/src/lib/default_config.ts @@ -14,6 +14,12 @@ import { REPO_ROOT } from './constants'; import { default as WebpackConfig } from '../webpack.config'; const toPath = (_path: string) => path.join(REPO_ROOT, _path); + +// This ignore pattern excludes all of node_modules EXCEPT for `@kbn`. This allows for +// changes to packages to cause a refresh in Storybook. +const IGNORE_PATTERN = + /[/\\]node_modules[/\\](?!@kbn[/\\][^/\\]+[/\\](?!node_modules)([^/\\]+))([^/\\]+[/\\][^/\\]+)/; + export const defaultConfig: StorybookConfig = { addons: ['@kbn/storybook/preset', '@storybook/addon-a11y', '@storybook/addon-essentials'], stories: ['../**/*.stories.tsx', '../**/*.stories.mdx'], @@ -45,6 +51,11 @@ export const defaultConfig: StorybookConfig = { } config.node = { fs: 'empty' }; + config.watch = true; + config.watchOptions = { + ...config.watchOptions, + ignored: [IGNORE_PATTERN], + }; // Remove when @storybook has moved to @emotion v11 // https://github.com/storybookjs/storybook/issues/13145 diff --git a/packages/kbn-test-jest-helpers/BUILD.bazel b/packages/kbn-test-jest-helpers/BUILD.bazel index dc8b83495494c..85192829003e4 100644 --- a/packages/kbn-test-jest-helpers/BUILD.bazel +++ b/packages/kbn-test-jest-helpers/BUILD.bazel @@ -60,7 +60,6 @@ RUNTIME_DEPS = [ "@npm//joi", "@npm//mustache", "@npm//normalize-path", - "@npm//parse-link-header", "@npm//prettier", "@npm//react", "@npm//react-dom", @@ -106,7 +105,6 @@ TYPES_DEPS = [ "@npm//@types/mustache", "@npm//@types/normalize-path", "@npm//@types/node", - "@npm//@types/parse-link-header", "@npm//@types/prettier", "@npm//@types/react", "@npm//@types/react-dom", diff --git a/packages/kbn-test/BUILD.bazel b/packages/kbn-test/BUILD.bazel index 8a7aabdecd61e..15487aa781b8d 100644 --- a/packages/kbn-test/BUILD.bazel +++ b/packages/kbn-test/BUILD.bazel @@ -64,9 +64,9 @@ RUNTIME_DEPS = [ "@npm//jest-snapshot", "@npm//jest-styled-components", "@npm//joi", + "@npm//js-yaml", "@npm//mustache", "@npm//normalize-path", - "@npm//parse-link-header", "@npm//prettier", "@npm//react-dom", "@npm//react-redux", @@ -108,12 +108,12 @@ TYPES_DEPS = [ "@npm//@types/he", "@npm//@types/history", "@npm//@types/jest", + "@npm//@types/js-yaml", "@npm//@types/joi", "@npm//@types/lodash", "@npm//@types/mustache", "@npm//@types/normalize-path", "@npm//@types/node", - "@npm//@types/parse-link-header", "@npm//@types/prettier", "@npm//@types/react-dom", "@npm//@types/react-redux", diff --git a/packages/kbn-test/README.md b/packages/kbn-test/README.md index 3159e5c2492b4..72fb5c3358ce7 100644 --- a/packages/kbn-test/README.md +++ b/packages/kbn-test/README.md @@ -15,14 +15,14 @@ Functional testing methods exist in the `src/functional_tests` directory. They d #### runTests(configPaths: Array) For each config file specified in configPaths, starts Elasticsearch and Kibana once, runs tests specified in that config file, and shuts down Elasticsearch and Kibana once completed. (Repeats for every config file.) -`configPaths`: array of strings, each an absolute path to a config file that looks like [this](../../test/functional/config.js), following the config schema specified [here](../../src/functional_test_runner/lib/config/schema.js). +`configPaths`: array of strings, each an absolute path to a config file that looks like [this](../../test/functional/config.base.js), following the config schema specified [here](../../src/functional_test_runner/lib/config/schema.js). Internally the method that starts Elasticsearch comes from [kbn-es](../../packages/kbn-es). #### startServers(configPath: string) Starts Elasticsearch and Kibana servers given a specified config. -`configPath`: absolute path to a config file that looks like [this](../../test/functional/config.js), following the config schema specified [here](../../src/functional_test_runner/lib/config/schema.js). +`configPath`: absolute path to a config file that looks like [this](../../test/functional/config.base.js), following the config schema specified [here](../../src/functional_test_runner/lib/config/schema.js). Allows users to start another process to run just the tests while keeping the servers running with this method. Start servers _and_ run tests using the same config file ([see how](../../scripts/README.md)). diff --git a/packages/kbn-test/jest-preset.js b/packages/kbn-test/jest-preset.js index 6245532b00e44..fb535c2766cc4 100644 --- a/packages/kbn-test/jest-preset.js +++ b/packages/kbn-test/jest-preset.js @@ -63,12 +63,16 @@ module.exports = { rootDirectory: '.', }, ], - [ - '@kbn/test/target_node/jest/ci_stats_jest_reporter', - { - testGroupType: 'Jest Unit Tests', - }, - ], + ...(process.env.TEST_GROUP_TYPE_UNIT + ? [ + [ + '@kbn/test/target_node/jest/ci_stats_jest_reporter', + { + testGroupType: process.env.TEST_GROUP_TYPE_UNIT, + }, + ], + ] + : []), ], // The paths to modules that run some code to configure or set up the testing environment before each test diff --git a/packages/kbn-test/jest_integration/jest-preset.js b/packages/kbn-test/jest_integration/jest-preset.js index f5593e3f57fb6..1f2626aef532e 100644 --- a/packages/kbn-test/jest_integration/jest-preset.js +++ b/packages/kbn-test/jest_integration/jest-preset.js @@ -28,12 +28,16 @@ module.exports = { reportName: 'Jest Integration Tests', }, ], - [ - '@kbn/test/target_node/jest/ci_stats_jest_reporter', - { - testGroupType: 'Jest Integration Tests', - }, - ], + ...(process.env.TEST_GROUP_TYPE_INTEGRATION + ? [ + [ + '@kbn/test/target_node/jest/ci_stats_jest_reporter', + { + testGroupType: process.env.TEST_GROUP_TYPE_INTEGRATION, + }, + ], + ] + : []), ], coverageReporters: !!process.env.CI ? [['json', { file: 'jest-integration.json' }]] diff --git a/packages/kbn-test/src/es/test_es_cluster.ts b/packages/kbn-test/src/es/test_es_cluster.ts index a12b1d852ced9..c065cb01a4c36 100644 --- a/packages/kbn-test/src/es/test_es_cluster.ts +++ b/packages/kbn-test/src/es/test_es_cluster.ts @@ -41,6 +41,7 @@ interface Node { ) => Promise<{ insallPath: string }>; start: (installPath: string, opts: Record) => Promise; stop: () => Promise; + kill: () => Promise; } export interface ICluster { @@ -145,6 +146,11 @@ export interface CreateTestEsClusterOptions { * defaults to the transport port from `packages/kbn-test/src/es/es_test_config.ts` */ transportPort?: number | string; + /** + * Report to the creator of the es-test-cluster that the es node has exitted before stop() was called, allowing + * this caller to react appropriately. If this is not passed then an uncatchable exception will be thrown + */ + onEarlyExit?: (msg: string) => void; } export function createTestEsCluster< @@ -164,6 +170,7 @@ export function createTestEsCluster< clusterName: customClusterName = 'es-test-cluster', ssl, transportPort, + onEarlyExit, } = options; const clusterName = `${CI_PARALLEL_PROCESS_PREFIX}${customClusterName}`; @@ -257,6 +264,7 @@ export function createTestEsCluster< // set it up after the last node is started. skipNativeRealmSetup: this.nodes.length > 1 && i < this.nodes.length - 1, skipReadyCheck: this.nodes.length > 1 && i < this.nodes.length - 1, + onEarlyExit, }); }); } @@ -268,20 +276,26 @@ export function createTestEsCluster< } async stop() { - const nodeStopPromises = []; - for (let i = 0; i < this.nodes.length; i++) { - nodeStopPromises.push(async () => { + await Promise.all( + this.nodes.map(async (node, i) => { log.info(`[es] stopping node ${nodes[i].name}`); - return await this.nodes[i].stop(); - }); - } - await Promise.all(nodeStopPromises.map(async (stop) => await stop())); + await node.stop(); + }) + ); log.info('[es] stopped'); } async cleanup() { - await this.stop(); + log.info('[es] killing', this.nodes.length === 1 ? 'node' : `${this.nodes.length} nodes`); + await Promise.all( + this.nodes.map(async (node, i) => { + log.info(`[es] stopping node ${nodes[i].name}`); + // we are deleting this install, stop ES more aggressively + await node.kill(); + }) + ); + await del(config.installPath, { force: true }); log.info('[es] cleanup complete'); } diff --git a/packages/kbn-test/src/functional_test_runner/cli.ts b/packages/kbn-test/src/functional_test_runner/cli.ts index ee01b0ccfde9c..f71e4ac7d6ccd 100644 --- a/packages/kbn-test/src/functional_test_runner/cli.ts +++ b/packages/kbn-test/src/functional_test_runner/cli.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { resolve } from 'path'; +import Path from 'path'; import { inspect } from 'util'; import { run, createFlagError, Flags } from '@kbn/dev-utils'; @@ -16,7 +16,7 @@ import exitHook from 'exit-hook'; import { FunctionalTestRunner } from './functional_test_runner'; -const makeAbsolutePath = (v: string) => resolve(process.cwd(), v); +const makeAbsolutePath = (v: string) => Path.resolve(process.cwd(), v); const toArray = (v: string | string[]) => ([] as string[]).concat(v || []); const parseInstallDir = (flags: Flags) => { const flag = flags['kibana-install-dir']; @@ -42,9 +42,15 @@ export function runFtrCli() { throw createFlagError('expected --es-version to be a string'); } + const configRel = flags.config; + if (typeof configRel !== 'string' || !configRel) { + throw createFlagError('--config is required'); + } + const configPath = makeAbsolutePath(configRel); + const functionalTestRunner = new FunctionalTestRunner( log, - makeAbsolutePath(flags.config as string), + configPath, { mochaOpts: { bail: flags.bail, @@ -69,6 +75,8 @@ export function runFtrCli() { esVersion ); + await functionalTestRunner.readConfigFile(); + if (flags.throttle) { process.env.TEST_THROTTLE_NETWORK = '1'; } @@ -98,11 +106,7 @@ export function runFtrCli() { }); } - try { - await functionalTestRunner.close(); - } finally { - process.exit(); - } + process.exit(); }; process.on('unhandledRejection', (err) => @@ -153,9 +157,6 @@ export function runFtrCli() { 'headless', 'dry-run', ], - default: { - config: 'test/functional/config.js', - }, help: ` --config=path path to a config file --bail stop tests after the first failure diff --git a/packages/kbn-test/src/functional_test_runner/fake_mocha_types.ts b/packages/kbn-test/src/functional_test_runner/fake_mocha_types.ts index 96ebcd79c4e43..506b6f139f736 100644 --- a/packages/kbn-test/src/functional_test_runner/fake_mocha_types.ts +++ b/packages/kbn-test/src/functional_test_runner/fake_mocha_types.ts @@ -37,6 +37,7 @@ export interface Test { export interface Runner extends EventEmitter { abort(): void; failures: any[]; + uncaught: (error: Error) => void; } export interface Mocha { diff --git a/packages/kbn-test/src/functional_test_runner/functional_test_runner.ts b/packages/kbn-test/src/functional_test_runner/functional_test_runner.ts index 1ba99fc69a5d3..9de6500a45323 100644 --- a/packages/kbn-test/src/functional_test_runner/functional_test_runner.ts +++ b/packages/kbn-test/src/functional_test_runner/functional_test_runner.ts @@ -14,10 +14,9 @@ import { REPO_ROOT } from '@kbn/utils'; import { Suite, Test } from './fake_mocha_types'; import { Lifecycle, - LifecyclePhase, - TestMetadata, readConfigFile, ProviderCollection, + Providers, readProviderSpec, setupMocha, runTests, @@ -29,10 +28,6 @@ import { import { createEsClientForFtrConfig } from '../es'; export class FunctionalTestRunner { - public readonly lifecycle = new Lifecycle(); - public readonly testMetadata = new TestMetadata(this.lifecycle); - private closed = false; - private readonly esVersion: EsVersion; constructor( private readonly log: ToolingLog, @@ -40,12 +35,6 @@ export class FunctionalTestRunner { private readonly configOverrides: any, esVersion?: string | EsVersion ) { - for (const [key, value] of Object.entries(this.lifecycle)) { - if (value instanceof LifecyclePhase) { - value.before$.subscribe(() => log.verbose('starting %j lifecycle phase', key)); - value.after$.subscribe(() => log.verbose('starting %j lifecycle phase', key)); - } - } this.esVersion = esVersion === undefined ? EsVersion.getDefault() @@ -54,20 +43,29 @@ export class FunctionalTestRunner { : new EsVersion(esVersion); } - async run() { - return await this._run(async (config, coreProviders) => { - SuiteTracker.startTracking(this.lifecycle, this.configFile); + async run(abortSignal?: AbortSignal) { + const testStats = await this.getTestStats(); - const providers = new ProviderCollection(this.log, [ - ...coreProviders, - ...readProviderSpec('Service', config.get('services')), - ...readProviderSpec('PageObject', config.get('pageObjects')), - ]); + return await this.runHarness(async (config, lifecycle, coreProviders) => { + SuiteTracker.startTracking(lifecycle, this.configFile); - if (providers.hasService('es')) { - await this.validateEsVersion(config); + const realServices = + !testStats || (testStats.testCount > 0 && testStats.nonSkippedTestCount > 0); + + const providers = realServices + ? new ProviderCollection(this.log, [ + ...coreProviders, + ...readProviderSpec('Service', config.get('services')), + ...readProviderSpec('PageObject', config.get('pageObjects')), + ]) + : this.getStubProviderCollection(config, coreProviders); + + if (realServices) { + if (providers.hasService('es')) { + await this.validateEsVersion(config); + } + await providers.loadAll(); } - await providers.loadAll(); const customTestRunner = config.get('testRunner'); if (customTestRunner) { @@ -90,7 +88,7 @@ export class FunctionalTestRunner { } const mocha = await setupMocha( - this.lifecycle, + lifecycle, this.log, config, providers, @@ -108,10 +106,19 @@ export class FunctionalTestRunner { return this.simulateMochaDryRun(mocha); } - await this.lifecycle.beforeTests.trigger(mocha.suite); - this.log.info('Starting tests'); + if (abortSignal?.aborted) { + this.log.warning('run aborted'); + return; + } + + await lifecycle.beforeTests.trigger(mocha.suite); + if (abortSignal?.aborted) { + this.log.warning('run aborted'); + return; + } - return await runTests(this.lifecycle, mocha); + this.log.info('Starting tests'); + return await runTests(lifecycle, mocha, abortSignal); }); } @@ -143,69 +150,77 @@ export class FunctionalTestRunner { } async getTestStats() { - return await this._run(async (config, coreProviders) => { + return await this.runHarness(async (config, lifecycle, coreProviders) => { if (config.get('testRunner')) { - throw new Error('Unable to get test stats for config that uses a custom test runner'); + return; } - // replace the function of custom service providers so that they return - // promise-like objects which never resolve, essentially disabling them - // allowing us to load the test files and populate the mocha suites - const readStubbedProviderSpec = (type: string, providers: any, skip: string[]) => - readProviderSpec(type, providers).map((p) => ({ - ...p, - fn: skip.includes(p.name) - ? (ctx: any) => { - const result = ProviderCollection.callProviderFn(p.fn, ctx); - - if ('then' in result) { - throw new Error( - `Provider [${p.name}] returns a promise so it can't loaded during test analysis` - ); - } - - return result; - } - : () => ({ - then: () => {}, - }), - })); - - const providers = new ProviderCollection(this.log, [ - ...coreProviders, - ...readStubbedProviderSpec( - 'Service', - config.get('services'), - config.get('servicesRequiredForTestAnalysis') - ), - ...readStubbedProviderSpec('PageObject', config.get('pageObjects'), []), - ]); - - const mocha = await setupMocha(this.lifecycle, this.log, config, providers, this.esVersion); - - const countTests = (suite: Suite): number => - suite.suites.reduce((sum, s) => sum + countTests(s), suite.tests.length); + const providers = this.getStubProviderCollection(config, coreProviders); + const mocha = await setupMocha(lifecycle, this.log, config, providers, this.esVersion); + + const queue = new Set([mocha.suite]); + const allTests: Test[] = []; + for (const suite of queue) { + for (const test of suite.tests) { + allTests.push(test); + } + for (const childSuite of suite.suites) { + queue.add(childSuite); + } + } return { - testCount: countTests(mocha.suite), + testCount: allTests.length, + nonSkippedTestCount: allTests.filter((t) => !t.pending).length, testsExcludedByTag: mocha.testsExcludedByTag.map((t: Test) => t.fullTitle()), }; }); } - async _run( - handler: (config: Config, coreProvider: ReturnType) => Promise + private getStubProviderCollection(config: Config, coreProviders: Providers) { + // when we want to load the tests but not actually run anything we can + // use stubbed providers which allow mocha to do it's thing without taking + // too much time + const readStubbedProviderSpec = (type: string, providers: any, skip: string[]) => + readProviderSpec(type, providers).map((p) => ({ + ...p, + fn: skip.includes(p.name) + ? (ctx: any) => { + const result = ProviderCollection.callProviderFn(p.fn, ctx); + + if ('then' in result) { + throw new Error( + `Provider [${p.name}] returns a promise so it can't loaded during test analysis` + ); + } + + return result; + } + : () => ({ + then: () => {}, + }), + })); + + return new ProviderCollection(this.log, [ + ...coreProviders, + ...readStubbedProviderSpec( + 'Service', + config.get('services'), + config.get('servicesRequiredForTestAnalysis') + ), + ...readStubbedProviderSpec('PageObject', config.get('pageObjects'), []), + ]); + } + + private async runHarness( + handler: (config: Config, lifecycle: Lifecycle, coreProviders: Providers) => Promise ): Promise { let runErrorOccurred = false; + const lifecycle = new Lifecycle(this.log); try { - const config = await readConfigFile( - this.log, - this.esVersion, - this.configFile, - this.configOverrides - ); - this.log.info('Config loaded'); + const config = await this.readConfigFile(); + this.log.debug('Config loaded'); if ( (!config.get('testFiles') || config.get('testFiles').length === 0) && @@ -217,26 +232,25 @@ export class FunctionalTestRunner { const dockerServers = new DockerServersService( config.get('dockerServers'), this.log, - this.lifecycle + lifecycle ); // base level services that functional_test_runner exposes const coreProviders = readProviderSpec('Service', { - lifecycle: () => this.lifecycle, + lifecycle: () => lifecycle, log: () => this.log, - testMetadata: () => this.testMetadata, config: () => config, dockerServers: () => dockerServers, esVersion: () => this.esVersion, }); - return await handler(config, coreProviders); + return await handler(config, lifecycle, coreProviders); } catch (runError) { runErrorOccurred = true; throw runError; } finally { try { - await this.close(); + await lifecycle.cleanup.trigger(); } catch (closeError) { if (runErrorOccurred) { this.log.error('failed to close functional_test_runner'); @@ -249,11 +263,8 @@ export class FunctionalTestRunner { } } - async close() { - if (this.closed) return; - - this.closed = true; - await this.lifecycle.cleanup.trigger(); + public async readConfigFile() { + return await readConfigFile(this.log, this.esVersion, this.configFile, this.configOverrides); } simulateMochaDryRun(mocha: any) { diff --git a/packages/kbn-test/src/functional_test_runner/index.ts b/packages/kbn-test/src/functional_test_runner/index.ts index b5d55c28ee9b2..1a8efb6097048 100644 --- a/packages/kbn-test/src/functional_test_runner/index.ts +++ b/packages/kbn-test/src/functional_test_runner/index.ts @@ -15,7 +15,6 @@ export { Lifecycle, LifecyclePhase, } from './lib'; -export type { ScreenshotRecord } from './lib'; export { runFtrCli } from './cli'; export * from './lib/docker_servers'; export * from './public_types'; diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/ftr_configs_manifest.ts b/packages/kbn-test/src/functional_test_runner/lib/config/ftr_configs_manifest.ts new file mode 100644 index 0000000000000..93cab4bfaac95 --- /dev/null +++ b/packages/kbn-test/src/functional_test_runner/lib/config/ftr_configs_manifest.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; +import Fs from 'fs'; + +import { REPO_ROOT } from '@kbn/utils'; +import JsYaml from 'js-yaml'; + +export const FTR_CONFIGS_MANIFEST_REL = '.buildkite/ftr_configs.yml'; + +const ftrConfigsManifest = JsYaml.safeLoad( + Fs.readFileSync(Path.resolve(REPO_ROOT, FTR_CONFIGS_MANIFEST_REL), 'utf8') +); + +export const FTR_CONFIGS_MANIFEST_PATHS = (Object.values(ftrConfigsManifest) as string[][]) + .flat() + .map((rel) => Path.resolve(REPO_ROOT, rel)); diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/read_config_file.test.js b/packages/kbn-test/src/functional_test_runner/lib/config/read_config_file.test.js deleted file mode 100644 index d1ce17cc95b7b..0000000000000 --- a/packages/kbn-test/src/functional_test_runner/lib/config/read_config_file.test.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { ToolingLog } from '@kbn/tooling-log'; -import { readConfigFile } from './read_config_file'; -import { Config } from './config'; -import { EsVersion } from '../es_version'; - -const log = new ToolingLog(); -const esVersion = new EsVersion('8.0.0'); - -describe('readConfigFile()', () => { - it('reads config from a file, returns an instance of Config class', async () => { - const config = await readConfigFile(log, esVersion, require.resolve('./__fixtures__/config.1')); - expect(config instanceof Config).toBeTruthy(); - expect(config.get('testFiles')).toEqual(['config.1']); - }); - - it('merges setting overrides into log', async () => { - const config = await readConfigFile( - log, - esVersion, - require.resolve('./__fixtures__/config.1'), - { - screenshots: { - directory: 'foo.bar', - }, - } - ); - - expect(config.get('screenshots.directory')).toBe('foo.bar'); - }); - - it('supports loading config files from within config files', async () => { - const config = await readConfigFile(log, esVersion, require.resolve('./__fixtures__/config.2')); - expect(config.get('testFiles')).toEqual(['config.1', 'config.2']); - }); - - it('throws if settings are invalid', async () => { - try { - await readConfigFile(log, esVersion, require.resolve('./__fixtures__/config.invalid')); - throw new Error('expected readConfigFile() to fail'); - } catch (err) { - expect(err.message).toMatch(/"foo"/); - } - }); -}); diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/read_config_file.test.ts b/packages/kbn-test/src/functional_test_runner/lib/config/read_config_file.test.ts new file mode 100644 index 0000000000000..29b723dae7195 --- /dev/null +++ b/packages/kbn-test/src/functional_test_runner/lib/config/read_config_file.test.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ToolingLog } from '@kbn/tooling-log'; +import { readConfigFile } from './read_config_file'; +import { Config } from './config'; +import { EsVersion } from '../es_version'; + +const log = new ToolingLog(); +const esVersion = new EsVersion('8.0.0'); + +const CONFIG_PATH_1 = require.resolve('./__fixtures__/config.1.js'); +const CONFIG_PATH_2 = require.resolve('./__fixtures__/config.2.js'); +const CONFIG_PATH_INVALID = require.resolve('./__fixtures__/config.invalid.js'); + +describe('readConfigFile()', () => { + it('reads config from a file, returns an instance of Config class', async () => { + const config = await readConfigFile(log, esVersion, CONFIG_PATH_1); + expect(config instanceof Config).toBeTruthy(); + expect(config.get('testFiles')).toEqual(['config.1']); + }); + + it('merges setting overrides into log', async () => { + const config = await readConfigFile(log, esVersion, CONFIG_PATH_1, { + screenshots: { + directory: 'foo.bar', + }, + }); + + expect(config.get('screenshots.directory')).toBe('foo.bar'); + }); + + it('supports loading config files from within config files', async () => { + const config = await readConfigFile(log, esVersion, CONFIG_PATH_2); + expect(config.get('testFiles')).toEqual(['config.1', 'config.2']); + }); + + it('throws if settings are invalid', async () => { + try { + await readConfigFile(log, esVersion, CONFIG_PATH_INVALID); + throw new Error('expected readConfigFile() to fail'); + } catch (err) { + expect(err.message).toMatch(/"foo"/); + } + }); +}); diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/read_config_file.ts b/packages/kbn-test/src/functional_test_runner/lib/config/read_config_file.ts index d026842f3a4f1..49a6ef16d6685 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/config/read_config_file.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/config/read_config_file.ts @@ -6,25 +6,54 @@ * Side Public License, v 1. */ +import Path from 'path'; import { ToolingLog } from '@kbn/tooling-log'; import { defaultsDeep } from 'lodash'; +import { createFlagError } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; import { Config } from './config'; import { EsVersion } from '../es_version'; +import { FTR_CONFIGS_MANIFEST_REL, FTR_CONFIGS_MANIFEST_PATHS } from './ftr_configs_manifest'; const cache = new WeakMap(); async function getSettingsFromFile( log: ToolingLog, esVersion: EsVersion, - path: string, - settingOverrides: any + options: { + path: string; + settingOverrides: any; + primary: boolean; + } ) { - const configModule = require(path); // eslint-disable-line @typescript-eslint/no-var-requires + let resolvedPath; + try { + resolvedPath = require.resolve(options.path); + } catch (error) { + if (error.code === 'MODULE_NOT_FOUND') { + throw createFlagError(`Unable to find config file [${options.path}]`); + } + + throw error; + } + + if ( + options.primary && + !FTR_CONFIGS_MANIFEST_PATHS.includes(resolvedPath) && + !resolvedPath.includes(`${Path.sep}__fixtures__${Path.sep}`) + ) { + const rel = Path.relative(REPO_ROOT, resolvedPath); + throw createFlagError( + `Refusing to load FTR Config at [${rel}] which is not listed in [${FTR_CONFIGS_MANIFEST_REL}]. All FTR Config files must be listed there, use the "enabled" key if the FTR Config should be run on automatically on PR CI, or the "disabled" key if it is run manually or by a special job.` + ); + } + + const configModule = require(resolvedPath); // eslint-disable-line @typescript-eslint/no-var-requires const configProvider = configModule.__esModule ? configModule.default : configModule; if (!cache.has(configProvider)) { - log.debug('Loading config file from %j', path); + log.debug('Loading config file from %j', resolvedPath); cache.set( configProvider, configProvider({ @@ -32,7 +61,11 @@ async function getSettingsFromFile( esVersion, async readConfigFile(p: string, o: any) { return new Config({ - settings: await getSettingsFromFile(log, esVersion, p, o), + settings: await getSettingsFromFile(log, esVersion, { + path: p, + settingOverrides: o, + primary: false, + }), primary: false, path: p, }); @@ -43,7 +76,7 @@ async function getSettingsFromFile( const settingsWithDefaults: any = defaultsDeep( {}, - settingOverrides, + options.settingOverrides, await cache.get(configProvider)! ); @@ -57,7 +90,11 @@ export async function readConfigFile( settingOverrides: any = {} ) { return new Config({ - settings: await getSettingsFromFile(log, esVersion, path, settingOverrides), + settings: await getSettingsFromFile(log, esVersion, { + path, + settingOverrides, + primary: true, + }), primary: true, path, }); diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts index 17c3af046f92f..d2182064d352e 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts @@ -226,6 +226,11 @@ export const schema = Joi.object() wait: Joi.object() .regex() .default(/Kibana is now available/), + + /** + * Does this test config only work when run against source? + */ + alwaysUseSource: Joi.boolean().default(false), }) .default(), env: Joi.object().unknown().default(), diff --git a/packages/kbn-test/src/functional_test_runner/lib/index.ts b/packages/kbn-test/src/functional_test_runner/lib/index.ts index 077a62e8e74e5..9f637f8bd5b4f 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/index.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/index.ts @@ -12,7 +12,6 @@ export { readConfigFile, Config } from './config'; export * from './providers'; // @internal export { runTests, setupMocha } from './mocha'; -export * from './test_metadata'; export * from './docker_servers'; export { SuiteTracker } from './suite_tracker'; diff --git a/packages/kbn-test/src/functional_test_runner/lib/lifecycle.ts b/packages/kbn-test/src/functional_test_runner/lib/lifecycle.ts index e683ec23a8d84..230eacb91008e 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/lifecycle.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/lifecycle.ts @@ -6,29 +6,51 @@ * Side Public License, v 1. */ +import * as Rx from 'rxjs'; +import { ToolingLog } from '@kbn/tooling-log'; + import { LifecyclePhase } from './lifecycle_phase'; import { Suite, Test } from '../fake_mocha_types'; export class Lifecycle { + /** root subscription to cleanup lifecycle phases when lifecycle completes */ + private readonly sub = new Rx.Subscription(); + /** lifecycle phase that will run handlers once before tests execute */ - public readonly beforeTests = new LifecyclePhase<[Suite]>({ + public readonly beforeTests = new LifecyclePhase<[Suite]>(this.sub, { singular: true, }); /** lifecycle phase that runs handlers before each runnable (test and hooks) */ - public readonly beforeEachRunnable = new LifecyclePhase<[Test]>(); + public readonly beforeEachRunnable = new LifecyclePhase<[Test]>(this.sub); /** lifecycle phase that runs handlers before each suite */ - public readonly beforeTestSuite = new LifecyclePhase<[Suite]>(); + public readonly beforeTestSuite = new LifecyclePhase<[Suite]>(this.sub); /** lifecycle phase that runs handlers before each test */ - public readonly beforeEachTest = new LifecyclePhase<[Test]>(); + public readonly beforeEachTest = new LifecyclePhase<[Test]>(this.sub); /** lifecycle phase that runs handlers after each suite */ - public readonly afterTestSuite = new LifecyclePhase<[Suite]>(); + public readonly afterTestSuite = new LifecyclePhase<[Suite]>(this.sub); /** lifecycle phase that runs handlers after a test fails */ - public readonly testFailure = new LifecyclePhase<[Error, Test]>(); + public readonly testFailure = new LifecyclePhase<[Error, Test]>(this.sub); /** lifecycle phase that runs handlers after a hook fails */ - public readonly testHookFailure = new LifecyclePhase<[Error, Test]>(); + public readonly testHookFailure = new LifecyclePhase<[Error, Test]>(this.sub); /** lifecycle phase that runs handlers at the very end of execution */ - public readonly cleanup = new LifecyclePhase<[]>({ + public readonly cleanup = new LifecyclePhase<[]>(this.sub, { singular: true, }); + + constructor(log: ToolingLog) { + for (const [name, phase] of Object.entries(this)) { + if (phase instanceof LifecyclePhase) { + phase.before$.subscribe(() => log.verbose('starting %j lifecycle phase', name)); + phase.after$.subscribe(() => log.verbose('starting %j lifecycle phase', name)); + } + } + + // after the singular cleanup lifecycle phase completes unsubscribe from the root subscription + this.cleanup.after$.pipe(Rx.materialize()).subscribe((n) => { + if (n.kind === 'C') { + this.sub.unsubscribe(); + } + }); + } } diff --git a/packages/kbn-test/src/functional_test_runner/lib/lifecycle_phase.test.ts b/packages/kbn-test/src/functional_test_runner/lib/lifecycle_phase.test.ts index 503a9490f2664..47ab24169d204 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/lifecycle_phase.test.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/lifecycle_phase.test.ts @@ -26,7 +26,7 @@ describe('with randomness', () => { }); it('calls handlers in random order', async () => { - const phase = new LifecyclePhase(); + const phase = new LifecyclePhase(new Rx.Subscription()); const order: string[] = []; phase.add( @@ -69,7 +69,7 @@ describe('without randomness', () => { afterEach(() => jest.restoreAllMocks()); it('calls all handlers and throws first error', async () => { - const phase = new LifecyclePhase(); + const phase = new LifecyclePhase(new Rx.Subscription()); const fn1 = jest.fn(); phase.add(fn1); @@ -88,7 +88,7 @@ describe('without randomness', () => { }); it('triggers before$ just before calling handler and after$ once it resolves', async () => { - const phase = new LifecyclePhase(); + const phase = new LifecyclePhase(new Rx.Subscription()); const order: string[] = []; const beforeSub = jest.fn(() => order.push('before')); @@ -116,7 +116,7 @@ describe('without randomness', () => { }); it('completes before$ and after$ if phase is singular', async () => { - const phase = new LifecyclePhase({ singular: true }); + const phase = new LifecyclePhase(new Rx.Subscription(), { singular: true }); const beforeNotifs: Array> = []; phase.before$.pipe(materialize()).subscribe((n) => beforeNotifs.push(n)); @@ -160,7 +160,7 @@ describe('without randomness', () => { }); it('completes before$ subscribers after trigger of singular phase', async () => { - const phase = new LifecyclePhase({ singular: true }); + const phase = new LifecyclePhase(new Rx.Subscription(), { singular: true }); await phase.trigger(); await expect(phase.before$.pipe(materialize(), toArray()).toPromise()).resolves @@ -177,7 +177,7 @@ describe('without randomness', () => { }); it('replays after$ event subscribers after trigger of singular phase', async () => { - const phase = new LifecyclePhase({ singular: true }); + const phase = new LifecyclePhase(new Rx.Subscription(), { singular: true }); await phase.trigger(); await expect(phase.after$.pipe(materialize(), toArray()).toPromise()).resolves diff --git a/packages/kbn-test/src/functional_test_runner/lib/lifecycle_phase.ts b/packages/kbn-test/src/functional_test_runner/lib/lifecycle_phase.ts index 09e7c6f3b8d15..df4b26230d4da 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/lifecycle_phase.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/lifecycle_phase.ts @@ -26,6 +26,7 @@ export class LifecyclePhase { public readonly after$: Rx.Observable; constructor( + sub: Rx.Subscription, private readonly options: { singular?: boolean; } = {} @@ -35,6 +36,12 @@ export class LifecyclePhase { this.afterSubj = this.options.singular ? new Rx.ReplaySubject(1) : new Rx.Subject(); this.after$ = this.afterSubj.asObservable(); + + sub.add(() => { + this.beforeSubj.complete(); + this.afterSubj.complete(); + this.handlers.length = 0; + }); } public add(fn: (...args: Args) => Promise | void) { diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js b/packages/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js index b9d4ed6ef7b5e..62104cebf9cba 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js @@ -12,6 +12,32 @@ import { createAssignmentProxy } from './assignment_proxy'; import { wrapFunction } from './wrap_function'; import { wrapRunnableArgs } from './wrap_runnable_args'; +const allTestsSkippedCache = new WeakMap(); +function allTestsAreSkipped(suite) { + // cache result for each suite so we don't have to traverse over and over + const cache = allTestsSkippedCache.get(suite); + if (cache) { + return cache; + } + + // if this suite is skipped directly then all it's children are skipped + if (suite.pending) { + allTestsSkippedCache.set(suite, true); + return true; + } + + // if any of this suites own tests are not skipped, then we don't need to traverse to child suites + if (suite.tests.some((t) => !t.pending)) { + allTestsSkippedCache.set(suite, false); + return false; + } + + // otherwise traverse down through the child suites and return true only if all children are all skipped + const childrenSkipped = suite.suites.every(allTestsAreSkipped); + allTestsSkippedCache.set(suite, childrenSkipped); + return childrenSkipped; +} + export function decorateMochaUi(log, lifecycle, context, { rootTags }) { // incremented at the start of each suite, decremented after // so that in each non-suite call we can know if we are within @@ -71,6 +97,12 @@ export function decorateMochaUi(log, lifecycle, context, { rootTags }) { provider.call(this); + if (allTestsAreSkipped(this)) { + // all the children in this suite are skipped, so make sure the suite is + // marked as pending so that its hooks are not run + this.pending = true; + } + after('afterTestSuite.trigger', async () => { await lifecycle.afterTestSuite.trigger(this); }); diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/filter_suites.test.js b/packages/kbn-test/src/functional_test_runner/lib/mocha/filter_suites.test.js index 191503af123d0..3d1867aa0eed0 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/filter_suites.test.js +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/filter_suites.test.js @@ -69,6 +69,9 @@ function setup({ include, exclude, esVersion }) { info(...args) { history.push(`info: ${format(...args)}`); }, + debug(...args) { + history.push(`debg: ${format(...args)}`); + }, }, mocha, include, @@ -221,7 +224,7 @@ it(`excludes tests which don't meet the esVersionRequirement`, async () => { expect(history).toMatchInlineSnapshot(` Array [ - "info: Only running suites which are compatible with ES version 9.0.0", + "debg: Only running suites which are compatible with ES version 9.0.0", "suite: ", "suite: level 1", "suite: level 1 level 1a", diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/filter_suites.ts b/packages/kbn-test/src/functional_test_runner/lib/mocha/filter_suites.ts index 98db434b3b088..6bb95acd407de 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/filter_suites.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/filter_suites.ts @@ -44,7 +44,7 @@ export function filterSuites({ log, mocha, include, exclude, esVersion }: Option if (esVersion) { // traverse the test graph and exclude any tests which don't meet their esVersionRequirement - log.info('Only running suites which are compatible with ES version', esVersion.toString()); + log.debug('Only running suites which are compatible with ES version', esVersion.toString()); (function recurse(parentSuite: SuiteInternal) { const children = parentSuite.suites; parentSuite.suites = []; diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/reporter/ci_stats_ftr_reporter.ts b/packages/kbn-test/src/functional_test_runner/lib/mocha/reporter/ci_stats_ftr_reporter.ts index 838b8ee9b9aa5..96900555db745 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/reporter/ci_stats_ftr_reporter.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/reporter/ci_stats_ftr_reporter.ts @@ -17,7 +17,6 @@ import { import { Config } from '../../config'; import { Runner } from '../../../fake_mocha_types'; -import { TestMetadata, ScreenshotRecord } from '../../test_metadata'; import { Lifecycle } from '../../lifecycle'; import { getSnapshotOfRunnableLogs } from '../../../../mocha'; @@ -36,7 +35,6 @@ interface Runnable { file: string; title: string; parent: Suite; - _screenshots?: ScreenshotRecord[]; } function getHookType(hook: Runnable): CiStatsTestType { @@ -60,15 +58,18 @@ export function setupCiStatsFtrTestGroupReporter({ config, lifecycle, runner, - testMetadata, reporter, }: { config: Config; lifecycle: Lifecycle; runner: Runner; - testMetadata: TestMetadata; reporter: CiStatsReporter; }) { + const testGroupType = process.env.TEST_GROUP_TYPE_FUNCTIONAL; + if (!testGroupType) { + throw new Error('missing process.env.TEST_GROUP_TYPE_FUNCTIONAL'); + } + let startMs: number | undefined; runner.on('start', () => { startMs = Date.now(); @@ -78,8 +79,9 @@ export function setupCiStatsFtrTestGroupReporter({ const group: CiStatsReportTestsOptions['group'] = { startTime: new Date(start).toJSON(), durationMs: 0, - type: config.path.startsWith('x-pack') ? 'X-Pack Functional Tests' : 'Functional Tests', + type: testGroupType, name: Path.relative(REPO_ROOT, config.path), + result: 'skip', meta: { ciGroup: config.get('suiteTags.include').find((t: string) => t.startsWith('ciGroup')), tags: [ @@ -105,10 +107,6 @@ export function setupCiStatsFtrTestGroupReporter({ type, error: error?.stack, stdout: getSnapshotOfRunnableLogs(runnable), - screenshots: testMetadata.getScreenshots(runnable).map((s) => ({ - base64Png: s.base64Png, - name: s.name, - })), }); } @@ -117,6 +115,11 @@ export function setupCiStatsFtrTestGroupReporter({ errors.set(test, error); }); + let passCount = 0; + let failCount = 0; + runner.on('pass', () => (passCount += 1)); + runner.on('fail', () => (failCount += 1)); + runner.on('hook end', (hook: Runnable) => { if (hook.isFailed()) { const error = errors.get(hook); @@ -150,6 +153,7 @@ export function setupCiStatsFtrTestGroupReporter({ // update the durationMs group.durationMs = Date.now() - startMs; + group.result = failCount ? 'fail' : passCount ? 'pass' : 'skip'; }); lifecycle.cleanup.add(async () => { diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/reporter/reporter.js b/packages/kbn-test/src/functional_test_runner/lib/mocha/reporter/reporter.js index 973a552ebb728..66a4c9ce4fd04 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/reporter/reporter.js +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/reporter/reporter.js @@ -24,7 +24,6 @@ export function MochaReporterProvider({ getService }) { const log = getService('log'); const config = getService('config'); const lifecycle = getService('lifecycle'); - const testMetadata = getService('testMetadata'); let originalLogWriters; let reporterCaptureStartTime; @@ -61,7 +60,6 @@ export function MochaReporterProvider({ getService }) { config, lifecycle, runner, - testMetadata, }); } } diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/run_tests.ts b/packages/kbn-test/src/functional_test_runner/lib/mocha/run_tests.ts index 89f0ea088cac8..12840b77dd8d9 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/run_tests.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/run_tests.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import * as Rx from 'rxjs'; import { Lifecycle } from '../lifecycle'; import { Mocha } from '../../fake_mocha_types'; @@ -18,14 +19,23 @@ import { Mocha } from '../../fake_mocha_types'; * @param {Mocha} mocha * @return {Promise} resolves to the number of test failures */ -export async function runTests(lifecycle: Lifecycle, mocha: Mocha) { +export async function runTests(lifecycle: Lifecycle, mocha: Mocha, abortSignal?: AbortSignal) { let runComplete = false; const runner = mocha.run(() => { runComplete = true; }); - lifecycle.cleanup.add(() => { - if (!runComplete) runner.abort(); + Rx.race( + lifecycle.cleanup.before$, + abortSignal ? Rx.fromEvent(abortSignal, 'abort').pipe(Rx.take(1)) : Rx.NEVER + ).subscribe({ + next() { + if (!runComplete) { + runComplete = true; + runner.uncaught(new Error('Forcing mocha to abort')); + runner.abort(); + } + }, }); return new Promise((resolve) => { diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/validate_ci_group_tags.js b/packages/kbn-test/src/functional_test_runner/lib/mocha/validate_ci_group_tags.js index 4f798839d7231..a0298b635a135 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/validate_ci_group_tags.js +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/validate_ci_group_tags.js @@ -48,8 +48,10 @@ export function validateCiGroupTags(log, mocha) { const queue = [mocha.suite]; while (queue.length) { const suite = queue.shift(); - if (getCiGroups(suite).length > 1) { - suitesWithMultipleCiGroups.push(suite); + if (getCiGroups(suite).length) { + throw new Error( + 'ciGroups are no longer needed and should be removed. If you need to split up your FTR config because it is taking too long to complete then create one or more a new FTR config files and split your test files amoungst them' + ); } else { queue.push(...(suite.suites ?? [])); } diff --git a/packages/kbn-test/src/functional_test_runner/lib/providers/index.ts b/packages/kbn-test/src/functional_test_runner/lib/providers/index.ts index 578e41ca8e827..c0b85370d321c 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/providers/index.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/providers/index.ts @@ -7,6 +7,6 @@ */ export { ProviderCollection } from './provider_collection'; -export { readProviderSpec } from './read_provider_spec'; +export * from './read_provider_spec'; export { createAsyncInstance } from './async_instance'; export type { Provider } from './read_provider_spec'; diff --git a/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts index c199dfc092789..403708b893db8 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts @@ -6,11 +6,14 @@ * Side Public License, v 1. */ +import path from 'path'; +import fs from 'fs'; + +import { ToolingLog } from '@kbn/tooling-log'; + import { Suite, Test } from '../../fake_mocha_types'; import { Lifecycle } from '../lifecycle'; import { decorateSnapshotUi, expectSnapshot } from './decorate_snapshot_ui'; -import path from 'path'; -import fs from 'fs'; const createRootSuite = () => { const suite = { @@ -65,7 +68,7 @@ describe('decorateSnapshotUi', () => { let lifecycle: Lifecycle; let rootSuite: Suite; beforeEach(async () => { - lifecycle = new Lifecycle(); + lifecycle = new Lifecycle(new ToolingLog()); rootSuite = createRootSuite(); decorateSnapshotUi({ lifecycle, updateSnapshots: false, isCi: false }); @@ -116,7 +119,7 @@ describe('decorateSnapshotUi', () => { let lifecycle: Lifecycle; let rootSuite: Suite; beforeEach(async () => { - lifecycle = new Lifecycle(); + lifecycle = new Lifecycle(new ToolingLog()); rootSuite = createRootSuite(); decorateSnapshotUi({ lifecycle, updateSnapshots: false, isCi: false }); @@ -162,7 +165,7 @@ exports[\`Test2 1\`] = \`"bar"\`; let lifecycle: Lifecycle; let rootSuite: Suite; beforeEach(async () => { - lifecycle = new Lifecycle(); + lifecycle = new Lifecycle(new ToolingLog()); rootSuite = createRootSuite(); decorateSnapshotUi({ lifecycle, updateSnapshots: true, isCi: false }); @@ -185,7 +188,7 @@ exports[\`Test2 1\`] = \`"bar"\`; fs.writeFileSync( snapshotFile, `// Jest Snapshot v1, https://goo.gl/fbAQLP - + exports[\`Test 1\`] = \`"foo"\`; `, { encoding: 'utf-8' } @@ -219,7 +222,7 @@ exports[\`Test2 1\`] = \`"bar"\`; let lifecycle: Lifecycle; let rootSuite: Suite; beforeEach(async () => { - lifecycle = new Lifecycle(); + lifecycle = new Lifecycle(new ToolingLog()); rootSuite = createRootSuite(); decorateSnapshotUi({ lifecycle, updateSnapshots: false, isCi: true }); diff --git a/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.test.ts b/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.test.ts index 53ce4c74c1388..43f1508ab7938 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.test.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.test.ts @@ -9,6 +9,8 @@ import fs from 'fs'; import { join, resolve } from 'path'; +import { ToolingLog } from '@kbn/tooling-log'; + jest.mock('fs'); jest.mock('@kbn/utils', () => { return { REPO_ROOT: '/dev/null/root' }; @@ -60,7 +62,7 @@ describe('SuiteTracker', () => { }; const runLifecycleWithMocks = async (mocks: Suite[], fn: (objs: any) => any = () => {}) => { - const lifecycle = new Lifecycle(); + const lifecycle = new Lifecycle(new ToolingLog()); const suiteTracker = SuiteTracker.startTracking( lifecycle, resolve(REPO_ROOT, MOCK_CONFIG_PATH) diff --git a/packages/kbn-test/src/functional_test_runner/lib/test_metadata.ts b/packages/kbn-test/src/functional_test_runner/lib/test_metadata.ts deleted file mode 100644 index 5789231f87044..0000000000000 --- a/packages/kbn-test/src/functional_test_runner/lib/test_metadata.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { Lifecycle } from './lifecycle'; - -export interface ScreenshotRecord { - name: string; - base64Png: string; - baselinePath?: string; - failurePath?: string; -} - -export class TestMetadata { - // mocha's global types mean we can't import Mocha or it will override the global jest types.............. - private currentRunnable?: any; - - constructor(lifecycle: Lifecycle) { - lifecycle.beforeEachRunnable.add((runnable) => { - this.currentRunnable = runnable; - }); - } - - addScreenshot(screenshot: ScreenshotRecord) { - this.currentRunnable._screenshots = (this.currentRunnable._screenshots || []).concat( - screenshot - ); - } - - getScreenshots(test: any): ScreenshotRecord[] { - if (!test || typeof test !== 'object' || !test._screenshots) { - return []; - } - - return test._screenshots.slice(); - } -} diff --git a/packages/kbn-test/src/functional_test_runner/public_types.ts b/packages/kbn-test/src/functional_test_runner/public_types.ts index 2d632b28d6e21..67adceaf22323 100644 --- a/packages/kbn-test/src/functional_test_runner/public_types.ts +++ b/packages/kbn-test/src/functional_test_runner/public_types.ts @@ -8,10 +8,10 @@ import type { ToolingLog } from '@kbn/tooling-log'; -import type { Config, Lifecycle, TestMetadata, DockerServersService, EsVersion } from './lib'; +import type { Config, Lifecycle, DockerServersService, EsVersion } from './lib'; import type { Test, Suite } from './fake_mocha_types'; -export { Lifecycle, Config, TestMetadata }; +export { Lifecycle, Config }; export interface AsyncInstance { /** @@ -56,9 +56,7 @@ export interface GenericFtrProviderContext< * Determine if a service is avaliable * @param serviceName */ - hasService( - serviceName: 'config' | 'log' | 'lifecycle' | 'testMetadata' | 'dockerServers' | 'esVersion' - ): true; + hasService(serviceName: 'config' | 'log' | 'lifecycle' | 'dockerServers' | 'esVersion'): true; hasService(serviceName: K): serviceName is K; hasService(serviceName: string): serviceName is Extract; @@ -71,7 +69,6 @@ export interface GenericFtrProviderContext< getService(serviceName: 'log'): ToolingLog; getService(serviceName: 'lifecycle'): Lifecycle; getService(serviceName: 'dockerServers'): DockerServersService; - getService(serviceName: 'testMetadata'): TestMetadata; getService(serviceName: 'esVersion'): EsVersion; getService(serviceName: T): ServiceMap[T]; diff --git a/packages/kbn-test/src/functional_tests/lib/index.ts b/packages/kbn-test/src/functional_tests/lib/index.ts index bf2cc43159526..2726192328bda 100644 --- a/packages/kbn-test/src/functional_tests/lib/index.ts +++ b/packages/kbn-test/src/functional_tests/lib/index.ts @@ -10,5 +10,5 @@ export { runKibanaServer } from './run_kibana_server'; export { runElasticsearch } from './run_elasticsearch'; export type { CreateFtrOptions, CreateFtrParams } from './run_ftr'; export { runFtr, hasTests, assertNoneExcluded } from './run_ftr'; -export { KIBANA_ROOT, KIBANA_FTR_SCRIPT, FUNCTIONAL_CONFIG_PATH, API_CONFIG_PATH } from './paths'; +export { KIBANA_ROOT, KIBANA_FTR_SCRIPT } from './paths'; export { runCli } from './run_cli'; diff --git a/packages/kbn-test/src/functional_tests/lib/paths.ts b/packages/kbn-test/src/functional_tests/lib/paths.ts index 37cd708de1e00..75a654fdfc513 100644 --- a/packages/kbn-test/src/functional_tests/lib/paths.ts +++ b/packages/kbn-test/src/functional_tests/lib/paths.ts @@ -19,6 +19,3 @@ export const KIBANA_EXEC = 'node'; export const KIBANA_EXEC_PATH = resolveRelative('scripts/kibana'); export const KIBANA_ROOT = REPO_ROOT; export const KIBANA_FTR_SCRIPT = resolve(KIBANA_ROOT, 'scripts/functional_test_runner'); -export const PROJECT_ROOT = resolve(__dirname, '../../../../../../'); -export const FUNCTIONAL_CONFIG_PATH = resolve(KIBANA_ROOT, 'test/functional/config'); -export const API_CONFIG_PATH = resolve(KIBANA_ROOT, 'test/api_integration/config'); diff --git a/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts b/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts index adbb18b5312d0..2ee9de4053fef 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts +++ b/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts @@ -17,6 +17,7 @@ interface RunElasticsearchOptions { log: ToolingLog; esFrom?: string; config: Config; + onEarlyExit?: (msg: string) => void; } interface CcsConfig { @@ -92,7 +93,8 @@ export async function runElasticsearch( async function startEsNode( log: ToolingLog, name: string, - config: EsConfig & { transportPort?: number } + config: EsConfig & { transportPort?: number }, + onEarlyExit?: (msg: string) => void ) { const cluster = createTestEsCluster({ clusterName: `cluster-${name}`, @@ -112,6 +114,7 @@ async function startEsNode( }, ], transportPort: config.transportPort, + onEarlyExit, }); await cluster.start(); diff --git a/packages/kbn-test/src/functional_tests/lib/run_ftr.ts b/packages/kbn-test/src/functional_tests/lib/run_ftr.ts index 6887a664d6657..b9945adbdfb56 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_ftr.ts +++ b/packages/kbn-test/src/functional_tests/lib/run_ftr.ts @@ -81,8 +81,8 @@ async function createFtr({ }; } -export async function assertNoneExcluded({ configPath, options }: CreateFtrParams) { - const { config, ftr } = await createFtr({ configPath, options }); +export async function assertNoneExcluded(params: CreateFtrParams) { + const { config, ftr } = await createFtr(params); if (config.get('testRunner')) { // tests with custom test runners are not included in this check @@ -90,23 +90,26 @@ export async function assertNoneExcluded({ configPath, options }: CreateFtrParam } const stats = await ftr.getTestStats(); + if (!stats) { + throw new Error('unable to get test stats'); + } if (stats.testsExcludedByTag.length > 0) { throw new CliError(` - ${stats.testsExcludedByTag.length} tests in the ${configPath} config + ${stats.testsExcludedByTag.length} tests in the ${params.configPath} config are excluded when filtering by the tags run on CI. Make sure that all suites are tagged with one of the following tags: - ${JSON.stringify(options.suiteTags)} + ${JSON.stringify(params.options.suiteTags)} - ${stats.testsExcludedByTag.join('\n - ')} `); } } -export async function runFtr({ configPath, options }: CreateFtrParams) { - const { ftr } = await createFtr({ configPath, options }); +export async function runFtr(params: CreateFtrParams, signal?: AbortSignal) { + const { ftr } = await createFtr(params); - const failureCount = await ftr.run(); + const failureCount = await ftr.run(signal); if (failureCount > 0) { throw new CliError( `${failureCount} functional test ${failureCount === 1 ? 'failure' : 'failures'}` @@ -114,13 +117,16 @@ export async function runFtr({ configPath, options }: CreateFtrParams) { } } -export async function hasTests({ configPath, options }: CreateFtrParams) { - const { ftr, config } = await createFtr({ configPath, options }); +export async function hasTests(params: CreateFtrParams) { + const { ftr, config } = await createFtr(params); if (config.get('testRunner')) { // configs with custom test runners are assumed to always have tests return true; } const stats = await ftr.getTestStats(); - return stats.testCount > 0; + if (!stats) { + throw new Error('unable to get test stats'); + } + return stats.nonSkippedTestCount > 0; } diff --git a/packages/kbn-test/src/functional_tests/lib/run_kibana_server.ts b/packages/kbn-test/src/functional_tests/lib/run_kibana_server.ts index 6305e522b3929..b5026d397139d 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_kibana_server.ts +++ b/packages/kbn-test/src/functional_tests/lib/run_kibana_server.ts @@ -31,18 +31,20 @@ export async function runKibanaServer({ procs, config, options, + onEarlyExit, }: { procs: ProcRunner; config: Config; options: { installDir?: string; extraKbnOpts?: string[] }; + onEarlyExit?: (msg: string) => void; }) { - const { installDir } = options; const runOptions = config.get('kbnTestServer.runOptions'); + const installDir = runOptions.alwaysUseSource ? undefined : options.installDir; const env = config.get('kbnTestServer.env'); await procs.run('kibana', { cmd: getKibanaCmd(installDir), - args: filterCliArgs(collectCliArgs(config, options)), + args: filterCliArgs(collectCliArgs(config, installDir, options.extraKbnOpts)), env: { FORCE_COLOR: 1, ...process.env, @@ -51,6 +53,7 @@ export async function runKibanaServer({ }, cwd: installDir || KIBANA_ROOT, wait: runOptions.wait, + onEarlyExit, }); } @@ -70,10 +73,7 @@ function getKibanaCmd(installDir?: string) { * passed, we run from source code. We also allow passing in extra * Kibana server options, so we tack those on here. */ -function collectCliArgs( - config: Config, - { installDir, extraKbnOpts }: { installDir?: string; extraKbnOpts?: string[] } -) { +function collectCliArgs(config: Config, installDir?: string, extraKbnOpts: string[] = []) { const buildArgs: string[] = config.get('kbnTestServer.buildArgs') || []; const sourceArgs: string[] = config.get('kbnTestServer.sourceArgs') || []; const serverArgs: string[] = config.get('kbnTestServer.serverArgs') || []; @@ -82,7 +82,7 @@ function collectCliArgs( serverArgs, (args) => (installDir ? args.filter((a: string) => a !== '--oss') : args), (args) => (installDir ? [...buildArgs, ...args] : [KIBANA_EXEC_PATH, ...sourceArgs, ...args]), - (args) => args.concat(extraKbnOpts || []) + (args) => args.concat(extraKbnOpts) ); } diff --git a/packages/kbn-test/src/functional_tests/tasks.ts b/packages/kbn-test/src/functional_tests/tasks.ts index 8116fa25650a1..33a49ae2c80d1 100644 --- a/packages/kbn-test/src/functional_tests/tasks.ts +++ b/packages/kbn-test/src/functional_tests/tasks.ts @@ -8,6 +8,7 @@ import { relative } from 'path'; import * as Rx from 'rxjs'; +import { setTimeout } from 'timers/promises'; import { startWith, switchMap, take } from 'rxjs/operators'; import { withProcRunner } from '@kbn/dev-utils'; import { ToolingLog } from '@kbn/tooling-log'; @@ -63,7 +64,7 @@ interface RunTestsParams extends CreateFtrOptions { assertNoneExcluded: boolean; } export async function runTests(options: RunTestsParams) { - if (!process.env.KBN_NP_PLUGINS_BUILT && !options.assertNoneExcluded) { + if (!process.env.CI && !options.assertNoneExcluded) { const log = options.createLogger(); log.warning('❗️❗️❗️'); log.warning('❗️❗️❗️'); @@ -91,38 +92,47 @@ export async function runTests(options: RunTestsParams) { return; } - log.write('--- determining which ftr configs to run'); - const configPathsWithTests: string[] = []; - for (const configPath of options.configs) { - log.info('testing', relative(REPO_ROOT, configPath)); - await log.indent(4, async () => { - if (await hasTests({ configPath, options: { ...options, log } })) { - configPathsWithTests.push(configPath); + for (const [i, configPath] of options.configs.entries()) { + await log.indent(0, async () => { + if (options.configs.length > 1) { + const progress = `${i + 1}/${options.configs.length}`; + log.write(`--- [${progress}] Running ${relative(REPO_ROOT, configPath)}`); } - }); - } - for (const [i, configPath] of configPathsWithTests.entries()) { - await log.indent(0, async () => { - const progress = `${i + 1}/${configPathsWithTests.length}`; - log.write(`--- [${progress}] Running ${relative(REPO_ROOT, configPath)}`); + if (!(await hasTests({ configPath, options: { ...options, log } }))) { + // just run the FTR, no Kibana or ES, which will quickly report a skipped test group to ci-stats and continue + await runFtr({ configPath, options: { ...options, log } }); + return; + } await withProcRunner(log, async (procs) => { const config = await readConfigFile(log, options.esVersion, configPath); + const abortCtrl = new AbortController(); + + const onEarlyExit = (msg: string) => { + log.error(msg); + abortCtrl.abort(); + }; let shutdownEs; try { if (process.env.TEST_ES_DISABLE_STARTUP !== 'true') { - shutdownEs = await runElasticsearch({ ...options, log, config }); + shutdownEs = await runElasticsearch({ ...options, log, config, onEarlyExit }); + if (abortCtrl.signal.aborted) { + return; + } + } + await runKibanaServer({ procs, config, options, onEarlyExit }); + if (abortCtrl.signal.aborted) { + return; } - await runKibanaServer({ procs, config, options }); - await runFtr({ configPath, options: { ...options, log } }); + await runFtr({ configPath, options: { ...options, log } }, abortCtrl.signal); } finally { try { const delay = config.get('kbnTestServer.delayShutdown'); if (typeof delay === 'number') { log.info('Delaying shutdown of Kibana for', delay, 'ms'); - await new Promise((r) => setTimeout(r, delay)); + await setTimeout(delay); } await procs.stop('kibana'); diff --git a/packages/kbn-test/src/jest/ci_stats_jest_reporter.ts b/packages/kbn-test/src/jest/ci_stats_jest_reporter.ts index 6294594ff6cf5..3ac4a64c1f3f7 100644 --- a/packages/kbn-test/src/jest/ci_stats_jest_reporter.ts +++ b/packages/kbn-test/src/jest/ci_stats_jest_reporter.ts @@ -39,6 +39,8 @@ export default class CiStatsJestReporter extends BaseReporter { private readonly reportName: string; private readonly rootDir: string; private startTime: number | undefined; + private passCount = 0; + private failCount = 0; private group: CiStatsReportTestsOptions['group'] | undefined; private readonly testRuns: CiStatsReportTestsOptions['testRuns'] = []; @@ -79,6 +81,7 @@ export default class CiStatsJestReporter extends BaseReporter { startTime: new Date(this.startTime).toJSON(), meta: {}, durationMs: 0, + result: 'skip', }; } @@ -89,6 +92,14 @@ export default class CiStatsJestReporter extends BaseReporter { let elapsedTime = 0; for (const t of testResult.testResults) { + const result = t.status === 'failed' ? 'fail' : t.status === 'passed' ? 'pass' : 'skip'; + + if (result === 'fail') { + this.failCount += 1; + } else if (result === 'pass') { + this.passCount += 1; + } + const startTime = new Date(testResult.perfStats.start + elapsedTime).toJSON(); elapsedTime += t.duration ?? 0; this.testRuns.push({ @@ -97,7 +108,7 @@ export default class CiStatsJestReporter extends BaseReporter { seq: this.testRuns.length + 1, file: Path.relative(this.rootDir, testResult.testFilePath), name: t.title, - result: t.status === 'failed' ? 'fail' : t.status === 'passed' ? 'pass' : 'skip', + result, suites: t.ancestorTitles, type: 'test', error: t.failureMessages.join('\n\n'), @@ -107,11 +118,12 @@ export default class CiStatsJestReporter extends BaseReporter { } async onRunComplete() { - if (!this.reporter || !this.group || !this.testRuns.length || !this.startTime) { + if (!this.reporter || !this.group || !this.startTime) { return; } this.group.durationMs = Date.now() - this.startTime; + this.group.result = this.failCount ? 'fail' : this.passCount ? 'pass' : 'skip'; await this.reporter.reportTests({ group: this.group, diff --git a/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts b/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts index 42766ad3f5a31..7a50e88b3396d 100644 --- a/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts +++ b/packages/kbn-test/src/jest/run_check_jest_configs_cli.ts @@ -17,14 +17,14 @@ import { getAllRepoRelativeBazelPackageDirs } from '@kbn/bazel-packages'; import { JestConfigs, CONFIG_NAMES } from './configs'; const unitTestingTemplate: string = `module.exports = { - preset: '@kbn/test', + preset: '@kbn/test/jest_node', rootDir: '{{{relToRoot}}}', roots: ['/{{{modulePath}}}'], }; `; const integrationTestingTemplate: string = `module.exports = { - preset: '@kbn/test/jest_integration', + preset: '@kbn/test/jest_integration_node', rootDir: '{{{relToRoot}}}', roots: ['/{{{modulePath}}}'], }; diff --git a/packages/kbn-test/src/jest/setup/polyfills.jsdom.js b/packages/kbn-test/src/jest/setup/polyfills.jsdom.js index ebe6178dbdd91..5857ca9b99b8a 100644 --- a/packages/kbn-test/src/jest/setup/polyfills.jsdom.js +++ b/packages/kbn-test/src/jest/setup/polyfills.jsdom.js @@ -14,7 +14,3 @@ require('whatwg-fetch'); if (!global.URL.hasOwnProperty('createObjectURL')) { Object.defineProperty(global.URL, 'createObjectURL', { value: () => '' }); } - -// Will be replaced with a better solution in EUI -// https://github.com/elastic/eui/issues/3713 -global._isJest = true; diff --git a/packages/shared-ux/avatar/solution/BUILD.bazel b/packages/shared-ux/avatar/solution/BUILD.bazel new file mode 100644 index 0000000000000..a253153cb9227 --- /dev/null +++ b/packages/shared-ux/avatar/solution/BUILD.bazel @@ -0,0 +1,146 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "solution" +PKG_REQUIRE_NAME = "@kbn/shared-ux-avatar-solution" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + "src/**/*.tsx", + "src/**/*.scss", + "src/**/*.mdx", + "src/**/*.svg", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ + "@npm//@elastic/eui", + "@npm//classnames", + "@npm//enzyme", + "@npm//react", + "@npm//url-loader", + "//packages/kbn-i18n-react", + "//packages/kbn-i18n", + "//packages/kbn-shared-ux-utility", +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "@npm//@elastic/eui", + "@npm//@storybook/addon-actions", + "@npm//@types/classnames", + "@npm//@types/enzyme", + "@npm//@types/jest", + "@npm//@types/node", + "@npm//@types/react", + "//packages/kbn-ambient-ui-types", + "//packages/kbn-i18n-react:npm_module_types", + "//packages/kbn-i18n:npm_module_types", + "//packages/kbn-shared-ux-utility:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, + additional_args = [ + "--copy-files", + "--quiet" + ], +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/shared-ux/avatar/solution/README.mdx b/packages/shared-ux/avatar/solution/README.mdx new file mode 100644 index 0000000000000..841274441f6ed --- /dev/null +++ b/packages/shared-ux/avatar/solution/README.mdx @@ -0,0 +1,26 @@ +--- +id: sharedUX/Components/KibanaSolutionAvatar +slug: /shared-ux/components/avatar-solution +title: Solution Avatar +summary: A wrapper around `EuiAvatar` tailored for use in Kibana solutions. +tags: ['shared-ux', 'component'] +date: 2022-05-04 +--- + +## Description + +A wrapper around `EuiAvatar` tailored for use in Kibana solutions. + +## Usage + +If using for a known solution, (e.g. one whose logo is in EUI as `logoSomeSolution`), you can simply set the `name` prop: + +```tsx + +``` + +If the name provided does not match a known solution, you *must* set the `iconType` prop: + +```tsx + +``` diff --git a/packages/shared-ux/avatar/solution/jest.config.js b/packages/shared-ux/avatar/solution/jest.config.js new file mode 100644 index 0000000000000..6ca49f67e1dd5 --- /dev/null +++ b/packages/shared-ux/avatar/solution/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/packages/shared-ux/avatar/solution'], +}; diff --git a/packages/shared-ux/avatar/solution/package.json b/packages/shared-ux/avatar/solution/package.json new file mode 100644 index 0000000000000..b0ec8ec947b09 --- /dev/null +++ b/packages/shared-ux/avatar/solution/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/shared-ux-avatar-solution", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/shared-ux/avatar/solution/src/__snapshots__/solution_avatar.test.tsx.snap b/packages/shared-ux/avatar/solution/src/__snapshots__/solution_avatar.test.tsx.snap new file mode 100644 index 0000000000000..f0666987e0f79 --- /dev/null +++ b/packages/shared-ux/avatar/solution/src/__snapshots__/solution_avatar.test.tsx.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`KibanaSolutionAvatar renders 1`] = ` + +`; + +exports[`KibanaSolutionAvatar renders 2`] = ` + +`; diff --git a/packages/kbn-shared-ux-components/src/solution_avatar/assets/texture.svg b/packages/shared-ux/avatar/solution/src/assets/texture.svg similarity index 100% rename from packages/kbn-shared-ux-components/src/solution_avatar/assets/texture.svg rename to packages/shared-ux/avatar/solution/src/assets/texture.svg diff --git a/packages/shared-ux/avatar/solution/src/index.tsx b/packages/shared-ux/avatar/solution/src/index.tsx new file mode 100644 index 0000000000000..c2c9613bab87d --- /dev/null +++ b/packages/shared-ux/avatar/solution/src/index.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { withSuspense } from '@kbn/shared-ux-utility'; + +export type { KibanaSolutionAvatarProps } from './solution_avatar'; + +/** + * The Lazily-loaded `KibanaSolutionAvatar` component. Consumers should use `React.Suspense` or + * the withSuspense` HOC to load this component. + */ +export const KibanaSolutionAvatarLazy = React.lazy(() => + import('./solution_avatar').then(({ KibanaSolutionAvatar }) => ({ + default: KibanaSolutionAvatar, + })) +); + +/** + * A `KibanaSolutionAvatar` component that is wrapped by the `withSuspense` HOC. This component can + * be used directly by consumers and will load the `KibanaPageTemplateSolutionNavAvatarLazy` component lazily with + * a predefined fallback and error boundary. + */ +export const KibanaSolutionAvatar = withSuspense(KibanaSolutionAvatarLazy); diff --git a/packages/kbn-shared-ux-components/src/solution_avatar/solution_avatar.scss b/packages/shared-ux/avatar/solution/src/solution_avatar.scss similarity index 100% rename from packages/kbn-shared-ux-components/src/solution_avatar/solution_avatar.scss rename to packages/shared-ux/avatar/solution/src/solution_avatar.scss diff --git a/packages/shared-ux/avatar/solution/src/solution_avatar.stories.tsx b/packages/shared-ux/avatar/solution/src/solution_avatar.stories.tsx new file mode 100644 index 0000000000000..b47ff7c837f24 --- /dev/null +++ b/packages/shared-ux/avatar/solution/src/solution_avatar.stories.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { KibanaSolutionAvatar, IconTypeProps, KnownSolutionProps } from './solution_avatar'; + +export default { + title: 'Solution Avatar', + description: 'A wrapper around EuiAvatar, specifically to stylize Elastic Solutions', +}; + +const argTypes = { + size: { + control: 'select', + options: ['s', 'm', 'l', 'xl', 'xxl'], + defaultValue: 'xxl', + }, +}; + +type KnownSolutionParams = Pick; + +export const SolutionAvatar = (params: KnownSolutionParams) => { + return ; +}; + +SolutionAvatar.argTypes = { + name: { + control: 'select', + options: ['Cloud', 'Elastic', 'Kibana', 'Observability', 'Security', 'Enterprise Search'], + defaultValue: 'Elastic', + }, + ...argTypes, +}; + +type IconTypeParams = Pick; + +export const IconTypeAvatar = (params: IconTypeParams) => { + return ; +}; + +IconTypeAvatar.argTypes = { + iconType: { + control: 'select', + options: [ + 'logoCloud', + 'logoElastic', + 'logoElasticsearch', + 'logoElasticStack', + 'logoKibana', + 'logoObservability', + 'logoSecurity', + 'logoSiteSearch', + 'logoWorkplaceSearch', + 'machineLearningApp', + 'managementApp', + ], + defaultValue: 'logoElastic', + }, + name: { + control: 'text', + defaultValue: 'Solution Name', + }, + ...argTypes, +}; diff --git a/packages/shared-ux/avatar/solution/src/solution_avatar.test.tsx b/packages/shared-ux/avatar/solution/src/solution_avatar.test.tsx new file mode 100644 index 0000000000000..ab7c675b24e0d --- /dev/null +++ b/packages/shared-ux/avatar/solution/src/solution_avatar.test.tsx @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { shallow } from 'enzyme'; +import { KibanaSolutionAvatar } from './solution_avatar'; + +describe('KibanaSolutionAvatar', () => { + test('renders', () => { + const nameAndIcon = shallow(); + expect(nameAndIcon).toMatchSnapshot(); + const nameOnly = shallow(); + expect(nameOnly).toMatchSnapshot(); + }); +}); diff --git a/packages/shared-ux/avatar/solution/src/solution_avatar.tsx b/packages/shared-ux/avatar/solution/src/solution_avatar.tsx new file mode 100644 index 0000000000000..0c38652a27395 --- /dev/null +++ b/packages/shared-ux/avatar/solution/src/solution_avatar.tsx @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 './solution_avatar.scss'; + +import React from 'react'; +import classNames from 'classnames'; + +import { DistributiveOmit, EuiAvatar, EuiAvatarProps, IconType } from '@elastic/eui'; + +import { SolutionNameType } from './types'; + +export type KnownSolutionProps = DistributiveOmit & { + /** + * Any EuiAvatar size available, or `xxl` for custom large, brand-focused version + */ + size?: EuiAvatarProps['size'] | 'xxl'; + name: SolutionNameType; +}; + +export type IconTypeProps = DistributiveOmit & { + /** + * Any EuiAvatar size available, or `xxl` for custom large, brand-focused version + */ + size?: EuiAvatarProps['size'] | 'xxl'; + name?: string; + iconType: IconType; +}; + +const isKnown = (props: any): props is KnownSolutionProps => { + return typeof props.iconType === 'undefined'; +}; + +export type KibanaSolutionAvatarProps = KnownSolutionProps | IconTypeProps; + +/** + * Applies extra styling to a typical EuiAvatar. + * The `name` value will be appended to 'logo' to configure the `iconType` unless `iconType` is provided. + */ +export const KibanaSolutionAvatar = (props: KibanaSolutionAvatarProps) => { + const { className, size, ...rest } = props; + + // If the name is a known solution, use the name to set the correct IconType. + // Create an empty object so `iconType` remains undefined or inherited from `props`. + const icon: { + iconType?: IconType; + } = {}; + + if (isKnown(props)) { + icon.iconType = `logo${props.name.replace(/\s+/g, '')}`; + } + + return ( + // @ts-ignore Complains about ExclusiveUnion between `iconSize` and `iconType`, but works fine + + ); +}; diff --git a/packages/shared-ux/avatar/solution/src/types.ts b/packages/shared-ux/avatar/solution/src/types.ts new file mode 100644 index 0000000000000..bf0ad682e3006 --- /dev/null +++ b/packages/shared-ux/avatar/solution/src/types.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +// Manual, exhaustive list at present. This was attempted dynamically using Typescript Template Literals and +// the computation cost exceeded the benefit. By enumerating them manually, we reduce the complexity of TS +// checking at the expense of not being dynamic against a very, very static list. +// +// The only consequence is requiring a solution name without a space, (e.g. `ElasticStack`) until it's added +// here. That's easy to do in the very unlikely event that ever happens. +export type SolutionNameType = + | 'App Search' + | 'Beats' + | 'Business Analytics' + | 'Cloud' + | 'Cloud Enterprise' + | 'Code' + | 'Elastic' + | 'Elastic Stack' + | 'Elasticsearch' + | 'Enterprise Search' + | 'Logstash' + | 'Maps' + | 'Metrics' + | 'Observability' + | 'Security' + | 'Site Search' + | 'Uptime' + | 'Webhook' + | 'Workplace Search'; diff --git a/packages/shared-ux/avatar/solution/tsconfig.json b/packages/shared-ux/avatar/solution/tsconfig.json new file mode 100644 index 0000000000000..93076efae5d7c --- /dev/null +++ b/packages/shared-ux/avatar/solution/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node", + "react", + "@kbn/ambient-ui-types" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/shared-ux/link/redirect_app/BUILD.bazel b/packages/shared-ux/link/redirect_app/BUILD.bazel new file mode 100644 index 0000000000000..861b9aa277db9 --- /dev/null +++ b/packages/shared-ux/link/redirect_app/BUILD.bazel @@ -0,0 +1,140 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "redirect_app" +PKG_REQUIRE_NAME = "@kbn/shared-ux-link-redirect-app" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + "src/**/*.tsx", + "src/**/*.mdx", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ + "@npm//@elastic/eui", + "@npm//@storybook/addon-actions", + "@npm//react-use", + "@npm//react", + "@npm//rxjs", + "//packages/kbn-shared-ux-utility", +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "@npm//@elastic/eui", + "@npm//@storybook/addon-actions", + "@npm//@types/jest", + "@npm//@types/node", + "@npm//@types/react", + "@npm//rxjs", + "@npm//react-use", + "//packages/kbn-ambient-ui-types", + "//packages/kbn-shared-ux-utility:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, + additional_args = [ + "--copy-files", + "--quiet" + ], +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/shared-ux/link/redirect_app/README.mdx b/packages/shared-ux/link/redirect_app/README.mdx new file mode 100644 index 0000000000000..8e2eada760ea2 --- /dev/null +++ b/packages/shared-ux/link/redirect_app/README.mdx @@ -0,0 +1,86 @@ +--- +id: sharedUX/Components/AppLink +slug: /shared-ux/components/redirect-app-links +title: Redirect App Links +summary: A component for redirecting links contained within it to the appropriate Kibana solution without a page refresh. +tags: ['shared-ux', 'component'] +date: 2022-05-04 +--- + +## Description + +This component is an "area of effect" component, which produces a container that intercepts actions for specific elements within it. In this case, the container intercepts clicks on anchor elements and redirects them to Kibana solutions without a page refresh. + +## Pure Component + +The pure component allows you create a container to intercept clicks without contextual services, (e.g. Kibana Core). This likely does not have much utility for solutions in Kibana, but rather is useful for shared components where we want to ensure clicks are redirected correctly. + +```tsx +import { RedirectAppLinksComponent as RedirectAppLinks } from '@kbn/shared-ux-links-redirect-app'; + + { ... }}> + Go to another-app + +``` + +## Connected Component + +The connected component uses a React Context to access services that provide the current app id and a function to navigate to a new url. This is useful in that a solution can wrap their entire application in the context and use `RedirectAppLinks` in specific areas. + +```tsx +import { RedirectAppLinksContainer as RedirectAppLinks, RedirectAppLinksProvider } from '@kbn/shared-ux-links-redirect-app'; + + { ... }}> + . + {/* other components that don't need to redirect */} + . + + Go to another-app + + . + . + . + +``` + +You can also use the Kibana provider: + +```tsx +import { + RedirectAppLinksContainer as RedirectAppLinks, + RedirectAppLinksKibanaProvider as RedirectAppLinksProvider +} from '@kbn/shared-ux-links-redirect-app'; + + + . + {/* other components that don't need to redirect */} + . + + Go to another-app + + . + . + +``` + +## Top-level Component + +This is the component is likely the most useful to solutions in Kibana. It assumes an entire solution needs this redirect functionality, and combines the context provider with the container. This top-level component can be used with either pure props or with Kibana services. + +```tsx +import { RedirectAppLinks } from '@kbn/shared-ux-links-redirect-app'; + + { ... }}> + . + Go to another-app + . + + +{/* OR */} + + + . + Go to another-app + . + +``` \ No newline at end of file diff --git a/packages/shared-ux/link/redirect_app/jest.config.js b/packages/shared-ux/link/redirect_app/jest.config.js new file mode 100644 index 0000000000000..5f564a9709d0c --- /dev/null +++ b/packages/shared-ux/link/redirect_app/jest.config.js @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/packages/shared-ux/link/redirect_app'], + verbose: true, +}; diff --git a/packages/shared-ux/link/redirect_app/package.json b/packages/shared-ux/link/redirect_app/package.json new file mode 100644 index 0000000000000..6deb187dcec2a --- /dev/null +++ b/packages/shared-ux/link/redirect_app/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/shared-ux-link-redirect-app", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/kbn-shared-ux-components/src/redirect_app_links/click_handler.test.ts b/packages/shared-ux/link/redirect_app/src/click_handler.test.ts similarity index 84% rename from packages/kbn-shared-ux-components/src/redirect_app_links/click_handler.test.ts rename to packages/shared-ux/link/redirect_app/src/click_handler.test.ts index dd26443eed171..c46b93bb67aaf 100644 --- a/packages/kbn-shared-ux-components/src/redirect_app_links/click_handler.test.ts +++ b/packages/shared-ux/link/redirect_app/src/click_handler.test.ts @@ -7,7 +7,7 @@ */ import { MouseEvent } from 'react'; -import { createNavigateToUrlClickHandler } from './click_handler'; +import { navigateToUrlClickHandler } from './click_handler'; const createLink = ({ href = '/base-path/app/targetApp', @@ -43,27 +43,59 @@ const createEvent = ({ type NavigateToURLFn = (url: string) => Promise; -describe('createNavigateToUrlClickHandler', () => { +describe('navigateToUrlClickHandler', () => { let container: HTMLElement; let navigateToUrl: jest.MockedFunction; + const currentAppId = 'abc123'; - const createHandler = () => - createNavigateToUrlClickHandler({ + const handler = (event: MouseEvent): void => { + navigateToUrlClickHandler({ + event, + currentAppId, container, navigateToUrl, }); + }; beforeEach(() => { container = document.createElement('div'); navigateToUrl = jest.fn(); }); - it('calls `navigateToUrl` with the link url', () => { - const handler = createHandler(); + it("doesn't call `navigateToUrl` without a container", () => { + const event = createEvent({ + target: createLink({ href: '/base-path/app/targetApp' }), + }); + navigateToUrlClickHandler({ + event, + currentAppId, + container: null, + navigateToUrl, + }); + + expect(event.preventDefault).toHaveBeenCalledTimes(0); + }); + + it("doesn't call `navigateToUrl` without a `currentAppId`", () => { const event = createEvent({ target: createLink({ href: '/base-path/app/targetApp' }), }); + + navigateToUrlClickHandler({ + event, + container, + navigateToUrl, + }); + + expect(event.preventDefault).toHaveBeenCalledTimes(0); + }); + + it('calls `navigateToUrl` with the link url', () => { + const event = createEvent({ + target: createLink({ href: '/base-path/app/targetApp' }), + }); + handler(event); expect(event.preventDefault).toHaveBeenCalledTimes(1); @@ -71,13 +103,12 @@ describe('createNavigateToUrlClickHandler', () => { }); it('is triggered if a non-link target has a parent link', () => { - const handler = createHandler(); - const link = createLink(); const target = document.createElement('span'); link.appendChild(target); const event = createEvent({ target }); + handler(event); expect(event.preventDefault).toHaveBeenCalledTimes(1); @@ -85,13 +116,12 @@ describe('createNavigateToUrlClickHandler', () => { }); it('is not triggered if a non-link target has no parent link', () => { - const handler = createHandler(); - const parent = document.createElement('div'); const target = document.createElement('span'); parent.appendChild(target); const event = createEvent({ target }); + handler(event); expect(event.preventDefault).not.toHaveBeenCalled(); @@ -99,11 +129,10 @@ describe('createNavigateToUrlClickHandler', () => { }); it('is not triggered when the link has no href', () => { - const handler = createHandler(); - const event = createEvent({ target: createLink({ href: '' }), }); + handler(event); expect(event.preventDefault).not.toHaveBeenCalled(); @@ -111,11 +140,10 @@ describe('createNavigateToUrlClickHandler', () => { }); it('is only triggered when the link does not have an external target', () => { - const handler = createHandler(); - let event = createEvent({ target: createLink({ target: '_blank' }), }); + handler(event); expect(event.preventDefault).not.toHaveBeenCalled(); @@ -124,6 +152,7 @@ describe('createNavigateToUrlClickHandler', () => { event = createEvent({ target: createLink({ target: 'some-target' }), }); + handler(event); expect(event.preventDefault).not.toHaveBeenCalled(); @@ -132,6 +161,7 @@ describe('createNavigateToUrlClickHandler', () => { event = createEvent({ target: createLink({ target: '_self' }), }); + handler(event); expect(event.preventDefault).toHaveBeenCalledTimes(1); @@ -140,6 +170,7 @@ describe('createNavigateToUrlClickHandler', () => { event = createEvent({ target: createLink({ target: '' }), }); + handler(event); expect(event.preventDefault).toHaveBeenCalledTimes(1); @@ -147,11 +178,10 @@ describe('createNavigateToUrlClickHandler', () => { }); it('is only triggered from left clicks', () => { - const handler = createHandler(); - let event = createEvent({ button: 1, }); + handler(event); expect(event.preventDefault).not.toHaveBeenCalled(); @@ -160,6 +190,7 @@ describe('createNavigateToUrlClickHandler', () => { event = createEvent({ button: 12, }); + handler(event); expect(event.preventDefault).not.toHaveBeenCalled(); @@ -168,6 +199,7 @@ describe('createNavigateToUrlClickHandler', () => { event = createEvent({ button: 0, }); + handler(event); expect(event.preventDefault).toHaveBeenCalledTimes(1); @@ -175,11 +207,10 @@ describe('createNavigateToUrlClickHandler', () => { }); it('is not triggered if the event default is prevented', () => { - const handler = createHandler(); - let event = createEvent({ defaultPrevented: true, }); + handler(event); expect(event.preventDefault).not.toHaveBeenCalled(); @@ -188,6 +219,7 @@ describe('createNavigateToUrlClickHandler', () => { event = createEvent({ defaultPrevented: false, }); + handler(event); expect(event.preventDefault).toHaveBeenCalledTimes(1); @@ -195,15 +227,15 @@ describe('createNavigateToUrlClickHandler', () => { }); it('is not triggered if any modifier key is pressed', () => { - const handler = createHandler(); - let event = createEvent({ modifierKey: true }); + handler(event); expect(event.preventDefault).not.toHaveBeenCalled(); expect(navigateToUrl).not.toHaveBeenCalled(); event = createEvent({ modifierKey: false }); + handler(event); expect(event.preventDefault).toHaveBeenCalledTimes(1); diff --git a/packages/shared-ux/link/redirect_app/src/click_handler.ts b/packages/shared-ux/link/redirect_app/src/click_handler.ts new file mode 100644 index 0000000000000..8c94aa0033f2b --- /dev/null +++ b/packages/shared-ux/link/redirect_app/src/click_handler.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { MouseEvent } from 'react'; +import { getClosestLink, hasActiveModifierKey } from '@kbn/shared-ux-utility'; +import { NavigateToUrl } from './types'; + +interface CreateCrossAppClickHandlerOptions { + event: MouseEvent; + navigateToUrl: NavigateToUrl; + container: HTMLElement | null; + currentAppId?: string; +} + +/** + * Constructs a click handler that will redirect the user using `navigateToUrl` if the + * correct conditions are met. + */ +export const navigateToUrlClickHandler = ({ + event, + container, + navigateToUrl, + currentAppId, +}: CreateCrossAppClickHandlerOptions) => { + if (!container || !currentAppId) { + return; + } + + // see https://github.com/DefinitelyTyped/DefinitelyTyped/pull/12239 + const target = event.target as HTMLElement; + + const link = getClosestLink(target, container); + + if (!link) { + return; + } + + const isNotEmptyHref = link.href; + const hasNoTarget = link.target === '' || link.target === '_self'; + const isLeftClickOnly = event.button === 0; + + if ( + isNotEmptyHref && + hasNoTarget && + isLeftClickOnly && + !event.defaultPrevented && + !hasActiveModifierKey(event) + ) { + event.preventDefault(); + navigateToUrl(link.href); + } +}; diff --git a/packages/shared-ux/link/redirect_app/src/index.tsx b/packages/shared-ux/link/redirect_app/src/index.tsx new file mode 100644 index 0000000000000..5efb99cc48664 --- /dev/null +++ b/packages/shared-ux/link/redirect_app/src/index.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { RedirectAppLinks as RedirectAppLinksContainer } from './redirect_app_links'; +export { RedirectAppLinks as RedirectAppLinksComponent } from './redirect_app_links'; +export { RedirectAppLinksKibanaProvider, RedirectAppLinksProvider } from './services'; + +import React, { FC } from 'react'; +import { RedirectAppLinks as RedirectAppLinksContainer } from './redirect_app_links'; +import { + Services, + KibanaServices, + RedirectAppLinksKibanaProvider, + RedirectAppLinksProvider, +} from './services'; + +const isKibanaContract = (services: any): services is KibanaServices => { + return typeof services.coreStart !== 'undefined'; +}; + +/** + * This component composes `RedirectAppLinksContainer` with either `RedirectAppLinksProvider` or + * `RedirectAppLinksKibanaProvider` based on the services provided, creating a single component + * with which consumers can wrap their components or solutions. + */ +export const RedirectAppLinks: FC = ({ children, ...services }) => { + const container = {children}; + + return isKibanaContract(services) ? ( + {container} + ) : ( + {container} + ); +}; diff --git a/packages/shared-ux/link/redirect_app/src/redirect_app_links.component.tsx b/packages/shared-ux/link/redirect_app/src/redirect_app_links.component.tsx new file mode 100644 index 0000000000000..477471fe71824 --- /dev/null +++ b/packages/shared-ux/link/redirect_app/src/redirect_app_links.component.tsx @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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, { useRef, MouseEventHandler, useCallback } from 'react'; +import type { HTMLAttributes, DetailedHTMLProps, FC } from 'react'; + +import { navigateToUrlClickHandler } from './click_handler'; +import { NavigateToUrl } from './types'; + +export interface Props extends DetailedHTMLProps, HTMLDivElement> { + navigateToUrl: NavigateToUrl; + currentAppId?: string | undefined; +} + +/** + * Utility component that will intercept click events on children anchor (``) elements to call + * `navigateToUrl` with the link's href. This will trigger SPA friendly navigation when the link points + * to a valid Kibana app. + * + * @example + * ```tsx + * { ... }}> + * Go to another-app + * + * ``` + */ +export const RedirectAppLinks: FC = ({ + children, + navigateToUrl, + currentAppId, + ...otherProps +}) => { + const containerRef = useRef(null); + + const handleClick: MouseEventHandler = useCallback( + (event) => + navigateToUrlClickHandler({ + event, + currentAppId, + navigateToUrl, + container: containerRef.current, + }), + [currentAppId, navigateToUrl] + ); + + return ( + // eslint-disable-next-line jsx-a11y/click-events-have-key-events +
+ {children} +
+ ); +}; diff --git a/packages/shared-ux/link/redirect_app/src/redirect_app_links.stories.tsx b/packages/shared-ux/link/redirect_app/src/redirect_app_links.stories.tsx new file mode 100644 index 0000000000000..9bb3d0d9782d4 --- /dev/null +++ b/packages/shared-ux/link/redirect_app/src/redirect_app_links.stories.tsx @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React from 'react'; + +import { action } from '@storybook/addon-actions'; +import { RedirectAppLinks } from '.'; +import mdx from '../README.mdx'; + +export default { + title: 'Redirect App Links', + description: + 'An "area of effect" component which intercepts clicks on anchor elements and redirects them to Kibana solutions without a page refresh.', + parameters: { + docs: { + page: mdx, + }, + }, +}; + +export const Component = () => { + const navigateToUrl = async (url: string) => { + action('navigateToUrl')(url); + }; + + const currentAppId = 'abc123'; + + return ( + <> + + + + + Button with URL + + + + + Button without URL + + + + + + + + Button outside RedirectAppLinks + + + + + ); +}; diff --git a/packages/shared-ux/link/redirect_app/src/redirect_app_links.test.tsx b/packages/shared-ux/link/redirect_app/src/redirect_app_links.test.tsx new file mode 100644 index 0000000000000..1bb3875aec7ae --- /dev/null +++ b/packages/shared-ux/link/redirect_app/src/redirect_app_links.test.tsx @@ -0,0 +1,292 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable jsx-a11y/click-events-have-key-events */ +import React, { MouseEvent } from 'react'; +import { mount as enzymeMount, ReactWrapper } from 'enzyme'; + +import { RedirectAppLinksKibanaProvider, RedirectAppLinksProvider } from './services'; +import { RedirectAppLinks } from './redirect_app_links'; +import { RedirectAppLinks as ComposedWrapper } from '.'; +import { Observable } from 'rxjs'; + +export type UnmountCallback = () => void; +export type MountPoint = (element: T) => UnmountCallback; +type Mount = ( + node: React.ReactElement +) => ReactWrapper, React.Component<{}, {}, any>>; + +const commonTests = (name: string, mount: Mount, navigateToUrl: jest.Mock) => { + beforeEach(() => { + navigateToUrl.mockReset(); + }); + + describe(`RedirectAppLinks with ${name}`, () => { + it('intercept click events on children link elements', () => { + let event: MouseEvent; + const component = mount( +
{ + event = e; + }} + > + +
+ content +
+
+
+ ); + + component.find('a').simulate('click', { button: 0, defaultPrevented: false }); + expect(navigateToUrl).toHaveBeenCalledTimes(1); + expect(event!.defaultPrevented).toBe(true); + }); + + it('intercept click events on children inside link elements', async () => { + let event: MouseEvent; + + const component = mount( +
{ + event = e; + }} + > + + + +
+ ); + + component.find('span').simulate('click', { button: 0, defaultPrevented: false }); + + expect(navigateToUrl).toHaveBeenCalledTimes(1); + expect(event!.defaultPrevented).toBe(true); + }); + + it('does not intercept click events when the target is not inside a link', () => { + let event: MouseEvent; + + const component = mount( +
{ + event = e; + }} + > + + + content + + +
+ ); + + component.find('span').simulate('click', { button: 0, defaultPrevented: false }); + + expect(navigateToUrl).not.toHaveBeenCalled(); + expect(event!.defaultPrevented).toBe(false); + }); + + it('does not intercept click events when the link has an external target', () => { + let event: MouseEvent; + + const component = mount( +
{ + event = e; + }} + > + + + content + + +
+ ); + + component.find('a').simulate('click', { button: 0, defaultPrevented: false }); + + expect(navigateToUrl).not.toHaveBeenCalled(); + expect(event!.defaultPrevented).toBe(false); + }); + + it('does not intercept click events when the event is already defaultPrevented', () => { + let event: MouseEvent; + + const component = mount( +
{ + event = e; + }} + > + + + e.preventDefault()}>content + + +
+ ); + + component.find('span').simulate('click', { button: 0, defaultPrevented: false }); + + expect(navigateToUrl).not.toHaveBeenCalled(); + expect(event!.defaultPrevented).toBe(true); + }); + + it('does not intercept click events when the event propagation is stopped', () => { + let event: MouseEvent; + + const component = mount( +
{ + event = e; + }} + > + + e.stopPropagation()}> + content + + +
+ ); + + component.find('a').simulate('click', { button: 0, defaultPrevented: false }); + + expect(navigateToUrl).not.toHaveBeenCalled(); + expect(event!).toBe(undefined); + }); + + it('does not intercept click events when the event is not triggered from the left button', () => { + let event: MouseEvent; + + const component = mount( +
{ + event = e; + }} + > + +
+ content +
+
+
+ ); + + component.find('a').simulate('click', { button: 1, defaultPrevented: false }); + + expect(navigateToUrl).not.toHaveBeenCalled(); + expect(event!.defaultPrevented).toBe(false); + }); + + it('does not intercept click events when the event has a modifier key enabled', () => { + let event: MouseEvent; + + const component = mount( +
{ + event = e; + }} + > + +
+ content +
+
+
+ ); + + component.find('a').simulate('click', { button: 0, ctrlKey: true, defaultPrevented: false }); + + expect(navigateToUrl).not.toHaveBeenCalled(); + expect(event!.defaultPrevented).toBe(false); + }); + }); +}; + +const targetedTests = (name: string, mount: Mount, navigateToUrl: jest.Mock) => { + beforeEach(() => { + navigateToUrl.mockReset(); + }); + + describe(`${name} with isolated areas of effect`, () => { + it(`does not intercept click events when the link is a parent of the container`, () => { + let event: MouseEvent; + + const component = mount( +
{ + event = e; + }} + > + + + content + + +
+ ); + + component.find('span').simulate('click', { button: 0, defaultPrevented: false }); + + expect(navigateToUrl).not.toHaveBeenCalled(); + expect(event!.defaultPrevented).toBe(false); + }); + }); +}; + +describe('RedirectAppLinks', () => { + const navigateToUrl = jest.fn(); + + beforeEach(() => { + navigateToUrl.mockReset(); + }); + + const kibana = { + coreStart: { + application: { + currentAppId$: new Observable((subscriber) => { + subscriber.next('123'); + }), + navigateToUrl, + }, + }, + }; + + const services = { + currentAppId: 'abc123', + navigateToUrl, + }; + + const provider = (node: React.ReactElement) => + enzymeMount({node}); + + const kibanaProvider = (node: React.ReactElement) => + enzymeMount( + {node} + ); + + const composedProvider = (node: React.ReactElement) => + enzymeMount({node}); + + const composedKibanaProvider = (node: React.ReactElement) => + enzymeMount({node}); + + describe('Test all Providers', () => { + commonTests('RedirectAppLinksProvider', provider, navigateToUrl); + targetedTests('RedirectAppLinksProvider', provider, navigateToUrl); + commonTests('RedirectAppLinksKibanaProvider', kibanaProvider, navigateToUrl); + targetedTests('RedirectAppLinksKibanaProvider', kibanaProvider, navigateToUrl); + commonTests('Provider Props', composedProvider, navigateToUrl); + commonTests('Kibana Props', composedKibanaProvider, navigateToUrl); + }); +}); diff --git a/packages/shared-ux/link/redirect_app/src/redirect_app_links.tsx b/packages/shared-ux/link/redirect_app/src/redirect_app_links.tsx new file mode 100644 index 0000000000000..1e805ad4475b6 --- /dev/null +++ b/packages/shared-ux/link/redirect_app/src/redirect_app_links.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { useServices } from './services'; +import { + RedirectAppLinks as Component, + Props as ComponentProps, +} from './redirect_app_links.component'; + +type Props = Omit; + +/** + * A service-enabled component that provides Kibana-specific functionality to the `RedirectAppLinks` + * pure component. + * + * @example + * ```tsx + * + * Go to another-app + * + * ``` + */ +export const RedirectAppLinks = (props: Props) => ; diff --git a/packages/shared-ux/link/redirect_app/src/services.tsx b/packages/shared-ux/link/redirect_app/src/services.tsx new file mode 100644 index 0000000000000..22bc5a5cd0c55 --- /dev/null +++ b/packages/shared-ux/link/redirect_app/src/services.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { FC, useContext } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import { Observable } from 'rxjs'; +import { NavigateToUrl } from './types'; + +/** + * Contextual services for this component. + */ +export interface Services { + navigateToUrl: NavigateToUrl; + currentAppId?: string; +} + +const RedirectAppLinksContext = React.createContext(null); + +/** + * Contextual services Provider. + */ +export const RedirectAppLinksProvider: FC = ({ children, ...services }) => { + return ( + + {children} + + ); +}; + +/** + * Kibana-specific contextual services to be adapted for this component. + */ +export interface KibanaServices { + coreStart: { + application: { + currentAppId$: Observable; + navigateToUrl: NavigateToUrl; + }; + }; +} + +/** + * Kibana-specific contextual services Provider. + */ +export const RedirectAppLinksKibanaProvider: FC = ({ children, coreStart }) => { + const { navigateToUrl, currentAppId$ } = coreStart.application; + const currentAppId = useObservable(currentAppId$, undefined); + + return ( + + {children} + + ); +}; + +/** + * React hook for accessing pre-wired services. + */ +export function useServices() { + const context = useContext(RedirectAppLinksContext); + + if (!context) { + throw new Error( + 'RedirectAppLinksContext is missing. Ensure your component or React root is wrapped with RedirectAppLinksProvider.' + ); + } + + return context; +} diff --git a/packages/shared-ux/link/redirect_app/src/types.ts b/packages/shared-ux/link/redirect_app/src/types.ts new file mode 100644 index 0000000000000..2c27ccde84d67 --- /dev/null +++ b/packages/shared-ux/link/redirect_app/src/types.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type NavigateToUrl = (url: string) => Promise | void; diff --git a/packages/shared-ux/link/redirect_app/tsconfig.json b/packages/shared-ux/link/redirect_app/tsconfig.json new file mode 100644 index 0000000000000..93076efae5d7c --- /dev/null +++ b/packages/shared-ux/link/redirect_app/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node", + "react", + "@kbn/ambient-ui-types" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/shared-ux/page/analytics_no_data/BUILD.bazel b/packages/shared-ux/page/analytics_no_data/BUILD.bazel new file mode 100644 index 0000000000000..ad687fe8a220b --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/BUILD.bazel @@ -0,0 +1,139 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "analytics_no_data" +PKG_REQUIRE_NAME = "@kbn/shared-ux-page-analytics-no-data" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + "src/**/*.tsx", + "src/**/*.mdx", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ + "@npm//react", + "@npm//rxjs", + "//packages/kbn-i18n", + "//packages/kbn-shared-ux-services", + "//packages/kbn-shared-ux-components", + "//packages/kbn-shared-ux-storybook" +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//@types/react", + "//packages/kbn-i18n:npm_module_types", + "//packages/kbn-shared-ux-services:npm_module_types", + "//packages/kbn-shared-ux-storybook:npm_module_types", + "//packages/kbn-shared-ux-components:npm_module_types", + "//packages/kbn-ambient-ui-types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, + additional_args = [ + "--copy-files", + "--quiet" + ], +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/shared-ux/page/analytics_no_data/README.mdx b/packages/shared-ux/page/analytics_no_data/README.mdx new file mode 100644 index 0000000000000..ab8cf8d1cb063 --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/README.mdx @@ -0,0 +1,16 @@ +--- +id: sharedUX/Components/AnalyticsNoDataPage +slug: /shared-ux/components/analytics-no-data-page +title: Analytics "No Data" Page +summary: An entire page that can be displayed when Kibana "has no data", specifically for Analytics. +tags: ['shared-ux', 'component'] +date: 2021-12-28 +--- + +## Description + +This is an Analytics-specific version of `KibanaNoDataPage`, which defaults most of the fields to give a consistent set of terms for Analytics solutions. + +## EUI Promotion Status + +This component is not currently considered for promotion to EUI. diff --git a/packages/shared-ux/page/analytics_no_data/jest.config.js b/packages/shared-ux/page/analytics_no_data/jest.config.js new file mode 100644 index 0000000000000..76067f82881f7 --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/packages/shared-ux/page/analytics_no_data'], +}; diff --git a/packages/shared-ux/page/analytics_no_data/package.json b/packages/shared-ux/page/analytics_no_data/package.json new file mode 100644 index 0000000000000..e9977444fb94e --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/shared-ux-page-analytics-no-data", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/shared-ux/page/analytics_no_data/src/__snapshots__/analytics_no_data_page.component.test.tsx.snap b/packages/shared-ux/page/analytics_no_data/src/__snapshots__/analytics_no_data_page.component.test.tsx.snap new file mode 100644 index 0000000000000..be6fd3c45744e --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/src/__snapshots__/analytics_no_data_page.component.test.tsx.snap @@ -0,0 +1,157 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AnalyticsNoDataPageComponent renders correctly 1`] = ` + + + + } + > + +
+ + + +
+
+
+
+
+
+`; diff --git a/packages/shared-ux/page/analytics_no_data/src/analytics_no_data_page.component.test.tsx b/packages/shared-ux/page/analytics_no_data/src/analytics_no_data_page.component.test.tsx new file mode 100644 index 0000000000000..0f18710197991 --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/src/analytics_no_data_page.component.test.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { mountWithIntl } from '@kbn/test-jest-helpers'; +import { KibanaNoDataPage } from '@kbn/shared-ux-components'; +import { AnalyticsNoDataPage } from './analytics_no_data_page.component'; + +describe('AnalyticsNoDataPageComponent', () => { + const onDataViewCreated = jest.fn(); + + it('renders correctly', () => { + const component = mountWithIntl( + + ); + expect(component).toMatchSnapshot(); + + expect(component.find(KibanaNoDataPage).length).toBe(1); + + const noDataConfig = component.find(KibanaNoDataPage).props().noDataConfig; + expect(noDataConfig.solution).toEqual('Analytics'); + expect(noDataConfig.pageTitle).toEqual('Welcome to Analytics!'); + expect(noDataConfig.logo).toEqual('logoKibana'); + expect(noDataConfig.docsLink).toEqual('http://www.test.com'); + expect(noDataConfig.action.elasticAgent).not.toBeNull(); + }); +}); diff --git a/packages/shared-ux/page/analytics_no_data/src/analytics_no_data_page.component.tsx b/packages/shared-ux/page/analytics_no_data/src/analytics_no_data_page.component.tsx new file mode 100644 index 0000000000000..31051328641f4 --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/src/analytics_no_data_page.component.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 and the Server Side Public License, v 1; you may 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 { i18n } from '@kbn/i18n'; +import { KibanaNoDataPage } from '@kbn/shared-ux-components'; + +/** + * Props for the pure component. + */ +export interface Props { + kibanaGuideDocLink: string; + onDataViewCreated: (dataView: unknown) => void; +} + +const solution = i18n.translate('sharedUXPackages.noDataConfig.analytics', { + defaultMessage: 'Analytics', +}); + +const pageTitle = i18n.translate('sharedUXPackages.noDataConfig.analyticsPageTitle', { + defaultMessage: 'Welcome to Analytics!', +}); + +const addIntegrationsTitle = i18n.translate('sharedUXPackages.noDataConfig.addIntegrationsTitle', { + defaultMessage: 'Add integrations', +}); + +const addIntegrationsDescription = i18n.translate( + 'sharedUXPackages.noDataConfig.addIntegrationsDescription', + { + defaultMessage: 'Use Elastic Agent to collect data and build out Analytics solutions.', + } +); + +/** + * A pure component of an entire page that can be displayed when Kibana "has no data", specifically for Analytics. + */ +export const AnalyticsNoDataPage = ({ kibanaGuideDocLink, onDataViewCreated }: Props) => { + const noDataConfig = { + solution, + pageTitle, + logo: 'logoKibana', + action: { + elasticAgent: { + title: addIntegrationsTitle, + description: addIntegrationsDescription, + 'data-test-subj': 'kbnOverviewAddIntegrations', + }, + }, + docsLink: kibanaGuideDocLink, + }; + + return ; +}; diff --git a/packages/shared-ux/page/analytics_no_data/src/analytics_no_data_page.stories.tsx b/packages/shared-ux/page/analytics_no_data/src/analytics_no_data_page.stories.tsx new file mode 100644 index 0000000000000..8471cdf9546d2 --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/src/analytics_no_data_page.stories.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { action } from '@storybook/addon-actions'; +import { servicesFactory, DataServiceFactoryConfig } from '@kbn/shared-ux-storybook'; + +import { AnalyticsNoDataPage as Component } from './analytics_no_data_page'; +import { AnalyticsNoDataPageProvider, Services } from './services'; +import mdx from '../README.mdx'; + +export default { + title: 'Analytics No Data Page', + description: 'An Analytics-specific version of KibanaNoDataPage.', + parameters: { + docs: { + page: mdx, + }, + }, +}; + +type Params = Pick; + +export const AnalyticsNoDataPage = (params: Params) => { + // Workaround to leverage the services package. + const { application, data, docLinks, editors, http, permissions, platform } = + servicesFactory(params); + + const services: Services = { + ...application, + ...data, + ...docLinks, + ...editors, + ...http, + ...permissions, + ...platform, + kibanaGuideDocLink: 'Kibana guide', + }; + + return ( + + + + ); +}; + +AnalyticsNoDataPage.argTypes = { + hasESData: { + control: 'boolean', + defaultValue: false, + }, + hasUserDataView: { + control: 'boolean', + defaultValue: false, + }, +}; diff --git a/packages/shared-ux/page/analytics_no_data/src/analytics_no_data_page.test.tsx b/packages/shared-ux/page/analytics_no_data/src/analytics_no_data_page.test.tsx new file mode 100644 index 0000000000000..e091cac70d32b --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/src/analytics_no_data_page.test.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { mountWithIntl } from '@kbn/test-jest-helpers'; +import { mockServicesFactory } from '@kbn/shared-ux-services'; + +import { Services, AnalyticsNoDataPageProvider } from './services'; +import { AnalyticsNoDataPage as Component } from './analytics_no_data_page.component'; +import { AnalyticsNoDataPage } from './analytics_no_data_page'; + +describe('AnalyticsNoDataPage', () => { + const onDataViewCreated = jest.fn(); + + // Workaround to leverage the services package. + const { application, data, docLinks, editors, http, permissions, platform } = + mockServicesFactory(); + + const services: Services = { + ...application, + ...data, + ...docLinks, + ...editors, + ...http, + ...permissions, + ...platform, + kibanaGuideDocLink: 'Kibana guide', + }; + + afterAll(() => { + jest.resetAllMocks(); + }); + + it('renders correctly', async () => { + const component = mountWithIntl( + + + + ); + + expect(component.find(Component).length).toBe(1); + expect(component.find(Component).props().kibanaGuideDocLink).toBe(services.kibanaGuideDocLink); + expect(component.find(Component).props().onDataViewCreated).toBe(onDataViewCreated); + }); +}); diff --git a/packages/shared-ux/page/analytics_no_data/src/analytics_no_data_page.tsx b/packages/shared-ux/page/analytics_no_data/src/analytics_no_data_page.tsx new file mode 100644 index 0000000000000..141f607a6257e --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/src/analytics_no_data_page.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { LegacyServicesProvider, getLegacyServices } from './legacy_services'; +import { useServices } from './services'; +import { AnalyticsNoDataPage as Component } from './analytics_no_data_page.component'; + +/** + * Props for the `AnalyticsNoDataPage` component. + */ +export interface AnalyticsNoDataPageProps { + onDataViewCreated: (dataView: unknown) => void; +} + +/** + * An entire page that can be displayed when Kibana "has no data", specifically for Analytics. Uses + * services from a provider to provide props to a pure component. + */ +export const AnalyticsNoDataPage = ({ onDataViewCreated }: AnalyticsNoDataPageProps) => { + const services = useServices(); + const { kibanaGuideDocLink } = services; + + return ( + + + + ); +}; diff --git a/packages/shared-ux/page/analytics_no_data/src/index.ts b/packages/shared-ux/page/analytics_no_data/src/index.ts new file mode 100644 index 0000000000000..7b87084f745ef --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/src/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { withSuspense } from '@kbn/shared-ux-utility'; + +export { AnalyticsNoDataPageProvider, AnalyticsNoDataPageKibanaProvider } from './services'; + +/** + * Lazy-loaded connected component. Must be wrapped in `React.Suspense`. + */ +export const LazyAnalyticsNoDataPage = React.lazy(() => + import('./analytics_no_data_page').then(({ AnalyticsNoDataPage }) => ({ + default: AnalyticsNoDataPage, + })) +); + +/** + * An entire page that can be displayed when Kibana "has no data", specifically for Analytics. + * Requires a Provider for relevant services. + */ +export const AnalyticsNoDataPage = withSuspense(LazyAnalyticsNoDataPage); diff --git a/packages/shared-ux/page/analytics_no_data/src/legacy_services.tsx b/packages/shared-ux/page/analytics_no_data/src/legacy_services.tsx new file mode 100644 index 0000000000000..3d690e56e0d23 --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/src/legacy_services.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { SharedUxServicesProvider as LegacyServicesProvider } from '@kbn/shared-ux-services'; +export type { SharedUxServices as LegacyServices } from '@kbn/shared-ux-services'; + +import { SharedUxServices as LegacyServices } from '@kbn/shared-ux-services'; +import { Services } from './services'; + +/** + * This list is temporary, a stop-gap as we migrate to a package-based architecture, where + * services are not collected in a single package. In order to make the transition, this + * interface is intentionally "flat". + * + * Expect this list to dwindle to zero as `@kbn/shared-ux-components` are migrated to their + * own packages, (and `@kbn/shared-ux-services` is removed). + */ +export const getLegacyServices = (services: Services): LegacyServices => ({ + application: { + currentAppId$: services.currentAppId$, + navigateToUrl: services.navigateToUrl, + }, + data: { + hasESData: services.hasESData, + hasDataView: services.hasDataView, + hasUserDataView: services.hasUserDataView, + }, + docLinks: { + dataViewsDocLink: services.dataViewsDocLink, + }, + editors: { + openDataViewEditor: services.openDataViewEditor, + }, + http: { + addBasePath: services.addBasePath, + }, + permissions: { + canAccessFleet: services.canAccessFleet, + canCreateNewDataView: services.canCreateNewDataView, + }, + platform: { + setIsFullscreen: services.setIsFullscreen, + }, +}); diff --git a/packages/shared-ux/page/analytics_no_data/src/services.tsx b/packages/shared-ux/page/analytics_no_data/src/services.tsx new file mode 100644 index 0000000000000..70ba29ed2f648 --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/src/services.tsx @@ -0,0 +1,162 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { FC, useContext } from 'react'; +import { Observable } from 'rxjs'; + +/** + * TODO: `DataView` is a class exported by `src/plugins/data_views/public`. Since this service + * is contained in this package-- and packages can only depend on other packages and never on + * plugins-- we have to set this to `unknown`. If and when `DataView` is exported from a + * stateless package, we can remove this. + * + * @see: https://github.com/elastic/kibana/issues/127695 + */ +type DataView = unknown; + +/** + * A subset of the `DataViewEditorOptions` interface relevant to this component. + * + * @see: src/plugins/data_view_editor/public/types.ts + */ +interface DataViewEditorOptions { + /** Handler to be invoked when the Data View Editor completes a save operation. */ + onSave: (dataView: DataView) => void; +} + +/** + * A list of Services that are consumed by this component. + * + * This list is temporary, a stopgap as we migrate to a package-based architecture, where + * services are not collected in a single package. In order to make the transition, this + * interface is intentionally "flat". + * + * Expect this list to dwindle to zero as `@kbn/shared-ux-components` are migrated to their + * own packages, (and `@kbn/shared-ux-services` is removed). + */ +export interface Services { + addBasePath: (url: string) => string; + canAccessFleet: boolean; + canCreateNewDataView: boolean; + currentAppId$: Observable; + dataViewsDocLink: string; + hasDataView: () => Promise; + hasESData: () => Promise; + hasUserDataView: () => Promise; + kibanaGuideDocLink: string; + navigateToUrl: (url: string) => Promise; + openDataViewEditor: (options: DataViewEditorOptions) => () => void; + setIsFullscreen: (isFullscreen: boolean) => void; +} + +const AnalyticsNoDataPageContext = React.createContext(null); + +/** + * A Context Provider that provides services to the component. + */ +export const AnalyticsNoDataPageProvider: FC = ({ children, ...services }) => { + return ( + + {children} + + ); +}; + +/** + * An interface containing a collection of Kibana plugins and services required to + * render this component and its dependencies. + */ +export interface AnalyticsNoDataPageKibanaDependencies { + coreStart: { + application: { + capabilities: { + navLinks: { + integrations: boolean; + }; + }; + currentAppId$: Observable; + navigateToUrl: (url: string) => Promise; + }; + chrome: { + setIsVisible: (isVisible: boolean) => void; + }; + docLinks: { + links: { + indexPatterns: { + introduction: string; + }; + kibana: { + guide: string; + }; + }; + }; + http: { + basePath: { + prepend: (url: string) => string; + }; + }; + }; + dataViews: { + hasData: { + hasDataView: () => Promise; + hasESData: () => Promise; + hasUserDataView: () => Promise; + }; + }; + dataViewEditor: { + openEditor: (options: DataViewEditorOptions) => () => void; + userPermissions: { + editDataView: () => boolean; + }; + }; +} + +/** + * Kibana-specific Provider that maps dependencies to services. + */ +export const AnalyticsNoDataPageKibanaProvider: FC = ({ + children, + ...dependencies +}) => { + const { coreStart, dataViewEditor, dataViews } = dependencies; + const value: Services = { + addBasePath: coreStart.http.basePath.prepend, + canAccessFleet: coreStart.application.capabilities.navLinks.integrations, + canCreateNewDataView: dataViewEditor.userPermissions.editDataView(), + currentAppId$: coreStart.application.currentAppId$, + dataViewsDocLink: coreStart.docLinks.links.indexPatterns?.introduction, + hasDataView: dataViews.hasData.hasDataView, + hasESData: dataViews.hasData.hasESData, + hasUserDataView: dataViews.hasData.hasUserDataView, + kibanaGuideDocLink: coreStart.docLinks.links.kibana.guide, + navigateToUrl: coreStart.application.navigateToUrl, + openDataViewEditor: dataViewEditor.openEditor, + setIsFullscreen: (isVisible: boolean) => coreStart.chrome.setIsVisible(isVisible), + }; + + return ( + + {children} + + ); +}; + +/** + * React hook for accessing pre-wired services. + */ +export function useServices() { + const context = useContext(AnalyticsNoDataPageContext); + + if (!context) { + throw new Error( + 'AnalyticsNoDataPageContext is missing. Ensure your component or React root is wrapped with AnalyticsNoDataPageContext.' + ); + } + + return context; +} diff --git a/packages/shared-ux/page/analytics_no_data/tsconfig.json b/packages/shared-ux/page/analytics_no_data/tsconfig.json new file mode 100644 index 0000000000000..573ad07325100 --- /dev/null +++ b/packages/shared-ux/page/analytics_no_data/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node", + "react", + "@kbn/ambient-ui-types", + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/scripts/README.md b/scripts/README.md index 960e8ab2ed0b8..a743ce5e1d53d 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -17,13 +17,13 @@ This directory is excluded from the build and tools within it should help users ## Functional Test Scripts -**`node scripts/functional_tests [--config test/functional/config.js --config test/api_integration/config.js]`** +**`node scripts/functional_tests [--config test/functional/config.base.js --config test/api_integration/config.js]`** Runs all the functional tests: selenium tests and api integration tests. List configs with multiple `--config` arguments. Uses the [@kbn/test](../packages/kbn-test) library to run Elasticsearch and Kibana servers and tests against those servers, for multiple server+test setups. In particular, calls out to [`runTests()`](../packages/kbn-test/src/functional_tests/tasks.js). Can be run on a single config. -**`node scripts/functional_tests_server [--config test/functional/config.js]`** +**`node scripts/functional_tests_server [--config test/functional/config.base.js]`** -Starts just the Elasticsearch and Kibana servers given a single config, i.e. via `--config test/functional/config.js` or `--config test/api_integration/config`. Allows the user to start just the servers with this script, and keep them running while running tests against these servers. The idea is that the same config file configures both Elasticsearch and Kibana servers. Uses the [`startServers()`](../packages/kbn-test/src/functional_tests/tasks.js#L52-L80) method from [@kbn/test](../packages/kbn-test) library. +Starts just the Elasticsearch and Kibana servers given a single config, i.e. via `--config test/functional/config.base.js` or `--config test/api_integration/config`. Allows the user to start just the servers with this script, and keep them running while running tests against these servers. The idea is that the same config file configures both Elasticsearch and Kibana servers. Uses the [`startServers()`](../packages/kbn-test/src/functional_tests/tasks.js#L52-L80) method from [@kbn/test](../packages/kbn-test) library. Example. Start servers _and_ run tests, separately, but using the same config: @@ -51,7 +51,7 @@ If you wish to load up specific es archived data for your test, you can do so vi node scripts/es_archiver.js load [--es-url=http://username:password@localhost:9200] [--kibana-url=http://username:password@localhost:5601/{basepath?}] ``` -That will load the specified archive located in the archive directory specified by the default functional config file, located in `test/functional/config.js`. To load archives from other function config files you can pass `--config path/to/config.js`. +That will load the specified archive located in the archive directory specified by the default functional config file, located in `test/functional/config.base.js`. To load archives from other function config files you can pass `--config path/to/config.js`. *Note:* The `--es-url` and `--kibana-url` options may or may not be neccessary depending on your current Kibana configuration settings, and their values may also change based on those settings (for example if you are not running with security you will not need the `username:password` portion). diff --git a/scripts/extract_performance_testing_dataset.js b/scripts/extract_performance_testing_dataset.js new file mode 100644 index 0000000000000..deb3da481f1e1 --- /dev/null +++ b/scripts/extract_performance_testing_dataset.js @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +require('../src/setup_node_env'); +require('@kbn/performance-testing-dataset-extractor').runExtractor(); diff --git a/scripts/find_target_node_imports_in_bundles.js b/scripts/find_target_node_imports_in_bundles.js new file mode 100644 index 0000000000000..eae3b94efeaba --- /dev/null +++ b/scripts/find_target_node_imports_in_bundles.js @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +require('../src/setup_node_env/no_transpilation'); +require('@kbn/optimizer').runFindTargetNodeImportsCli(); diff --git a/scripts/functional_tests.js b/scripts/functional_tests.js index 1e963660a1e03..eb1dea2dcab36 100644 --- a/scripts/functional_tests.js +++ b/scripts/functional_tests.js @@ -7,26 +7,4 @@ */ require('../src/setup_node_env'); -require('@kbn/test').runTestsCli([ - require.resolve('../test/functional/config.ccs.ts'), - require.resolve('../test/functional/config.js'), - require.resolve('../test/plugin_functional/config.ts'), - require.resolve('../test/ui_capabilities/newsfeed_err/config.ts'), - require.resolve('../test/new_visualize_flow/config.ts'), - require.resolve('../test/interactive_setup_api_integration/enrollment_flow.config.ts'), - require.resolve('../test/interactive_setup_api_integration/manual_configuration_flow.config.ts'), - require.resolve( - '../test/interactive_setup_api_integration/manual_configuration_flow_without_tls.config.ts' - ), - require.resolve('../test/interactive_setup_functional/enrollment_token.config.ts'), - require.resolve('../test/interactive_setup_functional/manual_configuration.config.ts'), - require.resolve( - '../test/interactive_setup_functional/manual_configuration_without_security.config.ts' - ), - require.resolve( - '../test/interactive_setup_functional/manual_configuration_without_tls.config.ts' - ), - require.resolve('../test/api_integration/config.js'), - require.resolve('../test/interpreter_functional/config.ts'), - require.resolve('../test/examples/config.js'), -]); +require('@kbn/test').runTestsCli(); diff --git a/scripts/functional_tests_server.js b/scripts/functional_tests_server.js index 4995eba4de670..836a1ede126e3 100644 --- a/scripts/functional_tests_server.js +++ b/scripts/functional_tests_server.js @@ -7,4 +7,4 @@ */ require('../src/setup_node_env'); -require('@kbn/test').startServersCli(require.resolve('../test/functional/config.js')); +require('@kbn/test').startServersCli(require.resolve('../test/functional/config.base.js')); diff --git a/src/core/public/analytics/analytics_service.mock.ts b/src/core/public/analytics/analytics_service.mock.ts index c14d4a8216850..9037a756473c3 100644 --- a/src/core/public/analytics/analytics_service.mock.ts +++ b/src/core/public/analytics/analytics_service.mock.ts @@ -40,6 +40,7 @@ const createAnalyticsServiceMock = (): jest.Mocked => return { setup: jest.fn().mockImplementation(createAnalyticsServiceSetup), start: jest.fn().mockImplementation(createAnalyticsServiceStart), + stop: jest.fn(), }; }; diff --git a/src/core/public/analytics/analytics_service.test.mocks.ts b/src/core/public/analytics/analytics_service.test.mocks.ts new file mode 100644 index 0000000000000..3d98cf4392926 --- /dev/null +++ b/src/core/public/analytics/analytics_service.test.mocks.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 { AnalyticsClient } from '@kbn/analytics-client'; +import { Subject } from 'rxjs'; + +export const analyticsClientMock: jest.Mocked = { + optIn: jest.fn(), + reportEvent: jest.fn(), + registerEventType: jest.fn(), + registerContextProvider: jest.fn(), + removeContextProvider: jest.fn(), + registerShipper: jest.fn(), + telemetryCounter$: new Subject(), + shutdown: jest.fn(), +}; + +jest.doMock('@kbn/analytics-client', () => ({ + createAnalytics: () => analyticsClientMock, +})); diff --git a/src/core/public/analytics/analytics_service.test.ts b/src/core/public/analytics/analytics_service.test.ts new file mode 100644 index 0000000000000..e2298a79ff134 --- /dev/null +++ b/src/core/public/analytics/analytics_service.test.ts @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { firstValueFrom, Observable } from 'rxjs'; +import { analyticsClientMock } from './analytics_service.test.mocks'; +import { coreMock, injectedMetadataServiceMock } from '../mocks'; +import { AnalyticsService } from './analytics_service'; + +describe('AnalyticsService', () => { + let analyticsService: AnalyticsService; + beforeEach(() => { + jest.clearAllMocks(); + analyticsService = new AnalyticsService(coreMock.createCoreContext()); + }); + test('should register some context providers on creation', async () => { + expect(analyticsClientMock.registerContextProvider).toHaveBeenCalledTimes(3); + await expect( + firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[0][0].context$) + ).resolves.toMatchInlineSnapshot(` + Object { + "branch": "branch", + "buildNum": 100, + "buildSha": "buildSha", + "isDev": true, + "isDistributable": false, + "version": "version", + } + `); + await expect( + firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[1][0].context$) + ).resolves.toEqual({ session_id: expect.any(String) }); + await expect( + firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[2][0].context$) + ).resolves.toEqual({ + preferred_language: 'en-US', + preferred_languages: ['en-US', 'en'], + user_agent: expect.any(String), + }); + }); + + test('setup should expose all the register APIs, reportEvent and opt-in', () => { + const injectedMetadata = injectedMetadataServiceMock.createSetupContract(); + expect(analyticsService.setup({ injectedMetadata })).toStrictEqual({ + registerShipper: expect.any(Function), + registerContextProvider: expect.any(Function), + removeContextProvider: expect.any(Function), + registerEventType: expect.any(Function), + reportEvent: expect.any(Function), + optIn: expect.any(Function), + telemetryCounter$: expect.any(Observable), + }); + }); + + test('setup should register the elasticsearch info context provider (undefined)', async () => { + const injectedMetadata = injectedMetadataServiceMock.createSetupContract(); + analyticsService.setup({ injectedMetadata }); + await expect( + firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[3][0].context$) + ).resolves.toMatchInlineSnapshot(`undefined`); + }); + + test('setup should register the elasticsearch info context provider (with info)', async () => { + const injectedMetadata = injectedMetadataServiceMock.createSetupContract(); + injectedMetadata.getElasticsearchInfo.mockReturnValue({ + cluster_name: 'cluster_name', + cluster_uuid: 'cluster_uuid', + cluster_version: 'version', + }); + analyticsService.setup({ injectedMetadata }); + await expect( + firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[3][0].context$) + ).resolves.toMatchInlineSnapshot(` + Object { + "cluster_name": "cluster_name", + "cluster_uuid": "cluster_uuid", + "cluster_version": "version", + } + `); + }); + + test('setup should expose only the APIs report and opt-in', () => { + expect(analyticsService.start()).toStrictEqual({ + reportEvent: expect.any(Function), + optIn: expect.any(Function), + telemetryCounter$: expect.any(Observable), + }); + }); +}); diff --git a/src/core/public/analytics/analytics_service.ts b/src/core/public/analytics/analytics_service.ts index 89e83c7e3c85b..f1c00b293808b 100644 --- a/src/core/public/analytics/analytics_service.ts +++ b/src/core/public/analytics/analytics_service.ts @@ -8,7 +8,11 @@ import type { AnalyticsClient } from '@kbn/analytics-client'; import { createAnalytics } from '@kbn/analytics-client'; +import { of } from 'rxjs'; +import { trackClicks } from './track_clicks'; +import { InjectedMetadataSetup } from '../injected_metadata'; import { CoreContext } from '../core_system'; +import { getSessionId } from './get_session_id'; import { createLogger } from './logger'; /** @@ -16,7 +20,7 @@ import { createLogger } from './logger'; * {@link AnalyticsClient} * @public */ -export type AnalyticsServiceSetup = AnalyticsClient; +export type AnalyticsServiceSetup = Omit; /** * Exposes the public APIs of the AnalyticsClient during the start phase * {@link AnalyticsClient} @@ -27,6 +31,11 @@ export type AnalyticsServiceStart = Pick< 'optIn' | 'reportEvent' | 'telemetryCounter$' >; +/** @internal */ +export interface AnalyticsServiceSetupDeps { + injectedMetadata: InjectedMetadataSetup; +} + export class AnalyticsService { private readonly analyticsClient: AnalyticsClient; @@ -38,9 +47,19 @@ export class AnalyticsService { // For now, we are relying on whether it's a distributable or running from source. sendTo: core.env.packageInfo.dist ? 'production' : 'staging', }); + + this.registerBuildInfoAnalyticsContext(core); + + // We may eventually move the following to the client's package since they are not Kibana-specific + // and can benefit other consumers of the client. + this.registerSessionIdContext(); + this.registerBrowserInfoAnalyticsContext(); + trackClicks(this.analyticsClient, core.env.mode.dev); } - public setup(): AnalyticsServiceSetup { + public setup({ injectedMetadata }: AnalyticsServiceSetupDeps): AnalyticsServiceSetup { + this.registerElasticsearchInfoContext(injectedMetadata); + return { optIn: this.analyticsClient.optIn, registerContextProvider: this.analyticsClient.registerContextProvider, @@ -51,6 +70,7 @@ export class AnalyticsService { telemetryCounter$: this.analyticsClient.telemetryCounter$, }; } + public start(): AnalyticsServiceStart { return { optIn: this.analyticsClient.optIn, @@ -58,4 +78,119 @@ export class AnalyticsService { telemetryCounter$: this.analyticsClient.telemetryCounter$, }; } + + public stop() { + this.analyticsClient.shutdown(); + } + + /** + * Enriches the events with a session_id, so we can correlate them and understand funnels. + * @private + */ + private registerSessionIdContext() { + this.analyticsClient.registerContextProvider({ + name: 'session-id', + context$: of({ session_id: getSessionId() }), + schema: { + session_id: { + type: 'keyword', + _meta: { description: 'Unique session ID for every browser session' }, + }, + }, + }); + } + + /** + * Enriches the event with the build information. + * @param core The core context. + * @private + */ + private registerBuildInfoAnalyticsContext(core: CoreContext) { + this.analyticsClient.registerContextProvider({ + name: 'build info', + context$: of({ + isDev: core.env.mode.dev, + isDistributable: core.env.packageInfo.dist, + version: core.env.packageInfo.version, + branch: core.env.packageInfo.branch, + buildNum: core.env.packageInfo.buildNum, + buildSha: core.env.packageInfo.buildSha, + }), + schema: { + isDev: { + type: 'boolean', + _meta: { description: 'Is it running in development mode?' }, + }, + isDistributable: { + type: 'boolean', + _meta: { description: 'Is it running from a distributable?' }, + }, + version: { type: 'keyword', _meta: { description: 'Version of the Kibana instance.' } }, + branch: { + type: 'keyword', + _meta: { description: 'Branch of source running Kibana from.' }, + }, + buildNum: { type: 'long', _meta: { description: 'Build number of the Kibana instance.' } }, + buildSha: { type: 'keyword', _meta: { description: 'Build SHA of the Kibana instance.' } }, + }, + }); + } + + /** + * Enriches events with the current Browser's information + * @private + */ + private registerBrowserInfoAnalyticsContext() { + this.analyticsClient.registerContextProvider({ + name: 'browser info', + context$: of({ + user_agent: navigator.userAgent, + preferred_language: navigator.language, + preferred_languages: navigator.languages, + }), + schema: { + user_agent: { + type: 'keyword', + _meta: { description: 'User agent of the browser.' }, + }, + preferred_language: { + type: 'keyword', + _meta: { description: 'Preferred language of the browser.' }, + }, + preferred_languages: { + type: 'array', + items: { + type: 'keyword', + _meta: { description: 'List of the preferred languages of the browser.' }, + }, + }, + }, + }); + } + + /** + * Enriches the events with the Elasticsearch info (cluster name, uuid and version). + * @param injectedMetadata The injected metadata service. + * @private + */ + private registerElasticsearchInfoContext(injectedMetadata: InjectedMetadataSetup) { + this.analyticsClient.registerContextProvider({ + name: 'elasticsearch info', + context$: of(injectedMetadata.getElasticsearchInfo()), + schema: { + cluster_name: { + type: 'keyword', + _meta: { description: 'The Cluster Name', optional: true }, + }, + cluster_uuid: { + type: 'keyword', + _meta: { description: 'The Cluster UUID', optional: true }, + }, + cluster_version: { + type: 'keyword', + _meta: { description: 'The Cluster version', optional: true }, + }, + }, + }); + } } diff --git a/src/core/public/analytics/get_session_id.test.ts b/src/core/public/analytics/get_session_id.test.ts new file mode 100644 index 0000000000000..85ac515e29f68 --- /dev/null +++ b/src/core/public/analytics/get_session_id.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { getSessionId } from './get_session_id'; + +describe('getSessionId', () => { + test('should return a session id', () => { + const sessionId = getSessionId(); + expect(sessionId).toStrictEqual(expect.any(String)); + }); + + test('calling it twice should return the same value', () => { + const sessionId1 = getSessionId(); + const sessionId2 = getSessionId(); + expect(sessionId2).toStrictEqual(sessionId1); + }); +}); diff --git a/src/core/public/analytics/get_session_id.ts b/src/core/public/analytics/get_session_id.ts new file mode 100644 index 0000000000000..62bb3a4a1c336 --- /dev/null +++ b/src/core/public/analytics/get_session_id.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { v4 } from 'uuid'; + +/** + * Returns a session ID for the current user. + * We are storing it to the sessionStorage. This means it remains the same through refreshes, + * but it is not persisted when closing the browser/tab or manually navigating to another URL. + */ +export function getSessionId(): string { + const sessionId = sessionStorage.getItem('sessionId') ?? v4(); + sessionStorage.setItem('sessionId', sessionId); + return sessionId; +} diff --git a/src/core/public/analytics/logger.test.ts b/src/core/public/analytics/logger.test.ts new file mode 100644 index 0000000000000..2fbe17e3f7d22 --- /dev/null +++ b/src/core/public/analytics/logger.test.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { LogRecord } from '@kbn/logging'; +import { createLogger } from './logger'; + +describe('createLogger', () => { + // Calling `.mockImplementation` on all of them to avoid jest logging the console usage + const logErrorSpy = jest.spyOn(console, 'error').mockImplementation(); + const logWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); + const logInfoSpy = jest.spyOn(console, 'info').mockImplementation(); + const logDebugSpy = jest.spyOn(console, 'debug').mockImplementation(); + const logTraceSpy = jest.spyOn(console, 'trace').mockImplementation(); + const logLogSpy = jest.spyOn(console, 'log').mockImplementation(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('should create a logger', () => { + const logger = createLogger(false); + expect(logger).toStrictEqual( + expect.objectContaining({ + fatal: expect.any(Function), + error: expect.any(Function), + warn: expect.any(Function), + info: expect.any(Function), + debug: expect.any(Function), + trace: expect.any(Function), + log: expect.any(Function), + get: expect.any(Function), + }) + ); + }); + + test('when isDev === false, it should not log anything', () => { + const logger = createLogger(false); + logger.fatal('fatal'); + expect(logErrorSpy).not.toHaveBeenCalled(); + logger.error('error'); + expect(logErrorSpy).not.toHaveBeenCalled(); + logger.warn('warn'); + expect(logWarnSpy).not.toHaveBeenCalled(); + logger.info('info'); + expect(logInfoSpy).not.toHaveBeenCalled(); + logger.debug('debug'); + expect(logDebugSpy).not.toHaveBeenCalled(); + logger.trace('trace'); + expect(logTraceSpy).not.toHaveBeenCalled(); + logger.log({} as LogRecord); + expect(logLogSpy).not.toHaveBeenCalled(); + logger.get().warn('warn'); + expect(logWarnSpy).not.toHaveBeenCalled(); + }); + + test('when isDev === true, it should log everything', () => { + const logger = createLogger(true); + logger.fatal('fatal'); + expect(logErrorSpy).toHaveBeenCalledTimes(1); + logger.error('error'); + expect(logErrorSpy).toHaveBeenCalledTimes(2); // fatal + error + logger.warn('warn'); + expect(logWarnSpy).toHaveBeenCalledTimes(1); + logger.info('info'); + expect(logInfoSpy).toHaveBeenCalledTimes(1); + logger.debug('debug'); + expect(logDebugSpy).toHaveBeenCalledTimes(1); + logger.trace('trace'); + expect(logTraceSpy).toHaveBeenCalledTimes(1); + logger.log({} as LogRecord); + expect(logLogSpy).toHaveBeenCalledTimes(1); + logger.get().warn('warn'); + expect(logWarnSpy).toHaveBeenCalledTimes(2); + }); +}); diff --git a/src/core/public/analytics/track_clicks.test.ts b/src/core/public/analytics/track_clicks.test.ts new file mode 100644 index 0000000000000..db1b8fc215cf8 --- /dev/null +++ b/src/core/public/analytics/track_clicks.test.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { firstValueFrom, ReplaySubject } from 'rxjs'; +import { analyticsClientMock } from './analytics_service.test.mocks'; +import { trackClicks } from './track_clicks'; +import { take } from 'rxjs/operators'; + +describe('trackClicks', () => { + const addEventListenerSpy = jest.spyOn(window, 'addEventListener'); + const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('registers the analytics event type and a listener to the "click" events', () => { + trackClicks(analyticsClientMock, true); + + expect(analyticsClientMock.registerEventType).toHaveBeenCalledTimes(1); + expect(analyticsClientMock.registerEventType).toHaveBeenCalledWith( + expect.objectContaining({ + eventType: 'click', + }) + ); + expect(addEventListenerSpy).toHaveBeenCalledTimes(1); + expect(addEventListenerSpy).toHaveBeenCalledWith('click', expect.any(Function), undefined); + }); + + test('reports an analytics event when a click event occurs', async () => { + // Gather an actual "click" event + const event$ = new ReplaySubject(1); + const parent = document.createElement('div'); + parent.setAttribute('data-test-subj', 'test-click-target-parent'); + const element = document.createElement('button'); + parent.appendChild(element); + element.setAttribute('data-test-subj', 'test-click-target'); + element.innerText = 'test'; // Only to validate that it is not included in the event. + element.value = 'test'; // Only to validate that it is not included in the event. + element.addEventListener('click', (e) => event$.next(e)); + element.click(); + // Using an observable because the event might not be immediately available + const event = await firstValueFrom(event$.pipe(take(1))); + event$.complete(); // No longer needed + + trackClicks(analyticsClientMock, true); + expect(addEventListenerSpy).toHaveBeenCalledTimes(1); + + (addEventListenerSpy.mock.calls[0][1] as EventListener)(event); + expect(analyticsClientMock.reportEvent).toHaveBeenCalledTimes(1); + expect(analyticsClientMock.reportEvent).toHaveBeenCalledWith('click', { + target: [ + 'DIV', + 'data-test-subj=test-click-target-parent', + 'BUTTON', + 'data-test-subj=test-click-target', + ], + }); + }); + + test('handles any processing errors logging them in dev mode', async () => { + trackClicks(analyticsClientMock, true); + expect(addEventListenerSpy).toHaveBeenCalledTimes(1); + + // A basic MouseEvent does not have a target and will fail the logic, making it go to the catch branch as intended. + (addEventListenerSpy.mock.calls[0][1] as EventListener)(new MouseEvent('click')); + expect(analyticsClientMock.reportEvent).toHaveBeenCalledTimes(0); + expect(consoleErrorSpy).toHaveBeenCalledTimes(1); + expect(consoleErrorSpy.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "Failed to report the click event", + Object { + "error": [TypeError: Cannot read properties of null (reading 'parentElement')], + "event": MouseEvent { + "isTrusted": false, + }, + }, + ] + `); + }); + + test('swallows any processing errors when not in dev mode', async () => { + trackClicks(analyticsClientMock, false); + expect(addEventListenerSpy).toHaveBeenCalledTimes(1); + + // A basic MouseEvent does not have a target and will fail the logic, making it go to the catch branch as intended. + (addEventListenerSpy.mock.calls[0][1] as EventListener)(new MouseEvent('click')); + expect(analyticsClientMock.reportEvent).toHaveBeenCalledTimes(0); + expect(consoleErrorSpy).toHaveBeenCalledTimes(0); + }); +}); diff --git a/src/core/public/analytics/track_clicks.ts b/src/core/public/analytics/track_clicks.ts new file mode 100644 index 0000000000000..f2ba7c25de768 --- /dev/null +++ b/src/core/public/analytics/track_clicks.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { fromEvent } from 'rxjs'; +import type { AnalyticsClient } from '@kbn/analytics-client'; + +/** HTML attributes that should be skipped from reporting because they might contain user data */ +const POTENTIAL_PII_HTML_ATTRIBUTES = ['value']; + +/** + * Registers the event type "click" in the analytics client. + * Then it listens to all the "click" events in the UI and reports them with the `target` property being a + * full list of the element's and its parents' attributes. This allows + * @param analytics + */ +export function trackClicks(analytics: AnalyticsClient, isDevMode: boolean) { + analytics.registerEventType<{ target: string[] }>({ + eventType: 'click', + schema: { + target: { + type: 'array', + items: { + type: 'keyword', + _meta: { + description: + 'The attributes of the clicked element and all its parents in the form `{attr.name}={attr.value}`. It allows finding the clicked elements by looking up its attributes like "data-test-subj=my-button".', + }, + }, + }, + }, + }); + + // window or document? + // I tested it on multiple browsers and it seems to work the same. + // My assumption is that window captures other documents like iframes as well? + return fromEvent(window, 'click').subscribe((event) => { + try { + const target = event.target as HTMLElement; + analytics.reportEvent('click', { target: getTargetDefinition(target) }); + } catch (error) { + if (isDevMode) { + // Defensively log the error in dev mode to catch any potential bugs. + // eslint-disable-next-line no-console + console.error(`Failed to report the click event`, { event, error }); + } + } + }); +} + +/** + * Returns a list of strings consisting on the tag name and all the attributes of the element. + * Additionally, it recursively walks up the DOM tree to find all the parents' definitions and prepends them to the list. + * + * @example + * From + * ```html + *
+ *
+ *
+ * ``` + * it returns ['DIV', 'data-test-subj=my-parent', 'DIV', 'data-test-subj=my-button'] + * @param target The child node to start from. + */ +function getTargetDefinition(target: HTMLElement): string[] { + return [ + ...(target.parentElement ? getTargetDefinition(target.parentElement) : []), + target.tagName, + ...[...target.attributes] + .filter((attr) => !POTENTIAL_PII_HTML_ATTRIBUTES.includes(attr.name)) + .map((attr) => `${attr.name}=${attr.value}`), + ]; +} diff --git a/src/core/public/core_system.test.mocks.ts b/src/core/public/core_system.test.mocks.ts index 6eddf08cd2ae1..ff24cc8839794 100644 --- a/src/core/public/core_system.test.mocks.ts +++ b/src/core/public/core_system.test.mocks.ts @@ -21,6 +21,20 @@ import { renderingServiceMock } from './rendering/rendering_service.mock'; import { integrationsServiceMock } from './integrations/integrations_service.mock'; import { coreAppMock } from './core_app/core_app.mock'; import { themeServiceMock } from './theme/theme_service.mock'; +import { analyticsServiceMock } from './analytics/analytics_service.mock'; + +export const analyticsServiceStartMock = analyticsServiceMock.createAnalyticsServiceStart(); +export const MockAnalyticsService = analyticsServiceMock.create(); +MockAnalyticsService.start.mockReturnValue(analyticsServiceStartMock); +export const AnalyticsServiceConstructor = jest.fn().mockReturnValue(MockAnalyticsService); +jest.doMock('./analytics', () => ({ + AnalyticsService: AnalyticsServiceConstructor, +})); + +export const fetchOptionalMemoryInfoMock = jest.fn(); +jest.doMock('./fetch_optional_memory_info', () => ({ + fetchOptionalMemoryInfo: fetchOptionalMemoryInfoMock, +})); export const MockInjectedMetadataService = injectedMetadataServiceMock.create(); export const InjectedMetadataServiceConstructor = jest diff --git a/src/core/public/core_system.test.ts b/src/core/public/core_system.test.ts index 553c1668951e8..2a57364c9f93f 100644 --- a/src/core/public/core_system.test.ts +++ b/src/core/public/core_system.test.ts @@ -34,6 +34,10 @@ import { MockCoreApp, MockThemeService, ThemeServiceConstructor, + AnalyticsServiceConstructor, + MockAnalyticsService, + analyticsServiceStartMock, + fetchOptionalMemoryInfoMock, } from './core_system.test.mocks'; import { CoreSystem } from './core_system'; @@ -56,6 +60,7 @@ const defaultCoreSystemParams = { }, packageInfo: { dist: false, + version: '1.2.3', }, }, version: 'version', @@ -90,6 +95,7 @@ describe('constructor', () => { expect(IntegrationsServiceConstructor).toHaveBeenCalledTimes(1); expect(CoreAppConstructor).toHaveBeenCalledTimes(1); expect(ThemeServiceConstructor).toHaveBeenCalledTimes(1); + expect(AnalyticsServiceConstructor).toHaveBeenCalledTimes(1); }); it('passes injectedMetadata param to InjectedMetadataService', () => { @@ -146,6 +152,11 @@ describe('#setup()', () => { return core.setup(); } + it('calls analytics#setup()', async () => { + await setupCore(); + expect(MockAnalyticsService.setup).toHaveBeenCalledTimes(1); + }); + it('calls application#setup()', async () => { await setupCore(); expect(MockApplicationService.setup).toHaveBeenCalledTimes(1); @@ -222,6 +233,36 @@ describe('#start()', () => { ); }); + it('reports the event Loaded Kibana', async () => { + await startCore(); + expect(analyticsServiceStartMock.reportEvent).toHaveBeenCalledTimes(1); + expect(analyticsServiceStartMock.reportEvent).toHaveBeenCalledWith('Loaded Kibana', { + kibana_version: '1.2.3', + }); + }); + + it('reports the event Loaded Kibana (with memory)', async () => { + fetchOptionalMemoryInfoMock.mockReturnValue({ + memory_js_heap_size_limit: 3, + memory_js_heap_size_total: 2, + memory_js_heap_size_used: 1, + }); + + await startCore(); + expect(analyticsServiceStartMock.reportEvent).toHaveBeenCalledTimes(1); + expect(analyticsServiceStartMock.reportEvent).toHaveBeenCalledWith('Loaded Kibana', { + kibana_version: '1.2.3', + memory_js_heap_size_limit: 3, + memory_js_heap_size_total: 2, + memory_js_heap_size_used: 1, + }); + }); + + it('calls analytics#start()', async () => { + await startCore(); + expect(MockAnalyticsService.start).toHaveBeenCalledTimes(1); + }); + it('calls application#start()', async () => { await startCore(); expect(MockApplicationService.start).toHaveBeenCalledTimes(1); diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts index 726f53a345208..9ea1f16f7f226 100644 --- a/src/core/public/core_system.ts +++ b/src/core/public/core_system.ts @@ -32,7 +32,9 @@ import { ThemeService } from './theme'; import { CoreApp } from './core_app'; import type { InternalApplicationSetup, InternalApplicationStart } from './application/types'; import { ExecutionContextService } from './execution_context'; +import type { AnalyticsServiceSetup } from './analytics'; import { AnalyticsService } from './analytics'; +import { fetchOptionalMemoryInfo } from './fetch_optional_memory_info'; interface Params { rootDomElement: HTMLElement; @@ -148,9 +150,10 @@ export class CoreSystem { await this.integrations.setup(); this.docLinks.setup(); - const analytics = this.analytics.setup(); + const analytics = this.analytics.setup({ injectedMetadata }); + this.registerLoadedKibanaEventType(analytics); - const executionContext = this.executionContext.setup(); + const executionContext = this.executionContext.setup({ analytics }); const http = this.http.setup({ injectedMetadata, fatalErrors: this.fatalErrorsSetup, @@ -273,6 +276,11 @@ export class CoreSystem { targetDomElement: coreUiTargetDomElement, }); + analytics.reportEvent('Loaded Kibana', { + kibana_version: this.coreContext.env.packageInfo.version, + ...fetchOptionalMemoryInfo(), + }); + return { application, executionContext, @@ -300,6 +308,31 @@ export class CoreSystem { this.application.stop(); this.deprecations.stop(); this.theme.stop(); + this.analytics.stop(); this.rootDomElement.textContent = ''; } + + private registerLoadedKibanaEventType(analytics: AnalyticsServiceSetup) { + analytics.registerEventType({ + eventType: 'Loaded Kibana', + schema: { + kibana_version: { + type: 'keyword', + _meta: { description: 'The version of Kibana' }, + }, + memory_js_heap_size_limit: { + type: 'long', + _meta: { description: 'The maximum size of the heap', optional: true }, + }, + memory_js_heap_size_total: { + type: 'long', + _meta: { description: 'The total size of the heap', optional: true }, + }, + memory_js_heap_size_used: { + type: 'long', + _meta: { description: 'The used size of the heap', optional: true }, + }, + }, + }); + } } diff --git a/src/core/public/execution_context/execution_context_service.test.ts b/src/core/public/execution_context/execution_context_service.test.ts index 70e57b8993bb1..5c8f8bfae89f8 100644 --- a/src/core/public/execution_context/execution_context_service.test.ts +++ b/src/core/public/execution_context/execution_context_service.test.ts @@ -5,23 +5,45 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, firstValueFrom } from 'rxjs'; import { ExecutionContextService, ExecutionContextSetup } from './execution_context_service'; +import type { AnalyticsServiceSetup } from '../analytics'; +import { analyticsServiceMock } from '../analytics/analytics_service.mock'; describe('ExecutionContextService', () => { let execContext: ExecutionContextSetup; let curApp$: BehaviorSubject; let execService: ExecutionContextService; + let analytics: jest.Mocked; beforeEach(() => { + analytics = analyticsServiceMock.createAnalyticsServiceSetup(); execService = new ExecutionContextService(); - execContext = execService.setup(); + execContext = execService.setup({ analytics }); curApp$ = new BehaviorSubject('app1'); execContext = execService.start({ curApp$, }); }); + it('should extend the analytics context', async () => { + expect(analytics.registerContextProvider).toHaveBeenCalledTimes(1); + const context$ = analytics.registerContextProvider.mock.calls[0][0].context$; + execContext.set({ + type: 'ghf', + description: 'first set', + }); + + await expect(firstValueFrom(context$)).resolves.toMatchInlineSnapshot(` + Object { + "applicationId": "app1", + "entityId": undefined, + "page": undefined, + "pageName": "ghf:app1", + } + `); + }); + it('app name updates automatically and clears everything else', () => { execContext.set({ type: 'ghf', diff --git a/src/core/public/execution_context/execution_context_service.ts b/src/core/public/execution_context/execution_context_service.ts index a14d876c9643c..c8d198b9c84f8 100644 --- a/src/core/public/execution_context/execution_context_service.ts +++ b/src/core/public/execution_context/execution_context_service.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ -import { isEqual, isUndefined, omitBy } from 'lodash'; -import { BehaviorSubject, Observable, Subscription } from 'rxjs'; +import { compact, isEqual, isUndefined, omitBy } from 'lodash'; +import { BehaviorSubject, Observable, Subscription, map } from 'rxjs'; +import { AnalyticsServiceSetup } from '../analytics'; import { CoreService, KibanaExecutionContext } from '../../types'; // Should be exported from elastic/apm-rum @@ -55,6 +56,10 @@ export interface ExecutionContextSetup { */ export type ExecutionContextStart = ExecutionContextSetup; +export interface SetupDeps { + analytics: AnalyticsServiceSetup; +} + export interface StartDeps { curApp$: Observable; } @@ -68,7 +73,9 @@ export class ExecutionContextService private subscription: Subscription = new Subscription(); private contract?: ExecutionContextSetup; - public setup() { + public setup({ analytics }: SetupDeps) { + this.enrichAnalyticsContext(analytics); + this.contract = { context$: this.context$.asObservable(), clear: () => { @@ -134,4 +141,45 @@ export class ExecutionContextService ...context, }; } + + /** + * Sets the analytics context provider based on the execution context details. + * @param analytics The analytics service + * @private + */ + private enrichAnalyticsContext(analytics: AnalyticsServiceSetup) { + analytics.registerContextProvider({ + name: 'execution_context', + context$: this.context$.pipe( + map(({ type, name, page, id }) => ({ + pageName: `${compact([type, name, page]).join(':')}`, + applicationId: name ?? type ?? 'unknown', + page, + entityId: id, + })) + ), + schema: { + pageName: { + type: 'keyword', + _meta: { description: 'The name of the current page' }, + }, + page: { + type: 'keyword', + _meta: { description: 'The current page', optional: true }, + }, + applicationId: { + type: 'keyword', + _meta: { description: 'The id of the current application' }, + }, + entityId: { + type: 'keyword', + _meta: { + description: + 'The id of the current entity (dashboard, visualization, canvas, lens, etc)', + optional: true, + }, + }, + }, + }); + } } diff --git a/src/core/public/fetch_optional_memory_info.test.ts b/src/core/public/fetch_optional_memory_info.test.ts new file mode 100644 index 0000000000000..f92fad9c14d63 --- /dev/null +++ b/src/core/public/fetch_optional_memory_info.test.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { fetchOptionalMemoryInfo } from './fetch_optional_memory_info'; + +describe('fetchOptionalMemoryInfo', () => { + test('should return undefined if no memory info is available', () => { + expect(fetchOptionalMemoryInfo()).toBeUndefined(); + }); + + test('should return the memory info when available', () => { + // @ts-expect-error 2339 + window.performance.memory = { + get jsHeapSizeLimit() { + return 3; + }, + get totalJSHeapSize() { + return 2; + }, + get usedJSHeapSize() { + return 1; + }, + }; + expect(fetchOptionalMemoryInfo()).toEqual({ + memory_js_heap_size_limit: 3, + memory_js_heap_size_total: 2, + memory_js_heap_size_used: 1, + }); + }); +}); diff --git a/src/core/public/fetch_optional_memory_info.ts b/src/core/public/fetch_optional_memory_info.ts new file mode 100644 index 0000000000000..b18f3ca2698da --- /dev/null +++ b/src/core/public/fetch_optional_memory_info.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** + * `Performance.memory` output. + * https://developer.mozilla.org/en-US/docs/Web/API/Performance/memory + */ +export interface BrowserPerformanceMemoryInfo { + /** + * The maximum size of the heap, in bytes, that is available to the context. + */ + memory_js_heap_size_limit: number; + /** + * The total allocated heap size, in bytes. + */ + memory_js_heap_size_total: number; + /** + * The currently active segment of JS heap, in bytes. + */ + memory_js_heap_size_used: number; +} + +/** + * Get performance information from the browser (non-standard property). + * @remarks Only available in Google Chrome and MS Edge for now. + */ +export function fetchOptionalMemoryInfo(): BrowserPerformanceMemoryInfo | undefined { + // @ts-expect-error 2339 + const memory = window.performance.memory; + if (memory) { + return { + memory_js_heap_size_limit: memory.jsHeapSizeLimit, + memory_js_heap_size_total: memory.totalJSHeapSize, + memory_js_heap_size_used: memory.usedJSHeapSize, + }; + } +} diff --git a/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap b/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap index 69439d1927745..a3e3ca7a7c207 100644 --- a/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap +++ b/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap @@ -141,6 +141,8 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiImage.openImage": [Function], "euiLink.external.ariaLabel": "External link", "euiLink.newTarget.screenReaderOnlyText": "(opens in a new tab or window)", + "euiMark.highlightEnd": "highlight end", + "euiMark.highlightStart": "highlight start", "euiMarkdownEditorFooter.closeButton": "Close", "euiMarkdownEditorFooter.errorsTitle": "Errors", "euiMarkdownEditorFooter.mdSyntaxLink": "GitHub flavored markdown", diff --git a/src/core/public/i18n/i18n_eui_mapping.tsx b/src/core/public/i18n/i18n_eui_mapping.tsx index 9969f4ee23f57..5344fddc4fe2e 100644 --- a/src/core/public/i18n/i18n_eui_mapping.tsx +++ b/src/core/public/i18n/i18n_eui_mapping.tsx @@ -618,6 +618,12 @@ export const getEuiContextMapping = (): EuiTokensObject => { defaultMessage: '(opens in a new tab or window)', } ), + 'euiMark.highlightStart': i18n.translate('core.euiMark.highlightStart', { + defaultMessage: 'highlight start', + }), + 'euiMark.highlightEnd': i18n.translate('core.euiMark.highlightEnd', { + defaultMessage: 'highlight end', + }), 'euiMarkdownEditorFooter.closeButton': i18n.translate( 'core.euiMarkdownEditorFooter.closeButton', { diff --git a/src/core/public/injected_metadata/injected_metadata_service.mock.ts b/src/core/public/injected_metadata/injected_metadata_service.mock.ts index dc8fe63724411..83903942df53d 100644 --- a/src/core/public/injected_metadata/injected_metadata_service.mock.ts +++ b/src/core/public/injected_metadata/injected_metadata_service.mock.ts @@ -16,6 +16,7 @@ const createSetupContractMock = () => { getPublicBaseUrl: jest.fn(), getKibanaVersion: jest.fn(), getKibanaBranch: jest.fn(), + getElasticsearchInfo: jest.fn(), getCspConfig: jest.fn(), getExternalUrlConfig: jest.fn(), getAnonymousStatusPage: jest.fn(), diff --git a/src/core/public/injected_metadata/injected_metadata_service.test.ts b/src/core/public/injected_metadata/injected_metadata_service.test.ts index 3237401b38fa8..ba0e2470d7f26 100644 --- a/src/core/public/injected_metadata/injected_metadata_service.test.ts +++ b/src/core/public/injected_metadata/injected_metadata_service.test.ts @@ -9,6 +9,36 @@ import { DiscoveredPlugin } from '../../server'; import { InjectedMetadataService } from './injected_metadata_service'; +describe('setup.getElasticsearchInfo()', () => { + it('returns elasticsearch info from injectedMetadata', () => { + const setup = new InjectedMetadataService({ + injectedMetadata: { + clusterInfo: { + cluster_uuid: 'foo', + cluster_name: 'cluster_name', + cluster_version: 'version', + }, + }, + } as any).setup(); + + expect(setup.getElasticsearchInfo()).toEqual({ + cluster_uuid: 'foo', + cluster_name: 'cluster_name', + cluster_version: 'version', + }); + }); + + it('returns elasticsearch info as undefined if not present in the injectedMetadata', () => { + const setup = new InjectedMetadataService({ + injectedMetadata: { + clusterInfo: {}, + }, + } as any).setup(); + + expect(setup.getElasticsearchInfo()).toEqual({}); + }); +}); + describe('setup.getKibanaBuildNumber()', () => { it('returns buildNumber from injectedMetadata', () => { const setup = new InjectedMetadataService({ diff --git a/src/core/public/injected_metadata/injected_metadata_service.ts b/src/core/public/injected_metadata/injected_metadata_service.ts index 07f56b889fc79..2e19da5c2cffe 100644 --- a/src/core/public/injected_metadata/injected_metadata_service.ts +++ b/src/core/public/injected_metadata/injected_metadata_service.ts @@ -27,6 +27,12 @@ export interface InjectedPluginMetadata { }; } +export interface InjectedMetadataClusterInfo { + cluster_uuid?: string; + cluster_name?: string; + cluster_version?: string; +} + /** @internal */ export interface InjectedMetadataParams { injectedMetadata: { @@ -36,6 +42,7 @@ export interface InjectedMetadataParams { basePath: string; serverBasePath: string; publicBaseUrl: string; + clusterInfo: InjectedMetadataClusterInfo; category?: AppCategory; csp: { warnLegacyBrowsers: boolean; @@ -143,6 +150,10 @@ export class InjectedMetadataService { getTheme: () => { return this.state.theme; }, + + getElasticsearchInfo: () => { + return this.state.clusterInfo; + }, }; } } @@ -169,6 +180,7 @@ export interface InjectedMetadataSetup { darkMode: boolean; version: ThemeVersion; }; + getElasticsearchInfo: () => InjectedMetadataClusterInfo; /** * An array of frontend plugins in topological order. */ diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 2a662e3f7f8bd..732ba71fcd2af 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -69,7 +69,7 @@ export { AnalyticsClient } // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver // // @public -export type AnalyticsServiceSetup = AnalyticsClient; +export type AnalyticsServiceSetup = Omit; // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver // @@ -1590,6 +1590,6 @@ export interface UserProvidedValues { // Warnings were encountered during analysis: // -// src/core/public/core_system.ts:192:21 - (ae-forgotten-export) The symbol "InternalApplicationStart" needs to be exported by the entry point index.d.ts +// src/core/public/core_system.ts:195:21 - (ae-forgotten-export) The symbol "InternalApplicationStart" needs to be exported by the entry point index.d.ts ``` diff --git a/src/core/server/analytics/analytics_service.mock.ts b/src/core/server/analytics/analytics_service.mock.ts index 74f675ac37b8f..7a00e573f3e7b 100644 --- a/src/core/server/analytics/analytics_service.mock.ts +++ b/src/core/server/analytics/analytics_service.mock.ts @@ -54,6 +54,7 @@ const createAnalyticsServiceMock = (): jest.Mocked => preboot: jest.fn().mockImplementation(createAnalyticsServicePreboot), setup: jest.fn().mockImplementation(createAnalyticsServiceSetup), start: jest.fn().mockImplementation(createAnalyticsServiceStart), + stop: jest.fn(), }; }; diff --git a/src/core/server/analytics/analytics_service.ts b/src/core/server/analytics/analytics_service.ts index 4262a966ceb10..24389dfa7e938 100644 --- a/src/core/server/analytics/analytics_service.ts +++ b/src/core/server/analytics/analytics_service.ts @@ -8,6 +8,7 @@ import type { AnalyticsClient } from '@kbn/analytics-client'; import { createAnalytics } from '@kbn/analytics-client'; +import { of } from 'rxjs'; import type { CoreContext } from '../core_context'; /** @@ -15,13 +16,13 @@ import type { CoreContext } from '../core_context'; * {@link AnalyticsClient} * @public */ -export type AnalyticsServicePreboot = AnalyticsClient; +export type AnalyticsServicePreboot = Omit; /** * Exposes the public APIs of the AnalyticsClient during the setup phase. * {@link AnalyticsClient} * @public */ -export type AnalyticsServiceSetup = AnalyticsClient; +export type AnalyticsServiceSetup = Omit; /** * Exposes the public APIs of the AnalyticsClient during the start phase * {@link AnalyticsClient} @@ -43,6 +44,8 @@ export class AnalyticsService { // For now, we are relying on whether it's a distributable or running from source. sendTo: core.env.packageInfo.dist ? 'production' : 'staging', }); + + this.registerBuildInfoAnalyticsContext(core); } public preboot(): AnalyticsServicePreboot { @@ -74,4 +77,44 @@ export class AnalyticsService { telemetryCounter$: this.analyticsClient.telemetryCounter$, }; } + + public stop() { + this.analyticsClient.shutdown(); + } + + /** + * Enriches the event with the build information. + * @param core The core context. + * @private + */ + private registerBuildInfoAnalyticsContext(core: CoreContext) { + this.analyticsClient.registerContextProvider({ + name: 'build info', + context$: of({ + isDev: core.env.mode.dev, + isDistributable: core.env.packageInfo.dist, + version: core.env.packageInfo.version, + branch: core.env.packageInfo.branch, + buildNum: core.env.packageInfo.buildNum, + buildSha: core.env.packageInfo.buildSha, + }), + schema: { + isDev: { + type: 'boolean', + _meta: { description: 'Is it running in development mode?' }, + }, + isDistributable: { + type: 'boolean', + _meta: { description: 'Is it running from a distributable?' }, + }, + version: { type: 'keyword', _meta: { description: 'Version of the Kibana instance.' } }, + branch: { + type: 'keyword', + _meta: { description: 'Branch of source running Kibana from.' }, + }, + buildNum: { type: 'long', _meta: { description: 'Build number of the Kibana instance.' } }, + buildSha: { type: 'keyword', _meta: { description: 'Build SHA of the Kibana instance.' } }, + }, + }); + } } diff --git a/src/core/server/elasticsearch/elasticsearch_service.mock.ts b/src/core/server/elasticsearch/elasticsearch_service.mock.ts index 3ef44e2690a95..02a846a5b8011 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.mock.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.mock.ts @@ -25,6 +25,7 @@ import { } from './types'; import { NodesVersionCompatibility } from './version_check/ensure_es_version'; import { ServiceStatus, ServiceStatusLevels } from '../status'; +import type { ClusterInfo } from './get_cluster_info'; type MockedElasticSearchServicePreboot = jest.Mocked; @@ -89,6 +90,11 @@ const createInternalSetupContractMock = () => { warningNodes: [], kibanaVersion: '8.0.0', }), + clusterInfo$: new BehaviorSubject({ + cluster_uuid: 'cluster-uuid', + cluster_name: 'cluster-name', + cluster_version: '8.0.0', + }), status$: new BehaviorSubject>({ level: ServiceStatusLevels.available, summary: 'Elasticsearch is available', diff --git a/src/core/server/elasticsearch/elasticsearch_service.test.ts b/src/core/server/elasticsearch/elasticsearch_service.test.ts index def2c400258b5..875995cd7cd96 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.test.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.test.ts @@ -34,6 +34,7 @@ import { elasticsearchClientMock } from './client/mocks'; import { duration } from 'moment'; import { isValidConnection as isValidConnectionMock } from './is_valid_connection'; import { pollEsNodesVersion as pollEsNodesVersionMocked } from './version_check/ensure_es_version'; +import { analyticsServiceMock } from '../analytics/analytics_service.mock'; const { pollEsNodesVersion: pollEsNodesVersionActual } = jest.requireActual( './version_check/ensure_es_version' @@ -53,6 +54,7 @@ let setupDeps: SetupDeps; beforeEach(() => { setupDeps = { + analytics: analyticsServiceMock.createAnalyticsServiceSetup(), http: httpServiceMock.createInternalSetupContract(), executionContext: executionContextServiceMock.createInternalSetupContract(), }; diff --git a/src/core/server/elasticsearch/elasticsearch_service.ts b/src/core/server/elasticsearch/elasticsearch_service.ts index d0cf23c539416..09e8b3172c8e7 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.ts @@ -9,6 +9,8 @@ import { firstValueFrom, Observable, Subject } from 'rxjs'; import { map, shareReplay, takeUntil } from 'rxjs/operators'; +import { registerAnalyticsContextProvider } from './register_analytics_context_provider'; +import { AnalyticsServiceSetup } from '../analytics'; import { CoreService } from '../../types'; import { CoreContext } from '../core_context'; import { Logger } from '../logging'; @@ -29,8 +31,10 @@ import { isValidConnection } from './is_valid_connection'; import { isInlineScriptingEnabled } from './is_scripting_enabled'; import type { UnauthorizedErrorHandler } from './client/retry_unauthorized'; import { mergeConfig } from './merge_config'; +import { getClusterInfo$ } from './get_cluster_info'; export interface SetupDeps { + analytics: AnalyticsServiceSetup; http: InternalHttpServiceSetup; executionContext: InternalExecutionContextSetup; } @@ -92,10 +96,14 @@ export class ElasticsearchService this.esNodesCompatibility$ = esNodesCompatibility$; + const clusterInfo$ = getClusterInfo$(this.client.asInternalUser); + registerAnalyticsContextProvider(deps.analytics, clusterInfo$); + return { legacy: { config$: this.config$, }, + clusterInfo$, esNodesCompatibility$, status$: calculateStatus$(esNodesCompatibility$), setUnauthorizedErrorHandler: (handler) => { diff --git a/src/core/server/elasticsearch/get_cluster_info.test.ts b/src/core/server/elasticsearch/get_cluster_info.test.ts new file mode 100644 index 0000000000000..fd3b3b71844ac --- /dev/null +++ b/src/core/server/elasticsearch/get_cluster_info.test.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { elasticsearchClientMock } from './client/mocks'; +import { firstValueFrom } from 'rxjs'; +import { getClusterInfo$ } from './get_cluster_info'; + +describe('getClusterInfo', () => { + let internalClient: ReturnType; + const infoResponse = { + cluster_name: 'cluster-name', + cluster_uuid: 'cluster_uuid', + name: 'name', + tagline: 'tagline', + version: { + number: '1.2.3', + lucene_version: '1.2.3', + build_date: 'DateString', + build_flavor: 'string', + build_hash: 'string', + build_snapshot: true, + build_type: 'string', + minimum_index_compatibility_version: '1.2.3', + minimum_wire_compatibility_version: '1.2.3', + }, + }; + + beforeEach(() => { + internalClient = elasticsearchClientMock.createInternalClient(); + }); + + test('it provides the context', async () => { + internalClient.info.mockResolvedValue(infoResponse); + const context$ = getClusterInfo$(internalClient); + await expect(firstValueFrom(context$)).resolves.toMatchInlineSnapshot(` + Object { + "cluster_name": "cluster-name", + "cluster_uuid": "cluster_uuid", + "cluster_version": "1.2.3", + } + `); + }); + + test('it retries if it fails to fetch the cluster info', async () => { + internalClient.info.mockRejectedValueOnce(new Error('Failed to fetch cluster info')); + internalClient.info.mockResolvedValue(infoResponse); + const context$ = getClusterInfo$(internalClient); + await expect(firstValueFrom(context$)).resolves.toMatchInlineSnapshot(` + Object { + "cluster_name": "cluster-name", + "cluster_uuid": "cluster_uuid", + "cluster_version": "1.2.3", + } + `); + expect(internalClient.info).toHaveBeenCalledTimes(2); + }); + + test('multiple subscribers do not trigger more ES requests', async () => { + internalClient.info.mockResolvedValue(infoResponse); + const context$ = getClusterInfo$(internalClient); + await expect(firstValueFrom(context$)).resolves.toMatchInlineSnapshot(` + Object { + "cluster_name": "cluster-name", + "cluster_uuid": "cluster_uuid", + "cluster_version": "1.2.3", + } + `); + await expect(firstValueFrom(context$)).resolves.toMatchInlineSnapshot(` + Object { + "cluster_name": "cluster-name", + "cluster_uuid": "cluster_uuid", + "cluster_version": "1.2.3", + } + `); + expect(internalClient.info).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/core/server/elasticsearch/get_cluster_info.ts b/src/core/server/elasticsearch/get_cluster_info.ts new file mode 100644 index 0000000000000..c807965d3bbf8 --- /dev/null +++ b/src/core/server/elasticsearch/get_cluster_info.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { Observable } from 'rxjs'; +import { defer, map, retry, shareReplay } from 'rxjs'; +import type { ElasticsearchClient } from './client'; + +/** @private */ +export interface ClusterInfo { + cluster_name: string; + cluster_uuid: string; + cluster_version: string; +} + +/** + * Returns the cluster info from the Elasticsearch cluster. + * @param internalClient Elasticsearch client + * @private + */ +export function getClusterInfo$(internalClient: ElasticsearchClient): Observable { + return defer(() => internalClient.info()).pipe( + map((info) => ({ + cluster_name: info.cluster_name, + cluster_uuid: info.cluster_uuid, + cluster_version: info.version.number, + })), + retry({ delay: 1000 }), + shareReplay(1) + ); +} diff --git a/src/core/server/elasticsearch/register_analytics_context_provider.test.ts b/src/core/server/elasticsearch/register_analytics_context_provider.test.ts new file mode 100644 index 0000000000000..4f09ea8677f44 --- /dev/null +++ b/src/core/server/elasticsearch/register_analytics_context_provider.test.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { firstValueFrom, of } from 'rxjs'; +import type { AnalyticsServiceSetup } from '../analytics'; +import { analyticsServiceMock } from '../analytics/analytics_service.mock'; +import { registerAnalyticsContextProvider } from './register_analytics_context_provider'; + +describe('registerAnalyticsContextProvider', () => { + let analyticsMock: jest.Mocked; + + beforeEach(() => { + analyticsMock = analyticsServiceMock.createAnalyticsServiceSetup(); + }); + + test('it provides the context', async () => { + registerAnalyticsContextProvider( + analyticsMock, + of({ cluster_name: 'cluster-name', cluster_uuid: 'cluster_uuid', cluster_version: '1.2.3' }) + ); + const { context$ } = analyticsMock.registerContextProvider.mock.calls[0][0]; + await expect(firstValueFrom(context$)).resolves.toMatchInlineSnapshot(` + Object { + "cluster_name": "cluster-name", + "cluster_uuid": "cluster_uuid", + "cluster_version": "1.2.3", + } + `); + }); +}); diff --git a/src/core/server/elasticsearch/register_analytics_context_provider.ts b/src/core/server/elasticsearch/register_analytics_context_provider.ts new file mode 100644 index 0000000000000..cc4523c0d4eb5 --- /dev/null +++ b/src/core/server/elasticsearch/register_analytics_context_provider.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { Observable } from 'rxjs'; +import type { AnalyticsServiceSetup } from '../analytics'; +import type { ClusterInfo } from './get_cluster_info'; + +/** + * Registers the Analytics context provider to enrich events with the cluster info. + * @param analytics Analytics service. + * @param context$ Observable emitting the cluster info. + * @private + */ +export function registerAnalyticsContextProvider( + analytics: AnalyticsServiceSetup, + context$: Observable +) { + analytics.registerContextProvider({ + name: 'elasticsearch info', + context$, + schema: { + cluster_name: { type: 'keyword', _meta: { description: 'The Cluster Name' } }, + cluster_uuid: { type: 'keyword', _meta: { description: 'The Cluster UUID' } }, + cluster_version: { type: 'keyword', _meta: { description: 'The Cluster version' } }, + }, + }); +} diff --git a/src/core/server/elasticsearch/types.ts b/src/core/server/elasticsearch/types.ts index 1f363804b3a33..12ba2575d2726 100644 --- a/src/core/server/elasticsearch/types.ts +++ b/src/core/server/elasticsearch/types.ts @@ -14,6 +14,7 @@ import { IClusterClient, ICustomClusterClient, ElasticsearchClientConfig } from import { NodesVersionCompatibility } from './version_check/ensure_es_version'; import { ServiceStatus } from '../status'; import type { UnauthorizedErrorHandler } from './client/retry_unauthorized'; +import { ClusterInfo } from './get_cluster_info'; /** * @public @@ -97,6 +98,7 @@ export type InternalElasticsearchServicePreboot = ElasticsearchServicePreboot; /** @internal */ export interface InternalElasticsearchServiceSetup extends ElasticsearchServiceSetup { + clusterInfo$: Observable; esNodesCompatibility$: Observable; status$: Observable>; } diff --git a/src/core/server/environment/environment_service.test.ts b/src/core/server/environment/environment_service.test.ts index 0817fad35f882..c285edc443ce8 100644 --- a/src/core/server/environment/environment_service.test.ts +++ b/src/core/server/environment/environment_service.test.ts @@ -13,10 +13,12 @@ import { resolveInstanceUuid } from './resolve_uuid'; import { createDataFolder } from './create_data_folder'; import { writePidFile } from './write_pid_file'; import { CoreContext } from '../core_context'; +import type { AnalyticsServicePreboot } from '../analytics'; import { configServiceMock } from '../config/mocks'; import { loggingSystemMock } from '../logging/logging_system.mock'; import { mockCoreContext } from '../core_context.mock'; +import { analyticsServiceMock } from '../analytics/analytics_service.mock'; jest.mock('./resolve_uuid', () => ({ resolveInstanceUuid: jest.fn().mockResolvedValue('SOME_UUID'), @@ -63,11 +65,13 @@ describe('UuidService', () => { let configService: ReturnType; let coreContext: CoreContext; let service: EnvironmentService; + let analytics: AnalyticsServicePreboot; beforeEach(async () => { logger = loggingSystemMock.create(); configService = getConfigService(); coreContext = mockCoreContext.create({ logger, configService }); + analytics = analyticsServiceMock.createAnalyticsServicePreboot(); service = new EnvironmentService(coreContext); }); @@ -78,7 +82,7 @@ describe('UuidService', () => { describe('#preboot()', () => { it('calls resolveInstanceUuid with correct parameters', async () => { - await service.preboot(); + await service.preboot({ analytics }); expect(resolveInstanceUuid).toHaveBeenCalledTimes(1); expect(resolveInstanceUuid).toHaveBeenCalledWith({ @@ -89,7 +93,7 @@ describe('UuidService', () => { }); it('calls createDataFolder with correct parameters', async () => { - await service.preboot(); + await service.preboot({ analytics }); expect(createDataFolder).toHaveBeenCalledTimes(1); expect(createDataFolder).toHaveBeenCalledWith({ @@ -99,7 +103,7 @@ describe('UuidService', () => { }); it('calls writePidFile with correct parameters', async () => { - await service.preboot(); + await service.preboot({ analytics }); expect(writePidFile).toHaveBeenCalledTimes(1); expect(writePidFile).toHaveBeenCalledWith({ @@ -109,14 +113,14 @@ describe('UuidService', () => { }); it('returns the uuid resolved from resolveInstanceUuid', async () => { - const preboot = await service.preboot(); + const preboot = await service.preboot({ analytics }); expect(preboot.instanceUuid).toEqual('SOME_UUID'); }); describe('process warnings', () => { it('logs warnings coming from the process', async () => { - await service.preboot(); + await service.preboot({ analytics }); const warning = new Error('something went wrong'); process.emit('warning', warning); @@ -126,7 +130,7 @@ describe('UuidService', () => { }); it('does not log deprecation warnings', async () => { - await service.preboot(); + await service.preboot({ analytics }); const warning = new Error('something went wrong'); warning.name = 'DeprecationWarning'; @@ -139,7 +143,7 @@ describe('UuidService', () => { // TODO: From Nodejs v16 emitting an unhandledRejection will kill the process describe.skip('unhandledRejection warnings', () => { it('logs warn for an unhandeld promise rejected with an Error', async () => { - await service.preboot(); + await service.preboot({ analytics }); const err = new Error('something went wrong'); process.emit('unhandledRejection', err, new Promise((res, rej) => rej(err))); @@ -151,7 +155,7 @@ describe('UuidService', () => { }); it('logs warn for an unhandeld promise rejected with a string', async () => { - await service.preboot(); + await service.preboot({ analytics }); const err = 'something went wrong'; process.emit('unhandledRejection', err, new Promise((res, rej) => rej(err))); @@ -166,7 +170,7 @@ describe('UuidService', () => { describe('#setup()', () => { it('returns the uuid resolved from resolveInstanceUuid', async () => { - await expect(service.preboot()).resolves.toEqual({ instanceUuid: 'SOME_UUID' }); + await expect(service.preboot({ analytics })).resolves.toEqual({ instanceUuid: 'SOME_UUID' }); expect(service.setup()).toEqual({ instanceUuid: 'SOME_UUID' }); }); }); diff --git a/src/core/server/environment/environment_service.ts b/src/core/server/environment/environment_service.ts index 65c03b108b28a..28e2da446eb95 100644 --- a/src/core/server/environment/environment_service.ts +++ b/src/core/server/environment/environment_service.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ -import { firstValueFrom } from 'rxjs'; +import { firstValueFrom, of } from 'rxjs'; import { PathConfigType, config as pathConfigDef } from '@kbn/utils'; +import type { AnalyticsServicePreboot } from '../analytics'; import { CoreContext } from '../core_context'; import { Logger } from '../logging'; import { IConfigService } from '../config'; @@ -17,6 +18,16 @@ import { resolveInstanceUuid } from './resolve_uuid'; import { createDataFolder } from './create_data_folder'; import { writePidFile } from './write_pid_file'; +/** + * @internal + */ +export interface PrebootDeps { + /** + * {@link AnalyticsServicePreboot} + */ + analytics: AnalyticsServicePreboot; +} + /** * @internal */ @@ -45,7 +56,7 @@ export class EnvironmentService { this.configService = core.configService; } - public async preboot() { + public async preboot({ analytics }: PrebootDeps) { // IMPORTANT: This code is based on the assumption that none of the configuration values used // here is supposed to change during preboot phase and it's safe to read them only once. const [pathConfig, serverConfig, pidConfig] = await Promise.all([ @@ -77,6 +88,24 @@ export class EnvironmentService { logger: this.log, }); + analytics.registerContextProvider({ + name: 'kibana info', + context$: of({ + kibana_uuid: this.uuid, + pid: process.pid, + }), + schema: { + kibana_uuid: { + type: 'keyword', + _meta: { description: 'Kibana instance UUID' }, + }, + pid: { + type: 'long', + _meta: { description: 'Process ID' }, + }, + }, + }); + return { instanceUuid: this.uuid, }; diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts index f251d3fb64cab..557a10da0839d 100644 --- a/src/core/server/http/http_service.mock.ts +++ b/src/core/server/http/http_service.mock.ts @@ -45,8 +45,9 @@ export type HttpServiceSetupMock = jest.Mocked< createRouter: jest.MockedFunction<() => RouterMock>; }; export type InternalHttpServiceSetupMock = jest.Mocked< - Omit + Omit > & { + auth: AuthMocked; basePath: BasePathMocked; createRouter: jest.MockedFunction<(path: string) => RouterMock>; authRequestHeaders: jest.Mocked; diff --git a/src/core/server/rendering/__mocks__/params.ts b/src/core/server/rendering/__mocks__/params.ts index 091d185cceefc..b4ead2e628688 100644 --- a/src/core/server/rendering/__mocks__/params.ts +++ b/src/core/server/rendering/__mocks__/params.ts @@ -7,6 +7,7 @@ */ import { mockCoreContext } from '../../core_context.mock'; +import { elasticsearchServiceMock } from '../../elasticsearch/elasticsearch_service.mock'; import { httpServiceMock } from '../../http/http_service.mock'; import { pluginServiceMock } from '../../plugins/plugins_service.mock'; import { statusServiceMock } from '../../status/status_service.mock'; @@ -15,6 +16,7 @@ const context = mockCoreContext.create(); const httpPreboot = httpServiceMock.createInternalPrebootContract(); const httpSetup = httpServiceMock.createInternalSetupContract(); const status = statusServiceMock.createInternalSetupContract(); +const elasticsearch = elasticsearchServiceMock.createInternalSetup(); export const mockRenderingServiceParams = context; export const mockRenderingPrebootDeps = { @@ -22,6 +24,7 @@ export const mockRenderingPrebootDeps = { uiPlugins: pluginServiceMock.createUiPlugins(), }; export const mockRenderingSetupDeps = { + elasticsearch, http: httpSetup, uiPlugins: pluginServiceMock.createUiPlugins(), status, diff --git a/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap b/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap index 4abf24911808c..9fe0cb545e7aa 100644 --- a/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap +++ b/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap @@ -6,6 +6,7 @@ Object { "basePath": "/mock-server-basepath", "branch": Any, "buildNumber": Any, + "clusterInfo": Object {}, "csp": Object { "warnLegacyBrowsers": true, }, @@ -61,6 +62,7 @@ Object { "basePath": "/mock-server-basepath", "branch": Any, "buildNumber": Any, + "clusterInfo": Object {}, "csp": Object { "warnLegacyBrowsers": true, }, @@ -120,6 +122,7 @@ Object { "basePath": "", "branch": Any, "buildNumber": Any, + "clusterInfo": Object {}, "csp": Object { "warnLegacyBrowsers": true, }, @@ -169,12 +172,69 @@ Object { } `; +exports[`RenderingService preboot() render() renders "core" page for unauthenticated requests 1`] = ` +Object { + "anonymousStatusPage": false, + "basePath": "/mock-server-basepath", + "branch": Any, + "buildNumber": Any, + "clusterInfo": Object {}, + "csp": Object { + "warnLegacyBrowsers": true, + }, + "env": Object { + "mode": Object { + "dev": Any, + "name": Any, + "prod": Any, + }, + "packageInfo": Object { + "branch": Any, + "buildNum": Any, + "buildSha": Any, + "dist": Any, + "version": Any, + }, + }, + "externalUrl": Object { + "policy": Array [ + Object { + "allow": true, + }, + ], + }, + "i18n": Object { + "translationsUrl": "/mock-server-basepath/translations/en.json", + }, + "legacyMetadata": Object { + "uiSettings": Object { + "defaults": Object { + "registered": Object { + "name": "title", + }, + }, + "user": Object {}, + }, + }, + "publicBaseUrl": "http://myhost.com/mock-server-basepath", + "serverBasePath": "/mock-server-basepath", + "theme": Object { + "darkMode": "theme:darkMode", + "version": "v8", + }, + "uiPlugins": Array [], + "vars": Object {}, + "version": Any, +} +`; + exports[`RenderingService preboot() render() renders "core" with excluded user settings 1`] = ` Object { "anonymousStatusPage": false, "basePath": "/mock-server-basepath", "branch": Any, "buildNumber": Any, + "clusterInfo": Object {}, "csp": Object { "warnLegacyBrowsers": true, }, @@ -230,6 +290,7 @@ Object { "basePath": "/mock-server-basepath", "branch": Any, "buildNumber": Any, + "clusterInfo": Object {}, "csp": Object { "warnLegacyBrowsers": true, }, @@ -285,6 +346,11 @@ Object { "basePath": "/mock-server-basepath", "branch": Any, "buildNumber": Any, + "clusterInfo": Object { + "cluster_name": "cluster-name", + "cluster_uuid": "cluster-uuid", + "cluster_version": "8.0.0", + }, "csp": Object { "warnLegacyBrowsers": true, }, @@ -344,6 +410,11 @@ Object { "basePath": "", "branch": Any, "buildNumber": Any, + "clusterInfo": Object { + "cluster_name": "cluster-name", + "cluster_uuid": "cluster-uuid", + "cluster_version": "8.0.0", + }, "csp": Object { "warnLegacyBrowsers": true, }, @@ -393,12 +464,73 @@ Object { } `; +exports[`RenderingService setup() render() renders "core" page for unauthenticated requests 1`] = ` +Object { + "anonymousStatusPage": false, + "basePath": "/mock-server-basepath", + "branch": Any, + "buildNumber": Any, + "clusterInfo": Object {}, + "csp": Object { + "warnLegacyBrowsers": true, + }, + "env": Object { + "mode": Object { + "dev": Any, + "name": Any, + "prod": Any, + }, + "packageInfo": Object { + "branch": Any, + "buildNum": Any, + "buildSha": Any, + "dist": Any, + "version": Any, + }, + }, + "externalUrl": Object { + "policy": Array [ + Object { + "allow": true, + }, + ], + }, + "i18n": Object { + "translationsUrl": "/mock-server-basepath/translations/en.json", + }, + "legacyMetadata": Object { + "uiSettings": Object { + "defaults": Object { + "registered": Object { + "name": "title", + }, + }, + "user": Object {}, + }, + }, + "publicBaseUrl": "http://myhost.com/mock-server-basepath", + "serverBasePath": "/mock-server-basepath", + "theme": Object { + "darkMode": "theme:darkMode", + "version": "v8", + }, + "uiPlugins": Array [], + "vars": Object {}, + "version": Any, +} +`; + exports[`RenderingService setup() render() renders "core" with excluded user settings 1`] = ` Object { "anonymousStatusPage": false, "basePath": "/mock-server-basepath", "branch": Any, "buildNumber": Any, + "clusterInfo": Object { + "cluster_name": "cluster-name", + "cluster_uuid": "cluster-uuid", + "cluster_version": "8.0.0", + }, "csp": Object { "warnLegacyBrowsers": true, }, diff --git a/src/core/server/rendering/rendering_service.test.ts b/src/core/server/rendering/rendering_service.test.ts index cb10d01e85773..8aecc536d8846 100644 --- a/src/core/server/rendering/rendering_service.test.ts +++ b/src/core/server/rendering/rendering_service.test.ts @@ -25,6 +25,7 @@ import { } from './__mocks__/params'; import { InternalRenderingServicePreboot, InternalRenderingServiceSetup } from './types'; import { RenderingService } from './rendering_service'; +import { AuthStatus } from '../http/auth_state_storage'; const INJECTED_METADATA = { version: expect.any(String), @@ -75,6 +76,23 @@ function renderTestCases( expect(data).toMatchSnapshot(INJECTED_METADATA); }); + it('renders "core" page for unauthenticated requests', async () => { + mockRenderingSetupDeps.http.auth.get.mockReturnValueOnce({ + status: AuthStatus.unauthenticated, + state: {}, + }); + + const [render] = await getRender(); + const content = await render( + createKibanaRequest({ auth: { isAuthenticated: false } }), + uiSettings + ); + const dom = load(content); + const data = JSON.parse(dom('kbn-injected-metadata').attr('data') ?? '""'); + + expect(data).toMatchSnapshot(INJECTED_METADATA); + }); + it('renders "core" page for blank basepath', async () => { const [render, deps] = await getRender(); deps.http.basePath.get.mockReturnValueOnce(''); diff --git a/src/core/server/rendering/rendering_service.tsx b/src/core/server/rendering/rendering_service.tsx index 73746a8f202ff..3e50aac6fcbdd 100644 --- a/src/core/server/rendering/rendering_service.tsx +++ b/src/core/server/rendering/rendering_service.tsx @@ -8,10 +8,11 @@ import React from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; -import { take } from 'rxjs/operators'; +import { catchError, take, timeout } from 'rxjs/operators'; import { i18n } from '@kbn/i18n'; import type { ThemeVersion } from '@kbn/ui-shared-deps-npm'; +import { firstValueFrom, of } from 'rxjs'; import type { UiPlugins } from '../plugins'; import { CoreContext } from '../core_context'; import { Template } from './views'; @@ -25,11 +26,13 @@ import { } from './types'; import { registerBootstrapRoute, bootstrapRendererFactory } from './bootstrap'; import { getSettingValue, getStylesheetPaths } from './render_utils'; -import { KibanaRequest } from '../http'; +import type { HttpAuth, KibanaRequest } from '../http'; import { IUiSettingsClient } from '../ui_settings'; import { filterUiPlugins } from './filter_ui_plugins'; -type RenderOptions = (RenderingPrebootDeps & { status?: never }) | RenderingSetupDeps; +type RenderOptions = + | (RenderingPrebootDeps & { status?: never; elasticsearch?: never }) + | RenderingSetupDeps; /** @internal */ export class RenderingService { @@ -57,6 +60,7 @@ export class RenderingService { } public async setup({ + elasticsearch, http, status, uiPlugins, @@ -72,12 +76,12 @@ export class RenderingService { }); return { - render: this.render.bind(this, { http, uiPlugins, status }), + render: this.render.bind(this, { elasticsearch, http, uiPlugins, status }), }; } private async render( - { http, uiPlugins, status }: RenderOptions, + { elasticsearch, http, uiPlugins, status }: RenderOptions, request: KibanaRequest, uiSettings: IUiSettingsClient, { isAnonymousPage = false, vars, includeExposedConfigKeys }: IRenderOptions = {} @@ -94,6 +98,21 @@ export class RenderingService { user: isAnonymousPage ? {} : await uiSettings.getUserProvided(), }; + let clusterInfo = {}; + try { + // Only provide the clusterInfo if the request is authenticated and the elasticsearch service is available. + if (isAuthenticated(http.auth, request) && elasticsearch) { + clusterInfo = await firstValueFrom( + elasticsearch.clusterInfo$.pipe( + timeout(50), // If not available, just return undefined + catchError(() => of({})) + ) + ); + } + } catch (err) { + // swallow error + } + const darkMode = getSettingValue('theme:darkMode', settings, Boolean); const themeVersion: ThemeVersion = 'v8'; @@ -123,6 +142,7 @@ export class RenderingService { serverBasePath, publicBaseUrl, env, + clusterInfo, anonymousStatusPage: status?.isStatusPageAnonymous() ?? false, i18n: { translationsUrl: `${basePath}/translations/${i18n.getLocale()}.json`, @@ -164,3 +184,9 @@ const getUiConfig = async (uiPlugins: UiPlugins, pluginId: string) => { exposedConfigKeys: {}, }) as { browserConfig: Record; exposedConfigKeys: Record }; }; + +const isAuthenticated = (auth: HttpAuth, request: KibanaRequest) => { + const { status: authStatus } = auth.get(request); + // status is 'unknown' when auth is disabled. we just need to not be `unauthenticated` here. + return authStatus !== 'unauthenticated'; +}; diff --git a/src/core/server/rendering/types.ts b/src/core/server/rendering/types.ts index 2c0aafe61e018..82758018b859d 100644 --- a/src/core/server/rendering/types.ts +++ b/src/core/server/rendering/types.ts @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { ThemeVersion } from '@kbn/ui-shared-deps-npm'; +import { InternalElasticsearchServiceSetup } from '../elasticsearch'; import { EnvironmentMode, PackageInfo } from '../config'; import { ICspConfig } from '../csp'; import { InternalHttpServicePreboot, InternalHttpServiceSetup, KibanaRequest } from '../http'; @@ -38,6 +39,11 @@ export interface InjectedMetadata { basePath: string; serverBasePath: string; publicBaseUrl?: string; + clusterInfo: { + cluster_uuid?: string; + cluster_name?: string; + cluster_version?: string; + }; env: { mode: EnvironmentMode; packageInfo: PackageInfo; @@ -74,6 +80,7 @@ export interface RenderingPrebootDeps { /** @internal */ export interface RenderingSetupDeps { + elasticsearch: InternalElasticsearchServiceSetup; http: InternalHttpServiceSetup; status: InternalStatusServiceSetup; uiPlugins: UiPlugins; diff --git a/src/core/server/saved_objects/import/import_saved_objects.ts b/src/core/server/saved_objects/import/import_saved_objects.ts index 0631d97b58a72..9e9f5f8b050dc 100644 --- a/src/core/server/saved_objects/import/import_saved_objects.ts +++ b/src/core/server/saved_objects/import/import_saved_objects.ts @@ -35,6 +35,8 @@ export interface ImportSavedObjectsOptions { objectLimit: number; /** If true, will override existing object if present. Note: this has no effect when used with the `createNewCopies` option. */ overwrite: boolean; + /** Refresh setting, defaults to `wait_for` */ + refresh?: boolean | 'wait_for'; /** {@link SavedObjectsClientContract | client} to use to perform the import operation */ savedObjectsClient: SavedObjectsClientContract; /** The registry of all known saved object types */ @@ -62,6 +64,7 @@ export async function importSavedObjectsFromStream({ typeRegistry, importHooks, namespace, + refresh, }: ImportSavedObjectsOptions): Promise { let errorAccumulator: SavedObjectsImportFailure[] = []; const supportedTypes = typeRegistry.getImportableAndExportableTypes().map((type) => type.name); @@ -141,6 +144,7 @@ export async function importSavedObjectsFromStream({ importStateMap, overwrite, namespace, + refresh, }; const createSavedObjectsResult = await createSavedObjects(createSavedObjectsParams); errorAccumulator = [...errorAccumulator, ...createSavedObjectsResult.errors]; diff --git a/src/core/server/saved_objects/import/lib/create_saved_objects.ts b/src/core/server/saved_objects/import/lib/create_saved_objects.ts index bf58b2bb4b00e..d6c7cbe934b51 100644 --- a/src/core/server/saved_objects/import/lib/create_saved_objects.ts +++ b/src/core/server/saved_objects/import/lib/create_saved_objects.ts @@ -18,6 +18,7 @@ export interface CreateSavedObjectsParams { importStateMap: ImportStateMap; namespace?: string; overwrite?: boolean; + refresh?: boolean | 'wait_for'; } export interface CreateSavedObjectsResult { createdObjects: Array>; @@ -35,6 +36,7 @@ export const createSavedObjects = async ({ importStateMap, namespace, overwrite, + refresh, }: CreateSavedObjectsParams): Promise> => { // filter out any objects that resulted in errors const errorSet = accumulatedErrors.reduce( @@ -87,6 +89,7 @@ export const createSavedObjects = async ({ const bulkCreateResponse = await savedObjectsClient.bulkCreate(objectsToCreate, { namespace, overwrite, + refresh, }); expectedResults = bulkCreateResponse.saved_objects; } diff --git a/src/core/server/saved_objects/import/saved_objects_importer.ts b/src/core/server/saved_objects/import/saved_objects_importer.ts index f4572e58d6fad..e9c54f7b44deb 100644 --- a/src/core/server/saved_objects/import/saved_objects_importer.ts +++ b/src/core/server/saved_objects/import/saved_objects_importer.ts @@ -66,12 +66,14 @@ export class SavedObjectsImporter { createNewCopies, namespace, overwrite, + refresh, }: SavedObjectsImportOptions): Promise { return importSavedObjectsFromStream({ readStream, createNewCopies, namespace, overwrite, + refresh, objectLimit: this.#importSizeLimit, savedObjectsClient: this.#savedObjectsClient, typeRegistry: this.#typeRegistry, diff --git a/src/core/server/saved_objects/import/types.ts b/src/core/server/saved_objects/import/types.ts index ccf58c99f1ad8..d3a38b48e92cb 100644 --- a/src/core/server/saved_objects/import/types.ts +++ b/src/core/server/saved_objects/import/types.ts @@ -155,6 +155,8 @@ export interface SavedObjectsImportOptions { namespace?: string; /** If true, will create new copies of import objects, each with a random `id` and undefined `originId`. */ createNewCopies: boolean; + /** Refresh setting, defaults to `wait_for` */ + refresh?: boolean | 'wait_for'; } /** diff --git a/src/core/server/saved_objects/migrations/__snapshots__/migrations_state_action_machine.test.ts.snap b/src/core/server/saved_objects/migrations/__snapshots__/migrations_state_action_machine.test.ts.snap index b1dcd51bbdd0d..e6e1fc2cdc21d 100644 --- a/src/core/server/saved_objects/migrations/__snapshots__/migrations_state_action_machine.test.ts.snap +++ b/src/core/server/saved_objects/migrations/__snapshots__/migrations_state_action_machine.test.ts.snap @@ -33,7 +33,9 @@ Object { ], "maxBatchSizeBytes": 100000000, "migrationDocLinks": Object { + "repeatedTimeoutRequests": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#_repeated_time_out_requests_that_eventually_fail", "resolveMigrationFailures": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html", + "routingAllocationDisabled": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#routing-allocation-disabled", }, "outdatedDocuments": Array [], "outdatedDocumentsQuery": Object { @@ -137,6 +139,11 @@ Object { "type": "tsvb-validation-telemetry", }, }, + Object { + "term": Object { + "type": "ui-counter", + }, + }, Object { "bool": Object { "must": Array [ @@ -197,7 +204,9 @@ Object { ], "maxBatchSizeBytes": 100000000, "migrationDocLinks": Object { + "repeatedTimeoutRequests": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#_repeated_time_out_requests_that_eventually_fail", "resolveMigrationFailures": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html", + "routingAllocationDisabled": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#routing-allocation-disabled", }, "outdatedDocuments": Array [], "outdatedDocumentsQuery": Object { @@ -301,6 +310,11 @@ Object { "type": "tsvb-validation-telemetry", }, }, + Object { + "term": Object { + "type": "ui-counter", + }, + }, Object { "bool": Object { "must": Array [ @@ -365,7 +379,9 @@ Object { ], "maxBatchSizeBytes": 100000000, "migrationDocLinks": Object { + "repeatedTimeoutRequests": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#_repeated_time_out_requests_that_eventually_fail", "resolveMigrationFailures": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html", + "routingAllocationDisabled": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#routing-allocation-disabled", }, "outdatedDocuments": Array [], "outdatedDocumentsQuery": Object { @@ -469,6 +485,11 @@ Object { "type": "tsvb-validation-telemetry", }, }, + Object { + "term": Object { + "type": "ui-counter", + }, + }, Object { "bool": Object { "must": Array [ @@ -537,7 +558,9 @@ Object { ], "maxBatchSizeBytes": 100000000, "migrationDocLinks": Object { + "repeatedTimeoutRequests": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#_repeated_time_out_requests_that_eventually_fail", "resolveMigrationFailures": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html", + "routingAllocationDisabled": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#routing-allocation-disabled", }, "outdatedDocuments": Array [], "outdatedDocumentsQuery": Object { @@ -641,6 +664,11 @@ Object { "type": "tsvb-validation-telemetry", }, }, + Object { + "term": Object { + "type": "ui-counter", + }, + }, Object { "bool": Object { "must": Array [ @@ -735,7 +763,9 @@ Object { ], "maxBatchSizeBytes": 100000000, "migrationDocLinks": Object { + "repeatedTimeoutRequests": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#_repeated_time_out_requests_that_eventually_fail", "resolveMigrationFailures": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html", + "routingAllocationDisabled": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#routing-allocation-disabled", }, "outdatedDocuments": Array [ Object { @@ -850,6 +880,11 @@ Object { "type": "tsvb-validation-telemetry", }, }, + Object { + "term": Object { + "type": "ui-counter", + }, + }, Object { "bool": Object { "must": Array [ @@ -910,7 +945,9 @@ Object { ], "maxBatchSizeBytes": 100000000, "migrationDocLinks": Object { + "repeatedTimeoutRequests": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#_repeated_time_out_requests_that_eventually_fail", "resolveMigrationFailures": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html", + "routingAllocationDisabled": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#routing-allocation-disabled", }, "outdatedDocuments": Array [ Object { @@ -1025,6 +1062,11 @@ Object { "type": "tsvb-validation-telemetry", }, }, + Object { + "term": Object { + "type": "ui-counter", + }, + }, Object { "bool": Object { "must": Array [ diff --git a/src/core/server/saved_objects/migrations/actions/initialize_action.test.ts b/src/core/server/saved_objects/migrations/actions/initialize_action.test.ts index 7c75470b890aa..5d831a5bb8f78 100644 --- a/src/core/server/saved_objects/migrations/actions/initialize_action.test.ts +++ b/src/core/server/saved_objects/migrations/actions/initialize_action.test.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import * as Either from 'fp-ts/lib/Either'; import { catchRetryableEsClientErrors } from './catch_retryable_es_client_errors'; import { errors as EsErrors } from '@elastic/elasticsearch'; jest.mock('./catch_retryable_es_client_errors'); @@ -16,16 +17,16 @@ describe('initAction', () => { beforeEach(() => { jest.clearAllMocks(); }); - const retryableError = new EsErrors.ResponseError( - elasticsearchClientMock.createApiResponse({ - statusCode: 503, - body: { error: { type: 'es_type', reason: 'es_reason' } }, - }) - ); - const client = elasticsearchClientMock.createInternalClient( - elasticsearchClientMock.createErrorTransportRequestPromise(retryableError) - ); it('calls catchRetryableEsClientErrors when the promise rejects', async () => { + const retryableError = new EsErrors.ResponseError( + elasticsearchClientMock.createApiResponse({ + statusCode: 503, + body: { error: { type: 'es_type', reason: 'es_reason' } }, + }) + ); + const client = elasticsearchClientMock.createInternalClient( + elasticsearchClientMock.createErrorTransportRequestPromise(retryableError) + ); const task = initAction({ client, indices: ['my_index'] }); try { await task(); @@ -34,4 +35,88 @@ describe('initAction', () => { } expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(retryableError); }); + it('resolves right when persistent and transient cluster settings are compatible', async () => { + const clusterSettingsResponse = { + transient: { 'cluster.routing.allocation.enable': 'all' }, + persistent: { 'cluster.routing.allocation.enable': 'all' }, + }; + const client = elasticsearchClientMock.createInternalClient( + new Promise((res) => res(clusterSettingsResponse)) + ); + const task = initAction({ client, indices: ['my_index'] }); + const result = await task(); + expect(Either.isRight(result)).toEqual(true); + }); + it('resolves right when persistent and transient cluster settings are undefined', async () => { + const clusterSettingsResponse = { + transient: {}, + persistent: {}, + }; + const client = elasticsearchClientMock.createInternalClient( + new Promise((res) => res(clusterSettingsResponse)) + ); + const task = initAction({ client, indices: ['my_index'] }); + const result = await task(); + expect(Either.isRight(result)).toEqual(true); + }); + it('resolves right when persistent cluster settings are compatible', async () => { + const clusterSettingsResponse = { + transient: {}, + persistent: { 'cluster.routing.allocation.enable': 'all' }, + }; + const client = elasticsearchClientMock.createInternalClient( + new Promise((res) => res(clusterSettingsResponse)) + ); + const task = initAction({ client, indices: ['my_index'] }); + const result = await task(); + expect(Either.isRight(result)).toEqual(true); + }); + it('resolves right when transient cluster settings are compatible', async () => { + const clusterSettingsResponse = { + transient: { 'cluster.routing.allocation.enable': 'all' }, + persistent: {}, + }; + const client = elasticsearchClientMock.createInternalClient( + new Promise((res) => res(clusterSettingsResponse)) + ); + const task = initAction({ client, indices: ['my_index'] }); + const result = await task(); + expect(Either.isRight(result)).toEqual(true); + }); + it('resolves right when valid transient settings, incompatible persistent settings', async () => { + const clusterSettingsResponse = { + transient: { 'cluster.routing.allocation.enable': 'all' }, + persistent: { 'cluster.routing.allocation.enable': 'primaries' }, + }; + const client = elasticsearchClientMock.createInternalClient( + new Promise((res) => res(clusterSettingsResponse)) + ); + const task = initAction({ client, indices: ['my_index'] }); + const result = await task(); + expect(Either.isRight(result)).toEqual(true); + }); + it('resolves left when valid persistent settings, incompatible transient settings', async () => { + const clusterSettingsResponse = { + transient: { 'cluster.routing.allocation.enable': 'primaries' }, + persistent: { 'cluster.routing.allocation.enable': 'alls' }, + }; + const client = elasticsearchClientMock.createInternalClient( + new Promise((res) => res(clusterSettingsResponse)) + ); + const task = initAction({ client, indices: ['my_index'] }); + const result = await task(); + expect(Either.isLeft(result)).toEqual(true); + }); + it('resolves left when transient cluster settings are incompatible', async () => { + const clusterSettingsResponse = { + transient: { 'cluster.routing.allocation.enable': 'none' }, + persistent: { 'cluster.routing.allocation.enable': 'all' }, + }; + const client = elasticsearchClientMock.createInternalClient( + new Promise((res) => res(clusterSettingsResponse)) + ); + const task = initAction({ client, indices: ['my_index'] }); + const result = await task(); + expect(Either.isLeft(result)).toEqual(true); + }); }); diff --git a/src/core/server/saved_objects/migrations/actions/initialize_action.ts b/src/core/server/saved_objects/migrations/actions/initialize_action.ts index 73502382c9ca0..e7f011cb4c5f2 100644 --- a/src/core/server/saved_objects/migrations/actions/initialize_action.ts +++ b/src/core/server/saved_objects/migrations/actions/initialize_action.ts @@ -29,6 +29,7 @@ export interface InitActionParams { export interface UnsupportedClusterRoutingAllocation { type: 'unsupported_cluster_routing_allocation'; + message: string; } export const checkClusterRoutingAllocationEnabledTask = @@ -43,17 +44,20 @@ export const checkClusterRoutingAllocationEnabledTask = flat_settings: true, }) .then((settings) => { - const clusterRoutingAllocations: string[] = + // transient settings take preference over persistent settings + const clusterRoutingAllocation = settings?.transient?.[routingAllocationEnable] ?? - settings?.persistent?.[routingAllocationEnable] ?? - []; + settings?.persistent?.[routingAllocationEnable]; - const clusterRoutingAllocationEnabled = - [...clusterRoutingAllocations].length === 0 || - [...clusterRoutingAllocations].every((s: string) => s === 'all'); // if set, only allow 'all' + const clusterRoutingAllocationEnabledIsAll = + clusterRoutingAllocation === undefined || clusterRoutingAllocation === 'all'; - if (!clusterRoutingAllocationEnabled) { - return Either.left({ type: 'unsupported_cluster_routing_allocation' as const }); + if (!clusterRoutingAllocationEnabledIsAll) { + return Either.left({ + type: 'unsupported_cluster_routing_allocation' as const, + message: + '[unsupported_cluster_routing_allocation] The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue.', + }); } else { return Either.right({}); } diff --git a/src/core/server/saved_objects/migrations/actions/integration_tests/actions.test.ts b/src/core/server/saved_objects/migrations/actions/integration_tests/actions.test.ts index 5e840d87ea1ab..cddd2f323f1fc 100644 --- a/src/core/server/saved_objects/migrations/actions/integration_tests/actions.test.ts +++ b/src/core/server/saved_objects/migrations/actions/integration_tests/actions.test.ts @@ -116,7 +116,7 @@ describe('migration actions', () => { await client.cluster.putSettings({ body: { persistent: { - // Remove persistent test settings + // Reset persistent test settings cluster: { routing: { allocation: { enable: null } } }, }, }, @@ -126,11 +126,11 @@ describe('migration actions', () => { expect.assertions(1); const task = initAction({ client, indices: ['no_such_index'] }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Right", - "right": Object {}, - } - `); + Object { + "_tag": "Right", + "right": Object {}, + } + `); }); it('resolves right record with found indices', async () => { expect.assertions(1); @@ -149,7 +149,7 @@ describe('migration actions', () => { }) ); }); - it('resolves left with cluster routing allocation disabled', async () => { + it('resolves left when cluster.routing.allocation.enabled is incompatible', async () => { expect.assertions(3); await client.cluster.putSettings({ body: { @@ -164,13 +164,14 @@ describe('migration actions', () => { indices: ['existing_index_with_docs'], }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Left", - "left": Object { - "type": "unsupported_cluster_routing_allocation", - }, - } - `); + Object { + "_tag": "Left", + "left": Object { + "message": "[unsupported_cluster_routing_allocation] The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue.", + "type": "unsupported_cluster_routing_allocation", + }, + } + `); await client.cluster.putSettings({ body: { persistent: { @@ -184,13 +185,14 @@ describe('migration actions', () => { indices: ['existing_index_with_docs'], }); await expect(task2()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Left", - "left": Object { - "type": "unsupported_cluster_routing_allocation", - }, - } - `); + Object { + "_tag": "Left", + "left": Object { + "message": "[unsupported_cluster_routing_allocation] The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue.", + "type": "unsupported_cluster_routing_allocation", + }, + } + `); await client.cluster.putSettings({ body: { persistent: { @@ -204,13 +206,30 @@ describe('migration actions', () => { indices: ['existing_index_with_docs'], }); await expect(task3()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Left", - "left": Object { - "type": "unsupported_cluster_routing_allocation", - }, - } - `); + Object { + "_tag": "Left", + "left": Object { + "message": "[unsupported_cluster_routing_allocation] The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue.", + "type": "unsupported_cluster_routing_allocation", + }, + } + `); + }); + it('resolves right when cluster.routing.allocation.enabled=all', async () => { + expect.assertions(1); + await client.cluster.putSettings({ + body: { + persistent: { + cluster: { routing: { allocation: { enable: 'all' } } }, + }, + }, + }); + const task = initAction({ + client, + indices: ['existing_index_with_docs'], + }); + const result = await task(); + expect(Either.isRight(result)).toBe(true); }); }); @@ -268,14 +287,14 @@ describe('migration actions', () => { expect.assertions(1); const task = setWriteBlock({ client, index: 'no_such_index' }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Left", - "left": Object { - "index": "no_such_index", - "type": "index_not_found_exception", - }, - } - `); + Object { + "_tag": "Left", + "left": Object { + "index": "no_such_index", + "type": "index_not_found_exception", + }, + } + `); }); }); @@ -297,21 +316,21 @@ describe('migration actions', () => { expect.assertions(1); const task = removeWriteBlock({ client, index: 'existing_index_with_write_block_2' }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Right", - "right": "remove_write_block_succeeded", - } - `); + Object { + "_tag": "Right", + "right": "remove_write_block_succeeded", + } + `); }); it('resolves right if successful when an index does not have a write block', async () => { expect.assertions(1); const task = removeWriteBlock({ client, index: 'existing_index_without_write_block_2' }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Right", - "right": "remove_write_block_succeeded", - } - `); + Object { + "_tag": "Right", + "right": "remove_write_block_succeeded", + } + `); }); it('rejects if there is a non-retryable error', async () => { expect.assertions(1); @@ -395,13 +414,13 @@ describe('migration actions', () => { timeout: '1s', }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Left", - "left": Object { - "message": "[index_not_yellow_timeout] Timeout waiting for the status of the [red_index] index to become 'yellow'", - "type": "index_not_yellow_timeout", - }, - } + Object { + "_tag": "Left", + "left": Object { + "message": "[index_not_yellow_timeout] Timeout waiting for the status of the [red_index] index to become 'yellow'", + "type": "index_not_yellow_timeout", + }, + } `); }); }); @@ -422,13 +441,13 @@ describe('migration actions', () => { }); expect.assertions(1); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Right", - "right": Object { - "acknowledged": true, - "shardsAcknowledged": true, - }, - } + Object { + "_tag": "Right", + "right": Object { + "acknowledged": true, + "shardsAcknowledged": true, + }, + } `); }); it('resolves right after waiting for index status to be yellow if clone target already existed', async () => { @@ -488,13 +507,13 @@ describe('migration actions', () => { expect.assertions(1); const task = cloneIndex({ client, source: 'no_such_index', target: 'clone_target_3' }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Left", - "left": Object { - "index": "no_such_index", - "type": "index_not_found_exception", - }, - } + Object { + "_tag": "Left", + "left": Object { + "index": "no_such_index", + "type": "index_not_found_exception", + }, + } `); }); it('resolves left with a index_not_yellow_timeout if clone target already exists but takes longer than the specified timeout before turning yellow', async () => { @@ -524,13 +543,13 @@ describe('migration actions', () => { })(); await expect(cloneIndexPromise).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Left", - "left": Object { - "message": "[index_not_yellow_timeout] Timeout waiting for the status of the [clone_red_index] index to become 'yellow'", - "type": "index_not_yellow_timeout", - }, - } + Object { + "_tag": "Left", + "left": Object { + "message": "[index_not_yellow_timeout] Timeout waiting for the status of the [clone_red_index] index to become 'yellow'", + "type": "index_not_yellow_timeout", + }, + } `); // Now that we know timeouts work, make the index yellow again and call cloneIndex a second time to verify that it completes @@ -552,13 +571,13 @@ describe('migration actions', () => { })(); await expect(cloneIndexPromise2).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Right", - "right": Object { - "acknowledged": true, - "shardsAcknowledged": true, - }, - } + Object { + "_tag": "Right", + "right": Object { + "acknowledged": true, + "shardsAcknowledged": true, + }, + } `); }); }); @@ -577,10 +596,10 @@ describe('migration actions', () => { })()) as Either.Right; const task = waitForReindexTask({ client, taskId: res.right.taskId, timeout: '10s' }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Right", - "right": "reindex_succeeded", - } + Object { + "_tag": "Right", + "right": "reindex_succeeded", + } `); const results = ( @@ -617,10 +636,10 @@ describe('migration actions', () => { })()) as Either.Right; const task = waitForReindexTask({ client, taskId: res.right.taskId, timeout: '10s' }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Right", - "right": "reindex_succeeded", - } + Object { + "_tag": "Right", + "right": "reindex_succeeded", + } `); const results = ( @@ -650,10 +669,10 @@ describe('migration actions', () => { })()) as Either.Right; const task = waitForReindexTask({ client, taskId: res.right.taskId, timeout: '10s' }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Right", - "right": "reindex_succeeded", - } + Object { + "_tag": "Right", + "right": "reindex_succeeded", + } `); const results = ( (await searchForOutdatedDocuments(client, { @@ -685,11 +704,11 @@ describe('migration actions', () => { })()) as Either.Right; let task = waitForReindexTask({ client, taskId: res.right.taskId, timeout: '10s' }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Right", - "right": "reindex_succeeded", - } - `); + Object { + "_tag": "Right", + "right": "reindex_succeeded", + } + `); // reindex without a script res = (await reindex({ @@ -702,11 +721,11 @@ describe('migration actions', () => { })()) as Either.Right; task = waitForReindexTask({ client, taskId: res.right.taskId, timeout: '10s' }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Right", - "right": "reindex_succeeded", - } - `); + Object { + "_tag": "Right", + "right": "reindex_succeeded", + } + `); // Assert that documents weren't overridden by the second, unscripted reindex const results = ( @@ -761,11 +780,11 @@ describe('migration actions', () => { })()) as Either.Right; const task = waitForReindexTask({ client, taskId: res.right.taskId, timeout: '10s' }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Right", - "right": "reindex_succeeded", - } - `); + Object { + "_tag": "Right", + "right": "reindex_succeeded", + } + `); // Assert that existing documents weren't overridden, but that missing // documents were added by the reindex const results = ( @@ -818,13 +837,13 @@ describe('migration actions', () => { const task = waitForReindexTask({ client, taskId: reindexTaskId, timeout: '10s' }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Left", - "left": Object { - "type": "incompatible_mapping_exception", - }, - } - `); + Object { + "_tag": "Left", + "left": Object { + "type": "incompatible_mapping_exception", + }, + } + `); }); it('resolves left incompatible_mapping_exception if all reindex failures are due to a mapper_parsing_exception', async () => { expect.assertions(1); @@ -857,13 +876,13 @@ describe('migration actions', () => { const task = waitForReindexTask({ client, taskId: reindexTaskId, timeout: '10s' }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Left", - "left": Object { - "type": "incompatible_mapping_exception", - }, - } - `); + Object { + "_tag": "Left", + "left": Object { + "type": "incompatible_mapping_exception", + }, + } + `); }); it('resolves left index_not_found_exception if source index does not exist', async () => { expect.assertions(1); @@ -879,14 +898,14 @@ describe('migration actions', () => { })()) as Either.Right; const task = waitForReindexTask({ client, taskId: res.right.taskId, timeout: '10s' }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Left", - "left": Object { - "index": "no_such_index", - "type": "index_not_found_exception", - }, - } - `); + Object { + "_tag": "Left", + "left": Object { + "index": "no_such_index", + "type": "index_not_found_exception", + }, + } + `); }); it('resolves left target_index_had_write_block if all failures are due to a write block', async () => { expect.assertions(1); @@ -902,13 +921,13 @@ describe('migration actions', () => { const task = waitForReindexTask({ client, taskId: res.right.taskId, timeout: '10s' }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Left", - "left": Object { - "type": "target_index_had_write_block", - }, - } - `); + Object { + "_tag": "Left", + "left": Object { + "type": "target_index_had_write_block", + }, + } + `); }); it('resolves left if requireAlias=true and the target is not an alias', async () => { expect.assertions(1); @@ -924,14 +943,14 @@ describe('migration actions', () => { const task = waitForReindexTask({ client, taskId: res.right.taskId, timeout: '10s' }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Left", - "left": Object { - "index": "existing_index_with_write_block", - "type": "index_not_found_exception", - }, - } - `); + Object { + "_tag": "Left", + "left": Object { + "index": "existing_index_with_write_block", + "type": "index_not_found_exception", + }, + } + `); }); it('resolves left wait_for_task_completion_timeout when the task does not finish within the timeout', async () => { @@ -983,11 +1002,11 @@ describe('migration actions', () => { targetIndex: 'reindex_target_7', }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Right", - "right": "verify_reindex_succeeded", - } - `); + Object { + "_tag": "Right", + "right": "verify_reindex_succeeded", + } + `); }); it('resolves left if source and target indices have different amount of documents', async () => { expect.assertions(1); @@ -997,13 +1016,13 @@ describe('migration actions', () => { targetIndex: 'existing_index_2', }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Left", - "left": Object { - "type": "verify_reindex_failed", - }, - } - `); + Object { + "_tag": "Left", + "left": Object { + "type": "verify_reindex_failed", + }, + } + `); }); it('rejects if source or target index does not exist', async () => { expect.assertions(2); @@ -1630,11 +1649,11 @@ describe('migration actions', () => { }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Right", - "right": "bulk_index_succeeded", - } - `); + Object { + "_tag": "Right", + "right": "bulk_index_succeeded", + } + `); }); it('resolves right even if there were some version_conflict_engine_exception', async () => { const existingDocs = ( @@ -1675,13 +1694,13 @@ describe('migration actions', () => { refresh: 'wait_for', })() ).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Left", - "left": Object { - "type": "target_index_had_write_block", - }, - } - `); + Object { + "_tag": "Left", + "left": Object { + "type": "target_index_had_write_block", + }, + } + `); }); it('resolves left request_entity_too_large_exception when the payload is too large', async () => { @@ -1697,13 +1716,13 @@ describe('migration actions', () => { transformedDocs: newDocs, }); await expect(task()).resolves.toMatchInlineSnapshot(` - Object { - "_tag": "Left", - "left": Object { - "type": "request_entity_too_large_exception", - }, - } - `); + Object { + "_tag": "Left", + "left": Object { + "type": "request_entity_too_large_exception", + }, + } + `); }); }); }); diff --git a/src/core/server/saved_objects/migrations/core/unused_types.ts b/src/core/server/saved_objects/migrations/core/unused_types.ts index fd4b8a09600d7..076bdb489cf49 100644 --- a/src/core/server/saved_objects/migrations/core/unused_types.ts +++ b/src/core/server/saved_objects/migrations/core/unused_types.ts @@ -33,6 +33,8 @@ export const REMOVED_TYPES: string[] = [ 'siem-detection-engine-rule-status', // Was removed in 7.16 'timelion-sheet', + // Removed in 8.3 https://github.com/elastic/kibana/issues/127745 + 'ui-counter', ].sort(); // When migrating from the outdated index we use a read query which excludes diff --git a/src/core/server/saved_objects/migrations/initial_state.test.ts b/src/core/server/saved_objects/migrations/initial_state.test.ts index 0fff4ddb06895..2ad3dc38e6d65 100644 --- a/src/core/server/saved_objects/migrations/initial_state.test.ts +++ b/src/core/server/saved_objects/migrations/initial_state.test.ts @@ -116,6 +116,10 @@ describe('createInitialState', () => { migrationDocLinks: { resolveMigrationFailures: 'https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html', + repeatedTimeoutRequests: + 'https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#_repeated_time_out_requests_that_eventually_fail', + routingAllocationDisabled: + 'https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#routing-allocation-disabled', }, }); }); diff --git a/src/core/server/saved_objects/migrations/integration_tests/cluster_routing_allocation_disabled.test.ts b/src/core/server/saved_objects/migrations/integration_tests/cluster_routing_allocation_disabled.test.ts index ea70478d6ce7b..525b9b3585c3f 100644 --- a/src/core/server/saved_objects/migrations/integration_tests/cluster_routing_allocation_disabled.test.ts +++ b/src/core/server/saved_objects/migrations/integration_tests/cluster_routing_allocation_disabled.test.ts @@ -114,7 +114,7 @@ describe('unsupported_cluster_routing_allocation', () => { await root.setup(); await expect(root.start()).rejects.toThrowError( - /Unable to complete saved object migrations for the \[\.kibana.*\] index: The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue\. To proceed, please remove the cluster routing allocation settings with PUT \/_cluster\/settings {"transient": {"cluster\.routing\.allocation\.enable": null}, "persistent": {"cluster\.routing\.allocation\.enable": null}}/ + /Unable to complete saved object migrations for the \[\.kibana.*\] index: \[unsupported_cluster_routing_allocation\] The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue\. To proceed, please remove the cluster routing allocation settings with PUT \/_cluster\/settings {\"transient\": {\"cluster\.routing\.allocation\.enable\": null}, \"persistent\": {\"cluster\.routing\.allocation\.enable\": null}}\. Refer to https:\/\/www.elastic.co\/guide\/en\/kibana\/master\/resolve-migrations-failures.html#routing-allocation-disabled for more information on how to resolve the issue\./ ); await retryAsync( @@ -126,7 +126,7 @@ describe('unsupported_cluster_routing_allocation', () => { .map((str) => JSON5.parse(str)) as LogRecord[]; expect( records.find((rec) => - /^Unable to complete saved object migrations for the \[\.kibana.*\] index: The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue\./.test( + /^Unable to complete saved object migrations for the \[\.kibana.*\] index: \[unsupported_cluster_routing_allocation\] The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue\./.test( rec.message ) ) @@ -149,7 +149,7 @@ describe('unsupported_cluster_routing_allocation', () => { await root.setup(); await expect(root.start()).rejects.toThrowError( - /Unable to complete saved object migrations for the \[\.kibana.*\] index: The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue\. To proceed, please remove the cluster routing allocation settings with PUT \/_cluster\/settings {"transient": {"cluster\.routing\.allocation\.enable": null}, "persistent": {"cluster\.routing\.allocation\.enable": null}}/ + /Unable to complete saved object migrations for the \[\.kibana.*\] index: \[unsupported_cluster_routing_allocation\] The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue\. To proceed, please remove the cluster routing allocation settings with PUT \/_cluster\/settings {\"transient\": {\"cluster\.routing\.allocation\.enable\": null}, \"persistent\": {\"cluster\.routing\.allocation\.enable\": null}}\. Refer to https:\/\/www.elastic.co\/guide\/en\/kibana\/master\/resolve-migrations-failures.html#routing-allocation-disabled for more information on how to resolve the issue\./ ); }); }); diff --git a/src/core/server/saved_objects/migrations/model/extract_errors.test.ts b/src/core/server/saved_objects/migrations/model/extract_errors.test.ts index a028c40ca6597..e434a5001a6ae 100644 --- a/src/core/server/saved_objects/migrations/model/extract_errors.test.ts +++ b/src/core/server/saved_objects/migrations/model/extract_errors.test.ts @@ -6,7 +6,11 @@ * Side Public License, v 1. */ -import { extractUnknownDocFailureReason } from './extract_errors'; +import { + extractUnknownDocFailureReason, + fatalReasonClusterRoutingAllocationUnsupported, + fatalReasonDocumentExceedsMaxBatchSizeBytes, +} from './extract_errors'; describe('extractUnknownDocFailureReason', () => { it('generates the correct error message', () => { @@ -37,3 +41,32 @@ describe('extractUnknownDocFailureReason', () => { `); }); }); + +describe('fatalReasonDocumentExceedsMaxBatchSizeBytes', () => { + it('generate the correct error message', () => { + expect( + fatalReasonDocumentExceedsMaxBatchSizeBytes({ + _id: 'abc', + docSizeBytes: 106954752, + maxBatchSizeBytes: 104857600, + }) + ).toMatchInlineSnapshot( + `"The document with _id \\"abc\\" is 106954752 bytes which exceeds the configured maximum batch size of 104857600 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value."` + ); + }); +}); + +describe('fatalReasonClusterRoutingAllocationUnsupported', () => { + it('generates the correct error message', () => { + const errorMessages = fatalReasonClusterRoutingAllocationUnsupported({ + errorMessage: '[some-error] message', + docSectionLink: 'linkToDocsSection', + }); + expect(errorMessages.fatalReason).toMatchInlineSnapshot( + `"[some-error] message To proceed, please remove the cluster routing allocation settings with PUT /_cluster/settings {\\"transient\\": {\\"cluster.routing.allocation.enable\\": null}, \\"persistent\\": {\\"cluster.routing.allocation.enable\\": null}}. Refer to linkToDocsSection for more information on how to resolve the issue."` + ); + expect(errorMessages.logsErrorMessage).toMatchInlineSnapshot( + `"[some-error] message Ensure that the persistent and transient Elasticsearch configuration option 'cluster.routing.allocation.enable' is not set or set it to a value of 'all'. Refer to linkToDocsSection for more information on how to resolve the issue."` + ); + }); +}); diff --git a/src/core/server/saved_objects/migrations/model/extract_errors.ts b/src/core/server/saved_objects/migrations/model/extract_errors.ts index 95d10603caa80..f41009ab2127c 100644 --- a/src/core/server/saved_objects/migrations/model/extract_errors.ts +++ b/src/core/server/saved_objects/migrations/model/extract_errors.ts @@ -51,3 +51,32 @@ export function extractUnknownDocFailureReason( `'` ); } + +/** + * Constructs migration failure message string for doc exceeds max batch size in bytes + */ +export const fatalReasonDocumentExceedsMaxBatchSizeBytes = ({ + _id, + docSizeBytes, + maxBatchSizeBytes, +}: { + _id: string; + docSizeBytes: number; + maxBatchSizeBytes: number; +}) => + `The document with _id "${_id}" is ${docSizeBytes} bytes which exceeds the configured maximum batch size of ${maxBatchSizeBytes} bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.`; + +/** + * Constructs migration failure message and logs message strings when an unsupported cluster routing allocation is configured. + * The full errorMessage is "[unsupported_cluster_routing_allocation] The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue." + */ +export const fatalReasonClusterRoutingAllocationUnsupported = ({ + errorMessage, + docSectionLink, +}: { + errorMessage: string; + docSectionLink: string; +}) => ({ + fatalReason: `${errorMessage} To proceed, please remove the cluster routing allocation settings with PUT /_cluster/settings {"transient": {"cluster.routing.allocation.enable": null}, "persistent": {"cluster.routing.allocation.enable": null}}. Refer to ${docSectionLink} for more information on how to resolve the issue.`, + logsErrorMessage: `${errorMessage} Ensure that the persistent and transient Elasticsearch configuration option 'cluster.routing.allocation.enable' is not set or set it to a value of 'all'. Refer to ${docSectionLink} for more information on how to resolve the issue.`, +}); diff --git a/src/core/server/saved_objects/migrations/model/model.test.ts b/src/core/server/saved_objects/migrations/model/model.test.ts index b80e2bceae846..e44995ac8d30e 100644 --- a/src/core/server/saved_objects/migrations/model/model.test.ts +++ b/src/core/server/saved_objects/migrations/model/model.test.ts @@ -96,6 +96,8 @@ describe('migrations v2 model', () => { excludeFromUpgradeFilterHooks: {}, migrationDocLinks: { resolveMigrationFailures: 'resolveMigrationFailures', + repeatedTimeoutRequests: 'repeatedTimeoutRequests', + routingAllocationDisabled: 'routingAllocationDisabled', }, }; @@ -283,12 +285,13 @@ describe('migrations v2 model', () => { test('INIT -> FATAL when cluster routing allocation is not enabled', () => { const res: ResponseType<'INIT'> = Either.left({ type: 'unsupported_cluster_routing_allocation', + message: '[unsupported_cluster_routing_allocation]', }); const newState = model(initState, res) as FatalState; expect(newState.controlState).toEqual('FATAL'); expect(newState.reason).toMatchInlineSnapshot( - `"The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue. To proceed, please remove the cluster routing allocation settings with PUT /_cluster/settings {\\"transient\\": {\\"cluster.routing.allocation.enable\\": null}, \\"persistent\\": {\\"cluster.routing.allocation.enable\\": null}}"` + `"[unsupported_cluster_routing_allocation] To proceed, please remove the cluster routing allocation settings with PUT /_cluster/settings {\\"transient\\": {\\"cluster.routing.allocation.enable\\": null}, \\"persistent\\": {\\"cluster.routing.allocation.enable\\": null}}. Refer to routingAllocationDisabled for more information on how to resolve the issue."` ); }); test("INIT -> FATAL when .kibana points to newer version's index", () => { diff --git a/src/core/server/saved_objects/migrations/model/model.ts b/src/core/server/saved_objects/migrations/model/model.ts index e711f62bcd8d6..cff23f0eeda65 100644 --- a/src/core/server/saved_objects/migrations/model/model.ts +++ b/src/core/server/saved_objects/migrations/model/model.ts @@ -21,7 +21,12 @@ import { setProgressTotal, } from './progress'; import { delayRetryState, resetRetryState } from './retry_state'; -import { extractTransformFailuresReason, extractUnknownDocFailureReason } from './extract_errors'; +import { + extractTransformFailuresReason, + extractUnknownDocFailureReason, + fatalReasonDocumentExceedsMaxBatchSizeBytes, + fatalReasonClusterRoutingAllocationUnsupported, +} from './extract_errors'; import type { ExcludeRetryableEsError } from './types'; import { getAliases, @@ -33,17 +38,7 @@ import { } from './helpers'; import { createBatches } from './create_batches'; -const FATAL_REASON_REQUEST_ENTITY_TOO_LARGE = `While indexing a batch of saved objects, Elasticsearch returned a 413 Request Entity Too Large exception. Ensure that the Kibana configuration option 'migrations.maxBatchSizeBytes' is set to a value that is lower than or equal to the Elasticsearch 'http.max_content_length' configuration option.`; -const fatalReasonDocumentExceedsMaxBatchSizeBytes = ({ - _id, - docSizeBytes, - maxBatchSizeBytes, -}: { - _id: string; - docSizeBytes: number; - maxBatchSizeBytes: number; -}) => - `The document with _id "${_id}" is ${docSizeBytes} bytes which exceeds the configured maximum batch size of ${maxBatchSizeBytes} bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.`; +export const FATAL_REASON_REQUEST_ENTITY_TOO_LARGE = `While indexing a batch of saved objects, Elasticsearch returned a 413 Request Entity Too Large exception. Ensure that the Kibana configuration option 'migrations.maxBatchSizeBytes' is set to a value that is lower than or equal to the Elasticsearch 'http.max_content_length' configuration option.`; export const model = (currentState: State, resW: ResponseType): State => { // The action response `resW` is weakly typed, the type includes all action @@ -73,15 +68,19 @@ export const model = (currentState: State, resW: ResponseType): if (Either.isLeft(res)) { const left = res.left; if (isLeftTypeof(left, 'unsupported_cluster_routing_allocation')) { + const initErrorMessages = fatalReasonClusterRoutingAllocationUnsupported({ + errorMessage: left.message, + docSectionLink: stateP.migrationDocLinks.routingAllocationDisabled, + }); return { ...stateP, controlState: 'FATAL', - reason: `The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue. To proceed, please remove the cluster routing allocation settings with PUT /_cluster/settings {"transient": {"cluster.routing.allocation.enable": null}, "persistent": {"cluster.routing.allocation.enable": null}}`, + reason: initErrorMessages.fatalReason, logs: [ ...stateP.logs, { level: 'error', - message: `The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue. Ensure that the persistent and transient Elasticsearch configuration option 'cluster.routing.allocation.enable' is not set or set it to a value of 'all'.`, + message: initErrorMessages.logsErrorMessage, }, ], }; @@ -244,7 +243,7 @@ export const model = (currentState: State, resW: ResponseType): // we get a response. // If the cluster hit the low watermark for disk usage the LEGACY_CREATE_REINDEX_TARGET action will // continue to timeout and eventually lead to a failed migration. - const retryErrorMessage = `${left.message} Refer to ${stateP.migrationDocLinks.resolveMigrationFailures} for information on how to resolve the issue.`; + const retryErrorMessage = `${left.message} Refer to ${stateP.migrationDocLinks.repeatedTimeoutRequests} for information on how to resolve the issue.`; return delayRetryState(stateP, retryErrorMessage, stateP.retryAttempts); } else { return throwBadResponse(stateP, left); @@ -366,7 +365,7 @@ export const model = (currentState: State, resW: ResponseType): // we get a response. // In the event of retries running out, we link to the docs to help with diagnosing // the problem. - const retryErrorMessage = `${left.message} Refer to ${stateP.migrationDocLinks.resolveMigrationFailures} for information on how to resolve the issue.`; + const retryErrorMessage = `${left.message} Refer to ${stateP.migrationDocLinks.repeatedTimeoutRequests} for information on how to resolve the issue.`; return delayRetryState(stateP, retryErrorMessage, stateP.retryAttempts); } else { return throwBadResponse(stateP, left); @@ -461,7 +460,7 @@ export const model = (currentState: State, resW: ResponseType): // // If there is a problem CREATE_REINDEX_TEMP action will // continue to timeout and eventually lead to a failed migration. - const retryErrorMessage = `${left.message} Refer to ${stateP.migrationDocLinks.resolveMigrationFailures} for information on how to resolve the issue.`; + const retryErrorMessage = `${left.message} Refer to ${stateP.migrationDocLinks.repeatedTimeoutRequests} for information on how to resolve the issue.`; return delayRetryState(stateP, retryErrorMessage, stateP.retryAttempts); } else { return throwBadResponse(stateP, left); @@ -696,7 +695,7 @@ export const model = (currentState: State, resW: ResponseType): // `_cluster/allocation/explain?index=${targetIndex}` API. // Unless the root cause is identified and addressed, the request will // continue to timeout and eventually lead to a failed migration. - const retryErrorMessage = `${left.message} Refer to ${stateP.migrationDocLinks.resolveMigrationFailures} for information on how to resolve the issue.`; + const retryErrorMessage = `${left.message} Refer to ${stateP.migrationDocLinks.repeatedTimeoutRequests} for information on how to resolve the issue.`; return delayRetryState(stateP, retryErrorMessage, stateP.retryAttempts); } else { throwBadResponse(stateP, left); @@ -951,7 +950,7 @@ export const model = (currentState: State, resW: ResponseType): // If the cluster hit the low watermark for disk usage the action will continue to timeout. // Unless the disk space is addressed, the LEGACY_CREATE_REINDEX_TARGET action will // continue to timeout and eventually lead to a failed migration. - const retryErrorMessage = `${left.message} Refer to ${stateP.migrationDocLinks.resolveMigrationFailures} for information on how to resolve the issue.`; + const retryErrorMessage = `${left.message} Refer to ${stateP.migrationDocLinks.repeatedTimeoutRequests} for information on how to resolve the issue.`; return delayRetryState(stateP, retryErrorMessage, stateP.retryAttempts); } else { return throwBadResponse(stateP, left); diff --git a/src/core/server/saved_objects/migrations/state.ts b/src/core/server/saved_objects/migrations/state.ts index 6630b5ee57808..dee6839d6b902 100644 --- a/src/core/server/saved_objects/migrations/state.ts +++ b/src/core/server/saved_objects/migrations/state.ts @@ -8,6 +8,7 @@ import * as Option from 'fp-ts/lib/Option'; import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { DocLinks } from '@kbn/doc-links'; import { ControlState } from './state_action_machine'; import { AliasAction } from './actions'; import { IndexMapping } from '../mappings'; @@ -125,7 +126,7 @@ export interface BaseState extends ControlState { /** * DocLinks for savedObjects. to reference online documentation */ - readonly migrationDocLinks: Record; + readonly migrationDocLinks: DocLinks['kibanaUpgradeSavedObjects']; } export interface InitState extends BaseState { diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 2228c8fee8794..4ff42f95b571a 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -77,12 +77,12 @@ export { AnalyticsClient } // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver // // @public -export type AnalyticsServicePreboot = AnalyticsClient; +export type AnalyticsServicePreboot = Omit; // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver // // @public -export type AnalyticsServiceSetup = AnalyticsClient; +export type AnalyticsServiceSetup = Omit; // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver // @@ -2541,7 +2541,7 @@ export class SavedObjectsImporter { typeRegistry: ISavedObjectTypeRegistry; importSizeLimit: number; }); - import({ readStream, createNewCopies, namespace, overwrite, }: SavedObjectsImportOptions): Promise; + import({ readStream, createNewCopies, namespace, overwrite, refresh, }: SavedObjectsImportOptions): Promise; resolveImportErrors({ readStream, createNewCopies, namespace, retries, }: SavedObjectsResolveImportErrorsOptions): Promise; } @@ -2604,6 +2604,7 @@ export interface SavedObjectsImportOptions { namespace?: string; overwrite: boolean; readStream: Readable; + refresh?: boolean | 'wait_for'; } // @public diff --git a/src/core/server/server.ts b/src/core/server/server.ts index c73e98f4bb6c4..234630734d437 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -56,10 +56,25 @@ import { config as executionContextConfig } from './execution_context'; import { PrebootCoreRouteHandlerContext } from './preboot_core_route_handler_context'; import { PrebootService } from './preboot'; import { DiscoveredPlugins } from './plugins'; -import { AnalyticsService } from './analytics'; +import { AnalyticsService, AnalyticsServiceSetup } from './analytics'; const coreId = Symbol('core'); const rootConfigPath = ''; +const KIBANA_STARTED_EVENT = 'kibana_started'; + +/** @internal */ +interface UptimePerStep { + start: number; + end: number; +} + +/** @internal */ +interface UptimeSteps { + constructor: UptimePerStep; + preboot: UptimePerStep; + setup: UptimePerStep; + start: UptimePerStep; +} export class Server { public readonly configService: ConfigService; @@ -94,11 +109,15 @@ export class Server { private discoveredPlugins?: DiscoveredPlugins; private readonly logger: LoggerFactory; + private readonly uptimePerStep: Partial = {}; + constructor( rawConfigProvider: RawConfigurationProvider, public readonly env: Env, private readonly loggingSystem: ILoggingSystem ) { + const constructorStartUptime = process.uptime(); + this.logger = this.loggingSystem.asLoggerFactory(); this.log = this.logger.get('server'); this.configService = new ConfigService(rawConfigProvider, env, this.logger); @@ -129,15 +148,18 @@ export class Server { this.savedObjectsStartPromise = new Promise((resolve) => { this.resolveSavedObjectsStartPromise = resolve; }); + + this.uptimePerStep.constructor = { start: constructorStartUptime, end: process.uptime() }; } public async preboot() { this.log.debug('prebooting server'); + const prebootStartUptime = process.uptime(); const prebootTransaction = apm.startTransaction('server-preboot', 'kibana-platform'); const analyticsPreboot = this.analytics.preboot(); - const environmentPreboot = await this.environment.preboot(); + const environmentPreboot = await this.environment.preboot({ analytics: analyticsPreboot }); // Discover any plugins before continuing. This allows other systems to utilize the plugin dependency graph. this.discoveredPlugins = await this.plugins.discover({ environment: environmentPreboot }); @@ -187,15 +209,19 @@ export class Server { this.coreApp.preboot(corePreboot, uiPlugins); prebootTransaction?.end(); + this.uptimePerStep.preboot = { start: prebootStartUptime, end: process.uptime() }; return corePreboot; } public async setup() { this.log.debug('setting up server'); + const setupStartUptime = process.uptime(); const setupTransaction = apm.startTransaction('server-setup', 'kibana-platform'); const analyticsSetup = this.analytics.setup(); + this.registerKibanaStartedEventType(analyticsSetup); + const environmentSetup = this.environment.setup(); // Configuration could have changed after preboot. @@ -223,6 +249,7 @@ export class Server { const capabilitiesSetup = this.capabilities.setup({ http: httpSetup }); const elasticsearchServiceSetup = await this.elasticsearch.setup({ + analytics: analyticsSetup, http: httpSetup, executionContext: executionContextSetup, }); @@ -249,6 +276,7 @@ export class Server { }); const statusSetup = await this.status.setup({ + analytics: analyticsSetup, elasticsearch: elasticsearchServiceSetup, pluginDependencies: pluginTree.asNames, savedObjects: savedObjectsSetup, @@ -259,6 +287,7 @@ export class Server { }); const renderingSetup = await this.rendering.setup({ + elasticsearch: elasticsearchServiceSetup, http: httpSetup, status: statusSetup, uiPlugins, @@ -299,11 +328,13 @@ export class Server { this.coreApp.setup(coreSetup, uiPlugins); setupTransaction?.end(); + this.uptimePerStep.setup = { start: setupStartUptime, end: process.uptime() }; return coreSetup; } public async start() { this.log.debug('starting server'); + const startStartUptime = process.uptime(); const startTransaction = apm.startTransaction('server-start', 'kibana-platform'); const analyticsStart = this.analytics.start(); @@ -352,12 +383,16 @@ export class Server { startTransaction?.end(); + this.uptimePerStep.start = { start: startStartUptime, end: process.uptime() }; + analyticsStart.reportEvent(KIBANA_STARTED_EVENT, { uptime_per_step: this.uptimePerStep }); + return this.coreStart; } public async stop() { this.log.debug('stopping server'); + 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(); await this.savedObjects.stop(); @@ -404,4 +439,92 @@ export class Server { this.configService.setSchema(descriptor.path, descriptor.schema); } } + + private registerKibanaStartedEventType(analyticsSetup: AnalyticsServiceSetup) { + analyticsSetup.registerEventType<{ uptime_per_step: UptimeSteps }>({ + eventType: KIBANA_STARTED_EVENT, + schema: { + uptime_per_step: { + properties: { + constructor: { + properties: { + start: { + type: 'float', + _meta: { + description: + 'Number of seconds the Node.js process has been running until the constructor was called', + }, + }, + end: { + type: 'float', + _meta: { + description: + 'Number of seconds the Node.js process has been running until the constructor finished', + }, + }, + }, + }, + preboot: { + properties: { + start: { + type: 'float', + _meta: { + description: + 'Number of seconds the Node.js process has been running until `preboot` was called', + }, + }, + end: { + type: 'float', + _meta: { + description: + 'Number of seconds the Node.js process has been running until `preboot` finished', + }, + }, + }, + }, + setup: { + properties: { + start: { + type: 'float', + _meta: { + description: + 'Number of seconds the Node.js process has been running until `setup` was called', + }, + }, + end: { + type: 'float', + _meta: { + description: + 'Number of seconds the Node.js process has been running until `setup` finished', + }, + }, + }, + }, + start: { + properties: { + start: { + type: 'float', + _meta: { + description: + 'Number of seconds the Node.js process has been running until `start` was called', + }, + }, + end: { + type: 'float', + _meta: { + description: + 'Number of seconds the Node.js process has been running until `start` finished', + }, + }, + }, + }, + }, + _meta: { + description: + 'Number of seconds the Node.js process has been running until each phase of the server execution is called and finished.', + }, + }, + }, + }); + } } diff --git a/src/core/server/status/status_service.test.ts b/src/core/server/status/status_service.test.ts index 262667fddf26a..70181db9380ff 100644 --- a/src/core/server/status/status_service.test.ts +++ b/src/core/server/status/status_service.test.ts @@ -6,11 +6,16 @@ * Side Public License, v 1. */ -import { of, BehaviorSubject } from 'rxjs'; - -import { ServiceStatus, ServiceStatusLevels, CoreStatus } from './types'; +import { of, BehaviorSubject, firstValueFrom } from 'rxjs'; + +import { + ServiceStatus, + ServiceStatusLevels, + CoreStatus, + InternalStatusServiceSetup, +} from './types'; import { StatusService } from './status_service'; -import { first } from 'rxjs/operators'; +import { first, take, toArray } from 'rxjs/operators'; import { mockCoreContext } from '../core_context.mock'; import { ServiceStatusLevelSnapshotSerializer } from './test_utils'; import { environmentServiceMock } from '../environment/environment_service.mock'; @@ -19,6 +24,8 @@ import { mockRouter, RouterMock } from '../http/router/router.mock'; import { metricsServiceMock } from '../metrics/metrics_service.mock'; import { configServiceMock } from '../config/mocks'; import { coreUsageDataServiceMock } from '../core_usage_data/core_usage_data_service.mock'; +import { analyticsServiceMock } from '../analytics/analytics_service.mock'; +import { AnalyticsServiceSetup } from '..'; expect.addSnapshotSerializer(ServiceStatusLevelSnapshotSerializer); @@ -47,6 +54,7 @@ describe('StatusService', () => { type SetupDeps = Parameters[0]; const setupDeps = (overrides: Partial): SetupDeps => { return { + analytics: analyticsServiceMock.createAnalyticsServiceSetup(), elasticsearch: { status$: of(available), }, @@ -535,5 +543,50 @@ describe('StatusService', () => { ); }); }); + + describe('analytics', () => { + let analyticsMock: jest.Mocked; + let setup: InternalStatusServiceSetup; + + beforeEach(async () => { + analyticsMock = analyticsServiceMock.createAnalyticsServiceSetup(); + setup = await service.setup(setupDeps({ analytics: analyticsMock })); + }); + + test('registers a context provider', async () => { + expect(analyticsMock.registerContextProvider).toHaveBeenCalledTimes(1); + const { context$ } = analyticsMock.registerContextProvider.mock.calls[0][0]; + await expect(firstValueFrom(context$.pipe(take(2), toArray()))).resolves + .toMatchInlineSnapshot(` + Array [ + Object { + "overall_status_level": "initializing", + "overall_status_summary": "Kibana is starting up", + }, + Object { + "overall_status_level": "available", + "overall_status_summary": "All services are available", + }, + ] + `); + }); + + test('registers and reports an event', async () => { + expect(analyticsMock.registerEventType).toHaveBeenCalledTimes(1); + expect(analyticsMock.reportEvent).toHaveBeenCalledTimes(0); + // wait for an emission of overall$ + await firstValueFrom(setup.overall$); + expect(analyticsMock.reportEvent).toHaveBeenCalledTimes(1); + expect(analyticsMock.reportEvent.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "core-overall_status_changed", + Object { + "overall_status_level": "available", + "overall_status_summary": "All services are available", + }, + ] + `); + }); + }); }); }); diff --git a/src/core/server/status/status_service.ts b/src/core/server/status/status_service.ts index 6c8f8716c036e..a3dc0335c88af 100644 --- a/src/core/server/status/status_service.ts +++ b/src/core/server/status/status_service.ts @@ -6,10 +6,21 @@ * Side Public License, v 1. */ -import { Observable, combineLatest, Subscription, Subject, firstValueFrom } from 'rxjs'; -import { map, distinctUntilChanged, shareReplay, debounceTime } from 'rxjs/operators'; +import { + Observable, + combineLatest, + Subscription, + Subject, + firstValueFrom, + tap, + BehaviorSubject, +} from 'rxjs'; +import { map, distinctUntilChanged, shareReplay, debounceTime, takeUntil } from 'rxjs/operators'; import { isDeepStrictEqual } from 'util'; +import type { RootSchema } from '@kbn/analytics-client'; + +import { AnalyticsServiceSetup } from '../analytics'; import { CoreService } from '../../types'; import { CoreContext } from '../core_context'; import { Logger, LogMeta } from '../logging'; @@ -32,7 +43,13 @@ interface StatusLogMeta extends LogMeta { kibana: { status: ServiceStatus }; } +interface StatusAnalyticsPayload { + overall_status_level: string; + overall_status_summary: string; +} + export interface SetupDeps { + analytics: AnalyticsServiceSetup; elasticsearch: Pick; environment: InternalEnvironmentServiceSetup; pluginDependencies: ReadonlyMap; @@ -57,6 +74,7 @@ export class StatusService implements CoreService { } public async setup({ + analytics, elasticsearch, pluginDependencies, http, @@ -88,6 +106,8 @@ export class StatusService implements CoreService { shareReplay(1) ); + this.setupAnalyticsContextAndEvents(analytics); + const coreOverall$ = core$.pipe( // Prevent many emissions at once from dependency status resolution from making this too noisy debounceTime(25), @@ -192,4 +212,40 @@ export class StatusService implements CoreService { shareReplay(1) ); } + + private setupAnalyticsContextAndEvents(analytics: AnalyticsServiceSetup) { + // Set an initial "initializing" status, so we can attach it to early events. + const context$ = new BehaviorSubject({ + overall_status_level: 'initializing', + overall_status_summary: 'Kibana is starting up', + }); + + // The schema is the same for the context and the events. + const schema: RootSchema = { + overall_status_level: { + type: 'keyword', + _meta: { description: 'The current availability level of the service.' }, + }, + overall_status_summary: { + type: 'text', + _meta: { description: 'A high-level summary of the service status.' }, + }, + }; + + const overallStatusChangedEventName = 'core-overall_status_changed'; + + analytics.registerEventType({ eventType: overallStatusChangedEventName, schema }); + analytics.registerContextProvider({ name: 'status info', context$, schema }); + + this.overall$!.pipe( + takeUntil(this.stop$), + map(({ level, summary }) => ({ + overall_status_level: level.toString(), + overall_status_summary: summary, + })), + // Emit the event before spreading the status to the context. + // This way we see from the context the previous status and the current one. + tap((statusPayload) => analytics.reportEvent(overallStatusChangedEventName, statusPayload)) + ).subscribe(context$); + } } diff --git a/src/core/types/execution_context.ts b/src/core/types/execution_context.ts index d790b8d855fd4..d1e5cd10e5e91 100644 --- a/src/core/types/execution_context.ts +++ b/src/core/types/execution_context.ts @@ -14,7 +14,7 @@ // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type KibanaExecutionContext = { /** - * Kibana application initated an operation. + * Kibana application initiated an operation. * */ readonly type?: string; // 'visualization' | 'actions' | 'server' | ..; /** public name of an application or a user-facing feature */ diff --git a/src/dev/build/build_distributables.ts b/src/dev/build/build_distributables.ts index ad9c8323b769d..0a3db5dc36d07 100644 --- a/src/dev/build/build_distributables.ts +++ b/src/dev/build/build_distributables.ts @@ -80,10 +80,11 @@ export async function buildDistributables(log: ToolingLog, options: BuildOptions await run(Tasks.CreatePackageJson); await run(Tasks.InstallDependencies); await run(Tasks.GeneratePackagesOptimizedAssets); - await run(Tasks.CleanPackages); + await run(Tasks.DeleteBazelPackagesFromBuildRoot); await run(Tasks.CreateNoticeFile); await run(Tasks.UpdateLicenseFile); await run(Tasks.RemovePackageJsonDeps); + await run(Tasks.CleanPackageManagerRelatedFiles); await run(Tasks.CleanTypescript); await run(Tasks.CleanExtraFilesFromModules); await run(Tasks.CleanEmptyFolders); diff --git a/src/dev/build/tasks/build_packages_task.ts b/src/dev/build/tasks/build_packages_task.ts index e30ffd082e250..62baf74559a2a 100644 --- a/src/dev/build/tasks/build_packages_task.ts +++ b/src/dev/build/tasks/build_packages_task.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import cpy from 'cpy'; import Path from 'path'; import { discoverBazelPackages } from '@kbn/bazel-packages'; @@ -53,9 +54,9 @@ export const BuildXpack: Task = { }); log.info('copying built x-pack into build dir'); - await scanCopy({ - source: config.resolveFromRepo('x-pack/build/plugin/kibana/x-pack'), - destination: build.resolvePath('x-pack'), + await cpy('**/{.,}*', build.resolvePath('x-pack'), { + cwd: config.resolveFromRepo('x-pack/build/plugin/kibana/x-pack'), + parents: true, }); }, }; diff --git a/src/dev/build/tasks/clean_tasks.ts b/src/dev/build/tasks/clean_tasks.ts index 19747ce72b5a6..c794ca277f77f 100644 --- a/src/dev/build/tasks/clean_tasks.ts +++ b/src/dev/build/tasks/clean_tasks.ts @@ -7,7 +7,7 @@ */ import minimatch from 'minimatch'; - +import { discoverBazelPackages } from '@kbn/bazel-packages'; import { deleteAll, deleteEmptyFolders, scanDelete, Task, GlobalTask } from '../lib'; export const Clean: GlobalTask = { @@ -26,14 +26,11 @@ export const Clean: GlobalTask = { }, }; -export const CleanPackages: Task = { - description: 'Cleaning source for packages that are now installed in node_modules', +export const CleanPackageManagerRelatedFiles: Task = { + description: 'Cleaning package manager related files from the build folder', async run(config, log, build) { - await deleteAll( - [build.resolvePath('packages'), build.resolvePath('yarn.lock'), build.resolvePath('.npmrc')], - log - ); + await deleteAll([build.resolvePath('yarn.lock'), build.resolvePath('.npmrc')], log); }, }; @@ -200,3 +197,16 @@ export const CleanEmptyFolders: Task = { ]); }, }; + +export const DeleteBazelPackagesFromBuildRoot: Task = { + description: + 'Deleting bazel packages outputs from build folder root as they are now installed as node_modules', + + async run(config, log, build) { + const bazelPackagesOnBuildRoot = (await discoverBazelPackages()).map((pkg) => + build.resolvePath(pkg.normalizedRepoRelativeDir) + ); + + await deleteAll(bazelPackagesOnBuildRoot, log); + }, +}; diff --git a/src/dev/build/tasks/copy_source_task.ts b/src/dev/build/tasks/copy_source_task.ts index 141eafc57ae1f..9fc0827e8c2c6 100644 --- a/src/dev/build/tasks/copy_source_task.ts +++ b/src/dev/build/tasks/copy_source_task.ts @@ -6,8 +6,7 @@ * Side Public License, v 1. */ -import { getAllRepoRelativeBazelPackageDirs } from '@kbn/bazel-packages'; -import normalizePath from 'normalize-path'; +import { discoverBazelPackages } from '@kbn/bazel-packages'; import { copyAll, Task } from '../lib'; @@ -48,8 +47,8 @@ export const CopySource: Task = { 'tsconfig*.json', '.i18nrc.json', 'kibana.d.ts', - // explicitly ignore all package roots, even if they're not selected by previous patterns - ...getAllRepoRelativeBazelPackageDirs().map((dir) => `!${normalizePath(dir)}/**`), + // explicitly ignore all bazel package locations, even if they're not selected by previous patterns + ...(await discoverBazelPackages()).map((pkg) => `!${pkg.normalizedRepoRelativeDir}/**`), ], }); }, 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 88240429856d1..49967feb214d6 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 @@ -65,16 +65,16 @@ export const CreateDockerUbuntu: Task = { async run(config, log, build) { await runDockerGenerator(config, log, build, { architecture: 'x64', + baseImage: 'ubuntu', context: false, image: true, - ubuntu: true, dockerBuildDate, }); await runDockerGenerator(config, log, build, { architecture: 'aarch64', + baseImage: 'ubuntu', context: false, image: true, - ubuntu: true, dockerBuildDate, }); }, @@ -86,8 +86,8 @@ export const CreateDockerUBI: Task = { async run(config, log, build) { await runDockerGenerator(config, log, build, { architecture: 'x64', + baseImage: 'ubi', context: false, - ubi: true, image: true, }); }, @@ -99,16 +99,16 @@ export const CreateDockerCloud: Task = { async run(config, log, build) { await runDockerGenerator(config, log, build, { architecture: 'x64', + baseImage: 'ubuntu', context: false, cloud: true, - ubuntu: true, image: true, }); await runDockerGenerator(config, log, build, { architecture: 'aarch64', + baseImage: 'ubuntu', context: false, cloud: true, - ubuntu: true, image: true, }); }, @@ -119,23 +119,25 @@ export const CreateDockerContexts: Task = { async run(config, log, build) { await runDockerGenerator(config, log, build, { - ubuntu: true, + baseImage: 'ubuntu', context: true, image: false, dockerBuildDate, }); await runDockerGenerator(config, log, build, { - ubi: true, + baseImage: 'ubi', context: true, image: false, }); await runDockerGenerator(config, log, build, { ironbank: true, + baseImage: 'none', context: true, image: false, }); await runDockerGenerator(config, log, build, { + baseImage: 'ubuntu', cloud: true, context: true, image: false, diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker index 7b569f8d02068..77e8e5d63bbdf 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker @@ -40,6 +40,13 @@ kibana_vars=( csp.report_to data.autocomplete.valueSuggestions.terminateAfter data.autocomplete.valueSuggestions.timeout + data.search.sessions.defaultExpiration + data.search.sessions.enabled + data.search.sessions.maxUpdateRetries + data.search.sessions.notTouchedInProgressTimeout + data.search.sessions.notTouchedTimeout + data.search.sessions.pageSize + data.search.sessions.trackingInterval unifiedSearch.autocomplete.valueSuggestions.terminateAfter unifiedSearch.autocomplete.valueSuggestions.timeout unifiedSearch.autocomplete.querySuggestions.enabled 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 264c6e52db0eb..d8b604f00b46e 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/run.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/run.ts @@ -29,22 +29,21 @@ export async function runDockerGenerator( build: Build, flags: { architecture?: string; + baseImage: 'none' | 'ubi' | 'ubuntu'; context: boolean; image: boolean; - ubi?: boolean; - ubuntu?: boolean; ironbank?: boolean; cloud?: boolean; dockerBuildDate?: string; } ) { - let baseOSImage = ''; - if (flags.ubuntu) baseOSImage = 'ubuntu:20.04'; - if (flags.ubi) baseOSImage = 'docker.elastic.co/ubi8/ubi-minimal:latest'; + let baseImageName = ''; + if (flags.baseImage === 'ubuntu') baseImageName = 'ubuntu:20.04'; + if (flags.baseImage === 'ubi') baseImageName = 'docker.elastic.co/ubi8/ubi-minimal:latest'; const ubiVersionTag = 'ubi8'; let imageFlavor = ''; - if (flags.ubi) imageFlavor += `-${ubiVersionTag}`; + if (flags.baseImage === 'ubi') imageFlavor += `-${ubiVersionTag}`; if (flags.ironbank) imageFlavor += '-ironbank'; if (flags.cloud) imageFlavor += '-cloud'; @@ -61,7 +60,6 @@ export async function runDockerGenerator( const artifactsDir = config.resolveFromTarget('.'); const beatsDir = config.resolveFromRepo('.beats'); const dockerBuildDate = flags.dockerBuildDate || new Date().toISOString(); - // That would produce oss, default and default-ubi7 const dockerBuildDir = config.resolveFromRepo('build', 'kibana-docker', `default${imageFlavor}`); const imageArchitecture = flags.architecture === 'aarch64' ? '-aarch64' : ''; const dockerTargetFilename = config.resolveFromTarget( @@ -93,10 +91,9 @@ export async function runDockerGenerator( dockerPush, dockerTagQualifier, dockerCrossCompile, - baseOSImage, + baseImageName, dockerBuildDate, - ubi: flags.ubi, - ubuntu: flags.ubuntu, + baseImage: flags.baseImage, cloud: flags.cloud, metricbeatTarball, filebeatTarball, diff --git a/src/dev/build/tasks/os_packages/docker_generator/template_context.ts b/src/dev/build/tasks/os_packages/docker_generator/template_context.ts index 35977d47aaaa7..32a551820a05b 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/template_context.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/template_context.ts @@ -20,12 +20,11 @@ export interface TemplateContext { imageTag: string; dockerBuildDir: string; dockerTargetFilename: string; - baseOSImage: string; dockerBuildDate: string; usePublicArtifact?: boolean; publicArtifactSubdomain: string; - ubi?: boolean; - ubuntu?: boolean; + baseImage: 'none' | 'ubi' | 'ubuntu'; + baseImageName: string; cloud?: boolean; metricbeatTarball?: string; filebeatTarball?: string; 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 95f6a56ef68cb..d171c48662cf6 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 @@ -9,7 +9,7 @@ # Build stage 0 `builder`: # Extract Kibana artifact ################################################################################ -FROM {{{baseOSImage}}} AS builder +FROM {{{baseImageName}}} AS builder {{#ubi}} RUN {{packageManager}} install -y findutils tar gzip @@ -54,7 +54,7 @@ RUN mkdir -p /opt/filebeat /opt/metricbeat && \ # Copy kibana from stage 0 # Add entrypoint ################################################################################ -FROM {{{baseOSImage}}} +FROM {{{baseImageName}}} EXPOSE 5601 {{#ubi}} diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.ts b/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.ts index 316428d46a957..472e64e849b58 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.ts @@ -18,7 +18,7 @@ function generator({ dockerCrossCompile, version, dockerTargetFilename, - baseOSImage, + baseImageName, architecture, }: TemplateContext) { const dockerTargetName = `${imageTag}${imageFlavor}:${version}${ @@ -61,7 +61,7 @@ function generator({ done } - retry_docker_pull ${baseOSImage} + retry_docker_pull ${baseImageName} echo "Building: kibana${imageFlavor}-docker"; \\ ${dockerBuild} diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts b/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts index 94068f2b64b12..63b04ed6f70b0 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts @@ -16,7 +16,9 @@ function generator(options: TemplateContext) { const dir = options.ironbank ? 'ironbank' : 'base'; const template = readFileSync(resolve(__dirname, dir, './Dockerfile')); return Mustache.render(template.toString(), { - packageManager: options.ubi ? 'microdnf' : 'apt-get', + packageManager: options.baseImage === 'ubi' ? 'microdnf' : 'apt-get', + ubi: options.baseImage === 'ubi', + ubuntu: options.baseImage === 'ubuntu', ...options, }); } diff --git a/src/dev/build/tasks/transpile_babel_task.ts b/src/dev/build/tasks/transpile_babel_task.ts index 37f63d31415e9..ee7d1e19de43a 100644 --- a/src/dev/build/tasks/transpile_babel_task.ts +++ b/src/dev/build/tasks/transpile_babel_task.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { discoverBazelPackages } from '@kbn/bazel-packages'; import { pipeline } from 'stream'; import { promisify } from 'util'; @@ -24,10 +25,10 @@ const transpileWithBabel = async (srcGlobs: string[], build: Build, preset: stri vfs.src( srcGlobs.concat([ '!**/*.d.ts', - '!packages/**', '!**/node_modules/**', '!**/bower_components/**', '!**/__tests__/**', + ...(await discoverBazelPackages()).map((pkg) => `!${pkg.normalizedRepoRelativeDir}/**`), ]), { cwd: buildRoot, diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index 1cfcb7b5cf284..bd5fd75e30998 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -77,6 +77,6 @@ export const LICENSE_OVERRIDES = { 'jsts@1.6.2': ['Eclipse Distribution License - v 1.0'], // cf. https://github.com/bjornharrtell/jsts '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint '@elastic/ems-client@8.3.0': ['Elastic License 2.0'], - '@elastic/eui@54.0.0': ['SSPL-1.0 OR Elastic License 2.0'], + '@elastic/eui@55.0.1': ['SSPL-1.0 OR Elastic License 2.0'], 'language-subtag-registry@0.3.21': ['CC-BY-4.0'], // retired ODC‑By license https://github.com/mattcg/language-subtag-registry }; diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index 8aa2d6f1cfe55..ced601d0f3981 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -146,10 +146,10 @@ export const TEMPORARILY_IGNORED_PATHS = [ 'x-pack/plugins/monitoring/public/icons/health-green.svg', 'x-pack/plugins/monitoring/public/icons/health-red.svg', 'x-pack/plugins/monitoring/public/icons/health-yellow.svg', - 'x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/noto/NotoSansCJKtc-Medium.ttf', - 'x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/noto/NotoSansCJKtc-Regular.ttf', - 'x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/roboto/Roboto-Italic.ttf', - 'x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/roboto/Roboto-Medium.ttf', - 'x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/roboto/Roboto-Regular.ttf', - 'x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/img/logo-grey.png', + 'x-pack/plugins/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Medium.ttf', + 'x-pack/plugins/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Regular.ttf', + 'x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Italic.ttf', + 'x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Medium.ttf', + 'x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Regular.ttf', + 'x-pack/plugins/screenshotting/server/assets/img/logo-grey.png', ]; diff --git a/src/dev/prs/kibana_qa_pr_list.json b/src/dev/prs/kibana_qa_pr_list.json index e8d27ba9f2f0a..503c95d2e7c0f 100644 --- a/src/dev/prs/kibana_qa_pr_list.json +++ b/src/dev/prs/kibana_qa_pr_list.json @@ -89,8 +89,10 @@ "Feature:Observability Landing - Milestone 1", "Feature:Osquery", "Feature:Transforms", +"Feature:Unified Integrations", "Synthetics", "Team: AWL: Platform", +"Team: AWP: Visualization", "Team: Actionable Observability", "Team: CTI", "Team: SecuritySolution", @@ -109,6 +111,7 @@ "Team:Infra Monitoring UI", "Team:Ingest Management", "Team:Observability", +"Team:Unified observability", "Team:Onboarding and Lifecycle Mgt", "Team:Operations", "Team:QA", diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 8075abcdcf79b..45b8aad7df8cf 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -8,6 +8,7 @@ // Please also add new aliases to test/scripts/jenkins_storybook.sh export const storybookAliases = { + unified_search: 'src/plugins/unified_search/.storybook', coloring: 'packages/kbn-coloring/.storybook', apm: 'x-pack/plugins/apm/.storybook', canvas: 'x-pack/plugins/canvas/storybook', @@ -17,7 +18,7 @@ export const storybookAliases = { custom_integrations: 'src/plugins/custom_integrations/storybook', dashboard_enhanced: 'x-pack/plugins/dashboard_enhanced/.storybook', dashboard: 'src/plugins/dashboard/.storybook', - data_enhanced: 'x-pack/plugins/data_enhanced/.storybook', + data: 'src/plugins/data/.storybook', discover: 'src/plugins/discover/.storybook', embeddable: 'src/plugins/embeddable/.storybook', expression_error: 'src/plugins/expression_error/.storybook', diff --git a/src/dev/typescript/projects.ts b/src/dev/typescript/projects.ts index 6d9b372069b22..848ca09a86671 100644 --- a/src/dev/typescript/projects.ts +++ b/src/dev/typescript/projects.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import glob from 'glob'; +import globby from 'globby'; import Path from 'path'; import { REPO_ROOT } from '@kbn/utils'; import { BAZEL_PACKAGE_DIRS } from '@kbn/bazel-packages'; @@ -23,11 +23,8 @@ const createProject = (rootRelativePath: string, options: ProjectOptions = {}) = cache: PROJECT_CACHE, }); -const findProjects = (pattern: string) => - // NOTE: using glob.sync rather than glob-all or globby - // because it takes less than 10 ms, while the other modules - // both took closer to 1000ms. - glob.sync(pattern, { cwd: REPO_ROOT }).map((path) => createProject(path)); +const findProjects = (patterns: string[]) => + globby.sync(patterns, { cwd: REPO_ROOT }).map((path) => createProject(path)); export const PROJECTS = [ createProject('tsconfig.json'), @@ -73,16 +70,18 @@ export const PROJECTS = [ disableTypeCheck: true, }), - ...findProjects('src/plugins/*/tsconfig.json'), - ...findProjects('src/plugins/chart_expressions/*/tsconfig.json'), - ...findProjects('src/plugins/vis_types/*/tsconfig.json'), - ...findProjects('x-pack/plugins/*/tsconfig.json'), - ...findProjects('examples/*/tsconfig.json'), - ...findProjects('x-pack/examples/*/tsconfig.json'), - ...findProjects('test/plugin_functional/plugins/*/tsconfig.json'), - ...findProjects('test/interpreter_functional/plugins/*/tsconfig.json'), - ...findProjects('test/server_integration/__fixtures__/plugins/*/tsconfig.json'), - ...findProjects('packages/kbn-type-summarizer/tests/tsconfig.json'), - - ...BAZEL_PACKAGE_DIRS.flatMap((dir) => findProjects(`${dir}/*/tsconfig.json`)), + // Glob patterns to be all search at once + ...findProjects([ + 'src/plugins/*/tsconfig.json', + 'src/plugins/chart_expressions/*/tsconfig.json', + 'src/plugins/vis_types/*/tsconfig.json', + 'x-pack/plugins/*/tsconfig.json', + 'examples/*/tsconfig.json', + 'x-pack/examples/*/tsconfig.json', + 'test/plugin_functional/plugins/*/tsconfig.json', + 'test/interpreter_functional/plugins/*/tsconfig.json', + 'test/server_integration/__fixtures__/plugins/*/tsconfig.json', + 'packages/kbn-type-summarizer/tests/tsconfig.json', + ...BAZEL_PACKAGE_DIRS.map((dir) => `${dir}/*/tsconfig.json`), + ]), ]; diff --git a/src/plugins/advanced_settings/public/management_app/components/field/field.test.tsx b/src/plugins/advanced_settings/public/management_app/components/field/field.test.tsx index 555df3c2c5c11..0e67d787be144 100644 --- a/src/plugins/advanced_settings/public/management_app/components/field/field.test.tsx +++ b/src/plugins/advanced_settings/public/management_app/components/field/field.test.tsx @@ -388,7 +388,7 @@ describe('Field', () => { const updated = wrapper.update(); findTestSubject(updated, `advancedSetting-resetField-${setting.name}`).simulate('click'); expect(handleChange).toBeCalledWith(setting.name, { - value: getEditableValue(setting.type, setting.defVal), + value: getEditableValue(setting.type, setting.defVal, setting.defVal), changeImage: true, }); }); diff --git a/src/plugins/advanced_settings/public/management_app/components/field/field.tsx b/src/plugins/advanced_settings/public/management_app/components/field/field.tsx index fd4674a7caf6e..56673cda1a953 100644 --- a/src/plugins/advanced_settings/public/management_app/components/field/field.tsx +++ b/src/plugins/advanced_settings/public/management_app/components/field/field.tsx @@ -100,7 +100,7 @@ export class Field extends PureComponent { if (type === 'image') { this.cancelChangeImage(); return this.handleChange({ - value: getEditableValue(type, defVal), + value: getEditableValue(type, defVal, defVal), changeImage: true, }); } diff --git a/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.test.ts b/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.test.ts index ac8cb5c8b86a3..705516a9f1e96 100644 --- a/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.test.ts +++ b/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.test.ts @@ -111,6 +111,7 @@ describe('interpreter/functions#gauge', () => { logDatatable: (name: string, datatable: Datatable) => { loggedTable = datatable; }, + reset: () => {}, }, }, }; diff --git a/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.ts b/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.ts index 48c7261e43016..70ecd25839d19 100644 --- a/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.ts +++ b/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.ts @@ -194,6 +194,9 @@ export const gaugeFunction = (): GaugeExpressionFunctionDefinition => ({ } if (handlers?.inspectorAdapters?.tables) { + handlers.inspectorAdapters.tables.reset(); + handlers.inspectorAdapters.tables.allowCsvExport = true; + const logTable = prepareLogTable( data, [ diff --git a/src/plugins/chart_expressions/expression_heatmap/common/expression_functions/heatmap_function.test.ts b/src/plugins/chart_expressions/expression_heatmap/common/expression_functions/heatmap_function.test.ts index 13beee6b0f701..d34442ca3f518 100644 --- a/src/plugins/chart_expressions/expression_heatmap/common/expression_functions/heatmap_function.test.ts +++ b/src/plugins/chart_expressions/expression_heatmap/common/expression_functions/heatmap_function.test.ts @@ -69,6 +69,7 @@ describe('interpreter/functions#heatmap', () => { logDatatable: (name: string, datatable: Datatable) => { loggedTable = datatable; }, + reset: () => {}, }, }, }; diff --git a/src/plugins/chart_expressions/expression_heatmap/common/expression_functions/heatmap_function.ts b/src/plugins/chart_expressions/expression_heatmap/common/expression_functions/heatmap_function.ts index c440176962faf..954c5acee7152 100644 --- a/src/plugins/chart_expressions/expression_heatmap/common/expression_functions/heatmap_function.ts +++ b/src/plugins/chart_expressions/expression_heatmap/common/expression_functions/heatmap_function.ts @@ -161,6 +161,9 @@ export const heatmapFunction = (): HeatmapExpressionFunctionDefinition => ({ validateAccessor(args.splitColumnAccessor, data.columns); if (handlers?.inspectorAdapters?.tables) { + handlers.inspectorAdapters.tables.reset(); + handlers.inspectorAdapters.tables.allowCsvExport = true; + const argsTable: Dimension[] = []; if (args.valueAccessor) { prepareHeatmapLogTable( diff --git a/src/plugins/chart_expressions/expression_heatmap/common/expression_functions/heatmap_legend.ts b/src/plugins/chart_expressions/expression_heatmap/common/expression_functions/heatmap_legend.ts index 1135708db8c22..79a356ddad934 100644 --- a/src/plugins/chart_expressions/expression_heatmap/common/expression_functions/heatmap_legend.ts +++ b/src/plugins/chart_expressions/expression_heatmap/common/expression_functions/heatmap_legend.ts @@ -8,6 +8,7 @@ import { Position } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; +import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/common/constants'; import { EXPRESSION_HEATMAP_LEGEND_NAME } from '../constants'; import { HeatmapLegendConfig, HeatmapLegendConfigResult } from '../types'; @@ -52,10 +53,19 @@ export const heatmapLegendConfig: ExpressionFunctionDefinition< }), }, legendSize: { - types: ['number'], + types: ['string'], + default: DEFAULT_LEGEND_SIZE, help: i18n.translate('expressionHeatmap.function.args.legendSize.help', { - defaultMessage: 'Specifies the legend size in pixels.', + defaultMessage: 'Specifies the legend size.', }), + options: [ + LegendSize.AUTO, + LegendSize.SMALL, + LegendSize.MEDIUM, + LegendSize.LARGE, + LegendSize.EXTRA_LARGE, + ], + strict: true, }, }, fn(input, args) { diff --git a/src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts index d3e7444ad08f2..19f63f9df9890 100644 --- a/src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts @@ -15,6 +15,7 @@ import { import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; import { CustomPaletteState } from '@kbn/charts-plugin/common'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; import { EXPRESSION_HEATMAP_NAME, EXPRESSION_HEATMAP_LEGEND_NAME, @@ -43,7 +44,7 @@ export interface HeatmapLegendConfig { * Exact legend width (vertical) or height (horizontal) * Limited to max of 70% of the chart container dimension Vertical legends limited to min of 30% of computed width */ - legendSize?: number; + legendSize?: LegendSize; } export type HeatmapLegendConfigResult = HeatmapLegendConfig & { diff --git a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx index 4f3e77b8f1d6e..19a57272116c8 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx +++ b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx @@ -17,6 +17,7 @@ import { findTestSubject } from '@elastic/eui/lib/test'; import { act } from 'react-dom/test-utils'; import { HeatmapRenderProps, HeatmapArguments } from '../../common'; import HeatmapComponent from './heatmap_component'; +import { LegendSize } from '@kbn/visualizations-plugin/common'; jest.mock('@elastic/charts', () => { const original = jest.requireActual('@elastic/charts'); @@ -47,6 +48,7 @@ const args: HeatmapArguments = { isVisible: true, position: 'top', type: 'heatmap_legend', + legendSize: LegendSize.SMALL, }, gridConfig: { isCellLabelVisible: true, @@ -119,6 +121,33 @@ describe('HeatmapComponent', function () { expect(component.find(Settings).prop('legendPosition')).toEqual('top'); }); + it('sets correct legend sizes', () => { + const component = shallowWithIntl(); + expect(component.find(Settings).prop('legendSize')).toEqual(80); + + component.setProps({ + args: { + ...args, + legend: { + ...args.legend, + legendSize: LegendSize.AUTO, + }, + }, + }); + expect(component.find(Settings).prop('legendSize')).toBeUndefined(); + + component.setProps({ + args: { + ...args, + legend: { + ...args.legend, + legendSize: undefined, + }, + }, + }); + expect(component.find(Settings).prop('legendSize')).toEqual(130); + }); + it('renders the legend toggle component if uiState is set', async () => { const component = mountWithIntl(); await actWithTimeout(async () => { diff --git a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx index a9b70d1bc2edd..36270ef896e46 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx +++ b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx @@ -29,6 +29,10 @@ import { getAccessorByDimension, getFormatByAccessor, } from '@kbn/visualizations-plugin/common/utils'; +import { + DEFAULT_LEGEND_SIZE, + LegendSizeToPixels, +} from '@kbn/visualizations-plugin/common/constants'; import type { HeatmapRenderProps, FilterEvent, BrushEvent } from '../../common'; import { applyPaletteParams, findMinMaxByColumnId, getSortPredicate } from './helpers'; import { @@ -485,7 +489,7 @@ export const HeatmapComponent: FC = memo( onElementClick={interactive ? (onElementClick as ElementClickListener) : undefined} showLegend={showLegend ?? args.legend.isVisible} legendPosition={args.legend.position} - legendSize={args.legend.legendSize} + legendSize={LegendSizeToPixels[args.legend.legendSize ?? DEFAULT_LEGEND_SIZE]} legendColorPicker={uiState ? LegendColorPickerWrapper : undefined} debugState={window._echDebugStateFlag ?? false} tooltip={tooltip} diff --git a/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.test.ts b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.test.ts index 65f738e8e227d..6524c15c44af1 100644 --- a/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.test.ts @@ -68,6 +68,7 @@ describe('interpreter/functions#metric', () => { logDatatable: (name: string, datatable: Datatable) => { loggedTable = datatable; }, + reset: () => {}, }, }, }; diff --git a/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts index 2310ffb8c5926..add31e7b12014 100644 --- a/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts +++ b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts @@ -146,6 +146,9 @@ export const metricVisFunction = (): MetricVisExpressionFunctionDefinition => ({ validateAccessor(args.bucket, input.columns); if (handlers?.inspectorAdapters?.tables) { + handlers.inspectorAdapters.tables.reset(); + handlers.inspectorAdapters.tables.allowCsvExport = true; + const argsTable: Dimension[] = [ [ args.metric, diff --git a/src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts index 695b7ad4754fa..8c370480a7be9 100644 --- a/src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts @@ -54,4 +54,6 @@ export interface MetricOptions { color?: string; bgColor?: string; lightText: boolean; + colIndex: number; + rowIndex: number; } diff --git a/src/plugins/chart_expressions/expression_metric/public/components/__snapshots__/metric_component.test.tsx.snap b/src/plugins/chart_expressions/expression_metric/public/components/__snapshots__/metric_component.test.tsx.snap index 684f42d527c19..b18c521bea653 100644 --- a/src/plugins/chart_expressions/expression_metric/public/components/__snapshots__/metric_component.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_metric/public/components/__snapshots__/metric_component.test.tsx.snap @@ -19,6 +19,7 @@ Array [ metric={ Object { "bgColor": undefined, + "colIndex": 0, "color": undefined, "label": "1st percentile of bytes", "lightText": false, @@ -26,6 +27,7 @@ Array [ "value": 182, } } + onFilter={[Function]} style={ Object { "bgColor": false, @@ -53,6 +55,7 @@ Array [ metric={ Object { "bgColor": undefined, + "colIndex": 1, "color": undefined, "label": "99th percentile of bytes", "lightText": false, @@ -60,6 +63,7 @@ Array [ "value": 445842.4634666484, } } + onFilter={[Function]} style={ Object { "bgColor": false, @@ -91,6 +95,7 @@ exports[`MetricVisComponent should render correct structure for single metric 1` metric={ Object { "bgColor": undefined, + "colIndex": 0, "color": undefined, "label": "Count", "lightText": false, @@ -98,6 +103,7 @@ exports[`MetricVisComponent should render correct structure for single metric 1` "value": 4301021, } } + onFilter={[Function]} style={ Object { "bgColor": false, diff --git a/src/plugins/chart_expressions/expression_metric/public/components/metric_component.tsx b/src/plugins/chart_expressions/expression_metric/public/components/metric_component.tsx index a7d0eb8cad8c6..6fe19c0e72515 100644 --- a/src/plugins/chart_expressions/expression_metric/public/components/metric_component.tsx +++ b/src/plugins/chart_expressions/expression_metric/public/components/metric_component.tsx @@ -63,6 +63,7 @@ class MetricVisComponent extends Component { return dimensions.metrics.reduce( (acc: MetricOptions[], metric: string | ExpressionValueVisDimension) => { const column = getColumnByAccessor(metric, table?.columns); + const colIndex = table?.columns.indexOf(column!); const formatter = getFormatService().deserialize( getFormatByAccessor(metric, table.columns) ); @@ -81,7 +82,7 @@ class MetricVisComponent extends Component { title = `${bucketValue} - ${title}`; } - const shouldBrush = stops.length > 1 && shouldApplyColor(color ?? ''); + const shouldBrush = shouldApplyColor(color ?? ''); return { label: title, value: formattedValue, @@ -89,6 +90,7 @@ class MetricVisComponent extends Component { bgColor: shouldBrush && (style.bgColor ?? false) ? color : undefined, lightText: shouldBrush && (style.bgColor ?? false) && needsLightText(color), rowIndex, + colIndex, }; }); @@ -98,20 +100,21 @@ class MetricVisComponent extends Component { ); } - private filterBucket = (row: number) => { + private filterColumn = (row: number, metricColIndex: number) => { const { dimensions } = this.props.visParams; - if (!dimensions.bucket) { - return; - } const table = this.props.visData; + let column = dimensions.bucket ? getAccessor(dimensions.bucket) : metricColIndex; + if (typeof column === 'object' && 'id' in column) { + column = table.columns.indexOf(column); + } this.props.fireEvent({ - name: 'filterBucket', + name: 'filter', data: { data: [ { table, - column: getAccessor(dimensions.bucket), + column, row, }, ], @@ -144,9 +147,7 @@ class MetricVisComponent extends Component { key={index} metric={metric} style={this.props.visParams.metric.style} - onFilter={ - this.props.visParams.dimensions.bucket ? () => this.filterBucket(index) : undefined - } + onFilter={() => this.filterColumn(metric.rowIndex, metric.colIndex)} autoScale={this.props.visParams.metric.autoScale} colorFullBackground={this.props.visParams.metric.colorFullBackground} labelConfig={this.props.visParams.metric.labels} diff --git a/src/plugins/chart_expressions/expression_metric/public/components/metric_value.test.tsx b/src/plugins/chart_expressions/expression_metric/public/components/metric_value.test.tsx index f86f70341891c..fee24d8aa5e7f 100644 --- a/src/plugins/chart_expressions/expression_metric/public/components/metric_value.test.tsx +++ b/src/plugins/chart_expressions/expression_metric/public/components/metric_value.test.tsx @@ -13,7 +13,13 @@ import { MetricVisValue } from './metric_value'; import { MetricOptions, MetricStyle, VisParams } from '../../common/types'; import { LabelPosition } from '../../common/constants'; -const baseMetric: MetricOptions = { label: 'Foo', value: 'foo', lightText: false }; +const baseMetric: MetricOptions = { + label: 'Foo', + value: 'foo', + lightText: false, + rowIndex: 0, + colIndex: 0, +}; const font: MetricStyle = { spec: { fontSize: '12px' }, diff --git a/src/plugins/chart_expressions/expression_metric/public/components/metric_value.tsx b/src/plugins/chart_expressions/expression_metric/public/components/metric_value.tsx index e948b95af52fe..40de364cfa5dc 100644 --- a/src/plugins/chart_expressions/expression_metric/public/components/metric_value.tsx +++ b/src/plugins/chart_expressions/expression_metric/public/components/metric_value.tsx @@ -8,6 +8,7 @@ import React, { CSSProperties } from 'react'; import classNames from 'classnames'; +import { i18n } from '@kbn/i18n'; import type { MetricOptions, MetricStyle, MetricVisParam } from '../../common/types'; interface MetricVisValueProps { @@ -72,7 +73,13 @@ export const MetricVisValue = ({ if (onFilter) { return ( - ); diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap index 81ada60a772cd..2a06459822a0e 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap @@ -112,7 +112,7 @@ Object { }, "legendDisplay": "show", "legendPosition": "right", - "legendSize": undefined, + "legendSize": "medium", "maxLegendLines": 2, "metric": Object { "accessor": 0, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap index 28d5f35c89cbf..0f64f4c0a4779 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap @@ -112,7 +112,7 @@ Object { }, "legendDisplay": "show", "legendPosition": "right", - "legendSize": undefined, + "legendSize": "small", "maxLegendLines": 2, "metric": Object { "accessor": 0, @@ -246,7 +246,7 @@ Object { }, "legendDisplay": "show", "legendPosition": "right", - "legendSize": undefined, + "legendSize": "small", "maxLegendLines": 2, "metric": Object { "accessor": 0, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap index e1d9f98f57209..9f6210f42b48a 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap @@ -112,7 +112,7 @@ Object { }, "legendDisplay": "show", "legendPosition": "right", - "legendSize": undefined, + "legendSize": "medium", "maxLegendLines": 2, "metric": Object { "accessor": 0, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap index 33525b33f6f96..9cdc69904460a 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap @@ -86,7 +86,7 @@ Object { }, "legendDisplay": "show", "legendPosition": "right", - "legendSize": undefined, + "legendSize": "medium", "maxLegendLines": 2, "metric": Object { "accessor": 0, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts index 250d0f1033ffe..d7839d1f7d1e9 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts @@ -47,7 +47,7 @@ export const strings = { }), getLegendSizeArgHelp: () => i18n.translate('expressionPartitionVis.reusable.function.args.legendSizeHelpText', { - defaultMessage: 'Specifies the legend size in pixels', + defaultMessage: 'Specifies the legend size', }), getNestedLegendArgHelp: () => i18n.translate('expressionPartitionVis.reusable.function.args.nestedLegendHelpText', { diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.test.ts index 0ce174b38677f..54b478e7deed9 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.test.ts @@ -135,6 +135,7 @@ describe('interpreter/functions#mosaicVis', () => { logDatatable: (name: string, datatable: Datatable) => { loggedTable = datatable; }, + reset: () => {}, }, }, }; diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts index 74a85dd01e6e4..ae3f17ff8df3a 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts @@ -8,6 +8,7 @@ import { Position } from '@elastic/charts'; import { prepareLogTable, validateAccessor } from '@kbn/visualizations-plugin/common/utils'; +import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/common/constants'; import { LegendDisplay, PartitionVisParams } from '../types/expression_renderers'; import { ChartTypes, MosaicVisExpressionFunctionDefinition } from '../types'; import { @@ -64,8 +65,17 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ strict: true, }, legendSize: { - types: ['number'], + types: ['string'], + default: DEFAULT_LEGEND_SIZE, help: strings.getLegendSizeArgHelp(), + options: [ + LegendSize.AUTO, + LegendSize.SMALL, + LegendSize.MEDIUM, + LegendSize.LARGE, + LegendSize.EXTRA_LARGE, + ], + strict: true, }, nestedLegend: { types: ['boolean'], @@ -134,6 +144,9 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ }; if (handlers?.inspectorAdapters?.tables) { + handlers.inspectorAdapters.tables.reset(); + handlers.inspectorAdapters.tables.allowCsvExport = true; + const logTable = prepareLogTable( context, [ diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts index c542a25c30875..2ac50372e178d 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts @@ -14,7 +14,7 @@ import { ValueFormats, LegendDisplay, } from '../types/expression_renderers'; -import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; +import { ExpressionValueVisDimension, LegendSize } from '@kbn/visualizations-plugin/common'; import { Datatable } from '@kbn/expressions-plugin/common/expression_types/specs'; import { pieVisFunction } from './pie_vis_function'; import { PARTITION_LABELS_VALUE } from '../constants'; @@ -31,6 +31,7 @@ describe('interpreter/functions#pieVis', () => { addTooltip: true, legendDisplay: LegendDisplay.SHOW, legendPosition: 'right', + legendSize: LegendSize.SMALL, isDonut: true, emptySizeRatio: EmptySizeRatios.SMALL, nestedLegend: true, @@ -128,6 +129,7 @@ describe('interpreter/functions#pieVis', () => { logDatatable: (name: string, datatable: Datatable) => { loggedTable = datatable; }, + reset: () => {}, }, }, }; diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts index 9a30008cc6bb3..5b69fbc6194fd 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts @@ -8,6 +8,7 @@ import { Position } from '@elastic/charts'; import { prepareLogTable, validateAccessor } from '@kbn/visualizations-plugin/common/utils'; +import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/common/constants'; import { EmptySizeRatios, LegendDisplay, PartitionVisParams } from '../types/expression_renderers'; import { ChartTypes, PieVisExpressionFunctionDefinition } from '../types'; import { @@ -64,8 +65,17 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ strict: true, }, legendSize: { - types: ['number'], + types: ['string'], + default: DEFAULT_LEGEND_SIZE, help: strings.getLegendSizeArgHelp(), + options: [ + LegendSize.AUTO, + LegendSize.SMALL, + LegendSize.MEDIUM, + LegendSize.LARGE, + LegendSize.EXTRA_LARGE, + ], + strict: true, }, nestedLegend: { types: ['boolean'], @@ -154,6 +164,9 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ }; if (handlers?.inspectorAdapters?.tables) { + handlers.inspectorAdapters.tables.reset(); + handlers.inspectorAdapters.tables.allowCsvExport = true; + const logTable = prepareLogTable( context, [ diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.test.ts index 5d2cd5b8a0c38..e10dbf09dd179 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.test.ts @@ -135,6 +135,7 @@ describe('interpreter/functions#treemapVis', () => { logDatatable: (name: string, datatable: Datatable) => { loggedTable = datatable; }, + reset: () => {}, }, }, }; diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts index 062cf7e78b4ea..427179ca5a25a 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts @@ -8,6 +8,7 @@ import { Position } from '@elastic/charts'; import { prepareLogTable, validateAccessor } from '@kbn/visualizations-plugin/common/utils'; +import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/common/constants'; import { LegendDisplay, PartitionVisParams } from '../types/expression_renderers'; import { ChartTypes, TreemapVisExpressionFunctionDefinition } from '../types'; import { @@ -64,8 +65,17 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => strict: true, }, legendSize: { - types: ['number'], + types: ['string'], + default: DEFAULT_LEGEND_SIZE, help: strings.getLegendSizeArgHelp(), + options: [ + LegendSize.AUTO, + LegendSize.SMALL, + LegendSize.MEDIUM, + LegendSize.LARGE, + LegendSize.EXTRA_LARGE, + ], + strict: true, }, nestedLegend: { types: ['boolean'], @@ -134,6 +144,9 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => }; if (handlers?.inspectorAdapters?.tables) { + handlers.inspectorAdapters.tables.reset(); + handlers.inspectorAdapters.tables.allowCsvExport = true; + const logTable = prepareLogTable( context, [ diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.test.ts index 01cbe844728b3..af36e4ea04a10 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.test.ts @@ -106,6 +106,7 @@ describe('interpreter/functions#waffleVis', () => { logDatatable: (name: string, datatable: Datatable) => { loggedTable = datatable; }, + reset: () => {}, }, }, }; diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts index 2f947a3d5fea6..0867e6cb9bd76 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts @@ -8,6 +8,7 @@ import { Position } from '@elastic/charts'; import { prepareLogTable, validateAccessor } from '@kbn/visualizations-plugin/common/utils'; +import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/common/constants'; import { LegendDisplay, PartitionVisParams } from '../types/expression_renderers'; import { ChartTypes, WaffleVisExpressionFunctionDefinition } from '../types'; import { @@ -63,8 +64,17 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ strict: true, }, legendSize: { - types: ['number'], + types: ['string'], + default: DEFAULT_LEGEND_SIZE, help: strings.getLegendSizeArgHelp(), + options: [ + LegendSize.AUTO, + LegendSize.SMALL, + LegendSize.MEDIUM, + LegendSize.LARGE, + LegendSize.EXTRA_LARGE, + ], + strict: true, }, truncateLegend: { types: ['boolean'], @@ -129,6 +139,9 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ }; if (handlers?.inspectorAdapters?.tables) { + handlers.inspectorAdapters.tables.reset(); + handlers.inspectorAdapters.tables.allowCsvExport = true; + const logTable = prepareLogTable( context, [ diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts index 05613af4f2f33..89a242fe26de1 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts @@ -11,6 +11,7 @@ import type { PaletteOutput } from '@kbn/coloring'; import { Datatable, DatatableColumn } from '@kbn/expressions-plugin/common'; import { SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; import { ChartTypes, ExpressionValuePartitionLabels } from './expression_functions'; export enum EmptySizeRatios { @@ -52,7 +53,7 @@ interface VisCommonParams { legendPosition: Position; truncateLegend: boolean; maxLegendLines: number; - legendSize?: number; + legendSize?: LegendSize; ariaLabel?: string; } diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap index 3c48d3cb36771..0fcee477c99de 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap @@ -247,6 +247,7 @@ exports[`PartitionVisComponent should render correct structure for donut 1`] = ` legendColorPicker={[Function]} legendMaxDepth={1} legendPosition="right" + legendSize={130} onElementClick={[Function]} onRenderChange={[Function]} showLegend={true} @@ -674,6 +675,7 @@ exports[`PartitionVisComponent should render correct structure for mosaic 1`] = legendAction={[Function]} legendColorPicker={[Function]} legendPosition="right" + legendSize={130} onElementClick={[Function]} onRenderChange={[Function]} showLegend={true} @@ -1054,6 +1056,7 @@ exports[`PartitionVisComponent should render correct structure for pie 1`] = ` legendColorPicker={[Function]} legendMaxDepth={1} legendPosition="right" + legendSize={130} onElementClick={[Function]} onRenderChange={[Function]} showLegend={true} @@ -1465,6 +1468,7 @@ exports[`PartitionVisComponent should render correct structure for treemap 1`] = legendAction={[Function]} legendColorPicker={[Function]} legendPosition="right" + legendSize={130} onElementClick={[Function]} onRenderChange={[Function]} showLegend={true} @@ -1866,6 +1870,7 @@ exports[`PartitionVisComponent should render correct structure for waffle 1`] = legendColorPicker={[Function]} legendMaxDepth={1} legendPosition="right" + legendSize={130} onElementClick={[Function]} onRenderChange={[Function]} showLegend={true} diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.test.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.test.tsx index 1281fb17bd990..70c120e4fd759 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.test.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.test.tsx @@ -12,7 +12,8 @@ import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks'; import type { Datatable } from '@kbn/expressions-plugin/public'; -import { shallow, mount } from 'enzyme'; +import { shallow } from 'enzyme'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; import { findTestSubject } from '@elastic/eui/lib/test'; import { act } from 'react-dom/test-utils'; import PartitionVisComponent, { PartitionVisComponentProps } from './partition_vis_component'; @@ -24,6 +25,7 @@ import { createMockWaffleParams, } from '../mocks'; import { ChartTypes } from '../../common/types'; +import { LegendSize } from '@kbn/visualizations-plugin/common'; jest.mock('@elastic/charts', () => { const original = jest.requireActual('@elastic/charts'); @@ -143,7 +145,7 @@ describe('PartitionVisComponent', function () { }); it('renders the legend toggle component', async () => { - const component = mount(); + const component = mountWithIntl(); await actWithTimeout(async () => { await component.update(); }); @@ -154,7 +156,7 @@ describe('PartitionVisComponent', function () { }); it('hides the legend if the legend toggle is clicked', async () => { - const component = mount(); + const component = mountWithIntl(); await actWithTimeout(async () => { await component.update(); }); @@ -176,6 +178,35 @@ describe('PartitionVisComponent', function () { expect(component.find(Settings).prop('legendMaxDepth')).toBeUndefined(); }); + it('sets correct legend sizes', () => { + const component = shallow( + + ); + expect(component.find(Settings).prop('legendSize')).toEqual(80); + + component.setProps({ + visParams: { + ...visParams, + legendSize: LegendSize.AUTO, + }, + }); + expect(component.find(Settings).prop('legendSize')).toBeUndefined(); + + component.setProps({ + visParams: { + ...visParams, + legendSize: undefined, + }, + }); + expect(component.find(Settings).prop('legendSize')).toEqual(130); + }); + it('defaults on displaying the tooltip', () => { const component = shallow(); expect(component.find(Settings).prop('tooltip')).toStrictEqual({ type: TooltipType.Follow }); @@ -233,7 +264,7 @@ describe('PartitionVisComponent', function () { ], } as unknown as Datatable; const newProps = { ...wrapperProps, visData: newVisData }; - const component = mount(); + const component = mountWithIntl(); expect(findTestSubject(component, 'partitionVisEmptyValues').text()).toEqual( 'No results found' ); @@ -264,7 +295,7 @@ describe('PartitionVisComponent', function () { ], } as unknown as Datatable; const newProps = { ...wrapperProps, visData: newVisData }; - const component = mount(); + const component = mountWithIntl(); expect(findTestSubject(component, 'partitionVisNegativeValues').text()).toEqual( "Pie chart can't render with negative values." ); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx index ef6d0d1c4525c..d25126869e087 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx @@ -22,7 +22,11 @@ import { import { useEuiTheme } from '@elastic/eui'; import type { PaletteRegistry } from '@kbn/coloring'; import { LegendToggle, ChartsPluginSetup } from '@kbn/charts-plugin/public'; -import type { PersistedState } from '@kbn/visualizations-plugin/public'; +import { + DEFAULT_LEGEND_SIZE, + LegendSizeToPixels, +} from '@kbn/visualizations-plugin/common/constants'; +import { PersistedState } from '@kbn/visualizations-plugin/public'; import { getColumnByAccessor } from '@kbn/visualizations-plugin/common/utils'; import { Datatable, @@ -387,7 +391,7 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { showLegend ?? shouldShowLegend(visType, visParams.legendDisplay, bucketColumns) } legendPosition={legendPosition} - legendSize={visParams.legendSize} + legendSize={LegendSizeToPixels[visParams.legendSize ?? DEFAULT_LEGEND_SIZE]} legendMaxDepth={visParams.nestedLegend ? undefined : 1} legendColorPicker={props.uiState ? LegendColorPickerWrapper : undefined} flatLegend={flatLegend} diff --git a/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/tagcloud_function.test.ts b/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/tagcloud_function.test.ts index c9ddd7c30557b..ccc365096495b 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/tagcloud_function.test.ts +++ b/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/tagcloud_function.test.ts @@ -93,6 +93,7 @@ describe('interpreter/functions#tagcloud', () => { logDatatable: (name: string, datatable: Datatable) => { loggedTable = datatable; }, + reset: () => {}, }, }, }; diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx index 49f376a8a4aa3..96857c2ec7426 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx +++ b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx @@ -165,7 +165,7 @@ export const TagCloudChart = ({ } fireEvent({ - name: 'filterBucket', + name: 'filter', data: { data: [ { diff --git a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts index f225846687e14..3a4a1fdb813fc 100644 --- a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts @@ -10,7 +10,7 @@ import { Position } from '@elastic/charts'; import type { PaletteOutput } from '@kbn/coloring'; import { Datatable, DatatableRow } from '@kbn/expressions-plugin'; import { LayerTypes } from '../constants'; -import { DataLayerConfigResult, LensMultiTable, XYArgs } from '../types'; +import { DataLayerConfig, XYProps } from '../types'; export const mockPaletteOutput: PaletteOutput = { type: 'palette', @@ -46,9 +46,9 @@ export const createSampleDatatableWithRows = (rows: DatatableRow[]): Datatable = rows, }); -export const sampleLayer: DataLayerConfigResult = { - type: 'dataLayer', +export const sampleLayer: DataLayerConfig = { layerId: 'first', + type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'c', @@ -59,9 +59,12 @@ export const sampleLayer: DataLayerConfigResult = { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: createSampleDatatableWithRows([]), }; -export const createArgsWithLayers = (layers: DataLayerConfigResult[] = [sampleLayer]): XYArgs => ({ +export const createArgsWithLayers = ( + layers: DataLayerConfig | DataLayerConfig[] = sampleLayer +): XYProps => ({ xTitle: '', yTitle: '', yRightTitle: '', @@ -104,25 +107,17 @@ export const createArgsWithLayers = (layers: DataLayerConfigResult[] = [sampleLa mode: 'full', type: 'axisExtentConfig', }, - layers, + layers: Array.isArray(layers) ? layers : [layers], }); export function sampleArgs() { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([ - { a: 1, b: 2, c: 'I', d: 'Foo' }, - { a: 1, b: 5, c: 'J', d: 'Bar' }, - ]), - }, - dateRange: { - fromDate: new Date('2019-01-02T05:00:00.000Z'), - toDate: new Date('2019-01-03T05:00:00.000Z'), - }, - }; - - const args: XYArgs = createArgsWithLayers(); + const data = createSampleDatatableWithRows([ + { a: 1, b: 2, c: 'I', d: 'Foo' }, + { a: 1, b: 5, c: 'J', d: 'Bar' }, + ]); - return { data, args }; + return { + data, + args: createArgsWithLayers({ ...sampleLayer, table: data }), + }; } diff --git a/src/plugins/chart_expressions/expression_xy/common/constants.ts b/src/plugins/chart_expressions/expression_xy/common/constants.ts index bf1e43b205843..68ac2963c9646 100644 --- a/src/plugins/chart_expressions/expression_xy/common/constants.ts +++ b/src/plugins/chart_expressions/expression_xy/common/constants.ts @@ -7,16 +7,20 @@ */ export const XY_VIS = 'xyVis'; +export const LAYERED_XY_VIS = 'layeredXyVis'; export const Y_CONFIG = 'yConfig'; -export const MULTITABLE = 'lens_multitable'; +export const EXTENDED_Y_CONFIG = 'extendedYConfig'; export const DATA_LAYER = 'dataLayer'; +export const EXTENDED_DATA_LAYER = 'extendedDataLayer'; export const LEGEND_CONFIG = 'legendConfig'; export const XY_VIS_RENDERER = 'xyVis'; export const GRID_LINES_CONFIG = 'gridlinesConfig'; export const ANNOTATION_LAYER = 'annotationLayer'; +export const EXTENDED_ANNOTATION_LAYER = 'extendedAnnotationLayer'; export const TICK_LABELS_CONFIG = 'tickLabelsConfig'; export const AXIS_EXTENT_CONFIG = 'axisExtentConfig'; export const REFERENCE_LINE_LAYER = 'referenceLineLayer'; +export const EXTENDED_REFERENCE_LINE_LAYER = 'extendedReferenceLineLayer'; export const LABELS_ORIENTATION_CONFIG = 'labelsOrientationConfig'; export const AXIS_TITLES_VISIBILITY_CONFIG = 'axisTitlesVisibilityConfig'; @@ -106,6 +110,23 @@ export const XYCurveTypes = { export const ValueLabelModes = { HIDE: 'hide', - INSIDE: 'inside', - OUTSIDE: 'outside', + SHOW: 'show', +} as const; + +export const AvailableReferenceLineIcons = { + EMPTY: 'empty', + ASTERISK: 'asterisk', + ALERT: 'alert', + BELL: 'bell', + BOLT: 'bolt', + BUG: 'bug', + CIRCLE: 'circle', + EDITOR_COMMENT: 'editorComment', + FLAG: 'flag', + HEART: 'heart', + MAP_MARKER: 'mapMarker', + PIN_FILLED: 'pinFilled', + STAR_EMPTY: 'starEmpty', + TAG: 'tag', + TRIANGLE: 'triangle', } as const; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts new file mode 100644 index 0000000000000..6174b9d40e452 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.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 and the Server Side Public License, v 1; you may 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 { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; +import { LayerTypes, ANNOTATION_LAYER } from '../constants'; +import { AnnotationLayerArgs, AnnotationLayerConfigResult } from '../types'; +import { strings } from '../i18n'; + +export function annotationLayerFunction(): ExpressionFunctionDefinition< + typeof ANNOTATION_LAYER, + Datatable, + AnnotationLayerArgs, + AnnotationLayerConfigResult +> { + return { + name: ANNOTATION_LAYER, + aliases: [], + type: ANNOTATION_LAYER, + inputTypes: ['datatable'], + help: strings.getAnnotationLayerFnHelp(), + args: { + hide: { + types: ['boolean'], + default: false, + help: strings.getAnnotationLayerHideHelp(), + }, + annotations: { + types: ['manual_point_event_annotation', 'manual_range_event_annotation'], + help: strings.getAnnotationLayerAnnotationsHelp(), + multi: true, + }, + }, + fn: (input, args) => { + return { + type: ANNOTATION_LAYER, + ...args, + annotations: args.annotations ?? [], + layerType: LayerTypes.ANNOTATIONS, + }; + }, + }; +} diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer_config.ts deleted file mode 100644 index 9ec3a43e1d710..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer_config.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; -import { LayerTypes, ANNOTATION_LAYER } from '../constants'; -import { AnnotationLayerArgs, AnnotationLayerConfigResult } from '../types'; - -export function annotationLayerConfigFunction(): ExpressionFunctionDefinition< - typeof ANNOTATION_LAYER, - null, - AnnotationLayerArgs, - AnnotationLayerConfigResult -> { - return { - name: ANNOTATION_LAYER, - aliases: [], - type: ANNOTATION_LAYER, - inputTypes: ['null'], - help: 'Annotation layer in lens', - args: { - layerId: { - types: ['string'], - help: '', - }, - hide: { - types: ['boolean'], - default: false, - help: 'Show details', - }, - annotations: { - types: ['manual_event_annotation'], - help: '', - multi: true, - }, - }, - fn: (input, args) => { - return { - type: ANNOTATION_LAYER, - ...args, - layerType: LayerTypes.ANNOTATIONS, - }; - }, - }; -} diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_extent_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_extent_config.ts index e65550c7aeeef..c1a4070225a64 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_extent_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_extent_config.ts @@ -11,6 +11,13 @@ import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/commo import { AxisExtentConfig, AxisExtentConfigResult } from '../types'; import { AxisExtentModes, AXIS_EXTENT_CONFIG } from '../constants'; +const errors = { + upperBoundLowerOrEqualToLowerBoundError: () => + i18n.translate('expressionXY.reusable.function.axisExtentConfig.errors.emptyUpperBound', { + defaultMessage: 'Upper bound should be greater than lower bound, if custom mode is enabled.', + }), +}; + export const axisExtentConfigFunction: ExpressionFunctionDefinition< typeof AXIS_EXTENT_CONFIG, null, @@ -27,10 +34,12 @@ export const axisExtentConfigFunction: ExpressionFunctionDefinition< args: { mode: { types: ['string'], - options: [...Object.values(AxisExtentModes)], help: i18n.translate('expressionXY.axisExtentConfig.extentMode.help', { defaultMessage: 'The extent mode', }), + options: [...Object.values(AxisExtentModes)], + strict: true, + default: AxisExtentModes.FULL, }, lowerBound: { types: ['number'], @@ -46,6 +55,16 @@ export const axisExtentConfigFunction: ExpressionFunctionDefinition< }, }, fn(input, args) { + if (args.mode === AxisExtentModes.CUSTOM) { + if ( + args.lowerBound !== undefined && + args.upperBound !== undefined && + args.lowerBound >= args.upperBound + ) { + throw new Error(errors.upperBoundLowerOrEqualToLowerBoundError()); + } + } + return { type: AXIS_EXTENT_CONFIG, ...args, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_data_layer_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_data_layer_args.ts new file mode 100644 index 0000000000000..5a1dad533c084 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_data_layer_args.ts @@ -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 { ArgumentType } from '@kbn/expressions-plugin/common'; +import { SeriesTypes, XScaleTypes, YScaleTypes, Y_CONFIG } from '../constants'; +import { strings } from '../i18n'; +import { DataLayerArgs, ExtendedDataLayerArgs } from '../types'; + +type CommonDataLayerArgs = ExtendedDataLayerArgs | DataLayerArgs; +type CommonDataLayerFnArgs = { + [key in keyof CommonDataLayerArgs]: ArgumentType; +}; + +export const commonDataLayerArgs: CommonDataLayerFnArgs = { + hide: { + types: ['boolean'], + default: false, + help: strings.getHideHelp(), + }, + xAccessor: { + types: ['string'], + help: strings.getXAccessorHelp(), + }, + seriesType: { + types: ['string'], + options: [...Object.values(SeriesTypes)], + help: strings.getSeriesTypeHelp(), + required: true, + strict: true, + }, + xScaleType: { + options: [...Object.values(XScaleTypes)], + help: strings.getXScaleTypeHelp(), + default: XScaleTypes.ORDINAL, + strict: true, + }, + isHistogram: { + types: ['boolean'], + default: false, + help: strings.getIsHistogramHelp(), + }, + yScaleType: { + options: [...Object.values(YScaleTypes)], + help: strings.getYScaleTypeHelp(), + default: YScaleTypes.LINEAR, + strict: true, + }, + splitAccessor: { + types: ['string'], + help: strings.getSplitAccessorHelp(), + }, + accessors: { + types: ['string'], + help: strings.getAccessorsHelp(), + multi: true, + }, + yConfig: { + types: [Y_CONFIG], + help: strings.getYConfigHelp(), + multi: true, + }, + columnToLabel: { + types: ['string'], + help: strings.getColumnToLabelHelp(), + }, + palette: { + types: ['palette', 'system_palette'], + help: strings.getPaletteHelp(), + default: '{palette}', + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_reference_line_layer_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_reference_line_layer_args.ts new file mode 100644 index 0000000000000..f338e08a88940 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_reference_line_layer_args.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 { EXTENDED_Y_CONFIG } from '../constants'; +import { strings } from '../i18n'; +import { ReferenceLineLayerFn, ExtendedReferenceLineLayerFn } from '../types'; + +type CommonReferenceLineLayerFn = ReferenceLineLayerFn | ExtendedReferenceLineLayerFn; + +export const commonReferenceLineLayerArgs: CommonReferenceLineLayerFn['args'] = { + accessors: { + types: ['string'], + help: strings.getRLAccessorsHelp(), + multi: true, + }, + yConfig: { + types: [EXTENDED_Y_CONFIG], + help: strings.getRLYConfigHelp(), + multi: true, + }, + columnToLabel: { + types: ['string'], + help: strings.getColumnToLabelHelp(), + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts new file mode 100644 index 0000000000000..f80d814571076 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + AXIS_EXTENT_CONFIG, + AXIS_TITLES_VISIBILITY_CONFIG, + EndValues, + FittingFunctions, + GRID_LINES_CONFIG, + LABELS_ORIENTATION_CONFIG, + LEGEND_CONFIG, + TICK_LABELS_CONFIG, + ValueLabelModes, + XYCurveTypes, +} from '../constants'; +import { strings } from '../i18n'; +import { LayeredXyVisFn, XyVisFn } from '../types'; + +type CommonXYFn = XyVisFn | LayeredXyVisFn; + +export const commonXYArgs: CommonXYFn['args'] = { + xTitle: { + types: ['string'], + help: strings.getXTitleHelp(), + }, + yTitle: { + types: ['string'], + help: strings.getYTitleHelp(), + }, + yRightTitle: { + types: ['string'], + help: strings.getYRightTitleHelp(), + }, + yLeftExtent: { + types: [AXIS_EXTENT_CONFIG], + help: strings.getYLeftExtentHelp(), + default: `{${AXIS_EXTENT_CONFIG}}`, + }, + yRightExtent: { + types: [AXIS_EXTENT_CONFIG], + help: strings.getYRightExtentHelp(), + default: `{${AXIS_EXTENT_CONFIG}}`, + }, + legend: { + types: [LEGEND_CONFIG], + help: strings.getLegendHelp(), + default: `{${LEGEND_CONFIG}}`, + }, + fittingFunction: { + types: ['string'], + options: [...Object.values(FittingFunctions)], + help: strings.getFittingFunctionHelp(), + strict: true, + }, + endValue: { + types: ['string'], + options: [...Object.values(EndValues)], + help: strings.getEndValueHelp(), + strict: true, + }, + emphasizeFitting: { + types: ['boolean'], + default: false, + help: '', + }, + valueLabels: { + types: ['string'], + options: [...Object.values(ValueLabelModes)], + help: strings.getValueLabelsHelp(), + strict: true, + default: ValueLabelModes.HIDE, + }, + tickLabelsVisibilitySettings: { + types: [TICK_LABELS_CONFIG], + help: strings.getTickLabelsVisibilitySettingsHelp(), + }, + labelsOrientation: { + types: [LABELS_ORIENTATION_CONFIG], + help: strings.getLabelsOrientationHelp(), + }, + gridlinesVisibilitySettings: { + types: [GRID_LINES_CONFIG], + help: strings.getGridlinesVisibilitySettingsHelp(), + }, + axisTitlesVisibilitySettings: { + types: [AXIS_TITLES_VISIBILITY_CONFIG], + help: strings.getAxisTitlesVisibilitySettingsHelp(), + }, + curveType: { + types: ['string'], + options: [...Object.values(XYCurveTypes)], + help: strings.getCurveTypeHelp(), + strict: true, + }, + fillOpacity: { + types: ['number'], + help: strings.getFillOpacityHelp(), + }, + hideEndzones: { + types: ['boolean'], + default: false, + help: strings.getHideEndzonesHelp(), + }, + valuesInLegend: { + types: ['boolean'], + default: false, + help: strings.getValuesInLegendHelp(), + }, + ariaLabel: { + types: ['string'], + help: strings.getAriaLabelHelp(), + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_y_config_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_y_config_args.ts new file mode 100644 index 0000000000000..76ac6ba2a1a97 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_y_config_args.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 { YAxisModes } from '../constants'; +import { strings } from '../i18n'; +import { YConfigFn, ExtendedYConfigFn } from '../types'; + +type CommonYConfigFn = YConfigFn | ExtendedYConfigFn; + +export const commonYConfigArgs: CommonYConfigFn['args'] = { + forAccessor: { + types: ['string'], + help: strings.getForAccessorHelp(), + }, + axisMode: { + types: ['string'], + options: [...Object.values(YAxisModes)], + help: strings.getAxisModeHelp(), + strict: true, + }, + color: { + types: ['string'], + help: strings.getColorHelp(), + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.test.ts deleted file mode 100644 index 7f8bf30d956ff..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { DataLayerArgs } from '../types'; -import { dataLayerConfigFunction } from '.'; -import { createMockExecutionContext } from '@kbn/expressions-plugin/common/mocks'; -import { mockPaletteOutput } from '../__mocks__'; -import { LayerTypes } from '../constants'; - -describe('dataLayerConfig', () => { - test('produces the correct arguments', () => { - const args: DataLayerArgs = { - layerId: 'first', - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'd', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }; - - const result = dataLayerConfigFunction.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ type: 'dataLayer', layerType: LayerTypes.DATA, ...args }); - }); -}); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts deleted file mode 100644 index d09b8481a6842..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; -import { DataLayerArgs, DataLayerConfigResult } from '../types'; -import { - DATA_LAYER, - LayerTypes, - SeriesTypes, - XScaleTypes, - YScaleTypes, - Y_CONFIG, -} from '../constants'; - -export const dataLayerConfigFunction: ExpressionFunctionDefinition< - typeof DATA_LAYER, - null, - DataLayerArgs, - DataLayerConfigResult -> = { - name: DATA_LAYER, - aliases: [], - type: DATA_LAYER, - help: i18n.translate('expressionXY.dataLayer.help', { - defaultMessage: `Configure a layer in the xy chart`, - }), - inputTypes: ['null'], - args: { - hide: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.dataLayer.hide.help', { - defaultMessage: 'Show / hide axis', - }), - }, - layerId: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.layerId.help', { - defaultMessage: 'Layer ID', - }), - }, - xAccessor: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.xAccessor.help', { - defaultMessage: 'X-axis', - }), - }, - seriesType: { - types: ['string'], - options: [...Object.values(SeriesTypes)], - help: i18n.translate('expressionXY.dataLayer.seriesType.help', { - defaultMessage: 'The type of chart to display.', - }), - }, - xScaleType: { - options: [...Object.values(XScaleTypes)], - help: i18n.translate('expressionXY.dataLayer.xScaleType.help', { - defaultMessage: 'The scale type of the x axis', - }), - default: XScaleTypes.ORDINAL, - }, - isHistogram: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.dataLayer.isHistogram.help', { - defaultMessage: 'Whether to layout the chart as a histogram', - }), - }, - yScaleType: { - options: [...Object.values(YScaleTypes)], - help: i18n.translate('expressionXY.dataLayer.yScaleType.help', { - defaultMessage: 'The scale type of the y axes', - }), - default: YScaleTypes.LINEAR, - }, - splitAccessor: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.splitAccessor.help', { - defaultMessage: 'The column to split by', - }), - }, - accessors: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.accessors.help', { - defaultMessage: 'The columns to display on the y axis.', - }), - multi: true, - }, - yConfig: { - types: [Y_CONFIG], - help: i18n.translate('expressionXY.dataLayer.yConfig.help', { - defaultMessage: 'Additional configuration for y axes', - }), - multi: true, - }, - columnToLabel: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.columnToLabel.help', { - defaultMessage: 'JSON key-value pairs of column ID to label', - }), - }, - palette: { - types: ['palette', 'system_palette'], - help: i18n.translate('expressionXY.dataLayer.palette.help', { - defaultMessage: 'Palette', - }), - default: '{palette}', - }, - }, - fn(input, args) { - return { - type: DATA_LAYER, - ...args, - layerType: LayerTypes.DATA, - }; - }, -}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts new file mode 100644 index 0000000000000..539c11854355c --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.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 and the Server Side Public License, v 1; you may 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 { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; +import { LayerTypes, EXTENDED_ANNOTATION_LAYER } from '../constants'; +import { ExtendedAnnotationLayerConfigResult, ExtendedAnnotationLayerArgs } from '../types'; +import { strings } from '../i18n'; + +export function extendedAnnotationLayerFunction(): ExpressionFunctionDefinition< + typeof EXTENDED_ANNOTATION_LAYER, + Datatable, + ExtendedAnnotationLayerArgs, + ExtendedAnnotationLayerConfigResult +> { + return { + name: EXTENDED_ANNOTATION_LAYER, + aliases: [], + type: EXTENDED_ANNOTATION_LAYER, + inputTypes: ['datatable'], + help: strings.getAnnotationLayerFnHelp(), + args: { + hide: { + types: ['boolean'], + default: false, + help: strings.getAnnotationLayerHideHelp(), + }, + annotations: { + types: ['manual_point_event_annotation', 'manual_range_event_annotation'], + help: strings.getAnnotationLayerAnnotationsHelp(), + multi: true, + }, + layerId: { + types: ['string'], + help: strings.getLayerIdHelp(), + }, + }, + fn: (input, args) => { + return { + type: EXTENDED_ANNOTATION_LAYER, + ...args, + annotations: args.annotations ?? [], + layerType: LayerTypes.ANNOTATIONS, + }; + }, + }; +} diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts new file mode 100644 index 0000000000000..84c1213fc069d --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ExtendedDataLayerFn } from '../types'; +import { EXTENDED_DATA_LAYER, LayerTypes } from '../constants'; +import { strings } from '../i18n'; +import { commonDataLayerArgs } from './common_data_layer_args'; + +export const extendedDataLayerFunction: ExtendedDataLayerFn = { + name: EXTENDED_DATA_LAYER, + aliases: [], + type: EXTENDED_DATA_LAYER, + help: strings.getDataLayerFnHelp(), + inputTypes: ['datatable'], + args: { + ...commonDataLayerArgs, + table: { + types: ['datatable'], + help: strings.getTableHelp(), + }, + layerId: { + types: ['string'], + help: strings.getLayerIdHelp(), + }, + }, + fn(input, args) { + return { + type: EXTENDED_DATA_LAYER, + ...args, + accessors: args.accessors ?? [], + layerType: LayerTypes.DATA, + table: args.table ?? input, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts new file mode 100644 index 0000000000000..4f75838bea114 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { LayerTypes, EXTENDED_REFERENCE_LINE_LAYER } from '../constants'; +import { ExtendedReferenceLineLayerFn } from '../types'; +import { strings } from '../i18n'; +import { commonReferenceLineLayerArgs } from './common_reference_line_layer_args'; + +export const extendedReferenceLineLayerFunction: ExtendedReferenceLineLayerFn = { + name: EXTENDED_REFERENCE_LINE_LAYER, + aliases: [], + type: EXTENDED_REFERENCE_LINE_LAYER, + help: strings.getRLHelp(), + inputTypes: ['datatable'], + args: { + ...commonReferenceLineLayerArgs, + table: { + types: ['datatable'], + help: strings.getTableHelp(), + }, + layerId: { + types: ['string'], + help: strings.getLayerIdHelp(), + }, + }, + fn(input, args) { + return { + type: EXTENDED_REFERENCE_LINE_LAYER, + ...args, + accessors: args.accessors ?? [], + layerType: LayerTypes.REFERENCELINE, + table: args.table ?? input, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts new file mode 100644 index 0000000000000..606cdd84ac710 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { + AvailableReferenceLineIcons, + EXTENDED_Y_CONFIG, + FillStyles, + IconPositions, + LineStyles, +} from '../constants'; +import { strings } from '../i18n'; +import { ExtendedYConfigFn } from '../types'; +import { commonYConfigArgs } from './common_y_config_args'; + +export const extendedYAxisConfigFunction: ExtendedYConfigFn = { + name: EXTENDED_Y_CONFIG, + aliases: [], + type: EXTENDED_Y_CONFIG, + help: strings.getYConfigFnHelp(), + inputTypes: ['null'], + args: { + ...commonYConfigArgs, + lineStyle: { + types: ['string'], + options: [...Object.values(LineStyles)], + help: i18n.translate('expressionXY.yConfig.lineStyle.help', { + defaultMessage: 'The style of the reference line', + }), + strict: true, + }, + lineWidth: { + types: ['number'], + help: i18n.translate('expressionXY.yConfig.lineWidth.help', { + defaultMessage: 'The width of the reference line', + }), + }, + icon: { + types: ['string'], + help: i18n.translate('expressionXY.yConfig.icon.help', { + defaultMessage: 'An optional icon used for reference lines', + }), + options: [...Object.values(AvailableReferenceLineIcons)], + strict: true, + }, + iconPosition: { + types: ['string'], + options: [...Object.values(IconPositions)], + help: i18n.translate('expressionXY.yConfig.iconPosition.help', { + defaultMessage: 'The placement of the icon for the reference line', + }), + strict: true, + }, + textVisibility: { + types: ['boolean'], + help: i18n.translate('expressionXY.yConfig.textVisibility.help', { + defaultMessage: 'Visibility of the label on the reference line', + }), + }, + fill: { + types: ['string'], + options: [...Object.values(FillStyles)], + help: i18n.translate('expressionXY.yConfig.fill.help', { + defaultMessage: 'Fill', + }), + strict: true, + }, + }, + fn(input, args) { + return { + type: EXTENDED_Y_CONFIG, + ...args, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts index 5c7e013a91332..30a76217b5c0e 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts @@ -7,13 +7,17 @@ */ export * from './xy_vis'; +export * from './layered_xy_vis'; export * from './legend_config'; -export * from './annotation_layer_config'; +export * from './annotation_layer'; +export * from './extended_annotation_layer'; export * from './y_axis_config'; -export * from './data_layer_config'; +export * from './extended_y_axis_config'; +export * from './extended_data_layer'; export * from './grid_lines_config'; export * from './axis_extent_config'; export * from './tick_labels_config'; export * from './labels_orientation_config'; -export * from './reference_line_layer_config'; +export * from './reference_line_layer'; +export * from './extended_reference_line_layer'; export * from './axis_titles_visibility_config'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts new file mode 100644 index 0000000000000..6b926e1ceff05 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { LayeredXyVisFn } from '../types'; +import { + EXTENDED_DATA_LAYER, + EXTENDED_REFERENCE_LINE_LAYER, + LAYERED_XY_VIS, + EXTENDED_ANNOTATION_LAYER, +} from '../constants'; +import { commonXYArgs } from './common_xy_args'; +import { strings } from '../i18n'; + +export const layeredXyVisFunction: LayeredXyVisFn = { + name: LAYERED_XY_VIS, + type: 'render', + inputTypes: ['datatable'], + help: strings.getXYHelp(), + args: { + ...commonXYArgs, + layers: { + types: [EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER, EXTENDED_ANNOTATION_LAYER], + help: i18n.translate('expressionXY.layeredXyVis.layers.help', { + defaultMessage: 'Layers of visual series', + }), + multi: true, + }, + }, + async fn(data, args, handlers) { + const { layeredXyVisFn } = await import('./layered_xy_vis_fn'); + return await layeredXyVisFn(data, args, handlers); + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis_fn.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis_fn.ts new file mode 100644 index 0000000000000..4b7de0eba3166 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis_fn.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 { XY_VIS_RENDERER } from '../constants'; +import { appendLayerIds } from '../helpers'; +import { LayeredXyVisFn } from '../types'; +import { logDatatables } from '../utils'; + +export const layeredXyVisFn: LayeredXyVisFn['fn'] = async (data, args, handlers) => { + const layers = appendLayerIds(args.layers ?? [], 'layers'); + + logDatatables(layers, handlers); + + return { + type: 'render', + as: XY_VIS_RENDERER, + value: { + args: { + ...args, + layers, + ariaLabel: + args.ariaLabel ?? + (handlers.variables?.embeddableTitle as string) ?? + handlers.getExecutionContext?.()?.description, + }, + }, + }; +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.test.ts index 9d58903e93c62..48e6d1c956acb 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.test.ts @@ -12,9 +12,9 @@ import { LegendConfig } from '../types'; import { legendConfigFunction } from './legend_config'; describe('legendConfigFunction', () => { - test('produces the correct arguments', () => { + test('produces the correct arguments', async () => { const args: LegendConfig = { isVisible: true, position: Position.Left }; - const result = legendConfigFunction.fn(null, args, createMockExecutionContext()); + const result = await legendConfigFunction.fn(null, args, createMockExecutionContext()); expect(result).toEqual({ type: 'legendConfig', ...args }); }); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts index 65f8a725518a3..ddb46d5e55f13 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts @@ -8,16 +8,11 @@ import { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; +import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/common/constants'; import { LEGEND_CONFIG } from '../constants'; -import { LegendConfig, LegendConfigResult } from '../types'; +import { LegendConfigFn } from '../types'; -export const legendConfigFunction: ExpressionFunctionDefinition< - typeof LEGEND_CONFIG, - null, - LegendConfig, - LegendConfigResult -> = { +export const legendConfigFunction: LegendConfigFn = { name: LEGEND_CONFIG, aliases: [], type: LEGEND_CONFIG, @@ -31,6 +26,7 @@ export const legendConfigFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.legendConfig.isVisible.help', { defaultMessage: 'Specifies whether or not the legend is visible.', }), + default: true, }, position: { types: ['string'], @@ -38,6 +34,7 @@ export const legendConfigFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.legendConfig.position.help', { defaultMessage: 'Specifies the legend position.', }), + strict: true, }, showSingleSeries: { types: ['boolean'], @@ -58,6 +55,7 @@ export const legendConfigFunction: ExpressionFunctionDefinition< defaultMessage: 'Specifies the horizontal alignment of the legend when it is displayed inside chart.', }), + strict: true, }, verticalAlignment: { types: ['string'], @@ -66,6 +64,7 @@ export const legendConfigFunction: ExpressionFunctionDefinition< defaultMessage: 'Specifies the vertical alignment of the legend when it is displayed inside chart.', }), + strict: true, }, floatingColumns: { types: ['number'], @@ -87,16 +86,23 @@ export const legendConfigFunction: ExpressionFunctionDefinition< }), }, legendSize: { - types: ['number'], + types: ['string'], + default: DEFAULT_LEGEND_SIZE, help: i18n.translate('expressionXY.legendConfig.legendSize.help', { - defaultMessage: 'Specifies the legend size in pixels.', + defaultMessage: 'Specifies the legend size.', }), + options: [ + LegendSize.AUTO, + LegendSize.SMALL, + LegendSize.MEDIUM, + LegendSize.LARGE, + LegendSize.EXTRA_LARGE, + ], + strict: true, }, }, - fn(input, args) { - return { - type: LEGEND_CONFIG, - ...args, - }; + async fn(input, args, handlers) { + const { legendConfigFn } = await import('./legend_config_fn'); + return await legendConfigFn(input, args, handlers); }, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config_fn.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config_fn.ts new file mode 100644 index 0000000000000..35df125ae230f --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config_fn.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { LEGEND_CONFIG } from '../constants'; +import { LegendConfigFn } from '../types'; + +const errors = { + positionUsageWithIsInsideError: () => + i18n.translate( + 'expressionXY.reusable.function.legendConfig.errors.positionUsageWithIsInsideError', + { + defaultMessage: + '`position` argument is not applied if `isInside = true`. Please, use `horizontalAlignment` and `verticalAlignment` arguments instead.', + } + ), + alignmentUsageWithFalsyIsInsideError: () => + i18n.translate( + 'expressionXY.reusable.function.legendConfig.errors.alignmentUsageWithFalsyIsInsideError', + { + defaultMessage: + '`horizontalAlignment` and `verticalAlignment` arguments are not applied if `isInside = false`. Please, use the `position` argument instead.', + } + ), + floatingColumnsWithFalsyIsInsideError: () => + i18n.translate( + 'expressionXY.reusable.function.legendConfig.errors.floatingColumnsWithFalsyIsInsideError', + { + defaultMessage: '`floatingColumns` arguments are not applied if `isInside = false`.', + } + ), + legendSizeWithFalsyIsInsideError: () => + i18n.translate( + 'expressionXY.reusable.function.legendConfig.errors.legendSizeWithFalsyIsInsideError', + { + defaultMessage: '`legendSize` argument is not applied if `isInside = false`.', + } + ), +}; + +export const legendConfigFn: LegendConfigFn['fn'] = async (data, args) => { + if (args.isInside) { + if (args.position) { + throw new Error(errors.positionUsageWithIsInsideError()); + } + + if (args.legendSize !== undefined) { + throw new Error(errors.legendSizeWithFalsyIsInsideError()); + } + } + + if (!args.isInside) { + if (args.verticalAlignment || args.horizontalAlignment) { + throw new Error(errors.alignmentUsageWithFalsyIsInsideError()); + } + + if (args.floatingColumns !== undefined) { + throw new Error(errors.floatingColumnsWithFalsyIsInsideError()); + } + } + + return { type: LEGEND_CONFIG, ...args }; +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts new file mode 100644 index 0000000000000..9c6e27c958530 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.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 { LayerTypes, REFERENCE_LINE_LAYER } from '../constants'; +import { ReferenceLineLayerFn } from '../types'; +import { strings } from '../i18n'; +import { commonReferenceLineLayerArgs } from './common_reference_line_layer_args'; + +export const referenceLineLayerFunction: ReferenceLineLayerFn = { + name: REFERENCE_LINE_LAYER, + aliases: [], + type: REFERENCE_LINE_LAYER, + help: strings.getRLHelp(), + inputTypes: ['datatable'], + args: { ...commonReferenceLineLayerArgs }, + fn(table, args) { + return { + type: REFERENCE_LINE_LAYER, + ...args, + accessors: args.accessors ?? [], + layerType: LayerTypes.REFERENCELINE, + table, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts deleted file mode 100644 index 46f6e7671c0ab..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; -import { LayerTypes, REFERENCE_LINE_LAYER, Y_CONFIG } from '../constants'; -import { ReferenceLineLayerArgs, ReferenceLineLayerConfigResult } from '../types'; - -export const referenceLineLayerConfigFunction: ExpressionFunctionDefinition< - typeof REFERENCE_LINE_LAYER, - null, - ReferenceLineLayerArgs, - ReferenceLineLayerConfigResult -> = { - name: REFERENCE_LINE_LAYER, - aliases: [], - type: REFERENCE_LINE_LAYER, - help: i18n.translate('expressionXY.referenceLineLayer.help', { - defaultMessage: `Configure a reference line in the xy chart`, - }), - inputTypes: ['null'], - args: { - layerId: { - types: ['string'], - help: i18n.translate('expressionXY.referenceLineLayer.layerId.help', { - defaultMessage: `Layer ID`, - }), - }, - accessors: { - types: ['string'], - help: i18n.translate('expressionXY.referenceLineLayer.accessors.help', { - defaultMessage: 'The columns to display on the y axis.', - }), - multi: true, - }, - yConfig: { - types: [Y_CONFIG], - help: i18n.translate('expressionXY.referenceLineLayer.yConfig.help', { - defaultMessage: 'Additional configuration for y axes', - }), - multi: true, - }, - columnToLabel: { - types: ['string'], - help: i18n.translate('expressionXY.referenceLineLayer.columnToLabel.help', { - defaultMessage: 'JSON key-value pairs of column ID to label', - }), - }, - }, - fn(input, args) { - return { - type: REFERENCE_LINE_LAYER, - ...args, - layerType: LayerTypes.REFERENCELINE, - }; - }, -}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts new file mode 100644 index 0000000000000..55d7cb12382c0 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { AxisExtentModes, ValueLabelModes } from '../constants'; +import { + AxisExtentConfigResult, + DataLayerConfigResult, + ValueLabelMode, + CommonXYDataLayerConfig, +} from '../types'; + +const errors = { + extendBoundsAreInvalidError: () => + i18n.translate('expressionXY.reusable.function.xyVis.errors.extendBoundsAreInvalidError', { + defaultMessage: + 'For area and bar modes, and custom extent mode, the lower bound should be less or greater than 0 and the upper bound - be greater or equal than 0', + }), + notUsedFillOpacityError: () => + i18n.translate('expressionXY.reusable.function.xyVis.errors.notUsedFillOpacityError', { + defaultMessage: '`fillOpacity` argument is applicable only for area charts.', + }), + valueLabelsForNotBarsOrHistogramBarsChartsError: () => + i18n.translate( + 'expressionXY.reusable.function.xyVis.errors.valueLabelsForNotBarsOrHistogramBarsChartsError', + { + defaultMessage: + '`valueLabels` argument is applicable only for bar charts, which are not histograms.', + } + ), + dataBoundsForNotLineChartError: () => + i18n.translate('expressionXY.reusable.function.xyVis.errors.dataBoundsForNotLineChartError', { + defaultMessage: 'Only line charts can be fit to the data bounds', + }), +}; + +export const hasBarLayer = (layers: Array) => + layers.filter(({ seriesType }) => seriesType.includes('bar')).length > 0; + +export const hasAreaLayer = (layers: Array) => + layers.filter(({ seriesType }) => seriesType.includes('area')).length > 0; + +export const hasHistogramBarLayer = ( + layers: Array +) => + layers.filter(({ seriesType, isHistogram }) => seriesType.includes('bar') && isHistogram).length > + 0; + +export const isValidExtentWithCustomMode = (extent: AxisExtentConfigResult) => { + const isValidLowerBound = + extent.lowerBound === undefined || (extent.lowerBound !== undefined && extent.lowerBound <= 0); + const isValidUpperBound = + extent.upperBound === undefined || (extent.upperBound !== undefined && extent.upperBound >= 0); + + return isValidLowerBound && isValidUpperBound; +}; + +export const validateExtentForDataBounds = ( + extent: AxisExtentConfigResult, + layers: Array +) => { + const lineSeries = layers.filter(({ seriesType }) => seriesType.includes('line')); + if (!lineSeries.length && extent.mode === AxisExtentModes.DATA_BOUNDS) { + throw new Error(errors.dataBoundsForNotLineChartError()); + } +}; + +export const validateExtent = ( + extent: AxisExtentConfigResult, + hasBarOrArea: boolean, + dataLayers: Array +) => { + if ( + extent.mode === AxisExtentModes.CUSTOM && + hasBarOrArea && + !isValidExtentWithCustomMode(extent) + ) { + throw new Error(errors.extendBoundsAreInvalidError()); + } + + validateExtentForDataBounds(extent, dataLayers); +}; + +export const validateFillOpacity = (fillOpacity: number | undefined, hasArea: boolean) => { + if (fillOpacity !== undefined && !hasArea) { + throw new Error(errors.notUsedFillOpacityError()); + } +}; + +export const validateValueLabels = ( + valueLabels: ValueLabelMode, + hasBar: boolean, + hasNotHistogramBars: boolean +) => { + if ((!hasBar || !hasNotHistogramBars) && valueLabels !== ValueLabelModes.HIDE) { + throw new Error(errors.valueLabelsForNotBarsOrHistogramBarsChartsError()); + } +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts index 27e68f0a49891..871135dd45bcb 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts @@ -8,14 +8,29 @@ import { xyVisFunction } from '.'; import { createMockExecutionContext } from '@kbn/expressions-plugin/common/mocks'; -import { sampleArgs } from '../__mocks__'; +import { sampleArgs, sampleLayer } from '../__mocks__'; import { XY_VIS } from '../constants'; describe('xyVis', () => { - test('it renders with the specified data and args', () => { + test('it renders with the specified data and args', async () => { const { data, args } = sampleArgs(); - const result = xyVisFunction.fn(data, args, createMockExecutionContext()); + const { layers, ...rest } = args; + const { layerId, layerType, table, type, ...restLayerArgs } = sampleLayer; + const result = await xyVisFunction.fn( + data, + { ...rest, ...restLayerArgs, referenceLineLayers: [], annotationLayers: [] }, + createMockExecutionContext() + ); - expect(result).toEqual({ type: 'render', as: XY_VIS, value: { data, args } }); + expect(result).toEqual({ + type: 'render', + as: XY_VIS, + value: { + args: { + ...rest, + layers: [{ layerType, table: data, layerId: 'dataLayers-0', type, ...restLayerArgs }], + }, + }, + }); }); }); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index 227b7553d6414..e8a5858d3ed26 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -6,243 +6,33 @@ * Side Public License, v 1. */ -import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin'; -import { prepareLogTable } from '@kbn/visualizations-plugin/common/utils'; -import { LensMultiTable, XYArgs, XYRender } from '../types'; -import { - XY_VIS, - DATA_LAYER, - MULTITABLE, - XYCurveTypes, - LEGEND_CONFIG, - ValueLabelModes, - FittingFunctions, - GRID_LINES_CONFIG, - XY_VIS_RENDERER, - AXIS_EXTENT_CONFIG, - TICK_LABELS_CONFIG, - REFERENCE_LINE_LAYER, - LABELS_ORIENTATION_CONFIG, - AXIS_TITLES_VISIBILITY_CONFIG, - EndValues, - ANNOTATION_LAYER, - LayerTypes, -} from '../constants'; +import { XyVisFn } from '../types'; +import { XY_VIS, REFERENCE_LINE_LAYER, ANNOTATION_LAYER } from '../constants'; +import { strings } from '../i18n'; +import { commonXYArgs } from './common_xy_args'; +import { commonDataLayerArgs } from './common_data_layer_args'; -const strings = { - getMetricHelp: () => - i18n.translate('expressionXY.xyVis.logDatatable.metric', { - defaultMessage: 'Vertical axis', - }), - getXAxisHelp: () => - i18n.translate('expressionXY.xyVis.logDatatable.x', { - defaultMessage: 'Horizontal axis', - }), - getBreakdownHelp: () => - i18n.translate('expressionXY.xyVis.logDatatable.breakDown', { - defaultMessage: 'Break down by', - }), - getReferenceLineHelp: () => - i18n.translate('expressionXY.xyVis.logDatatable.breakDown', { - defaultMessage: 'Break down by', - }), -}; - -export const xyVisFunction: ExpressionFunctionDefinition< - typeof XY_VIS, - LensMultiTable, - XYArgs, - XYRender -> = { +export const xyVisFunction: XyVisFn = { name: XY_VIS, type: 'render', - inputTypes: [MULTITABLE], - help: i18n.translate('expressionXY.xyVis.help', { - defaultMessage: 'An X/Y chart', - }), + inputTypes: ['datatable'], + help: strings.getXYHelp(), args: { - title: { - types: ['string'], - help: 'The chart title.', - }, - description: { - types: ['string'], - help: '', - }, - xTitle: { - types: ['string'], - help: i18n.translate('expressionXY.xyVis.xTitle.help', { - defaultMessage: 'X axis title', - }), - }, - yTitle: { - types: ['string'], - help: i18n.translate('expressionXY.xyVis.yLeftTitle.help', { - defaultMessage: 'Y left axis title', - }), - }, - yRightTitle: { - types: ['string'], - help: i18n.translate('expressionXY.xyVis.yRightTitle.help', { - defaultMessage: 'Y right axis title', - }), - }, - yLeftExtent: { - types: [AXIS_EXTENT_CONFIG], - help: i18n.translate('expressionXY.xyVis.yLeftExtent.help', { - defaultMessage: 'Y left axis extents', - }), - }, - yRightExtent: { - types: [AXIS_EXTENT_CONFIG], - help: i18n.translate('expressionXY.xyVis.yRightExtent.help', { - defaultMessage: 'Y right axis extents', - }), - }, - legend: { - types: [LEGEND_CONFIG], - help: i18n.translate('expressionXY.xyVis.legend.help', { - defaultMessage: 'Configure the chart legend.', - }), - }, - fittingFunction: { - types: ['string'], - options: [...Object.values(FittingFunctions)], - help: i18n.translate('expressionXY.xyVis.fittingFunction.help', { - defaultMessage: 'Define how missing values are treated', - }), - }, - endValue: { - types: ['string'], - options: [...Object.values(EndValues)], - help: i18n.translate('expressionXY.xyVis.endValue.help', { - defaultMessage: 'End value', - }), - }, - emphasizeFitting: { - types: ['boolean'], - default: false, - help: '', - }, - valueLabels: { - types: ['string'], - options: [...Object.values(ValueLabelModes)], - help: i18n.translate('expressionXY.xyVis.valueLabels.help', { - defaultMessage: 'Value labels mode', - }), - }, - tickLabelsVisibilitySettings: { - types: [TICK_LABELS_CONFIG], - help: i18n.translate('expressionXY.xyVis.tickLabelsVisibilitySettings.help', { - defaultMessage: 'Show x and y axes tick labels', - }), - }, - labelsOrientation: { - types: [LABELS_ORIENTATION_CONFIG], - help: i18n.translate('expressionXY.xyVis.labelsOrientation.help', { - defaultMessage: 'Defines the rotation of the axis labels', - }), - }, - gridlinesVisibilitySettings: { - types: [GRID_LINES_CONFIG], - help: i18n.translate('expressionXY.xyVis.gridlinesVisibilitySettings.help', { - defaultMessage: 'Show x and y axes gridlines', - }), - }, - axisTitlesVisibilitySettings: { - types: [AXIS_TITLES_VISIBILITY_CONFIG], - help: i18n.translate('expressionXY.xyVis.axisTitlesVisibilitySettings.help', { - defaultMessage: 'Show x and y axes titles', - }), - }, - layers: { - types: [DATA_LAYER, REFERENCE_LINE_LAYER, ANNOTATION_LAYER], - help: i18n.translate('expressionXY.xyVis.layers.help', { - defaultMessage: 'Layers of visual series', - }), + ...commonXYArgs, + ...commonDataLayerArgs, + referenceLineLayers: { + types: [REFERENCE_LINE_LAYER], + help: strings.getReferenceLineLayerHelp(), multi: true, }, - curveType: { - types: ['string'], - options: [...Object.values(XYCurveTypes)], - help: i18n.translate('expressionXY.xyVis.curveType.help', { - defaultMessage: 'Define how curve type is rendered for a line chart', - }), - }, - fillOpacity: { - types: ['number'], - help: i18n.translate('expressionXY.xyVis.fillOpacity.help', { - defaultMessage: 'Define the area chart fill opacity', - }), - }, - hideEndzones: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.xyVis.hideEndzones.help', { - defaultMessage: 'Hide endzone markers for partial data', - }), - }, - valuesInLegend: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.xyVis.valuesInLegend.help', { - defaultMessage: 'Show values in legend', - }), - }, - ariaLabel: { - types: ['string'], - help: i18n.translate('expressionXY.xyVis.ariaLabel.help', { - defaultMessage: 'Specifies the aria label of the xy chart', - }), - required: false, + annotationLayers: { + types: [ANNOTATION_LAYER], + help: strings.getAnnotationLayerHelp(), + multi: true, }, }, - fn(data, args, handlers) { - if (handlers?.inspectorAdapters?.tables) { - args.layers.forEach((layer) => { - if (layer.layerType === LayerTypes.ANNOTATIONS) { - return; - } - - let xAccessor; - let splitAccessor; - if (layer.layerType === LayerTypes.DATA) { - xAccessor = layer.xAccessor; - splitAccessor = layer.splitAccessor; - } - - const { layerId, accessors, layerType } = layer; - const logTable = prepareLogTable( - data.tables[layerId], - [ - [ - accessors ? accessors : undefined, - layerType === 'data' ? strings.getMetricHelp() : strings.getReferenceLineHelp(), - ], - [xAccessor ? [xAccessor] : undefined, strings.getXAxisHelp()], - [splitAccessor ? [splitAccessor] : undefined, strings.getBreakdownHelp()], - ], - true - ); - - handlers.inspectorAdapters.tables.logDatatable(layerId, logTable); - }); - } - - return { - type: 'render', - as: XY_VIS_RENDERER, - value: { - data, - args: { - ...args, - ariaLabel: - args.ariaLabel ?? - (handlers.variables?.embeddableTitle as string) ?? - handlers.getExecutionContext?.()?.description, - }, - }, - }; + async fn(data, args, handlers) { + const { xyVisFn } = await import('./xy_vis_fn'); + return await xyVisFn(data, args, handlers); }, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts new file mode 100644 index 0000000000000..5e2f7432ddf93 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Dimension, prepareLogTable } from '@kbn/visualizations-plugin/common/utils'; +import { LayerTypes, XY_VIS_RENDERER, DATA_LAYER } from '../constants'; +import { appendLayerIds } from '../helpers'; +import { DataLayerConfigResult, XYLayerConfig, XyVisFn } from '../types'; +import { getLayerDimensions } from '../utils'; +import { + hasAreaLayer, + hasBarLayer, + hasHistogramBarLayer, + validateExtent, + validateFillOpacity, + validateValueLabels, +} from './validate'; + +export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { + const { + referenceLineLayers = [], + annotationLayers = [], + seriesType, + accessors = [], + xAccessor, + hide, + splitAccessor, + columnToLabel, + yScaleType, + xScaleType, + isHistogram, + yConfig, + palette, + ...restArgs + } = args; + const dataLayers: DataLayerConfigResult[] = [ + { + type: DATA_LAYER, + seriesType, + accessors, + xAccessor, + hide, + splitAccessor, + columnToLabel, + yScaleType, + xScaleType, + isHistogram, + palette, + yConfig, + layerType: LayerTypes.DATA, + table: data, + }, + ]; + const layers: XYLayerConfig[] = [ + ...appendLayerIds(dataLayers, 'dataLayers'), + ...appendLayerIds(referenceLineLayers, 'referenceLineLayers'), + ...appendLayerIds(annotationLayers, 'annotationLayers'), + ]; + + if (handlers.inspectorAdapters.tables) { + handlers.inspectorAdapters.tables.reset(); + handlers.inspectorAdapters.tables.allowCsvExport = true; + + const layerDimensions = layers.reduce((dimensions, layer) => { + if (layer.layerType === LayerTypes.ANNOTATIONS) { + return dimensions; + } + + return [...dimensions, ...getLayerDimensions(layer)]; + }, []); + + const logTable = prepareLogTable(data, layerDimensions, true); + handlers.inspectorAdapters.tables.logDatatable('default', logTable); + } + + const hasBar = hasBarLayer(dataLayers); + const hasArea = hasAreaLayer(dataLayers); + + validateExtent(args.yLeftExtent, hasBar || hasArea, dataLayers); + validateExtent(args.yRightExtent, hasBar || hasArea, dataLayers); + validateFillOpacity(args.fillOpacity, hasArea); + + const hasNotHistogramBars = !hasHistogramBarLayer(dataLayers); + + validateValueLabels(args.valueLabels, hasBar, hasNotHistogramBars); + + return { + type: 'render', + as: XY_VIS_RENDERER, + value: { + args: { + ...restArgs, + layers, + ariaLabel: + args.ariaLabel ?? + (handlers.variables?.embeddableTitle as string) ?? + handlers.getExecutionContext?.()?.description, + }, + }, + }; +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts index 943c6910952a0..882a3231148f5 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts @@ -6,84 +6,18 @@ * Side Public License, v 1. */ -import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; -import { FillStyles, IconPositions, LineStyles, YAxisModes, Y_CONFIG } from '../constants'; -import { YConfig, YConfigResult } from '../types'; +import { Y_CONFIG } from '../constants'; +import { YConfigFn } from '../types'; +import { strings } from '../i18n'; +import { commonYConfigArgs } from './common_y_config_args'; -export const yAxisConfigFunction: ExpressionFunctionDefinition< - typeof Y_CONFIG, - null, - YConfig, - YConfigResult -> = { +export const yAxisConfigFunction: YConfigFn = { name: Y_CONFIG, aliases: [], type: Y_CONFIG, - help: i18n.translate('expressionXY.yConfig.help', { - defaultMessage: `Configure the behavior of a xy chart's y axis metric`, - }), + help: strings.getYConfigFnHelp(), inputTypes: ['null'], - args: { - forAccessor: { - types: ['string'], - help: i18n.translate('expressionXY.yConfig.forAccessor.help', { - defaultMessage: 'The accessor this configuration is for', - }), - }, - axisMode: { - types: ['string'], - options: [...Object.values(YAxisModes)], - help: i18n.translate('expressionXY.yConfig.axisMode.help', { - defaultMessage: 'The axis mode of the metric', - }), - }, - color: { - types: ['string'], - help: i18n.translate('expressionXY.yConfig.color.help', { - defaultMessage: 'The color of the series', - }), - }, - lineStyle: { - types: ['string'], - options: [...Object.values(LineStyles)], - help: i18n.translate('expressionXY.yConfig.lineStyle.help', { - defaultMessage: 'The style of the reference line', - }), - }, - lineWidth: { - types: ['number'], - help: i18n.translate('expressionXY.yConfig.lineWidth.help', { - defaultMessage: 'The width of the reference line', - }), - }, - icon: { - types: ['string'], - help: i18n.translate('expressionXY.yConfig.icon.help', { - defaultMessage: 'An optional icon used for reference lines', - }), - }, - iconPosition: { - types: ['string'], - options: [...Object.values(IconPositions)], - help: i18n.translate('expressionXY.yConfig.iconPosition.help', { - defaultMessage: 'The placement of the icon for the reference line', - }), - }, - textVisibility: { - types: ['boolean'], - help: i18n.translate('expressionXY.yConfig.textVisibility.help', { - defaultMessage: 'Visibility of the label on the reference line', - }), - }, - fill: { - types: ['string'], - options: [...Object.values(FillStyles)], - help: i18n.translate('expressionXY.yConfig.fill.help', { - defaultMessage: 'Fill', - }), - }, - }, + args: { ...commonYConfigArgs }, fn(input, args) { return { type: Y_CONFIG, diff --git a/src/plugins/chart_expressions/expression_xy/common/helpers/index.ts b/src/plugins/chart_expressions/expression_xy/common/helpers/index.ts new file mode 100644 index 0000000000000..55c4136e0c00d --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/helpers/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 { appendLayerIds } from './layers'; diff --git a/src/plugins/chart_expressions/expression_xy/common/helpers/layers.test.ts b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.test.ts new file mode 100644 index 0000000000000..ac44ef18fc505 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.test.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { generateLayerId, appendLayerIds } from './layers'; + +describe('#generateLayerId', () => { + it('should return the combination of keyword and index', () => { + const key = 'some-key'; + const index = 10; + const id = generateLayerId(key, index); + expect(id).toBe(`${key}-${index}`); + }); +}); + +describe('#appendLayerIds', () => { + it('should add layerId to each layer', () => { + const layers = [{ name: 'someName' }, { name: 'someName2' }, { name: 'someName3' }]; + const keyword = 'keyword'; + const expectedLayerIds = [ + { ...layers[0], layerId: `${keyword}-0` }, + { ...layers[1], layerId: `${keyword}-1` }, + { ...layers[2], layerId: `${keyword}-2` }, + ]; + + const layersWithIds = appendLayerIds(layers, keyword); + expect(layersWithIds).toStrictEqual(expectedLayerIds); + }); + + it('should filter out undefined layers', () => { + const layers = [undefined, undefined, undefined]; + const result = appendLayerIds(layers, 'some-key'); + expect(result).toStrictEqual([]); + + const layers2 = [{ name: 'someName' }, undefined, { name: 'someName3' }]; + const keyword = 'keyword'; + const expectedLayerIds = [ + { ...layers2[0], layerId: `${keyword}-0` }, + { ...layers2[2], layerId: `${keyword}-1` }, + ]; + + const layersWithIds = appendLayerIds(layers2, keyword); + expect(layersWithIds).toStrictEqual(expectedLayerIds); + }); +}); diff --git a/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts new file mode 100644 index 0000000000000..d62ea264acb1a --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { WithLayerId } from '../types'; + +function isWithLayerId(layer: T): layer is T & WithLayerId { + return (layer as T & WithLayerId).layerId ? true : false; +} + +export const generateLayerId = (keyword: string, index: number) => `${keyword}-${index}`; + +export function appendLayerIds( + layers: Array, + keyword: string +): Array { + return layers + .filter((l): l is T => l !== undefined) + .map((l, index) => ({ + ...l, + layerId: isWithLayerId(l) ? l.layerId : generateLayerId(keyword, index), + })); +} diff --git a/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx new file mode 100644 index 0000000000000..225f9de0d6a7c --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx @@ -0,0 +1,212 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; + +export const strings = { + getXYHelp: () => + i18n.translate('expressionXY.xyVis.help', { + defaultMessage: 'An X/Y chart', + }), + getMetricHelp: () => + i18n.translate('expressionXY.xyVis.logDatatable.metric', { + defaultMessage: 'Vertical axis', + }), + getXAxisHelp: () => + i18n.translate('expressionXY.xyVis.logDatatable.x', { + defaultMessage: 'Horizontal axis', + }), + getBreakdownHelp: () => + i18n.translate('expressionXY.xyVis.logDatatable.breakDown', { + defaultMessage: 'Break down by', + }), + getReferenceLineHelp: () => + i18n.translate('expressionXY.xyVis.logDatatable.breakDown', { + defaultMessage: 'Break down by', + }), + getXTitleHelp: () => + i18n.translate('expressionXY.xyVis.xTitle.help', { + defaultMessage: 'X axis title', + }), + getYTitleHelp: () => + i18n.translate('expressionXY.xyVis.yLeftTitle.help', { + defaultMessage: 'Y left axis title', + }), + getYRightTitleHelp: () => + i18n.translate('expressionXY.xyVis.yRightTitle.help', { + defaultMessage: 'Y right axis title', + }), + getYLeftExtentHelp: () => + i18n.translate('expressionXY.xyVis.yLeftExtent.help', { + defaultMessage: 'Y left axis extents', + }), + getYRightExtentHelp: () => + i18n.translate('expressionXY.xyVis.yRightExtent.help', { + defaultMessage: 'Y right axis extents', + }), + getLegendHelp: () => + i18n.translate('expressionXY.xyVis.legend.help', { + defaultMessage: 'Configure the chart legend.', + }), + getFittingFunctionHelp: () => + i18n.translate('expressionXY.xyVis.fittingFunction.help', { + defaultMessage: 'Define how missing values are treated', + }), + getEndValueHelp: () => + i18n.translate('expressionXY.xyVis.endValue.help', { + defaultMessage: 'End value', + }), + getValueLabelsHelp: () => + i18n.translate('expressionXY.xyVis.valueLabels.help', { + defaultMessage: 'Value labels mode', + }), + getTickLabelsVisibilitySettingsHelp: () => + i18n.translate('expressionXY.xyVis.tickLabelsVisibilitySettings.help', { + defaultMessage: 'Show x and y axes tick labels', + }), + getLabelsOrientationHelp: () => + i18n.translate('expressionXY.xyVis.labelsOrientation.help', { + defaultMessage: 'Defines the rotation of the axis labels', + }), + getGridlinesVisibilitySettingsHelp: () => + i18n.translate('expressionXY.xyVis.gridlinesVisibilitySettings.help', { + defaultMessage: 'Show x and y axes gridlines', + }), + getAxisTitlesVisibilitySettingsHelp: () => + i18n.translate('expressionXY.xyVis.axisTitlesVisibilitySettings.help', { + defaultMessage: 'Show x and y axes titles', + }), + getDataLayerHelp: () => + i18n.translate('expressionXY.xyVis.dataLayer.help', { + defaultMessage: 'Data layer of visual series', + }), + getReferenceLineLayerHelp: () => + i18n.translate('expressionXY.xyVis.referenceLineLayer.help', { + defaultMessage: 'Reference line layer', + }), + getAnnotationLayerHelp: () => + i18n.translate('expressionXY.xyVis.annotationLayer.help', { + defaultMessage: 'Annotation layer', + }), + getCurveTypeHelp: () => + i18n.translate('expressionXY.xyVis.curveType.help', { + defaultMessage: 'Define how curve type is rendered for a line chart', + }), + getFillOpacityHelp: () => + i18n.translate('expressionXY.xyVis.fillOpacity.help', { + defaultMessage: 'Define the area chart fill opacity', + }), + getHideEndzonesHelp: () => + i18n.translate('expressionXY.xyVis.hideEndzones.help', { + defaultMessage: 'Hide endzone markers for partial data', + }), + getValuesInLegendHelp: () => + i18n.translate('expressionXY.xyVis.valuesInLegend.help', { + defaultMessage: 'Show values in legend', + }), + getAriaLabelHelp: () => + i18n.translate('expressionXY.xyVis.ariaLabel.help', { + defaultMessage: 'Specifies the aria label of the xy chart', + }), + getDataLayerFnHelp: () => + i18n.translate('expressionXY.dataLayer.help', { + defaultMessage: `Configure a layer in the xy chart`, + }), + getHideHelp: () => + i18n.translate('expressionXY.dataLayer.hide.help', { + defaultMessage: 'Show / hide axis', + }), + getXAccessorHelp: () => + i18n.translate('expressionXY.dataLayer.xAccessor.help', { + defaultMessage: 'X-axis', + }), + getSeriesTypeHelp: () => + i18n.translate('expressionXY.dataLayer.seriesType.help', { + defaultMessage: 'The type of chart to display.', + }), + getXScaleTypeHelp: () => + i18n.translate('expressionXY.dataLayer.xScaleType.help', { + defaultMessage: 'The scale type of the x axis', + }), + getIsHistogramHelp: () => + i18n.translate('expressionXY.dataLayer.isHistogram.help', { + defaultMessage: 'Whether to layout the chart as a histogram', + }), + getYScaleTypeHelp: () => + i18n.translate('expressionXY.dataLayer.yScaleType.help', { + defaultMessage: 'The scale type of the y axes', + }), + getSplitAccessorHelp: () => + i18n.translate('expressionXY.dataLayer.splitAccessor.help', { + defaultMessage: 'The column to split by', + }), + getAccessorsHelp: () => + i18n.translate('expressionXY.dataLayer.accessors.help', { + defaultMessage: 'The columns to display on the y axis.', + }), + getYConfigHelp: () => + i18n.translate('expressionXY.dataLayer.yConfig.help', { + defaultMessage: 'Additional configuration for y axes', + }), + getColumnToLabelHelp: () => + i18n.translate('expressionXY.layer.columnToLabel.help', { + defaultMessage: 'JSON key-value pairs of column ID to label', + }), + getPaletteHelp: () => + i18n.translate('expressionXY.dataLayer.palette.help', { + defaultMessage: 'Palette', + }), + getTableHelp: () => + i18n.translate('expressionXY.layers.table.help', { + defaultMessage: 'Table', + }), + getLayerIdHelp: () => + i18n.translate('expressionXY.layers.layerId.help', { + defaultMessage: 'Layer ID', + }), + getRLAccessorsHelp: () => + i18n.translate('expressionXY.referenceLineLayer.accessors.help', { + defaultMessage: 'The columns to display on the y axis.', + }), + getRLYConfigHelp: () => + i18n.translate('expressionXY.referenceLineLayer.yConfig.help', { + defaultMessage: 'Additional configuration for y axes', + }), + getRLHelp: () => + i18n.translate('expressionXY.referenceLineLayer.help', { + defaultMessage: `Configure a reference line in the xy chart`, + }), + getYConfigFnHelp: () => + i18n.translate('expressionXY.yConfig.help', { + defaultMessage: `Configure the behavior of a xy chart's y axis metric`, + }), + getForAccessorHelp: () => + i18n.translate('expressionXY.yConfig.forAccessor.help', { + defaultMessage: 'The accessor this configuration is for', + }), + getAxisModeHelp: () => + i18n.translate('expressionXY.yConfig.axisMode.help', { + defaultMessage: 'The axis mode of the metric', + }), + getColorHelp: () => + i18n.translate('expressionXY.yConfig.color.help', { + defaultMessage: 'The color of the series', + }), + getAnnotationLayerFnHelp: () => + i18n.translate('expressionXY.annotationLayer.help', { + defaultMessage: `Configure an annotation layer in the xy chart`, + }), + getAnnotationLayerHideHelp: () => + i18n.translate('expressionXY.annotationLayer.hide.help', { + defaultMessage: 'Show / hide details', + }), + getAnnotationLayerAnnotationsHelp: () => + i18n.translate('expressionXY.annotationLayer.annotations.help', { + defaultMessage: 'Annotations', + }), +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index 68f9f946baeb0..7211a7a7db1b7 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -9,20 +9,6 @@ export const PLUGIN_ID = 'expressionXy'; export const PLUGIN_NAME = 'expressionXy'; -export { - xyVisFunction, - yAxisConfigFunction, - legendConfigFunction, - gridlinesConfigFunction, - dataLayerConfigFunction, - axisExtentConfigFunction, - tickLabelsConfigFunction, - annotationLayerConfigFunction, - labelsOrientationConfigFunction, - referenceLineLayerConfigFunction, - axisTitlesVisibilityConfigFunction, -} from './expression_functions'; - export type { XYArgs, YConfig, @@ -42,25 +28,36 @@ export type { XYChartProps, LegendConfig, IconPosition, - YConfigResult, DataLayerArgs, - LensMultiTable, ValueLabelMode, AxisExtentMode, + DataLayerConfig, FittingFunction, + ExtendedYConfig, AxisExtentConfig, + CollectiveConfig, LegendConfigResult, AxesSettingsConfig, + CommonXYLayerConfig, AnnotationLayerArgs, - XYLayerConfigResult, + ExtendedYConfigResult, GridlinesConfigResult, DataLayerConfigResult, TickLabelsConfigResult, AxisExtentConfigResult, ReferenceLineLayerArgs, + CommonXYDataLayerConfig, LabelsOrientationConfig, - AnnotationLayerConfigResult, + ReferenceLineLayerConfig, + AvailableReferenceLineIcon, + XYExtendedLayerConfigResult, + CommonXYAnnotationLayerConfig, + ExtendedDataLayerConfigResult, LabelsOrientationConfigResult, + CommonXYDataLayerConfigResult, ReferenceLineLayerConfigResult, + CommonXYReferenceLineLayerConfig, AxisTitlesVisibilityConfigResult, + ExtendedReferenceLineLayerConfigResult, + CommonXYReferenceLineLayerConfigResult, } from './types'; diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index a1984c78fe0ac..3cd84fa14682c 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -9,7 +9,8 @@ import { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/charts'; import { $Values } from '@kbn/utility-types'; import type { PaletteOutput } from '@kbn/coloring'; -import { Datatable } from '@kbn/expressions-plugin'; +import { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; import { EventAnnotationOutput } from '@kbn/event-annotation-plugin/common'; import { AxisExtentModes, @@ -17,7 +18,6 @@ import { FittingFunctions, IconPositions, LayerTypes, - MULTITABLE, LineStyles, SeriesTypes, ValueLabelModes, @@ -34,9 +34,17 @@ import { LEGEND_CONFIG, DATA_LAYER, AXIS_EXTENT_CONFIG, + EXTENDED_DATA_LAYER, + EXTENDED_REFERENCE_LINE_LAYER, ANNOTATION_LAYER, EndValues, + EXTENDED_Y_CONFIG, + AvailableReferenceLineIcons, + XY_VIS, + LAYERED_XY_VIS, + EXTENDED_ANNOTATION_LAYER, } from '../constants'; +import { XYRender } from './expression_renderers'; export type EndValue = $Values; export type LayerType = $Values; @@ -51,6 +59,7 @@ export type IconPosition = $Values; export type ValueLabelMode = $Values; export type AxisExtentMode = $Values; export type FittingFunction = $Values; +export type AvailableReferenceLineIcon = $Values; export interface AxesSettingsConfig { x: boolean; @@ -69,11 +78,8 @@ export interface AxisConfig { hide?: boolean; } -export interface YConfig { - forAccessor: string; - axisMode?: YAxisMode; - color?: string; - icon?: string; +export interface ExtendedYConfig extends YConfig { + icon?: AvailableReferenceLineIcon; lineWidth?: number; lineStyle?: LineStyle; fill?: FillStyle; @@ -81,12 +87,32 @@ export interface YConfig { textVisibility?: boolean; } +export interface YConfig { + forAccessor: string; + axisMode?: YAxisMode; + color?: string; +} + +export interface DataLayerArgs { + accessors: string[]; + seriesType: SeriesType; + xAccessor?: string; + hide?: boolean; + splitAccessor?: string; + columnToLabel?: string; // Actually a JSON key-value pair + yScaleType: YScaleType; + xScaleType: XScaleType; + isHistogram: boolean; + palette: PaletteOutput; + yConfig?: YConfigResult[]; +} + export interface ValidLayer extends DataLayerConfigResult { xAccessor: NonNullable; } -export interface DataLayerArgs { - layerId: string; +export interface ExtendedDataLayerArgs { + layerId?: string; accessors: string[]; seriesType: SeriesType; xAccessor?: string; @@ -96,9 +122,9 @@ export interface DataLayerArgs { yScaleType: YScaleType; xScaleType: XScaleType; isHistogram: boolean; - // palette will always be set on the expression palette: PaletteOutput; yConfig?: YConfigResult[]; + table?: Datatable; } export interface LegendConfig { @@ -121,11 +147,11 @@ export interface LegendConfig { /** * Horizontal Alignment of the legend when it is set inside chart */ - horizontalAlignment?: HorizontalAlignment; + horizontalAlignment?: typeof HorizontalAlignment.Right | typeof HorizontalAlignment.Left; /** * Vertical Alignment of the legend when it is set inside chart */ - verticalAlignment?: VerticalAlignment; + verticalAlignment?: typeof VerticalAlignment.Top | typeof VerticalAlignment.Bottom; /** * Number of columns when legend is set inside chart */ @@ -144,7 +170,7 @@ export interface LegendConfig { * Exact legend width (vertical) or height (horizontal) * Limited to max of 70% of the chart container dimension Vertical legends limited to min of 30% of computed width */ - legendSize?: number; + legendSize?: LegendSize; } export interface LabelsOrientationConfig { @@ -154,17 +180,62 @@ export interface LabelsOrientationConfig { } // Arguments to XY chart expression, with computed properties -export interface XYArgs { - title?: string; - description?: string; +export interface XYArgs extends DataLayerArgs { xTitle: string; yTitle: string; yRightTitle: string; yLeftExtent: AxisExtentConfigResult; yRightExtent: AxisExtentConfigResult; legend: LegendConfigResult; + endValue?: EndValue; + emphasizeFitting?: boolean; valueLabels: ValueLabelMode; - layers: XYLayerConfigResult[]; + referenceLineLayers: ReferenceLineLayerConfigResult[]; + annotationLayers: AnnotationLayerConfigResult[]; + fittingFunction?: FittingFunction; + axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; + tickLabelsVisibilitySettings?: TickLabelsConfigResult; + gridlinesVisibilitySettings?: GridlinesConfigResult; + labelsOrientation?: LabelsOrientationConfigResult; + curveType?: XYCurveType; + fillOpacity?: number; + hideEndzones?: boolean; + valuesInLegend?: boolean; + ariaLabel?: string; +} + +export interface LayeredXYArgs { + xTitle: string; + yTitle: string; + yRightTitle: string; + yLeftExtent: AxisExtentConfigResult; + yRightExtent: AxisExtentConfigResult; + legend: LegendConfigResult; + endValue?: EndValue; + emphasizeFitting?: boolean; + valueLabels: ValueLabelMode; + layers?: XYExtendedLayerConfigResult[]; + fittingFunction?: FittingFunction; + axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; + tickLabelsVisibilitySettings?: TickLabelsConfigResult; + gridlinesVisibilitySettings?: GridlinesConfigResult; + labelsOrientation?: LabelsOrientationConfigResult; + curveType?: XYCurveType; + fillOpacity?: number; + hideEndzones?: boolean; + valuesInLegend?: boolean; + ariaLabel?: string; +} + +export interface XYProps { + xTitle: string; + yTitle: string; + yRightTitle: string; + yLeftExtent: AxisExtentConfigResult; + yRightExtent: AxisExtentConfigResult; + legend: LegendConfigResult; + valueLabels: ValueLabelMode; + layers: CommonXYLayerConfig[]; endValue?: EndValue; emphasizeFitting?: boolean; fittingFunction?: FittingFunction; @@ -181,49 +252,89 @@ export interface XYArgs { export interface AnnotationLayerArgs { annotations: EventAnnotationOutput[]; - layerId: string; hide?: boolean; } +export type ExtendedAnnotationLayerArgs = AnnotationLayerArgs & { + layerId?: string; +}; + export type AnnotationLayerConfigResult = AnnotationLayerArgs & { type: typeof ANNOTATION_LAYER; layerType: typeof LayerTypes.ANNOTATIONS; }; +export type ExtendedAnnotationLayerConfigResult = ExtendedAnnotationLayerArgs & { + type: typeof EXTENDED_ANNOTATION_LAYER; + layerType: typeof LayerTypes.ANNOTATIONS; +}; + export interface ReferenceLineLayerArgs { - layerId: string; accessors: string[]; columnToLabel?: string; - yConfig?: YConfigResult[]; + yConfig?: ExtendedYConfigResult[]; } -export type XYLayerArgs = DataLayerArgs | ReferenceLineLayerArgs | AnnotationLayerArgs; +export interface ExtendedReferenceLineLayerArgs { + layerId?: string; + accessors: string[]; + columnToLabel?: string; + yConfig?: ExtendedYConfigResult[]; + table?: Datatable; +} -export type XYLayerConfigResult = - | DataLayerConfigResult - | ReferenceLineLayerConfigResult - | AnnotationLayerConfigResult; +export type XYLayerArgs = DataLayerArgs | ReferenceLineLayerArgs | AnnotationLayerArgs; +export type XYLayerConfig = DataLayerConfig | ReferenceLineLayerConfig | AnnotationLayerConfig; +export type XYExtendedLayerConfig = + | ExtendedDataLayerConfig + | ExtendedReferenceLineLayerConfig + | ExtendedAnnotationLayerConfig; -export interface LensMultiTable { - type: typeof MULTITABLE; - tables: Record; - dateRange?: { - fromDate: Date; - toDate: Date; - }; -} +export type XYExtendedLayerConfigResult = + | ExtendedDataLayerConfigResult + | ExtendedReferenceLineLayerConfigResult + | ExtendedAnnotationLayerConfigResult; export type ReferenceLineLayerConfigResult = ReferenceLineLayerArgs & { type: typeof REFERENCE_LINE_LAYER; layerType: typeof LayerTypes.REFERENCELINE; + table: Datatable; +}; + +export type ExtendedReferenceLineLayerConfigResult = ExtendedReferenceLineLayerArgs & { + type: typeof EXTENDED_REFERENCE_LINE_LAYER; + layerType: typeof LayerTypes.REFERENCELINE; + table: Datatable; }; -export type DataLayerConfigResult = DataLayerArgs & { +export type DataLayerConfigResult = Omit & { type: typeof DATA_LAYER; layerType: typeof LayerTypes.DATA; + palette: PaletteOutput; + table: Datatable; +}; + +export interface WithLayerId { + layerId: string; +} + +export type DataLayerConfig = DataLayerConfigResult & WithLayerId; +export type ReferenceLineLayerConfig = ReferenceLineLayerConfigResult & WithLayerId; +export type AnnotationLayerConfig = AnnotationLayerConfigResult & WithLayerId; + +export type ExtendedDataLayerConfig = ExtendedDataLayerConfigResult & WithLayerId; +export type ExtendedReferenceLineLayerConfig = ExtendedReferenceLineLayerConfigResult & WithLayerId; +export type ExtendedAnnotationLayerConfig = ExtendedAnnotationLayerConfigResult & WithLayerId; + +export type ExtendedDataLayerConfigResult = Omit & { + type: typeof EXTENDED_DATA_LAYER; + layerType: typeof LayerTypes.DATA; + palette: PaletteOutput; + table: Datatable; }; export type YConfigResult = YConfig & { type: typeof Y_CONFIG }; +export type ExtendedYConfigResult = ExtendedYConfig & { type: typeof EXTENDED_Y_CONFIG }; export type AxisTitlesVisibilityConfigResult = AxesSettingsConfig & { type: typeof AXIS_TITLES_VISIBILITY_CONFIG; @@ -237,3 +348,64 @@ export type LegendConfigResult = LegendConfig & { type: typeof LEGEND_CONFIG }; export type AxisExtentConfigResult = AxisExtentConfig & { type: typeof AXIS_EXTENT_CONFIG }; export type GridlinesConfigResult = AxesSettingsConfig & { type: typeof GRID_LINES_CONFIG }; export type TickLabelsConfigResult = AxesSettingsConfig & { type: typeof TICK_LABELS_CONFIG }; + +export type CommonXYLayerConfig = XYLayerConfig | XYExtendedLayerConfig; +export type CommonXYDataLayerConfigResult = DataLayerConfigResult | ExtendedDataLayerConfigResult; +export type CommonXYReferenceLineLayerConfigResult = + | ReferenceLineLayerConfigResult + | ExtendedReferenceLineLayerConfigResult; + +export type CommonXYDataLayerConfig = DataLayerConfig | ExtendedDataLayerConfig; +export type CommonXYReferenceLineLayerConfig = + | ReferenceLineLayerConfig + | ExtendedReferenceLineLayerConfig; + +export type CommonXYAnnotationLayerConfig = AnnotationLayerConfig | ExtendedAnnotationLayerConfig; + +export type XyVisFn = ExpressionFunctionDefinition< + typeof XY_VIS, + Datatable, + XYArgs, + Promise +>; +export type LayeredXyVisFn = ExpressionFunctionDefinition< + typeof LAYERED_XY_VIS, + Datatable, + LayeredXYArgs, + Promise +>; + +export type ExtendedDataLayerFn = ExpressionFunctionDefinition< + typeof EXTENDED_DATA_LAYER, + Datatable, + ExtendedDataLayerArgs, + ExtendedDataLayerConfigResult +>; + +export type ReferenceLineLayerFn = ExpressionFunctionDefinition< + typeof REFERENCE_LINE_LAYER, + Datatable, + ReferenceLineLayerArgs, + ReferenceLineLayerConfigResult +>; +export type ExtendedReferenceLineLayerFn = ExpressionFunctionDefinition< + typeof EXTENDED_REFERENCE_LINE_LAYER, + Datatable, + ExtendedReferenceLineLayerArgs, + ExtendedReferenceLineLayerConfigResult +>; + +export type YConfigFn = ExpressionFunctionDefinition; +export type ExtendedYConfigFn = ExpressionFunctionDefinition< + typeof EXTENDED_Y_CONFIG, + null, + ExtendedYConfig, + ExtendedYConfigResult +>; + +export type LegendConfigFn = ExpressionFunctionDefinition< + typeof LEGEND_CONFIG, + null, + LegendConfig, + Promise +>; diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts index 1acb98903d06b..b03ea975b0143 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts @@ -6,12 +6,16 @@ * Side Public License, v 1. */ +import { AnnotationTooltipFormatter } from '@elastic/charts'; +import { + AvailableAnnotationIcon, + ManualPointEventAnnotationArgs, +} from '@kbn/event-annotation-plugin/common'; import { XY_VIS_RENDERER } from '../constants'; -import { LensMultiTable, XYArgs } from './expression_functions'; +import { XYProps } from './expression_functions'; export interface XYChartProps { - data: LensMultiTable; - args: XYArgs; + args: XYProps; } export interface XYRender { @@ -19,3 +23,10 @@ export interface XYRender { as: typeof XY_VIS_RENDERER; value: XYChartProps; } + +export interface CollectiveConfig extends Omit { + roundedTimestamp: number; + axisMode: 'bottom'; + icon?: AvailableAnnotationIcon | string; + customTooltipDetails?: AnnotationTooltipFormatter | undefined; +} diff --git a/src/plugins/chart_expressions/expression_xy/common/utils/index.tsx b/src/plugins/chart_expressions/expression_xy/common/utils/index.tsx new file mode 100644 index 0000000000000..ba40d5768f50c --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/utils/index.tsx @@ -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 { logDatatables, getLayerDimensions } from './log_datatables'; diff --git a/src/plugins/chart_expressions/expression_xy/common/utils/log_datatables.ts b/src/plugins/chart_expressions/expression_xy/common/utils/log_datatables.ts new file mode 100644 index 0000000000000..79a3cbd2eef19 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/utils/log_datatables.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { ExecutionContext } from '@kbn/expressions-plugin'; +import { Dimension, prepareLogTable } from '@kbn/visualizations-plugin/common/utils'; +import { LayerTypes } from '../constants'; +import { strings } from '../i18n'; +import { + CommonXYDataLayerConfig, + CommonXYLayerConfig, + CommonXYReferenceLineLayerConfig, +} from '../types'; + +export const logDatatables = (layers: CommonXYLayerConfig[], handlers: ExecutionContext) => { + if (!handlers?.inspectorAdapters?.tables) { + return; + } + + handlers.inspectorAdapters.tables.reset(); + handlers.inspectorAdapters.tables.allowCsvExport = true; + + layers.forEach((layer) => { + if (layer.layerType === LayerTypes.ANNOTATIONS) { + return; + } + const logTable = prepareLogTable(layer.table, getLayerDimensions(layer), true); + handlers.inspectorAdapters.tables.logDatatable(layer.layerId, logTable); + }); +}; + +export const getLayerDimensions = ( + layer: CommonXYDataLayerConfig | CommonXYReferenceLineLayerConfig +): Dimension[] => { + let xAccessor; + let splitAccessor; + if (layer.layerType === LayerTypes.DATA) { + xAccessor = layer.xAccessor; + splitAccessor = layer.splitAccessor; + } + + const { accessors, layerType } = layer; + return [ + [ + accessors ? accessors : undefined, + layerType === LayerTypes.DATA ? strings.getMetricHelp() : strings.getReferenceLineHelp(), + ], + [xAccessor ? [xAccessor] : undefined, strings.getXAxisHelp()], + [splitAccessor ? [splitAccessor] : undefined, strings.getBreakdownHelp()], + ]; +}; diff --git a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx index 60b9f6a5788b4..e84d8c001fb82 100644 --- a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx @@ -6,9 +6,10 @@ * Side Public License, v 1. */ +import { Datatable } from '@kbn/expressions-plugin/common'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; -import { DataLayerConfigResult, LensMultiTable, XYArgs } from '../../common'; import { LayerTypes } from '../../common/constants'; +import { DataLayerConfig, XYProps } from '../../common/types'; import { mockPaletteOutput, sampleArgs } from '../../common/__mocks__'; const chartSetupContract = chartPluginMock.createSetupContract(); @@ -19,156 +20,147 @@ export const chartsActiveCursorService = chartStartContract.activeCursor; export const paletteService = chartPluginMock.createPaletteRegistry(); -export const dateHistogramData: LensMultiTable = { - type: 'lens_multitable', - tables: { - timeLayer: { - type: 'datatable', - rows: [ - { - xAccessorId: 1585758120000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585758360000, - splitAccessorId: "Women's Accessories", - yAccessorId: 1, - }, - { - xAccessorId: 1585758360000, - splitAccessorId: "Women's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585759380000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585759380000, - splitAccessorId: "Men's Shoes", - yAccessorId: 1, - }, - { - xAccessorId: 1585759380000, - splitAccessorId: "Women's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585760700000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585760760000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585760760000, - splitAccessorId: "Men's Shoes", - yAccessorId: 1, - }, - { - xAccessorId: 1585761120000, - splitAccessorId: "Men's Shoes", - yAccessorId: 1, - }, - ], - columns: [ - { - id: 'xAccessorId', - name: 'order_date per minute', - meta: { - type: 'date', +export const dateHistogramData: Datatable = { + type: 'datatable', + rows: [ + { + xAccessorId: 1585758120000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585758360000, + splitAccessorId: "Women's Accessories", + yAccessorId: 1, + }, + { + xAccessorId: 1585758360000, + splitAccessorId: "Women's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Women's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585760700000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585760760000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585760760000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + }, + { + xAccessorId: 1585761120000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + }, + ], + columns: [ + { + id: 'xAccessorId', + name: 'order_date per minute', + meta: { + type: 'date', + field: 'order_date', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'date_histogram', + appliedTimeRange: { + from: '2020-04-01T16:14:16.246Z', + to: '2020-04-01T17:15:41.263Z', + }, + params: { field: 'order_date', - source: 'esaggs', - index: 'indexPatternId', - sourceParams: { - indexPatternId: 'indexPatternId', - type: 'date_histogram', - appliedTimeRange: { - from: '2020-04-01T16:14:16.246Z', - to: '2020-04-01T17:15:41.263Z', - }, - params: { - field: 'order_date', - timeRange: { from: '2020-04-01T16:14:16.246Z', to: '2020-04-01T17:15:41.263Z' }, - useNormalizedEsInterval: true, - scaleMetricValues: false, - interval: '1m', - drop_partials: false, - min_doc_count: 0, - extended_bounds: {}, - }, - }, - params: { id: 'date', params: { pattern: 'HH:mm' } }, + timeRange: { from: '2020-04-01T16:14:16.246Z', to: '2020-04-01T17:15:41.263Z' }, + useNormalizedEsInterval: true, + scaleMetricValues: false, + interval: '1m', + drop_partials: false, + min_doc_count: 0, + extended_bounds: {}, }, }, - { - id: 'splitAccessorId', - name: 'Top values of category.keyword', - meta: { - type: 'string', + params: { id: 'date', params: { pattern: 'HH:mm' } }, + }, + }, + { + id: 'splitAccessorId', + name: 'Top values of category.keyword', + meta: { + type: 'string', + field: 'category.keyword', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'terms', + params: { field: 'category.keyword', - source: 'esaggs', - index: 'indexPatternId', - sourceParams: { - indexPatternId: 'indexPatternId', - type: 'terms', - params: { - field: 'category.keyword', - orderBy: 'yAccessorId', - order: 'desc', - size: 3, - otherBucket: false, - otherBucketLabel: 'Other', - missingBucket: false, - missingBucketLabel: 'Missing', - }, - }, - params: { - id: 'terms', - params: { - id: 'string', - otherBucketLabel: 'Other', - missingBucketLabel: 'Missing', - parsedUrl: { - origin: 'http://localhost:5601', - pathname: '/jiy/app/kibana', - basePath: '/jiy', - }, - }, - }, + orderBy: 'yAccessorId', + order: 'desc', + size: 3, + otherBucket: false, + otherBucketLabel: 'Other', + missingBucket: false, + missingBucketLabel: 'Missing', }, }, - { - id: 'yAccessorId', - name: 'Count of records', - meta: { - type: 'number', - source: 'esaggs', - index: 'indexPatternId', - sourceParams: { - indexPatternId: 'indexPatternId', - params: {}, + params: { + id: 'terms', + params: { + id: 'string', + otherBucketLabel: 'Other', + missingBucketLabel: 'Missing', + parsedUrl: { + origin: 'http://localhost:5601', + pathname: '/jiy/app/kibana', + basePath: '/jiy', }, - params: { id: 'number' }, }, }, - ], + }, + }, + { + id: 'yAccessorId', + name: 'Count of records', + meta: { + type: 'number', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + params: {}, + }, + params: { id: 'number' }, + }, }, - }, - dateRange: { - fromDate: new Date('2020-04-01T16:14:16.246Z'), - toDate: new Date('2020-04-01T17:15:41.263Z'), - }, + ], }; -export const dateHistogramLayer: DataLayerConfigResult = { +export const dateHistogramLayer: DataLayerConfig = { + layerId: 'dateHistogramLayer', type: 'dataLayer', - layerId: 'timeLayer', layerType: LayerTypes.DATA, hide: false, xAccessor: 'xAccessorId', @@ -179,46 +171,37 @@ export const dateHistogramLayer: DataLayerConfigResult = { seriesType: 'bar_stacked', accessors: ['yAccessorId'], palette: mockPaletteOutput, + table: dateHistogramData, }; export function sampleArgsWithReferenceLine(value: number = 150) { - const { data, args } = sampleArgs(); + const { args: sArgs } = sampleArgs(); + const data: Datatable = { + type: 'datatable', + columns: [ + { + id: 'referenceLine-a', + meta: { params: { id: 'number' }, type: 'number' }, + name: 'Static value', + }, + ], + rows: [{ 'referenceLine-a': value }], + }; - return { - data: { - ...data, - tables: { - ...data.tables, - referenceLine: { - type: 'datatable', - columns: [ - { - id: 'referenceLine-a', - meta: { params: { id: 'number' }, type: 'number' }, - name: 'Static value', - }, - ], - rows: [{ 'referenceLine-a': value }], - }, + const args: XYProps = { + ...sArgs, + layers: [ + ...sArgs.layers, + { + layerId: 'referenceLine-a', + type: 'referenceLineLayer', + layerType: LayerTypes.REFERENCELINE, + accessors: ['referenceLine-a'], + yConfig: [{ axisMode: 'left', forAccessor: 'referenceLine-a', type: 'extendedYConfig' }], + table: data, }, - } as LensMultiTable, - args: { - ...args, - layers: [ - ...args.layers, - { - layerType: LayerTypes.REFERENCELINE, - accessors: ['referenceLine-a'], - layerId: 'referenceLine', - seriesType: 'line', - xScaleType: 'linear', - yScaleType: 'linear', - palette: mockPaletteOutput, - isHistogram: false, - hide: true, - yConfig: [{ axisMode: 'left', forAccessor: 'referenceLine-a', type: 'yConfig' }], - }, - ], - } as XYArgs, + ], }; + + return { data, args }; } diff --git a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap index f534db5a6c4e9..2bcfb37aca2e5 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`XYChart component annotations should render basic annotation 1`] = ` +exports[`XYChart component annotations should render basic line annotation 1`] = ` `; -exports[`XYChart component annotations should render grouped annotations preserving the shared styles 1`] = ` +exports[`XYChart component annotations should render basic range annotation 1`] = ` +Array [ + , + , +] +`; + +exports[`XYChart component annotations should render grouped line annotations preserving the shared styles 1`] = ` `; -exports[`XYChart component annotations should render grouped annotations with default styles 1`] = ` +exports[`XYChart component annotations should render grouped line annotations with default styles 1`] = ` `; -exports[`XYChart component annotations should render simplified annotation when hide is true 1`] = ` +exports[`XYChart component annotations should render simplified annotations when hide is true 1`] = ` - } - markerBody={ - - } markerPosition="top" style={ Object { @@ -213,6 +236,50 @@ exports[`XYChart component annotations should render simplified annotation when /> `; +exports[`XYChart component annotations should render simplified annotations when hide is true 2`] = ` +Array [ + , + , +] +`; + exports[`XYChart component it renders area 1`] = ` - - `; @@ -457,6 +831,14 @@ exports[`XYChart component it renders bar 1`] = ` ariaUseDefaultSummary={true} baseTheme={Object {}} debugState={false} + externalPointerEvents={ + Object { + "tooltip": Object { + "placement": "right", + "visible": false, + }, + } + } legendAction={ Object { "$$typeof": Symbol(react.memo), @@ -465,6 +847,7 @@ exports[`XYChart component it renders bar 1`] = ` } } legendPosition="top" + legendSize={130} onBrushEnd={[Function]} onElementClick={[Function]} onPointerUpdate={[Function]} @@ -558,139 +941,425 @@ exports[`XYChart component it renders bar 1`] = ` darkMode={false} histogramMode={false} /> - - `; @@ -704,6 +1373,14 @@ exports[`XYChart component it renders horizontal bar 1`] = ` ariaUseDefaultSummary={true} baseTheme={Object {}} debugState={false} + externalPointerEvents={ + Object { + "tooltip": Object { + "placement": "right", + "visible": false, + }, + } + } legendAction={ Object { "$$typeof": Symbol(react.memo), @@ -712,6 +1389,7 @@ exports[`XYChart component it renders horizontal bar 1`] = ` } } legendPosition="top" + legendSize={130} onBrushEnd={[Function]} onElementClick={[Function]} onPointerUpdate={[Function]} @@ -805,139 +1483,425 @@ exports[`XYChart component it renders horizontal bar 1`] = ` darkMode={false} histogramMode={false} /> - - `; @@ -951,6 +1915,14 @@ exports[`XYChart component it renders line 1`] = ` ariaUseDefaultSummary={true} baseTheme={Object {}} debugState={false} + externalPointerEvents={ + Object { + "tooltip": Object { + "placement": "right", + "visible": false, + }, + } + } legendAction={ Object { "$$typeof": Symbol(react.memo), @@ -959,6 +1931,7 @@ exports[`XYChart component it renders line 1`] = ` } } legendPosition="top" + legendSize={130} onBrushEnd={[Function]} onElementClick={[Function]} onPointerUpdate={[Function]} @@ -1052,127 +2025,425 @@ exports[`XYChart component it renders line 1`] = ` darkMode={false} histogramMode={false} /> - - `; @@ -1186,6 +2457,14 @@ exports[`XYChart component it renders stacked area 1`] = ` ariaUseDefaultSummary={true} baseTheme={Object {}} debugState={false} + externalPointerEvents={ + Object { + "tooltip": Object { + "placement": "right", + "visible": false, + }, + } + } legendAction={ Object { "$$typeof": Symbol(react.memo), @@ -1194,6 +2473,7 @@ exports[`XYChart component it renders stacked area 1`] = ` } } legendPosition="top" + legendSize={130} onBrushEnd={[Function]} onElementClick={[Function]} onPointerUpdate={[Function]} @@ -1287,135 +2567,425 @@ exports[`XYChart component it renders stacked area 1`] = ` darkMode={false} histogramMode={false} /> - - `; @@ -1429,6 +2999,14 @@ exports[`XYChart component it renders stacked bar 1`] = ` ariaUseDefaultSummary={true} baseTheme={Object {}} debugState={false} + externalPointerEvents={ + Object { + "tooltip": Object { + "placement": "right", + "visible": false, + }, + } + } legendAction={ Object { "$$typeof": Symbol(react.memo), @@ -1437,6 +3015,7 @@ exports[`XYChart component it renders stacked bar 1`] = ` } } legendPosition="top" + legendSize={130} onBrushEnd={[Function]} onElementClick={[Function]} onPointerUpdate={[Function]} @@ -1530,147 +3109,425 @@ exports[`XYChart component it renders stacked bar 1`] = ` darkMode={false} histogramMode={false} /> - - `; @@ -1684,6 +3541,14 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` ariaUseDefaultSummary={true} baseTheme={Object {}} debugState={false} + externalPointerEvents={ + Object { + "tooltip": Object { + "placement": "right", + "visible": false, + }, + } + } legendAction={ Object { "$$typeof": Symbol(react.memo), @@ -1692,6 +3557,7 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` } } legendPosition="top" + legendSize={130} onBrushEnd={[Function]} onElementClick={[Function]} onPointerUpdate={[Function]} @@ -1785,147 +3651,425 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` darkMode={false} histogramMode={false} /> - - `; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/annotations.scss b/src/plugins/chart_expressions/expression_xy/public/components/annotations.scss index 88881ae718925..e1e58252b5de7 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/annotations.scss +++ b/src/plugins/chart_expressions/expression_xy/public/components/annotations.scss @@ -16,3 +16,7 @@ transform: rotate(45deg); transform-origin: center; } + +.xyAnnotationTooltipDetail { + padding: $euiSizeXS ($euiSizeXS * 2); +} \ No newline at end of file diff --git a/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx index 976bec6678fe5..fa2c081f08700 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx @@ -16,13 +16,25 @@ import { AnnotationTooltipFormatter, LineAnnotation, Position, + RectAnnotation, } from '@elastic/charts'; import moment from 'moment'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import type { EventAnnotationArgs } from '@kbn/event-annotation-plugin/common'; +import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import type { + ManualPointEventAnnotationArgs, + ManualRangeEventAnnotationOutput, +} from '@kbn/event-annotation-plugin/common'; import type { FieldFormat } from '@kbn/field-formats-plugin/common'; -import { defaultAnnotationColor } from '@kbn/event-annotation-plugin/public'; -import type { AnnotationLayerArgs, AnnotationLayerConfigResult } from '../../common/types'; +import { + defaultAnnotationColor, + defaultAnnotationRangeColor, +} from '@kbn/event-annotation-plugin/public'; +import type { + AnnotationLayerArgs, + CommonXYAnnotationLayerConfig, + CollectiveConfig, +} from '../../common'; + import { AnnotationIcon, hasIcon, Marker, MarkerBody } from '../helpers'; import { mapVerticalToHorizontalPlacement, LINES_MARKER_SIZE } from '../helpers'; @@ -34,19 +46,15 @@ const getRoundedTimestamp = (timestamp: number, firstTimestamp?: number, minInte }; export interface AnnotationsProps { - groupedAnnotations: CollectiveConfig[]; + groupedLineAnnotations: CollectiveConfig[]; + rangeAnnotations: ManualRangeEventAnnotationOutput[]; formatter?: FieldFormat; isHorizontal: boolean; paddingMap: Partial>; hide?: boolean; minInterval?: number; isBarChart?: boolean; -} - -interface CollectiveConfig extends EventAnnotationArgs { - roundedTimestamp: number; - axisMode: 'bottom'; - customTooltipDetails?: AnnotationTooltipFormatter | undefined; + outsideDimension: number; } const groupVisibleConfigsByInterval = ( @@ -55,9 +63,11 @@ const groupVisibleConfigsByInterval = ( firstTimestamp?: number ) => { return layers - .flatMap(({ annotations }) => annotations.filter((a) => !a.isHidden)) + .flatMap(({ annotations }) => + annotations.filter((a) => !a.isHidden && a.type === 'manual_point_event_annotation') + ) .sort((a, b) => moment(a.time).valueOf() - moment(b.time).valueOf()) - .reduce>((acc, current) => { + .reduce>((acc, current) => { const roundedTimestamp = getRoundedTimestamp( moment(current.time).valueOf(), firstTimestamp, @@ -72,7 +82,7 @@ const groupVisibleConfigsByInterval = ( const createCustomTooltipDetails = ( - config: EventAnnotationArgs[], + config: ManualPointEventAnnotationArgs[], formatter?: FieldFormat ): AnnotationTooltipFormatter | undefined => () => { @@ -95,8 +105,8 @@ const createCustomTooltipDetails = ); }; -function getCommonProperty( - configArr: EventAnnotationArgs[], +function getCommonProperty( + configArr: ManualPointEventAnnotationArgs[], propertyName: K, fallbackValue: T ) { @@ -107,9 +117,9 @@ function getCommonProperty( return fallbackValue; } -const getCommonStyles = (configArr: EventAnnotationArgs[]) => { +const getCommonStyles = (configArr: ManualPointEventAnnotationArgs[]) => { return { - color: getCommonProperty( + color: getCommonProperty( configArr, 'color', defaultAnnotationColor @@ -120,8 +130,22 @@ const getCommonStyles = (configArr: EventAnnotationArgs[]) => { }; }; +export const getRangeAnnotations = (layers: CommonXYAnnotationLayerConfig[]) => { + return layers + .flatMap(({ annotations }) => + annotations.filter( + (a): a is ManualRangeEventAnnotationOutput => + a.type === 'manual_range_event_annotation' && !a.isHidden + ) + ) + .sort((a, b) => moment(a.time).valueOf() - moment(b.time).valueOf()); +}; + +export const OUTSIDE_RECT_ANNOTATION_WIDTH = 8; +export const OUTSIDE_RECT_ANNOTATION_WIDTH_SUGGESTION = 2; + export const getAnnotationsGroupedByInterval = ( - layers: AnnotationLayerConfigResult[], + layers: CommonXYAnnotationLayerConfig[], minInterval?: number, firstTimestamp?: number, formatter?: FieldFormat @@ -147,18 +171,23 @@ export const getAnnotationsGroupedByInterval = ( }); }; +// todo: remove when closed https://github.com/elastic/elastic-charts/issues/1647 +RectAnnotation.displayName = 'RectAnnotation'; + export const Annotations = ({ - groupedAnnotations, + groupedLineAnnotations, + rangeAnnotations, formatter, isHorizontal, paddingMap, hide, minInterval, isBarChart, + outsideDimension, }: AnnotationsProps) => { return ( <> - {groupedAnnotations.map((annotation) => { + {groupedLineAnnotations.map((annotation) => { const markerPositionVertical = Position.Top; const markerPosition = isHorizontal ? mapVerticalToHorizontalPlacement(markerPositionVertical) @@ -229,6 +258,40 @@ export const Annotations = ({ /> ); })} + {rangeAnnotations.map(({ label, time, color, endTime, outside }) => { + const id = snakeCase(label); + + return ( + ( +
+ +

+ {formatter + ? `${formatter.convert(time)} — ${formatter?.convert(endTime)}` + : `${moment(time).toISOString()} — ${moment(endTime).toISOString()}`} +

+
+
{label}
+
+ )} + dataValues={[ + { + coordinates: { + x0: moment(time).valueOf(), + x1: moment(endTime).valueOf(), + }, + details: label, + }, + ]} + style={{ fill: color || defaultAnnotationRangeColor, opacity: 1 }} + outside={Boolean(outside)} + outsideDimension={outsideDimension} + /> + ); + })} ); }; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx new file mode 100644 index 0000000000000..1166d41a9e402 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx @@ -0,0 +1,166 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { + AreaSeries, + BarSeries, + CurveType, + LabelOverflowConstraint, + LineSeries, +} from '@elastic/charts'; +import React, { FC } from 'react'; +import { PaletteRegistry } from '@kbn/coloring'; +import { FormatFactory } from '@kbn/field-formats-plugin/common'; +import { + CommonXYDataLayerConfig, + EndValue, + FittingFunction, + ValueLabelMode, + XYCurveType, +} from '../../common'; +import { SeriesTypes, ValueLabelModes } from '../../common/constants'; +import { + getColorAssignments, + getFitOptions, + GroupsConfiguration, + getSeriesProps, + DatatablesWithFormatInfo, +} from '../helpers'; + +interface Props { + layers: CommonXYDataLayerConfig[]; + formatFactory: FormatFactory; + chartHasMoreThanOneBarSeries?: boolean; + yAxesConfiguration: GroupsConfiguration; + curveType?: XYCurveType; + fittingFunction?: FittingFunction; + endValue?: EndValue | undefined; + paletteService: PaletteRegistry; + formattedDatatables: DatatablesWithFormatInfo; + syncColors?: boolean; + timeZone?: string; + emphasizeFitting?: boolean; + fillOpacity?: number; + shouldShowValueLabels?: boolean; + valueLabels: ValueLabelMode; +} + +export const DataLayers: FC = ({ + layers, + endValue, + timeZone, + curveType, + syncColors, + valueLabels, + fillOpacity, + formatFactory, + paletteService, + fittingFunction, + emphasizeFitting, + yAxesConfiguration, + shouldShowValueLabels, + formattedDatatables, + chartHasMoreThanOneBarSeries, +}) => { + const colorAssignments = getColorAssignments(layers, formatFactory); + return ( + <> + {layers.flatMap((layer) => + layer.accessors.map((accessor, accessorIndex) => { + const { seriesType, columnToLabel, layerId } = layer; + const columnToLabelMap: Record = columnToLabel + ? JSON.parse(columnToLabel) + : {}; + + // what if row values are not primitive? That is the case of, for instance, Ranges + // remaps them to their serialized version with the formatHint metadata + // In order to do it we need to make a copy of the table as the raw one is required for more features (filters, etc...) later on + const formattedDatatableInfo = formattedDatatables[layerId]; + + const isPercentage = seriesType.includes('percentage'); + + const yAxis = yAxesConfiguration.find((axisConfiguration) => + axisConfiguration.series.find((currentSeries) => currentSeries.accessor === accessor) + ); + + const seriesProps = getSeriesProps({ + layer, + accessor, + chartHasMoreThanOneBarSeries, + colorAssignments, + formatFactory, + columnToLabelMap, + paletteService, + formattedDatatableInfo, + syncColors, + yAxis, + timeZone, + emphasizeFitting, + fillOpacity, + }); + + const index = `${layer.layerId}-${accessorIndex}`; + + const curve = curveType ? CurveType[curveType] : undefined; + + switch (seriesType) { + case SeriesTypes.LINE: + return ( + + ); + case SeriesTypes.BAR: + case SeriesTypes.BAR_STACKED: + case SeriesTypes.BAR_PERCENTAGE_STACKED: + case SeriesTypes.BAR_HORIZONTAL: + case SeriesTypes.BAR_HORIZONTAL_STACKED: + case SeriesTypes.BAR_HORIZONTAL_PERCENTAGE_STACKED: + const valueLabelsSettings = { + displayValueSettings: { + // This format double fixes two issues in elastic-chart + // * when rotating the chart, the formatter is not correctly picked + // * in some scenarios value labels are not strings, and this breaks the elastic-chart lib + valueFormatter: (d: unknown) => yAxis?.formatter?.convert(d) || '', + showValueLabel: shouldShowValueLabels && valueLabels !== ValueLabelModes.HIDE, + isValueContainedInElement: false, + isAlternatingValueLabel: false, + overflowConstraints: [ + LabelOverflowConstraint.ChartEdges, + LabelOverflowConstraint.BarGeometry, + ], + }, + }; + return ; + case SeriesTypes.AREA_STACKED: + case SeriesTypes.AREA_PERCENTAGE_STACKED: + return ( + + ); + case SeriesTypes.AREA: + return ( + + ); + } + }) + )} + + ); +}; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx index 0f1cdebc5bf59..78ac1ed8d10cf 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx @@ -7,172 +7,171 @@ */ import React from 'react'; +import { Datatable } from '@kbn/expressions-plugin/common'; import { LegendActionProps, SeriesIdentifier } from '@elastic/charts'; import { EuiPopover } from '@elastic/eui'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { ComponentType, ReactWrapper } from 'enzyme'; -import type { LensMultiTable } from '../../common'; +import type { DataLayerConfig } from '../../common'; import { LayerTypes } from '../../common/constants'; -import type { DataLayerArgs } from '../../common'; import { getLegendAction } from './legend_action'; import { LegendActionPopover } from './legend_action_popover'; import { mockPaletteOutput } from '../../common/__mocks__'; -const sampleLayer = { - layerId: 'first', - layerType: LayerTypes.DATA, - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'splitAccessorId', - columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, -} as DataLayerArgs; - -const tables = { - first: { - type: 'datatable', - rows: [ - { - xAccessorId: 1585758120000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585758360000, - splitAccessorId: "Women's Accessories", - yAccessorId: 1, - }, - { - xAccessorId: 1585758360000, - splitAccessorId: "Women's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585759380000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585759380000, - splitAccessorId: "Men's Shoes", - yAccessorId: 1, - }, - { - xAccessorId: 1585759380000, - splitAccessorId: "Women's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585760700000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585760760000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585760760000, - splitAccessorId: "Men's Shoes", - yAccessorId: 1, - }, - { - xAccessorId: 1585761120000, - splitAccessorId: "Men's Shoes", - yAccessorId: 1, - }, - ], - columns: [ - { - id: 'xAccessorId', - name: 'order_date per minute', - meta: { - type: 'date', - field: 'order_date', - source: 'esaggs', - index: 'indexPatternId', - sourceParams: { - indexPatternId: 'indexPatternId', - type: 'date_histogram', - params: { - field: 'order_date', - timeRange: { from: '2020-04-01T16:14:16.246Z', to: '2020-04-01T17:15:41.263Z' }, - useNormalizedEsInterval: true, - scaleMetricValues: false, - interval: '1m', - drop_partials: false, - min_doc_count: 0, - extended_bounds: {}, - }, +const table: Datatable = { + type: 'datatable', + rows: [ + { + xAccessorId: 1585758120000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585758360000, + splitAccessorId: "Women's Accessories", + yAccessorId: 1, + }, + { + xAccessorId: 1585758360000, + splitAccessorId: "Women's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Women's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585760700000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585760760000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585760760000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + }, + { + xAccessorId: 1585761120000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + }, + ], + columns: [ + { + id: 'xAccessorId', + name: 'order_date per minute', + meta: { + type: 'date', + field: 'order_date', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'date_histogram', + params: { + field: 'order_date', + timeRange: { from: '2020-04-01T16:14:16.246Z', to: '2020-04-01T17:15:41.263Z' }, + useNormalizedEsInterval: true, + scaleMetricValues: false, + interval: '1m', + drop_partials: false, + min_doc_count: 0, + extended_bounds: {}, }, - params: { id: 'date', params: { pattern: 'HH:mm' } }, }, + params: { id: 'date', params: { pattern: 'HH:mm' } }, }, - { - id: 'splitAccessorId', - name: 'Top values of category.keyword', - meta: { - type: 'string', - field: 'category.keyword', - source: 'esaggs', - index: 'indexPatternId', - sourceParams: { - indexPatternId: 'indexPatternId', - type: 'terms', - params: { - field: 'category.keyword', - orderBy: 'yAccessorId', - order: 'desc', - size: 3, - otherBucket: false, - otherBucketLabel: 'Other', - missingBucket: false, - missingBucketLabel: 'Missing', - }, + }, + { + id: 'splitAccessorId', + name: 'Top values of category.keyword', + meta: { + type: 'string', + field: 'category.keyword', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'terms', + params: { + field: 'category.keyword', + orderBy: 'yAccessorId', + order: 'desc', + size: 3, + otherBucket: false, + otherBucketLabel: 'Other', + missingBucket: false, + missingBucketLabel: 'Missing', }, + }, + params: { + id: 'terms', params: { - id: 'terms', - params: { - id: 'string', - otherBucketLabel: 'Other', - missingBucketLabel: 'Missing', - parsedUrl: { - origin: 'http://localhost:5601', - pathname: '/jiy/app/kibana', - basePath: '/jiy', - }, + id: 'string', + otherBucketLabel: 'Other', + missingBucketLabel: 'Missing', + parsedUrl: { + origin: 'http://localhost:5601', + pathname: '/jiy/app/kibana', + basePath: '/jiy', }, }, }, }, - { - id: 'yAccessorId', - name: 'Count of records', - meta: { - type: 'number', - source: 'esaggs', - index: 'indexPatternId', - sourceParams: { - indexPatternId: 'indexPatternId', - params: {}, - }, - params: { id: 'number' }, + }, + { + id: 'yAccessorId', + name: 'Count of records', + meta: { + type: 'number', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + params: {}, }, + params: { id: 'number' }, }, - ], - }, -} as LensMultiTable['tables']; + }, + ], +}; + +const sampleLayer: DataLayerConfig = { + layerId: 'first', + type: 'dataLayer', + layerType: LayerTypes.DATA, + seriesType: 'line', + xAccessor: 'c', + accessors: ['a', 'b'], + splitAccessor: 'splitAccessorId', + columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', + xScaleType: 'ordinal', + yScaleType: 'linear', + isHistogram: false, + palette: mockPaletteOutput, + table, +}; describe('getLegendAction', function () { let wrapperProps: LegendActionProps; const Component: ComponentType = getLegendAction( [sampleLayer], - tables, jest.fn(), jest.fn(), {} @@ -228,7 +227,7 @@ describe('getLegendAction', function () { { column: 1, row: 1, - table: tables.first, + table, value: "Women's Accessories", }, ], diff --git a/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx index 9bbdec3635fa8..da1939f223649 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx @@ -9,23 +9,28 @@ import React from 'react'; import type { LegendAction, XYChartSeriesIdentifier } from '@elastic/charts'; import type { FilterEvent } from '../types'; -import type { LensMultiTable, DataLayerArgs } from '../../common'; +import type { CommonXYDataLayerConfig } from '../../common'; import type { FormatFactory } from '../types'; import { LegendActionPopover } from './legend_action_popover'; +import { DatatablesWithFormatInfo } from '../helpers'; export const getLegendAction = ( - filteredLayers: DataLayerArgs[], - tables: LensMultiTable['tables'], + dataLayers: CommonXYDataLayerConfig[], onFilter: (data: FilterEvent['data']) => void, formatFactory: FormatFactory, - layersAlreadyFormatted: Record + formattedDatatables: DatatablesWithFormatInfo ): LegendAction => React.memo(({ series: [xySeries] }) => { const series = xySeries as XYChartSeriesIdentifier; - const layer = filteredLayers.find((l) => + const layerIndex = dataLayers.findIndex((l) => series.seriesKeys.some((key: string | number) => l.accessors.includes(key.toString())) ); + if (layerIndex === -1) { + return null; + } + + const layer = dataLayers[layerIndex]; if (!layer || !layer.splitAccessor) { return null; } @@ -33,12 +38,12 @@ export const getLegendAction = ( const splitLabel = series.seriesKeys[0] as string; const accessor = layer.splitAccessor; - const table = tables[layer.layerId]; + const { table } = layer; const splitColumn = table.columns.find(({ id }) => id === layer.splitAccessor); const formatter = formatFactory(splitColumn && splitColumn.meta?.params); const rowIndex = table.rows.findIndex((row) => { - if (layersAlreadyFormatted[accessor]) { + if (formattedDatatables[layer.layerId]?.formattedColumns[accessor]) { // stringify the value to compare with the chart value return formatter.convert(row[accessor]) === splitLabel; } @@ -63,7 +68,7 @@ export const getLegendAction = ( return ( = { @@ -23,34 +28,28 @@ const row: Record = { yAccessorRightSecondId: 10, }; -const histogramData: LensMultiTable = { - type: 'lens_multitable', - tables: { - firstLayer: { - type: 'datatable', - rows: [row], - columns: Object.keys(row).map((id) => ({ - id, - name: `Static value: ${row[id]}`, - meta: { - type: 'number', - params: { id: 'number' }, - }, - })), +const data: Datatable = { + type: 'datatable', + rows: [row], + columns: Object.keys(row).map((id) => ({ + id, + name: `Static value: ${row[id]}`, + meta: { + type: 'number', + params: { id: 'number' }, }, - }, - dateRange: { - fromDate: new Date('2020-04-01T16:14:16.246Z'), - toDate: new Date('2020-04-01T17:15:41.263Z'), - }, + })), }; -function createLayers(yConfigs: ReferenceLineLayerArgs['yConfig']): ReferenceLineLayerArgs[] { +function createLayers(yConfigs: ReferenceLineLayerArgs['yConfig']): ReferenceLineLayerConfig[] { return [ { - layerId: 'firstLayer', + layerId: 'first', accessors: (yConfigs || []).map(({ forAccessor }) => forAccessor), yConfig: yConfigs, + type: 'referenceLineLayer', + layerType: LayerTypes.REFERENCELINE, + table: data, }, ]; } @@ -64,7 +63,7 @@ interface XCoords { x1: number | undefined; } -function getAxisFromId(layerPrefix: string): YConfig['axisMode'] { +function getAxisFromId(layerPrefix: string): ExtendedYConfig['axisMode'] { return /left/i.test(layerPrefix) ? 'left' : /right/i.test(layerPrefix) ? 'right' : 'bottom'; } @@ -95,21 +94,20 @@ describe('ReferenceLineAnnotations', () => { ['yAccessorLeft', 'below'], ['yAccessorRight', 'above'], ['yAccessorRight', 'below'], - ] as Array<[string, YConfig['fill']]>)( + ] as Array<[string, ExtendedYConfig['fill']]>)( 'should render a RectAnnotation for a reference line with fill set: %s %s', (layerPrefix, fill) => { const axisMode = getAxisFromId(layerPrefix); const wrapper = shallow( @@ -135,19 +133,18 @@ describe('ReferenceLineAnnotations', () => { it.each([ ['xAccessor', 'above'], ['xAccessor', 'below'], - ] as Array<[string, YConfig['fill']]>)( + ] as Array<[string, ExtendedYConfig['fill']]>)( 'should render a RectAnnotation for a reference line with fill set: %s %s', (layerPrefix, fill) => { const wrapper = shallow( { ['yAccessorLeft', 'below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }], ['yAccessorRight', 'above', { y0: 5, y1: 10 }, { y0: 10, y1: undefined }], ['yAccessorRight', 'below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }], - ] as Array<[string, YConfig['fill'], YCoords, YCoords]>)( + ] as Array<[string, ExtendedYConfig['fill'], YCoords, YCoords]>)( 'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s', (layerPrefix, fill, coordsA, coordsB) => { const axisMode = getAxisFromId(layerPrefix); const wrapper = shallow( { it.each([ ['xAccessor', 'above', { x0: 1, x1: 2 }, { x0: 2, x1: undefined }], ['xAccessor', 'below', { x0: undefined, x1: 1 }, { x0: 1, x1: 2 }], - ] as Array<[string, YConfig['fill'], XCoords, XCoords]>)( + ] as Array<[string, ExtendedYConfig['fill'], XCoords, XCoords]>)( 'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s', (layerPrefix, fill, coordsA, coordsB) => { const wrapper = shallow( { const wrapper = shallow( @@ -326,27 +320,26 @@ describe('ReferenceLineAnnotations', () => { it.each([ ['above', { y0: 5, y1: 10 }, { y0: 10, y1: undefined }], ['below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }], - ] as Array<[YConfig['fill'], YCoords, YCoords]>)( + ] as Array<[ExtendedYConfig['fill'], YCoords, YCoords]>)( 'should be robust and works also for different axes when on same direction: 1x Left + 1x Right both %s', (fill, coordsA, coordsB) => { const wrapper = shallow( diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx index 6146cc379227d..d17dbf2a70ad1 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx @@ -13,8 +13,7 @@ import { groupBy } from 'lodash'; import { RectAnnotation, AnnotationDomainType, LineAnnotation, Position } from '@elastic/charts'; import { euiLightVars } from '@kbn/ui-theme'; import type { FieldFormat } from '@kbn/field-formats-plugin/common'; -import type { IconPosition, ReferenceLineLayerArgs, YAxisMode } from '../../common/types'; -import type { LensMultiTable } from '../../common/types'; +import type { CommonXYReferenceLineLayerConfig, IconPosition, YAxisMode } from '../../common/types'; import { LINES_MARKER_SIZE, mapVerticalToHorizontalPlacement, @@ -89,8 +88,7 @@ export function getBaseIconPlacement( } export interface ReferenceLineAnnotationsProps { - layers: ReferenceLineLayerArgs[]; - data: LensMultiTable; + layers: CommonXYReferenceLineLayerConfig[]; formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>; axesMap: Record<'left' | 'right', boolean>; isHorizontal: boolean; @@ -99,7 +97,6 @@ export interface ReferenceLineAnnotationsProps { export const ReferenceLineAnnotations = ({ layers, - data, formatters, axesMap, isHorizontal, @@ -111,11 +108,10 @@ export const ReferenceLineAnnotations = ({ if (!layer.yConfig) { return []; } - const { columnToLabel, yConfig: yConfigs, layerId } = layer; + const { columnToLabel, yConfig: yConfigs, table } = layer; const columnToLabelMap: Record = columnToLabel ? JSON.parse(columnToLabel) : {}; - const table = data.tables[layerId]; const row = table.rows[0]; @@ -194,8 +190,8 @@ export const ReferenceLineAnnotations = ({ annotations.push( ({ dataValue: row[yConfig.forAccessor], header: columnToLabelMap[yConfig.forAccessor], @@ -225,8 +221,8 @@ export const ReferenceLineAnnotations = ({ annotations.push( { const nextValue = shouldCheckNextReferenceLine ? row[groupedByDirection[yConfig.fill!][indexFromSameType + 1].forAccessor] diff --git a/src/plugins/chart_expressions/expression_xy/public/components/x_domain.tsx b/src/plugins/chart_expressions/expression_xy/public/components/x_domain.tsx index 257d299616946..78b6ef91926a8 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/x_domain.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/x_domain.tsx @@ -11,7 +11,7 @@ import React from 'react'; import moment from 'moment'; import { Endzones } from '@kbn/charts-plugin/public'; import { search } from '@kbn/data-plugin/public'; -import type { LensMultiTable, DataLayerArgs } from '../../common'; +import type { CommonXYDataLayerConfig } from '../../common'; export interface XDomain { min?: number; @@ -19,17 +19,16 @@ export interface XDomain { minInterval?: number; } -export const getAppliedTimeRange = (layers: DataLayerArgs[], data: LensMultiTable) => { - return Object.entries(data.tables) - .map(([tableId, table]) => { - const layer = layers.find((l) => l.layerId === tableId); - const xColumn = table.columns.find((col) => col.id === layer?.xAccessor); +export const getAppliedTimeRange = (layers: CommonXYDataLayerConfig[]) => { + return layers + .map(({ xAccessor, table }) => { + const xColumn = table.columns.find((col) => col.id === xAccessor); const timeRange = xColumn && search.aggs.getDateHistogramMetaDataByDatatableColumn(xColumn)?.timeRange; if (timeRange) { return { timeRange, - field: xColumn.meta.field, + field: xColumn?.meta.field, }; } }) @@ -37,13 +36,12 @@ export const getAppliedTimeRange = (layers: DataLayerArgs[], data: LensMultiTabl }; export const getXDomain = ( - layers: DataLayerArgs[], - data: LensMultiTable, + layers: CommonXYDataLayerConfig[], minInterval: number | undefined, isTimeViz: boolean, isHistogram: boolean ) => { - const appliedTimeRange = getAppliedTimeRange(layers, data)?.timeRange; + const appliedTimeRange = getAppliedTimeRange(layers)?.timeRange; const from = appliedTimeRange?.from; const to = appliedTimeRange?.to; const baseDomain = isTimeViz @@ -59,8 +57,8 @@ export const getXDomain = ( if (isHistogram && isFullyQualified(baseDomain)) { const xValues = uniq( layers - .flatMap((layer) => - data.tables[layer.layerId].rows.map((row) => row[layer.xAccessor!].valueOf() as number) + .flatMap(({ table, xAccessor }) => + table.rows.map((row) => row[xAccessor!].valueOf()) ) .sort() ); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index 6ac39b1c0f941..48f2393935413 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -9,13 +9,6 @@ import React from 'react'; import { mount, shallow } from 'enzyme'; import { mountWithIntl } from '@kbn/test-jest-helpers'; -import { - AnnotationLayerConfigResult, - DataLayerConfigResult, - LensMultiTable, - XYArgs, -} from '../../common'; -import { LayerTypes } from '../../common/constants'; import { AreaSeries, Axis, @@ -27,13 +20,19 @@ import { LineAnnotation, LineSeries, Position, + RectAnnotation, ScaleType, SeriesNameFn, Settings, VerticalAlignment, XYChartSeriesIdentifier, } from '@elastic/charts'; +import { Datatable } from '@kbn/expressions-plugin/common'; import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; +import { eventAnnotationServiceMock } from '@kbn/event-annotation-plugin/public/mocks'; +import { EventAnnotationOutput } from '@kbn/event-annotation-plugin/common'; +import { DataLayerConfig } from '../../common'; +import { LayerTypes } from '../../common/constants'; import { XyEndzones } from './x_domain'; import { chartsActiveCursorService, @@ -51,8 +50,14 @@ import { sampleLayer, } from '../../common/__mocks__'; import { XYChart, XYChartRenderProps } from './xy_chart'; -import { eventAnnotationServiceMock } from '@kbn/event-annotation-plugin/public/mocks'; -import { EventAnnotationOutput } from '@kbn/event-annotation-plugin/common'; +import { + CommonXYAnnotationLayerConfig, + ExtendedDataLayerConfig, + XYProps, +} from '../../common/types'; +import { DataLayers } from './data_layers'; +import { Annotations } from './annotations'; +import { LegendSize } from '@kbn/visualizations-plugin/common'; const onClickValue = jest.fn(); const onSelectRange = jest.fn(); @@ -62,45 +67,36 @@ describe('XYChart component', () => { let convertSpy: jest.Mock; let defaultProps: Omit; - const dataWithoutFormats: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - { id: 'd', name: 'd', meta: { type: 'string' } }, - ], - rows: [ - { a: 1, b: 2, c: 'I', d: 'Row 1' }, - { a: 1, b: 5, c: 'J', d: 'Row 2' }, - ], - }, - }, + const dataWithoutFormats: Datatable = { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + { id: 'd', name: 'd', meta: { type: 'string' } }, + ], + rows: [ + { a: 1, b: 2, c: 'I', d: 'Row 1' }, + { a: 1, b: 5, c: 'J', d: 'Row 2' }, + ], }; - const dataWithFormats: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - { id: 'd', name: 'd', meta: { type: 'string', params: { id: 'custom' } } }, - ], - rows: [ - { a: 1, b: 2, c: 'I', d: 'Row 1' }, - { a: 1, b: 5, c: 'J', d: 'Row 2' }, - ], - }, - }, + + const dataWithFormats: Datatable = { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + { id: 'd', name: 'd', meta: { type: 'string', params: { id: 'custom' } } }, + ], + rows: [ + { a: 1, b: 2, c: 'I', d: 'Row 1' }, + { a: 1, b: 5, c: 'J', d: 'Row 2' }, + ], }; - const getRenderedComponent = (data: LensMultiTable, args: XYArgs) => { - return shallow(); + const getRenderedComponent = (args: XYProps) => { + return shallow(); }; beforeEach(() => { @@ -119,34 +115,38 @@ describe('XYChart component', () => { onClickValue, onSelectRange, syncColors: false, + syncTooltips: false, useLegacyTimeAxis: false, eventAnnotationService: eventAnnotationServiceMock, }; }); test('it renders line', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( ); expect(component).toMatchSnapshot(); - expect(component.find(LineSeries)).toHaveLength(2); - expect(component.find(LineSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(LineSeries).at(1).prop('yAccessors')).toEqual(['b']); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + expect(lineSeries).toHaveLength(2); + expect(lineSeries.at(0).prop('yAccessors')).toEqual(['a']); + expect(lineSeries.at(1).prop('yAccessors')).toEqual(['b']); }); describe('date range', () => { - const timeSampleLayer: DataLayerConfigResult = { + const { data, args } = sampleArgs(); + + const timeSampleLayer: DataLayerConfig = { + layerId: 'timeLayer', type: 'dataLayer', - layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'c', @@ -157,56 +157,47 @@ describe('XYChart component', () => { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: { + ...data, + columns: data.columns.map((c) => + c.id !== 'c' + ? c + : { + ...c, + meta: { + type: 'date', + source: 'esaggs', + sourceParams: { + type: 'date_histogram', + params: {}, + appliedTimeRange: { + from: '2019-01-02T05:00:00.000Z', + to: '2019-01-03T05:00:00.000Z', + }, + }, + }, + } + ), + }, }; + const multiLayerArgs = createArgsWithLayers([ timeSampleLayer, - { - ...timeSampleLayer, - layerId: 'second', - seriesType: 'bar', - xScaleType: 'time', - }, + { ...timeSampleLayer, seriesType: 'bar', xScaleType: 'time' }, ]); - test('it uses the full date range', () => { - const { data, args } = sampleArgs(); + test('it uses the full date range', () => { const component = shallow( - c.id !== 'c' - ? c - : { - ...c, - meta: { - type: 'date', - source: 'esaggs', - sourceParams: { - type: 'date_histogram', - params: {}, - appliedTimeRange: { - from: '2019-01-02T05:00:00.000Z', - to: '2019-01-03T05:00:00.000Z', - }, - }, - }, - } - ), - }, - }, - }} args={{ ...args, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...(args.layers[0] as DataLayerConfig), seriesType: 'line', xScaleType: 'time', + table: timeSampleLayer.table, }, ], }} @@ -223,15 +214,21 @@ describe('XYChart component', () => { }); test('it uses passed in minInterval', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([{ a: 1, b: 2, c: 'I', d: 'Foo' }]), - second: createSampleDatatableWithRows([]), - }, - }; + const table1 = createSampleDatatableWithRows([{ a: 1, b: 2, c: 'I', d: 'Foo' }]); + const table2 = createSampleDatatableWithRows([]); - const component = shallow(); + const component = shallow( + + ); // real auto interval is 30mins = 1800000 expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` @@ -244,9 +241,9 @@ describe('XYChart component', () => { }); describe('axis time', () => { - const defaultTimeLayer: DataLayerConfigResult = { + const defaultTimeLayer: DataLayerConfig = { + layerId: 'defaultTimeLayer', type: 'dataLayer', - layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'c', @@ -257,21 +254,28 @@ describe('XYChart component', () => { yScaleType: 'linear', isHistogram: true, palette: mockPaletteOutput, + table: data, + }; + + const newData = { + ...data, + dateRange: { + fromDate: new Date('2019-01-02T05:00:00.000Z'), + toDate: new Date('2019-01-03T05:00:00.000Z'), + }, }; - test('it should disable the new time axis for a line time layer when isHistogram is set to false', () => { - const { data } = sampleArgs(); + test('it should disable the new time axis for a line time layer when isHistogram is set to false', () => { const instance = shallow( ({ + ...layer, + table: newData, + })), }} - args={multiLayerArgs} /> ); @@ -280,20 +284,18 @@ describe('XYChart component', () => { expect(axisStyle).toBe(0); }); test('it should enable the new time axis for a line time layer when isHistogram is set to true', () => { - const { data } = sampleArgs(); const timeLayerArgs = createArgsWithLayers([defaultTimeLayer]); const instance = shallow( ({ + ...layer, + table: newData, + })), }} - args={timeLayerArgs} /> ); @@ -302,8 +304,7 @@ describe('XYChart component', () => { expect(axisStyle).toBe(3); }); test('it should disable the new time axis for a vertical bar with break down dimension', () => { - const { data } = sampleArgs(); - const timeLayer: DataLayerConfigResult = { + const timeLayer: DataLayerConfig = { ...defaultTimeLayer, seriesType: 'bar', }; @@ -312,14 +313,13 @@ describe('XYChart component', () => { const instance = shallow( ({ + ...layer, + table: newData, + })), }} - args={timeLayerArgs} /> ); @@ -329,8 +329,7 @@ describe('XYChart component', () => { }); test('it should enable the new time axis for a stacked vertical bar with break down dimension', () => { - const { data } = sampleArgs(); - const timeLayer: DataLayerConfigResult = { + const timeLayer: DataLayerConfig = { ...defaultTimeLayer, seriesType: 'bar_stacked', }; @@ -339,14 +338,13 @@ describe('XYChart component', () => { const instance = shallow( ({ + ...layer, + table: newData, + })), }} - args={timeLayerArgs} /> ); @@ -356,65 +354,53 @@ describe('XYChart component', () => { }); }); describe('endzones', () => { - const { args } = sampleArgs(); const table = createSampleDatatableWithRows([ { a: 1, b: 2, c: new Date('2021-04-22').valueOf(), d: 'Foo' }, { a: 1, b: 2, c: new Date('2021-04-23').valueOf(), d: 'Foo' }, { a: 1, b: 2, c: new Date('2021-04-24').valueOf(), d: 'Foo' }, ]); - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - ...table, - columns: table.columns.map((c) => - c.id !== 'c' - ? c - : { - ...c, - meta: { - type: 'date', - source: 'esaggs', - sourceParams: { - type: 'date_histogram', - params: {}, - appliedTimeRange: { - from: '2021-04-22T12:00:00.000Z', - to: '2021-04-24T12:00:00.000Z', - }, - }, + const newData = { + ...table, + type: 'datatable', + + columns: table.columns.map((c) => + c.id !== 'c' + ? c + : { + ...c, + meta: { + type: 'date', + source: 'esaggs', + sourceParams: { + type: 'date_histogram', + params: {}, + appliedTimeRange: { + from: '2021-04-22T12:00:00.000Z', + to: '2021-04-24T12:00:00.000Z', }, - } - ), - }, - }, - dateRange: { - // first and last bucket are partial - fromDate: new Date('2021-04-22T12:00:00.000Z'), - toDate: new Date('2021-04-24T12:00:00.000Z'), - }, + }, + }, + } + ), }; - const timeArgs: XYArgs = { + const timeArgs: XYProps = { ...args, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...args.layers[0], + type: 'dataLayer', seriesType: 'line', xScaleType: 'time', isHistogram: true, splitAccessor: undefined, - }, + table: newData, + } as DataLayerConfig, ], }; test('it extends interval if data is exceeding it', () => { const component = shallow( - + ); expect(component.find(Settings).prop('xDomain')).toEqual({ @@ -426,14 +412,17 @@ describe('XYChart component', () => { }); }); + const defaultTimeArgs = { + ...timeArgs, + layers: timeArgs.layers.map((layer) => ({ + ...layer, + table: data, + })), + }; + test('it renders endzone component bridging gap between domain and extended domain', () => { const component = shallow( - + ); expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual( @@ -448,12 +437,7 @@ describe('XYChart component', () => { test('should pass enabled histogram mode and min interval to endzones component', () => { const component = shallow( - + ); expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual( @@ -469,12 +453,11 @@ describe('XYChart component', () => { { ); @@ -508,13 +490,12 @@ describe('XYChart component', () => { }); describe('y axis extents', () => { - test('it passes custom y axis extents to elastic-charts axis spec', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); + test('it passes custom y axis extents to elastic-charts axis spec', () => { const component = shallow( { }); test('it passes fit to bounds y axis extents to elastic-charts axis spec', () => { - const { data, args } = sampleArgs(); - const component = shallow( { }); test('it does not allow fit for area chart', () => { - const { data, args } = sampleArgs(); - const component = shallow( { }, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...(args.layers[0] as DataLayerConfig), seriesType: 'area', }, ], @@ -589,12 +564,9 @@ describe('XYChart component', () => { }); test('it does not allow positive lower bound for bar', () => { - const { data, args } = sampleArgs(); - const component = shallow( { }, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...(args.layers[0] as DataLayerConfig), seriesType: 'bar', }, ], @@ -621,36 +593,29 @@ describe('XYChart component', () => { }); test('it does include referenceLine values when in full extent mode', () => { - const { data, args } = sampleArgsWithReferenceLine(); + const { args: refArgs } = sampleArgsWithReferenceLine(); - const component = shallow(); + const component = shallow(); expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ fit: false, min: NaN, max: NaN, - includeDataFromIds: ['referenceLine-referenceLine-a-rect'], + includeDataFromIds: ['referenceLine-a-referenceLine-a-rect'], }); }); }); test('it has xDomain undefined if the x is not a time scale or a histogram', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( { }); test('it uses min interval if interval is passed in and visualization is histogram', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( { }); test('disabled legend extra by default', () => { - const { data, args } = sampleArgs(); - const component = shallow(); + const { args } = sampleArgs(); + const component = shallow(); expect(component.find(Settings).at(0).prop('showLegendExtra')).toEqual(false); }); test('ignores legend extra for ordinal chart', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( - + ); expect(component.find(Settings).at(0).prop('showLegendExtra')).toEqual(false); }); @@ -709,7 +673,6 @@ describe('XYChart component', () => { const component = shallow( { }); test('it renders bar', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( ); expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(BarSeries).at(1).prop('yAccessors')).toEqual(['b']); + + const barSeries = component.find(DataLayers).dive().find(BarSeries); + expect(barSeries).toHaveLength(2); + expect(barSeries.at(0).prop('yAccessors')).toEqual(['a']); + expect(barSeries.at(1).prop('yAccessors')).toEqual(['b']); }); test('it renders area', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( ); expect(component).toMatchSnapshot(); - expect(component.find(AreaSeries)).toHaveLength(2); - expect(component.find(AreaSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(AreaSeries).at(1).prop('yAccessors')).toEqual(['b']); + + const areaSeries = component.find(DataLayers).dive().find(AreaSeries); + expect(areaSeries).toHaveLength(2); + expect(areaSeries.at(0).prop('yAccessors')).toEqual(['a']); + expect(areaSeries.at(1).prop('yAccessors')).toEqual(['b']); }); test('it renders horizontal bar', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( ); expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(BarSeries).at(1).prop('yAccessors')).toEqual(['b']); + + const barSeries = component.find(DataLayers).dive().find(BarSeries); + expect(barSeries).toHaveLength(2); + expect(barSeries.at(0).prop('yAccessors')).toEqual(['a']); + expect(barSeries.at(1).prop('yAccessors')).toEqual(['b']); expect(component.find(Settings).prop('rotation')).toEqual(90); }); test('it renders regular bar empty placeholder for no results', () => { const { data, args } = sampleArgs(); - - // send empty data to the chart - data.tables.first.rows = []; - - const component = shallow(); + const component = shallow( + ({ ...layer, table: { ...data, rows: [] } })), + }} + /> + ); expect(component.find(BarSeries)).toHaveLength(0); expect(component.find(EmptyPlaceholder).prop('icon')).toBeDefined(); @@ -793,7 +763,6 @@ describe('XYChart component', () => { const wrapper = mountWithIntl( { expect(onSelectRange).toHaveBeenCalledWith({ column: 0, - table: dateHistogramData.tables.timeLayer, + table: dateHistogramData, range: [1585757732783, 1585758880838], }); }); @@ -812,9 +781,43 @@ describe('XYChart component', () => { test('onBrushEnd returns correct context data for number histogram data', () => { const { args } = sampleArgs(); - const numberLayer: DataLayerConfigResult = { - type: 'dataLayer', + const numberHistogramData: Datatable = { + type: 'datatable', + rows: [ + { + xAccessorId: 5, + yAccessorId: 1, + }, + { + xAccessorId: 7, + yAccessorId: 1, + }, + { + xAccessorId: 8, + yAccessorId: 1, + }, + { + xAccessorId: 10, + yAccessorId: 1, + }, + ], + columns: [ + { + id: 'xAccessorId', + name: 'bytes', + meta: { type: 'number' }, + }, + { + id: 'yAccessorId', + name: 'Count of records', + meta: { type: 'number' }, + }, + ], + }; + + const numberLayer: DataLayerConfig = { layerId: 'numberLayer', + type: 'dataLayer', layerType: LayerTypes.DATA, hide: false, xAccessor: 'xAccessorId', @@ -824,55 +827,12 @@ describe('XYChart component', () => { seriesType: 'bar_stacked', accessors: ['yAccessorId'], palette: mockPaletteOutput, - }; - - const numberHistogramData: LensMultiTable = { - type: 'lens_multitable', - tables: { - numberLayer: { - type: 'datatable', - rows: [ - { - xAccessorId: 5, - yAccessorId: 1, - }, - { - xAccessorId: 7, - yAccessorId: 1, - }, - { - xAccessorId: 8, - yAccessorId: 1, - }, - { - xAccessorId: 10, - yAccessorId: 1, - }, - ], - columns: [ - { - id: 'xAccessorId', - name: 'bytes', - meta: { type: 'number' }, - }, - { - id: 'yAccessorId', - name: 'Count of records', - meta: { type: 'number' }, - }, - ], - }, - }, - dateRange: { - fromDate: new Date('2020-04-01T16:14:16.246Z'), - toDate: new Date('2020-04-01T17:15:41.263Z'), - }, + table: numberHistogramData, }; const wrapper = mountWithIntl( { expect(onSelectRange).toHaveBeenCalledWith({ column: 0, - table: numberHistogramData.tables.numberLayer, + table: numberHistogramData, range: [5, 8], }); }); test('onBrushEnd is not set on non-interactive mode', () => { - const { args, data } = sampleArgs(); + const { args } = sampleArgs(); - const wrapper = mountWithIntl( - - ); + const wrapper = mountWithIntl(); expect(wrapper.find(Settings).first().prop('onBrushEnd')).toBeUndefined(); }); @@ -905,7 +863,6 @@ describe('XYChart component', () => { const wrapper = mountWithIntl( { const wrapper = mountWithIntl( { accessors: ['d'], columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', palette: mockPaletteOutput, + table: data, }, ], }} @@ -962,13 +919,13 @@ describe('XYChart component', () => { { column: 1, row: 1, - table: data.tables.first, + table: data, value: 5, }, { column: 1, row: 0, - table: data.tables.first, + table: data, value: 2, }, ], @@ -996,7 +953,6 @@ describe('XYChart component', () => { const wrapper = mountWithIntl( { { column: 0, row: 0, - table: dateHistogramData.tables.timeLayer, + table: dateHistogramData, value: 1585758120000, }, ], @@ -1023,9 +979,43 @@ describe('XYChart component', () => { test('onElementClick returns correct context data for numeric histogram', () => { const { args } = sampleArgs(); - const numberLayer: DataLayerConfigResult = { - type: 'dataLayer', + const numberHistogramData: Datatable = { + type: 'datatable', + rows: [ + { + xAccessorId: 5, + yAccessorId: 1, + }, + { + xAccessorId: 7, + yAccessorId: 1, + }, + { + xAccessorId: 8, + yAccessorId: 1, + }, + { + xAccessorId: 10, + yAccessorId: 1, + }, + ], + columns: [ + { + id: 'xAccessorId', + name: 'bytes', + meta: { type: 'number' }, + }, + { + id: 'yAccessorId', + name: 'Count of records', + meta: { type: 'number' }, + }, + ], + }; + + const numberLayer: DataLayerConfig = { layerId: 'numberLayer', + type: 'dataLayer', layerType: LayerTypes.DATA, hide: false, xAccessor: 'xAccessorId', @@ -1035,50 +1025,9 @@ describe('XYChart component', () => { seriesType: 'bar_stacked', accessors: ['yAccessorId'], palette: mockPaletteOutput, + table: numberHistogramData, }; - const numberHistogramData: LensMultiTable = { - type: 'lens_multitable', - tables: { - numberLayer: { - type: 'datatable', - rows: [ - { - xAccessorId: 5, - yAccessorId: 1, - }, - { - xAccessorId: 7, - yAccessorId: 1, - }, - { - xAccessorId: 8, - yAccessorId: 1, - }, - { - xAccessorId: 10, - yAccessorId: 1, - }, - ], - columns: [ - { - id: 'xAccessorId', - name: 'bytes', - meta: { type: 'number' }, - }, - { - id: 'yAccessorId', - name: 'Count of records', - meta: { type: 'number' }, - }, - ], - }, - }, - dateRange: { - fromDate: new Date('2020-04-01T16:14:16.246Z'), - toDate: new Date('2020-04-01T17:15:41.263Z'), - }, - }; const geometry: GeometryValue = { x: 5, y: 1, @@ -1097,7 +1046,6 @@ describe('XYChart component', () => { const wrapper = mountWithIntl( { { column: 0, row: 0, - table: numberHistogramData.tables.numberLayer, + table: numberHistogramData, value: 5, }, ], @@ -1139,13 +1087,12 @@ describe('XYChart component', () => { const wrapper = mountWithIntl( { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }, ], }} @@ -1170,7 +1118,7 @@ describe('XYChart component', () => { { column: 3, row: 1, - table: data.tables.first, + table: data, value: 'Bar', }, ], @@ -1179,21 +1127,31 @@ describe('XYChart component', () => { test('sets up correct yScaleType equal to binary_linear for bytes formatting', () => { const { args, data } = sampleArgs(); - data.tables.first.columns[0].meta = { - type: 'number', - params: { id: 'bytes', params: { pattern: '0,0.00b' } }, + + const [firstCol, ...rest] = data.columns; + const newData: Datatable = { + ...data, + columns: [ + { + ...firstCol, + meta: { + type: 'number', + params: { id: 'bytes', params: { pattern: '0,0.00b' } }, + }, + }, + ...rest, + ], }; const wrapper = mountWithIntl( { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: newData, }, ], }} @@ -1218,13 +1177,12 @@ describe('XYChart component', () => { const wrapper = mountWithIntl( { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }, ], }} @@ -1244,94 +1203,92 @@ describe('XYChart component', () => { }); test('onElementClick is not triggering event on non-interactive mode', () => { - const { args, data } = sampleArgs(); + const { args } = sampleArgs(); - const wrapper = mountWithIntl( - - ); + const wrapper = mountWithIntl(); expect(wrapper.find(Settings).first().prop('onElementClick')).toBeUndefined(); }); test('legendAction is not triggering event on non-interactive mode', () => { - const { args, data } = sampleArgs(); + const { args } = sampleArgs(); - const wrapper = mountWithIntl( - - ); + const wrapper = mountWithIntl(); expect(wrapper.find(Settings).first().prop('legendAction')).toBeUndefined(); }); test('it renders stacked bar', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( ); expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('stackAccessors')).toHaveLength(1); - expect(component.find(BarSeries).at(1).prop('stackAccessors')).toHaveLength(1); + + const barSeries = component.find(DataLayers).dive().find(BarSeries); + expect(barSeries).toHaveLength(2); + expect(barSeries.at(0).prop('stackAccessors')).toHaveLength(1); + expect(barSeries.at(1).prop('stackAccessors')).toHaveLength(1); }); test('it renders stacked area', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( ); expect(component).toMatchSnapshot(); - expect(component.find(AreaSeries)).toHaveLength(2); - expect(component.find(AreaSeries).at(0).prop('stackAccessors')).toHaveLength(1); - expect(component.find(AreaSeries).at(1).prop('stackAccessors')).toHaveLength(1); + + const areaSeries = component.find(DataLayers).dive().find(AreaSeries); + expect(areaSeries).toHaveLength(2); + expect(areaSeries.at(0).prop('stackAccessors')).toHaveLength(1); + expect(areaSeries.at(1).prop('stackAccessors')).toHaveLength(1); }); test('it renders stacked horizontal bar', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( ); expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('stackAccessors')).toHaveLength(1); - expect(component.find(BarSeries).at(1).prop('stackAccessors')).toHaveLength(1); + + const barSeries = component.find(DataLayers).dive().find(BarSeries); + expect(barSeries).toHaveLength(2); + expect(barSeries.at(0).prop('stackAccessors')).toHaveLength(1); + expect(barSeries.at(1).prop('stackAccessors')).toHaveLength(1); expect(component.find(Settings).prop('rotation')).toEqual(90); }); test('it renders stacked bar empty placeholder for no results', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( { /> ); - expect(component.find(BarSeries)).toHaveLength(0); + expect(component.find(DataLayers)).toHaveLength(0); expect(component.find(EmptyPlaceholder).prop('icon')).toBeDefined(); }); test('it passes time zone to the series', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component.find(LineSeries).at(0).prop('timeZone')).toEqual('CEST'); - expect(component.find(LineSeries).at(1).prop('timeZone')).toEqual('CEST'); + const { args } = sampleArgs(); + const component = shallow(); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + expect(lineSeries.at(0).prop('timeZone')).toEqual('CEST'); + expect(lineSeries.at(1).prop('timeZone')).toEqual('CEST'); }); test('it applies histogram mode to the series for single series', () => { - const { data, args } = sampleArgs(); - const firstLayer: DataLayerConfigResult = { + const { args } = sampleArgs(); + const firstLayer: DataLayerConfig = { ...args.layers[0], accessors: ['b'], seriesType: 'bar', isHistogram: true, - } as DataLayerConfigResult; + } as DataLayerConfig; delete firstLayer.splitAccessor; const component = shallow( - + ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); + expect( + component.find(DataLayers).dive().find(BarSeries).at(0).prop('enableHistogramMode') + ).toEqual(true); }); test('it does not apply histogram mode to more than one bar series for unstacked bar chart', () => { - const { data, args } = sampleArgs(); - const firstLayer: DataLayerConfigResult = { + const { args } = sampleArgs(); + const firstLayer: DataLayerConfig = { ...args.layers[0], seriesType: 'bar', isHistogram: true, - } as DataLayerConfigResult; + } as DataLayerConfig; delete firstLayer.splitAccessor; const component = shallow( - + ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false); - expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false); + + const barSeries = component.find(DataLayers).dive().find(BarSeries); + expect(barSeries.at(0).prop('enableHistogramMode')).toEqual(false); + expect(barSeries.at(1).prop('enableHistogramMode')).toEqual(false); }); test('it applies histogram mode to more than one the series for unstacked line/area chart', () => { - const { data, args } = sampleArgs(); - const firstLayer: DataLayerConfigResult = { + const { args } = sampleArgs(); + const firstLayer: DataLayerConfig = { ...args.layers[0], seriesType: 'line', isHistogram: true, - } as DataLayerConfigResult; + } as DataLayerConfig; delete firstLayer.splitAccessor; - const secondLayer: DataLayerConfigResult = { + const secondLayer: DataLayerConfig = { ...args.layers[0], seriesType: 'line', isHistogram: true, - } as DataLayerConfigResult; + } as DataLayerConfig; delete secondLayer.splitAccessor; const component = shallow( - + ); - expect(component.find(LineSeries).at(0).prop('enableHistogramMode')).toEqual(true); - expect(component.find(LineSeries).at(1).prop('enableHistogramMode')).toEqual(true); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + expect(lineSeries.at(0).prop('enableHistogramMode')).toEqual(true); + expect(lineSeries.at(1).prop('enableHistogramMode')).toEqual(true); }); test('it applies histogram mode to the series for stacked series', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( { }} /> ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); - expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(true); + + const barSeries = component.find(DataLayers).dive().find(BarSeries); + expect(barSeries.at(0).prop('enableHistogramMode')).toEqual(true); + expect(barSeries.at(1).prop('enableHistogramMode')).toEqual(true); }); test('it does not apply histogram mode for splitted series', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false); - expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false); + + const barSeries = component.find(DataLayers).dive().find(BarSeries); + expect(barSeries.at(0).prop('enableHistogramMode')).toEqual(false); + expect(barSeries.at(1).prop('enableHistogramMode')).toEqual(false); }); describe('y axes', () => { - test('single axis if possible', () => { - const args = createArgsWithLayers(); + const args = createArgsWithLayers(); + const layer = args.layers[0] as DataLayerConfig; - const component = getRenderedComponent(dataWithoutFormats, args); + test('single axis if possible', () => { + const newArgs = { + ...args, + layers: args.layers.map((l) => ({ + ...layer, + table: dataWithoutFormats, + })), + }; + const component = getRenderedComponent(newArgs); const axes = component.find(Axis); expect(axes).toHaveLength(2); }); test('multiple axes because of config', () => { - const args = createArgsWithLayers(); - const newArgs = { + const newArgs: XYProps = { ...args, layers: [ { - ...args.layers[0], + ...layer, accessors: ['a', 'b'], yConfig: [ { + type: 'yConfig', forAccessor: 'a', axisMode: 'left', }, { + type: 'yConfig', forAccessor: 'b', axisMode: 'right', }, ], + table: dataWithoutFormats, }, ], - } as XYArgs; + }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); + const component = getRenderedComponent(newArgs); const axes = component.find(Axis); expect(axes).toHaveLength(3); - expect(component.find(LineSeries).at(0).prop('groupId')).toEqual(axes.at(1).prop('groupId')); - expect(component.find(LineSeries).at(1).prop('groupId')).toEqual(axes.at(2).prop('groupId')); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + expect(lineSeries.at(0).prop('groupId')).toEqual(axes.at(1).prop('groupId')); + expect(lineSeries.at(1).prop('groupId')).toEqual(axes.at(2).prop('groupId')); }); test('multiple axes because of incompatible formatters', () => { - const args = createArgsWithLayers(); - const newArgs = { + const newArgs: XYProps = { ...args, layers: [ { - ...args.layers[0], + ...layer, accessors: ['c', 'd'], + table: dataWithFormats, }, ], - } as XYArgs; + }; - const component = getRenderedComponent(dataWithFormats, newArgs); + const component = getRenderedComponent(newArgs); const axes = component.find(Axis); expect(axes).toHaveLength(3); - expect(component.find(LineSeries).at(0).prop('groupId')).toEqual(axes.at(1).prop('groupId')); - expect(component.find(LineSeries).at(1).prop('groupId')).toEqual(axes.at(2).prop('groupId')); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + expect(lineSeries.at(0).prop('groupId')).toEqual(axes.at(1).prop('groupId')); + expect(lineSeries.at(1).prop('groupId')).toEqual(axes.at(2).prop('groupId')); }); test('single axis despite different formatters if enforced', () => { - const args = createArgsWithLayers(); - const newArgs = { + const newArgs: XYProps = { ...args, layers: [ { - ...args.layers[0], + ...layer, accessors: ['c', 'd'], yConfig: [ { + type: 'yConfig', forAccessor: 'c', axisMode: 'left', }, { + type: 'yConfig', forAccessor: 'd', axisMode: 'left', }, ], + table: dataWithoutFormats, }, ], - } as XYArgs; + }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); + const component = getRenderedComponent(newArgs); const axes = component.find(Axis); expect(axes).toHaveLength(2); }); }); describe('y series coloring', () => { + const args = createArgsWithLayers(); + const layer = args.layers[0] as DataLayerConfig; + test('color is applied to chart for multiple series', () => { - const args = createArgsWithLayers(); - const newArgs = { + const newArgs: XYProps = { ...args, layers: [ { - ...args.layers[0], - splitAccessor: undefined, + ...layer, + type: 'extendedDataLayer', accessors: ['a', 'b'], + splitAccessor: undefined, yConfig: [ { + type: 'yConfig', forAccessor: 'a', color: '#550000', }, { + type: 'yConfig', forAccessor: 'b', color: '#FFFF00', }, ], - }, + table: dataWithoutFormats, + } as ExtendedDataLayerConfig, { - ...args.layers[0], - splitAccessor: undefined, + ...layer, + type: 'extendedDataLayer', accessors: ['c'], + splitAccessor: undefined, yConfig: [ { + type: 'yConfig', forAccessor: 'c', color: '#FEECDF', }, ], - }, + table: dataWithoutFormats, + } as ExtendedDataLayerConfig, ], - } as XYArgs; + }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); + const component = getRenderedComponent(newArgs); + const lineSeries = component.find(DataLayers).dive().find(LineSeries); expect( - (component.find(LineSeries).at(0).prop('color') as Function)!({ + (lineSeries.at(0).prop('color') as Function)!({ yAccessor: 'a', seriesKeys: ['a'], }) ).toEqual('#550000'); expect( - (component.find(LineSeries).at(1).prop('color') as Function)!({ + (lineSeries.at(1).prop('color') as Function)!({ yAccessor: 'b', seriesKeys: ['b'], }) ).toEqual('#FFFF00'); expect( - (component.find(LineSeries).at(2).prop('color') as Function)!({ + (lineSeries.at(2).prop('color') as Function)!({ yAccessor: 'c', seriesKeys: ['c'], }) ).toEqual('#FEECDF'); }); test('color is not applied to chart when splitAccessor is defined or when yConfig is not configured', () => { - const args = createArgsWithLayers(); - const newArgs = { + const newArgs: XYProps = { ...args, layers: [ { - ...args.layers[0], + ...layer, accessors: ['a'], yConfig: [ { + type: 'yConfig', forAccessor: 'a', color: '#550000', }, ], + table: dataWithoutFormats, }, { - ...args.layers[0], - splitAccessor: undefined, + ...layer, accessors: ['c'], + table: dataWithoutFormats, }, ], - } as XYArgs; + }; + + const component = getRenderedComponent(newArgs); - const component = getRenderedComponent(dataWithoutFormats, newArgs); + const lineSeries = component.find(DataLayers).dive().find(LineSeries); expect( - (component.find(LineSeries).at(0).prop('color') as Function)!({ + (lineSeries.at(0).prop('color') as Function)!({ yAccessor: 'a', seriesKeys: ['a'], }) ).toEqual('blue'); expect( - (component.find(LineSeries).at(1).prop('color') as Function)!({ + (lineSeries.at(1).prop('color') as Function)!({ yAccessor: 'c', seriesKeys: ['c'], }) @@ -1647,16 +1637,21 @@ describe('XYChart component', () => { accessors: ['a'], splitAccessor: undefined, columnToLabel: '', + table: dataWithoutFormats, }, ], }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + const component = getRenderedComponent(newArgs); + const nameFn = component + .find(DataLayers) + .dive() + .find(LineSeries) + .prop('name') as SeriesNameFn; // In this case, the ID is used as the name. This shouldn't happen in practice - expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(''); - expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); + expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(null); + expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(null); }); test('simplest xy chart with empty name', () => { @@ -1669,16 +1664,21 @@ describe('XYChart component', () => { accessors: ['a'], splitAccessor: undefined, columnToLabel: '{"a":""}', + table: dataWithoutFormats, }, ], }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + const component = getRenderedComponent(newArgs); + const nameFn = component + .find(DataLayers) + .dive() + .find(LineSeries) + .prop('name') as SeriesNameFn; // In this case, the ID is used as the name. This shouldn't happen in practice expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(''); - expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); + expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(null); }); test('simplest xy chart with human-readable name', () => { @@ -1691,12 +1691,17 @@ describe('XYChart component', () => { accessors: ['a'], splitAccessor: undefined, columnToLabel: '{"a":"Column A"}', + table: dataWithoutFormats, }, ], }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + const component = getRenderedComponent(newArgs); + const nameFn = component + .find(DataLayers) + .dive() + .find(LineSeries) + .prop('name') as SeriesNameFn; expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual('Column A'); }); @@ -1711,19 +1716,22 @@ describe('XYChart component', () => { accessors: ['a', 'b'], splitAccessor: undefined, columnToLabel: '{"a": "Label A"}', + table: dataWithoutFormats, }, ], }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - const nameFn2 = component.find(LineSeries).at(1).prop('name') as SeriesNameFn; + const component = getRenderedComponent(newArgs); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + const nameFn1 = lineSeries.at(0).prop('name') as SeriesNameFn; + const nameFn2 = lineSeries.at(1).prop('name') as SeriesNameFn; // This accessor has a human-readable name expect(nameFn1({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual('Label A'); // This accessor does not - expect(nameFn2({ ...nameFnArgs, seriesKeys: ['b'] }, false)).toEqual(''); - expect(nameFn1({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); + expect(nameFn2({ ...nameFnArgs, seriesKeys: ['b'] }, false)).toEqual(null); + expect(nameFn1({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(null); }); test('split series without formatting and single y accessor', () => { @@ -1736,12 +1744,17 @@ describe('XYChart component', () => { accessors: ['a'], splitAccessor: 'd', columnToLabel: '{"a": "Label A"}', + table: dataWithoutFormats, }, ], }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + const component = getRenderedComponent(newArgs); + const nameFn = component + .find(DataLayers) + .dive() + .find(LineSeries) + .prop('name') as SeriesNameFn; expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('split1'); }); @@ -1756,12 +1769,17 @@ describe('XYChart component', () => { accessors: ['a'], splitAccessor: 'd', columnToLabel: '{"a": "Label A"}', + table: dataWithFormats, }, ], }; - const component = getRenderedComponent(dataWithFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + const component = getRenderedComponent(newArgs); + const nameFn = component + .find(DataLayers) + .dive() + .find(LineSeries) + .prop('name') as SeriesNameFn; convertSpy.mockReturnValueOnce('formatted'); expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('formatted'); @@ -1778,13 +1796,16 @@ describe('XYChart component', () => { accessors: ['a', 'b'], splitAccessor: 'd', columnToLabel: '{"a": "Label A","b": "Label B"}', + table: dataWithoutFormats, }, ], }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - const nameFn2 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; + const component = getRenderedComponent(newArgs); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + const nameFn1 = lineSeries.at(0).prop('name') as SeriesNameFn; + const nameFn2 = lineSeries.at(0).prop('name') as SeriesNameFn; expect(nameFn1({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual( 'split1 - Label A' @@ -1804,13 +1825,16 @@ describe('XYChart component', () => { accessors: ['a', 'b'], splitAccessor: 'd', columnToLabel: '{"a": "Label A","b": "Label B"}', + table: dataWithFormats, }, ], }; - const component = getRenderedComponent(dataWithFormats, newArgs); - const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - const nameFn2 = component.find(LineSeries).at(1).prop('name') as SeriesNameFn; + const component = getRenderedComponent(newArgs); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + const nameFn1 = lineSeries.at(0).prop('name') as SeriesNameFn; + const nameFn2 = lineSeries.at(1).prop('name') as SeriesNameFn; convertSpy.mockReturnValueOnce('formatted1').mockReturnValueOnce('formatted2'); expect(nameFn1({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual( @@ -1823,57 +1847,58 @@ describe('XYChart component', () => { }); test('it set the scale of the x axis according to the args prop', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( ); - expect(component.find(LineSeries).at(0).prop('xScaleType')).toEqual(ScaleType.Ordinal); - expect(component.find(LineSeries).at(1).prop('xScaleType')).toEqual(ScaleType.Ordinal); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + expect(lineSeries.at(0).prop('xScaleType')).toEqual(ScaleType.Ordinal); + expect(lineSeries.at(1).prop('xScaleType')).toEqual(ScaleType.Ordinal); }); test('it set the scale of the y axis according to the args prop', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( ); - expect(component.find(LineSeries).at(0).prop('yScaleType')).toEqual(ScaleType.Sqrt); - expect(component.find(LineSeries).at(1).prop('yScaleType')).toEqual(ScaleType.Sqrt); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + expect(lineSeries.at(0).prop('yScaleType')).toEqual(ScaleType.Sqrt); + expect(lineSeries.at(1).prop('yScaleType')).toEqual(ScaleType.Sqrt); }); test('it gets the formatter for the x axis', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); - shallow(); + shallow(); expect(getFormatSpy).toHaveBeenCalledWith({ id: 'string' }); }); test('it gets the formatter for the y axis if there is only one accessor', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); shallow( ); @@ -1884,9 +1909,9 @@ describe('XYChart component', () => { }); test('it should pass the formatter function to the axis', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); - const instance = shallow(); + const instance = shallow(); const tickFormatter = instance.find(Axis).first().prop('tickFormat'); @@ -1900,7 +1925,7 @@ describe('XYChart component', () => { }); test('it should set the tickLabel visibility on the x axis if the tick labels is hidden', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.tickLabelsVisibilitySettings = { x: false, @@ -1909,7 +1934,7 @@ describe('XYChart component', () => { type: 'tickLabelsConfig', }; - const instance = shallow(); + const instance = shallow(); const axisStyle = instance.find(Axis).first().prop('style'); @@ -1921,7 +1946,7 @@ describe('XYChart component', () => { }); test('it should set the tickLabel visibility on the y axis if the tick labels is hidden', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.tickLabelsVisibilitySettings = { x: true, @@ -1930,7 +1955,7 @@ describe('XYChart component', () => { type: 'tickLabelsConfig', }; - const instance = shallow(); + const instance = shallow(); const axisStyle = instance.find(Axis).at(1).prop('style'); @@ -1942,7 +1967,7 @@ describe('XYChart component', () => { }); test('it should set the tickLabel visibility on the x axis if the tick labels is shown', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.tickLabelsVisibilitySettings = { x: true, @@ -1951,7 +1976,7 @@ describe('XYChart component', () => { type: 'tickLabelsConfig', }; - const instance = shallow(); + const instance = shallow(); const axisStyle = instance.find(Axis).first().prop('style'); @@ -1963,7 +1988,7 @@ describe('XYChart component', () => { }); test('it should set the tickLabel orientation on the x axis', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.labelsOrientation = { x: -45, @@ -1972,7 +1997,7 @@ describe('XYChart component', () => { type: 'labelsOrientationConfig', }; - const instance = shallow(); + const instance = shallow(); const axisStyle = instance.find(Axis).first().prop('style'); @@ -1984,7 +2009,7 @@ describe('XYChart component', () => { }); test('it should set the tickLabel visibility on the y axis if the tick labels is shown', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.tickLabelsVisibilitySettings = { x: false, @@ -1993,7 +2018,7 @@ describe('XYChart component', () => { type: 'tickLabelsConfig', }; - const instance = shallow(); + const instance = shallow(); const axisStyle = instance.find(Axis).at(1).prop('style'); @@ -2005,7 +2030,7 @@ describe('XYChart component', () => { }); test('it should set the tickLabel orientation on the y axis', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.labelsOrientation = { x: -45, @@ -2014,7 +2039,7 @@ describe('XYChart component', () => { type: 'labelsOrientationConfig', }; - const instance = shallow(); + const instance = shallow(); const axisStyle = instance.find(Axis).at(1).prop('style'); @@ -2026,37 +2051,33 @@ describe('XYChart component', () => { }); test('it should remove invalid rows', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - ], - rows: [ - { a: undefined, b: 2, c: 'I', d: 'Row 1' }, - { a: 1, b: 5, c: 'J', d: 'Row 2' }, - ], - }, - second: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - ], - rows: [ - { a: undefined, b: undefined, c: undefined }, - { a: undefined, b: undefined, c: undefined }, - ], - }, - }, + const data1: Datatable = { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + ], + rows: [ + { a: undefined, b: 2, c: 'I', d: 'Row 1' }, + { a: 1, b: 5, c: 'J', d: 'Row 2' }, + ], + }; + + const data2: Datatable = { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + ], + rows: [ + { a: undefined, b: undefined, c: undefined }, + { a: undefined, b: undefined, c: undefined }, + ], }; - const args: XYArgs = { + const args: XYProps = { xTitle: '', yTitle: '', yRightTitle: '', @@ -2090,8 +2111,8 @@ describe('XYChart component', () => { }, layers: [ { - type: 'dataLayer', layerId: 'first', + type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'a', @@ -2102,10 +2123,11 @@ describe('XYChart component', () => { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data1, }, { - type: 'dataLayer', layerId: 'second', + type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'a', @@ -2116,13 +2138,14 @@ describe('XYChart component', () => { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data2, }, ], }; - const component = shallow(); + const component = shallow(); - const series = component.find(LineSeries); + const series = component.find(DataLayers).dive().find(LineSeries); // Only one series should be rendered, even though 2 are configured // This one series should only have one row, even though 2 are sent @@ -2130,25 +2153,20 @@ describe('XYChart component', () => { }); test('it should not remove rows with falsy but non-undefined values', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'number' } }, - ], - rows: [ - { a: 0, b: 2, c: 5 }, - { a: 1, b: 0, c: 7 }, - ], - }, - }, + const data: Datatable = { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'number' } }, + ], + rows: [ + { a: 0, b: 2, c: 5 }, + { a: 1, b: 0, c: 7 }, + ], }; - const args: XYArgs = { + const args: XYProps = { xTitle: '', yTitle: '', yRightTitle: '', @@ -2182,8 +2200,8 @@ describe('XYChart component', () => { }, layers: [ { - type: 'dataLayer', layerId: 'first', + type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'a', @@ -2194,13 +2212,14 @@ describe('XYChart component', () => { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }, ], }; - const component = shallow(); + const component = shallow(); - const series = component.find(LineSeries); + const series = component.find(DataLayers).dive().find(LineSeries); expect(series.prop('data')).toEqual([ { a: 0, b: 2, c: 5 }, @@ -2209,22 +2228,17 @@ describe('XYChart component', () => { }); test('it should show legend for split series, even with one row', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - ], - rows: [{ a: 1, b: 5, c: 'J' }], - }, - }, + const data: Datatable = { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + ], + rows: [{ a: 1, b: 5, c: 'J' }], }; - const args: XYArgs = { + const args: XYProps = { xTitle: '', yTitle: '', yRightTitle: '', @@ -2258,8 +2272,8 @@ describe('XYChart component', () => { }, layers: [ { - type: 'dataLayer', layerId: 'first', + type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'a', @@ -2270,27 +2284,27 @@ describe('XYChart component', () => { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }, ], }; - const component = shallow(); + const component = shallow(); expect(component.find(Settings).prop('showLegend')).toEqual(true); }); test('it should always show legend if showSingleSeries is set', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( { }); test('it should populate the correct legendPosition if isInside is set', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( { }); test('it not show legend if isVisible is set to false', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( { }); test('it should show legend on right side', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( { expect(component.find(Settings).prop('legendPosition')).toEqual('top'); }); - test('it should apply the fitting function to all non-bar series', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([ - { a: 1, b: 2, c: 'I', d: 'Foo' }, - { a: 1, b: 5, c: 'J', d: 'Bar' }, - ]), + it('computes correct legend sizes', () => { + const { args } = sampleArgs(); + + const component = shallow( + + ); + expect(component.find(Settings).prop('legendSize')).toEqual(80); + + component.setProps({ + args: { + ...args, + legend: { ...args.legend, legendSize: LegendSize.AUTO }, }, - }; + }); + expect(component.find(Settings).prop('legendSize')).toBeUndefined(); + + component.setProps({ + args: { + ...args, + legend: { ...args.legend, legendSize: undefined }, + }, + }); + expect(component.find(Settings).prop('legendSize')).toEqual(130); + }); + + test('it should apply the fitting function to all non-bar series', () => { + const data: Datatable = createSampleDatatableWithRows([ + { a: 1, b: 2, c: 'I', d: 'Foo' }, + { a: 1, b: 5, c: 'J', d: 'Bar' }, + ]); - const args: XYArgs = createArgsWithLayers([ - { ...sampleLayer, accessors: ['a'] }, - { ...sampleLayer, seriesType: 'bar', accessors: ['a'] }, - { ...sampleLayer, seriesType: 'area', accessors: ['a'] }, - { ...sampleLayer, seriesType: 'area_stacked', accessors: ['a'] }, + const args: XYProps = createArgsWithLayers([ + { ...sampleLayer, accessors: ['a'], table: data }, + { ...sampleLayer, seriesType: 'bar', accessors: ['a'], table: data }, + { ...sampleLayer, seriesType: 'area', accessors: ['a'], table: data }, + { ...sampleLayer, seriesType: 'area_stacked', accessors: ['a'], table: data }, ]); const component = shallow( - + ); - - expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.Carry }); - expect(component.find(BarSeries).prop('fit')).toEqual(undefined); - expect(component.find(AreaSeries).at(0).prop('fit')).toEqual({ type: Fit.Carry }); - expect(component.find(AreaSeries).at(0).prop('stackAccessors')).toEqual([]); - expect(component.find(AreaSeries).at(1).prop('fit')).toEqual({ type: Fit.Carry }); - expect(component.find(AreaSeries).at(1).prop('stackAccessors')).toEqual(['c']); + const dataLayers = component.find(DataLayers).dive(); + expect(dataLayers.find(LineSeries).prop('fit')).toEqual({ type: Fit.Carry }); + expect(dataLayers.find(BarSeries).prop('fit')).toEqual(undefined); + expect(dataLayers.find(AreaSeries).at(0).prop('fit')).toEqual({ type: Fit.Carry }); + expect(dataLayers.find(AreaSeries).at(0).prop('stackAccessors')).toEqual([]); + expect(dataLayers.find(AreaSeries).at(1).prop('fit')).toEqual({ type: Fit.Carry }); + expect(dataLayers.find(AreaSeries).at(1).prop('stackAccessors')).toEqual(['c']); }); test('it should apply None fitting function if not specified', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); - (args.layers[0] as DataLayerConfigResult).accessors = ['a']; + (args.layers[0] as DataLayerConfig).accessors = ['a']; - const component = shallow(); + const component = shallow(); - expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.None }); + expect(component.find(DataLayers).dive().find(LineSeries).prop('fit')).toEqual({ + type: Fit.None, + }); }); test('it should apply the xTitle if is specified', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.xTitle = 'My custom x-axis title'; - const component = shallow(); + const component = shallow(); expect(component.find(Axis).at(0).prop('title')).toEqual('My custom x-axis title'); }); test('it should hide the X axis title if the corresponding switch is off', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.axisTitlesVisibilitySettings = { x: false, @@ -2427,7 +2466,7 @@ describe('XYChart component', () => { type: 'axisTitlesVisibilityConfig', }; - const component = shallow(); + const component = shallow(); const axisStyle = component.find(Axis).first().prop('style'); @@ -2439,7 +2478,7 @@ describe('XYChart component', () => { }); test('it should show the X axis gridlines if the setting is on', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.gridlinesVisibilitySettings = { x: true, @@ -2448,7 +2487,7 @@ describe('XYChart component', () => { type: 'gridlinesConfig', }; - const component = shallow(); + const component = shallow(); expect(component.find(Axis).at(0).prop('gridLine')).toMatchObject({ visible: true, @@ -2456,45 +2495,37 @@ describe('XYChart component', () => { }); test('it should format the boolean values correctly', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { - id: 'a', - name: 'a', - meta: { type: 'number', params: { id: 'number', params: { pattern: '0,0.000' } } }, - }, - { - id: 'b', - name: 'b', - meta: { type: 'number', params: { id: 'number', params: { pattern: '000,0' } } }, - }, - { - id: 'c', - name: 'c', - meta: { - type: 'boolean', - params: { id: 'boolean' }, - }, - }, - ], - rows: [ - { a: 5, b: 2, c: 0 }, - { a: 19, b: 5, c: 1 }, - ], + const data: Datatable = { + type: 'datatable', + columns: [ + { + id: 'a', + name: 'a', + meta: { type: 'number', params: { id: 'number', params: { pattern: '0,0.000' } } }, }, - }, - dateRange: { - fromDate: new Date('2019-01-02T05:00:00.000Z'), - toDate: new Date('2019-01-03T05:00:00.000Z'), - }, + { + id: 'b', + name: 'b', + meta: { type: 'number', params: { id: 'number', params: { pattern: '000,0' } } }, + }, + { + id: 'c', + name: 'c', + meta: { + type: 'boolean', + params: { id: 'boolean' }, + }, + }, + ], + rows: [ + { a: 5, b: 2, c: 0 }, + { a: 19, b: 5, c: 1 }, + ], }; - const timeSampleLayer: DataLayerConfigResult = { + + const timeSampleLayer: DataLayerConfig = { + layerId: 'timeLayer', type: 'dataLayer', - layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'c', @@ -2503,22 +2534,19 @@ describe('XYChart component', () => { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }; + const args = createArgsWithLayers([timeSampleLayer]); const getCustomFormatSpy = jest.fn(); getCustomFormatSpy.mockReturnValue({ convert: jest.fn((x) => Boolean(x)) }); const component = shallow( - + ); - expect(component.find(LineSeries).at(1).prop('data')).toEqual([ + expect(component.find(DataLayers).dive().find(LineSeries).at(1).prop('data')).toEqual([ { a: 5, b: 2, @@ -2533,69 +2561,80 @@ describe('XYChart component', () => { }); describe('annotations', () => { - const sampleStyledAnnotation: EventAnnotationOutput = { + const customLineStaticAnnotation: EventAnnotationOutput = { time: '2022-03-18T08:25:00.000Z', label: 'Event 1', icon: 'triangle', - type: 'manual_event_annotation', + type: 'manual_point_event_annotation' as const, color: 'red', lineStyle: 'dashed', lineWidth: 3, }; - const sampleAnnotationLayers: AnnotationLayerConfigResult[] = [ - { - type: 'annotationLayer', - layerType: LayerTypes.ANNOTATIONS, - layerId: 'annotation', - annotations: [ - { - time: '2022-03-18T08:25:17.140Z', - label: 'Annotation', - type: 'manual_event_annotation', - }, - ], - }, - ]; - function sampleArgsWithAnnotation(annotationLayers = sampleAnnotationLayers) { + + const defaultLineStaticAnnotation = { + time: '2022-03-18T08:25:17.140Z', + label: 'Annotation', + type: 'manual_point_event_annotation' as const, + }; + const defaultRangeStaticAnnotation = { + time: '2022-03-18T08:25:17.140Z', + endTime: '2022-03-31T08:25:17.140Z', + label: 'Event range', + type: 'manual_range_event_annotation' as const, + }; + const createLayerWithAnnotations = ( + annotations: EventAnnotationOutput[] = [defaultLineStaticAnnotation] + ): CommonXYAnnotationLayerConfig => ({ + type: 'annotationLayer', + layerType: LayerTypes.ANNOTATIONS, + layerId: 'annotation', + annotations, + }); + function sampleArgsWithAnnotations(annotationLayers = [createLayerWithAnnotations()]) { const { args } = sampleArgs(); return { - data: dateHistogramData, args: { ...args, layers: [dateHistogramLayer, ...annotationLayers], - } as XYArgs, + }, }; } - test('should render basic annotation', () => { - const { data, args } = sampleArgsWithAnnotation(); - const component = mount(); + + test('should render basic line annotation', () => { + const { args } = sampleArgsWithAnnotations(); + const component = mount(); expect(component.find('LineAnnotation')).toMatchSnapshot(); }); - test('should render simplified annotation when hide is true', () => { - const { data, args } = sampleArgsWithAnnotation(); - (args.layers[0] as AnnotationLayerConfigResult).hide = true; - const component = mount(); + test('should render basic range annotation', () => { + const { args } = sampleArgsWithAnnotations([ + createLayerWithAnnotations([defaultLineStaticAnnotation, defaultRangeStaticAnnotation]), + ]); + const component = mount(); + expect(component.find(RectAnnotation)).toMatchSnapshot(); + }); + test('should render simplified annotations when hide is true', () => { + const { args } = sampleArgsWithAnnotations([ + createLayerWithAnnotations([defaultLineStaticAnnotation, defaultRangeStaticAnnotation]), + ]); + (args.layers[1] as CommonXYAnnotationLayerConfig).hide = true; + const component = mount(); expect(component.find('LineAnnotation')).toMatchSnapshot(); + expect(component.find('RectAnnotation')).toMatchSnapshot(); }); - test('should render grouped annotations preserving the shared styles', () => { - const { data, args } = sampleArgsWithAnnotation([ - { - type: 'annotationLayer', - layerType: LayerTypes.ANNOTATIONS, - layerId: 'annotation', - annotations: [ - sampleStyledAnnotation, - { ...sampleStyledAnnotation, time: '2022-03-18T08:25:00.020Z', label: 'Event 2' }, - { - ...sampleStyledAnnotation, - time: '2022-03-18T08:25:00.001Z', - label: 'Event 3', - }, - ], - }, + test('should render grouped line annotations preserving the shared styles', () => { + const { args } = sampleArgsWithAnnotations([ + createLayerWithAnnotations([ + customLineStaticAnnotation, + { ...customLineStaticAnnotation, time: '2022-03-18T08:25:00.020Z', label: 'Event 2' }, + { + ...customLineStaticAnnotation, + time: '2022-03-18T08:25:00.001Z', + label: 'Event 3', + }, + ]), ]); - const component = mount(); + const component = mount(); const groupedAnnotation = component.find(LineAnnotation); expect(groupedAnnotation.length).toEqual(1); @@ -2613,32 +2652,23 @@ describe('XYChart component', () => { ' Event 1 2022-03-18T08:25:00.000Z Event 3 2022-03-18T08:25:00.001Z Event 2 2022-03-18T08:25:00.020Z' ); }); - test('should render grouped annotations with default styles', () => { - const { data, args } = sampleArgsWithAnnotation([ - { - type: 'annotationLayer', - layerType: LayerTypes.ANNOTATIONS, - layerId: 'annotation', - annotations: [sampleStyledAnnotation], - }, - { - type: 'annotationLayer', - layerType: LayerTypes.ANNOTATIONS, - layerId: 'annotation', - annotations: [ - { - ...sampleStyledAnnotation, - icon: 'square', - color: 'blue', - lineStyle: 'dotted', - lineWidth: 10, - time: '2022-03-18T08:25:00.001Z', - label: 'Event 2', - }, - ], - }, + + test('should render grouped line annotations with default styles', () => { + const { args } = sampleArgsWithAnnotations([ + createLayerWithAnnotations([customLineStaticAnnotation]), + createLayerWithAnnotations([ + { + ...customLineStaticAnnotation, + icon: 'triangle' as const, + color: 'blue', + lineStyle: 'dotted', + lineWidth: 10, + time: '2022-03-18T08:25:00.001Z', + label: 'Event 2', + }, + ]), ]); - const component = mount(); + const component = mount(); const groupedAnnotation = component.find(LineAnnotation); expect(groupedAnnotation.length).toEqual(1); @@ -2646,27 +2676,26 @@ describe('XYChart component', () => { expect(groupedAnnotation).toMatchSnapshot(); }); test('should not render hidden annotations', () => { - const { data, args } = sampleArgsWithAnnotation([ - { - type: 'annotationLayer', - layerType: LayerTypes.ANNOTATIONS, - layerId: 'annotation', - annotations: [ - sampleStyledAnnotation, - { ...sampleStyledAnnotation, time: '2022-03-18T08:30:00.020Z', label: 'Event 2' }, - { - ...sampleStyledAnnotation, - time: '2022-03-18T08:35:00.001Z', - label: 'Event 3', - isHidden: true, - }, - ], - }, + const { args } = sampleArgsWithAnnotations([ + createLayerWithAnnotations([ + customLineStaticAnnotation, + { ...customLineStaticAnnotation, time: '2022-03-18T08:30:00.020Z', label: 'Event 2' }, + { + ...customLineStaticAnnotation, + time: '2022-03-18T08:35:00.001Z', + label: 'Event 3', + isHidden: true, + }, + defaultRangeStaticAnnotation, + { ...defaultRangeStaticAnnotation, label: 'range', isHidden: true }, + ]), ]); - const component = mount(); - const annotations = component.find(LineAnnotation); + const component = mount(); + const lineAnnotations = component.find(LineAnnotation); + const rectAnnotations = component.find(Annotations).find(RectAnnotation); - expect(annotations.length).toEqual(2); + expect(lineAnnotations.length).toEqual(2); + expect(rectAnnotations.length).toEqual(1); }); }); }); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 4f543ae0f09ae..6e3f142996949 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -6,67 +6,69 @@ * Side Public License, v 1. */ -import React, { useRef } from 'react'; +import React, { useMemo, useRef } from 'react'; import { Chart, Settings, Axis, - LineSeries, - AreaSeries, - BarSeries, Position, GeometryValue, XYChartSeriesIdentifier, - StackMode, VerticalAlignment, HorizontalAlignment, LayoutDirection, ElementClickListener, BrushEndListener, XYBrushEvent, - CurveType, LegendPositionConfig, - LabelOverflowConstraint, DisplayValueStyle, RecursivePartial, AxisStyle, - ScaleType, - AreaSeriesProps, - BarSeriesProps, - LineSeriesProps, - ColorVariant, + Placement, } from '@elastic/charts'; import { IconType } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { PaletteRegistry, SeriesLayer } from '@kbn/coloring'; -import type { Datatable, DatatableRow, DatatableColumn } from '@kbn/expressions-plugin/public'; +import { PaletteRegistry } from '@kbn/coloring'; import { RenderMode } from '@kbn/expressions-plugin/common'; -import { FieldFormat } from '@kbn/field-formats-plugin/common'; import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { ChartsPluginSetup, ChartsPluginStart, useActiveCursor } from '@kbn/charts-plugin/public'; import { MULTILAYER_TIME_AXIS_STYLE } from '@kbn/charts-plugin/common'; +import { + DEFAULT_LEGEND_SIZE, + LegendSizeToPixels, +} from '@kbn/visualizations-plugin/common/constants'; import type { FilterEvent, BrushEvent, FormatFactory } from '../types'; -import type { SeriesType, XYChartProps } from '../../common/types'; -import { isHorizontalChart, getSeriesColor, getAnnotationsLayers, getDataLayers } from '../helpers'; +import type { CommonXYDataLayerConfig, SeriesType, XYChartProps } from '../../common/types'; +import { + isHorizontalChart, + getAnnotationsLayers, + getDataLayers, + Series, + getFormattedTablesByLayers, + validateExtent, +} from '../helpers'; import { getFilteredLayers, getReferenceLayers, isDataLayer, - getFitOptions, getAxesConfiguration, GroupsConfiguration, - validateExtent, - getColorAssignments, getLinesCausedPaddings, } from '../helpers'; import { getXDomain, XyEndzones } from './x_domain'; import { getLegendAction } from './legend_action'; import { ReferenceLineAnnotations, computeChartMargins } from './reference_lines'; import { visualizationDefinitions } from '../definitions'; -import { XYLayerConfigResult } from '../../common/types'; -import { Annotations, getAnnotationsGroupedByInterval } from './annotations'; - +import { CommonXYLayerConfig } from '../../common/types'; +import { + Annotations, + getAnnotationsGroupedByInterval, + getRangeAnnotations, + OUTSIDE_RECT_ANNOTATION_WIDTH, + OUTSIDE_RECT_ANNOTATION_WIDTH_SUGGESTION, +} from './annotations'; +import { AxisExtentModes, SeriesTypes, ValueLabelModes } from '../../common/constants'; +import { DataLayers } from './data_layers'; import './xy_chart.scss'; declare global { @@ -78,8 +80,6 @@ declare global { } } -type SeriesSpec = LineSeriesProps & BarSeriesProps & AreaSeriesProps; - export type XYChartRenderProps = XYChartProps & { chartsThemeService: ChartsPluginSetup['theme']; chartsActiveCursorService: ChartsPluginStart['activeCursor']; @@ -93,11 +93,10 @@ export type XYChartRenderProps = XYChartProps & { onSelectRange: (data: BrushEvent['data']) => void; renderMode: RenderMode; syncColors: boolean; + syncTooltips: boolean; eventAnnotationService: EventAnnotationServiceType; }; -const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; - function getValueLabelsStyling(isHorizontal: boolean): { displayValue: RecursivePartial; } { @@ -126,7 +125,6 @@ function getIconForSeriesType(seriesType: SeriesType): IconType { export const XYChartReportable = React.memo(XYChart); export function XYChart({ - data, args, formatFactory, timeZone, @@ -138,6 +136,7 @@ export function XYChart({ onSelectRange, interactive = true, syncColors, + syncTooltips, useLegacyTimeAxis, }: XYChartRenderProps) { const { @@ -157,50 +156,47 @@ export function XYChart({ const chartTheme = chartsThemeService.useChartsTheme(); const chartBaseTheme = chartsThemeService.useChartsBaseTheme(); const darkMode = chartsThemeService.useDarkMode(); - const filteredLayers = getFilteredLayers(layers, data); - const layersById = filteredLayers.reduce>( - (hashMap, layer) => { - hashMap[layer.layerId] = layer; - return hashMap; - }, + const filteredLayers = getFilteredLayers(layers); + const layersById = filteredLayers.reduce>( + (hashMap, layer) => ({ ...hashMap, [layer.layerId]: layer }), {} ); const handleCursorUpdate = useActiveCursor(chartsActiveCursorService, chartRef, { - datatables: Object.values(data.tables), + datatables: filteredLayers.map(({ table }) => table), }); + const dataLayers: CommonXYDataLayerConfig[] = filteredLayers.filter(isDataLayer); + const formattedDatatables = useMemo( + () => getFormattedTablesByLayers(dataLayers, formatFactory), + [dataLayers, formatFactory] + ); + if (filteredLayers.length === 0) { - const icon: IconType = getIconForSeriesType(getDataLayers(layers)?.[0]?.seriesType || 'bar'); + const icon: IconType = getIconForSeriesType( + getDataLayers(layers)?.[0]?.seriesType || SeriesTypes.BAR + ); return ; } // use formatting hint of first x axis column to format ticks - const xAxisColumn = data.tables[filteredLayers[0].layerId].columns.find( - ({ id }) => id === filteredLayers[0].xAccessor - ); + const xAxisColumn = dataLayers[0]?.table.columns.find(({ id }) => id === dataLayers[0].xAccessor); const xAxisFormatter = formatFactory(xAxisColumn && xAxisColumn.meta?.params); - const layersAlreadyFormatted: Record = {}; // This is a safe formatter for the xAccessor that abstracts the knowledge of already formatted layers const safeXAccessorLabelRenderer = (value: unknown): string => - xAxisColumn && layersAlreadyFormatted[xAxisColumn.id] + xAxisColumn && formattedDatatables[dataLayers[0]?.layerId]?.formattedColumns[xAxisColumn.id] ? String(value) : String(xAxisFormatter.convert(value)); const chartHasMoreThanOneSeries = filteredLayers.length > 1 || filteredLayers.some((layer) => layer.accessors.length > 1) || - filteredLayers.some((layer) => layer.splitAccessor); - const shouldRotate = isHorizontalChart(filteredLayers); - - const yAxesConfiguration = getAxesConfiguration( - filteredLayers, - shouldRotate, - data.tables, - formatFactory - ); + filteredLayers.some((layer) => isDataLayer(layer) && layer.splitAccessor); + const shouldRotate = isHorizontalChart(dataLayers); + + const yAxesConfiguration = getAxesConfiguration(dataLayers, shouldRotate, formatFactory); const xTitle = args.xTitle || (xAxisColumn && xAxisColumn.name); const axisTitlesVisibilitySettings = args.axisTitlesVisibilitySettings || { @@ -214,25 +210,20 @@ export function XYChart({ yRight: true, }; - const labelsOrientation = args.labelsOrientation || { - x: 0, - yLeft: 0, - yRight: 0, - }; + const labelsOrientation = args.labelsOrientation || { x: 0, yLeft: 0, yRight: 0 }; - const filteredBarLayers = filteredLayers.filter((layer) => layer.seriesType.includes('bar')); + const filteredBarLayers = dataLayers.filter((layer) => layer.seriesType.includes('bar')); const chartHasMoreThanOneBarSeries = filteredBarLayers.length > 1 || filteredBarLayers.some((layer) => layer.accessors.length > 1) || - filteredBarLayers.some((layer) => layer.splitAccessor); + filteredBarLayers.some((layer) => isDataLayer(layer) && layer.splitAccessor); - const isTimeViz = Boolean(filteredLayers.every((l) => l.xScaleType === 'time')); - const isHistogramViz = filteredLayers.every((l) => l.isHistogram); + const isTimeViz = Boolean(dataLayers.every((l) => l.xScaleType === 'time')); + const isHistogramViz = dataLayers.every((l) => l.isHistogram); const { baseDomain: rawXDomain, extendedDomain: xDomain } = getXDomain( - filteredLayers, - data, + dataLayers, minInterval, isTimeViz, isHistogramViz @@ -243,17 +234,16 @@ export function XYChart({ right: yAxesConfiguration.find(({ groupId }) => groupId === 'right'), }; - const getYAxesTitles = ( - axisSeries: Array<{ layer: string; accessor: string }>, - groupId: string - ) => { + const getYAxesTitles = (axisSeries: Series[], groupId: 'right' | 'left') => { const yTitle = groupId === 'right' ? args.yRightTitle : args.yTitle; return ( yTitle || axisSeries .map( (series) => - data.tables[series.layer].columns.find((column) => column.id === series.accessor)?.name + filteredLayers + .find(({ layerId }) => series.layer === layerId) + ?.table.columns.find((column) => column.id === series.accessor)?.name ) .filter((name) => Boolean(name))[0] ); @@ -261,22 +251,25 @@ export function XYChart({ const referenceLineLayers = getReferenceLayers(layers); const annotationsLayers = getAnnotationsLayers(layers); - const firstTable = data.tables[filteredLayers[0].layerId]; + const firstTable = dataLayers[0]?.table; - const xColumnId = firstTable.columns.find((col) => col.id === filteredLayers[0].xAccessor)?.id; + const xColumnId = firstTable.columns.find((col) => col.id === dataLayers[0]?.xAccessor)?.id; - const groupedAnnotations = getAnnotationsGroupedByInterval( + const groupedLineAnnotations = getAnnotationsGroupedByInterval( annotationsLayers, minInterval, xColumnId ? firstTable.rows[0]?.[xColumnId] : undefined, xAxisFormatter ); + const rangeAnnotations = getRangeAnnotations(annotationsLayers); + const visualConfigs = [ ...referenceLineLayers.flatMap(({ yConfig }) => yConfig), - ...groupedAnnotations, + ...groupedLineAnnotations, ].filter(Boolean); - const linesPaddings = getLinesCausedPaddings(visualConfigs, yAxesMap); + const shouldHideDetails = annotationsLayers.length > 0 ? annotationsLayers[0].hide : false; + const linesPaddings = !shouldHideDetails ? getLinesCausedPaddings(visualConfigs, yAxesMap) : {}; const getYAxesStyle = (groupId: 'left' | 'right') => { const tickVisible = @@ -327,10 +320,11 @@ export function XYChart({ return layer.seriesType.includes('bar') || layer.seriesType.includes('area'); }) ); - const fit = !hasBarOrArea && extent.mode === 'dataBounds'; + + const fit = !hasBarOrArea && extent.mode === AxisExtentModes.DATA_BOUNDS; + let min: number = NaN; let max: number = NaN; - if (extent.mode === 'custom') { const { inclusiveZeroError, boundaryError } = validateExtent(hasBarOrArea, extent); if (!inclusiveZeroError && !boundaryError) { @@ -357,38 +351,42 @@ export function XYChart({ const shouldShowValueLabels = // No stacked bar charts - filteredLayers.every((layer) => !layer.seriesType.includes('stacked')) && + dataLayers.every((layer) => !layer.seriesType.includes('stacked')) && // No histogram charts !isHistogramViz; const valueLabelsStyling = - shouldShowValueLabels && valueLabels !== 'hide' && getValueLabelsStyling(shouldRotate); - - const colorAssignments = getColorAssignments(getDataLayers(args.layers), data, formatFactory); + shouldShowValueLabels && + valueLabels !== ValueLabelModes.HIDE && + getValueLabelsStyling(shouldRotate); const clickHandler: ElementClickListener = ([[geometry, series]]) => { // for xyChart series is always XYChartSeriesIdentifier and geometry is always type of GeometryValue const xySeries = series as XYChartSeriesIdentifier; const xyGeometry = geometry as GeometryValue; - const layer = filteredLayers.find((l) => + const layerIndex = dataLayers.findIndex((l) => xySeries.seriesKeys.some((key: string | number) => l.accessors.includes(key.toString())) ); - if (!layer) { + + if (layerIndex === -1) { return; } - const table = data.tables[layer.layerId]; + const layer = dataLayers[layerIndex]; + const { table } = layer; const xColumn = table.columns.find((col) => col.id === layer.xAccessor); const currentXFormatter = - layer.xAccessor && layersAlreadyFormatted[layer.xAccessor] && xColumn + layer.xAccessor && + formattedDatatables[layer.layerId]?.formattedColumns[layer.xAccessor] && + xColumn ? formatFactory(xColumn.meta.params) : xAxisFormatter; const rowIndex = table.rows.findIndex((row) => { if (layer.xAccessor) { - if (layersAlreadyFormatted[layer.xAccessor]) { + if (formattedDatatables[layer.layerId]?.formattedColumns[layer.xAccessor]) { // stringify the value to compare with the chart value return currentXFormatter.convert(row[layer.xAccessor]) === xyGeometry.x; } @@ -413,7 +411,7 @@ export function XYChart({ points.push({ row: table.rows.findIndex((row) => { if (layer.splitAccessor) { - if (layersAlreadyFormatted[layer.splitAccessor]) { + if (formattedDatatables[layer.layerId]?.formattedColumns[layer.splitAccessor]) { return splitFormatter.convert(row[layer.splitAccessor]) === pointValue; } return row[layer.splitAccessor] === pointValue; @@ -424,12 +422,7 @@ export function XYChart({ }); } const context: FilterEvent['data'] = { - data: points.map((point) => ({ - row: point.row, - column: point.column, - value: point.value, - table, - })), + data: points.map(({ row, column, value }) => ({ row, column, value, table })), }; onClickValue(context); }; @@ -443,27 +436,23 @@ export function XYChart({ return; } - const table = data.tables[filteredLayers[0].layerId]; + const { table } = dataLayers[0]; - const xAxisColumnIndex = table.columns.findIndex((el) => el.id === filteredLayers[0].xAccessor); + const xAxisColumnIndex = table.columns.findIndex((el) => el.id === dataLayers[0].xAccessor); - const context: BrushEvent['data'] = { - range: [min, max], - table, - column: xAxisColumnIndex, - }; + const context: BrushEvent['data'] = { range: [min, max], table, column: xAxisColumnIndex }; onSelectRange(context); }; - const legendInsideParams = { + const legendInsideParams: LegendPositionConfig = { vAlign: legend.verticalAlignment ?? VerticalAlignment.Top, hAlign: legend?.horizontalAlignment ?? HorizontalAlignment.Right, direction: LayoutDirection.Vertical, floating: true, floatingColumns: legend?.floatingColumns ?? 1, - } as LegendPositionConfig; + }; - const isHistogramModeEnabled = filteredLayers.some( + const isHistogramModeEnabled = dataLayers.some( ({ isHistogram, seriesType }) => isHistogram && (seriesType.includes('stacked') || @@ -507,11 +496,13 @@ export function XYChart({ : undefined, }, }; - return ( safeXAccessorLabelRenderer(d)} style={xAxisStyle} timeAxisLayerCount={shouldUseNewTimeAxis ? 3 : 0} @@ -595,9 +580,9 @@ export function XYChart({ ? gridlinesVisibilitySettings?.yRight : gridlinesVisibilitySettings?.yLeft, }} - hide={filteredLayers[0].hide} + hide={dataLayers[0]?.hide} tickFormat={(d) => axis.formatter?.convert(d) || ''} - style={getYAxesStyle(axis.groupId as 'left' | 'right')} + style={getYAxesStyle(axis.groupId)} domain={getYAxisDomain(axis)} ticks={5} /> @@ -609,7 +594,7 @@ export function XYChart({ baseDomain={rawXDomain} extendedDomain={xDomain} darkMode={darkMode} - histogramMode={filteredLayers.every( + histogramMode={dataLayers.every( (layer) => layer.isHistogram && (layer.seriesType.includes('stacked') || !layer.splitAccessor) && @@ -620,282 +605,28 @@ export function XYChart({ /> )} - {filteredLayers.flatMap((layer, layerIndex) => - layer.accessors.map((accessor, accessorIndex) => { - const { - splitAccessor, - seriesType, - accessors, - xAccessor, - layerId, - columnToLabel, - yScaleType, - xScaleType, - isHistogram, - palette, - } = layer; - const columnToLabelMap: Record = columnToLabel - ? JSON.parse(columnToLabel) - : {}; - - const table = data.tables[layerId]; - - const formatterPerColumn = new Map(); - for (const column of table.columns) { - formatterPerColumn.set(column, formatFactory(column.meta.params)); - } - - // what if row values are not primitive? That is the case of, for instance, Ranges - // remaps them to their serialized version with the formatHint metadata - // In order to do it we need to make a copy of the table as the raw one is required for more features (filters, etc...) later on - const tableConverted: Datatable = { - ...table, - rows: table.rows.map((row: DatatableRow) => { - const newRow = { ...row }; - for (const column of table.columns) { - const record = newRow[column.id]; - if ( - record != null && - // pre-format values for ordinal x axes because there can only be a single x axis formatter on chart level - (!isPrimitive(record) || (column.id === xAccessor && xScaleType === 'ordinal')) - ) { - newRow[column.id] = formatterPerColumn.get(column)!.convert(record); - } - } - return newRow; - }), - }; - - // save the id of the layer with the custom table - table.columns.reduce>( - (alreadyFormatted: Record, { id }) => { - if (alreadyFormatted[id]) { - return alreadyFormatted; - } - alreadyFormatted[id] = table.rows.some( - (row, i) => row[id] !== tableConverted.rows[i][id] - ); - return alreadyFormatted; - }, - layersAlreadyFormatted - ); - - const isStacked = seriesType.includes('stacked'); - const isPercentage = seriesType.includes('percentage'); - const isBarChart = seriesType.includes('bar'); - const enableHistogramMode = - isHistogram && - (isStacked || !splitAccessor) && - (isStacked || !isBarChart || !chartHasMoreThanOneBarSeries); - - // For date histogram chart type, we're getting the rows that represent intervals without data. - // To not display them in the legend, they need to be filtered out. - const rows = tableConverted.rows.filter( - (row) => - !(xAccessor && typeof row[xAccessor] === 'undefined') && - !( - splitAccessor && - typeof row[splitAccessor] === 'undefined' && - typeof row[accessor] === 'undefined' - ) - ); - - if (!xAccessor) { - rows.forEach((row) => { - row.unifiedX = i18n.translate('expressionXY.xyChart.emptyXLabel', { - defaultMessage: '(empty)', - }); - }); - } - - const yAxis = yAxesConfiguration.find((axisConfiguration) => - axisConfiguration.series.find((currentSeries) => currentSeries.accessor === accessor) - ); - - const formatter = table?.columns.find((column) => column.id === accessor)?.meta?.params; - const splitHint = table.columns.find((col) => col.id === splitAccessor)?.meta?.params; - const splitFormatter = formatFactory(splitHint); - - const seriesProps: SeriesSpec = { - splitSeriesAccessors: splitAccessor ? [splitAccessor] : [], - stackAccessors: isStacked ? [xAccessor as string] : [], - id: `${splitAccessor}-${accessor}`, - xAccessor: xAccessor || 'unifiedX', - yAccessors: [accessor], - data: rows, - xScaleType: xAccessor ? xScaleType : 'ordinal', - yScaleType: - formatter?.id === 'bytes' && yScaleType === ScaleType.Linear - ? ScaleType.LinearBinary - : yScaleType, - color: ({ yAccessor, seriesKeys }) => { - const overwriteColor = getSeriesColor(layer, accessor); - if (overwriteColor !== null) { - return overwriteColor; - } - const colorAssignment = colorAssignments[palette.name]; - const seriesLayers: SeriesLayer[] = [ - { - name: splitAccessor ? String(seriesKeys[0]) : columnToLabelMap[seriesKeys[0]], - totalSeriesAtDepth: colorAssignment.totalSeriesCount, - rankAtDepth: colorAssignment.getRank( - layer, - String(seriesKeys[0]), - String(yAccessor) - ), - }, - ]; - return paletteService.get(palette.name).getCategoricalColor( - seriesLayers, - { - maxDepth: 1, - behindText: false, - totalSeries: colorAssignment.totalSeriesCount, - syncColors, - }, - palette.params - ); - }, - groupId: yAxis?.groupId, - enableHistogramMode, - stackMode: isPercentage ? StackMode.Percentage : undefined, - timeZone, - areaSeriesStyle: { - point: { - visible: !xAccessor, - radius: xAccessor && !emphasizeFitting ? 5 : 0, - }, - ...(args.fillOpacity && { area: { opacity: args.fillOpacity } }), - ...(emphasizeFitting && { - fit: { - area: { - opacity: args.fillOpacity || 0.5, - }, - line: { - visible: true, - stroke: ColorVariant.Series, - opacity: 1, - dash: [], - }, - }, - }), - }, - lineSeriesStyle: { - point: { - visible: !xAccessor, - radius: xAccessor && !emphasizeFitting ? 5 : 0, - }, - ...(emphasizeFitting && { - fit: { - line: { - visible: true, - stroke: ColorVariant.Series, - opacity: 1, - dash: [], - }, - }, - }), - }, - name(d) { - // For multiple y series, the name of the operation is used on each, either: - // * Key - Y name - // * Formatted value - Y name - if (accessors.length > 1) { - const result = d.seriesKeys - .map((key: string | number, i) => { - if ( - i === 0 && - splitHint && - splitAccessor && - !layersAlreadyFormatted[splitAccessor] - ) { - return splitFormatter.convert(key); - } - return splitAccessor && i === 0 ? key : columnToLabelMap[key] ?? ''; - }) - .join(' - '); - return result; - } - - // For formatted split series, format the key - // This handles splitting by dates, for example - if (splitHint) { - if (splitAccessor && layersAlreadyFormatted[splitAccessor]) { - return d.seriesKeys[0]; - } - return splitFormatter.convert(d.seriesKeys[0]); - } - // This handles both split and single-y cases: - // * If split series without formatting, show the value literally - // * If single Y, the seriesKey will be the accessor, so we show the human-readable name - return splitAccessor ? d.seriesKeys[0] : columnToLabelMap[d.seriesKeys[0]] ?? ''; - }, - }; - - const index = `${layerIndex}-${accessorIndex}`; - - const curveType = args.curveType ? CurveType[args.curveType] : undefined; - - switch (seriesType) { - case 'line': - return ( - - ); - case 'bar': - case 'bar_stacked': - case 'bar_percentage_stacked': - case 'bar_horizontal': - case 'bar_horizontal_stacked': - case 'bar_horizontal_percentage_stacked': - const valueLabelsSettings = { - displayValueSettings: { - // This format double fixes two issues in elastic-chart - // * when rotating the chart, the formatter is not correctly picked - // * in some scenarios value labels are not strings, and this breaks the elastic-chart lib - valueFormatter: (d: unknown) => yAxis?.formatter?.convert(d) || '', - showValueLabel: shouldShowValueLabels && valueLabels !== 'hide', - isValueContainedInElement: false, - isAlternatingValueLabel: false, - overflowConstraints: [ - LabelOverflowConstraint.ChartEdges, - LabelOverflowConstraint.BarGeometry, - ], - }, - }; - return ; - case 'area_stacked': - case 'area_percentage_stacked': - return ( - - ); - case 'area': - return ( - - ); - default: - return assertNever(seriesType); - } - }) + {dataLayers.length && ( + )} {referenceLineLayers.length ? ( ) : null} - {groupedAnnotations.length ? ( + {rangeAnnotations.length || groupedLineAnnotations.length ? ( 0} minInterval={minInterval} + hide={annotationsLayers?.[0].hide} + outsideDimension={ + rangeAnnotations.length && shouldHideDetails + ? OUTSIDE_RECT_ANNOTATION_WIDTH_SUGGESTION + : shouldUseNewTimeAxis + ? Number(MULTILAYER_TIME_AXIS_STYLE.tickLine?.padding || 0) + + Number(chartTheme.axes?.tickLabel?.fontSize || 0) + : Number(chartTheme.axes?.tickLine?.size) || OUTSIDE_RECT_ANNOTATION_WIDTH + } /> ) : null} ); } - -function assertNever(x: never): never { - throw new Error('Unexpected series type: ' + x); -} diff --git a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx index a35216821c077..c6ad977bbad3a 100644 --- a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx @@ -18,7 +18,6 @@ import { ExpressionRenderDefinition } from '@kbn/expressions-plugin'; import { FormatFactory } from '@kbn/field-formats-plugin/common'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import type { XYChartProps } from '../../common'; -import { calculateMinInterval } from '../helpers/interval'; import type { BrushEvent, FilterEvent } from '../types'; export type GetStartDepsFn = () => Promise<{ @@ -56,7 +55,10 @@ export const getXyChartRenderer = ({ }; const deps = await getStartDeps(); - const { XYChartReportable } = await import('../components/xy_chart'); + const [{ XYChartReportable }, { calculateMinInterval }] = await Promise.all([ + import('../components/xy_chart'), + import('../helpers/interval'), + ]); ReactDOM.render( @@ -80,6 +82,7 @@ export const getXyChartRenderer = ({ onSelectRange={onSelectRange} renderMode={handlers.getRenderMode()} syncColors={handlers.isSyncColorsEnabled()} + syncTooltips={handlers.isSyncTooltipsEnabled()} />
{' '} diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx index 9050bdee4a365..d6746cafc0296 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx @@ -9,19 +9,32 @@ import React from 'react'; import { Position } from '@elastic/charts'; import { EuiFlexGroup, EuiIcon, EuiIconProps, EuiText } from '@elastic/eui'; import classnames from 'classnames'; -import type { IconPosition, YAxisMode, YConfig } from '../../common/types'; +import type { + IconPosition, + YAxisMode, + ExtendedYConfig, + CollectiveConfig, +} from '../../common/types'; import { getBaseIconPlacement } from '../components'; -import { hasIcon } from './icon'; -import { annotationsIconSet } from './annotations_icon_set'; +import { hasIcon, iconSet } from './icon'; export const LINES_MARKER_SIZE = 20; -// Note: it does not take into consideration whether the reference line is in view or not +type PartialExtendedYConfig = Pick< + ExtendedYConfig, + 'axisMode' | 'icon' | 'iconPosition' | 'textVisibility' +>; + +type PartialCollectiveConfig = Pick; + +const isExtendedYConfig = ( + config: PartialExtendedYConfig | PartialCollectiveConfig | undefined +): config is PartialExtendedYConfig => + (config as PartialExtendedYConfig)?.iconPosition ? true : false; +// Note: it does not take into consideration whether the reference line is in view or not export const getLinesCausedPaddings = ( - visualConfigs: Array< - Pick | undefined - >, + visualConfigs: Array, axesMap: Record<'left' | 'right', unknown> ) => { // collect all paddings for the 4 axis: if any text is detected double it. @@ -31,7 +44,9 @@ export const getLinesCausedPaddings = ( if (!config) { return; } - const { axisMode, icon, iconPosition, textVisibility } = config; + const { axisMode, icon, textVisibility } = config; + const iconPosition = isExtendedYConfig(config) ? config.iconPosition : undefined; + if (axisMode && (hasIcon(icon) || textVisibility)) { const placement = getBaseIconPlacement(iconPosition, axesMap, axisMode); paddings[placement] = Math.max( @@ -48,6 +63,7 @@ export const getLinesCausedPaddings = ( paddings[placement] = LINES_MARKER_SIZE; } }); + return paddings; }; @@ -138,7 +154,7 @@ export const AnnotationIcon = ({ if (isNumericalString(type)) { return ; } - const iconConfig = annotationsIconSet.find((i) => i.value === type); + const iconConfig = iconSet.find((i) => i.value === type); if (!iconConfig) { return null; } diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations_icon_set.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations_icon_set.tsx deleted file mode 100644 index 99b4648e4d556..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations_icon_set.tsx +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; -import { TriangleIcon, CircleIcon } from '../icons'; - -export const annotationsIconSet = [ - { - value: 'asterisk', - label: i18n.translate('expressionXY.xyChart.iconSelect.asteriskIconLabel', { - defaultMessage: 'Asterisk', - }), - }, - { - value: 'alert', - label: i18n.translate('expressionXY.xyChart.iconSelect.alertIconLabel', { - defaultMessage: 'Alert', - }), - }, - { - value: 'bell', - label: i18n.translate('expressionXY.xyChart.iconSelect.bellIconLabel', { - defaultMessage: 'Bell', - }), - }, - { - value: 'bolt', - label: i18n.translate('expressionXY.xyChart.iconSelect.boltIconLabel', { - defaultMessage: 'Bolt', - }), - }, - { - value: 'bug', - label: i18n.translate('expressionXY.xyChart.iconSelect.bugIconLabel', { - defaultMessage: 'Bug', - }), - }, - { - value: 'circle', - label: i18n.translate('expressionXY.xyChart.iconSelect.circleIconLabel', { - defaultMessage: 'Circle', - }), - icon: CircleIcon, - canFill: true, - }, - - { - value: 'editorComment', - label: i18n.translate('expressionXY.xyChart.iconSelect.commentIconLabel', { - defaultMessage: 'Comment', - }), - }, - { - value: 'flag', - label: i18n.translate('expressionXY.xyChart.iconSelect.flagIconLabel', { - defaultMessage: 'Flag', - }), - }, - { - value: 'heart', - label: i18n.translate('expressionXY.xyChart.iconSelect.heartLabel', { - defaultMessage: 'Heart', - }), - }, - { - value: 'mapMarker', - label: i18n.translate('expressionXY.xyChart.iconSelect.mapMarkerLabel', { - defaultMessage: 'Map Marker', - }), - }, - { - value: 'pinFilled', - label: i18n.translate('expressionXY.xyChart.iconSelect.mapPinLabel', { - defaultMessage: 'Map Pin', - }), - }, - { - value: 'starEmpty', - label: i18n.translate('expressionXY.xyChart.iconSelect.starLabel', { defaultMessage: 'Star' }), - }, - { - value: 'tag', - label: i18n.translate('expressionXY.xyChart.iconSelect.tagIconLabel', { - defaultMessage: 'Tag', - }), - }, - { - value: 'triangle', - label: i18n.translate('expressionXY.xyChart.iconSelect.triangleIconLabel', { - defaultMessage: 'Triangle', - }), - icon: TriangleIcon, - shouldRotate: true, - canFill: true, - }, -]; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.test.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.test.ts index c5f2f6d7151e5..f3abf76b2d05a 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.test.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { DataLayerConfigResult } from '../../common'; +import { DataLayerConfig } from '../../common'; import { LayerTypes } from '../../common/constants'; import { Datatable } from '@kbn/expressions-plugin/public'; import { getAxesConfiguration } from './axes_configuration'; @@ -220,9 +220,9 @@ describe('axes_configuration', () => { }, }; - const sampleLayer: DataLayerConfigResult = { - type: 'dataLayer', + const sampleLayer: DataLayerConfig = { layerId: 'first', + type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'c', @@ -233,11 +233,12 @@ describe('axes_configuration', () => { yScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, + table: tables.first, }; it('should map auto series to left axis', () => { const formatFactory = jest.fn(); - const groups = getAxesConfiguration([sampleLayer], false, tables, formatFactory); + const groups = getAxesConfiguration([sampleLayer], false, formatFactory); expect(groups.length).toEqual(1); expect(groups[0].position).toEqual('left'); expect(groups[0].series[0].accessor).toEqual('yAccessorId'); @@ -247,7 +248,7 @@ describe('axes_configuration', () => { it('should map auto series to right axis if formatters do not match', () => { const formatFactory = jest.fn(); const twoSeriesLayer = { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId2'] }; - const groups = getAxesConfiguration([twoSeriesLayer], false, tables, formatFactory); + const groups = getAxesConfiguration([twoSeriesLayer], false, formatFactory); expect(groups.length).toEqual(2); expect(groups[0].position).toEqual('left'); expect(groups[1].position).toEqual('right'); @@ -261,7 +262,7 @@ describe('axes_configuration', () => { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId2', 'yAccessorId3'], }; - const groups = getAxesConfiguration([threeSeriesLayer], false, tables, formatFactory); + const groups = getAxesConfiguration([threeSeriesLayer], false, formatFactory); expect(groups.length).toEqual(2); expect(groups[0].position).toEqual('left'); expect(groups[1].position).toEqual('right'); @@ -280,7 +281,6 @@ describe('axes_configuration', () => { }, ], false, - tables, formatFactory ); expect(groups.length).toEqual(1); @@ -300,7 +300,6 @@ describe('axes_configuration', () => { }, ], false, - tables, formatFactory ); expect(groups.length).toEqual(2); @@ -324,7 +323,6 @@ describe('axes_configuration', () => { }, ], false, - tables, formatFactory ); expect(formatFactory).toHaveBeenCalledTimes(2); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts index 38d2220904226..65f5441d67226 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts @@ -6,22 +6,25 @@ * Side Public License, v 1. */ -import { Datatable } from '@kbn/expressions-plugin/public'; import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; import { FormatFactory } from '../types'; -import { AxisExtentConfig, DataLayerConfigResult } from '../../common'; +import { AxisExtentConfig, CommonXYDataLayerConfig, ExtendedYConfig, YConfig } from '../../common'; +import { isDataLayer } from './visualization'; -interface FormattedMetric { +export interface Series { layer: string; accessor: string; +} + +interface FormattedMetric extends Series { fieldFormat: SerializedFieldFormat; } export type GroupsConfiguration = Array<{ - groupId: string; + groupId: 'left' | 'right'; position: 'left' | 'right' | 'bottom' | 'top'; formatter?: IFieldFormat; - series: Array<{ layer: string; accessor: string }>; + series: Series[]; }>; export function isFormatterCompatible( @@ -31,10 +34,7 @@ export function isFormatterCompatible( return formatter1.id === formatter2.id; } -export function groupAxesByType( - layers: DataLayerConfigResult[], - tables?: Record -) { +export function groupAxesByType(layers: CommonXYDataLayerConfig[]) { const series: { auto: FormattedMetric[]; left: FormattedMetric[]; @@ -47,15 +47,19 @@ export function groupAxesByType( bottom: [], }; - layers?.forEach((layer) => { - const table = tables?.[layer.layerId]; + layers.forEach((layer) => { + const { table } = layer; layer.accessors.forEach((accessor) => { + const yConfig: Array | undefined = layer.yConfig; const mode = - layer.yConfig?.find((yAxisConfig) => yAxisConfig.forAccessor === accessor)?.axisMode || - 'auto'; - let formatter: SerializedFieldFormat = table?.columns.find((column) => column.id === accessor) + yConfig?.find((yAxisConfig) => yAxisConfig.forAccessor === accessor)?.axisMode || 'auto'; + let formatter: SerializedFieldFormat = table.columns?.find((column) => column.id === accessor) ?.meta?.params || { id: 'number' }; - if (layer.seriesType.includes('percentage') && formatter.id !== 'percent') { + if ( + isDataLayer(layer) && + layer.seriesType.includes('percentage') && + formatter.id !== 'percent' + ) { formatter = { id: 'percent', params: { @@ -71,10 +75,12 @@ export function groupAxesByType( }); }); + const tablesExist = layers.filter(({ table }) => Boolean(table)).length > 0; + series.auto.forEach((currentSeries) => { if ( series.left.length === 0 || - (tables && + (tablesExist && series.left.every((leftSeries) => isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) )) @@ -82,7 +88,7 @@ export function groupAxesByType( series.left.push(currentSeries); } else if ( series.right.length === 0 || - (tables && + (tablesExist && series.left.every((leftSeries) => isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) )) @@ -98,12 +104,11 @@ export function groupAxesByType( } export function getAxesConfiguration( - layers: DataLayerConfigResult[], + layers: CommonXYDataLayerConfig[], shouldRotate: boolean, - tables?: Record, formatFactory?: FormatFactory ): GroupsConfiguration { - const series = groupAxesByType(layers, tables); + const series = groupAxesByType(layers); const axisGroups: GroupsConfiguration = []; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts index bd13e3217c2af..8b1bdeeadb834 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts @@ -7,76 +7,76 @@ */ import { getColorAssignments } from './color_assignment'; -import type { DataLayerConfigResult, LensMultiTable } from '../../common'; +import type { DataLayerConfig } from '../../common'; import type { FormatFactory } from '../types'; import { LayerTypes } from '../../common/constants'; +import { Datatable } from '@kbn/expressions-plugin'; describe('color_assignment', () => { - const layers: DataLayerConfigResult[] = [ + const tables: Record = { + '1': { + type: 'datatable', + columns: [ + { id: 'split1', name: '', meta: { type: 'number' } }, + { id: 'y1', name: '', meta: { type: 'number' } }, + { id: 'y2', name: '', meta: { type: 'number' } }, + ], + rows: [ + { split1: 1 }, + { split1: 2 }, + { split1: 3 }, + { split1: 1 }, + { split1: 2 }, + { split1: 3 }, + ], + }, + '2': { + type: 'datatable', + columns: [ + { id: 'split2', name: '', meta: { type: 'number' } }, + { id: 'y1', name: '', meta: { type: 'number' } }, + { id: 'y2', name: '', meta: { type: 'number' } }, + ], + rows: [ + { split2: 1 }, + { split2: 2 }, + { split2: 3 }, + { split2: 1 }, + { split2: 2 }, + { split2: 3 }, + ], + }, + }; + + const layers: DataLayerConfig[] = [ { + layerId: 'first', type: 'dataLayer', yScaleType: 'linear', xScaleType: 'linear', isHistogram: true, seriesType: 'bar', palette: { type: 'palette', name: 'palette1' }, - layerId: '1', layerType: LayerTypes.DATA, splitAccessor: 'split1', accessors: ['y1', 'y2'], + table: tables['1'], }, { + layerId: 'second', type: 'dataLayer', yScaleType: 'linear', xScaleType: 'linear', isHistogram: true, seriesType: 'bar', palette: { type: 'palette', name: 'palette2' }, - layerId: '2', layerType: LayerTypes.DATA, splitAccessor: 'split2', accessors: ['y3', 'y4'], + table: tables['2'], }, ]; - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - '1': { - type: 'datatable', - columns: [ - { id: 'split1', name: '', meta: { type: 'number' } }, - { id: 'y1', name: '', meta: { type: 'number' } }, - { id: 'y2', name: '', meta: { type: 'number' } }, - ], - rows: [ - { split1: 1 }, - { split1: 2 }, - { split1: 3 }, - { split1: 1 }, - { split1: 2 }, - { split1: 3 }, - ], - }, - '2': { - type: 'datatable', - columns: [ - { id: 'split2', name: '', meta: { type: 'number' } }, - { id: 'y1', name: '', meta: { type: 'number' } }, - { id: 'y2', name: '', meta: { type: 'number' } }, - ], - rows: [ - { split2: 1 }, - { split2: 2 }, - { split2: 3 }, - { split2: 1 }, - { split2: 2 }, - { split2: 3 }, - ], - }, - }, - }; - const formatFactory = (() => ({ convert(x: unknown) { @@ -86,7 +86,7 @@ describe('color_assignment', () => { describe('totalSeriesCount', () => { it('should calculate total number of series per palette', () => { - const assignments = getColorAssignments(layers, data, formatFactory); + const assignments = getColorAssignments(layers, formatFactory); // two y accessors, with 3 splitted series expect(assignments.palette1.totalSeriesCount).toEqual(2 * 3); expect(assignments.palette2.totalSeriesCount).toEqual(2 * 3); @@ -95,7 +95,6 @@ describe('color_assignment', () => { it('should calculate total number of series spanning multible layers', () => { const assignments = getColorAssignments( [layers[0], { ...layers[1], palette: layers[0].palette }], - data, formatFactory ); // two y accessors, with 3 splitted series, two times @@ -106,7 +105,6 @@ describe('color_assignment', () => { it('should calculate total number of series for non split series', () => { const assignments = getColorAssignments( [layers[0], { ...layers[1], palette: layers[0].palette, splitAccessor: undefined }], - data, formatFactory ); // two y accessors, with 3 splitted series for the first layer, 2 non splitted y accessors for the second layer @@ -117,15 +115,16 @@ describe('color_assignment', () => { it('should format non-primitive values and count them correctly', () => { const complexObject = { aProp: 123 }; const formatMock = jest.fn((x) => 'formatted'); - const assignments = getColorAssignments( - layers, + const newLayers = [ { - ...data, - tables: { - ...data.tables, - '1': { ...data.tables['1'], rows: [{ split1: complexObject }, { split1: 'abc' }] }, - }, + ...layers[0], + table: { ...tables['1'], rows: [{ split1: complexObject }, { split1: 'abc' }] }, }, + layers[1], + ]; + + const assignments = getColorAssignments( + newLayers, (() => ({ convert: formatMock, @@ -137,26 +136,18 @@ describe('color_assignment', () => { }); it('should handle missing tables', () => { - const assignments = getColorAssignments(layers, { ...data, tables: {} }, formatFactory); + const assignments = getColorAssignments( + layers.map((l) => ({ ...l, table: {} as any })), + formatFactory + ); // if there is no data, just assume a single split expect(assignments.palette1.totalSeriesCount).toEqual(2); }); it('should handle missing columns', () => { - const assignments = getColorAssignments( - layers, - { - ...data, - tables: { - ...data.tables, - '1': { - ...data.tables['1'], - columns: [], - }, - }, - }, - formatFactory - ); + const newLayers = [{ ...layers[0], table: { ...tables['1'], columns: [] } }, layers[1]]; + const assignments = getColorAssignments(newLayers, formatFactory); + // if the split column is missing, just assume a single split expect(assignments.palette1.totalSeriesCount).toEqual(2); }); @@ -164,7 +155,7 @@ describe('color_assignment', () => { describe('getRank', () => { it('should return the correct rank for a series key', () => { - const assignments = getColorAssignments(layers, data, formatFactory); + const assignments = getColorAssignments(layers, formatFactory); // 3 series in front of 2/y2 - 1/y1, 1/y2 and 2/y1 expect(assignments.palette1.getRank(layers[0], '2', 'y2')).toEqual(3); // 1 series in front of 1/y4 - 1/y3 @@ -173,7 +164,7 @@ describe('color_assignment', () => { it('should return the correct rank for a series key spanning multiple layers', () => { const newLayers = [layers[0], { ...layers[1], palette: layers[0].palette }]; - const assignments = getColorAssignments(newLayers, data, formatFactory); + const assignments = getColorAssignments(newLayers, formatFactory); // 3 series in front of 2/y2 - 1/y1, 1/y2 and 2/y1 expect(assignments.palette1.getRank(newLayers[0], '2', 'y2')).toEqual(3); // 2 series in front for the current layer (1/y3, 1/y4), plus all 6 series from the first layer @@ -185,7 +176,7 @@ describe('color_assignment', () => { layers[0], { ...layers[1], palette: layers[0].palette, splitAccessor: undefined }, ]; - const assignments = getColorAssignments(newLayers, data, formatFactory); + const assignments = getColorAssignments(newLayers, formatFactory); // 3 series in front of 2/y2 - 1/y1, 1/y2 and 2/y1 expect(assignments.palette1.getRank(newLayers[0], '2', 'y2')).toEqual(3); // 1 series in front for the current layer (y3), plus all 6 series from the first layer @@ -193,15 +184,16 @@ describe('color_assignment', () => { }); it('should return the correct rank for a series with a non-primitive value', () => { - const assignments = getColorAssignments( - layers, + const newLayers = [ { - ...data, - tables: { - ...data.tables, - '1': { ...data.tables['1'], rows: [{ split1: 'abc' }, { split1: { aProp: 123 } }] }, - }, + ...layers[0], + table: { ...tables['1'], rows: [{ split1: 'abc' }, { split1: { aProp: 123 } }] }, }, + layers[1], + ]; + + const assignments = getColorAssignments( + newLayers, (() => ({ convert: () => 'formatted', @@ -212,26 +204,19 @@ describe('color_assignment', () => { }); it('should handle missing tables', () => { - const assignments = getColorAssignments(layers, { ...data, tables: {} }, formatFactory); + const assignments = getColorAssignments( + layers.map((l) => ({ ...l, table: {} as any })), + formatFactory + ); // if there is no data, assume it is the first splitted series. One series in front - 0/y1 expect(assignments.palette1.getRank(layers[0], '2', 'y2')).toEqual(1); }); it('should handle missing columns', () => { - const assignments = getColorAssignments( - layers, - { - ...data, - tables: { - ...data.tables, - '1': { - ...data.tables['1'], - columns: [], - }, - }, - }, - formatFactory - ); + const newLayers = [{ ...layers[0], table: { ...tables['1'], columns: [] } }, layers[1]]; + + const assignments = getColorAssignments(newLayers, formatFactory); + // if the split column is missing, assume it is the first splitted series. One series in front - 0/y1 expect(assignments.palette1.getRank(layers[0], '2', 'y2')).toEqual(1); }); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts index ee2f5a0325287..e94d22471aba9 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts @@ -8,10 +8,9 @@ import { uniq, mapValues } from 'lodash'; import { euiLightVars } from '@kbn/ui-theme'; -import type { Datatable } from '@kbn/expressions-plugin'; import { FormatFactory } from '../types'; import { isDataLayer } from './visualization'; -import { DataLayerConfigResult, XYLayerConfigResult } from '../../common'; +import { CommonXYDataLayerConfig, CommonXYLayerConfig } from '../../common'; const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; @@ -21,40 +20,41 @@ export type ColorAssignments = Record< string, { totalSeriesCount: number; - getRank(sortedLayer: DataLayerConfigResult, seriesKey: string, yAccessor: string): number; + getRank(sortedLayer: CommonXYDataLayerConfig, seriesKey: string, yAccessor: string): number; } >; export function getColorAssignments( - layers: XYLayerConfigResult[], - data: { tables: Record }, + layers: CommonXYLayerConfig[], formatFactory: FormatFactory ): ColorAssignments { - const layersPerPalette: Record = {}; + const layersPerPalette: Record = {}; - layers - .filter((layer): layer is DataLayerConfigResult => isDataLayer(layer)) - .forEach((layer) => { - const palette = layer.palette?.name || 'default'; - if (!layersPerPalette[palette]) { - layersPerPalette[palette] = []; - } - layersPerPalette[palette].push(layer); - }); + layers.forEach((layer) => { + if (!isDataLayer(layer)) { + return; + } + + const palette = layer.palette?.name || 'default'; + if (!layersPerPalette[palette]) { + layersPerPalette[palette] = []; + } + layersPerPalette[palette].push(layer); + }); return mapValues(layersPerPalette, (paletteLayers) => { - const seriesPerLayer = paletteLayers.map((layer, layerIndex) => { + const seriesPerLayer = paletteLayers.map((layer) => { if (!layer.splitAccessor) { return { numberOfSeries: layer.accessors.length, splits: [] }; } const splitAccessor = layer.splitAccessor; - const column = data.tables[layer.layerId]?.columns.find(({ id }) => id === splitAccessor); + const column = layer.table.columns?.find(({ id }) => id === splitAccessor); const columnFormatter = column && formatFactory(column.meta.params); const splits = - !column || !data.tables[layer.layerId] + !column || !layer.table ? [] : uniq( - data.tables[layer.layerId].rows.map((row) => { + layer.table.rows.map((row) => { let value = row[splitAccessor]; if (value && !isPrimitive(value)) { value = columnFormatter?.convert(value) ?? value; @@ -72,8 +72,10 @@ export function getColorAssignments( ); return { totalSeriesCount, - getRank(sortedLayer: DataLayerConfigResult, seriesKey: string, yAccessor: string) { - const layerIndex = paletteLayers.findIndex((l) => sortedLayer.layerId === l.layerId); + getRank(sortedLayer: CommonXYDataLayerConfig, seriesKey: string, yAccessor: string) { + const layerIndex = paletteLayers.findIndex( + (layer) => sortedLayer.layerId === layer.layerId + ); const currentSeriesPerLayer = seriesPerLayer[layerIndex]; const splitRank = currentSeriesPerLayer.splits.indexOf(seriesKey); return ( diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx new file mode 100644 index 0000000000000..07af8a3c408c2 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx @@ -0,0 +1,332 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { + AreaSeriesProps, + BarSeriesProps, + ColorVariant, + LineSeriesProps, + ScaleType, + SeriesName, + StackMode, + XYChartSeriesIdentifier, +} from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import { + FieldFormat, + FieldFormatParams, + IFieldFormat, + SerializedFieldFormat, +} from '@kbn/field-formats-plugin/common'; +import { Datatable } from '@kbn/expressions-plugin'; +import { PaletteRegistry, SeriesLayer } from '@kbn/coloring'; +import { CommonXYDataLayerConfig, XScaleType } from '../../common'; +import { FormatFactory } from '../types'; +import { getSeriesColor } from './state'; +import { ColorAssignments } from './color_assignment'; +import { GroupsConfiguration } from './axes_configuration'; + +type SeriesSpec = LineSeriesProps & BarSeriesProps & AreaSeriesProps; + +type GetSeriesPropsFn = (config: { + layer: CommonXYDataLayerConfig; + accessor: string; + chartHasMoreThanOneBarSeries?: boolean; + formatFactory: FormatFactory; + colorAssignments: ColorAssignments; + columnToLabelMap: Record; + paletteService: PaletteRegistry; + syncColors?: boolean; + yAxis?: GroupsConfiguration[number]; + timeZone?: string; + emphasizeFitting?: boolean; + fillOpacity?: number; + formattedDatatableInfo: DatatableWithFormatInfo; +}) => SeriesSpec; + +type GetSeriesNameFn = ( + data: XYChartSeriesIdentifier, + config: { + layer: CommonXYDataLayerConfig; + splitHint: SerializedFieldFormat | undefined; + splitFormatter: FieldFormat; + alreadyFormattedColumns: Record; + columnToLabelMap: Record; + } +) => SeriesName; + +type GetColorFn = ( + seriesIdentifier: XYChartSeriesIdentifier, + config: { + layer: CommonXYDataLayerConfig; + accessor: string; + colorAssignments: ColorAssignments; + columnToLabelMap: Record; + paletteService: PaletteRegistry; + syncColors?: boolean; + } +) => string | null; + +export interface DatatableWithFormatInfo { + table: Datatable; + formattedColumns: Record; +} + +export type DatatablesWithFormatInfo = Record; + +export type FormattedDatatables = Record; + +const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; + +export const getFormattedRow = ( + row: Datatable['rows'][number], + columns: Datatable['columns'], + columnsFormatters: Record, + xAccessor: string | undefined, + xScaleType: XScaleType +): { row: Datatable['rows'][number]; formattedColumns: Record } => + columns.reduce( + (formattedInfo, { id }) => { + const record = formattedInfo.row[id]; + if ( + record != null && + // pre-format values for ordinal x axes because there can only be a single x axis formatter on chart level + (!isPrimitive(record) || (id === xAccessor && xScaleType === 'ordinal')) + ) { + return { + row: { ...formattedInfo.row, [id]: columnsFormatters[id]!.convert(record) }, + formattedColumns: { ...formattedInfo.formattedColumns, [id]: true }, + }; + } + return formattedInfo; + }, + { row, formattedColumns: {} } + ); + +export const getFormattedTable = ( + table: Datatable, + formatFactory: FormatFactory, + xAccessor: string | undefined, + xScaleType: XScaleType +): { table: Datatable; formattedColumns: Record } => { + const columnsFormatters = table.columns.reduce>( + (formatters, { id, meta }) => ({ ...formatters, [id]: formatFactory(meta.params) }), + {} + ); + + const formattedTableInfo = table.rows.reduce<{ + rows: Datatable['rows']; + formattedColumns: Record; + }>( + ({ rows: formattedRows, formattedColumns }, row) => { + const formattedRowInfo = getFormattedRow( + row, + table.columns, + columnsFormatters, + xAccessor, + xScaleType + ); + return { + rows: [...formattedRows, formattedRowInfo.row], + formattedColumns: { ...formattedColumns, ...formattedRowInfo.formattedColumns }, + }; + }, + { + rows: [], + formattedColumns: {}, + } + ); + + return { + table: { ...table, rows: formattedTableInfo.rows }, + formattedColumns: formattedTableInfo.formattedColumns, + }; +}; + +export const getFormattedTablesByLayers = ( + layers: CommonXYDataLayerConfig[], + formatFactory: FormatFactory +): DatatablesWithFormatInfo => + layers.reduce( + (formattedDatatables, { layerId, table, xAccessor, xScaleType }) => ({ + ...formattedDatatables, + [layerId]: getFormattedTable(table, formatFactory, xAccessor, xScaleType), + }), + {} + ); + +const getSeriesName: GetSeriesNameFn = ( + data, + { layer, splitHint, splitFormatter, alreadyFormattedColumns, columnToLabelMap } +) => { + // For multiple y series, the name of the operation is used on each, either: + // * Key - Y name + // * Formatted value - Y name + if (layer.splitAccessor && layer.accessors.length > 1) { + const formatted = alreadyFormattedColumns[layer.splitAccessor]; + const result = data.seriesKeys + .map((key: string | number, i) => { + if (i === 0 && splitHint && layer.splitAccessor && !formatted) { + return splitFormatter.convert(key); + } + return layer.splitAccessor && i === 0 ? key : columnToLabelMap[key] ?? null; + }) + .join(' - '); + return result; + } + + // For formatted split series, format the key + // This handles splitting by dates, for example + if (splitHint) { + if (layer.splitAccessor && alreadyFormattedColumns[layer.splitAccessor]) { + return data.seriesKeys[0]; + } + return splitFormatter.convert(data.seriesKeys[0]); + } + // This handles both split and single-y cases: + // * If split series without formatting, show the value literally + // * If single Y, the seriesKey will be the accessor, so we show the human-readable name + return layer.splitAccessor ? data.seriesKeys[0] : columnToLabelMap[data.seriesKeys[0]] ?? null; +}; + +const getPointConfig = (xAccessor?: string, emphasizeFitting?: boolean) => ({ + visible: !xAccessor, + radius: xAccessor && !emphasizeFitting ? 5 : 0, +}); + +const getLineConfig = () => ({ visible: true, stroke: ColorVariant.Series, opacity: 1, dash: [] }); + +const getColor: GetColorFn = ( + { yAccessor, seriesKeys }, + { layer, accessor, colorAssignments, columnToLabelMap, paletteService, syncColors } +) => { + const overwriteColor = getSeriesColor(layer, accessor); + if (overwriteColor !== null) { + return overwriteColor; + } + const colorAssignment = colorAssignments[layer.palette.name]; + const seriesLayers: SeriesLayer[] = [ + { + name: layer.splitAccessor ? String(seriesKeys[0]) : columnToLabelMap[seriesKeys[0]], + totalSeriesAtDepth: colorAssignment.totalSeriesCount, + rankAtDepth: colorAssignment.getRank(layer, String(seriesKeys[0]), String(yAccessor)), + }, + ]; + return paletteService.get(layer.palette.name).getCategoricalColor( + seriesLayers, + { + maxDepth: 1, + behindText: false, + totalSeries: colorAssignment.totalSeriesCount, + syncColors, + }, + layer.palette.params + ); +}; + +export const getSeriesProps: GetSeriesPropsFn = ({ + layer, + accessor, + chartHasMoreThanOneBarSeries, + colorAssignments, + formatFactory, + columnToLabelMap, + paletteService, + syncColors, + yAxis, + timeZone, + emphasizeFitting, + fillOpacity, + formattedDatatableInfo, +}): SeriesSpec => { + const { table } = layer; + const isStacked = layer.seriesType.includes('stacked'); + const isPercentage = layer.seriesType.includes('percentage'); + const isBarChart = layer.seriesType.includes('bar'); + const enableHistogramMode = + layer.isHistogram && + (isStacked || !layer.splitAccessor) && + (isStacked || !isBarChart || !chartHasMoreThanOneBarSeries); + + const formatter = table?.columns.find((column) => column.id === accessor)?.meta?.params; + const splitHint = table?.columns.find((col) => col.id === layer.splitAccessor)?.meta?.params; + const splitFormatter = formatFactory(splitHint); + + // what if row values are not primitive? That is the case of, for instance, Ranges + // remaps them to their serialized version with the formatHint metadata + // In order to do it we need to make a copy of the table as the raw one is required for more features (filters, etc...) later on + const { table: formattedTable, formattedColumns } = formattedDatatableInfo; + + // For date histogram chart type, we're getting the rows that represent intervals without data. + // To not display them in the legend, they need to be filtered out. + let rows = formattedTable.rows.filter( + (row) => + !(layer.xAccessor && typeof row[layer.xAccessor] === 'undefined') && + !( + layer.splitAccessor && + typeof row[layer.splitAccessor] === 'undefined' && + typeof row[accessor] === 'undefined' + ) + ); + + if (!layer.xAccessor) { + rows = rows.map((row) => ({ + ...row, + unifiedX: i18n.translate('expressionXY.xyChart.emptyXLabel', { + defaultMessage: '(empty)', + }), + })); + } + + return { + splitSeriesAccessors: layer.splitAccessor ? [layer.splitAccessor] : [], + stackAccessors: isStacked ? [layer.xAccessor as string] : [], + id: layer.splitAccessor ? `${layer.splitAccessor}-${accessor}` : `${accessor}`, + xAccessor: layer.xAccessor || 'unifiedX', + yAccessors: [accessor], + data: rows, + xScaleType: layer.xAccessor ? layer.xScaleType : 'ordinal', + yScaleType: + formatter?.id === 'bytes' && layer.yScaleType === ScaleType.Linear + ? ScaleType.LinearBinary + : layer.yScaleType, + color: (series) => + getColor(series, { + layer, + accessor, + colorAssignments, + columnToLabelMap, + paletteService, + syncColors, + }), + groupId: yAxis?.groupId, + enableHistogramMode, + stackMode: isPercentage ? StackMode.Percentage : undefined, + timeZone, + areaSeriesStyle: { + point: getPointConfig(layer.xAccessor, emphasizeFitting), + ...(fillOpacity && { area: { opacity: fillOpacity } }), + ...(emphasizeFitting && { + fit: { area: { opacity: fillOpacity || 0.5 }, line: getLineConfig() }, + }), + }, + lineSeriesStyle: { + point: getPointConfig(layer.xAccessor, emphasizeFitting), + ...(emphasizeFitting && { fit: { line: getLineConfig() } }), + }, + name(d) { + return getSeriesName(d, { + layer, + splitHint, + splitFormatter, + alreadyFormattedColumns: formattedColumns, + columnToLabelMap, + }); + }, + }; +}; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/fitting_functions.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/fitting_functions.ts index 43d5ad9b4c19f..4c26caf59d8d3 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/fitting_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/fitting_functions.ts @@ -8,6 +8,7 @@ import { Fit } from '@elastic/charts'; import { EndValue, FittingFunction } from '../../common'; +import { EndValues } from '../../common/constants'; export function getFitEnum(fittingFunction?: FittingFunction | EndValue) { if (fittingFunction) { @@ -17,10 +18,10 @@ export function getFitEnum(fittingFunction?: FittingFunction | EndValue) { } export function getEndValue(endValue?: EndValue) { - if (endValue === 'Nearest') { + if (endValue === EndValues.NEAREST) { return Fit[endValue]; } - if (endValue === 'Zero') { + if (endValue === EndValues.ZERO) { return 0; } return undefined; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/icon.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/icon.ts index 57e285a07232f..8b4113b3ada11 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/icon.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/icon.ts @@ -6,6 +6,107 @@ * Side Public License, v 1. */ +import { i18n } from '@kbn/i18n'; +import { TriangleIcon, CircleIcon } from '../icons'; +import { AvailableReferenceLineIcons } from '../../common/constants'; + export function hasIcon(icon: string | undefined): icon is string { return icon != null && icon !== 'empty'; } + +export const iconSet = [ + { + value: AvailableReferenceLineIcons.EMPTY, + label: i18n.translate('expressionXY.xyChart.iconSelect.noIconLabel', { + defaultMessage: 'None', + }), + }, + { + value: AvailableReferenceLineIcons.ASTERISK, + label: i18n.translate('expressionXY.xyChart.iconSelect.asteriskIconLabel', { + defaultMessage: 'Asterisk', + }), + }, + { + value: AvailableReferenceLineIcons.ALERT, + label: i18n.translate('expressionXY.xyChart.iconSelect.alertIconLabel', { + defaultMessage: 'Alert', + }), + }, + { + value: AvailableReferenceLineIcons.BELL, + label: i18n.translate('expressionXY.xyChart.iconSelect.bellIconLabel', { + defaultMessage: 'Bell', + }), + }, + { + value: AvailableReferenceLineIcons.BOLT, + label: i18n.translate('expressionXY.xyChart.iconSelect.boltIconLabel', { + defaultMessage: 'Bolt', + }), + }, + { + value: AvailableReferenceLineIcons.BUG, + label: i18n.translate('expressionXY.xyChart.iconSelect.bugIconLabel', { + defaultMessage: 'Bug', + }), + }, + { + value: AvailableReferenceLineIcons.CIRCLE, + label: i18n.translate('expressionXY.xyChart.iconSelect.circleIconLabel', { + defaultMessage: 'Circle', + }), + icon: CircleIcon, + canFill: true, + }, + + { + value: AvailableReferenceLineIcons.EDITOR_COMMENT, + label: i18n.translate('expressionXY.xyChart.iconSelect.commentIconLabel', { + defaultMessage: 'Comment', + }), + }, + { + value: AvailableReferenceLineIcons.FLAG, + label: i18n.translate('expressionXY.xyChart.iconSelect.flagIconLabel', { + defaultMessage: 'Flag', + }), + }, + { + value: AvailableReferenceLineIcons.HEART, + label: i18n.translate('expressionXY.xyChart.iconSelect.heartLabel', { + defaultMessage: 'Heart', + }), + }, + { + value: AvailableReferenceLineIcons.MAP_MARKER, + label: i18n.translate('expressionXY.xyChart.iconSelect.mapMarkerLabel', { + defaultMessage: 'Map Marker', + }), + }, + { + value: AvailableReferenceLineIcons.PIN_FILLED, + label: i18n.translate('expressionXY.xyChart.iconSelect.mapPinLabel', { + defaultMessage: 'Map Pin', + }), + }, + { + value: AvailableReferenceLineIcons.STAR_EMPTY, + label: i18n.translate('expressionXY.xyChart.iconSelect.starLabel', { defaultMessage: 'Star' }), + }, + { + value: AvailableReferenceLineIcons.TAG, + label: i18n.translate('expressionXY.xyChart.iconSelect.tagIconLabel', { + defaultMessage: 'Tag', + }), + }, + { + value: AvailableReferenceLineIcons.TRIANGLE, + label: i18n.translate('expressionXY.xyChart.iconSelect.triangleIconLabel', { + defaultMessage: 'Triangle', + }), + icon: TriangleIcon, + shouldRotate: true, + canFill: true, + }, +]; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts index 2bb3a5a927774..773ae4ee22d94 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts @@ -14,5 +14,5 @@ export * from './fitting_functions'; export * from './axes_configuration'; export * from './icon'; export * from './color_assignment'; -export * from './annotations_icon_set'; export * from './annotations'; +export * from './data_layers'; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts index 0fe979b8c3fc1..6721c293dbe57 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts @@ -6,32 +6,36 @@ * Side Public License, v 1. */ -import { DataLayerConfigResult, XYChartProps } from '../../common'; +import { DataLayerConfig, XYChartProps } from '../../common'; import { sampleArgs } from '../../common/__mocks__'; import { calculateMinInterval } from './interval'; describe('calculateMinInterval', () => { let xyProps: XYChartProps; - + let layer: DataLayerConfig; beforeEach(() => { - xyProps = sampleArgs(); - (xyProps.args.layers[0] as DataLayerConfigResult).xScaleType = 'time'; + const { layers, ...restArgs } = sampleArgs().args; + + xyProps = { args: { ...restArgs, layers } }; + layer = xyProps.args.layers[0] as DataLayerConfig; + layer.xScaleType = 'time'; }); it('should use first valid layer and determine interval', async () => { - xyProps.data.tables.first.columns[2].meta.source = 'esaggs'; - xyProps.data.tables.first.columns[2].meta.sourceParams = { + layer.table.columns[2].meta.source = 'esaggs'; + layer.table.columns[2].meta.sourceParams = { type: 'date_histogram', params: { used_interval: '5m', }, }; + xyProps.args.layers[0] = layer; const result = await calculateMinInterval(xyProps); expect(result).toEqual(5 * 60 * 1000); }); it('should return interval of number histogram if available on first x axis columns', async () => { - (xyProps.args.layers[0] as DataLayerConfigResult).xScaleType = 'linear'; - xyProps.data.tables.first.columns[2].meta = { + layer.xScaleType = 'linear'; + layer.table.columns[2].meta = { source: 'esaggs', type: 'number', field: 'someField', @@ -43,19 +47,22 @@ describe('calculateMinInterval', () => { }, }, }; + xyProps.args.layers[0] = layer; const result = await calculateMinInterval(xyProps); expect(result).toEqual(5); }); it('should return undefined if data table is empty', async () => { - xyProps.data.tables.first.rows = []; - xyProps.data.tables.first.columns[2].meta.source = 'esaggs'; - xyProps.data.tables.first.columns[2].meta.sourceParams = { + layer.table.rows = []; + layer.table.columns[2].meta.source = 'esaggs'; + layer.table.columns[2].meta.sourceParams = { type: 'date_histogram', params: { used_interval: '5m', }, }; + + xyProps.args.layers[0] = layer; const result = await calculateMinInterval(xyProps); expect(result).toEqual(undefined); }); @@ -66,14 +73,16 @@ describe('calculateMinInterval', () => { }); it('should return undefined if date column is not found', async () => { - xyProps.data.tables.first.columns.splice(2, 1); + layer.table.columns.splice(2, 1); + xyProps.args.layers[0] = layer; const result = await calculateMinInterval(xyProps); expect(result).toEqual(undefined); }); it('should return undefined if x axis is not a date', async () => { - (xyProps.args.layers[0] as DataLayerConfigResult).xScaleType = 'ordinal'; - xyProps.data.tables.first.columns.splice(2, 1); + layer.xScaleType = 'ordinal'; + xyProps.args.layers[0] = layer; + xyProps.args.layers[0].table.columns.splice(2, 1); const result = await calculateMinInterval(xyProps); expect(result).toEqual(undefined); }); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.ts index b81fcd33fccad..17e7a9c2aba32 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.ts @@ -11,11 +11,11 @@ import { XYChartProps } from '../../common'; import { getFilteredLayers } from './layers'; import { isDataLayer } from './visualization'; -export function calculateMinInterval({ args: { layers }, data }: XYChartProps) { - const filteredLayers = getFilteredLayers(layers, data); +export function calculateMinInterval({ args: { layers } }: XYChartProps) { + const filteredLayers = getFilteredLayers(layers); if (filteredLayers.length === 0) return; const isTimeViz = filteredLayers.every((l) => isDataLayer(l) && l.xScaleType === 'time'); - const xColumn = data.tables[filteredLayers[0].layerId].columns.find( + const xColumn = filteredLayers[0].table.columns.find( (column) => isDataLayer(filteredLayers[0]) && column.id === filteredLayers[0].xAccessor ); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts index be1701e6b6e4b..4408ebd3feb84 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts @@ -6,24 +6,42 @@ * Side Public License, v 1. */ -import { LensMultiTable } from '../../common'; -import { DataLayerConfigResult, XYLayerConfigResult } from '../../common/types'; -import { getDataLayers } from './visualization'; +import { Datatable } from '@kbn/expressions-plugin/common'; +import { + CommonXYDataLayerConfig, + CommonXYLayerConfig, + CommonXYReferenceLineLayerConfig, +} from '../../common/types'; +import { isDataLayer, isReferenceLayer } from './visualization'; + +export function getFilteredLayers(layers: CommonXYLayerConfig[]) { + return layers.filter( + (layer): layer is CommonXYReferenceLineLayerConfig | CommonXYDataLayerConfig => { + let table: Datatable | undefined; + let accessors: string[] = []; + let xAccessor: undefined | string | number; + let splitAccessor: undefined | string | number; + + if (isDataLayer(layer)) { + xAccessor = layer.xAccessor; + splitAccessor = layer.splitAccessor; + } + + if (isDataLayer(layer) || isReferenceLayer(layer)) { + table = layer.table; + accessors = layer.accessors; + } -export function getFilteredLayers(layers: XYLayerConfigResult[], data: LensMultiTable) { - return getDataLayers(layers).filter( - (layer): layer is DataLayerConfigResult => { - const { layerId, xAccessor, accessors, splitAccessor } = layer; return !( !accessors.length || - !data.tables[layerId] || - data.tables[layerId].rows.length === 0 || + !table || + table.rows.length === 0 || (xAccessor && - data.tables[layerId].rows.every((row) => typeof row[xAccessor] === 'undefined')) || + table.rows.every((row) => xAccessor && typeof row[xAccessor] === 'undefined')) || // stacked percentage bars have no xAccessors but splitAccessor with undefined values in them when empty (!xAccessor && splitAccessor && - data.tables[layerId].rows.every((row) => typeof row[splitAccessor] === 'undefined')) + table.rows.every((row) => splitAccessor && typeof row[splitAccessor] === 'undefined')) ); } ); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts index a5cd66f178b63..e2f95491dbce8 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { SeriesType, XYLayerConfigResult, YConfig } from '../../common'; +import type { CommonXYLayerConfig, SeriesType, ExtendedYConfig, YConfig } from '../../common'; import { getDataLayers, isAnnotationsLayer, isDataLayer } from './visualization'; export function isHorizontalSeries(seriesType: SeriesType) { @@ -21,16 +21,14 @@ export function isStackedChart(seriesType: SeriesType) { return seriesType.includes('stacked'); } -export function isHorizontalChart(layers: XYLayerConfigResult[]) { +export function isHorizontalChart(layers: CommonXYLayerConfig[]) { return getDataLayers(layers).every((l) => isHorizontalSeries(l.seriesType)); } -export const getSeriesColor = (layer: XYLayerConfigResult, accessor: string) => { +export const getSeriesColor = (layer: CommonXYLayerConfig, accessor: string) => { if ((isDataLayer(layer) && layer.splitAccessor) || isAnnotationsLayer(layer)) { return null; } - - return ( - layer?.yConfig?.find((yConfig: YConfig) => yConfig.forAccessor === accessor)?.color || null - ); + const yConfig: Array | undefined = layer?.yConfig; + return yConfig?.find((yConf) => yConf.forAccessor === accessor)?.color || null; }; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts index af2e80948ffdf..db0b431d56fac 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts @@ -6,38 +6,40 @@ * Side Public License, v 1. */ +import { LayerTypes } from '../../common/constants'; import { - DataLayerConfigResult, - ReferenceLineLayerConfigResult, - XYLayerConfigResult, - AnnotationLayerConfigResult, + CommonXYLayerConfig, + CommonXYDataLayerConfig, + CommonXYReferenceLineLayerConfig, + CommonXYAnnotationLayerConfig, } from '../../common/types'; -import { LayerTypes } from '../../common/constants'; -export const isDataLayer = (layer: XYLayerConfigResult): layer is DataLayerConfigResult => +export const isDataLayer = (layer: CommonXYLayerConfig): layer is CommonXYDataLayerConfig => layer.layerType === LayerTypes.DATA || !layer.layerType; -export const getDataLayers = (layers: XYLayerConfigResult[]) => - (layers || []).filter((layer): layer is DataLayerConfigResult => isDataLayer(layer)); +export const getDataLayers = (layers: CommonXYLayerConfig[]) => + (layers || []).filter((layer): layer is CommonXYDataLayerConfig => isDataLayer(layer)); export const isReferenceLayer = ( - layer: XYLayerConfigResult -): layer is ReferenceLineLayerConfigResult => layer.layerType === LayerTypes.REFERENCELINE; + layer: CommonXYLayerConfig +): layer is CommonXYReferenceLineLayerConfig => layer.layerType === LayerTypes.REFERENCELINE; -export const getReferenceLayers = (layers: XYLayerConfigResult[]) => - (layers || []).filter((layer): layer is ReferenceLineLayerConfigResult => +export const getReferenceLayers = (layers: CommonXYLayerConfig[]) => + (layers || []).filter((layer): layer is CommonXYReferenceLineLayerConfig => isReferenceLayer(layer) ); const isAnnotationLayerCommon = ( - layer: XYLayerConfigResult -): layer is AnnotationLayerConfigResult => layer.layerType === LayerTypes.ANNOTATIONS; + layer: CommonXYLayerConfig +): layer is CommonXYAnnotationLayerConfig => layer.layerType === LayerTypes.ANNOTATIONS; export const isAnnotationsLayer = ( - layer: XYLayerConfigResult -): layer is AnnotationLayerConfigResult => isAnnotationLayerCommon(layer); + layer: CommonXYLayerConfig +): layer is CommonXYAnnotationLayerConfig => isAnnotationLayerCommon(layer); export const getAnnotationsLayers = ( - layers: XYLayerConfigResult[] -): AnnotationLayerConfigResult[] => - (layers || []).filter((layer): layer is AnnotationLayerConfigResult => isAnnotationsLayer(layer)); + layers: CommonXYLayerConfig[] +): CommonXYAnnotationLayerConfig[] => + (layers || []).filter((layer): layer is CommonXYAnnotationLayerConfig => + isAnnotationsLayer(layer) + ); diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index d08ffc0fd2ec6..5c27da6b82b28 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -16,17 +16,21 @@ import { EventAnnotationPluginSetup } from '@kbn/event-annotation-plugin/public' import { ExpressionXyPluginSetup, ExpressionXyPluginStart, SetupDeps } from './types'; import { xyVisFunction, + layeredXyVisFunction, + extendedDataLayerFunction, yAxisConfigFunction, + extendedYAxisConfigFunction, legendConfigFunction, gridlinesConfigFunction, - dataLayerConfigFunction, axisExtentConfigFunction, tickLabelsConfigFunction, - annotationLayerConfigFunction, + referenceLineLayerFunction, + extendedReferenceLineLayerFunction, + annotationLayerFunction, labelsOrientationConfigFunction, - referenceLineLayerConfigFunction, axisTitlesVisibilityConfigFunction, -} from '../common'; + extendedAnnotationLayerFunction, +} from '../common/expression_functions'; import { GetStartDepsFn, getXyChartRenderer } from './expression_renderers'; export interface XYPluginStartDependencies { @@ -51,16 +55,20 @@ export class ExpressionXyPlugin { { expressions, charts }: SetupDeps ): ExpressionXyPluginSetup { expressions.registerFunction(yAxisConfigFunction); + expressions.registerFunction(extendedYAxisConfigFunction); expressions.registerFunction(legendConfigFunction); expressions.registerFunction(gridlinesConfigFunction); - expressions.registerFunction(dataLayerConfigFunction); + expressions.registerFunction(extendedDataLayerFunction); expressions.registerFunction(axisExtentConfigFunction); expressions.registerFunction(tickLabelsConfigFunction); - expressions.registerFunction(annotationLayerConfigFunction); + expressions.registerFunction(annotationLayerFunction); + expressions.registerFunction(extendedAnnotationLayerFunction); expressions.registerFunction(labelsOrientationConfigFunction); - expressions.registerFunction(referenceLineLayerConfigFunction); + expressions.registerFunction(referenceLineLayerFunction); + expressions.registerFunction(extendedReferenceLineLayerFunction); expressions.registerFunction(axisTitlesVisibilityConfigFunction); expressions.registerFunction(xyVisFunction); + expressions.registerFunction(layeredXyVisFunction); const getStartDeps: GetStartDepsFn = async () => { const [coreStart, deps] = await core.getStartServices(); diff --git a/src/plugins/chart_expressions/expression_xy/server/plugin.ts b/src/plugins/chart_expressions/expression_xy/server/plugin.ts index c16bd2eb28161..cefde5d38a5f4 100755 --- a/src/plugins/chart_expressions/expression_xy/server/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/server/plugin.ts @@ -12,16 +12,20 @@ import { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; import { xyVisFunction, yAxisConfigFunction, + extendedYAxisConfigFunction, legendConfigFunction, gridlinesConfigFunction, - dataLayerConfigFunction, axisExtentConfigFunction, tickLabelsConfigFunction, - annotationLayerConfigFunction, + annotationLayerFunction, labelsOrientationConfigFunction, - referenceLineLayerConfigFunction, + referenceLineLayerFunction, axisTitlesVisibilityConfigFunction, -} from '../common'; + extendedDataLayerFunction, + extendedReferenceLineLayerFunction, + layeredXyVisFunction, + extendedAnnotationLayerFunction, +} from '../common/expression_functions'; import { SetupDeps } from './types'; export class ExpressionXyPlugin @@ -29,16 +33,20 @@ export class ExpressionXyPlugin { public setup(core: CoreSetup, { expressions }: SetupDeps) { expressions.registerFunction(yAxisConfigFunction); + expressions.registerFunction(extendedYAxisConfigFunction); expressions.registerFunction(legendConfigFunction); expressions.registerFunction(gridlinesConfigFunction); - expressions.registerFunction(dataLayerConfigFunction); + expressions.registerFunction(extendedDataLayerFunction); expressions.registerFunction(axisExtentConfigFunction); expressions.registerFunction(tickLabelsConfigFunction); - expressions.registerFunction(annotationLayerConfigFunction); + expressions.registerFunction(annotationLayerFunction); + expressions.registerFunction(extendedAnnotationLayerFunction); expressions.registerFunction(labelsOrientationConfigFunction); - expressions.registerFunction(referenceLineLayerConfigFunction); + expressions.registerFunction(referenceLineLayerFunction); + expressions.registerFunction(extendedReferenceLineLayerFunction); expressions.registerFunction(axisTitlesVisibilityConfigFunction); expressions.registerFunction(xyVisFunction); + expressions.registerFunction(layeredXyVisFunction); } public start(core: CoreStart) {} diff --git a/src/plugins/charts/public/static/utils/transform_click_event.ts b/src/plugins/charts/public/static/utils/transform_click_event.ts index 3d0cc2b47b215..ec35fa85d59a1 100644 --- a/src/plugins/charts/public/static/utils/transform_click_event.ts +++ b/src/plugins/charts/public/static/utils/transform_click_event.ts @@ -19,7 +19,7 @@ import { RangeSelectContext, ValueClickContext } from '@kbn/embeddable-plugin/pu import { Datatable } from '@kbn/expressions-plugin/public'; export interface ClickTriggerEvent { - name: 'filterBucket'; + name: 'filter'; data: ValueClickContext['data']; } @@ -214,7 +214,7 @@ export const getFilterFromChartClickEventFn = }); return { - name: 'filterBucket', + name: 'filter', data: { negate, data, @@ -250,7 +250,7 @@ export const getFilterFromSeriesFn = })); return { - name: 'filterBucket', + name: 'filter', data: { negate, data, diff --git a/src/plugins/console/public/application/containers/console_history/history_viewer.tsx b/src/plugins/console/public/application/containers/console_history/history_viewer.tsx index 605f9a254f228..11f4b0a99a993 100644 --- a/src/plugins/console/public/application/containers/console_history/history_viewer.tsx +++ b/src/plugins/console/public/application/containers/console_history/history_viewer.tsx @@ -17,6 +17,7 @@ import * as InputMode from '../../models/legacy_core_editor/mode/input'; const inputMode = new InputMode.Mode(); import * as editor from '../../models/legacy_core_editor'; import { applyCurrentSettings } from '../editor/legacy/console_editor/apply_editor_settings'; +import { formatRequestBodyDoc } from '../../../lib/utils'; interface Props { settings: DevToolsSettings; @@ -41,7 +42,9 @@ export function HistoryViewer({ settings, req }: Props) { if (viewerRef.current) { const { current: viewer } = viewerRef; if (req) { - const s = req.method + ' ' + req.endpoint + '\n' + (req.data || ''); + const indent = true; + const formattedData = req.data ? formatRequestBodyDoc([req.data], indent).data : ''; + const s = req.method + ' ' + req.endpoint + '\n' + formattedData; viewer.update(s, inputMode); viewer.clearSelection(); } else { diff --git a/src/plugins/console/public/application/hooks/use_restore_request_from_history/restore_request_from_history.ts b/src/plugins/console/public/application/hooks/use_restore_request_from_history/restore_request_from_history.ts index 85c9cf6b9f014..ca8a118e767b6 100644 --- a/src/plugins/console/public/application/hooks/use_restore_request_from_history/restore_request_from_history.ts +++ b/src/plugins/console/public/application/hooks/use_restore_request_from_history/restore_request_from_history.ts @@ -9,6 +9,7 @@ import RowParser from '../../../lib/row_parser'; import { ESRequest } from '../../../types'; import { SenseEditor } from '../../models/sense_editor'; +import { formatRequestBodyDoc } from '../../../lib/utils'; export function restoreRequestFromHistory(editor: SenseEditor, req: ESRequest) { const coreEditor = editor.getCoreEditor(); @@ -32,7 +33,9 @@ export function restoreRequestFromHistory(editor: SenseEditor, req: ESRequest) { let s = prefix + req.method + ' ' + req.endpoint; if (req.data) { - s += '\n' + req.data; + const indent = true; + const formattedData = formatRequestBodyDoc([req.data], indent); + s += '\n' + formattedData.data; } s += suffix; diff --git a/src/plugins/console/public/application/models/sense_editor/__fixtures__/editor_input1.txt b/src/plugins/console/public/application/models/sense_editor/__fixtures__/editor_input1.txt index 398a0fdeab61f..517f22bd8ad6a 100644 --- a/src/plugins/console/public/application/models/sense_editor/__fixtures__/editor_input1.txt +++ b/src/plugins/console/public/application/models/sense_editor/__fixtures__/editor_input1.txt @@ -31,3 +31,7 @@ POST /_sql?format=txt "query": "SELECT prenom FROM claude_index WHERE prenom = 'claude' ", "fetch_size": 1 } + +GET ,,/_search?pretty + +GET kbn:/api/spaces/space \ No newline at end of file diff --git a/src/plugins/console/public/application/models/sense_editor/sense_editor.test.js b/src/plugins/console/public/application/models/sense_editor/sense_editor.test.js index ff9d245f61275..4751d3ca29863 100644 --- a/src/plugins/console/public/application/models/sense_editor/sense_editor.test.js +++ b/src/plugins/console/public/application/models/sense_editor/sense_editor.test.js @@ -10,6 +10,7 @@ import './sense_editor.test.mocks'; import $ from 'jquery'; import _ from 'lodash'; +import { URL } from 'url'; import { create } from './create'; import { XJson } from '@kbn/es-ui-shared-plugin/public'; @@ -19,6 +20,8 @@ const { collapseLiteralStrings } = XJson; describe('Editor', () => { let input; + let oldUrl; + let olldWindow; beforeEach(function () { // Set up our document body @@ -31,8 +34,19 @@ describe('Editor', () => { input = create(document.querySelector('#ConAppEditor')); $(input.getCoreEditor().getContainer()).show(); input.autocomplete._test.removeChangeListener(); + oldUrl = global.URL; + olldWindow = { ...global.window }; + global.URL = URL; + global.window = Object.create(window); + Object.defineProperty(window, 'location', { + value: { + origin: 'http://localhost:5620', + }, + }); }); afterEach(function () { + global.URL = oldUrl; + global.window = olldWindow; $(input.getCoreEditor().getContainer()).hide(); input.autocomplete._test.addChangeListener(); }); @@ -476,4 +490,20 @@ curl -XPOST "http://localhost:9200/_sql?format=txt" -H "kbn-xsrf: reporting" -H "fetch_size": 1 }'`.trim() ); + + multiReqCopyAsCurlTest( + 'with date math index', + editorInput1, + { start: { lineNumber: 35 }, end: { lineNumber: 35 } }, + ` + curl -XGET "http://localhost:9200/%3Cindex_1-%7Bnow%2Fd-2d%7D%3E%2C%3Cindex_1-%7Bnow%2Fd-1d%7D%3E%2C%3Cindex_1-%7Bnow%2Fd%7D%3E%2F_search?pretty" -H "kbn-xsrf: reporting"`.trim() + ); + + multiReqCopyAsCurlTest( + 'with Kibana API request', + editorInput1, + { start: { lineNumber: 37 }, end: { lineNumber: 37 } }, + ` +curl -XGET "http://localhost:5620/api/spaces/space" -H \"kbn-xsrf: reporting\"`.trim() + ); }); diff --git a/src/plugins/console/public/lib/es/es.ts b/src/plugins/console/public/lib/es/es.ts index 10d0ad95b0496..5e22c78547b96 100644 --- a/src/plugins/console/public/lib/es/es.ts +++ b/src/plugins/console/public/lib/es/es.ts @@ -7,7 +7,7 @@ */ import type { HttpResponse, HttpSetup } from '@kbn/core/public'; -import { trimStart } from 'lodash'; +import { trimStart, trimEnd } from 'lodash'; import { API_BASE_PATH, KIBANA_API_PREFIX } from '../../../common/constants'; const esVersion: string[] = []; @@ -79,11 +79,23 @@ function getKibanaRequestUrl(path: string) { export function constructUrl(baseUri: string, path: string) { const kibanaRequestUrl = getKibanaRequestUrl(path); + let url = `${trimEnd(baseUri, '/')}/${trimStart(path, '/')}`; if (kibanaRequestUrl) { - return kibanaRequestUrl; + url = kibanaRequestUrl; } - baseUri = baseUri.replace(/\/+$/, ''); - path = path.replace(/^\/+/, ''); - return baseUri + '/' + path; + + const { origin, pathname, search } = new URL(url); + return `${origin}${encodePathname(pathname)}${search ?? ''}`; } + +const encodePathname = (path: string) => { + const decodedPath = new URLSearchParams(`path=${path}`).get('path') ?? ''; + + // Skip if it is valid + if (path === decodedPath) { + return path; + } + + return `/${encodeURIComponent(trimStart(decodedPath, '/'))}`; +}; diff --git a/src/plugins/controls/public/control_types/range_slider/range_slider_popover.tsx b/src/plugins/controls/public/control_types/range_slider/range_slider_popover.tsx index a51b46d98ff85..1bb7501f7104f 100644 --- a/src/plugins/controls/public/control_types/range_slider/range_slider_popover.tsx +++ b/src/plugins/controls/public/control_types/range_slider/range_slider_popover.tsx @@ -45,6 +45,8 @@ export const RangeSliderPopover: FC = ({ fieldFormatter, }) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const [rangeSliderMin, setRangeSliderMin] = useState(-Infinity); + const [rangeSliderMax, setRangeSliderMax] = useState(Infinity); const rangeRef = useRef(null); let errorMessage = ''; let helpText = ''; @@ -79,17 +81,6 @@ export const RangeSliderPopover: FC = ({ errorMessage = RangeSliderStrings.errors.getUpperLessThanLowerErrorMessage(); } - const rangeSliderMin = Math.min( - roundedMin, - isNaN(lowerBoundValue) ? Infinity : lowerBoundValue, - isNaN(upperBoundValue) ? Infinity : upperBoundValue - ); - const rangeSliderMax = Math.max( - roundedMax, - isNaN(lowerBoundValue) ? -Infinity : lowerBoundValue, - isNaN(upperBoundValue) ? -Infinity : upperBoundValue - ); - const displayedValue = [ hasLowerBoundSelection ? String(lowerBoundValue) : hasAvailableRange ? String(roundedMin) : '', hasUpperBoundSelection ? String(upperBoundValue) : hasAvailableRange ? String(roundedMax) : '', @@ -106,7 +97,27 @@ export const RangeSliderPopover: FC = ({ const button = ( - - - - - - -`; diff --git a/src/plugins/discover/public/application/main/components/sidebar/change_indexpattern.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/change_indexpattern.test.tsx deleted file mode 100644 index a5e93c1d895bc..0000000000000 --- a/src/plugins/discover/public/application/main/components/sidebar/change_indexpattern.test.tsx +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { EuiSelectable } from '@elastic/eui'; -import { ShallowWrapper } from 'enzyme'; -import { act } from 'react-dom/test-utils'; -import { shallowWithIntl } from '@kbn/test-jest-helpers'; -import { ChangeIndexPattern } from './change_indexpattern'; -import { indexPatternMock } from '../../../../__mocks__/index_pattern'; -import { indexPatternWithTimefieldMock } from '../../../../__mocks__/index_pattern_with_timefield'; -import { IndexPatternRef } from './types'; - -function getProps() { - return { - indexPatternId: indexPatternMock.id, - indexPatternRefs: [ - indexPatternMock as IndexPatternRef, - indexPatternWithTimefieldMock as IndexPatternRef, - ], - onChangeIndexPattern: jest.fn(), - trigger: { - label: indexPatternMock.title, - title: indexPatternMock.title, - 'data-test-subj': 'indexPattern-switch-link', - }, - }; -} - -function getIndexPatternPickerList(instance: ShallowWrapper) { - return instance.find(EuiSelectable).first(); -} - -function getIndexPatternPickerOptions(instance: ShallowWrapper) { - return getIndexPatternPickerList(instance).prop('options'); -} - -export function selectIndexPatternPickerOption(instance: ShallowWrapper, selectedLabel: string) { - const options: Array<{ label: string; checked?: 'on' | 'off' }> = getIndexPatternPickerOptions( - instance - ).map((option: { label: string }) => - option.label === selectedLabel - ? { ...option, checked: 'on' } - : { ...option, checked: undefined } - ); - return getIndexPatternPickerList(instance).prop('onChange')!(options); -} - -describe('ChangeIndexPattern', () => { - test('switching index pattern to the same index pattern does not trigger onChangeIndexPattern', async () => { - const props = getProps(); - const comp = shallowWithIntl(); - await act(async () => { - selectIndexPatternPickerOption(comp, indexPatternMock.title); - }); - expect(props.onChangeIndexPattern).toHaveBeenCalledTimes(0); - }); - test('switching index pattern to a different index pattern triggers onChangeIndexPattern', async () => { - const props = getProps(); - const comp = shallowWithIntl(); - await act(async () => { - selectIndexPatternPickerOption(comp, indexPatternWithTimefieldMock.title); - }); - expect(props.onChangeIndexPattern).toHaveBeenCalledTimes(1); - expect(props.onChangeIndexPattern).toHaveBeenCalledWith(indexPatternWithTimefieldMock.id); - }); -}); diff --git a/src/plugins/discover/public/application/main/components/sidebar/change_indexpattern.tsx b/src/plugins/discover/public/application/main/components/sidebar/change_indexpattern.tsx deleted file mode 100644 index ceee905cff6fa..0000000000000 --- a/src/plugins/discover/public/application/main/components/sidebar/change_indexpattern.tsx +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; -import React, { useState } from 'react'; -import { - EuiButton, - EuiPopover, - EuiPopoverTitle, - EuiSelectable, - EuiButtonProps, -} from '@elastic/eui'; -import { EuiSelectableProps } from '@elastic/eui/src/components/selectable/selectable'; -import { IndexPatternRef } from './types'; - -export type ChangeIndexPatternTriggerProps = EuiButtonProps & { - label: string; - title?: string; -}; - -// TODO: refactor to shared component with ../../../../../../../../x-pack/legacy/plugins/lens/public/indexpattern_plugin/change_indexpattern - -export function ChangeIndexPattern({ - indexPatternId, - indexPatternRefs, - onChangeIndexPattern, - selectableProps, - trigger, -}: { - indexPatternId?: string; - indexPatternRefs: IndexPatternRef[]; - onChangeIndexPattern: (newId: string) => void; - selectableProps?: EuiSelectableProps<{ value: string }>; - trigger: ChangeIndexPatternTriggerProps; -}) { - const [isPopoverOpen, setPopoverIsOpen] = useState(false); - - const createTrigger = function () { - const { label, title, ...rest } = trigger; - return ( - setPopoverIsOpen(!isPopoverOpen)} - {...rest} - > - {label} - - ); - }; - - return ( - setPopoverIsOpen(false)} - display="block" - panelPaddingSize="s" - > -
- - {i18n.translate('discover.fieldChooser.indexPattern.changeDataViewTitle', { - defaultMessage: 'Change data view', - })} - - - data-test-subj="indexPattern-switcher" - {...selectableProps} - searchable - singleSelection="always" - options={indexPatternRefs.map(({ title, id }) => ({ - label: title, - key: id, - value: id, - checked: id === indexPatternId ? 'on' : undefined, - }))} - onChange={(choices) => { - const choice = choices.find(({ checked }) => checked) as unknown as { - value: string; - }; - if (choice.value !== indexPatternId) { - onChangeIndexPattern(choice.value); - } - setPopoverIsOpen(false); - }} - searchProps={{ - compressed: true, - ...(selectableProps ? selectableProps.searchProps : undefined), - }} - > - {(list, search) => ( - <> - {search} - {list} - - )} - -
-
- ); -} diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_index_pattern.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_index_pattern.test.tsx deleted file mode 100644 index d640e2fa11594..0000000000000 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_index_pattern.test.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 and the Server Side Public License, v 1; you may 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 { act } from 'react-dom/test-utils'; -import { shallowWithIntl as shallow } from '@kbn/test-jest-helpers'; -import { ShallowWrapper } from 'enzyme'; -import { ChangeIndexPattern } from './change_indexpattern'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { SavedObject } from '@kbn/core/server'; -import { DiscoverIndexPattern, DiscoverIndexPatternProps } from './discover_index_pattern'; -import { EuiSelectable } from '@elastic/eui'; -import type { DataView, DataViewAttributes } from '@kbn/data-views-plugin/public'; -import { indexPatternsMock } from '../../../../__mocks__/index_patterns'; - -const indexPattern = { - id: 'the-index-pattern-id-first', - title: 'test1 title', -} as DataView; - -const indexPattern1 = { - id: 'the-index-pattern-id-first', - attributes: { - title: 'test1 title', - }, -} as SavedObject; - -const indexPattern2 = { - id: 'the-index-pattern-id', - attributes: { - title: 'test2 title', - }, -} as SavedObject; - -const defaultProps = { - indexPatternList: [indexPattern1, indexPattern2], - selectedIndexPattern: indexPattern, - useNewFieldsApi: true, - indexPatterns: indexPatternsMock, - onChangeIndexPattern: jest.fn(), -}; - -function getIndexPatternPickerList(instance: ShallowWrapper) { - return instance.find(ChangeIndexPattern).first().dive().find(EuiSelectable); -} - -function getIndexPatternPickerOptions(instance: ShallowWrapper) { - return getIndexPatternPickerList(instance).prop('options'); -} - -function selectIndexPatternPickerOption(instance: ShallowWrapper, selectedLabel: string) { - const options: Array<{ label: string; checked?: 'on' | 'off' }> = getIndexPatternPickerOptions( - instance - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ).map((option: any) => - option.label === selectedLabel - ? { ...option, checked: 'on' } - : { ...option, checked: undefined } - ); - return getIndexPatternPickerList(instance).prop('onChange')!(options); -} - -describe('DiscoverIndexPattern', () => { - test('Invalid props dont cause an exception', () => { - const props = { - indexPatternList: null, - selectedIndexPattern: null, - onChangeIndexPattern: jest.fn(), - } as unknown as DiscoverIndexPatternProps; - - expect(shallow()).toMatchSnapshot(`""`); - }); - test('should list all index patterns', () => { - const instance = shallow(); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect(getIndexPatternPickerOptions(instance)!.map((option: any) => option.label)).toEqual([ - 'test1 title', - 'test2 title', - ]); - }); - - test('should switch data panel to target index pattern', async () => { - const instance = shallow(); - await act(async () => { - selectIndexPatternPickerOption(instance, 'test2 title'); - }); - expect(defaultProps.onChangeIndexPattern).toHaveBeenCalledWith('the-index-pattern-id'); - }); -}); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_index_pattern.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_index_pattern.tsx deleted file mode 100644 index 83aa3ce478215..0000000000000 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_index_pattern.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React, { useState, useEffect } from 'react'; -import { SavedObject } from '@kbn/core/public'; -import type { DataView, DataViewAttributes } from '@kbn/data-views-plugin/public'; -import { IndexPatternRef } from './types'; -import { ChangeIndexPattern } from './change_indexpattern'; - -export interface DiscoverIndexPatternProps { - /** - * list of available index patterns, if length > 1, component offers a "change" link - */ - indexPatternList: Array>; - /** - * Callback function when changing an index pattern - */ - onChangeIndexPattern: (id: string) => void; - /** - * currently selected index pattern - */ - selectedIndexPattern: DataView; -} - -/** - * Component allows you to select an index pattern in discovers side bar - */ -export function DiscoverIndexPattern({ - indexPatternList, - onChangeIndexPattern, - selectedIndexPattern, -}: DiscoverIndexPatternProps) { - const options: IndexPatternRef[] = (indexPatternList || []).map((entity) => ({ - id: entity.id, - title: entity.attributes!.title, - })); - const { id: selectedId, title: selectedTitle } = selectedIndexPattern || {}; - - const [selected, setSelected] = useState({ - id: selectedId, - title: selectedTitle || '', - }); - useEffect(() => { - const { id, title } = selectedIndexPattern; - setSelected({ id, title }); - }, [selectedIndexPattern]); - if (!selectedId) { - return null; - } - - return ( - { - const indexPattern = options.find((pattern) => pattern.id === id); - if (indexPattern) { - onChangeIndexPattern(id); - setSelected(indexPattern); - } - }} - /> - ); -} diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_index_pattern_management.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_index_pattern_management.test.tsx deleted file mode 100644 index cddbe087030e7..0000000000000 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_index_pattern_management.test.tsx +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { mountWithIntl, findTestSubject } from '@kbn/test-jest-helpers'; -import { EuiContextMenuPanel, EuiPopover, EuiContextMenuItem } from '@elastic/eui'; -import { DiscoverServices } from '../../../../build_services'; -import { DiscoverIndexPatternManagement } from './discover_index_pattern_management'; -import { stubLogstashIndexPattern } from '@kbn/data-plugin/common/stubs'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; - -const mockServices = { - history: () => ({ - location: { - search: '', - }, - }), - capabilities: { - visualize: { - show: true, - }, - discover: { - save: false, - }, - }, - core: { - application: { - navigateToApp: jest.fn(), - }, - }, - uiSettings: { - get: (key: string) => { - if (key === 'fields:popularLimit') { - return 5; - } - }, - }, - dataViewFieldEditor: { - openEditor: jest.fn(), - userPermissions: { - editIndexPattern: () => { - return true; - }, - }, - }, -} as unknown as DiscoverServices; - -describe('Discover DataView Management', () => { - const indexPattern = stubLogstashIndexPattern; - - const editField = jest.fn(); - const createNewDataView = jest.fn(); - - const mountComponent = () => { - return mountWithIntl( - - - - ); - }; - - test('renders correctly', () => { - const component = mountComponent(); - expect(component).toMatchSnapshot(); - expect(component.find(EuiPopover).length).toBe(1); - }); - - test('click on a button opens popover', () => { - const component = mountComponent(); - expect(component.find(EuiContextMenuPanel).length).toBe(0); - - const button = findTestSubject(component, 'discoverIndexPatternActions'); - button.simulate('click'); - - expect(component.find(EuiContextMenuPanel).length).toBe(1); - expect(component.find(EuiContextMenuItem).length).toBe(3); - }); - - test('click on an add button executes editField callback', () => { - const component = mountComponent(); - const button = findTestSubject(component, 'discoverIndexPatternActions'); - button.simulate('click'); - - const addButton = findTestSubject(component, 'indexPattern-add-field'); - addButton.simulate('click'); - expect(editField).toHaveBeenCalledWith(undefined); - }); - - test('click on a manage button navigates away from discover', () => { - const component = mountComponent(); - const button = findTestSubject(component, 'discoverIndexPatternActions'); - button.simulate('click'); - - const manageButton = findTestSubject(component, 'indexPattern-manage-field'); - manageButton.simulate('click'); - expect(mockServices.core.application.navigateToApp).toHaveBeenCalled(); - }); - - test('click on add dataView button executes createNewDataView callback', () => { - const component = mountComponent(); - const button = findTestSubject(component, 'discoverIndexPatternActions'); - button.simulate('click'); - - const manageButton = findTestSubject(component, 'dataview-create-new'); - manageButton.simulate('click'); - expect(createNewDataView).toHaveBeenCalled(); - }); -}); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_index_pattern_management.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_index_pattern_management.tsx deleted file mode 100644 index 823aa9c0050c0..0000000000000 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_index_pattern_management.tsx +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React, { useState } from 'react'; -import { - EuiButtonIcon, - EuiContextMenuItem, - EuiContextMenuPanel, - EuiHorizontalRule, - EuiPopover, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import { useDiscoverServices } from '../../../../utils/use_discover_services'; - -export interface DiscoverIndexPatternManagementProps { - /** - * Currently selected index pattern - */ - selectedIndexPattern?: DataView; - /** - * Read from the Fields API - */ - useNewFieldsApi?: boolean; - /** - * Callback to execute on edit field action - * @param fieldName - */ - editField: (fieldName?: string) => void; - - /** - * Callback to execute on create new data action - */ - createNewDataView: () => void; -} - -export function DiscoverIndexPatternManagement(props: DiscoverIndexPatternManagementProps) { - const { dataViewFieldEditor, core } = useDiscoverServices(); - const { useNewFieldsApi, selectedIndexPattern, editField, createNewDataView } = props; - const dataViewEditPermission = dataViewFieldEditor?.userPermissions.editIndexPattern(); - const canEditDataViewField = !!dataViewEditPermission && useNewFieldsApi; - const [isAddIndexPatternFieldPopoverOpen, setIsAddIndexPatternFieldPopoverOpen] = useState(false); - - if (!useNewFieldsApi || !selectedIndexPattern || !canEditDataViewField) { - return null; - } - - const addField = () => { - editField(undefined); - }; - - return ( - { - setIsAddIndexPatternFieldPopoverOpen(false); - }} - ownFocus - data-test-subj="discover-addRuntimeField-popover" - button={ - { - setIsAddIndexPatternFieldPopoverOpen(!isAddIndexPatternFieldPopoverOpen); - }} - /> - } - > - { - setIsAddIndexPatternFieldPopoverOpen(false); - addField(); - }} - > - {i18n.translate('discover.fieldChooser.indexPatterns.addFieldButton', { - defaultMessage: 'Add field', - })} - , - { - setIsAddIndexPatternFieldPopoverOpen(false); - core.application.navigateToApp('management', { - path: `/kibana/indexPatterns/patterns/${props.selectedIndexPattern?.id}`, - }); - }} - > - {i18n.translate('discover.fieldChooser.indexPatterns.manageFieldButton', { - defaultMessage: 'Manage settings', - })} - , - , - { - setIsAddIndexPatternFieldPopoverOpen(false); - createNewDataView(); - }} - > - {i18n.translate('discover.fieldChooser.dataViews.createNewDataView', { - defaultMessage: 'Create new data view', - })} - , - ]} - /> - - ); -} diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.scss b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.scss index 9ef123fa1a60f..6845b1c89901d 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.scss +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.scss @@ -2,7 +2,7 @@ overflow: hidden; margin: 0 !important; flex-grow: 1; - padding-left: $euiSize; + padding: $euiSizeS 0 $euiSizeS $euiSizeS; width: $euiSize * 19; height: 100%; @@ -19,7 +19,7 @@ .dscSidebar__mobile { width: 100%; - padding: $euiSize $euiSize 0; + padding: $euiSizeS $euiSizeS 0; .dscSidebar__mobileBadge { margin-left: $euiSizeS; diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx index fb6af1bc1b775..22f954e714987 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx @@ -20,16 +20,17 @@ import { EuiNotificationBadge, EuiPageSideBar, useResizeObserver, + EuiButton, } from '@elastic/eui'; import useShallowCompareEffect from 'react-use/lib/useShallowCompareEffect'; -import { isEqual, sortBy } from 'lodash'; +import { isEqual } from 'lodash'; import { FormattedMessage } from '@kbn/i18n-react'; import { indexPatterns as indexPatternUtils } from '@kbn/data-plugin/public'; +import { DataViewPicker } from '@kbn/unified-search-plugin/public'; import { DataViewField } from '@kbn/data-views-plugin/public'; import { useDiscoverServices } from '../../../../utils/use_discover_services'; import { DiscoverField } from './discover_field'; -import { DiscoverIndexPattern } from './discover_index_pattern'; import { DiscoverFieldSearch } from './discover_field_search'; import { FIELDS_LIMIT_SETTING } from '../../../../../common'; import { groupFields } from './lib/group_fields'; @@ -37,7 +38,6 @@ import { getDetails } from './lib/get_details'; import { FieldFilterState, getDefaultFieldFilter, setFieldFilterProp } from './lib/field_filter'; import { getIndexPatternFieldList } from './lib/get_index_pattern_field_list'; import { DiscoverSidebarResponsiveProps } from './discover_sidebar_responsive'; -import { DiscoverIndexPatternManagement } from './discover_index_pattern_management'; import { VIEW_MODE } from '../../../../components/view_mode_toggle'; import { ElasticSearchHit } from '../../../../types'; @@ -83,6 +83,8 @@ export interface DiscoverSidebarProps extends Omit(null); @@ -297,34 +299,6 @@ export function DiscoverSidebarComponent({ return null; } - if (useFlyout) { - return ( -
- - - o.attributes.title)} - onChangeIndexPattern={onChangeIndexPattern} - /> - - - - - -
- ); - } - return ( - - - - o.attributes.title)} - onChangeIndexPattern={onChangeIndexPattern} - /> - - - - - - + {Boolean(showDataViewPicker) && ( + + )}
+ + editField()} + size="s" + > + {i18n.translate('discover.fieldChooser.addField.label', { + defaultMessage: 'Add a field', + })} + + ); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx index f2f58c43d5e7f..f7664197ca98c 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx @@ -7,7 +7,6 @@ */ import React, { useEffect, useRef, useState, useCallback } from 'react'; -import { sortBy } from 'lodash'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { UiCounterMetricType } from '@kbn/analytics'; @@ -19,21 +18,16 @@ import { EuiBadge, EuiFlyoutHeader, EuiFlyout, - EuiSpacer, EuiIcon, EuiLink, EuiPortal, - EuiFlexGroup, - EuiFlexItem, } from '@elastic/eui'; import type { DataViewField, DataView, DataViewAttributes } from '@kbn/data-views-plugin/public'; import { SavedObject } from '@kbn/core/types'; import { useDiscoverServices } from '../../../../utils/use_discover_services'; -import { DiscoverIndexPattern } from './discover_index_pattern'; import { getDefaultFieldFilter } from './lib/field_filter'; import { DiscoverSidebar } from './discover_sidebar'; import { AppState } from '../../services/discover_state'; -import { DiscoverIndexPatternManagement } from './discover_index_pattern_management'; import { AvailableFields$, DataDocuments$ } from '../../utils/use_saved_search'; import { calcFieldCounts } from '../../utils/calc_field_counts'; import { VIEW_MODE } from '../../../../components/view_mode_toggle'; @@ -91,10 +85,6 @@ export interface DiscoverSidebarResponsiveProps { * @param eventName */ trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; - /** - * Shows index pattern and a button that displays the sidebar in a flyout - */ - useFlyout?: boolean; /** * Read from the Fields API */ @@ -124,13 +114,7 @@ export interface DiscoverSidebarResponsiveProps { */ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) { const services = useDiscoverServices(); - const { - selectedIndexPattern, - onEditRuntimeField, - useNewFieldsApi, - onChangeIndexPattern, - onDataViewCreated, - } = props; + const { selectedIndexPattern, onEditRuntimeField, useNewFieldsApi, onDataViewCreated } = props; const [fieldFilter, setFieldFilter] = useState(getDefaultFieldFilter()); const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); /** @@ -291,34 +275,6 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) )}
-
- - - o.attributes.title)} - /> - - - - - -
- -
diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.test.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.test.tsx index 938d2d55df004..7b8831f734279 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.test.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.test.tsx @@ -40,6 +40,8 @@ function getProps(savePermissions = true): DiscoverTopNavProps { onOpenInspector: jest.fn(), searchSource: {} as ISearchSource, resetSavedSearch: () => {}, + onEditRuntimeField: jest.fn(), + onChangeIndexPattern: jest.fn(), }; } diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index 8656a2fdb7072..87d2f04bd604b 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -5,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, useMemo } from 'react'; +import React, { useCallback, useMemo, useRef, useEffect } from 'react'; import { useHistory } from 'react-router-dom'; import { Query, TimeRange } from '@kbn/data-plugin/public'; import { DataViewType } from '@kbn/data-views-plugin/public'; @@ -25,6 +25,9 @@ export type DiscoverTopNavProps = Pick< updateQuery: (payload: { dateRange: TimeRange; query?: Query }, isUpdate?: boolean) => void; stateContainer: GetStateReturn; resetSavedSearch: () => void; + onChangeIndexPattern: (indexPattern: string) => void; + onEditRuntimeField: () => void; + useNewFieldsApi?: boolean; }; export const DiscoverTopNav = ({ @@ -38,6 +41,9 @@ export const DiscoverTopNav = ({ navigateTo, savedSearch, resetSavedSearch, + onChangeIndexPattern, + onEditRuntimeField, + useNewFieldsApi = false, }: DiscoverTopNavProps) => { const history = useHistory(); const showDatePicker = useMemo( @@ -45,7 +51,16 @@ export const DiscoverTopNav = ({ [indexPattern] ); const services = useDiscoverServices(); - const { TopNavMenu } = services.navigation.ui; + const { dataViewEditor, navigation, dataViewFieldEditor, data } = services; + const editPermission = useMemo( + () => dataViewFieldEditor.userPermissions.editIndexPattern(), + [dataViewFieldEditor] + ); + const canEditDataViewField = !!editPermission && useNewFieldsApi; + const closeFieldEditor = useRef<() => void | undefined>(); + const closeDataViewEditor = useRef<() => void | undefined>(); + + const { TopNavMenu } = navigation.ui; const onOpenSavedSearch = useCallback( (newSavedSearchId: string) => { @@ -58,6 +73,64 @@ export const DiscoverTopNav = ({ [history, resetSavedSearch, savedSearch.id] ); + useEffect(() => { + return () => { + // Make sure to close the editors when unmounting + if (closeFieldEditor.current) { + closeFieldEditor.current(); + } + if (closeDataViewEditor.current) { + closeDataViewEditor.current(); + } + }; + }, []); + + const editField = useMemo( + () => + canEditDataViewField + ? async (fieldName?: string, uiAction: 'edit' | 'add' = 'edit') => { + if (indexPattern?.id) { + const indexPatternInstance = await data.dataViews.get(indexPattern.id); + closeFieldEditor.current = dataViewFieldEditor.openEditor({ + ctx: { + dataView: indexPatternInstance, + }, + fieldName, + onSave: async () => { + onEditRuntimeField(); + }, + }); + } + } + : undefined, + [ + canEditDataViewField, + indexPattern?.id, + data.dataViews, + dataViewFieldEditor, + onEditRuntimeField, + ] + ); + + const addField = useMemo( + () => (canEditDataViewField && editField ? () => editField(undefined, 'add') : undefined), + [editField, canEditDataViewField] + ); + + const createNewDataView = useCallback(() => { + const indexPatternFieldEditPermission = dataViewEditor.userPermissions.editDataView; + if (!indexPatternFieldEditPermission) { + return; + } + closeDataViewEditor.current = dataViewEditor.openEditor({ + onSave: async (dataView) => { + if (dataView.id) { + onChangeIndexPattern(dataView.id); + } + }, + }); + }, [dataViewEditor, onChangeIndexPattern]); + const topNavMenu = useMemo( () => getTopNavLinks({ @@ -99,6 +172,18 @@ export const DiscoverTopNav = ({ return getHeaderActionMenuMounter(); }, []); + const dataViewPickerProps = { + trigger: { + label: indexPattern?.title || '', + 'data-test-subj': 'discover-dataView-switch-link', + title: indexPattern?.title || '', + }, + currentDataViewId: indexPattern?.id, + onAddField: addField, + onDataViewCreated: createNewDataView, + onChangeDataView: (newIndexPatternId: string) => onChangeIndexPattern(newIndexPatternId), + }; + return ( ); }; diff --git a/src/plugins/discover/public/application/main/discover_main_app.test.tsx b/src/plugins/discover/public/application/main/discover_main_app.test.tsx index ceb06df058fae..d2f0c7e2dd005 100644 --- a/src/plugins/discover/public/application/main/discover_main_app.test.tsx +++ b/src/plugins/discover/public/application/main/discover_main_app.test.tsx @@ -9,11 +9,11 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { indexPatternMock } from '../../__mocks__/index_pattern'; import { DiscoverMainApp } from './discover_main_app'; +import { DiscoverTopNav } from './components/top_nav/discover_topnav'; import { savedSearchMock } from '../../__mocks__/saved_search'; import { SavedObject } from '@kbn/core/types'; import type { DataViewAttributes } from '@kbn/data-views-plugin/public'; import { setHeaderActionMenuMounter } from '../../kibana_services'; -import { findTestSubject } from '@elastic/eui/lib/test'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { discoverServiceMock } from '../../__mocks__/services'; import { Router } from 'react-router-dom'; @@ -42,8 +42,7 @@ describe('DiscoverMainApp', () => { ); - expect(findTestSubject(component, 'indexPattern-switch-link').text()).toBe( - indexPatternMock.title - ); + expect(component.find(DiscoverTopNav).exists()).toBe(true); + expect(component.find(DiscoverTopNav).prop('indexPattern')).toEqual(indexPatternMock); }); }); diff --git a/src/plugins/discover/public/components/discover_grid/constants.ts b/src/plugins/discover/public/components/discover_grid/constants.ts index d026607aef373..f2f5a8e8bebc7 100644 --- a/src/plugins/discover/public/components/discover_grid/constants.ts +++ b/src/plugins/discover/public/components/discover_grid/constants.ts @@ -19,7 +19,7 @@ export const GRID_STYLE = { export const pageSizeArr = [25, 50, 100, 250]; export const defaultPageSize = 100; -export const defaultTimeColumnWidth = 190; +export const defaultTimeColumnWidth = 210; export const toolbarVisibility = { showColumnSelector: { allowHide: false, diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid.scss b/src/plugins/discover/public/components/discover_grid/discover_grid.scss index 0204433a5ba1c..113bb60924850 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid.scss +++ b/src/plugins/discover/public/components/discover_grid/discover_grid.scss @@ -30,6 +30,15 @@ } } +.dscDiscoverGrid__cellValue { + font-family: $euiCodeFontFamily; +} + +.dscDiscoverGrid__cellPopoverValue { + font-family: $euiCodeFontFamily; + font-size: $euiFontSizeS; +} + .dscDiscoverGrid__footer { background-color: $euiColorLightShade; padding: $euiSize / 2 $euiSize; diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid_columns.test.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid_columns.test.tsx index a9116e616946f..c98db31a97f7f 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid_columns.test.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid_columns.test.tsx @@ -207,7 +207,7 @@ describe('Discover grid columns', function () { /> , "id": "timestamp", - "initialWidth": 190, + "initialWidth": 210, "isSortable": true, "schema": "datetime", }, diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid_document_selection.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid_document_selection.tsx index e804dcb8eca7a..2f1f74153d7af 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid_document_selection.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid_document_selection.tsx @@ -18,6 +18,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { euiLightVars as themeLight, euiDarkVars as themeDark } from '@kbn/ui-theme'; +import { i18n } from '@kbn/i18n'; import { DiscoverGridContext } from './discover_grid_context'; import { ElasticSearchHit } from '../../types'; @@ -36,6 +37,11 @@ export const SelectButton = ({ rowIndex, setCellProps }: EuiDataGridCellValueEle const id = useMemo(() => getDocId(doc), [doc]); const checked = useMemo(() => selectedDocs.includes(id), [selectedDocs, id]); + const toggleDocumentSelectionLabel = i18n.translate('discover.grid.selectDoc', { + defaultMessage: `Select document '{rowNumber}'`, + values: { rowNumber: rowIndex + 1 }, + }); + useEffect(() => { if (expanded && doc && expanded._id === doc._id) { setCellProps({ @@ -51,7 +57,7 @@ export const SelectButton = ({ rowIndex, setCellProps }: EuiDataGridCellValueEle return ( { diff --git a/src/plugins/discover/public/components/discover_grid/get_render_cell_value.test.tsx b/src/plugins/discover/public/components/discover_grid/get_render_cell_value.test.tsx index be4c69f1ced25..53e5c23cb47d5 100644 --- a/src/plugins/discover/public/components/discover_grid/get_render_cell_value.test.tsx +++ b/src/plugins/discover/public/components/discover_grid/get_render_cell_value.test.tsx @@ -92,7 +92,9 @@ describe('Discover grid cell rendering', function () { setCellProps={jest.fn()} /> ); - expect(component.html()).toMatchInlineSnapshot(`"100"`); + expect(component.html()).toMatchInlineSnapshot( + `"100"` + ); }); it('renders bytes column correctly using _source when details is true', () => { @@ -115,7 +117,9 @@ describe('Discover grid cell rendering', function () { setCellProps={jest.fn()} /> ); - expect(component.html()).toMatchInlineSnapshot(`"100"`); + expect(component.html()).toMatchInlineSnapshot( + `"100"` + ); }); it('renders bytes column correctly using fields when details is true', () => { @@ -138,7 +142,9 @@ describe('Discover grid cell rendering', function () { setCellProps={jest.fn()} /> ); - expect(component.html()).toMatchInlineSnapshot(`"100"`); + expect(component.html()).toMatchInlineSnapshot( + `"100"` + ); }); it('renders _source column correctly', () => { @@ -163,7 +169,7 @@ describe('Discover grid cell rendering', function () { ); expect(component).toMatchInlineSnapshot(` @@ -280,7 +286,7 @@ describe('Discover grid cell rendering', function () { ); expect(component).toMatchInlineSnapshot(` @@ -359,7 +365,7 @@ describe('Discover grid cell rendering', function () { ); expect(component).toMatchInlineSnapshot(` @@ -485,7 +491,7 @@ describe('Discover grid cell rendering', function () { ); expect(component).toMatchInlineSnapshot(` @@ -527,7 +533,7 @@ describe('Discover grid cell rendering', function () { ); expect(component).toMatchInlineSnapshot(` @@ -603,6 +609,7 @@ describe('Discover grid cell rendering', function () { ); expect(component).toMatchInlineSnapshot(` ); - expect(component.html()).toMatchInlineSnapshot(`"-"`); + expect(component.html()).toMatchInlineSnapshot( + `"-"` + ); }); it('renders correctly when invalid column is given', () => { @@ -657,7 +666,9 @@ describe('Discover grid cell rendering', function () { setCellProps={jest.fn()} /> ); - expect(component.html()).toMatchInlineSnapshot(`"-"`); + expect(component.html()).toMatchInlineSnapshot( + `"-"` + ); }); it('renders unmapped fields correctly', () => { @@ -695,6 +706,7 @@ describe('Discover grid cell rendering', function () { ); expect(component).toMatchInlineSnapshot(` -; + return -; } /** @@ -102,7 +105,11 @@ export const getRenderCellValueFn = : formatHit(row, dataView, fieldsToShow, maxEntries, fieldFormats); return ( - + {pairs.map(([key, value]) => ( {key} @@ -118,6 +125,7 @@ export const getRenderCellValueFn = return ( { - describe('getSavedSearchUrl', () => { - test('should return valid saved search url', () => { - expect(getSavedSearchUrl()).toBe('#/'); - expect(getSavedSearchUrl('id')).toBe('#/view/id'); - }); - }); - - describe('getSavedSearchFullPathUrl', () => { - test('should return valid full path url', () => { - expect(getSavedSearchFullPathUrl()).toBe('/app/discover#/'); - expect(getSavedSearchFullPathUrl('id')).toBe('/app/discover#/view/id'); - }); - }); - describe('fromSavedSearchAttributes', () => { test('should convert attributes into SavedSearch', () => { const attributes: SavedSearchAttributes = { diff --git a/src/plugins/discover/public/services/saved_searches/saved_searches_utils.ts b/src/plugins/discover/public/services/saved_searches/saved_searches_utils.ts index 4dbb84613ead8..26b3c0b7cf9b5 100644 --- a/src/plugins/discover/public/services/saved_searches/saved_searches_utils.ts +++ b/src/plugins/discover/public/services/saved_searches/saved_searches_utils.ts @@ -8,9 +8,10 @@ import { i18n } from '@kbn/i18n'; import type { SavedSearchAttributes, SavedSearch } from './types'; -export const getSavedSearchUrl = (id?: string) => (id ? `#/view/${encodeURIComponent(id)}` : '#/'); - -export const getSavedSearchFullPathUrl = (id?: string) => `/app/discover${getSavedSearchUrl(id)}`; +export { + getSavedSearchUrl, + getSavedSearchFullPathUrl, +} from '../../../common/services/saved_searches'; export const getSavedSearchUrlConflictMessage = async (savedSearch: SavedSearch) => i18n.translate('discover.savedSearchEmbeddable.legacyURLConflict.errorMessage', { diff --git a/src/plugins/discover/server/plugin.ts b/src/plugins/discover/server/plugin.ts index 9147f533d28d6..888fcf55c2351 100644 --- a/src/plugins/discover/server/plugin.ts +++ b/src/plugins/discover/server/plugin.ts @@ -8,15 +8,18 @@ import { CoreSetup, CoreStart, Plugin } from '@kbn/core/server'; import type { PluginSetup as DataPluginSetup } from '@kbn/data-plugin/server'; +import type { HomeServerPluginSetup } from '@kbn/home-plugin/server'; import { getUiSettings } from './ui_settings'; import { capabilitiesProvider } from './capabilities_provider'; import { getSavedSearchObjectType } from './saved_objects'; +import { registerSampleData } from './sample_data'; export class DiscoverServerPlugin implements Plugin { public setup( core: CoreSetup, plugins: { data: DataPluginSetup; + home?: HomeServerPluginSetup; } ) { const getSearchSourceMigrations = plugins.data.search.searchSource.getAllMigrations.bind( @@ -26,6 +29,10 @@ export class DiscoverServerPlugin implements Plugin { core.uiSettings.register(getUiSettings(core.docLinks)); core.savedObjects.registerType(getSavedSearchObjectType(getSearchSourceMigrations)); + if (plugins.home) { + registerSampleData(plugins.home.sampleData); + } + return {}; } diff --git a/src/plugins/discover/server/sample_data/index.ts b/src/plugins/discover/server/sample_data/index.ts new file mode 100644 index 0000000000000..43edd42293edf --- /dev/null +++ b/src/plugins/discover/server/sample_data/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 { registerSampleData } from './register_sample_data'; diff --git a/src/plugins/discover/server/sample_data/register_sample_data.ts b/src/plugins/discover/server/sample_data/register_sample_data.ts new file mode 100644 index 0000000000000..a1ff9951d9179 --- /dev/null +++ b/src/plugins/discover/server/sample_data/register_sample_data.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import type { SampleDataRegistrySetup } from '@kbn/home-plugin/server'; +import { APP_ICON } from '../../common'; +import { getSavedSearchFullPathUrl } from '../../common/services/saved_searches'; + +function getDiscoverPathForSampleDataset(objId: string) { + // TODO: remove the time range from the URL query when saved search objects start supporting time range configuration + // https://github.com/elastic/kibana/issues/9761 + return `${getSavedSearchFullPathUrl(objId)}?_g=(time:(from:now-7d,to:now))`; +} + +export function registerSampleData(sampleDataRegistry: SampleDataRegistrySetup) { + const linkLabel = i18n.translate('discover.sampleData.viewLinkLabel', { + defaultMessage: 'Discover', + }); + const { addAppLinksToSampleDataset, getSampleDatasets } = sampleDataRegistry; + const sampleDatasets = getSampleDatasets(); + + sampleDatasets.forEach((sampleDataset) => { + const sampleSavedSearchObject = sampleDataset.savedObjects.find( + (object) => object.type === 'search' + ); + + if (sampleSavedSearchObject) { + addAppLinksToSampleDataset(sampleDataset.id, [ + { + sampleObject: sampleSavedSearchObject, + getPath: getDiscoverPathForSampleDataset, + label: linkLabel, + icon: APP_ICON, + order: -1, + }, + ]); + } + }); +} diff --git a/src/plugins/discover/server/saved_objects/search_migrations.test.ts b/src/plugins/discover/server/saved_objects/search_migrations.test.ts index 9563bd6dc86c3..fcce5d41fe90b 100644 --- a/src/plugins/discover/server/saved_objects/search_migrations.test.ts +++ b/src/plugins/discover/server/saved_objects/search_migrations.test.ts @@ -350,6 +350,7 @@ Object { testMigrateMatchAllQuery(migrationFn); }); }); + it('should apply search source migrations within saved search', () => { const savedSearch = { attributes: { @@ -379,4 +380,27 @@ Object { }, }); }); + + it('should not apply search source migrations within saved search when searchSourceJSON is not an object', () => { + const savedSearch = { + attributes: { + kibanaSavedObjectMeta: { + searchSourceJSON: '5', + }, + }, + } as SavedObjectUnsanitizedDoc; + + const versionToTest = '9.1.2'; + const migrations = getAllMigrations({ + [versionToTest]: (state) => ({ ...state, migrated: true }), + }); + + expect(migrations[versionToTest](savedSearch, {} as SavedObjectMigrationContext)).toEqual({ + attributes: { + kibanaSavedObjectMeta: { + searchSourceJSON: '5', + }, + }, + }); + }); }); diff --git a/src/plugins/discover/server/saved_objects/search_migrations.ts b/src/plugins/discover/server/saved_objects/search_migrations.ts index 95da82fa38acf..2fb49628f53bc 100644 --- a/src/plugins/discover/server/saved_objects/search_migrations.ts +++ b/src/plugins/discover/server/saved_objects/search_migrations.ts @@ -17,7 +17,7 @@ import type { import { mergeSavedObjectMigrationMaps } from '@kbn/core/server'; import { DEFAULT_QUERY_LANGUAGE } from '@kbn/data-plugin/server'; import { MigrateFunctionsObject, MigrateFunction } from '@kbn/kibana-utils-plugin/common'; -import type { SerializedSearchSourceFields } from '@kbn/data-plugin/common'; +import { isSerializedSearchSource, SerializedSearchSourceFields } from '@kbn/data-plugin/common'; export interface SavedSearchMigrationAttributes extends SavedObjectAttributes { kibanaSavedObjectMeta: { @@ -135,27 +135,31 @@ const migrateSearchSortToNestedArray: SavedObjectMigrationFn = (doc) = /** * This creates a migration map that applies search source migrations */ -const getSearchSourceMigrations = (searchSourceMigrations: MigrateFunctionsObject) => +const getSearchSourceMigrations = ( + searchSourceMigrations: MigrateFunctionsObject +): MigrateFunctionsObject => mapValues( searchSourceMigrations, (migrate: MigrateFunction): MigrateFunction => (state) => { - const _state = state as unknown as { attributes: SavedSearchMigrationAttributes }; - - const parsedSearchSourceJSON = _state.attributes.kibanaSavedObjectMeta.searchSourceJSON; - - if (!parsedSearchSourceJSON) return _state; - - return { - ..._state, - attributes: { - ..._state.attributes, - kibanaSavedObjectMeta: { - ..._state.attributes.kibanaSavedObjectMeta, - searchSourceJSON: JSON.stringify(migrate(JSON.parse(parsedSearchSourceJSON))), + const _state = state as { attributes: SavedSearchMigrationAttributes }; + + const parsedSearchSourceJSON = JSON.parse( + _state.attributes.kibanaSavedObjectMeta.searchSourceJSON + ); + if (isSerializedSearchSource(parsedSearchSourceJSON)) { + return { + ..._state, + attributes: { + ..._state.attributes, + kibanaSavedObjectMeta: { + ..._state.attributes.kibanaSavedObjectMeta, + searchSourceJSON: JSON.stringify(migrate(parsedSearchSourceJSON)), + }, }, - }, - }; + }; + } + return _state; } ); @@ -171,6 +175,6 @@ export const getAllMigrations = ( ): SavedObjectMigrationMap => { return mergeSavedObjectMigrationMaps( searchMigrations, - getSearchSourceMigrations(searchSourceMigrations) as unknown as SavedObjectMigrationMap + getSearchSourceMigrations(searchSourceMigrations) as SavedObjectMigrationMap ); }; diff --git a/src/plugins/discover/tsconfig.json b/src/plugins/discover/tsconfig.json index 817e73f16617e..9915680ada26e 100644 --- a/src/plugins/discover/tsconfig.json +++ b/src/plugins/discover/tsconfig.json @@ -25,6 +25,7 @@ { "path": "../data_view_field_editor/tsconfig.json"}, { "path": "../field_formats/tsconfig.json" }, { "path": "../data_views/tsconfig.json" }, + { "path": "../unified_search/tsconfig.json" }, { "path": "../../../x-pack/plugins/spaces/tsconfig.json" }, { "path": "../data_view_editor/tsconfig.json" }, { "path": "../../../x-pack/plugins/triggers_actions_ui/tsconfig.json" } diff --git a/src/plugins/embeddable/common/index.ts b/src/plugins/embeddable/common/index.ts index 4eed6531cf7d5..ad362a0d8dc7c 100644 --- a/src/plugins/embeddable/common/index.ts +++ b/src/plugins/embeddable/common/index.ts @@ -12,6 +12,7 @@ export type { EmbeddableStateWithType, PanelState, EmbeddablePersistableStateService, + EmbeddableRegistryDefinition, } from './types'; export { ViewMode } from './types'; export type { SavedObjectEmbeddableInput } from './lib'; diff --git a/src/plugins/embeddable/common/types.ts b/src/plugins/embeddable/common/types.ts index b7cdc9bd08df3..c37b3ee2b720c 100644 --- a/src/plugins/embeddable/common/types.ts +++ b/src/plugins/embeddable/common/types.ts @@ -8,7 +8,11 @@ import type { SerializableRecord } from '@kbn/utility-types'; import type { KibanaExecutionContext } from '@kbn/core/public'; -import { PersistableStateService, PersistableState } from '@kbn/kibana-utils-plugin/common'; +import type { + PersistableStateService, + PersistableState, + PersistableStateDefinition, +} from '@kbn/kibana-utils-plugin/common'; export enum ViewMode { EDIT = 'edit', @@ -54,6 +58,11 @@ export type EmbeddableInput = { */ syncColors?: boolean; + /** + * Flag whether tooltips should be synced with other panels on hover + */ + syncTooltips?: boolean; + executionContext?: KibanaExecutionContext; }; @@ -69,6 +78,12 @@ export interface PanelState extends PersistableStateDefinition

{ + id: string; +} + export type EmbeddablePersistableStateService = PersistableStateService; export interface CommonEmbeddableStartContract { diff --git a/src/plugins/embeddable/public/lib/embeddables/diff_embeddable_input.test.ts b/src/plugins/embeddable/public/lib/embeddables/diff_embeddable_input.test.ts index 00c4de5ff426f..1dfd056bc75c0 100644 --- a/src/plugins/embeddable/public/lib/embeddables/diff_embeddable_input.test.ts +++ b/src/plugins/embeddable/public/lib/embeddables/diff_embeddable_input.test.ts @@ -20,6 +20,7 @@ const getGenericEmbeddableState = (state?: Partial): Embeddable disableTriggers: false, enhancements: undefined, syncColors: false, + syncTooltips: false, viewMode: ViewMode.VIEW, title: 'So Very Generic', id: 'soVeryGeneric', @@ -44,6 +45,7 @@ test('Omitting generic embeddable input omits all generic input keys', () => { 'disableTriggers', 'enhancements', 'syncColors', + 'syncTooltips', 'viewMode', 'title', 'id', diff --git a/src/plugins/embeddable/public/lib/embeddables/diff_embeddable_input.ts b/src/plugins/embeddable/public/lib/embeddables/diff_embeddable_input.ts index a396ed324a949..a66294d08bdc4 100644 --- a/src/plugins/embeddable/public/lib/embeddables/diff_embeddable_input.ts +++ b/src/plugins/embeddable/public/lib/embeddables/diff_embeddable_input.ts @@ -20,6 +20,7 @@ const allGenericInputKeys: Readonly> = [ 'disableTriggers', 'enhancements', 'syncColors', + 'syncTooltips', 'viewMode', 'title', 'id', @@ -31,6 +32,7 @@ const genericInputKeysToCompare = [ 'disableTriggers', 'enhancements', 'syncColors', + 'syncTooltips', 'title', 'id', ] as const; diff --git a/src/plugins/embeddable/server/index.ts b/src/plugins/embeddable/server/index.ts index 8d88f35a4be22..4b93f0838c649 100644 --- a/src/plugins/embeddable/server/index.ts +++ b/src/plugins/embeddable/server/index.ts @@ -10,6 +10,8 @@ import { EmbeddableServerPlugin, EmbeddableSetup, EmbeddableStart } from './plug export type { EmbeddableSetup, EmbeddableStart }; -export type { EnhancementRegistryDefinition, EmbeddableRegistryDefinition } from './types'; +export type { EnhancementRegistryDefinition } from './types'; + +export type { EmbeddableRegistryDefinition } from '../common'; export const plugin = () => new EmbeddableServerPlugin(); diff --git a/src/plugins/embeddable/server/plugin.ts b/src/plugins/embeddable/server/plugin.ts index 51fa1edb2c634..2260d6b34c8e8 100644 --- a/src/plugins/embeddable/server/plugin.ts +++ b/src/plugins/embeddable/server/plugin.ts @@ -19,7 +19,6 @@ import { EnhancementsRegistry, EnhancementRegistryDefinition, EnhancementRegistryItem, - EmbeddableRegistryDefinition, } from './types'; import { getExtractFunction, @@ -27,7 +26,11 @@ import { getMigrateFunction, getTelemetryFunction, } from '../common/lib'; -import { EmbeddableStateWithType, CommonEmbeddableStartContract } from '../common/types'; +import { + EmbeddableStateWithType, + CommonEmbeddableStartContract, + EmbeddableRegistryDefinition, +} from '../common/types'; import { getAllMigrations } from '../common/lib/get_all_migrations'; export interface EmbeddableSetup extends PersistableStateService { diff --git a/src/plugins/embeddable/server/types.ts b/src/plugins/embeddable/server/types.ts index 9b0479d6bc25d..bd78265bea6b1 100644 --- a/src/plugins/embeddable/server/types.ts +++ b/src/plugins/embeddable/server/types.ts @@ -23,12 +23,6 @@ export interface EnhancementRegistryItem

extends PersistableStateDefinition

{ - id: string; -} - export interface EmbeddableRegistryItem

extends PersistableState

{ id: string; diff --git a/src/plugins/es_ui_shared/kibana.json b/src/plugins/es_ui_shared/kibana.json index 1a4ff33674f95..c7fd36e0c1467 100644 --- a/src/plugins/es_ui_shared/kibana.json +++ b/src/plugins/es_ui_shared/kibana.json @@ -14,5 +14,5 @@ "static/forms/components", "static/forms/helpers/field_validators/types" ], - "requiredBundles": ["data", "kibanaReact"] + "requiredBundles": ["dataViews", "kibanaReact"] } diff --git a/src/plugins/es_ui_shared/public/components/cron_editor/__snapshots__/cron_editor.test.tsx.snap b/src/plugins/es_ui_shared/public/components/cron_editor/__snapshots__/cron_editor.test.tsx.snap index 17cf07121e7ce..692c006fe099a 100644 --- a/src/plugins/es_ui_shared/public/components/cron_editor/__snapshots__/cron_editor.test.tsx.snap +++ b/src/plugins/es_ui_shared/public/components/cron_editor/__snapshots__/cron_editor.test.tsx.snap @@ -23,7 +23,7 @@ exports[`CronEditor is rendered with a DAY frequency 1`] = ` labelType="label" >

@@ -1663,7 +1663,7 @@ exports[`CronEditor is rendered with a HOUR frequency 1`] = ` labelType="label" >
@@ -2860,7 +2860,7 @@ exports[`CronEditor is rendered with a MINUTE frequency 1`] = ` labelType="label" >
@@ -4195,7 +4195,7 @@ exports[`CronEditor is rendered with a MONTH frequency 1`] = ` labelType="label" >
@@ -5287,7 +5287,7 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = ` labelType="label" >
@@ -6078,7 +6078,7 @@ exports[`CronEditor is rendered with a WEEK frequency 1`] = ` labelType="label" >
@@ -7170,7 +7170,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = ` labelType="label" >
@@ -8160,7 +8160,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = ` labelType="label" >
@@ -8610,7 +8610,7 @@ exports[`CronEditor is rendered with a YEAR frequency 1`] = ` labelType="label" >
diff --git a/src/plugins/es_ui_shared/public/indices/constants/index.ts b/src/plugins/es_ui_shared/public/indices/constants/index.ts index f7858c94dcab9..01bd15bedc26a 100644 --- a/src/plugins/es_ui_shared/public/indices/constants/index.ts +++ b/src/plugins/es_ui_shared/public/indices/constants/index.ts @@ -6,11 +6,11 @@ * Side Public License, v 1. */ -import { indexPatterns } from '@kbn/data-plugin/public'; +import { ILLEGAL_CHARACTERS_VISIBLE } from '@kbn/data-views-plugin/public'; -export const INDEX_ILLEGAL_CHARACTERS_VISIBLE = [...indexPatterns.ILLEGAL_CHARACTERS_VISIBLE, '*']; +export const INDEX_ILLEGAL_CHARACTERS_VISIBLE = [...ILLEGAL_CHARACTERS_VISIBLE, '*']; // Insert the comma into the middle, so it doesn't look as if it has grammatical meaning when // these characters are rendered in the UI. -const insertionIndex = Math.floor(indexPatterns.ILLEGAL_CHARACTERS_VISIBLE.length / 2); +const insertionIndex = Math.floor(ILLEGAL_CHARACTERS_VISIBLE.length / 2); INDEX_ILLEGAL_CHARACTERS_VISIBLE.splice(insertionIndex, 0, ','); diff --git a/src/plugins/es_ui_shared/static/forms/helpers/field_validators/index_pattern_field.ts b/src/plugins/es_ui_shared/static/forms/helpers/field_validators/index_pattern_field.ts index 52cd12b09a32e..3edb6b2c0090a 100644 --- a/src/plugins/es_ui_shared/static/forms/helpers/field_validators/index_pattern_field.ts +++ b/src/plugins/es_ui_shared/static/forms/helpers/field_validators/index_pattern_field.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { indexPatterns } from '@kbn/data-plugin/public'; +import { validateDataView } from '@kbn/data-views-plugin/public'; import { ValidationFunc } from '../../hook_form_lib'; import { containsChars } from '../../../validators/string'; import { ERROR_CODE } from './types'; @@ -34,7 +34,7 @@ export const indexPatternField = } // Validate illegal characters - const errors = indexPatterns.validate(value); + const errors = validateDataView(value); if (errors.ILLEGAL_CHARACTERS) { return { diff --git a/src/plugins/es_ui_shared/tsconfig.json b/src/plugins/es_ui_shared/tsconfig.json index 38e6cf78f8f60..90459058eec71 100644 --- a/src/plugins/es_ui_shared/tsconfig.json +++ b/src/plugins/es_ui_shared/tsconfig.json @@ -15,6 +15,6 @@ ], "references": [ { "path": "../../core/tsconfig.json" }, - { "path": "../data/tsconfig.json" } + { "path": "../data_views/tsconfig.json" } ] } diff --git a/src/plugins/event_annotation/common/constants.ts b/src/plugins/event_annotation/common/constants.ts new file mode 100644 index 0000000000000..3338450b64ce5 --- /dev/null +++ b/src/plugins/event_annotation/common/constants.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const AvailableAnnotationIcons = { + ASTERISK: 'asterisk', + ALERT: 'alert', + BELL: 'bell', + BOLT: 'bolt', + BUG: 'bug', + CIRCLE: 'circle', + EDITOR_COMMENT: 'editorComment', + FLAG: 'flag', + HEART: 'heart', + MAP_MARKER: 'mapMarker', + PIN_FILLED: 'pinFilled', + STAR_EMPTY: 'starEmpty', + TAG: 'tag', + TRIANGLE: 'triangle', +} as const; diff --git a/src/plugins/event_annotation/common/event_annotation_group/index.ts b/src/plugins/event_annotation/common/event_annotation_group/index.ts index f6a1f38459c13..a3a36505b1c2d 100644 --- a/src/plugins/event_annotation/common/event_annotation_group/index.ts +++ b/src/plugins/event_annotation/common/event_annotation_group/index.ts @@ -35,7 +35,7 @@ export function eventAnnotationGroup(): ExpressionFunctionDefinition< }), args: { annotations: { - types: ['manual_event_annotation'], + types: ['manual_point_event_annotation', 'manual_range_event_annotation'], help: i18n.translate('eventAnnotation.group.args.annotationConfigs', { defaultMessage: 'Annotation configs', }), diff --git a/src/plugins/event_annotation/common/index.ts b/src/plugins/event_annotation/common/index.ts index 332fa19150aad..50d7c8b851776 100644 --- a/src/plugins/event_annotation/common/index.ts +++ b/src/plugins/event_annotation/common/index.ts @@ -6,8 +6,19 @@ * Side Public License, v 1. */ -export type { EventAnnotationArgs, EventAnnotationOutput } from './manual_event_annotation/types'; -export { manualEventAnnotation } from './manual_event_annotation'; +export type { + EventAnnotationArgs, + EventAnnotationOutput, + ManualPointEventAnnotationArgs, + ManualPointEventAnnotationOutput, + ManualRangeEventAnnotationArgs, + ManualRangeEventAnnotationOutput, +} from './manual_event_annotation/types'; +export { manualPointEventAnnotation, manualRangeEventAnnotation } from './manual_event_annotation'; export { eventAnnotationGroup } from './event_annotation_group'; export type { EventAnnotationGroupArgs } from './event_annotation_group'; -export type { EventAnnotationConfig } from './types'; +export type { + EventAnnotationConfig, + RangeEventAnnotationConfig, + AvailableAnnotationIcon, +} from './types'; diff --git a/src/plugins/event_annotation/common/manual_event_annotation/index.ts b/src/plugins/event_annotation/common/manual_event_annotation/index.ts index 167adcb3ed739..bb02018f5a81a 100644 --- a/src/plugins/event_annotation/common/manual_event_annotation/index.ts +++ b/src/plugins/event_annotation/common/manual_event_annotation/index.ts @@ -8,16 +8,24 @@ import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { i18n } from '@kbn/i18n'; -import type { EventAnnotationArgs, EventAnnotationOutput } from './types'; -export const manualEventAnnotation: ExpressionFunctionDefinition< - 'manual_event_annotation', +import { AvailableAnnotationIcons } from '../constants'; + +import type { + ManualRangeEventAnnotationArgs, + ManualRangeEventAnnotationOutput, + ManualPointEventAnnotationArgs, + ManualPointEventAnnotationOutput, +} from './types'; + +export const manualPointEventAnnotation: ExpressionFunctionDefinition< + 'manual_point_event_annotation', null, - EventAnnotationArgs, - EventAnnotationOutput + ManualPointEventAnnotationArgs, + ManualPointEventAnnotationOutput > = { - name: 'manual_event_annotation', + name: 'manual_point_event_annotation', aliases: [], - type: 'manual_event_annotation', + type: 'manual_point_event_annotation', help: i18n.translate('eventAnnotation.manualAnnotation.description', { defaultMessage: `Configure manual annotation`, }), @@ -59,6 +67,8 @@ export const manualEventAnnotation: ExpressionFunctionDefinition< help: i18n.translate('eventAnnotation.manualAnnotation.args.icon', { defaultMessage: 'An optional icon used for annotation lines', }), + options: [...Object.values(AvailableAnnotationIcons)], + strict: true, }, textVisibility: { types: ['boolean'], @@ -73,9 +83,68 @@ export const manualEventAnnotation: ExpressionFunctionDefinition< }), }, }, - fn: function fn(input: unknown, args: EventAnnotationArgs) { + fn: function fn(input: unknown, args: ManualPointEventAnnotationArgs) { + return { + type: 'manual_point_event_annotation', + ...args, + }; + }, +}; + +export const manualRangeEventAnnotation: ExpressionFunctionDefinition< + 'manual_range_event_annotation', + null, + ManualRangeEventAnnotationArgs, + ManualRangeEventAnnotationOutput +> = { + name: 'manual_range_event_annotation', + aliases: [], + type: 'manual_range_event_annotation', + help: i18n.translate('eventAnnotation.manualAnnotation.description', { + defaultMessage: `Configure manual annotation`, + }), + inputTypes: ['null'], + args: { + time: { + types: ['string'], + help: i18n.translate('eventAnnotation.manualAnnotation.args.time', { + defaultMessage: `Timestamp for annotation`, + }), + }, + endTime: { + types: ['string'], + help: i18n.translate('eventAnnotation.manualAnnotation.args.endTime', { + defaultMessage: `Timestamp for range annotation`, + }), + required: false, + }, + outside: { + types: ['boolean'], + help: '', + required: false, + }, + label: { + types: ['string'], + help: i18n.translate('eventAnnotation.manualAnnotation.args.label', { + defaultMessage: `The name of the annotation`, + }), + }, + color: { + types: ['string'], + help: i18n.translate('eventAnnotation.manualAnnotation.args.color', { + defaultMessage: 'The color of the line', + }), + }, + isHidden: { + types: ['boolean'], + help: i18n.translate('eventAnnotation.manualAnnotation.args.isHidden', { + defaultMessage: `Switch to hide annotation`, + }), + }, + }, + fn: function fn(input: unknown, args: ManualRangeEventAnnotationArgs) { return { - type: 'manual_event_annotation', + type: 'manual_range_event_annotation', ...args, }; }, diff --git a/src/plugins/event_annotation/common/manual_event_annotation/types.ts b/src/plugins/event_annotation/common/manual_event_annotation/types.ts index e1bed4a592d23..208383734924c 100644 --- a/src/plugins/event_annotation/common/manual_event_annotation/types.ts +++ b/src/plugins/event_annotation/common/manual_event_annotation/types.ts @@ -6,10 +6,26 @@ * Side Public License, v 1. */ -import { StyleProps } from '../types'; +import { PointStyleProps, RangeStyleProps } from '../types'; -export type EventAnnotationArgs = { +export type ManualPointEventAnnotationArgs = { time: string; -} & StyleProps; +} & PointStyleProps; -export type EventAnnotationOutput = EventAnnotationArgs & { type: 'manual_event_annotation' }; +export type ManualPointEventAnnotationOutput = ManualPointEventAnnotationArgs & { + type: 'manual_point_event_annotation'; +}; + +export type ManualRangeEventAnnotationArgs = { + time: string; + endTime: string; +} & RangeStyleProps; + +export type ManualRangeEventAnnotationOutput = ManualRangeEventAnnotationArgs & { + type: 'manual_range_event_annotation'; +}; + +export type EventAnnotationArgs = ManualPointEventAnnotationArgs | ManualRangeEventAnnotationArgs; +export type EventAnnotationOutput = + | ManualPointEventAnnotationOutput + | ManualRangeEventAnnotationOutput; diff --git a/src/plugins/event_annotation/common/types.ts b/src/plugins/event_annotation/common/types.ts index 95275804d1d1f..e0b0de3c85c9e 100644 --- a/src/plugins/event_annotation/common/types.ts +++ b/src/plugins/event_annotation/common/types.ts @@ -6,24 +6,48 @@ * Side Public License, v 1. */ +import { $Values } from '@kbn/utility-types'; +import { AvailableAnnotationIcons } from './constants'; + export type LineStyle = 'solid' | 'dashed' | 'dotted'; +export type Fill = 'inside' | 'outside' | 'none'; export type AnnotationType = 'manual'; -export type KeyType = 'point_in_time'; - -export interface StyleProps { +export type KeyType = 'point_in_time' | 'range'; +export type AvailableAnnotationIcon = $Values; +export interface PointStyleProps { label: string; color?: string; - icon?: string; + icon?: AvailableAnnotationIcon; lineWidth?: number; lineStyle?: LineStyle; textVisibility?: boolean; isHidden?: boolean; } -export type EventAnnotationConfig = { +export type PointInTimeEventAnnotationConfig = { + id: string; + key: { + type: 'point_in_time'; + timestamp: string; + }; +} & PointStyleProps; + +export interface RangeStyleProps { + label: string; + color?: string; + outside?: boolean; + isHidden?: boolean; +} + +export type RangeEventAnnotationConfig = { id: string; key: { - type: KeyType; + type: 'range'; timestamp: string; + endTimestamp: string; }; -} & StyleProps; +} & RangeStyleProps; + +export type StyleProps = PointStyleProps & RangeStyleProps; + +export type EventAnnotationConfig = PointInTimeEventAnnotationConfig | RangeEventAnnotationConfig; diff --git a/src/plugins/event_annotation/jest.config.js b/src/plugins/event_annotation/jest.config.js deleted file mode 100644 index a6ea4a6b430df..0000000000000 --- a/src/plugins/event_annotation/jest.config.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../../..', - roots: ['/src/plugins/event_annotation'], - coverageDirectory: '/target/kibana-coverage/jest/src/plugins/event_annotation', - coverageReporters: ['text', 'html'], - collectCoverageFrom: [ - '/src/plugins/event_annotation/{common,public,server}/**/*.{ts,tsx}', - ], -}; diff --git a/src/plugins/event_annotation/public/event_annotation_service/helpers.ts b/src/plugins/event_annotation/public/event_annotation_service/helpers.ts index aed33da840574..8eb3d05309ec1 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/helpers.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/helpers.ts @@ -5,5 +5,21 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ +import { i18n } from '@kbn/i18n'; import { euiLightVars } from '@kbn/ui-theme'; +import { EventAnnotationConfig, RangeEventAnnotationConfig } from '../../common'; export const defaultAnnotationColor = euiLightVars.euiColorAccent; +export const defaultAnnotationRangeColor = `#F04E981A`; // defaultAnnotationColor with opacity 0.1 + +export const defaultAnnotationLabel = i18n.translate( + 'eventAnnotation.manualAnnotation.defaultAnnotationLabel', + { + defaultMessage: 'Event', + } +); + +export const isRangeAnnotation = ( + annotation?: EventAnnotationConfig +): annotation is RangeEventAnnotationConfig => { + return Boolean(annotation && annotation?.key.type === 'range'); +}; diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index 3d81ea6a3e3a6..4770c1c182af6 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -7,43 +7,70 @@ */ import { EventAnnotationServiceType } from './types'; -import { defaultAnnotationColor } from './helpers'; +import { + defaultAnnotationColor, + defaultAnnotationRangeColor, + defaultAnnotationLabel, +} from './helpers'; +import { EventAnnotationConfig } from '../../common'; +import { RangeEventAnnotationConfig } from '../../common/types'; export function hasIcon(icon: string | undefined): icon is string { return icon != null && icon !== 'empty'; } +const isRangeAnnotation = ( + annotation?: EventAnnotationConfig +): annotation is RangeEventAnnotationConfig => { + return Boolean(annotation && annotation?.key.type === 'range'); +}; + export function getEventAnnotationService(): EventAnnotationServiceType { return { - toExpression: ({ - label, - isHidden, - color, - lineStyle, - lineWidth, - icon, - textVisibility, - time, - }) => { - return { - type: 'expression', - chain: [ - { - type: 'function', - function: 'manual_event_annotation', - arguments: { - time: [time], - label: [label], - color: [color || defaultAnnotationColor], - lineWidth: [lineWidth || 1], - lineStyle: [lineStyle || 'solid'], - icon: hasIcon(icon) ? [icon] : ['triangle'], - textVisibility: [textVisibility || false], - isHidden: [Boolean(isHidden)], + toExpression: (annotation) => { + if (isRangeAnnotation(annotation)) { + const { label, isHidden, color, key, outside } = annotation; + const { timestamp: time, endTimestamp: endTime } = key; + return { + type: 'expression', + chain: [ + { + type: 'function', + function: 'manual_range_event_annotation', + arguments: { + time: [time], + endTime: [endTime], + label: [label || defaultAnnotationLabel], + color: [color || defaultAnnotationRangeColor], + outside: [Boolean(outside)], + isHidden: [Boolean(isHidden)], + }, + }, + ], + }; + } else { + const { label, isHidden, color, lineStyle, lineWidth, icon, key, textVisibility } = + annotation; + return { + type: 'expression', + chain: [ + { + type: 'function', + function: 'manual_point_event_annotation', + arguments: { + time: [key.timestamp], + label: [label || defaultAnnotationLabel], + color: [color || defaultAnnotationColor], + lineWidth: [lineWidth || 1], + lineStyle: [lineStyle || 'solid'], + icon: hasIcon(icon) ? [icon] : ['triangle'], + textVisibility: [textVisibility || false], + isHidden: [Boolean(isHidden)], + }, }, - }, - ], - }; + ], + }; + } }, }; } diff --git a/src/plugins/event_annotation/public/event_annotation_service/types.ts b/src/plugins/event_annotation/public/event_annotation_service/types.ts index c44b2d1e536d5..d5fcaa23107c8 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/types.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/types.ts @@ -7,8 +7,8 @@ */ import { ExpressionAstExpression } from '@kbn/expressions-plugin/common/ast'; -import { EventAnnotationArgs } from '../../common'; +import { EventAnnotationConfig } from '../../common'; export interface EventAnnotationServiceType { - toExpression: (props: EventAnnotationArgs) => ExpressionAstExpression; + toExpression: (props: EventAnnotationConfig) => ExpressionAstExpression; } diff --git a/src/plugins/event_annotation/public/index.ts b/src/plugins/event_annotation/public/index.ts index c15429c94cbe4..56ddc4b8a60e1 100644 --- a/src/plugins/event_annotation/public/index.ts +++ b/src/plugins/event_annotation/public/index.ts @@ -14,4 +14,8 @@ export const plugin = () => new EventAnnotationPlugin(); export type { EventAnnotationPluginSetup, EventAnnotationPluginStart } from './plugin'; export * from './event_annotation_service/types'; export { EventAnnotationService } from './event_annotation_service'; -export { defaultAnnotationColor } from './event_annotation_service/helpers'; +export { + defaultAnnotationColor, + defaultAnnotationRangeColor, + isRangeAnnotation, +} from './event_annotation_service/helpers'; diff --git a/src/plugins/event_annotation/public/plugin.ts b/src/plugins/event_annotation/public/plugin.ts index f3f4fcfcc60f6..9314151375f20 100644 --- a/src/plugins/event_annotation/public/plugin.ts +++ b/src/plugins/event_annotation/public/plugin.ts @@ -8,7 +8,11 @@ import { Plugin, CoreSetup } from '@kbn/core/public'; import { ExpressionsSetup } from '@kbn/expressions-plugin/public'; -import { manualEventAnnotation, eventAnnotationGroup } from '../common'; +import { + manualPointEventAnnotation, + manualRangeEventAnnotation, + eventAnnotationGroup, +} from '../common'; import { EventAnnotationService } from './event_annotation_service'; interface SetupDependencies { @@ -28,7 +32,8 @@ export class EventAnnotationPlugin private readonly eventAnnotationService = new EventAnnotationService(); public setup(core: CoreSetup, dependencies: SetupDependencies): EventAnnotationPluginSetup { - dependencies.expressions.registerFunction(manualEventAnnotation); + dependencies.expressions.registerFunction(manualPointEventAnnotation); + dependencies.expressions.registerFunction(manualRangeEventAnnotation); dependencies.expressions.registerFunction(eventAnnotationGroup); return this.eventAnnotationService; } diff --git a/src/plugins/event_annotation/server/plugin.ts b/src/plugins/event_annotation/server/plugin.ts index 0643611af9bb3..387326fcf2a21 100644 --- a/src/plugins/event_annotation/server/plugin.ts +++ b/src/plugins/event_annotation/server/plugin.ts @@ -8,7 +8,11 @@ import { CoreSetup, Plugin } from '@kbn/core/server'; import { ExpressionsServerSetup } from '@kbn/expressions-plugin/server'; -import { manualEventAnnotation, eventAnnotationGroup } from '../common'; +import { + manualPointEventAnnotation, + eventAnnotationGroup, + manualRangeEventAnnotation, +} from '../common'; interface SetupDependencies { expressions: ExpressionsServerSetup; @@ -16,7 +20,8 @@ interface SetupDependencies { export class EventAnnotationServerPlugin implements Plugin { public setup(core: CoreSetup, dependencies: SetupDependencies) { - dependencies.expressions.registerFunction(manualEventAnnotation); + dependencies.expressions.registerFunction(manualPointEventAnnotation); + dependencies.expressions.registerFunction(manualRangeEventAnnotation); dependencies.expressions.registerFunction(eventAnnotationGroup); return {}; diff --git a/src/plugins/expressions/common/execution/execution.test.ts b/src/plugins/expressions/common/execution/execution.test.ts index 78d88adfa3dbd..75a95035bb89f 100644 --- a/src/plugins/expressions/common/execution/execution.test.ts +++ b/src/plugins/expressions/common/execution/execution.test.ts @@ -714,7 +714,7 @@ describe('Execution', () => { expect(result).toMatchObject({ type: 'error', error: { - message: '[requiredArg] > requiredArg requires an argument', + message: '[requiredArg] > requiredArg requires the "arg" argument', }, }); }); @@ -725,7 +725,7 @@ describe('Execution', () => { expect(result).toMatchObject({ type: 'error', error: { - message: '[var_set] > var_set requires an "name" argument', + message: '[var_set] > var_set requires the "name" argument', }, }); }); diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts index 17b6338a48f89..2fda462929ff4 100644 --- a/src/plugins/expressions/common/execution/execution.ts +++ b/src/plugins/expressions/common/execution/execution.ts @@ -224,6 +224,7 @@ export class Execution< inspectorAdapters.tables[name] = datatable; }, isSyncColorsEnabled: () => execution.params.syncColors!, + isSyncTooltipsEnabled: () => execution.params.syncTooltips!, ...execution.executor.context, getExecutionContext: () => execution.params.executionContext, }; @@ -480,7 +481,7 @@ export class Execution< ); // Check for missing required arguments. - for (const { aliases, default: argDefault, name, required } of Object.values(argDefs)) { + for (const { default: argDefault, name, required } of Object.values(argDefs)) { if (!(name in dealiasedArgAsts) && typeof argDefault !== 'undefined') { dealiasedArgAsts[name] = [parse(argDefault as string, 'argument')]; } @@ -489,13 +490,7 @@ export class Execution< continue; } - if (!aliases?.length) { - throw new Error(`${fnDef.name} requires an argument`); - } - - // use an alias if _ is the missing arg - const errorArg = name === '_' ? aliases[0] : name; - throw new Error(`${fnDef.name} requires an "${errorArg}" argument`); + throw new Error(`${fnDef.name} requires the "${name}" argument`); } // Create the functions to resolve the argument ASTs into values diff --git a/src/plugins/expressions/common/execution/types.ts b/src/plugins/expressions/common/execution/types.ts index 44d6fef6f79a6..686ade0869171 100644 --- a/src/plugins/expressions/common/execution/types.ts +++ b/src/plugins/expressions/common/execution/types.ts @@ -66,6 +66,11 @@ export interface ExecutionContext< */ isSyncColorsEnabled?: () => boolean; + /** + * Returns the state (true|false) of the sync tooltips across panels switch. + */ + isSyncTooltipsEnabled?: () => boolean; + /** * Contains the meta-data about the source of the expression. */ diff --git a/src/plugins/expressions/common/executor/executor.test.ts b/src/plugins/expressions/common/executor/executor.test.ts index 35f0b9c13aa1a..ea7116a5307ba 100644 --- a/src/plugins/expressions/common/executor/executor.test.ts +++ b/src/plugins/expressions/common/executor/executor.test.ts @@ -141,13 +141,15 @@ describe('Executor', () => { inject: (state: ExpressionAstFunction['arguments']) => { return injectFn(state); }, - migrations: { - '7.10.0': ((state: ExpressionAstFunction, version: string): ExpressionAstFunction => { - return migrateFn(state, version); - }) as unknown as MigrateFunction, - '7.10.1': ((state: ExpressionAstFunction, version: string): ExpressionAstFunction => { - return migrateFn(state, version); - }) as unknown as MigrateFunction, + migrations: () => { + return { + '7.10.0': ((state: ExpressionAstFunction, version: string): ExpressionAstFunction => { + return migrateFn(state, version); + }) as unknown as MigrateFunction, + '7.10.1': ((state: ExpressionAstFunction, version: string): ExpressionAstFunction => { + return migrateFn(state, version); + }) as unknown as MigrateFunction, + }; }, fn: jest.fn(), }; diff --git a/src/plugins/expressions/common/executor/executor.ts b/src/plugins/expressions/common/executor/executor.ts index 0a5e8d388fe00..4071f8f7f003f 100644 --- a/src/plugins/expressions/common/executor/executor.ts +++ b/src/plugins/expressions/common/executor/executor.ts @@ -336,7 +336,11 @@ export class Executor = Record Object.keys(fn.migrations)) + .map((fn) => { + const migrations = + typeof fn.migrations === 'function' ? fn.migrations() : fn.migrations || {}; + return Object.keys(migrations); + }) .flat(1) ); diff --git a/src/plugins/expressions/common/expression_renderers/types.ts b/src/plugins/expressions/common/expression_renderers/types.ts index 1e40ba2a65fff..06d3617d74784 100644 --- a/src/plugins/expressions/common/expression_renderers/types.ts +++ b/src/plugins/expressions/common/expression_renderers/types.ts @@ -91,6 +91,8 @@ export interface IInterpreterRenderHandlers { isInteractive(): boolean; isSyncColorsEnabled(): boolean; + + isSyncTooltipsEnabled(): boolean; /** * This uiState interface is actually `PersistedState` from the visualizations plugin, * but expressions cannot know about vis or it creates a mess of circular dependencies. diff --git a/src/plugins/expressions/common/service/expressions_services.ts b/src/plugins/expressions/common/service/expressions_services.ts index d873a1957cd1f..d4bac702bd6e0 100644 --- a/src/plugins/expressions/common/service/expressions_services.ts +++ b/src/plugins/expressions/common/service/expressions_services.ts @@ -152,6 +152,8 @@ export interface ExpressionExecutionParams { syncColors?: boolean; + syncTooltips?: boolean; + inspectorAdapters?: Adapters; executionContext?: KibanaExecutionContext; diff --git a/src/plugins/expressions/public/loader.ts b/src/plugins/expressions/public/loader.ts index 6482da0af21ee..7f7a96fde6f1a 100644 --- a/src/plugins/expressions/public/loader.ts +++ b/src/plugins/expressions/public/loader.ts @@ -58,6 +58,7 @@ export class ExpressionLoader { onRenderError: params && params.onRenderError, renderMode: params?.renderMode, syncColors: params?.syncColors, + syncTooltips: params?.syncTooltips, hasCompatibleActions: params?.hasCompatibleActions, }); this.render$ = this.renderHandler.render$; @@ -142,6 +143,7 @@ export class ExpressionLoader { searchSessionId: params.searchSessionId, debug: params.debug, syncColors: params.syncColors, + syncTooltips: params.syncTooltips, executionContext: params.executionContext, }); this.subscription = this.execution @@ -182,6 +184,7 @@ export class ExpressionLoader { this.params.searchSessionId = params.searchSessionId; } this.params.syncColors = params.syncColors; + this.params.syncTooltips = params.syncTooltips; this.params.debug = Boolean(params.debug); this.params.partial = Boolean(params.partial); this.params.throttle = Number(params.throttle ?? 1000); diff --git a/src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts b/src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts index 033d50d7faf0d..7daa4b3626fa7 100644 --- a/src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts +++ b/src/plugins/expressions/public/react_expression_renderer/use_expression_renderer.ts @@ -111,6 +111,7 @@ export function useExpressionRenderer( debouncedLoaderParams.interactive, debouncedLoaderParams.renderMode, debouncedLoaderParams.syncColors, + debouncedLoaderParams.syncTooltips, ]); useEffect(() => { diff --git a/src/plugins/expressions/public/render.ts b/src/plugins/expressions/public/render.ts index 25bffdca089ee..1d90a795a03a4 100644 --- a/src/plugins/expressions/public/render.ts +++ b/src/plugins/expressions/public/render.ts @@ -28,6 +28,7 @@ export interface ExpressionRenderHandlerParams { onRenderError?: RenderErrorHandlerFnType; renderMode?: RenderMode; syncColors?: boolean; + syncTooltips?: boolean; interactive?: boolean; hasCompatibleActions?: (event: ExpressionRendererEvent) => Promise; } @@ -54,6 +55,7 @@ export class ExpressionRenderHandler { onRenderError, renderMode, syncColors, + syncTooltips, interactive, hasCompatibleActions = async () => false, }: ExpressionRenderHandlerParams = {} @@ -94,6 +96,9 @@ export class ExpressionRenderHandler { isSyncColorsEnabled: () => { return syncColors || false; }, + isSyncTooltipsEnabled: () => { + return syncTooltips || false; + }, isInteractive: () => { return interactive ?? true; }, diff --git a/src/plugins/expressions/public/types/index.ts b/src/plugins/expressions/public/types/index.ts index 322c2895f9bb5..b035daf4deefc 100644 --- a/src/plugins/expressions/public/types/index.ts +++ b/src/plugins/expressions/public/types/index.ts @@ -50,6 +50,7 @@ export interface IExpressionLoaderParams { searchSessionId?: string; renderMode?: RenderMode; syncColors?: boolean; + syncTooltips?: boolean; hasCompatibleActions?: ExpressionRenderHandlerParams['hasCompatibleActions']; executionContext?: KibanaExecutionContext; diff --git a/src/plugins/home/public/application/components/__snapshots__/sample_data_view_data_button.test.js.snap b/src/plugins/home/public/application/components/__snapshots__/sample_data_view_data_button.test.js.snap index d9e341394ee00..0d634049305ad 100644 --- a/src/plugins/home/public/application/components/__snapshots__/sample_data_view_data_button.test.js.snap +++ b/src/plugins/home/public/application/components/__snapshots__/sample_data_view_data_button.test.js.snap @@ -57,6 +57,90 @@ exports[`should render popover when appLinks is not empty 1`] = ` `; +exports[`should render popover with ordered appLinks 1`] = ` + + View data + + } + closePopover={[Function]} + data-test-subj="launchSampleDataSetecommerce" + display="inlineBlock" + hasArrow={true} + id="sampleDataLinksecommerce" + isOpen={false} + ownFocus={true} + panelPaddingSize="none" +> + , + "name": "myAppLabel[-1]", + "onClick": [Function], + }, + Object { + "data-test-subj": "viewSampleDataSetecommerce-dashboard", + "href": "root/app/dashboards#/view/722b74f0-b882-11e8-a6d9-e546fe2bba5f", + "icon": , + "name": "Dashboard", + "onClick": [Function], + }, + Object { + "href": "rootapp/myAppPath", + "icon": , + "name": "myAppLabel[3]", + "onClick": [Function], + }, + Object { + "href": "rootapp/myAppPath", + "icon": , + "name": "myAppLabel[5]", + "onClick": [Function], + }, + Object { + "href": "rootapp/myAppPath", + "icon": , + "name": "myAppLabel", + "onClick": [Function], + }, + ], + }, + ] + } + size="m" + /> + +`; + exports[`should render simple button when appLinks is empty 1`] = ` { + const dashboardAppLink = { + path: dashboardPath, + label: i18n.translate('home.sampleDataSetCard.dashboardLinkLabel', { + defaultMessage: 'Dashboard', + }), + icon: 'dashboardApp', + order: 0, + 'data-test-subj': `viewSampleDataSet${this.props.id}-dashboard`, + }; + + const sortedItems = sortBy([dashboardAppLink, ...this.props.appLinks], 'order'); + const items = sortedItems.map(({ path, label, icon, ...rest }) => { return { name: label, icon: , href: this.addBasePath(path), onClick: createAppNavigationHandler(path), + ...(rest['data-test-subj'] ? { 'data-test-subj': rest['data-test-subj'] } : {}), }; }); @@ -75,18 +87,7 @@ export class SampleDataViewDataButton extends React.Component { const panels = [ { id: 0, - items: [ - { - name: i18n.translate('home.sampleDataSetCard.dashboardLinkLabel', { - defaultMessage: 'Dashboard', - }), - icon: , - href: prefixedDashboardPath, - onClick: createAppNavigationHandler(dashboardPath), - 'data-test-subj': `viewSampleDataSet${this.props.id}-dashboard`, - }, - ...additionalItems, - ], + items, }, ]; const popoverButton = ( @@ -124,6 +125,7 @@ SampleDataViewDataButton.propTypes = { path: PropTypes.string.isRequired, label: PropTypes.string.isRequired, icon: PropTypes.string.isRequired, + order: PropTypes.number, }) ).isRequired, }; diff --git a/src/plugins/home/public/application/components/sample_data_view_data_button.test.js b/src/plugins/home/public/application/components/sample_data_view_data_button.test.js index b097b5e322500..f3cfd5a7a661e 100644 --- a/src/plugins/home/public/application/components/sample_data_view_data_button.test.js +++ b/src/plugins/home/public/application/components/sample_data_view_data_button.test.js @@ -48,3 +48,41 @@ test('should render popover when appLinks is not empty', () => { ); expect(component).toMatchSnapshot(); // eslint-disable-line }); + +test('should render popover with ordered appLinks', () => { + const appLinks = [ + { + path: 'app/myAppPath', + label: 'myAppLabel[-1]', + icon: 'logoKibana', + order: -1, // to position it above Dashboard link + }, + { + path: 'app/myAppPath', + label: 'myAppLabel', + icon: 'logoKibana', + }, + { + path: 'app/myAppPath', + label: 'myAppLabel[5]', + icon: 'logoKibana', + order: 5, + }, + { + path: 'app/myAppPath', + label: 'myAppLabel[3]', + icon: 'logoKibana', + order: 3, + }, + ]; + + const component = shallow( + + ); + expect(component).toMatchSnapshot(); // eslint-disable-line +}); diff --git a/src/plugins/home/server/services/sample_data/lib/sample_dataset_registry_types.ts b/src/plugins/home/server/services/sample_data/lib/sample_dataset_registry_types.ts index 8d26d08460b5b..9b1212e13b024 100644 --- a/src/plugins/home/server/services/sample_data/lib/sample_dataset_registry_types.ts +++ b/src/plugins/home/server/services/sample_data/lib/sample_dataset_registry_types.ts @@ -58,4 +58,11 @@ export interface AppLinkData { * The icon for this app link. */ icon: string; + /** + * Index of the links (ascending order, smallest will be displayed first). + * Used for ordering in the dropdown. + * + * @remark links without order defined will be displayed last + */ + order?: number; } diff --git a/src/plugins/home/server/services/sample_data/routes/list.ts b/src/plugins/home/server/services/sample_data/routes/list.ts index 39690b3944d0c..a83ee7a57c432 100644 --- a/src/plugins/home/server/services/sample_data/routes/list.ts +++ b/src/plugins/home/server/services/sample_data/routes/list.ts @@ -35,12 +35,12 @@ export const createListRoute = ( ?.foundObjectId ?? id; const appLinks = (appLinksMap.get(sampleDataset.id) ?? []).map((data) => { - const { sampleObject, getPath, label, icon } = data; + const { sampleObject, getPath, label, icon, order } = data; if (sampleObject === null) { - return { path: getPath(''), label, icon }; + return { path: getPath(''), label, icon, order }; } const objectId = findObjectId(sampleObject.type, sampleObject.id); - return { path: getPath(objectId), label, icon }; + return { path: getPath(objectId), label, icon, order }; }); const sampleDataStatus = await getSampleDatasetStatus( context, diff --git a/src/plugins/kibana_usage_collection/README.md b/src/plugins/kibana_usage_collection/README.md index 4ea014457fd07..08e830fba4155 100644 --- a/src/plugins/kibana_usage_collection/README.md +++ b/src/plugins/kibana_usage_collection/README.md @@ -2,17 +2,18 @@ This plugin registers the Platform Usage Collectors in Kibana. -| Collector name | Description | Extended documentation | -|----------------|:------------|:----------------------:| -| **Application Usage** | Measures how popular an App in Kibana is by reporting the on-screen time and the number of general clicks that happen in it. | [Link](./server/collectors/application_usage/README.md) | -| **Core Metrics** | Collects the usage reported by the core APIs | - | -| **Config Usage** | Reports the non-default values set via `kibana.yml` config file or CLI options. It `[redacts]` any potential PII-sensitive values. | [Link](./server/collectors/config_usage/README.md) | -| **User-changed UI Settings** | Reports all the UI Settings that have been overwritten by the user. It `[redacts]` any potential PII-sensitive values. | [Link](./server/collectors/management/README.md) | -| **CSP configuration** | Reports the key values regarding the CSP configuration. | - | -| **Kibana** | It reports the number of Saved Objects per type. It is limited to `dashboard`, `visualization`, `search`, `index-pattern`, `graph-workspace`.
It exists for legacy purposes, and may still be used by Monitoring via Metricbeat. | - | -| **Saved Objects Counts** | Number of Saved Objects per type. | - | -| **Localization data** | Localization settings: setup locale and installed translation files. | - | -| **Ops stats** | Operation metrics from the system. | - | -| **UI Counters** | Daily aggregation of the number of times an event occurs in the UI. | [Link](../usage_collection/README.mdx#ui-counters) | -| **UI Metrics** | Deprecated. Old form of UI Counters. It reports the _count of the repetitions since the cluster's first start_ of any UI events that may have happened. | - | -| **Usage Counters** | Daily aggregation of the number of times an event occurs on the Server. | [Link](../usage_collection/README.mdx#usage-counters) | +| Collector name | Description | Extended documentation | +|--------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------:| +| **Application Usage** | Measures how popular an App in Kibana is by reporting the on-screen time and the number of general clicks that happen in it. | [Link](./server/collectors/application_usage/README.md) | +| **Core Metrics** | Collects the usage reported by the core APIs | - | +| **Config Usage** | Reports the non-default values set via `kibana.yml` config file or CLI options. It `[redacts]` any potential PII-sensitive values. | [Link](./server/collectors/config_usage/README.md) | +| **User-changed UI Settings** | Reports all the UI Settings that have been overwritten by the user. It `[redacts]` any potential PII-sensitive values. | [Link](./server/collectors/management/README.md) | +| **CSP configuration** | Reports the key values regarding the CSP configuration. | - | +| **Kibana** | It reports the number of Saved Objects per type. It is limited to `dashboard`, `visualization`, `search`, `index-pattern`, `graph-workspace`.
It exists for legacy purposes, and may still be used by Monitoring via Metricbeat. | - | +| **Saved Objects Counts** | Number of Saved Objects per type. | - | +| **Localization data** | Localization settings: setup locale and installed translation files. | - | +| **Ops stats** | Operation metrics from the system. | - | +| **UI Counters** | Daily aggregation of the number of times an event occurs in the UI. | [Link](../usage_collection/README.mdx#ui-counters) | +| **UI Metrics** | Deprecated. Old form of UI Counters. It reports the _count of the repetitions since the cluster's first start_ of any UI events that may have happened. | - | +| **Usage Counters** | Daily aggregation of the number of times an event occurs on the Server. | [Link](../usage_collection/README.mdx#usage-counters) | +| **Event-based Telemetry Success Counters** | Using the UI and Usage Counters APIs, it reports the stats coming out of the `core.analytics.telemetryCounters$` observable. | [Browser](./public/ebt_counters/README.md) and [Server](./server/ebt_counters/README.md) | diff --git a/src/plugins/kibana_usage_collection/kibana.json b/src/plugins/kibana_usage_collection/kibana.json index 39b55e5c6dd94..41fc5c6c37b78 100644 --- a/src/plugins/kibana_usage_collection/kibana.json +++ b/src/plugins/kibana_usage_collection/kibana.json @@ -6,7 +6,7 @@ }, "version": "kibana", "server": true, - "ui": false, + "ui": true, "requiredPlugins": [ "usageCollection" ], diff --git a/src/plugins/kibana_usage_collection/public/ebt_counters/README.md b/src/plugins/kibana_usage_collection/public/ebt_counters/README.md new file mode 100644 index 0000000000000..d30aa0661e977 --- /dev/null +++ b/src/plugins/kibana_usage_collection/public/ebt_counters/README.md @@ -0,0 +1,14 @@ +# Event-based Telemetry Success Counters (browser-side) + +Using the UI Counters API, it reports the stats coming from the `core.analytics.telemetryCounters$` observable. It allows us to track the success of the EBT client on the browser. + +## Field mappings + +As the number of fields available in the Usage API is reduced, this collection merges some fields to be able to report it. + +| UI Counter field | Telemetry Counter fields | +|------------------|--------------------------------------------------------------------------------------------------| +| `appName` | Concatenation of the string `'ebt_counters.'` and the `source` (`'client'` or the shipper name). | +| `eventName` | Matches the `eventType`. | +| `counterType` | Concatenation of the `type` and the `code` (i.e.: `'succeeded_200'`). | +| `total` | Matches the value in `count`. | \ No newline at end of file diff --git a/src/plugins/kibana_usage_collection/public/ebt_counters/index.ts b/src/plugins/kibana_usage_collection/public/ebt_counters/index.ts new file mode 100644 index 0000000000000..24deee4afb5d0 --- /dev/null +++ b/src/plugins/kibana_usage_collection/public/ebt_counters/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 { registerEbtCounters } from './register_ebt_counters'; diff --git a/src/plugins/kibana_usage_collection/public/ebt_counters/register_ebt_counters.test.ts b/src/plugins/kibana_usage_collection/public/ebt_counters/register_ebt_counters.test.ts new file mode 100644 index 0000000000000..2bf67d02fe110 --- /dev/null +++ b/src/plugins/kibana_usage_collection/public/ebt_counters/register_ebt_counters.test.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { TelemetryCounter } from '@kbn/analytics-client'; +import { TelemetryCounterType } from '@kbn/analytics-client'; +import { coreMock } from '@kbn/core/public/mocks'; +import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/public/mocks'; +import { registerEbtCounters } from './register_ebt_counters'; + +describe('registerEbtCounters', () => { + let core: ReturnType; + let usageCollection: ReturnType; + let internalListener: (counter: TelemetryCounter) => void; + let telemetryCounter$Spy: jest.SpyInstance; + + beforeEach(() => { + core = coreMock.createSetup(); + usageCollection = usageCollectionPluginMock.createSetupContract(); + telemetryCounter$Spy = jest + .spyOn(core.analytics.telemetryCounter$, 'subscribe') + .mockImplementation(((listener) => { + internalListener = listener as (counter: TelemetryCounter) => void; + }) as typeof core.analytics.telemetryCounter$['subscribe']); + }); + + test('it subscribes to `analytics.telemetryCounters$`', () => { + registerEbtCounters(core.analytics, usageCollection); + expect(telemetryCounter$Spy).toHaveBeenCalledTimes(1); + }); + + test('it reports a UI counter whenever a counter is emitted', () => { + registerEbtCounters(core.analytics, usageCollection); + expect(telemetryCounter$Spy).toHaveBeenCalledTimes(1); + internalListener({ + type: TelemetryCounterType.succeeded, + source: 'test-shipper', + event_type: 'test-event', + code: 'test-code', + count: 1, + }); + expect(usageCollection.reportUiCounter).toHaveBeenCalledTimes(1); + expect(usageCollection.reportUiCounter).toHaveBeenCalledWith( + 'ebt_counters.test-shipper', + 'succeeded_test-code', + 'test-event', + 1 + ); + }); +}); diff --git a/src/plugins/kibana_usage_collection/public/ebt_counters/register_ebt_counters.ts b/src/plugins/kibana_usage_collection/public/ebt_counters/register_ebt_counters.ts new file mode 100644 index 0000000000000..483e00d8d03fe --- /dev/null +++ b/src/plugins/kibana_usage_collection/public/ebt_counters/register_ebt_counters.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { AnalyticsServiceSetup } from '@kbn/core/public'; +import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; + +export function registerEbtCounters( + analytics: AnalyticsServiceSetup, + usageCollection: UsageCollectionSetup +) { + // The client should complete telemetryCounter$ when shutting down. We shouldn't need to pipe(takeUntil(stop$)). + analytics.telemetryCounter$.subscribe(({ type, source, event_type: eventType, code, count }) => { + usageCollection.reportUiCounter(`ebt_counters.${source}`, `${type}_${code}`, eventType, count); + }); +} diff --git a/src/plugins/kibana_usage_collection/public/index.ts b/src/plugins/kibana_usage_collection/public/index.ts new file mode 100644 index 0000000000000..5474b8db0b27f --- /dev/null +++ b/src/plugins/kibana_usage_collection/public/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { KibanaUsageCollectionPlugin } from './plugin'; + +export function plugin() { + return new KibanaUsageCollectionPlugin(); +} diff --git a/src/plugins/kibana_usage_collection/public/plugin.ts b/src/plugins/kibana_usage_collection/public/plugin.ts new file mode 100644 index 0000000000000..2b7a4b868b76a --- /dev/null +++ b/src/plugins/kibana_usage_collection/public/plugin.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 { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; +import type { CoreSetup, Plugin } from '@kbn/core/public'; +import { registerEbtCounters } from './ebt_counters'; + +interface KibanaUsageCollectionPluginsDepsSetup { + usageCollection: UsageCollectionSetup; +} + +export class KibanaUsageCollectionPlugin implements Plugin { + public setup(coreSetup: CoreSetup, { usageCollection }: KibanaUsageCollectionPluginsDepsSetup) { + registerEbtCounters(coreSetup.analytics, usageCollection); + } + + public start() {} + + public stop() {} +} diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/README.md b/src/plugins/kibana_usage_collection/server/collectors/application_usage/README.md index 1f7344a801227..9b2c6690626fd 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/README.md +++ b/src/plugins/kibana_usage_collection/server/collectors/application_usage/README.md @@ -120,10 +120,9 @@ This collection occurs by default for every application registered via the menti In order to keep the count of the events, this collector uses 3 Saved Objects: -1. `application_usage_transactional`: It stores each individually reported event. Grouped by `timestamp` and `appId`. The reason for having these documents instead of editing `application_usage_daily` documents on very report is to provide faster response to the requests to `/api/ui_counters/_report` (creating new documents instead of finding and editing existing ones) and to avoid conflicts when multiple users reach to the API concurrently. -2. `application_usage_daily`: Periodically, documents from `application_usage_transactional` are aggregated to daily summaries and deleted. Also grouped by `timestamp` and `appId` for the main view concatenated with `viewId` for other views. -3. `application_usage_totals`: It stores the sum of all the events older than 90 days old, grouped by `appId` for the main view concatenated with `viewId` for other views. +1. `application_usage_transactional`: It stores each individually reported event. Grouped by `timestamp` and `appId`. +2. `application_usage_totals`: It stores the sum of all the events older than 90 days old, grouped by `appId` for the main view concatenated with `viewId` for other views. -All the types use the shared fields `appId: 'keyword'`, `viewId: 'keyword'`, `numberOfClicks: 'long'` and `minutesOnScreen: 'float'`, but they are currently not added in the mappings because we don't use them for search purposes, and we need to be thoughtful with the number of mapped fields in the SavedObjects index ([#43673](https://github.com/elastic/kibana/issues/43673)). `application_usage_transactional` and `application_usage_daily` also store `timestamp: { type: 'date' }`. +All the types use the shared fields `appId: 'keyword'`, `viewId: 'keyword'`, `numberOfClicks: 'long'` and `minutesOnScreen: 'float'`, but they are currently not added in the mappings because we don't use them for search purposes, and we need to be thoughtful with the number of mapped fields in the SavedObjects index ([#43673](https://github.com/elastic/kibana/issues/43673)). The SO type `application_usage_transactional` also stores `timestamp: { type: 'date' }`. Rollups uses `appId` in the savedObject id for the default view. For other views `viewId` is concatenated. This keeps backwards compatiblity with previously stored documents on the clusters without requiring any form of migration. diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/constants.ts b/src/plugins/kibana_usage_collection/server/collectors/application_usage/constants.ts index f072f044925bf..1706ec195e577 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/constants.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/application_usage/constants.ts @@ -11,11 +11,6 @@ */ export const ROLL_TOTAL_INDICES_INTERVAL = 24 * 60 * 60 * 1000; -/** - * Roll daily indices every 24h - */ -export const ROLL_DAILY_INDICES_INTERVAL = 24 * 60 * 60 * 1000; - /** * Start rolling indices after 5 minutes up */ diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/index.ts b/src/plugins/kibana_usage_collection/server/collectors/application_usage/index.ts index 2d2d07d9d1894..676f5fddc16e1 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/index.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/application_usage/index.ts @@ -7,4 +7,3 @@ */ export { registerApplicationUsageCollector } from './telemetry_application_usage_collector'; -export { rollDailyData as migrateTransactionalDocs } from './rollups'; diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/rollups/daily.test.ts b/src/plugins/kibana_usage_collection/server/collectors/application_usage/rollups/daily.test.ts deleted file mode 100644 index 9c0fab85844bb..0000000000000 --- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/rollups/daily.test.ts +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { savedObjectsRepositoryMock, loggingSystemMock } from '@kbn/core/server/mocks'; -import { SavedObjectsErrorHelpers } from '@kbn/core/server'; -import { SAVED_OBJECTS_DAILY_TYPE, SAVED_OBJECTS_TRANSACTIONAL_TYPE } from '../saved_objects_types'; -import { rollDailyData } from './daily'; - -describe('rollDailyData', () => { - const logger = loggingSystemMock.createLogger(); - - test('returns false if no savedObjectsClient initialised yet', async () => { - await expect(rollDailyData(logger, undefined)).resolves.toBe(false); - }); - - test('handle empty results', async () => { - const savedObjectClient = savedObjectsRepositoryMock.create(); - savedObjectClient.find.mockImplementation(async ({ type, page = 1, perPage = 10 }) => { - switch (type) { - case SAVED_OBJECTS_TRANSACTIONAL_TYPE: - return { saved_objects: [], total: 0, page, per_page: perPage }; - default: - throw new Error(`Unexpected type [${type}]`); - } - }); - await expect(rollDailyData(logger, savedObjectClient)).resolves.toBe(true); - expect(savedObjectClient.get).not.toBeCalled(); - expect(savedObjectClient.bulkCreate).not.toBeCalled(); - expect(savedObjectClient.delete).not.toBeCalled(); - }); - - test('migrate some docs', async () => { - const savedObjectClient = savedObjectsRepositoryMock.create(); - let timesCalled = 0; - savedObjectClient.find.mockImplementation(async ({ type, page = 1, perPage = 10 }) => { - switch (type) { - case SAVED_OBJECTS_TRANSACTIONAL_TYPE: - if (timesCalled++ > 0) { - return { saved_objects: [], total: 0, page, per_page: perPage }; - } - return { - saved_objects: [ - { - id: 'test-id-1', - type, - score: 0, - references: [], - attributes: { - appId: 'appId', - timestamp: '2020-01-01T10:31:00.000Z', - minutesOnScreen: 0.5, - numberOfClicks: 1, - }, - }, - { - id: 'test-id-2', - type, - score: 0, - references: [], - attributes: { - appId: 'appId', - timestamp: '2020-01-01T11:31:00.000Z', - minutesOnScreen: 2.5, - numberOfClicks: 2, - }, - }, - { - id: 'test-id-3', - type, - score: 0, - references: [], - attributes: { - appId: 'appId', - viewId: 'appId_viewId', - timestamp: '2020-01-01T11:31:00.000Z', - minutesOnScreen: 1, - numberOfClicks: 5, - }, - }, - ], - total: 3, - page, - per_page: perPage, - }; - default: - throw new Error(`Unexpected type [${type}]`); - } - }); - - savedObjectClient.get.mockImplementation(async (type, id) => { - throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); - }); - - await expect(rollDailyData(logger, savedObjectClient)).resolves.toBe(true); - expect(savedObjectClient.get).toHaveBeenCalledTimes(2); - expect(savedObjectClient.get).toHaveBeenNthCalledWith( - 1, - SAVED_OBJECTS_DAILY_TYPE, - 'appId:2020-01-01' - ); - expect(savedObjectClient.get).toHaveBeenNthCalledWith( - 2, - SAVED_OBJECTS_DAILY_TYPE, - 'appId:2020-01-01:appId_viewId' - ); - expect(savedObjectClient.bulkCreate).toHaveBeenCalledTimes(1); - expect(savedObjectClient.bulkCreate).toHaveBeenCalledWith( - [ - { - type: SAVED_OBJECTS_DAILY_TYPE, - id: 'appId:2020-01-01', - attributes: { - appId: 'appId', - viewId: undefined, - timestamp: '2020-01-01T00:00:00.000Z', - minutesOnScreen: 3.0, - numberOfClicks: 3, - }, - }, - { - type: SAVED_OBJECTS_DAILY_TYPE, - id: 'appId:2020-01-01:appId_viewId', - attributes: { - appId: 'appId', - viewId: 'appId_viewId', - timestamp: '2020-01-01T00:00:00.000Z', - minutesOnScreen: 1.0, - numberOfClicks: 5, - }, - }, - ], - { overwrite: true } - ); - expect(savedObjectClient.delete).toHaveBeenCalledTimes(3); - expect(savedObjectClient.delete).toHaveBeenNthCalledWith( - 1, - SAVED_OBJECTS_TRANSACTIONAL_TYPE, - 'test-id-1' - ); - expect(savedObjectClient.delete).toHaveBeenNthCalledWith( - 2, - SAVED_OBJECTS_TRANSACTIONAL_TYPE, - 'test-id-2' - ); - expect(savedObjectClient.delete).toHaveBeenNthCalledWith( - 3, - SAVED_OBJECTS_TRANSACTIONAL_TYPE, - 'test-id-3' - ); - }); - - test('error getting the daily document', async () => { - const savedObjectClient = savedObjectsRepositoryMock.create(); - let timesCalled = 0; - savedObjectClient.find.mockImplementation(async ({ type, page = 1, perPage = 10 }) => { - switch (type) { - case SAVED_OBJECTS_TRANSACTIONAL_TYPE: - if (timesCalled++ > 0) { - return { saved_objects: [], total: 0, page, per_page: perPage }; - } - return { - saved_objects: [ - { - id: 'test-id-1', - type, - score: 0, - references: [], - attributes: { - appId: 'appId', - timestamp: '2020-01-01T10:31:00.000Z', - minutesOnScreen: 0.5, - numberOfClicks: 1, - }, - }, - ], - total: 1, - page, - per_page: perPage, - }; - default: - throw new Error(`Unexpected type [${type}]`); - } - }); - - savedObjectClient.get.mockImplementation(async (type, id) => { - throw new Error('Something went terribly wrong'); - }); - - await expect(rollDailyData(logger, savedObjectClient)).resolves.toBe(false); - expect(savedObjectClient.get).toHaveBeenCalledTimes(1); - expect(savedObjectClient.get).toHaveBeenCalledWith( - SAVED_OBJECTS_DAILY_TYPE, - 'appId:2020-01-01' - ); - expect(savedObjectClient.bulkCreate).toHaveBeenCalledTimes(0); - expect(savedObjectClient.delete).toHaveBeenCalledTimes(0); - }); -}); diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/rollups/daily.ts b/src/plugins/kibana_usage_collection/server/collectors/application_usage/rollups/daily.ts deleted file mode 100644 index 7cd326eeec346..0000000000000 --- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/rollups/daily.ts +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import moment from 'moment'; -import type { Logger } from '@kbn/logging'; -import { ISavedObjectsRepository, SavedObject, SavedObjectsErrorHelpers } from '@kbn/core/server'; -import { getDailyId } from '@kbn/usage-collection-plugin/common/application_usage'; -import { - ApplicationUsageDaily, - ApplicationUsageTransactional, - SAVED_OBJECTS_DAILY_TYPE, - SAVED_OBJECTS_TRANSACTIONAL_TYPE, -} from '../saved_objects_types'; - -/** - * For Rolling the daily data, we only care about the stored attributes and the version (to avoid overwriting via concurrent requests) - */ -type ApplicationUsageDailyWithVersion = Pick< - SavedObject, - 'version' | 'attributes' ->; - -/** - * Aggregates all the transactional events into daily aggregates - * @param logger - * @param savedObjectsClient - */ -export async function rollDailyData( - logger: Logger, - savedObjectsClient?: ISavedObjectsRepository -): Promise { - if (!savedObjectsClient) { - return false; - } - - try { - let toCreate: Map; - do { - toCreate = new Map(); - const { saved_objects: rawApplicationUsageTransactional } = - await savedObjectsClient.find({ - type: SAVED_OBJECTS_TRANSACTIONAL_TYPE, - perPage: 1000, // Process 1000 at a time as a compromise of speed and overload - }); - - for (const doc of rawApplicationUsageTransactional) { - const { - attributes: { appId, viewId, minutesOnScreen, numberOfClicks, timestamp }, - } = doc; - const dayId = moment(timestamp).format('YYYY-MM-DD'); - - const dailyId = getDailyId({ dayId, appId, viewId }); - - const existingDoc = - toCreate.get(dailyId) || - (await getDailyDoc(savedObjectsClient, dailyId, appId, viewId, dayId)); - toCreate.set(dailyId, { - ...existingDoc, - attributes: { - ...existingDoc.attributes, - minutesOnScreen: existingDoc.attributes.minutesOnScreen + minutesOnScreen, - numberOfClicks: existingDoc.attributes.numberOfClicks + numberOfClicks, - }, - }); - } - if (toCreate.size > 0) { - await savedObjectsClient.bulkCreate( - [...toCreate.entries()].map(([id, { attributes, version }]) => ({ - type: SAVED_OBJECTS_DAILY_TYPE, - id, - attributes, - version, // Providing version to ensure via conflict matching that only 1 Kibana instance (or interval) is taking care of the updates - })), - { overwrite: true } - ); - const promiseStatuses = await Promise.allSettled( - rawApplicationUsageTransactional.map( - ({ id }) => savedObjectsClient.delete(SAVED_OBJECTS_TRANSACTIONAL_TYPE, id) // There is no bulkDelete :( - ) - ); - const rejectedPromises = promiseStatuses.filter( - (settledResult): settledResult is PromiseRejectedResult => - settledResult.status === 'rejected' - ); - if (rejectedPromises.length > 0) { - throw new Error( - `Failed to delete some items in ${SAVED_OBJECTS_TRANSACTIONAL_TYPE}: ${JSON.stringify( - rejectedPromises.map(({ reason }) => reason) - )}` - ); - } - } - } while (toCreate.size > 0); - return true; - } catch (err) { - logger.debug(`Failed to rollup transactional to daily entries`); - logger.debug(err); - return false; - } -} - -/** - * Gets daily doc from the SavedObjects repository. Creates a new one if not found - * @param savedObjectsClient - * @param id The ID of the document to retrieve (typically, `${appId}:${dayId}`) - * @param appId The application ID - * @param viewId The application view ID - * @param dayId The date of the document in the format YYYY-MM-DD - */ -async function getDailyDoc( - savedObjectsClient: ISavedObjectsRepository, - id: string, - appId: string, - viewId: string, - dayId: string -): Promise { - try { - const { attributes, version } = await savedObjectsClient.get( - SAVED_OBJECTS_DAILY_TYPE, - id - ); - return { attributes, version }; - } catch (err) { - if (SavedObjectsErrorHelpers.isNotFoundError(err)) { - return { - attributes: { - appId, - viewId, - // Concatenating the day in YYYY-MM-DD form to T00:00:00Z to reduce the TZ effects - timestamp: moment(`${moment(dayId).format('YYYY-MM-DD')}T00:00:00Z`).toISOString(), - minutesOnScreen: 0, - numberOfClicks: 0, - }, - }; - } - throw err; - } -} diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/rollups/index.ts b/src/plugins/kibana_usage_collection/server/collectors/application_usage/rollups/index.ts index 8f3d83613aa9d..484036841b8f7 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/rollups/index.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/application_usage/rollups/index.ts @@ -6,6 +6,5 @@ * Side Public License, v 1. */ -export { rollDailyData } from './daily'; export { rollTotals } from './total'; export { serializeKey } from './utils'; diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts index b99a65accd072..4683565b1134c 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts @@ -166,5 +166,6 @@ export const applicationUsageSchema = { siem: commonSchema, space_selector: commonSchema, uptime: commonSchema, + synthetics: commonSchema, ux: commonSchema, }; diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/telemetry_application_usage_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/application_usage/telemetry_application_usage_collector.ts index 15856c21760ce..5a75cea43d88c 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/telemetry_application_usage_collector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/application_usage/telemetry_application_usage_collector.ts @@ -19,12 +19,8 @@ import { SAVED_OBJECTS_TOTAL_TYPE, } from './saved_objects_types'; import { applicationUsageSchema } from './schema'; -import { rollTotals, rollDailyData, serializeKey } from './rollups'; -import { - ROLL_TOTAL_INDICES_INTERVAL, - ROLL_DAILY_INDICES_INTERVAL, - ROLL_INDICES_START, -} from './constants'; +import { rollTotals, serializeKey } from './rollups'; +import { ROLL_TOTAL_INDICES_INTERVAL, ROLL_INDICES_START } from './constants'; import { ApplicationUsageTelemetryReport, ApplicationUsageViews } from './types'; export const transformByApplicationViews = ( @@ -60,17 +56,6 @@ export function registerApplicationUsageCollector( rollTotals(logger, getSavedObjectsClient()) ); - const dailyRollingSub = timer(ROLL_INDICES_START, ROLL_DAILY_INDICES_INTERVAL).subscribe( - async () => { - const success = await rollDailyData(logger, getSavedObjectsClient()); - // we only need to roll the transactional documents once to assure BWC - // once we rolling succeeds, we can stop. - if (success) { - dailyRollingSub.unsubscribe(); - } - } - ); - const collector = usageCollection.makeUsageCollector( { type: 'application_usage', diff --git a/src/plugins/kibana_usage_collection/server/collectors/index.ts b/src/plugins/kibana_usage_collection/server/collectors/index.ts index e4ed24611bfa8..6de234b5de434 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/index.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/index.ts @@ -19,11 +19,7 @@ export { registerCspCollector } from './csp'; export { registerCoreUsageCollector } from './core'; export { registerLocalizationUsageCollector } from './localization'; export { registerConfigUsageCollector } from './config_usage'; -export { - registerUiCountersUsageCollector, - registerUiCounterSavedObjectType, - registerUiCountersRollups, -} from './ui_counters'; +export { registerUiCountersUsageCollector } from './ui_counters'; export { registerUsageCountersRollups, registerUsageCountersUsageCollector, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index 7a19ff022226e..a948a035f2d48 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -418,6 +418,10 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, }, + 'observability:enableNewSyntheticsView': { + type: 'boolean', + _meta: { description: 'Non-default value of setting.' }, + }, 'observability:maxSuggestions': { type: 'integer', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index b9d50f888fa93..718f75b80a77d 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -36,6 +36,7 @@ export interface UsageStats { 'discover:maxDocFieldsDisplayed': number; 'securitySolution:rulesTableRefresh': string; 'observability:enableInspectEsQueries': boolean; + 'observability:enableNewSyntheticsView': boolean; 'observability:maxSuggestions': number; 'observability:enableComparisonByDefault': boolean; 'observability:enableInfrastructureView': boolean; diff --git a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/__fixtures__/ui_counter_saved_objects.ts b/src/plugins/kibana_usage_collection/server/collectors/ui_counters/__fixtures__/ui_counter_saved_objects.ts deleted file mode 100644 index ebc958c7be8c6..0000000000000 --- a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/__fixtures__/ui_counter_saved_objects.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { UICounterSavedObject } from '../ui_counter_saved_object_type'; -export const rawUiCounters: UICounterSavedObject[] = [ - { - type: 'ui-counter', - id: 'Kibana_home:23102020:click:different_type', - attributes: { - count: 1, - }, - references: [], - updated_at: '2020-11-24T11:27:57.067Z', - version: 'WzI5NDRd', - }, - { - type: 'ui-counter', - id: 'Kibana_home:25102020:loaded:intersecting_event', - attributes: { - count: 1, - }, - references: [], - updated_at: '2020-10-25T11:27:57.067Z', - version: 'WzI5NDRd', - }, - { - type: 'ui-counter', - id: 'Kibana_home:23102020:loaded:intersecting_event', - attributes: { - count: 3, - }, - references: [], - updated_at: '2020-10-23T11:27:57.067Z', - version: 'WzI5NDRd', - }, - { - type: 'ui-counter', - id: 'Kibana_home:24112020:click:only_reported_in_ui_counters', - attributes: { - count: 1, - }, - references: [], - updated_at: '2020-11-24T11:27:57.067Z', - version: 'WzI5NDRd', - }, -]; diff --git a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/index.ts b/src/plugins/kibana_usage_collection/server/collectors/ui_counters/index.ts index 795e4a75aa236..cc547266c618d 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/index.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/ui_counters/index.ts @@ -7,5 +7,3 @@ */ export { registerUiCountersUsageCollector } from './register_ui_counters_collector'; -export { registerUiCounterSavedObjectType } from './ui_counter_saved_object_type'; -export { registerUiCountersRollups } from './rollups'; diff --git a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/register_ui_counters_collector.test.ts b/src/plugins/kibana_usage_collection/server/collectors/ui_counters/register_ui_counters_collector.test.ts index 9d702be86aa48..0e84df3325d3d 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/register_ui_counters_collector.test.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/ui_counters/register_ui_counters_collector.test.ts @@ -6,16 +6,9 @@ * Side Public License, v 1. */ -import { - transformRawUiCounterObject, - transformRawUsageCounterObject, - createFetchUiCounters, -} from './register_ui_counters_collector'; -import { BehaviorSubject } from 'rxjs'; -import { rawUiCounters } from './__fixtures__/ui_counter_saved_objects'; +import { transformRawUsageCounterObject, fetchUiCounters } from './register_ui_counters_collector'; import { rawUsageCounters } from './__fixtures__/usage_counter_saved_objects'; import { savedObjectsClientMock } from '@kbn/core/server/mocks'; -import { UI_COUNTER_SAVED_OBJECT_TYPE } from './ui_counter_saved_object_type'; import { USAGE_COUNTERS_SAVED_OBJECT_TYPE } from '@kbn/usage-collection-plugin/server'; describe('transformRawUsageCounterObject', () => { @@ -63,84 +56,16 @@ describe('transformRawUsageCounterObject', () => { }); }); -describe('transformRawUiCounterObject', () => { - it('transforms ui counters savedObject raw entries', () => { - const result = rawUiCounters.map(transformRawUiCounterObject); - expect(result).toMatchInlineSnapshot(` - Array [ - Object { - "appName": "Kibana_home", - "counterType": "click", - "eventName": "different_type", - "fromTimestamp": "2020-11-24T00:00:00Z", - "lastUpdatedAt": "2020-11-24T11:27:57.067Z", - "total": 1, - }, - Object { - "appName": "Kibana_home", - "counterType": "loaded", - "eventName": "intersecting_event", - "fromTimestamp": "2020-10-25T00:00:00Z", - "lastUpdatedAt": "2020-10-25T11:27:57.067Z", - "total": 1, - }, - Object { - "appName": "Kibana_home", - "counterType": "loaded", - "eventName": "intersecting_event", - "fromTimestamp": "2020-10-23T00:00:00Z", - "lastUpdatedAt": "2020-10-23T11:27:57.067Z", - "total": 3, - }, - Object { - "appName": "Kibana_home", - "counterType": "click", - "eventName": "only_reported_in_ui_counters", - "fromTimestamp": "2020-11-24T00:00:00Z", - "lastUpdatedAt": "2020-11-24T11:27:57.067Z", - "total": 1, - }, - ] - `); - }); -}); - -describe('createFetchUiCounters', () => { - let stopUsingUiCounterIndicies$: BehaviorSubject; +describe('fetchUiCounters', () => { const soClientMock = savedObjectsClientMock.create(); beforeEach(() => { jest.clearAllMocks(); - stopUsingUiCounterIndicies$ = new BehaviorSubject(false); - }); - - it('does not query ui_counters saved objects if stopUsingUiCounterIndicies$ is complete', async () => { - // @ts-expect-error incomplete mock implementation - soClientMock.find.mockImplementation(async ({ type }) => { - switch (type) { - case USAGE_COUNTERS_SAVED_OBJECT_TYPE: - return { saved_objects: rawUsageCounters }; - default: - throw new Error(`unexpected type ${type}`); - } - }); - - stopUsingUiCounterIndicies$.complete(); - // @ts-expect-error incomplete mock implementation - const { dailyEvents } = await createFetchUiCounters(stopUsingUiCounterIndicies$)({ - soClient: soClientMock, - }); - - const transforemdUsageCounters = rawUsageCounters.map(transformRawUsageCounterObject); - expect(soClientMock.find).toBeCalledTimes(1); - expect(dailyEvents).toEqual(transforemdUsageCounters.filter(Boolean)); }); - it('merges saved objects from both ui_counters and usage_counters saved objects', async () => { + it('returns saved objects only from usage_counters saved objects', async () => { // @ts-expect-error incomplete mock implementation soClientMock.find.mockImplementation(async ({ type }) => { switch (type) { - case UI_COUNTER_SAVED_OBJECT_TYPE: - return { saved_objects: rawUiCounters }; case USAGE_COUNTERS_SAVED_OBJECT_TYPE: return { saved_objects: rawUsageCounters }; default: @@ -149,10 +74,10 @@ describe('createFetchUiCounters', () => { }); // @ts-expect-error incomplete mock implementation - const { dailyEvents } = await createFetchUiCounters(stopUsingUiCounterIndicies$)({ + const { dailyEvents } = await fetchUiCounters({ soClient: soClientMock, }); - expect(dailyEvents).toHaveLength(7); + expect(dailyEvents).toHaveLength(4); const intersectingEntry = dailyEvents.find( ({ eventName, fromTimestamp }) => eventName === 'intersecting_event' && fromTimestamp === '2020-10-23T00:00:00Z' @@ -179,16 +104,7 @@ describe('createFetchUiCounters', () => { expect(invalidCountEntry).toBe(undefined); expect(nonUiCountersEntry).toBe(undefined); expect(zeroCountEntry).toBe(undefined); - expect(onlyFromUICountersEntry).toMatchInlineSnapshot(` - Object { - "appName": "Kibana_home", - "counterType": "click", - "eventName": "only_reported_in_ui_counters", - "fromTimestamp": "2020-11-24T00:00:00Z", - "lastUpdatedAt": "2020-11-24T11:27:57.067Z", - "total": 1, - } - `); + expect(onlyFromUICountersEntry).toBe(undefined); expect(onlyFromUsageCountersEntry).toMatchInlineSnapshot(` Object { "appName": "myApp", @@ -206,7 +122,7 @@ describe('createFetchUiCounters', () => { "eventName": "intersecting_event", "fromTimestamp": "2020-10-23T00:00:00Z", "lastUpdatedAt": "2020-10-23T11:27:57.067Z", - "total": 63, + "total": 60, } `); }); diff --git a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/register_ui_counters_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/ui_counters/register_ui_counters_collector.ts index 5d741a6df8e3d..c2e17c24de488 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/register_ui_counters_collector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/ui_counters/register_ui_counters_collector.ts @@ -7,8 +7,6 @@ */ import moment from 'moment'; -import { mergeWith } from 'lodash'; -import type { Subject } from 'rxjs'; import { CollectorFetchContext, @@ -16,18 +14,9 @@ import { USAGE_COUNTERS_SAVED_OBJECT_TYPE, UsageCountersSavedObject, UsageCountersSavedObjectAttributes, - serializeCounterKey, } from '@kbn/usage-collection-plugin/server'; -import { - deserializeUiCounterName, - serializeUiCounterName, -} from '@kbn/usage-collection-plugin/common/ui_counters'; -import { - UICounterSavedObject, - UICounterSavedObjectAttributes, - UI_COUNTER_SAVED_OBJECT_TYPE, -} from './ui_counter_saved_object_type'; +import { deserializeUiCounterName } from '@kbn/usage-collection-plugin/common/ui_counters'; interface UiCounterEvent { appName: string; @@ -42,32 +31,6 @@ export interface UiCountersUsage { dailyEvents: UiCounterEvent[]; } -export function transformRawUiCounterObject( - rawUiCounter: UICounterSavedObject -): UiCounterEvent | undefined { - const { - id, - attributes: { count }, - updated_at: lastUpdatedAt, - } = rawUiCounter; - if (typeof count !== 'number' || count < 1) { - return; - } - - const [appName, , counterType, ...restId] = id.split(':'); - const eventName = restId.join(':'); - const fromTimestamp = moment(lastUpdatedAt).utc().startOf('day').format(); - - return { - appName, - eventName, - lastUpdatedAt, - fromTimestamp, - counterType, - total: count, - }; -} - export function transformRawUsageCounterObject( rawUsageCounter: UsageCountersSavedObject ): UiCounterEvent | undefined { @@ -93,80 +56,33 @@ export function transformRawUsageCounterObject( }; } -export const createFetchUiCounters = (stopUsingUiCounterIndicies$: Subject) => - async function fetchUiCounters({ soClient }: CollectorFetchContext) { - const { saved_objects: rawUsageCounters } = - await soClient.find({ - type: USAGE_COUNTERS_SAVED_OBJECT_TYPE, - fields: ['count', 'counterName', 'counterType', 'domainId'], - filter: `${USAGE_COUNTERS_SAVED_OBJECT_TYPE}.attributes.domainId: uiCounter`, - perPage: 10000, - }); - - const skipFetchingUiCounters = stopUsingUiCounterIndicies$.isStopped; - const result = - skipFetchingUiCounters || - (await soClient.find({ - type: UI_COUNTER_SAVED_OBJECT_TYPE, - fields: ['count'], - perPage: 10000, - })); +export async function fetchUiCounters({ soClient }: CollectorFetchContext) { + const { saved_objects: rawUsageCounters } = + await soClient.find({ + type: USAGE_COUNTERS_SAVED_OBJECT_TYPE, + fields: ['count', 'counterName', 'counterType', 'domainId'], + filter: `${USAGE_COUNTERS_SAVED_OBJECT_TYPE}.attributes.domainId: uiCounter`, + perPage: 10000, + }); - const rawUiCounters = typeof result === 'object' ? result.saved_objects : []; - const dailyEventsFromUiCounters = rawUiCounters.reduce((acc, raw) => { - try { - const event = transformRawUiCounterObject(raw); - if (event) { - const { appName, eventName, counterType } = event; - const key = serializeCounterKey({ - domainId: 'uiCounter', - counterName: serializeUiCounterName({ appName, eventName }), - counterType, - date: event.lastUpdatedAt, - }); - - acc[key] = event; - } - } catch (_) { - // swallow error; allows sending successfully transformed objects. - } - return acc; - }, {} as Record); - - const dailyEventsFromUsageCounters = rawUsageCounters.reduce((acc, raw) => { - try { - const event = transformRawUsageCounterObject(raw); - if (event) { - acc[raw.id] = event; - } - } catch (_) { - // swallow error; allows sending successfully transformed objects. - } - return acc; - }, {} as Record); - - const mergedDailyCounters = mergeWith( - dailyEventsFromUsageCounters, - dailyEventsFromUiCounters, - (value: UiCounterEvent | undefined, srcValue: UiCounterEvent): UiCounterEvent => { - if (!value) { - return srcValue; + return { + dailyEvents: Object.values( + rawUsageCounters.reduce((acc, raw) => { + try { + const event = transformRawUsageCounterObject(raw); + if (event) { + acc[raw.id] = event; + } + } catch (_) { + // swallow error; allows sending successfully transformed objects. } - - return { - ...srcValue, - total: srcValue.total + value.total, - }; - } - ); - - return { dailyEvents: Object.values(mergedDailyCounters) }; + return acc; + }, {} as Record) + ), }; +} -export function registerUiCountersUsageCollector( - usageCollection: UsageCollectionSetup, - stopUsingUiCounterIndicies$: Subject -) { +export function registerUiCountersUsageCollector(usageCollection: UsageCollectionSetup) { const collector = usageCollection.makeUsageCollector({ type: 'ui_counters', schema: { @@ -197,7 +113,7 @@ export function registerUiCountersUsageCollector( }, }, }, - fetch: createFetchUiCounters(stopUsingUiCounterIndicies$), + fetch: fetchUiCounters, isReady: () => true, }); diff --git a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/rollups/constants.ts b/src/plugins/kibana_usage_collection/server/collectors/ui_counters/rollups/constants.ts deleted file mode 100644 index 1301c4b57d380..0000000000000 --- a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/rollups/constants.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -/** - * Roll indices every 24h - */ -export const ROLL_INDICES_INTERVAL = 24 * 60 * 60 * 1000; - -/** - * Start rolling indices after 5 minutes up - */ -export const ROLL_INDICES_START = 5 * 60 * 1000; - -/** - * Number of days to keep the UI counters saved object documents - */ -export const UI_COUNTERS_KEEP_DOCS_FOR_DAYS = 3; diff --git a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/rollups/index.ts b/src/plugins/kibana_usage_collection/server/collectors/ui_counters/rollups/index.ts deleted file mode 100644 index c4ce88e1a851a..0000000000000 --- a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/rollups/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { registerUiCountersRollups } from './register_rollups'; diff --git a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/rollups/register_rollups.ts b/src/plugins/kibana_usage_collection/server/collectors/ui_counters/rollups/register_rollups.ts deleted file mode 100644 index 859a50e01401a..0000000000000 --- a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/rollups/register_rollups.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 and the Server Side Public License, v 1; you may 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 { Subject, timer } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { Logger, ISavedObjectsRepository } from '@kbn/core/server'; -import { ROLL_INDICES_INTERVAL, ROLL_INDICES_START } from './constants'; -import { rollUiCounterIndices } from './rollups'; - -export function registerUiCountersRollups( - logger: Logger, - stopRollingUiCounterIndicies$: Subject, - getSavedObjectsClient: () => ISavedObjectsRepository | undefined -) { - timer(ROLL_INDICES_START, ROLL_INDICES_INTERVAL) - .pipe(takeUntil(stopRollingUiCounterIndicies$)) - .subscribe(() => - rollUiCounterIndices(logger, stopRollingUiCounterIndicies$, getSavedObjectsClient()) - ); -} diff --git a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/rollups/rollups.test.ts b/src/plugins/kibana_usage_collection/server/collectors/ui_counters/rollups/rollups.test.ts deleted file mode 100644 index e5414ed0d5001..0000000000000 --- a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/rollups/rollups.test.ts +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import moment from 'moment'; -import * as Rx from 'rxjs'; -import { isSavedObjectOlderThan, rollUiCounterIndices } from './rollups'; -import { savedObjectsRepositoryMock, loggingSystemMock } from '@kbn/core/server/mocks'; -import { SavedObjectsFindResult } from '@kbn/core/server'; - -import { - UICounterSavedObjectAttributes, - UI_COUNTER_SAVED_OBJECT_TYPE, -} from '../ui_counter_saved_object_type'; -import { UI_COUNTERS_KEEP_DOCS_FOR_DAYS } from './constants'; - -const createMockSavedObjectDoc = (updatedAt: moment.Moment, id: string) => - ({ - id, - type: 'ui-counter', - attributes: { - count: 3, - }, - references: [], - updated_at: updatedAt.format(), - version: 'WzI5LDFd', - score: 0, - } as SavedObjectsFindResult); - -describe('isSavedObjectOlderThan', () => { - it(`returns true if doc is older than x days`, () => { - const numberOfDays = 1; - const startDate = moment().format(); - const doc = createMockSavedObjectDoc(moment().subtract(2, 'days'), 'some-id'); - const result = isSavedObjectOlderThan({ - numberOfDays, - startDate, - doc, - }); - expect(result).toBe(true); - }); - - it(`returns false if doc is exactly x days old`, () => { - const numberOfDays = 1; - const startDate = moment().format(); - const doc = createMockSavedObjectDoc(moment().subtract(1, 'days'), 'some-id'); - const result = isSavedObjectOlderThan({ - numberOfDays, - startDate, - doc, - }); - expect(result).toBe(false); - }); - - it(`returns false if doc is younger than x days`, () => { - const numberOfDays = 2; - const startDate = moment().format(); - const doc = createMockSavedObjectDoc(moment().subtract(1, 'days'), 'some-id'); - const result = isSavedObjectOlderThan({ - numberOfDays, - startDate, - doc, - }); - expect(result).toBe(false); - }); -}); - -describe('rollUiCounterIndices', () => { - let logger: ReturnType; - let savedObjectClient: ReturnType; - let stopUsingUiCounterIndicies$: Rx.Subject; - - beforeEach(() => { - logger = loggingSystemMock.createLogger(); - savedObjectClient = savedObjectsRepositoryMock.create(); - stopUsingUiCounterIndicies$ = new Rx.Subject(); - }); - - it('returns undefined if no savedObjectsClient initialised yet', async () => { - await expect( - rollUiCounterIndices(logger, stopUsingUiCounterIndicies$, undefined) - ).resolves.toBe(undefined); - expect(logger.warn).toHaveBeenCalledTimes(0); - }); - - it('does not delete any documents on empty saved objects', async () => { - savedObjectClient.find.mockImplementation(async ({ type, page = 1, perPage = 10 }) => { - switch (type) { - case UI_COUNTER_SAVED_OBJECT_TYPE: - return { saved_objects: [], total: 0, page, per_page: perPage }; - default: - throw new Error(`Unexpected type [${type}]`); - } - }); - await expect( - rollUiCounterIndices(logger, stopUsingUiCounterIndicies$, savedObjectClient) - ).resolves.toEqual([]); - expect(savedObjectClient.find).toBeCalled(); - expect(savedObjectClient.delete).not.toBeCalled(); - expect(logger.warn).toHaveBeenCalledTimes(0); - }); - it('calls Subject complete() on empty saved objects', async () => { - savedObjectClient.find.mockImplementation(async ({ type, page = 1, perPage = 10 }) => { - switch (type) { - case UI_COUNTER_SAVED_OBJECT_TYPE: - return { saved_objects: [], total: 0, page, per_page: perPage }; - default: - throw new Error(`Unexpected type [${type}]`); - } - }); - await expect( - rollUiCounterIndices(logger, stopUsingUiCounterIndicies$, savedObjectClient) - ).resolves.toEqual([]); - expect(stopUsingUiCounterIndicies$.isStopped).toBe(true); - }); - - it(`deletes documents older than ${UI_COUNTERS_KEEP_DOCS_FOR_DAYS} days`, async () => { - const mockSavedObjects = [ - createMockSavedObjectDoc(moment().subtract(5, 'days'), 'doc-id-1'), - createMockSavedObjectDoc(moment().subtract(1, 'days'), 'doc-id-2'), - createMockSavedObjectDoc(moment().subtract(6, 'days'), 'doc-id-3'), - ]; - - savedObjectClient.find.mockImplementation(async ({ type, page = 1, perPage = 10 }) => { - switch (type) { - case UI_COUNTER_SAVED_OBJECT_TYPE: - return { saved_objects: mockSavedObjects, total: 0, page, per_page: perPage }; - default: - throw new Error(`Unexpected type [${type}]`); - } - }); - await expect( - rollUiCounterIndices(logger, stopUsingUiCounterIndicies$, savedObjectClient) - ).resolves.toHaveLength(2); - expect(savedObjectClient.find).toBeCalled(); - expect(savedObjectClient.delete).toHaveBeenCalledTimes(2); - expect(savedObjectClient.delete).toHaveBeenNthCalledWith( - 1, - UI_COUNTER_SAVED_OBJECT_TYPE, - 'doc-id-1' - ); - expect(savedObjectClient.delete).toHaveBeenNthCalledWith( - 2, - UI_COUNTER_SAVED_OBJECT_TYPE, - 'doc-id-3' - ); - expect(logger.warn).toHaveBeenCalledTimes(0); - }); - - it(`logs warnings on savedObject.find failure`, async () => { - savedObjectClient.find.mockImplementation(async () => { - throw new Error(`Expected error!`); - }); - await expect( - rollUiCounterIndices(logger, stopUsingUiCounterIndicies$, savedObjectClient) - ).resolves.toEqual(undefined); - expect(savedObjectClient.find).toBeCalled(); - expect(savedObjectClient.delete).not.toBeCalled(); - expect(logger.warn).toHaveBeenCalledTimes(2); - }); - - it(`logs warnings on savedObject.delete failure`, async () => { - const mockSavedObjects = [createMockSavedObjectDoc(moment().subtract(5, 'days'), 'doc-id-1')]; - - savedObjectClient.find.mockImplementation(async ({ type, page = 1, perPage = 10 }) => { - switch (type) { - case UI_COUNTER_SAVED_OBJECT_TYPE: - return { saved_objects: mockSavedObjects, total: 0, page, per_page: perPage }; - default: - throw new Error(`Unexpected type [${type}]`); - } - }); - savedObjectClient.delete.mockImplementation(async () => { - throw new Error(`Expected error!`); - }); - await expect( - rollUiCounterIndices(logger, stopUsingUiCounterIndicies$, savedObjectClient) - ).resolves.toEqual(undefined); - expect(savedObjectClient.find).toBeCalled(); - expect(savedObjectClient.delete).toHaveBeenCalledTimes(1); - expect(savedObjectClient.delete).toHaveBeenNthCalledWith( - 1, - UI_COUNTER_SAVED_OBJECT_TYPE, - 'doc-id-1' - ); - expect(logger.warn).toHaveBeenCalledTimes(2); - }); -}); diff --git a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/rollups/rollups.ts b/src/plugins/kibana_usage_collection/server/collectors/ui_counters/rollups/rollups.ts deleted file mode 100644 index ca472fe0825f9..0000000000000 --- a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/rollups/rollups.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { ISavedObjectsRepository, Logger } from '@kbn/core/server'; -import moment from 'moment'; -import type { Subject } from 'rxjs'; - -import { UI_COUNTERS_KEEP_DOCS_FOR_DAYS } from './constants'; -import { - UICounterSavedObject, - UI_COUNTER_SAVED_OBJECT_TYPE, -} from '../ui_counter_saved_object_type'; - -export function isSavedObjectOlderThan({ - numberOfDays, - startDate, - doc, -}: { - numberOfDays: number; - startDate: moment.Moment | string | number; - doc: Pick; -}): boolean { - const { updated_at: updatedAt } = doc; - const today = moment(startDate).startOf('day'); - const updateDay = moment(updatedAt).startOf('day'); - - const diffInDays = today.diff(updateDay, 'days'); - if (diffInDays > numberOfDays) { - return true; - } - - return false; -} - -export async function rollUiCounterIndices( - logger: Logger, - stopUsingUiCounterIndicies$: Subject, - savedObjectsClient?: ISavedObjectsRepository -) { - if (!savedObjectsClient) { - return; - } - - const now = moment(); - - try { - const { saved_objects: rawUiCounterDocs } = await savedObjectsClient.find( - { - type: UI_COUNTER_SAVED_OBJECT_TYPE, - perPage: 1000, // Process 1000 at a time as a compromise of speed and overload - } - ); - - if (rawUiCounterDocs.length === 0) { - /** - * @deprecated 7.13 to be removed in 8.0.0 - * Stop triggering rollups when we've rolled up all documents. - * - * This Saved Object registry is no longer used. - * Migration from one SO registry to another is not yet supported. - * In a future release we can remove this piece of code and - * migrate any docs to the Usage Counters Saved object. - * - * @removeBy 8.0.0 - */ - - stopUsingUiCounterIndicies$.complete(); - } - - const docsToDelete = rawUiCounterDocs.filter((doc) => - isSavedObjectOlderThan({ - numberOfDays: UI_COUNTERS_KEEP_DOCS_FOR_DAYS, - startDate: now, - doc, - }) - ); - - return await Promise.all( - docsToDelete.map(({ id }) => savedObjectsClient.delete(UI_COUNTER_SAVED_OBJECT_TYPE, id)) - ); - } catch (err) { - logger.warn(`Failed to rollup UI Counters saved objects.`); - logger.warn(err); - } -} diff --git a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/ui_counter_saved_object_type.ts b/src/plugins/kibana_usage_collection/server/collectors/ui_counters/ui_counter_saved_object_type.ts deleted file mode 100644 index 2d4e680a61a2f..0000000000000 --- a/src/plugins/kibana_usage_collection/server/collectors/ui_counters/ui_counter_saved_object_type.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 { SavedObject, SavedObjectAttributes, SavedObjectsServiceSetup } from '@kbn/core/server'; - -export interface UICounterSavedObjectAttributes extends SavedObjectAttributes { - count: number; -} - -export type UICounterSavedObject = SavedObject; - -export const UI_COUNTER_SAVED_OBJECT_TYPE = 'ui-counter'; - -export function registerUiCounterSavedObjectType(savedObjectsSetup: SavedObjectsServiceSetup) { - savedObjectsSetup.registerType({ - name: UI_COUNTER_SAVED_OBJECT_TYPE, - hidden: false, - namespaceType: 'agnostic', - mappings: { - properties: { - count: { type: 'integer' }, - }, - }, - }); -} diff --git a/src/plugins/kibana_usage_collection/server/ebt_counters/README.md b/src/plugins/kibana_usage_collection/server/ebt_counters/README.md new file mode 100644 index 0000000000000..46762148a952c --- /dev/null +++ b/src/plugins/kibana_usage_collection/server/ebt_counters/README.md @@ -0,0 +1,14 @@ +# Event-based Telemetry Success Counters (server-side) + +Using the Usage Counters API, it reports the stats coming from the `core.analytics.telemetryCounters$` observable. It allows us to track the success of the EBT client on the server side. + +## Field mappings + +As the number of fields available in the Usage API is reduced, this collection merges some fields to be able to report it. + +| Usage Counter field | Telemetry Counter fields | +|---------------------|--------------------------------------------------------------------------------------------------| +| `domainId` | Concatenation of the string `'ebt_counters.'` and the `source` (`'client'` or the shipper name). | +| `counterName` | Matches the `eventType`. | +| `counterType` | Concatenation of the `type` and the `code` (i.e.: `'succeeded_200'`). | +| `total` | Matches the value in `count`. | \ No newline at end of file diff --git a/src/plugins/kibana_usage_collection/server/ebt_counters/index.ts b/src/plugins/kibana_usage_collection/server/ebt_counters/index.ts new file mode 100644 index 0000000000000..24deee4afb5d0 --- /dev/null +++ b/src/plugins/kibana_usage_collection/server/ebt_counters/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 { registerEbtCounters } from './register_ebt_counters'; diff --git a/src/plugins/kibana_usage_collection/server/ebt_counters/register_ebt_counters.test.ts b/src/plugins/kibana_usage_collection/server/ebt_counters/register_ebt_counters.test.ts new file mode 100644 index 0000000000000..ddeb85ee1be02 --- /dev/null +++ b/src/plugins/kibana_usage_collection/server/ebt_counters/register_ebt_counters.test.ts @@ -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 type { TelemetryCounter } from '@kbn/analytics-client'; +import { TelemetryCounterType } from '@kbn/analytics-client'; +import { coreMock } from '@kbn/core/server/mocks'; +import { createUsageCollectionSetupMock } from '@kbn/usage-collection-plugin/server/mocks'; +import { registerEbtCounters } from './register_ebt_counters'; + +describe('registerEbtCounters', () => { + let core: ReturnType; + let usageCollection: ReturnType; + let internalListener: (counter: TelemetryCounter) => void; + let telemetryCounter$Spy: jest.SpyInstance; + + beforeEach(() => { + core = coreMock.createSetup(); + usageCollection = createUsageCollectionSetupMock(); + telemetryCounter$Spy = jest + .spyOn(core.analytics.telemetryCounter$, 'subscribe') + .mockImplementation(((listener) => { + internalListener = listener as (counter: TelemetryCounter) => void; + }) as typeof core.analytics.telemetryCounter$['subscribe']); + }); + + test('it subscribes to `analytics.telemetryCounters$`', () => { + registerEbtCounters(core.analytics, usageCollection); + expect(telemetryCounter$Spy).toHaveBeenCalledTimes(1); + }); + + test('it creates a new usageCounter when it does not exist', () => { + registerEbtCounters(core.analytics, usageCollection); + expect(telemetryCounter$Spy).toHaveBeenCalledTimes(1); + internalListener({ + type: TelemetryCounterType.succeeded, + source: 'test-shipper', + event_type: 'test-event', + code: 'test-code', + count: 1, + }); + expect(usageCollection.getUsageCounterByType).toHaveBeenCalledTimes(1); + expect(usageCollection.getUsageCounterByType).toHaveBeenCalledWith('ebt_counters.test-shipper'); + expect(usageCollection.createUsageCounter).toHaveBeenCalledTimes(1); + expect(usageCollection.createUsageCounter).toHaveBeenCalledWith('ebt_counters.test-shipper'); + }); + + test('it reuses the usageCounter when it already exists', () => { + const incrementCounterMock = jest.fn(); + usageCollection.getUsageCounterByType.mockReturnValue({ + incrementCounter: incrementCounterMock, + }); + registerEbtCounters(core.analytics, usageCollection); + expect(telemetryCounter$Spy).toHaveBeenCalledTimes(1); + internalListener({ + type: TelemetryCounterType.succeeded, + source: 'test-shipper', + event_type: 'test-event', + code: 'test-code', + count: 1, + }); + expect(usageCollection.getUsageCounterByType).toHaveBeenCalledTimes(1); + expect(usageCollection.getUsageCounterByType).toHaveBeenCalledWith('ebt_counters.test-shipper'); + expect(usageCollection.createUsageCounter).toHaveBeenCalledTimes(0); + expect(incrementCounterMock).toHaveBeenCalledTimes(1); + expect(incrementCounterMock).toHaveBeenCalledWith({ + counterName: 'test-event', + counterType: `succeeded_test-code`, + incrementBy: 1, + }); + }); +}); diff --git a/src/plugins/kibana_usage_collection/server/ebt_counters/register_ebt_counters.ts b/src/plugins/kibana_usage_collection/server/ebt_counters/register_ebt_counters.ts new file mode 100644 index 0000000000000..ed2100dccf929 --- /dev/null +++ b/src/plugins/kibana_usage_collection/server/ebt_counters/register_ebt_counters.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 { AnalyticsServiceSetup } from '@kbn/core/server'; +import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; + +export function registerEbtCounters( + analytics: AnalyticsServiceSetup, + usageCollection: UsageCollectionSetup +) { + // The client should complete telemetryCounter$ when shutting down. We shouldn't need to pipe(takeUntil(stop$)). + analytics.telemetryCounter$.subscribe(({ type, source, event_type: eventType, code, count }) => { + // We create one counter per source ('client'|). + const domainId = `ebt_counters.${source}`; + const usageCounter = + usageCollection.getUsageCounterByType(domainId) ?? + usageCollection.createUsageCounter(domainId); + + usageCounter.incrementCounter({ + counterName: eventType, // the name of the event + counterType: `${type}_${code}`, // e.g. 'succeeded_200' + incrementBy: count, + }); + }); +} diff --git a/src/plugins/kibana_usage_collection/server/mocks.ts b/src/plugins/kibana_usage_collection/server/mocks.ts deleted file mode 100644 index 7df27a3719e92..0000000000000 --- a/src/plugins/kibana_usage_collection/server/mocks.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { cloudDetectorMock } from './collectors/cloud/detector/cloud_detector.mock'; - -const mock = cloudDetectorMock.create(); - -export const cloudDetailsMock = mock.getCloudDetails; -export const detectCloudServiceMock = mock.detectCloudService; - -jest.doMock('./collectors/cloud/detector', () => ({ - CloudDetector: jest.fn().mockImplementation(() => mock), -})); diff --git a/src/plugins/kibana_usage_collection/server/plugin.test.mocks.ts b/src/plugins/kibana_usage_collection/server/plugin.test.mocks.ts new file mode 100644 index 0000000000000..a21b2b007f5e9 --- /dev/null +++ b/src/plugins/kibana_usage_collection/server/plugin.test.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 and the Server Side Public License, v 1; you may 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 { cloudDetectorMock } from './collectors/cloud/detector/cloud_detector.mock'; + +const mock = cloudDetectorMock.create(); + +export const cloudDetailsMock = mock.getCloudDetails; +export const detectCloudServiceMock = mock.detectCloudService; + +jest.doMock('./collectors/cloud/detector', () => ({ + CloudDetector: jest.fn().mockImplementation(() => mock), +})); + +export const registerEbtCountersMock = jest.fn(); + +jest.doMock('./ebt_counters', () => ({ + registerEbtCounters: registerEbtCountersMock, +})); diff --git a/src/plugins/kibana_usage_collection/server/plugin.test.ts b/src/plugins/kibana_usage_collection/server/plugin.test.ts index a6604ac0bc1cd..ef26492c2d6fd 100644 --- a/src/plugins/kibana_usage_collection/server/plugin.test.ts +++ b/src/plugins/kibana_usage_collection/server/plugin.test.ts @@ -15,7 +15,7 @@ import { CollectorOptions, createUsageCollectionSetupMock, } from '@kbn/usage-collection-plugin/server/mocks'; -import { cloudDetailsMock } from './mocks'; +import { cloudDetailsMock, registerEbtCountersMock } from './plugin.test.mocks'; import { plugin } from '.'; describe('kibana_usage_collection', () => { @@ -44,6 +44,9 @@ describe('kibana_usage_collection', () => { expect(coreSetup.coreUsageData.registerUsageCounter).toHaveBeenCalled(); + expect(registerEbtCountersMock).toHaveBeenCalledTimes(1); + expect(registerEbtCountersMock).toHaveBeenCalledWith(coreSetup.analytics, usageCollection); + await expect( Promise.all( usageCollectors.map(async (usageCollector) => { diff --git a/src/plugins/kibana_usage_collection/server/plugin.ts b/src/plugins/kibana_usage_collection/server/plugin.ts index 4442757e2df68..10f05ccbac945 100644 --- a/src/plugins/kibana_usage_collection/server/plugin.ts +++ b/src/plugins/kibana_usage_collection/server/plugin.ts @@ -21,6 +21,7 @@ import type { CoreUsageDataStart, } from '@kbn/core/server'; import { SavedObjectsClient, EventLoopDelaysMonitor } from '@kbn/core/server'; +import { registerEbtCounters } from './ebt_counters'; import { startTrackingEventLoopDelaysUsage, startTrackingEventLoopDelaysThreshold, @@ -37,8 +38,6 @@ import { registerCoreUsageCollector, registerLocalizationUsageCollector, registerUiCountersUsageCollector, - registerUiCounterSavedObjectType, - registerUiCountersRollups, registerConfigUsageCollector, registerUsageCountersRollups, registerUsageCountersUsageCollector, @@ -70,6 +69,7 @@ export class KibanaUsageCollectionPlugin implements Plugin { } public setup(coreSetup: CoreSetup, { usageCollection }: KibanaUsageCollectionPluginsDepsSetup) { + registerEbtCounters(coreSetup.analytics, usageCollection); usageCollection.createUsageCounter('uiCounters'); this.eventLoopUsageCounter = usageCollection.createUsageCounter('eventLoop'); coreSetup.coreUsageData.registerUsageCounter(usageCollection.createUsageCounter('core')); @@ -125,9 +125,7 @@ export class KibanaUsageCollectionPlugin implements Plugin { const getUiSettingsClient = () => this.uiSettingsClient; const getCoreUsageDataService = () => this.coreUsageData!; - registerUiCounterSavedObjectType(coreSetup.savedObjects); - registerUiCountersRollups(this.logger.get('ui-counters'), pluginStop$, getSavedObjectsClient); - registerUiCountersUsageCollector(usageCollection, pluginStop$); + registerUiCountersUsageCollector(usageCollection); registerUsageCountersRollups(this.logger.get('usage-counters-rollup'), getSavedObjectsClient); registerUsageCountersUsageCollector(usageCollection); diff --git a/src/plugins/kibana_usage_collection/tsconfig.json b/src/plugins/kibana_usage_collection/tsconfig.json index e57d6e25db8cd..d9a1e648995bb 100644 --- a/src/plugins/kibana_usage_collection/tsconfig.json +++ b/src/plugins/kibana_usage_collection/tsconfig.json @@ -9,6 +9,7 @@ }, "include": [ "common/*", + "public/**/**/*", "server/**/**/*", "../../../typings/*" ], diff --git a/src/plugins/management/public/components/landing/landing.tsx b/src/plugins/management/public/components/landing/landing.tsx index 6ebf980420918..0851ba3343eee 100644 --- a/src/plugins/management/public/components/landing/landing.tsx +++ b/src/plugins/management/public/components/landing/landing.tsx @@ -46,7 +46,7 @@ export const ManagementLandingPage = ({

diff --git a/src/plugins/navigation/public/top_nav_menu/_index.scss b/src/plugins/navigation/public/top_nav_menu/_index.scss index db6cf1bc3d006..5ae2a4498b55b 100644 --- a/src/plugins/navigation/public/top_nav_menu/_index.scss +++ b/src/plugins/navigation/public/top_nav_menu/_index.scss @@ -4,6 +4,12 @@ } } +.kbnTopNavMenu__wrapper { + &--hidden { + display: none; + } +} + .kbnTopNavMenu__badgeWrapper { display: flex; align-items: baseline; diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx index 85a9803ffced6..aee35c1f331c7 100644 --- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx +++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx @@ -159,7 +159,6 @@ describe('TopNavMenu', () => { await refresh(); - expect(component.find(WRAPPER_SELECTOR).length).toBe(1); expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(1); // menu is rendered outside of the component diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx index 7eb7365ed79f3..86c83a6b48be5 100644 --- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx +++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx @@ -28,6 +28,7 @@ export type TopNavMenuProps = StatefulSearchBarProps & showFilterBar?: boolean; unifiedSearch?: UnifiedSearchPublicPluginStart; className?: string; + visible?: boolean; /** * If provided, the menu part of the component will be rendered as a portal inside the given mount point. * @@ -105,9 +106,11 @@ export function TopNavMenu(props: TopNavMenuProps): ReactElement | null { } function renderLayout() { - const { setMenuMountPoint } = props; + const { setMenuMountPoint, visible } = props; const menuClassName = classNames('kbnTopNavMenu', props.className); - const wrapperClassName = 'kbnTopNavMenu__wrapper'; + const wrapperClassName = classNames('kbnTopNavMenu__wrapper', { + 'kbnTopNavMenu__wrapper--hidden': visible === false, + }); if (setMenuMountPoint) { return ( <> @@ -117,15 +120,15 @@ export function TopNavMenu(props: TopNavMenuProps): ReactElement | null { {renderMenu(menuClassName)} - {renderSearchBar()} + {renderSearchBar()} ); } else { return ( - - {renderMenu(menuClassName)} + <> + {renderMenu(menuClassName)} {renderSearchBar()} - + ); } } diff --git a/src/plugins/newsfeed/README.md b/src/plugins/newsfeed/README.md index d8a0bffb4ed0b..398578092425d 100644 --- a/src/plugins/newsfeed/README.md +++ b/src/plugins/newsfeed/README.md @@ -1,4 +1,4 @@ # newsfeed The newsfeed plugin adds a NewsfeedNavButton to the top navigation bar and renders the content in the flyout. -Content is fetched from the remote (https://feeds.elastic.co and https://feeds-staging.elastic.co in dev mode) once a day, with periodic checks if the content needs to be refreshed. All newsfeed content is hosted remotely. +Content is fetched from the remote (https://feeds.elastic.co) once a day, with periodic checks if the content needs to be refreshed. All newsfeed content is hosted remotely. diff --git a/src/plugins/newsfeed/common/constants.ts b/src/plugins/newsfeed/common/constants.ts index 6ba5e07ea873e..f4467dfe35011 100644 --- a/src/plugins/newsfeed/common/constants.ts +++ b/src/plugins/newsfeed/common/constants.ts @@ -8,5 +8,4 @@ export const NEWSFEED_FALLBACK_LANGUAGE = 'en'; export const NEWSFEED_DEFAULT_SERVICE_BASE_URL = 'https://feeds.elastic.co'; -export const NEWSFEED_DEV_SERVICE_BASE_URL = 'https://feeds-staging.elastic.co'; export const NEWSFEED_DEFAULT_SERVICE_PATH = '/kibana/v{VERSION}.json'; diff --git a/src/plugins/newsfeed/public/lib/convert_items.test.ts b/src/plugins/newsfeed/public/lib/convert_items.test.ts index 8b599d841935c..cd2c8e4675396 100644 --- a/src/plugins/newsfeed/public/lib/convert_items.test.ts +++ b/src/plugins/newsfeed/public/lib/convert_items.test.ts @@ -34,31 +34,35 @@ const createNewsfeedItem = (parts: Partial = {}): NewsfeedItem => }); describe('localizeItem', () => { - const item = createApiItem({ - languages: ['en', 'fr'], - title: { - en: 'en title', - fr: 'fr title', - }, - description: { - en: 'en desc', - fr: 'fr desc', - }, - link_text: { - en: 'en link text', - fr: 'fr link text', - }, - link_url: { - en: 'en link url', - fr: 'fr link url', - }, - badge: { - en: 'en badge', - fr: 'fr badge', - }, - publish_on: new Date('2014-10-31T04:23:47Z'), - expire_on: new Date('2049-10-31T04:23:47Z'), - hash: 'hash', + let item: ApiItem; + + beforeEach(() => { + item = createApiItem({ + languages: ['en', 'fr'], + title: { + en: 'en title', + fr: 'fr title', + }, + description: { + en: 'en desc', + fr: 'fr desc', + }, + link_text: { + en: 'en link text', + fr: 'fr link text', + }, + link_url: { + en: 'en link url', + fr: 'fr link url', + }, + badge: { + en: 'en badge', + fr: 'fr badge', + }, + publish_on: new Date('2014-10-31T04:23:47Z'), + expire_on: new Date('2049-10-31T04:23:47Z'), + hash: 'hash', + }); }); it('converts api items to newsfeed items using the specified language', () => { @@ -82,6 +86,39 @@ describe('localizeItem', () => { hash: 'hash', }); }); + + it('uses the fallback language when `languages` is `null`', () => { + item = createApiItem({ + languages: null, + title: { + en: 'en title', + }, + description: { + en: 'en desc', + }, + link_text: { + en: 'en link text', + }, + link_url: { + en: 'en link url', + }, + badge: { + en: 'en badge', + }, + publish_on: new Date('2014-10-31T04:23:47Z'), + expire_on: new Date('2049-10-31T04:23:47Z'), + hash: 'hash', + }); + + expect(localizeItem(item, 'fr')).toMatchObject({ + title: 'en title', + description: 'en desc', + linkText: 'en link text', + linkUrl: 'en link url', + badge: 'en badge', + hash: 'hash', + }); + }); }); describe('validatePublishedDate', () => { diff --git a/src/plugins/newsfeed/public/lib/convert_items.ts b/src/plugins/newsfeed/public/lib/convert_items.ts index 38ea2cc895f3e..9235906a15fb1 100644 --- a/src/plugins/newsfeed/public/lib/convert_items.ts +++ b/src/plugins/newsfeed/public/lib/convert_items.ts @@ -42,7 +42,7 @@ export const localizeItem = (rawItem: ApiItem, userLanguage: string): NewsfeedIt } = rawItem; let chosenLanguage = userLanguage; - if (languages && !languages.includes(chosenLanguage)) { + if (!languages || !languages.includes(chosenLanguage)) { chosenLanguage = NEWSFEED_FALLBACK_LANGUAGE; // don't remove the item: fallback on a language } diff --git a/src/plugins/newsfeed/server/config.ts b/src/plugins/newsfeed/server/config.ts index f14f3452761e1..f371da244f871 100644 --- a/src/plugins/newsfeed/server/config.ts +++ b/src/plugins/newsfeed/server/config.ts @@ -10,19 +10,13 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { NEWSFEED_DEFAULT_SERVICE_PATH, NEWSFEED_DEFAULT_SERVICE_BASE_URL, - NEWSFEED_DEV_SERVICE_BASE_URL, } from '../common/constants'; export const configSchema = schema.object({ enabled: schema.boolean({ defaultValue: true }), service: schema.object({ pathTemplate: schema.string({ defaultValue: NEWSFEED_DEFAULT_SERVICE_PATH }), - urlRoot: schema.conditional( - schema.contextRef('prod'), - schema.literal(true), // Point to staging if it's not a production release - schema.string({ defaultValue: NEWSFEED_DEFAULT_SERVICE_BASE_URL }), - schema.string({ defaultValue: NEWSFEED_DEV_SERVICE_BASE_URL }) - ), + urlRoot: schema.string({ defaultValue: NEWSFEED_DEFAULT_SERVICE_BASE_URL }), }), mainInterval: schema.duration({ defaultValue: '2m' }), // (2min) How often to retry failed fetches, and/or check if newsfeed items need to be refreshed from remote fetchInterval: schema.duration({ defaultValue: '1d' }), // (1day) How often to fetch remote and reset the last fetched time diff --git a/src/plugins/presentation_util/public/__stories__/render.tsx b/src/plugins/presentation_util/public/__stories__/render.tsx index bd6463ca254d6..7e24606ca5331 100644 --- a/src/plugins/presentation_util/public/__stories__/render.tsx +++ b/src/plugins/presentation_util/public/__stories__/render.tsx @@ -13,6 +13,7 @@ import { ExpressionRenderDefinition, IInterpreterRenderHandlers } from '@kbn/exp export const defaultHandlers: IInterpreterRenderHandlers = { getRenderMode: () => 'view', isSyncColorsEnabled: () => false, + isSyncTooltipsEnabled: () => false, isInteractive: () => true, done: action('done'), onDestroy: action('onDestroy'), diff --git a/src/plugins/shared_ux/jest.config.js b/src/plugins/shared_ux/jest.config.js deleted file mode 100644 index bc8d67e5ac35b..0000000000000 --- a/src/plugins/shared_ux/jest.config.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../../..', - roots: ['/src/plugins/shared_ux'], - transform: { - '^.+\\.stories\\.tsx?$': '@storybook/addon-storyshots/injectFileName', - }, - coverageDirectory: '/target/kibana-coverage/jest/src/plugins/shared_ux', - coverageReporters: ['text', 'html'], - collectCoverageFrom: ['/src/plugins/shared_ux/{common,public,server}/**/*.{js,ts,tsx}'], -}; diff --git a/src/plugins/shared_ux/kibana.json b/src/plugins/shared_ux/kibana.json index 540514b4ab7e7..308a252f70b54 100755 --- a/src/plugins/shared_ux/kibana.json +++ b/src/plugins/shared_ux/kibana.json @@ -9,6 +9,6 @@ "description": "A plugin providing components and services for shared user experiences in Kibana.", "server": true, "ui": true, - "requiredPlugins": ["dataViewEditor"], + "requiredPlugins": ["dataViewEditor", "dataViews"], "optionalPlugins": [] } diff --git a/src/plugins/shared_ux/public/services/data.ts b/src/plugins/shared_ux/public/services/data.ts new file mode 100644 index 0000000000000..d68d35848e812 --- /dev/null +++ b/src/plugins/shared_ux/public/services/data.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 { SharedUxDataService } from '@kbn/shared-ux-services'; +import { KibanaPluginServiceFactory } from './types'; +import { SharedUXPluginStartDeps } from '../types'; + +export type DataServiceFactory = KibanaPluginServiceFactory< + SharedUxDataService, + SharedUXPluginStartDeps +>; + +/** + * A factory function for creating a Kibana-based implementation of `SharedUXDataService`. + */ +export const dataServiceFactory: DataServiceFactory = ({ coreStart, startPlugins }) => ({ + hasDataView: startPlugins.dataViews.hasData.hasDataView, + hasESData: startPlugins.dataViews.hasData.hasESData, + hasUserDataView: startPlugins.dataViews.hasData.hasUserDataView, +}); diff --git a/src/plugins/shared_ux/public/services/index.ts b/src/plugins/shared_ux/public/services/index.ts index 358a2b8035ecf..c8a572c262518 100644 --- a/src/plugins/shared_ux/public/services/index.ts +++ b/src/plugins/shared_ux/public/services/index.ts @@ -17,6 +17,7 @@ import { editorsServiceFactory } from './editors'; import { docLinksServiceFactory } from './doc_links'; import { httpServiceFactory } from './http'; import { applicationServiceFactory } from './application'; +import { dataServiceFactory } from './data'; /** * A factory function for creating a Kibana-based implementation of `SharedUXServices`. @@ -31,4 +32,5 @@ export const servicesFactory: KibanaPluginServiceFactory< docLinks: docLinksServiceFactory(params), http: httpServiceFactory(params), application: applicationServiceFactory(params), + data: dataServiceFactory(params), }); diff --git a/src/plugins/shared_ux/public/types/index.ts b/src/plugins/shared_ux/public/types/index.ts index 5a6aace584bec..e3e7cb760268f 100644 --- a/src/plugins/shared_ux/public/types/index.ts +++ b/src/plugins/shared_ux/public/types/index.ts @@ -10,6 +10,7 @@ import { SharedUxServices } from '@kbn/shared-ux-services'; import { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; +import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; /** @internal */ export interface SharedUXPluginSetup {} @@ -62,4 +63,5 @@ export interface SharedUXPluginSetupDeps {} /** @internal */ export interface SharedUXPluginStartDeps { dataViewEditor: DataViewEditorStart; + dataViews: DataViewsPublicPluginStart; } diff --git a/src/plugins/shared_ux/tsconfig.json b/src/plugins/shared_ux/tsconfig.json index 9e9844d985987..3a287353a5341 100644 --- a/src/plugins/shared_ux/tsconfig.json +++ b/src/plugins/shared_ux/tsconfig.json @@ -18,6 +18,9 @@ }, { "path": "../data_view_editor/tsconfig.json" + }, + { + "path": "../data_views/tsconfig.json" } ] } diff --git a/src/plugins/telemetry/common/constants.ts b/src/plugins/telemetry/common/constants.ts index 7b7d0a0db5057..78887c27fbcfb 100644 --- a/src/plugins/telemetry/common/constants.ts +++ b/src/plugins/telemetry/common/constants.ts @@ -12,6 +12,12 @@ */ export const REPORT_INTERVAL_MS = 86400000; +/** + * How often we poll for the opt-in status. + * Currently, 10 seconds. + */ +export const OPT_IN_POLL_INTERVAL_MS = 10000; + /** * Key for the localStorage service */ diff --git a/src/plugins/telemetry/public/plugin.test.ts b/src/plugins/telemetry/public/plugin.test.ts index 5d9e03fcbae20..f25bf92340ba6 100644 --- a/src/plugins/telemetry/public/plugin.test.ts +++ b/src/plugins/telemetry/public/plugin.test.ts @@ -6,17 +6,18 @@ * Side Public License, v 1. */ -import { TelemetryPlugin } from './plugin'; +import { ElasticV3BrowserShipper } from '@kbn/analytics-shippers-elastic-v3-browser'; import { coreMock } from '@kbn/core/public/mocks'; import { homePluginMock } from '@kbn/home-plugin/public/mocks'; import { screenshotModePluginMock } from '@kbn/screenshot-mode-plugin/public/mocks'; import { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/public'; - -let screenshotMode: ScreenshotModePluginSetup; -let home: HomePublicPluginSetup; +import { TelemetryPlugin } from './plugin'; describe('TelemetryPlugin', () => { + let screenshotMode: ScreenshotModePluginSetup; + let home: HomePublicPluginSetup; + beforeEach(() => { screenshotMode = screenshotModePluginMock.createSetupContract(); home = homePluginMock.createSetupContract(); @@ -56,5 +57,18 @@ describe('TelemetryPlugin', () => { }); }); }); + describe('EBT shipper registration', () => { + it('registers the UI telemetry shipper', () => { + const initializerContext = coreMock.createPluginInitializerContext(); + const coreSetupMock = coreMock.createSetup(); + + new TelemetryPlugin(initializerContext).setup(coreSetupMock, { screenshotMode, home }); + + expect(coreSetupMock.analytics.registerShipper).toHaveBeenCalledWith( + ElasticV3BrowserShipper, + { channelName: 'kibana-browser', version: 'version' } + ); + }); + }); }); }); diff --git a/src/plugins/telemetry/public/plugin.ts b/src/plugins/telemetry/public/plugin.ts index 133b2c6157c3c..6fe7fe4138a31 100644 --- a/src/plugins/telemetry/public/plugin.ts +++ b/src/plugins/telemetry/public/plugin.ts @@ -6,6 +6,8 @@ * Side Public License, v 1. */ +import { ElasticV3BrowserShipper } from '@kbn/analytics-shippers-elastic-v3-browser'; + import type { Plugin, CoreStart, @@ -20,7 +22,7 @@ import type { import type { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/public'; -import { HomePublicPluginSetup } from '@kbn/home-plugin/public'; +import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import { TelemetrySender, TelemetryService, TelemetryNotifications } from './services'; import type { TelemetrySavedObjectAttributes, @@ -153,6 +155,11 @@ export class TelemetryPlugin implements Plugin { await this.refreshConfig(); analytics.optIn({ global: { enabled: this.telemetryService!.isOptedIn } }); diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index ca9a57bb4462a..0c0cafad6bec6 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -202,6 +202,19 @@ } } }, + "search-session": { + "properties": { + "transientCount": { + "type": "long" + }, + "persistedCount": { + "type": "long" + }, + "totalCount": { + "type": "long" + } + } + }, "search": { "properties": { "successCount": { @@ -5878,6 +5891,137 @@ } } }, + "synthetics": { + "properties": { + "appId": { + "type": "keyword", + "_meta": { + "description": "The application being tracked" + } + }, + "viewId": { + "type": "keyword", + "_meta": { + "description": "Always `main`" + } + }, + "clicks_total": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application since we started counting them" + } + }, + "clicks_7_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application over the last 7 days" + } + }, + "clicks_30_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application over the last 30 days" + } + }, + "clicks_90_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application over the last 90 days" + } + }, + "minutes_on_screen_total": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen since we started counting them." + } + }, + "minutes_on_screen_7_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen over the last 7 days" + } + }, + "minutes_on_screen_30_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen over the last 30 days" + } + }, + "minutes_on_screen_90_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen over the last 90 days" + } + }, + "views": { + "type": "array", + "items": { + "properties": { + "appId": { + "type": "keyword", + "_meta": { + "description": "The application being tracked" + } + }, + "viewId": { + "type": "keyword", + "_meta": { + "description": "The application view being tracked" + } + }, + "clicks_total": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application sub view since we started counting them" + } + }, + "clicks_7_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the active application sub view over the last 7 days" + } + }, + "clicks_30_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the active application sub view over the last 30 days" + } + }, + "clicks_90_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the active application sub view over the last 90 days" + } + }, + "minutes_on_screen_total": { + "type": "float", + "_meta": { + "description": "Minutes the application sub view is active and on-screen since we started counting them." + } + }, + "minutes_on_screen_7_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen active application sub view over the last 7 days" + } + }, + "minutes_on_screen_30_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen active application sub view over the last 30 days" + } + }, + "minutes_on_screen_90_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen active application sub view over the last 90 days" + } + } + } + } + } + } + }, "ux": { "properties": { "appId": { @@ -7989,6 +8133,12 @@ "description": "Non-default value of setting." } }, + "observability:enableNewSyntheticsView": { + "type": "boolean", + "_meta": { + "description": "Non-default value of setting." + } + }, "observability:maxSuggestions": { "type": "integer", "_meta": { diff --git a/src/plugins/telemetry/server/plugin.test.ts b/src/plugins/telemetry/server/plugin.test.ts new file mode 100644 index 0000000000000..c31e78722abf5 --- /dev/null +++ b/src/plugins/telemetry/server/plugin.test.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ElasticV3ServerShipper } from '@kbn/analytics-shippers-elastic-v3-server'; +import { coreMock } from '@kbn/core/server/mocks'; +import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/server/mocks'; +import { telemetryCollectionManagerPluginMock } from '@kbn/telemetry-collection-manager-plugin/server/mocks'; +import { TelemetryPlugin } from './plugin'; + +describe('TelemetryPlugin', () => { + describe('setup', () => { + describe('EBT shipper registration', () => { + it('registers the Server telemetry shipper', () => { + const initializerContext = coreMock.createPluginInitializerContext(); + const coreSetupMock = coreMock.createSetup(); + + new TelemetryPlugin(initializerContext).setup(coreSetupMock, { + usageCollection: usageCollectionPluginMock.createSetupContract(), + telemetryCollectionManager: telemetryCollectionManagerPluginMock.createSetupContract(), + }); + + expect(coreSetupMock.analytics.registerShipper).toHaveBeenCalledWith( + ElasticV3ServerShipper, + { channelName: 'kibana-server', version: 'version' } + ); + }); + }); + }); +}); diff --git a/src/plugins/telemetry/server/plugin.ts b/src/plugins/telemetry/server/plugin.ts index 7c01b85130977..c3c2604dc9a03 100644 --- a/src/plugins/telemetry/server/plugin.ts +++ b/src/plugins/telemetry/server/plugin.ts @@ -8,7 +8,18 @@ import { URL } from 'url'; import type { Observable } from 'rxjs'; -import { firstValueFrom, ReplaySubject } from 'rxjs'; +import { + BehaviorSubject, + firstValueFrom, + ReplaySubject, + exhaustMap, + timer, + distinctUntilChanged, + filter, +} from 'rxjs'; + +import { ElasticV3ServerShipper } from '@kbn/analytics-shippers-elastic-v3-server'; + import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; import type { TelemetryCollectionManagerPluginSetup, @@ -33,6 +44,7 @@ import { import type { TelemetryConfigType } from './config'; import { FetcherTask } from './fetcher'; import { getTelemetrySavedObject, TelemetrySavedObject } from './telemetry_repository'; +import { OPT_IN_POLL_INTERVAL_MS } from '../common/constants'; import { getTelemetryOptIn, getTelemetryChannelEndpoint } from '../common/telemetry_config'; interface TelemetryPluginsDepsSetup { @@ -74,6 +86,7 @@ export class TelemetryPlugin implements Plugin; + private readonly isOptedIn$ = new BehaviorSubject(undefined); private readonly isDev: boolean; private readonly fetcherTask: FetcherTask; /** @@ -91,6 +104,17 @@ export class TelemetryPlugin implements Plugin(1); + /** + * Poll for the opt-in status and update the `isOptedIn$` subject. + * @private + */ + private readonly optInPollerSubscription = timer(0, OPT_IN_POLL_INTERVAL_MS) + .pipe( + exhaustMap(() => this.getOptInStatus()), + distinctUntilChanged() + ) + .subscribe((isOptedIn) => this.isOptedIn$.next(isOptedIn)); + private security?: SecurityPluginStart; constructor(initializerContext: PluginInitializerContext) { @@ -102,13 +126,29 @@ export class TelemetryPlugin implements Plugin typeof isOptedIn === 'boolean')) + .subscribe((isOptedIn) => analytics.optIn({ global: { enabled: isOptedIn } })); + const savedObjectsInternalRepository = savedObjects.createInternalRepository(); this.savedObjectsInternalRepository = savedObjectsInternalRepository; this.savedObjectsInternalClient$.next(new SavedObjectsClient(savedObjectsInternalRepository)); @@ -155,31 +200,46 @@ export class TelemetryPlugin implements Plugin { - const internalRepositoryClient = await firstValueFrom(this.savedObjectsInternalClient$); - let telemetrySavedObject: TelemetrySavedObject = false; // if an error occurs while fetching opt-in status, a `false` result indicates that Kibana cannot opt-in - try { - telemetrySavedObject = await getTelemetrySavedObject(internalRepositoryClient); - } catch (err) { - this.logger.debug('Failed to check telemetry opt-in status: ' + err.message); - } - - const config = await firstValueFrom(this.config$); - const allowChangingOptInStatus = config.allowChangingOptInStatus; - const configTelemetryOptIn = typeof config.optIn === 'undefined' ? null : config.optIn; - const currentKibanaVersion = this.currentKibanaVersion; - const isOptedIn = getTelemetryOptIn({ - currentKibanaVersion, - telemetrySavedObject, - allowChangingOptInStatus, - configTelemetryOptIn, - }); - - return isOptedIn === true; - }, + getIsOptedIn: async () => this.isOptedIn$.value === true, }; } + public stop() { + this.optInPollerSubscription.unsubscribe(); + this.isOptedIn$.complete(); + } + + private async getOptInStatus(): Promise { + const internalRepositoryClient = await firstValueFrom(this.savedObjectsInternalClient$); + + let telemetrySavedObject: TelemetrySavedObject | undefined; + try { + telemetrySavedObject = await getTelemetrySavedObject(internalRepositoryClient); + } catch (err) { + this.logger.debug('Failed to check telemetry opt-in status: ' + err.message); + } + + // If we can't get the saved object due to permissions or other error other than 404, skip this round. + if (typeof telemetrySavedObject === 'undefined' || telemetrySavedObject === false) { + return; + } + + const config = await firstValueFrom(this.config$); + const allowChangingOptInStatus = config.allowChangingOptInStatus; + const configTelemetryOptIn = typeof config.optIn === 'undefined' ? null : config.optIn; + const currentKibanaVersion = this.currentKibanaVersion; + const isOptedIn = getTelemetryOptIn({ + currentKibanaVersion, + telemetrySavedObject, + allowChangingOptInStatus, + configTelemetryOptIn, + }); + + if (typeof isOptedIn === 'boolean') { + return isOptedIn; + } + } + private startFetcher( core: CoreStart, telemetryCollectionManager: TelemetryCollectionManagerPluginStart diff --git a/src/plugins/unified_search/.storybook/main.js b/src/plugins/unified_search/.storybook/main.js new file mode 100644 index 0000000000000..8dc3c5d1518f4 --- /dev/null +++ b/src/plugins/unified_search/.storybook/main.js @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = require('@kbn/storybook').defaultConfig; diff --git a/src/plugins/unified_search/kibana.json b/src/plugins/unified_search/kibana.json index b947141a0c68a..07e438ab52174 100755 --- a/src/plugins/unified_search/kibana.json +++ b/src/plugins/unified_search/kibana.json @@ -9,7 +9,7 @@ }, "server": true, "ui": true, - "requiredPlugins": ["dataViews", "data", "uiActions"], + "requiredPlugins": ["dataViews", "data", "uiActions", "screenshotMode"], "requiredBundles": ["kibanaUtils", "kibanaReact", "data"], "serviceFolders": ["autocomplete"], "configPath": ["unifiedSearch"] diff --git a/src/plugins/unified_search/public/__stories__/search_bar.stories.tsx b/src/plugins/unified_search/public/__stories__/search_bar.stories.tsx new file mode 100644 index 0000000000000..49e25e04d01a8 --- /dev/null +++ b/src/plugins/unified_search/public/__stories__/search_bar.stories.tsx @@ -0,0 +1,431 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { action } from '@storybook/addon-actions'; +import { storiesOf } from '@storybook/react'; +import { I18nProvider } from '@kbn/i18n-react'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import type { DataView, DataViewsContract } from '@kbn/data-views-plugin/public'; +import { SearchBar, SearchBarProps } from '../search_bar'; +import { setIndexPatterns } from '../services'; + +const mockIndexPatterns = [ + { + id: '1234', + title: 'logstash-*', + fields: [ + { + name: 'response', + type: 'number', + esTypes: ['integer'], + aggregatable: true, + filterable: true, + searchable: true, + }, + ], + }, + { + id: '1235', + title: 'test-*', + fields: [ + { + name: 'response', + type: 'number', + esTypes: ['integer'], + aggregatable: true, + filterable: true, + searchable: true, + }, + ], + }, +] as DataView[]; + +const mockTimeHistory = { + get: () => { + return []; + }, + add: action('set'), + get$: () => { + return { + pipe: () => {}, + }; + }, +}; + +const createMockWebStorage = () => ({ + clear: action('clear'), + getItem: action('getItem'), + key: action('key'), + removeItem: action('removeItem'), + setItem: action('setItem'), + length: 0, +}); + +const createMockStorage = () => ({ + storage: createMockWebStorage(), + set: action('set'), + remove: action('remove'), + clear: action('clear'), + get: () => true, +}); + +const services = { + uiSettings: { + get: () => {}, + }, + savedObjects: action('savedObjects'), + notifications: action('notifications'), + http: { + basePath: { + prepend: () => 'http://test', + }, + }, + docLinks: { + links: { + query: { + kueryQuerySyntax: '', + }, + }, + }, + storage: createMockStorage(), + data: { + query: { + savedQueries: { + findSavedQueries: () => + Promise.resolve({ + queries: [ + { + id: 'testwewe', + attributes: { + title: 'Saved query 1', + description: '', + query: { + query: 'category.keyword : "Men\'s Shoes" ', + language: 'kuery', + }, + filters: [], + }, + }, + { + id: '0173d0d0-b19a-11ec-8323-837d6b231b82', + attributes: { + title: 'test', + description: '', + query: { + query: '', + language: 'kuery', + }, + filters: [ + { + meta: { + index: '1234', + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'category.keyword', + params: { + query: "Men's Accessories", + }, + }, + query: { + match_phrase: { + 'category.keyword': "Men's Accessories", + }, + }, + $state: { + store: 'appState', + }, + }, + ], + }, + }, + ], + }), + }, + }, + autocomplete: { + hasQuerySuggestions: () => Promise.resolve(false), + getQuerySuggestions: () => [], + }, + dataViews: { + getIdsWithTitle: () => [ + { id: '1234', title: 'logstash-*' }, + { id: '1235', title: 'test-*' }, + ], + }, + }, +}; + +setIndexPatterns({ + get: () => Promise.resolve(mockIndexPatterns[0]), +} as unknown as DataViewsContract); + +function wrapSearchBarInContext(testProps: SearchBarProps) { + const defaultOptions = { + appName: 'test', + timeHistory: mockTimeHistory, + intl: null as any, + showQueryBar: true, + showFilterBar: true, + showDatePicker: true, + showAutoRefreshOnly: false, + showSaveQuery: true, + showQueryInput: true, + indexPatterns: mockIndexPatterns, + dateRangeFrom: 'now-15m', + dateRangeTo: 'now', + query: { query: '', language: 'kuery' }, + filters: [], + onClearSavedQuery: action('onClearSavedQuery'), + onFiltersUpdated: action('onFiltersUpdated'), + } as unknown as SearchBarProps; + + return ( + + + + + + ); +} + +storiesOf('SearchBar', module) + .add('default', () => wrapSearchBarInContext({ showQueryInput: true } as SearchBarProps)) + .add('with dataviewPicker', () => + wrapSearchBarInContext({ + dataViewPickerComponentProps: { + currentDataViewId: '1234', + trigger: { + 'data-test-subj': 'dataView-switch-link', + label: 'logstash-*', + title: 'logstash-*', + }, + onChangeDataView: action('onChangeDataView'), + }, + } as SearchBarProps) + ) + .add('with dataviewPicker enhanced', () => + wrapSearchBarInContext({ + dataViewPickerComponentProps: { + currentDataViewId: '1234', + trigger: { + 'data-test-subj': 'dataView-switch-link', + label: 'logstash-*', + title: 'logstash-*', + }, + onChangeDataView: action('onChangeDataView'), + onAddField: action('onAddField'), + onDataViewCreated: action('onDataViewCreated'), + }, + } as SearchBarProps) + ) + .add('with filterBar off', () => + wrapSearchBarInContext({ + showFilterBar: false, + } as SearchBarProps) + ) + .add('with query input off', () => + wrapSearchBarInContext({ + showQueryInput: false, + } as SearchBarProps) + ) + .add('with date picker off', () => + wrapSearchBarInContext({ + showDatePicker: false, + } as SearchBarProps) + ) + .add('with date picker off', () => + wrapSearchBarInContext({ + showDatePicker: false, + } as SearchBarProps) + ) + .add('with only the date picker on', () => + wrapSearchBarInContext({ + showDatePicker: true, + showFilterBar: false, + showQueryInput: false, + } as SearchBarProps) + ) + .add('with only the filter bar on', () => + wrapSearchBarInContext({ + showDatePicker: false, + showFilterBar: true, + showQueryInput: false, + filters: [ + { + meta: { + index: '1234', + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'category.keyword', + params: { + query: "Men's Accessories", + }, + }, + query: { + match_phrase: { + 'category.keyword': "Men's Accessories", + }, + }, + $state: { + store: 'appState', + }, + }, + ], + } as unknown as SearchBarProps) + ) + .add('with only the query bar on', () => + wrapSearchBarInContext({ + showDatePicker: false, + showFilterBar: false, + showQueryInput: true, + query: { query: 'Test: miaou', language: 'kuery' }, + } as unknown as SearchBarProps) + ) + .add('with only the filter bar and the date picker on', () => + wrapSearchBarInContext({ + showDatePicker: true, + showFilterBar: true, + showQueryInput: false, + filters: [ + { + meta: { + index: '1234', + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'category.keyword', + params: { + query: "Men's Accessories", + }, + }, + query: { + match_phrase: { + 'category.keyword': "Men's Accessories", + }, + }, + $state: { + store: 'appState', + }, + }, + ], + } as unknown as SearchBarProps) + ) + .add('with loaded saved query without changes', () => + wrapSearchBarInContext({ + savedQuery: { + id: '0173d0d0-b19a-11ec-8323-837d6b231b82', + attributes: { + title: 'test', + description: '', + query: { + query: '', + language: 'kuery', + }, + filters: [ + { + meta: { + index: '1234', + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'category.keyword', + params: { + query: "Men's Accessories", + }, + }, + query: { + match_phrase: { + 'category.keyword': "Men's Accessories", + }, + }, + $state: { + store: 'appState', + }, + }, + ], + }, + }, + filters: [ + { + meta: { + index: '1234', + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'category.keyword', + params: { + query: "Men's Accessories", + }, + }, + query: { + match_phrase: { + 'category.keyword': "Men's Accessories", + }, + }, + $state: { + store: 'appState', + }, + }, + ], + } as unknown as SearchBarProps) + ) + .add('with loaded saved query with changes', () => + wrapSearchBarInContext({ + savedQuery: { + id: '0173d0d0-b19a-11ec-8323-837d6b231b82', + attributes: { + title: 'test', + description: '', + query: { + query: '', + language: 'kuery', + }, + filters: [ + { + meta: { + index: '1234', + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'category.keyword', + params: { + query: "Men's Accessories", + }, + }, + query: { + match_phrase: { + 'category.keyword': "Men's Accessories", + }, + }, + $state: { + store: 'appState', + }, + }, + ], + }, + }, + } as unknown as SearchBarProps) + ) + .add('show only query bar without submit', () => + wrapSearchBarInContext({ + showDatePicker: false, + showFilterBar: false, + showAutoRefreshOnly: false, + showQueryInput: true, + showSubmitButton: false, + } as SearchBarProps) + ); diff --git a/src/plugins/unified_search/public/apply_filters/apply_filter_popover_content.tsx b/src/plugins/unified_search/public/apply_filters/apply_filter_popover_content.tsx index 25d6f27bc51f1..9017fbf40ee2f 100644 --- a/src/plugins/unified_search/public/apply_filters/apply_filter_popover_content.tsx +++ b/src/plugins/unified_search/public/apply_filters/apply_filter_popover_content.tsx @@ -19,7 +19,11 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { Component } from 'react'; -import { getDisplayValueFromFilter, mapAndFlattenFilters } from '@kbn/data-plugin/public'; +import { + getDisplayValueFromFilter, + mapAndFlattenFilters, + getFieldDisplayValueFromFilter, +} from '@kbn/data-plugin/public'; import { Filter } from '@kbn/data-plugin/common'; import { DataView } from '@kbn/data-views-plugin/public'; import { FilterLabel } from '../filter_bar'; @@ -33,6 +37,7 @@ interface Props { interface State { isFilterSelected: boolean[]; + fieldLabel?: string; } // Needed for React.lazy @@ -46,12 +51,15 @@ export default class ApplyFiltersPopoverContent extends Component super(props); this.state = { isFilterSelected: props.filters.map(() => true), + fieldLabel: undefined, }; } - private getLabel(filter: Filter) { + + private getLabel = (filter: Filter) => { const valueLabel = getDisplayValueFromFilter(filter, this.props.indexPatterns); - return ; - } + const fieldLabel = getFieldDisplayValueFromFilter(filter, this.props.indexPatterns); + return ; + }; public render() { if (this.props.filters.length === 0) { diff --git a/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx index 139405f6af9f4..723b7e6896229 100644 --- a/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx +++ b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx @@ -8,7 +8,6 @@ import { IFieldType, indexPatterns as indexPatternsUtils } from '@kbn/data-plugin/public'; import { flatten } from 'lodash'; -import { escapeKuery } from './lib/escape_kuery'; import { sortPrefixFirst } from './sort_prefix_first'; import { QuerySuggestionField, QuerySuggestionTypes } from '../query_suggestion_provider'; import { KqlQuerySuggestionProvider } from './types'; @@ -27,7 +26,7 @@ const keywordComparator = (first: IFieldType, second: IFieldType) => { export const setupGetFieldSuggestions: KqlQuerySuggestionProvider = ( core ) => { - return ({ indexPatterns }, { start, end, prefix, suffix, nestedPath = '' }) => { + return async ({ indexPatterns }, { start, end, prefix, suffix, nestedPath = '' }) => { const allFields = flatten( indexPatterns.map((indexPattern) => { return indexPattern.fields.filter(indexPatternsUtils.isFilterable); @@ -42,7 +41,7 @@ export const setupGetFieldSuggestions: KqlQuerySuggestionProvider { const remainingPath = field.subType && field.subType.nested diff --git a/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/index.ts b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/index.ts index 1002863fec7f4..cd022ec371e65 100644 --- a/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/index.ts +++ b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/index.ts @@ -9,7 +9,6 @@ import { CoreSetup } from '@kbn/core/public'; import { $Keys } from 'utility-types'; import { flatten, uniqBy } from 'lodash'; -import { fromKueryExpression } from '@kbn/es-query'; import { setupGetFieldSuggestions } from './field'; import { setupGetValueSuggestions } from './value'; import { setupGetOperatorSuggestions } from './operator'; @@ -38,11 +37,12 @@ export const setupKqlQuerySuggestionProvider = ( conjunction: setupGetConjunctionSuggestions(core), }; - const getSuggestionsByType = ( + const getSuggestionsByType = async ( cursoredQuery: string, querySuggestionsArgs: QuerySuggestionGetFnArgs - ): Array> | [] => { + ): Promise> | []> => { try { + const { fromKueryExpression } = await import('@kbn/es-query'); const cursorNode = fromKueryExpression(cursoredQuery, { cursorSymbol, parseCursor: true, @@ -56,13 +56,13 @@ export const setupKqlQuerySuggestionProvider = ( } }; - return (querySuggestionsArgs) => { + return async (querySuggestionsArgs): Promise => { const { query, selectionStart, selectionEnd } = querySuggestionsArgs; const cursoredQuery = `${query.substr(0, selectionStart)}${cursorSymbol}${query.substr( selectionEnd )}`; - return Promise.all(getSuggestionsByType(cursoredQuery, querySuggestionsArgs)).then( + return Promise.all(await getSuggestionsByType(cursoredQuery, querySuggestionsArgs)).then( (suggestionsByType) => dedup(flatten(suggestionsByType)) ); }; diff --git a/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.ts b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.ts index 6636f9b602687..20a20797c15e2 100644 --- a/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.ts +++ b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.ts @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -import { escapeKuery } from '@kbn/es-query'; - /** * Escapes backslashes and double-quotes. (Useful when putting a string in quotes to use as a value * in a KQL expression. See the QuotedCharacter rule in kuery.peg.) @@ -15,6 +13,3 @@ import { escapeKuery } from '@kbn/es-query'; export function escapeQuotes(str: string) { return str.replace(/[\\"]/g, '\\$&'); } - -// Re-export this function from the @kbn/es-query package to avoid refactoring -export { escapeKuery }; diff --git a/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/types.ts b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/types.ts index e9ca34e546f0b..d7b8b3315fafd 100644 --- a/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/types.ts +++ b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/types.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ -import { CoreSetup } from '@kbn/core/public'; -import { KueryNode } from '@kbn/es-query'; +import type { KueryNode } from '@kbn/es-query'; +import type { CoreSetup } from '@kbn/core/public'; import type { UnifiedSearchPublicPluginStart } from '../../../types'; -import { QuerySuggestionBasic, QuerySuggestionGetFnArgs } from '../query_suggestion_provider'; +import type { QuerySuggestionBasic, QuerySuggestionGetFnArgs } from '../query_suggestion_provider'; export type KqlQuerySuggestionProvider = ( core: CoreSetup diff --git a/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts b/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts index 054a243064329..2c25fe0230501 100644 --- a/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts +++ b/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts @@ -8,7 +8,6 @@ import { CoreSetup } from '@kbn/core/public'; import dateMath from '@kbn/datemath'; -import { buildQueryFromFilters } from '@kbn/es-query'; import { memoize } from 'lodash'; import { IIndexPattern, @@ -124,6 +123,7 @@ export const setupValueSuggestionProvider = ( const timeFilter = useTimeRange ? getAutocompleteTimefilter(timefilter, indexPattern) : undefined; + const { buildQueryFromFilters } = await import('@kbn/es-query'); const filterQuery = timeFilter ? buildQueryFromFilters([timeFilter], indexPattern).filter : []; const filters = [...(boolFilter ? boolFilter : []), ...filterQuery]; try { diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.styles.ts b/src/plugins/unified_search/public/dataview_picker/change_dataview.styles.ts new file mode 100644 index 0000000000000..1c505752d392c --- /dev/null +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.styles.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const DATA_VIEW_POPOVER_CONTENT_WIDTH = 280; + +export const changeDataViewStyles = ({ fullWidth }: { fullWidth?: boolean }) => { + return { + trigger: { + maxWidth: fullWidth ? undefined : DATA_VIEW_POPOVER_CONTENT_WIDTH, + }, + popoverContent: { + width: DATA_VIEW_POPOVER_CONTENT_WIDTH, + }, + }; +}; diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx new file mode 100644 index 0000000000000..d3081561a0c4e --- /dev/null +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx @@ -0,0 +1,138 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { I18nProvider } from '@kbn/i18n-react'; +import { act } from 'react-dom/test-utils'; +import { mountWithIntl as mount } from '@kbn/test-jest-helpers'; +import { findTestSubject } from '@elastic/eui/lib/test'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { ChangeDataView } from './change_dataview'; +import { EuiTourStep } from '@elastic/eui'; +import type { DataViewPickerProps } from '.'; + +describe('DataView component', () => { + const createMockWebStorage = () => ({ + clear: jest.fn(), + getItem: jest.fn(), + key: jest.fn(), + removeItem: jest.fn(), + setItem: jest.fn(), + length: 0, + }); + + const createMockStorage = () => ({ + storage: createMockWebStorage(), + get: jest.fn(), + set: jest.fn(), + remove: jest.fn(), + clear: jest.fn(), + }); + const getStorage = (v: boolean) => { + const storage = createMockStorage(); + storage.get.mockReturnValue(v); + return storage; + }; + + function wrapDataViewComponentInContext(testProps: DataViewPickerProps, storageValue: boolean) { + let dataMock = dataPluginMock.createStartContract(); + dataMock = { + ...dataMock, + dataViews: { + ...dataMock.dataViews, + getIdsWithTitle: jest.fn(), + }, + }; + const services = { + data: dataMock, + storage: getStorage(storageValue), + }; + + return ( + + + + + + ); + } + let props: DataViewPickerProps; + beforeEach(() => { + props = { + currentDataViewId: 'dataview-1', + trigger: { + label: 'Dataview 1', + title: 'Dataview 1', + fullWidth: true, + 'data-test-subj': 'dataview-trigger', + }, + onChangeDataView: jest.fn(), + }; + }); + it('should not render the tour component by default', async () => { + await act(async () => { + const component = mount(wrapDataViewComponentInContext(props, true)); + expect(component.find(EuiTourStep).prop('isStepOpen')).toBe(false); + }); + }); + it('should render the tour component if the showNewMenuTour is true', async () => { + const component = mount( + wrapDataViewComponentInContext({ ...props, showNewMenuTour: true }, false) + ); + expect(component.find(EuiTourStep).prop('isStepOpen')).toBe(true); + }); + + it('should not render the add runtime field menu if addField is not given', async () => { + await act(async () => { + const component = mount(wrapDataViewComponentInContext(props, true)); + findTestSubject(component, 'dataview-trigger').simulate('click'); + expect(component.find('[data-test-subj="indexPattern-add-field"]').length).toBe(0); + }); + }); + + it('should render the add runtime field menu if addField is given', async () => { + const addFieldSpy = jest.fn(); + const component = mount( + wrapDataViewComponentInContext( + { ...props, onAddField: addFieldSpy, showNewMenuTour: true }, + false + ) + ); + findTestSubject(component, 'dataview-trigger').simulate('click'); + expect(component.find('[data-test-subj="indexPattern-add-field"]').at(0).text()).toContain( + 'Add a field to this data view' + ); + component.find('[data-test-subj="indexPattern-add-field"]').first().simulate('click'); + expect(addFieldSpy).toHaveBeenCalled(); + }); + + it('should not render the add datavuew menu if onDataViewCreated is not given', async () => { + await act(async () => { + const component = mount(wrapDataViewComponentInContext(props, true)); + findTestSubject(component, 'dataview-trigger').simulate('click'); + expect(component.find('[data-test-subj="idataview-create-new"]').length).toBe(0); + }); + }); + + it('should render the add datavuew menu if onDataViewCreated is given', async () => { + const addDataViewSpy = jest.fn(); + const component = mount( + wrapDataViewComponentInContext( + { ...props, onDataViewCreated: addDataViewSpy, showNewMenuTour: true }, + false + ) + ); + findTestSubject(component, 'dataview-trigger').simulate('click'); + expect(component.find('[data-test-subj="dataview-create-new"]').at(0).text()).toContain( + 'Create a data view' + ); + component.find('[data-test-subj="dataview-create-new"]').first().simulate('click'); + expect(addDataViewSpy).toHaveBeenCalled(); + }); +}); diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx new file mode 100644 index 0000000000000..3e0ed7cc8a266 --- /dev/null +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -0,0 +1,244 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import React, { useState, useEffect } from 'react'; +import { css } from '@emotion/react'; +import { + EuiPopover, + EuiHorizontalRule, + EuiButton, + EuiContextMenuPanel, + EuiContextMenuItem, + useEuiTheme, + useGeneratedHtmlId, + EuiIcon, + EuiLink, + EuiText, + EuiTourStep, + EuiContextMenuPanelProps, +} from '@elastic/eui'; +import type { DataViewListItem } from '@kbn/data-views-plugin/public'; +import { IDataPluginServices } from '@kbn/data-plugin/public'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import type { DataViewPickerProps } from '.'; +import { DataViewsList } from './dataview_list'; +import { changeDataViewStyles } from './change_dataview.styles'; + +const NEW_DATA_VIEW_MENU_STORAGE_KEY = 'data.newDataViewMenu'; + +const newMenuTourTitle = i18n.translate('unifiedSearch.query.dataViewMenu.newMenuTour.title', { + defaultMessage: 'A better data view menu', +}); + +const newMenuTourDescription = i18n.translate( + 'unifiedSearch.query.dataViewMenu.newMenuTour.description', + { + defaultMessage: + 'This menu now offers all the tools you need to create, find, and edit your data views.', + } +); + +const newMenuTourDismissLabel = i18n.translate( + 'unifiedSearch.query.dataViewMenu.newMenuTour.dismissLabel', + { + defaultMessage: 'Got it', + } +); + +export function ChangeDataView({ + isMissingCurrent, + currentDataViewId, + onChangeDataView, + onAddField, + onDataViewCreated, + trigger, + selectableProps, + showNewMenuTour = false, +}: DataViewPickerProps) { + const { euiTheme } = useEuiTheme(); + const [isPopoverOpen, setPopoverIsOpen] = useState(false); + const [dataViewsList, setDataViewsList] = useState([]); + const [triggerLabel, setTriggerLabel] = useState(''); + const kibana = useKibana(); + const { application, data, storage } = kibana.services; + const styles = changeDataViewStyles({ fullWidth: trigger.fullWidth }); + + const [isTourDismissed, setIsTourDismissed] = useState(() => + Boolean(storage.get(NEW_DATA_VIEW_MENU_STORAGE_KEY)) + ); + const [isTourOpen, setIsTourOpen] = useState(false); + + useEffect(() => { + if (showNewMenuTour && !isTourDismissed) { + setIsTourOpen(true); + } + }, [isTourDismissed, setIsTourOpen, showNewMenuTour]); + + const onTourDismiss = () => { + storage.set(NEW_DATA_VIEW_MENU_STORAGE_KEY, true); + setIsTourDismissed(true); + setIsTourOpen(false); + }; + + // Create a reusable id to ensure search input is the first focused item in the popover even though it's not the first item + const searchListInputId = useGeneratedHtmlId({ prefix: 'dataviewPickerListSearchInput' }); + + useEffect(() => { + const fetchDataViews = async () => { + const dataViewsRefs = await data.dataViews.getIdsWithTitle(); + setDataViewsList(dataViewsRefs); + }; + fetchDataViews(); + }, [data, currentDataViewId]); + + useEffect(() => { + if (trigger.label) { + setTriggerLabel(trigger.label); + } + }, [trigger.label]); + + const createTrigger = function () { + const { label, title, 'data-test-subj': dataTestSubj, fullWidth, ...rest } = trigger; + return ( + { + setPopoverIsOpen(!isPopoverOpen); + setIsTourOpen(false); + // onTourDismiss(); TODO: Decide if opening the menu should also dismiss the tour + }} + color={isMissingCurrent ? 'danger' : 'primary'} + iconSide="right" + iconType="arrowDown" + title={title} + fullWidth={fullWidth} + {...rest} + > + {triggerLabel} + + ); + }; + + const getPanelItems = () => { + const panelItems: EuiContextMenuPanelProps['items'] = []; + if (onAddField) { + panelItems.push( + { + setPopoverIsOpen(false); + onAddField(); + }} + > + {i18n.translate('unifiedSearch.query.queryBar.indexPattern.addFieldButton', { + defaultMessage: 'Add a field to this data view', + })} + , + { + setPopoverIsOpen(false); + application.navigateToApp('management', { + path: `/kibana/indexPatterns/patterns/${currentDataViewId}`, + }); + }} + > + {i18n.translate('unifiedSearch.query.queryBar.indexPattern.manageFieldButton', { + defaultMessage: 'Manage this data view', + })} + , + + ); + } + panelItems.push( + { + onChangeDataView(newId); + setPopoverIsOpen(false); + }} + currentDataViewId={currentDataViewId} + selectableProps={selectableProps} + searchListInputId={searchListInputId} + /> + ); + + if (onDataViewCreated) { + panelItems.push( + , + { + setPopoverIsOpen(false); + onDataViewCreated(); + }} + > + {i18n.translate('unifiedSearch.query.queryBar.indexPattern.addNewDataView', { + defaultMessage: 'Create a data view', + })} + + ); + } + + return panelItems; + }; + + return ( + +   {newMenuTourTitle} + + } + content={ + +

{newMenuTourDescription}

+
+ } + isStepOpen={isTourOpen} + onFinish={onTourDismiss} + step={1} + stepsTotal={1} + footerAction={ + + {newMenuTourDismissLabel} + + } + repositionOnScroll + display="block" + > + setPopoverIsOpen(false)} + panelPaddingSize="none" + initialFocus={`#${searchListInputId}`} + display="block" + buffer={8} + > +
+ +
+
+
+ ); +} diff --git a/src/plugins/unified_search/public/dataview_picker/dataview_list.test.tsx b/src/plugins/unified_search/public/dataview_picker/dataview_list.test.tsx new file mode 100644 index 0000000000000..813beae20369c --- /dev/null +++ b/src/plugins/unified_search/public/dataview_picker/dataview_list.test.tsx @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { EuiSelectable } from '@elastic/eui'; +import { act } from 'react-dom/test-utils'; +import { ShallowWrapper } from 'enzyme'; +import { shallowWithIntl as shallow } from '@kbn/test-jest-helpers'; +import { DataViewsList, DataViewsListProps } from './dataview_list'; + +function getDataViewPickerList(instance: ShallowWrapper) { + return instance.find(EuiSelectable).first(); +} + +function getDataViewPickerOptions(instance: ShallowWrapper) { + return getDataViewPickerList(instance).prop('options'); +} + +function selectDataViewPickerOption(instance: ShallowWrapper, selectedLabel: string) { + const options: Array<{ label: string; checked?: 'on' | 'off' }> = getDataViewPickerOptions( + instance + ).map((option: { label: string }) => + option.label === selectedLabel + ? { ...option, checked: 'on' } + : { ...option, checked: undefined } + ); + return getDataViewPickerList(instance).prop('onChange')!(options); +} + +describe('DataView list component', () => { + const list = [ + { + id: 'dataview-1', + title: 'dataview-1', + }, + { + id: 'dataview-2', + title: 'dataview-2', + }, + ]; + const changeDataViewSpy = jest.fn(); + let props: DataViewsListProps; + beforeEach(() => { + props = { + currentDataViewId: 'dataview-1', + onChangeDataView: changeDataViewSpy, + dataViewsList: list, + }; + }); + it('should trigger the onChangeDataView if a new dataview is selected', async () => { + const component = shallow(); + await act(async () => { + selectDataViewPickerOption(component, 'dataview-2'); + }); + expect(changeDataViewSpy).toHaveBeenCalled(); + }); + + it('should list all dataviiew', () => { + const component = shallow(); + + expect(getDataViewPickerOptions(component)!.map((option: any) => option.label)).toEqual([ + 'dataview-1', + 'dataview-2', + ]); + }); +}); diff --git a/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx b/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx new file mode 100644 index 0000000000000..153cbdd3cf3f2 --- /dev/null +++ b/src/plugins/unified_search/public/dataview_picker/dataview_list.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 from 'react'; +import { EuiSelectable, EuiSelectableProps, EuiPanel } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; +import { DataViewListItem } from '@kbn/data-views-plugin/public'; + +export interface DataViewsListProps { + dataViewsList: DataViewListItem[]; + onChangeDataView: (newId: string) => void; + currentDataViewId?: string; + selectableProps?: EuiSelectableProps; + searchListInputId?: string; +} + +export function DataViewsList({ + dataViewsList, + onChangeDataView, + currentDataViewId, + selectableProps, + searchListInputId, +}: DataViewsListProps) { + return ( + + {...selectableProps} + data-test-subj="indexPattern-switcher" + searchable + singleSelection="always" + options={dataViewsList?.map(({ title, id }) => ({ + key: id, + label: title, + value: id, + checked: id === currentDataViewId ? 'on' : undefined, + }))} + onChange={(choices) => { + const choice = choices.find(({ checked }) => checked) as unknown as { + value: string; + }; + onChangeDataView(choice.value); + }} + searchProps={{ + id: searchListInputId, + compressed: true, + placeholder: i18n.translate('unifiedSearch.query.queryBar.indexPattern.findDataView', { + defaultMessage: 'Find a data view', + }), + ...(selectableProps ? selectableProps.searchProps : undefined), + }} + > + {(list, search) => ( + + {search} + {list} + + )} + + ); +} diff --git a/src/plugins/unified_search/public/dataview_picker/index.tsx b/src/plugins/unified_search/public/dataview_picker/index.tsx new file mode 100644 index 0000000000000..bd24aef0498ef --- /dev/null +++ b/src/plugins/unified_search/public/dataview_picker/index.tsx @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { EuiButtonProps, EuiSelectableProps } from '@elastic/eui'; +import { ChangeDataView } from './change_dataview'; + +export type ChangeDataViewTriggerProps = EuiButtonProps & { + label: string; + title?: string; +}; + +/** @public */ +export interface DataViewPickerProps { + trigger: ChangeDataViewTriggerProps; + isMissingCurrent?: boolean; + onChangeDataView: (newId: string) => void; + currentDataViewId?: string; + selectableProps?: EuiSelectableProps; + onAddField?: () => void; + onDataViewCreated?: () => void; + showNewMenuTour?: boolean; +} + +export const DataViewPicker = ({ + isMissingCurrent, + currentDataViewId, + onChangeDataView, + onAddField, + onDataViewCreated, + trigger, + selectableProps, + showNewMenuTour, +}: DataViewPickerProps) => { + return ( + + ); +}; diff --git a/src/plugins/unified_search/public/filter_bar/_global_filter_group.scss b/src/plugins/unified_search/public/filter_bar/_global_filter_group.scss deleted file mode 100644 index 24f3ca05a5685..0000000000000 --- a/src/plugins/unified_search/public/filter_bar/_global_filter_group.scss +++ /dev/null @@ -1,54 +0,0 @@ -// SASSTODO: Probably not the right file for this selector, but temporary until the files get re-organized -.globalQueryBar { - padding: 0 $euiSizeS $euiSizeS $euiSizeS; -} - -.globalQueryBar:first-child { - padding-top: $euiSizeS; -} - -.globalQueryBar:not(:empty) { - padding-bottom: $euiSizeS; -} - -.globalQueryBar--inPage { - padding: 0; -} - -.globalFilterGroup__filterBar { - margin-top: $euiSizeXS; -} - -.globalFilterBar__addButton { - min-height: $euiSizeL + $euiSizeXS; // same height as the badges -} - -// sass-lint:disable quotes -.globalFilterGroup__branch { - padding: $euiSizeS $euiSizeM 0 0; - background-repeat: no-repeat; - background-position: $euiSizeM ($euiSizeS * -1); - background-image: url("data:image/svg+xml,%0A%3Csvg width='28px' height='28px' viewBox='0 0 28 28' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='#{hexToRGB($euiColorLightShade)}'%3E%3Crect x='14' y='27' width='14' height='1'%3E%3C/rect%3E%3C/g%3E%3C/svg%3E"); -} - -.globalFilterGroup__wrapper { - line-height: 1; // Override kuiLocalNav & kuiLocalNavRow - overflow: hidden; - transition: height $euiAnimSpeedNormal $euiAnimSlightResistance; -} - -.globalFilterGroup__filterFlexItem { - overflow: hidden; - padding-bottom: 2px; // Allow the shadows of the pills to show -} - -.globalFilterBar__flexItem { - max-width: calc(100% - #{$euiSizeXS}); // Width minus margin around each flex itm -} - -@include euiBreakpoint('xs', 's') { - .globalFilterGroup__wrapper-isVisible { - // EUI Flexbox adds too much margin between responded items, this just moves it up - margin-top: $euiSize * -1; - } -} diff --git a/src/plugins/unified_search/public/filter_bar/_global_filter_item.scss b/src/plugins/unified_search/public/filter_bar/_global_filter_item.scss deleted file mode 100644 index 1c9cea7291770..0000000000000 --- a/src/plugins/unified_search/public/filter_bar/_global_filter_item.scss +++ /dev/null @@ -1,95 +0,0 @@ -/** - * 1. Allow wrapping of long filter items - */ - -.globalFilterItem { - line-height: $euiSize; - border: none; - color: $euiTextColor; - padding-top: $euiSizeM / 2; - padding-bottom: $euiSizeM / 2; - white-space: normal; /* 1 */ - - .euiBadge__childButton { - flex-shrink: 1; /* 1 */ - } - - .euiBadge__iconButton:focus { - background-color: transparentize($euiColorPrimary, .9); - } - - &:not(.globalFilterItem-isDisabled) { - @include euiFormControlDefaultShadow; - box-shadow: #{$euiFormControlBoxShadow}, inset 0 0 0 1px $kbnGlobalFilterItemBorderColor; // Make the actual border more visible - } - - &:focus-within { - animation: none !important; // Remove focus ring animation otherwise it overrides simulated border via box-shadow - } -} - -.globalFilterItem-isDisabled { - color: $euiColorDarkShade; - background-color: transparentize($euiColorLightShade, .5); - text-decoration: line-through; - font-weight: $euiFontWeightRegular; - font-style: italic; -} - -.globalFilterItem-isError, .globalFilterItem-isWarning { - .globalFilterLabel__value { - font-weight: $euiFontWeightBold; - } -} - -.globalFilterItem-isError { - .globalFilterLabel__value { - color: makeHighContrastColor($euiColorDangerText, $euiColorLightShade); - } -} - -.globalFilterItem-isWarning { - .globalFilterLabel__value { - color: makeHighContrastColor($euiColorWarningText, $euiColorLightShade); - } -} - -.globalFilterItem-isPinned { - position: relative; - - &::before { - content: ''; - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: $euiSizeXS; - background-color: $kbnGlobalFilterItemBorderColor; - border-top-left-radius: $euiBorderRadius / 2; - border-bottom-left-radius: $euiBorderRadius / 2; - } -} - -.globalFilterItem-isExcluded { - &:not(.globalFilterItem-isDisabled) { - box-shadow: #{$euiFormControlBoxShadow}, inset 0 0 0 1px $kbnGlobalFilterItemBorderColorExcluded; - - &::before { - background-color: $kbnGlobalFilterItemPinnedColorExcluded; - } - } -} - -.globalFilterItem__editorForm { - padding: $euiSizeM; -} - -.globalFilterItem__popover, -.globalFilterItem__popoverAnchor { - display: block; -} - -.globalFilterItem__readonlyPanel { - min-width: auto; - padding: $euiSizeM; -} diff --git a/src/plugins/unified_search/public/filter_bar/_index.scss b/src/plugins/unified_search/public/filter_bar/_index.scss deleted file mode 100644 index 5333aff8b87da..0000000000000 --- a/src/plugins/unified_search/public/filter_bar/_index.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import 'variables'; -@import 'global_filter_group'; -@import 'global_filter_item'; diff --git a/src/plugins/unified_search/public/filter_bar/filter_bar.styles.ts b/src/plugins/unified_search/public/filter_bar/filter_bar.styles.ts new file mode 100644 index 0000000000000..919655e0af160 --- /dev/null +++ b/src/plugins/unified_search/public/filter_bar/filter_bar.styles.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { UseEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; + +export const filterBarStyles = ({ euiTheme }: UseEuiTheme, afterQueryBar?: boolean) => { + return { + group: css` + gap: ${euiTheme.size.xs}; + + &:not(:empty) { + margin-top: ${afterQueryBar ? euiTheme.size.s : 0}; + } + `, + }; +}; diff --git a/src/plugins/unified_search/public/filter_bar/filter_bar.tsx b/src/plugins/unified_search/public/filter_bar/filter_bar.tsx index 43b511b2c9f7d..ec7205d3a7df7 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_bar.tsx +++ b/src/plugins/unified_search/public/filter_bar/filter_bar.tsx @@ -6,224 +6,49 @@ * Side Public License, v 1. */ -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiPopover } from '@elastic/eui'; -import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n-react'; -import { - buildEmptyFilter, - Filter, - enableFilter, - disableFilter, - pinFilter, - toggleFilterDisabled, - toggleFilterNegated, - unpinFilter, -} from '@kbn/es-query'; -import classNames from 'classnames'; -import React, { useState, useRef } from 'react'; - -import { METRIC_TYPE } from '@kbn/analytics'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { UI_SETTINGS } from '@kbn/data-plugin/common'; -import { IDataPluginServices } from '@kbn/data-plugin/public'; +import { EuiFlexGroup, useEuiTheme } from '@elastic/eui'; +import { InjectedIntl, injectI18n } from '@kbn/i18n-react'; +import type { Filter } from '@kbn/es-query'; +import React, { useRef } from 'react'; import { DataView } from '@kbn/data-views-plugin/public'; -import { FilterOptions } from './filter_options'; -import { FILTER_EDITOR_WIDTH, FilterItem } from './filter_item'; -import { FilterEditor } from './filter_editor'; +import FilterItems from './filter_item/filter_items'; + +import { filterBarStyles } from './filter_bar.styles'; export interface Props { filters: Filter[]; onFiltersUpdated?: (filters: Filter[]) => void; - className: string; + className?: string; indexPatterns: DataView[]; intl: InjectedIntl; - appName: string; timeRangeForSuggestionsOverride?: boolean; + /** + * Applies extra styles necessary when coupled with the query bar + */ + afterQueryBar?: boolean; } const FilterBarUI = React.memo(function FilterBarUI(props: Props) { + const euiTheme = useEuiTheme(); + const styles = filterBarStyles(euiTheme, props.afterQueryBar); const groupRef = useRef(null); - const [isAddFilterPopoverOpen, setIsAddFilterPopoverOpen] = useState(false); - const kibana = useKibana(); - const { appName, usageCollection, uiSettings } = kibana.services; - if (!uiSettings) return null; - - const reportUiCounter = usageCollection?.reportUiCounter.bind(usageCollection, appName); - - function onFiltersUpdated(filters: Filter[]) { - if (props.onFiltersUpdated) { - props.onFiltersUpdated(filters); - } - } - - const onAddFilterClick = () => setIsAddFilterPopoverOpen(!isAddFilterPopoverOpen); - - function renderItems() { - return props.filters.map((filter, i) => ( - - onUpdate(i, newFilter)} - onRemove={() => onRemove(i)} - indexPatterns={props.indexPatterns} - uiSettings={uiSettings!} - timeRangeForSuggestionsOverride={props.timeRangeForSuggestionsOverride} - /> - - )); - } - - function renderAddFilter() { - const isPinned = uiSettings!.get(UI_SETTINGS.FILTERS_PINNED_BY_DEFAULT); - const [indexPattern] = props.indexPatterns; - const index = indexPattern && indexPattern.id; - const newFilter = buildEmptyFilter(isPinned, index); - - const button = ( - - +{' '} - - - ); - - return ( - - setIsAddFilterPopoverOpen(false)} - anchorPosition="downLeft" - panelPaddingSize="none" - initialFocus=".filterEditor__hiddenItem" - ownFocus - repositionOnScroll - > - -
- setIsAddFilterPopoverOpen(false)} - key={JSON.stringify(newFilter)} - timeRangeForSuggestionsOverride={props.timeRangeForSuggestionsOverride} - /> -
-
-
-
- ); - } - - function onAdd(filter: Filter) { - reportUiCounter?.(METRIC_TYPE.CLICK, `filter:added`); - setIsAddFilterPopoverOpen(false); - - const filters = [...props.filters, filter]; - onFiltersUpdated(filters); - } - - function onRemove(i: number) { - reportUiCounter?.(METRIC_TYPE.CLICK, `filter:removed`); - const filters = [...props.filters]; - filters.splice(i, 1); - onFiltersUpdated(filters); - groupRef.current?.focus(); - } - - function onUpdate(i: number, filter: Filter) { - reportUiCounter?.(METRIC_TYPE.CLICK, `filter:edited`); - const filters = [...props.filters]; - filters[i] = filter; - onFiltersUpdated(filters); - } - - function onEnableAll() { - reportUiCounter?.(METRIC_TYPE.CLICK, `filter:enable_all`); - const filters = props.filters.map(enableFilter); - onFiltersUpdated(filters); - } - - function onDisableAll() { - reportUiCounter?.(METRIC_TYPE.CLICK, `filter:disable_all`); - const filters = props.filters.map(disableFilter); - onFiltersUpdated(filters); - } - - function onPinAll() { - reportUiCounter?.(METRIC_TYPE.CLICK, `filter:pin_all`); - const filters = props.filters.map(pinFilter); - onFiltersUpdated(filters); - } - - function onUnpinAll() { - reportUiCounter?.(METRIC_TYPE.CLICK, `filter:unpin_all`); - const filters = props.filters.map(unpinFilter); - onFiltersUpdated(filters); - } - - function onToggleAllNegated() { - reportUiCounter?.(METRIC_TYPE.CLICK, `filter:invert_all`); - const filters = props.filters.map(toggleFilterNegated); - onFiltersUpdated(filters); - } - - function onToggleAllDisabled() { - reportUiCounter?.(METRIC_TYPE.CLICK, `filter:toggle_all`); - const filters = props.filters.map(toggleFilterDisabled); - onFiltersUpdated(filters); - } - - function onRemoveAll() { - reportUiCounter?.(METRIC_TYPE.CLICK, `filter:remove_all`); - onFiltersUpdated([]); - } - - const classes = classNames('globalFilterBar', props.className); return ( - - - - - - - {renderItems()} - {renderAddFilter()} - - + ); }); diff --git a/src/plugins/unified_search/public/filter_bar/filter_button_group/filter_button_group.scss b/src/plugins/unified_search/public/filter_bar/filter_button_group/filter_button_group.scss new file mode 100644 index 0000000000000..95b87e1d827c6 --- /dev/null +++ b/src/plugins/unified_search/public/filter_bar/filter_button_group/filter_button_group.scss @@ -0,0 +1,24 @@ +.kbnFilterButtonGroup { + height: $euiFormControlHeight; + background-color: $euiFormInputGroupLabelBackground; + border-radius: $euiFormControlBorderRadius; + box-shadow: 0 0 1px inset rgba($euiFormBorderOpaqueColor, .4); + + // Targets any interactable elements + *:enabled { + transform: none !important; + } + + &--s { + height: $euiFormControlCompressedHeight; + } + + &--attached { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + > *:not(:last-of-type) { + border-right: 1px solid $euiFormBorderColor; + } +} diff --git a/src/plugins/unified_search/public/filter_bar/filter_button_group/filter_button_group.tsx b/src/plugins/unified_search/public/filter_bar/filter_button_group/filter_button_group.tsx new file mode 100644 index 0000000000000..1de5c71f4a301 --- /dev/null +++ b/src/plugins/unified_search/public/filter_bar/filter_button_group/filter_button_group.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import './filter_button_group.scss'; + +import React, { FC, ReactNode } from 'react'; +import classNames from 'classnames'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +interface Props { + items: ReactNode[]; + /** + * Displays the last item without a border radius as if attached to the next DOM node + */ + attached?: boolean; + /** + * Matches overall height with standard form/button sizes + */ + size?: 'm' | 's'; +} + +export const FilterButtonGroup: FC = ({ items, attached, size = 'm', ...rest }: Props) => { + return ( + + {items.map((item, i) => + item == null ? undefined : ( + + {item} + + ) + )} + + ); +}; diff --git a/src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx b/src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx index f136da2a7503a..ae7917e7a1c7a 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx +++ b/src/plugins/unified_search/public/filter_bar/filter_editor/index.tsx @@ -14,6 +14,7 @@ import { EuiFlexItem, EuiForm, EuiFormRow, + EuiPopoverFooter, EuiPopoverTitle, EuiSpacer, EuiSwitch, @@ -55,6 +56,7 @@ export interface Props { onCancel: () => void; intl: InjectedIntl; timeRangeForSuggestionsOverride?: boolean; + mode?: 'edit' | 'add'; } interface State { @@ -68,6 +70,20 @@ interface State { isCustomEditorOpen: boolean; } +const panelTitleAdd = i18n.translate('unifiedSearch.filter.filterEditor.addFilterPopupTitle', { + defaultMessage: 'Add filter', +}); +const panelTitleEdit = i18n.translate('unifiedSearch.filter.filterEditor.editFilterPopupTitle', { + defaultMessage: 'Edit filter', +}); + +const addButtonLabel = i18n.translate('unifiedSearch.filter.filterEditor.addButtonLabel', { + defaultMessage: 'Add filter', +}); +const updateButtonLabel = i18n.translate('unifiedSearch.filter.filterEditor.updateButtonLabel', { + defaultMessage: 'Update filter', +}); + class FilterEditorUI extends Component { constructor(props: Props) { super(props); @@ -86,14 +102,9 @@ class FilterEditorUI extends Component { public render() { return (
- + - - - + {this.props.mode === 'add' ? panelTitleAdd : panelTitleEdit} { -
- + +
{this.renderIndexPatternInput()} {this.state.isCustomEditorOpen ? this.renderCustomEditor() : this.renderRegularEditor()} @@ -154,9 +165,9 @@ class FilterEditorUI extends Component {
)} +
- - + { isDisabled={!this.isFilterValid()} data-test-subj="saveFilter" > - + {this.props.mode === 'add' ? addButtonLabel : updateButtonLabel} @@ -185,8 +193,8 @@ class FilterEditorUI extends Component { - -
+ +
); } @@ -207,32 +215,31 @@ class FilterEditorUI extends Component { } const { selectedIndexPattern } = this.state; return ( - - - + + - indexPattern.title} - onChange={this.onIndexPatternChange} - singleSelection={{ asPlainText: true }} - isClearable={false} - data-test-subj="filterIndexPatternsSelect" - /> - - - + options={this.props.indexPatterns} + selectedOptions={selectedIndexPattern ? [selectedIndexPattern] : []} + getLabel={(indexPattern) => indexPattern.title} + onChange={this.onIndexPatternChange} + singleSelection={{ asPlainText: true }} + isClearable={false} + data-test-subj="filterIndexPatternsSelect" + /> + + + ); } @@ -273,7 +280,7 @@ class FilterEditorUI extends Component { })} options={fields} selectedOptions={selectedField ? [selectedField] : []} - getLabel={(field) => field.name} + getLabel={(field) => field.customLabel || field.name} onChange={this.onFieldChange} singleSelection={{ asPlainText: true }} isClearable={false} diff --git a/src/plugins/unified_search/public/filter_bar/filter_editor/lib/__snapshots__/filter_label.test.tsx.snap b/src/plugins/unified_search/public/filter_bar/filter_editor/lib/__snapshots__/filter_label.test.tsx.snap index 208af5049d137..82ac1e09386c1 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_editor/lib/__snapshots__/filter_label.test.tsx.snap +++ b/src/plugins/unified_search/public/filter_bar/filter_editor/lib/__snapshots__/filter_label.test.tsx.snap @@ -54,6 +54,13 @@ exports[`error 1`] = `
`; +exports[`field custom label 1`] = ` +
+ + geo.coordinates in US +
+`; + exports[`warning 1`] = `
diff --git a/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.test.tsx b/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.test.tsx index 3bd0db41cb10c..47902f3e8e047 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.test.tsx +++ b/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.test.tsx @@ -23,6 +23,18 @@ test('alias', () => { expect(container).toMatchSnapshot(); }); +test('field custom label', () => { + const filter = { + ...phraseFilter, + meta: { + ...phraseFilter.meta, + alias: 'geo.coordinates in US', + }, + }; + const { container } = render(); + expect(container).toMatchSnapshot(); +}); + test('alias with warning status', () => { const filter = { ...phraseFilter, diff --git a/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx b/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx index 0f5ea0cc136ad..601cf68141c49 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx +++ b/src/plugins/unified_search/public/filter_bar/filter_editor/lib/filter_label.tsx @@ -11,11 +11,12 @@ import { EuiTextColor } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Filter, FILTERS } from '@kbn/data-plugin/common'; import { existsOperator, isOneOfOperator } from './filter_operators'; -import type { FilterLabelStatus } from '../../filter_item'; +import type { FilterLabelStatus } from '../../filter_item/filter_item'; export interface FilterLabelProps { filter: Filter; valueLabel?: string; + fieldLabel?: string; filterLabelStatus?: FilterLabelStatus; hideAlias?: boolean; } @@ -25,6 +26,7 @@ export interface FilterLabelProps { export default function FilterLabel({ filter, valueLabel, + fieldLabel, filterLabelStatus, hideAlias, }: FilterLabelProps) { @@ -59,14 +61,14 @@ export default function FilterLabel({ return ( {prefix} - {filter.meta.key}: {getValue(`${existsOperator.message}`)} + {fieldLabel || filter.meta.key}: {getValue(`${existsOperator.message}`)} ); case FILTERS.PHRASES: return ( {prefix} - {filter.meta.key}: {getValue(`${isOneOfOperator.message} ${valueLabel}`)} + {fieldLabel || filter.meta.key}: {getValue(`${isOneOfOperator.message} ${valueLabel}`)} ); case FILTERS.QUERY_STRING: @@ -81,7 +83,7 @@ export default function FilterLabel({ return ( {prefix} - {filter.meta.key}: {getValue(valueLabel)} + {fieldLabel || filter.meta.key}: {getValue(valueLabel)} ); default: diff --git a/src/plugins/unified_search/public/filter_bar/filter_editor/value_input_type.tsx b/src/plugins/unified_search/public/filter_bar/filter_editor/value_input_type.tsx index e66c5d52770f5..7d4b597129847 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_editor/value_input_type.tsx +++ b/src/plugins/unified_search/public/filter_bar/filter_editor/value_input_type.tsx @@ -26,6 +26,13 @@ interface Props { } class ValueInputTypeUI extends Component { + private getValueForNumberField = (value?: string | number): string | number | undefined => { + if (typeof value === 'string') { + const parsedValue = parseFloat(value); + return isNaN(parsedValue) ? value : parsedValue; + } + return value; + }; public render() { const value = this.props.value; const type = this.props.field.type; @@ -50,7 +57,7 @@ class ValueInputTypeUI extends Component { ) { + props.onRemove(); + setIsPopoverOpen(false); + } + function onSubmit(f: Filter) { setIsPopoverOpen(false); props.onUpdate(f); @@ -355,10 +366,11 @@ export function FilterItem(props: FilterItemProps) { const filterViewProps = { filter, valueLabel: valueLabelConfig.title, + fieldLabel: getFieldDisplayValueFromFilter(filter, indexPatterns), filterLabelStatus: valueLabelConfig.status, errorMessage: valueLabelConfig.message, className: getClasses(!!filter.meta.negate, valueLabelConfig), - iconOnClick: props.onRemove, + iconOnClick: handleIconClick, onClick: handleBadgeClick, 'data-test-subj': getDataTestSubj(valueLabelConfig), readonly: props.readonly, @@ -394,6 +406,6 @@ export function FilterItem(props: FilterItemProps) { ); } - +// Needed for React.lazy // eslint-disable-next-line import/no-default-export export default FilterItem; diff --git a/src/plugins/unified_search/public/filter_bar/filter_item/filter_items.tsx b/src/plugins/unified_search/public/filter_bar/filter_item/filter_items.tsx new file mode 100644 index 0000000000000..95d49450dd390 --- /dev/null +++ b/src/plugins/unified_search/public/filter_bar/filter_item/filter_items.tsx @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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, { useRef } from 'react'; +import { css } from '@emotion/react'; +import { EuiFlexItem } from '@elastic/eui'; +import { InjectedIntl, injectI18n } from '@kbn/i18n-react'; +import type { Filter } from '@kbn/es-query'; +import { IDataPluginServices } from '@kbn/data-plugin/public'; +import { METRIC_TYPE } from '@kbn/analytics'; +import { DataView } from '@kbn/data-views-plugin/public'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { FilterItem } from './filter_item'; + +export interface Props { + filters: Filter[]; + onFiltersUpdated?: (filters: Filter[]) => void; + indexPatterns: DataView[]; + intl: InjectedIntl; + timeRangeForSuggestionsOverride?: boolean; +} + +const FilterItemsUI = React.memo(function FilterItemsUI(props: Props) { + const groupRef = useRef(null); + const kibana = useKibana(); + const { appName, usageCollection, uiSettings } = kibana.services; + if (!uiSettings) return null; + + const reportUiCounter = usageCollection?.reportUiCounter.bind(usageCollection, appName); + + function onFiltersUpdated(filters: Filter[]) { + if (props.onFiltersUpdated) { + props.onFiltersUpdated(filters); + } + } + + function renderItems() { + return props.filters.map((filter, i) => ( + + onUpdate(i, newFilter)} + onRemove={() => onRemove(i)} + indexPatterns={props.indexPatterns} + uiSettings={uiSettings!} + timeRangeForSuggestionsOverride={props.timeRangeForSuggestionsOverride} + /> + + )); + } + + function onRemove(i: number) { + reportUiCounter?.(METRIC_TYPE.CLICK, `filter:removed`); + const filters = [...props.filters]; + filters.splice(i, 1); + onFiltersUpdated(filters); + groupRef.current?.focus(); + } + + function onUpdate(i: number, filter: Filter) { + reportUiCounter?.(METRIC_TYPE.CLICK, `filter:edited`); + const filters = [...props.filters]; + filters[i] = filter; + onFiltersUpdated(filters); + } + + return <>{renderItems()}; +}); + +const FilterItems = injectI18n(FilterItemsUI); +// Needed for React.lazy +// eslint-disable-next-line import/no-default-export +export default FilterItems; diff --git a/src/plugins/unified_search/public/filter_bar/filter_options.tsx b/src/plugins/unified_search/public/filter_bar/filter_options.tsx deleted file mode 100644 index d2e229c988711..0000000000000 --- a/src/plugins/unified_search/public/filter_bar/filter_options.tsx +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { EuiButtonIcon, EuiContextMenu, EuiPopover, EuiPopoverTitle } from '@elastic/eui'; -import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n-react'; -import { Component } from 'react'; -import React from 'react'; - -interface Props { - onEnableAll: () => void; - onDisableAll: () => void; - onPinAll: () => void; - onUnpinAll: () => void; - onToggleAllNegated: () => void; - onToggleAllDisabled: () => void; - onRemoveAll: () => void; - intl: InjectedIntl; -} - -interface State { - isPopoverOpen: boolean; -} - -class FilterOptionsUI extends Component { - private buttonRef = React.createRef(); - - public state: State = { - isPopoverOpen: false, - }; - - public togglePopover = () => { - this.setState((prevState) => ({ - isPopoverOpen: !prevState.isPopoverOpen, - })); - }; - - public closePopover = () => { - this.setState({ isPopoverOpen: false }); - this.buttonRef.current?.focus(); - }; - - public render() { - const panelTree = { - id: 0, - items: [ - { - name: this.props.intl.formatMessage({ - id: 'unifiedSearch.filter.options.enableAllFiltersButtonLabel', - defaultMessage: 'Enable all', - }), - icon: 'eye', - onClick: () => { - this.closePopover(); - this.props.onEnableAll(); - }, - 'data-test-subj': 'enableAllFilters', - }, - { - name: this.props.intl.formatMessage({ - id: 'unifiedSearch.filter.options.disableAllFiltersButtonLabel', - defaultMessage: 'Disable all', - }), - icon: 'eyeClosed', - onClick: () => { - this.closePopover(); - this.props.onDisableAll(); - }, - 'data-test-subj': 'disableAllFilters', - }, - { - name: this.props.intl.formatMessage({ - id: 'unifiedSearch.filter.options.pinAllFiltersButtonLabel', - defaultMessage: 'Pin all', - }), - icon: 'pin', - onClick: () => { - this.closePopover(); - this.props.onPinAll(); - }, - 'data-test-subj': 'pinAllFilters', - }, - { - name: this.props.intl.formatMessage({ - id: 'unifiedSearch.filter.options.unpinAllFiltersButtonLabel', - defaultMessage: 'Unpin all', - }), - icon: 'pin', - onClick: () => { - this.closePopover(); - this.props.onUnpinAll(); - }, - 'data-test-subj': 'unpinAllFilters', - }, - { - name: this.props.intl.formatMessage({ - id: 'unifiedSearch.filter.options.invertNegatedFiltersButtonLabel', - defaultMessage: 'Invert inclusion', - }), - icon: 'invert', - onClick: () => { - this.closePopover(); - this.props.onToggleAllNegated(); - }, - 'data-test-subj': 'invertInclusionAllFilters', - }, - { - name: this.props.intl.formatMessage({ - id: 'unifiedSearch.filter.options.invertDisabledFiltersButtonLabel', - defaultMessage: 'Invert enabled/disabled', - }), - icon: 'eye', - onClick: () => { - this.closePopover(); - this.props.onToggleAllDisabled(); - }, - 'data-test-subj': 'invertEnableDisableAllFilters', - }, - { - name: this.props.intl.formatMessage({ - id: 'unifiedSearch.filter.options.deleteAllFiltersButtonLabel', - defaultMessage: 'Remove all', - }), - icon: 'trash', - onClick: () => { - this.closePopover(); - this.props.onRemoveAll(); - }, - 'data-test-subj': 'removeAllFilters', - }, - ], - }; - - return ( - - } - anchorPosition="rightUp" - panelPaddingSize="none" - repositionOnScroll - > - - - - - - ); - } -} - -export const FilterOptions = injectI18n(FilterOptionsUI); diff --git a/src/plugins/unified_search/public/filter_bar/filter_view/index.tsx b/src/plugins/unified_search/public/filter_bar/filter_view/index.tsx index 974b94e95b0e0..d399bb0025a10 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_view/index.tsx +++ b/src/plugins/unified_search/public/filter_bar/filter_view/index.tsx @@ -11,11 +11,12 @@ import { i18n } from '@kbn/i18n'; import React, { FC } from 'react'; import { Filter, isFilterPinned } from '@kbn/es-query'; import { FilterLabel } from '..'; -import type { FilterLabelStatus } from '../filter_item'; +import type { FilterLabelStatus } from '../filter_item/filter_item'; interface Props { filter: Filter; valueLabel: string; + fieldLabel?: string; filterLabelStatus: FilterLabelStatus; errorMessage?: string; readonly?: boolean; @@ -28,6 +29,7 @@ export const FilterView: FC = ({ iconOnClick, onClick, valueLabel, + fieldLabel, errorMessage, filterLabelStatus, readonly, @@ -100,6 +102,7 @@ export const FilterView: FC = ({ diff --git a/src/plugins/unified_search/public/filter_bar/index.tsx b/src/plugins/unified_search/public/filter_bar/index.tsx index 70a108f359790..30f94c3972ee1 100644 --- a/src/plugins/unified_search/public/filter_bar/index.tsx +++ b/src/plugins/unified_search/public/filter_bar/index.tsx @@ -17,6 +17,13 @@ export const FilterBar = (props: React.ComponentProps) => ); +const LazyFilterItems = React.lazy(() => import('./filter_item/filter_items')); +export const FilterItems = (props: React.ComponentProps) => ( + }> + + +); + const LazyFilterLabel = React.lazy(() => import('./filter_editor/lib/filter_label')); export const FilterLabel = (props: React.ComponentProps) => ( }> @@ -24,7 +31,7 @@ export const FilterLabel = (props: React.ComponentProps) ); -const LazyFilterItem = React.lazy(() => import('./filter_item')); +const LazyFilterItem = React.lazy(() => import('./filter_item/filter_item')); export const FilterItem = (props: React.ComponentProps) => ( }> diff --git a/src/plugins/unified_search/public/index.scss b/src/plugins/unified_search/public/index.scss index 7f7704c64e9b4..72e1c0c313f74 100755 --- a/src/plugins/unified_search/public/index.scss +++ b/src/plugins/unified_search/public/index.scss @@ -3,5 +3,3 @@ @import './saved_query_management/index'; @import './query_string_input/index'; - -@import './filter_bar/index'; diff --git a/src/plugins/unified_search/public/index.ts b/src/plugins/unified_search/public/index.ts index 1200234a793a4..bc7974b42efb3 100755 --- a/src/plugins/unified_search/public/index.ts +++ b/src/plugins/unified_search/public/index.ts @@ -5,6 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import { PluginInitializerContext } from '@kbn/core/public'; import { ConfigSchema } from '../config'; export type { IndexPatternSelectProps } from './index_pattern_select'; @@ -14,6 +15,8 @@ export type { StatefulSearchBarProps, SearchBarProps } from './search_bar'; export type { UnifiedSearchPublicPluginStart, UnifiedSearchPluginSetup } from './types'; export { SearchBar } from './search_bar'; export { FilterLabel, FilterItem } from './filter_bar'; +export { DataViewsList } from './dataview_picker/dataview_list'; +export { DataViewPicker } from './dataview_picker'; export type { ApplyGlobalFilterActionContext } from './actions'; export { ACTION_GLOBAL_APPLY_FILTER } from './actions'; @@ -28,7 +31,7 @@ export type { AutocompleteStart, } from './autocomplete'; -export { QuerySuggestionTypes } from './autocomplete'; +export { QuerySuggestionTypes } from './autocomplete/providers/query_suggestion_provider'; import { UnifiedSearchPublicPlugin } from './plugin'; diff --git a/src/plugins/unified_search/public/plugin.ts b/src/plugins/unified_search/public/plugin.ts index 93f1aaf19fae8..08f07e507d96b 100755 --- a/src/plugins/unified_search/public/plugin.ts +++ b/src/plugins/unified_search/public/plugin.ts @@ -5,16 +5,13 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - -import './index.scss'; - import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import { Storage, IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import { APPLY_FILTER_TRIGGER } from '@kbn/data-plugin/public'; import { ConfigSchema } from '../config'; import { setIndexPatterns, setTheme, setOverlays, setAutocomplete } from './services'; -import { AutocompleteService } from './autocomplete'; +import { AutocompleteService } from './autocomplete/autocomplete_service'; import { createSearchBar } from './search_bar'; import { createIndexPatternSelect } from './index_pattern_select'; import type { @@ -58,7 +55,7 @@ export class UnifiedSearchPublicPlugin public start( core: CoreStart, - { data, dataViews, uiActions }: UnifiedSearchStartDependencies + { data, dataViews, uiActions, screenshotMode }: UnifiedSearchStartDependencies ): UnifiedSearchPublicPluginStart { setTheme(core.theme); setOverlays(core.overlays); @@ -71,6 +68,7 @@ export class UnifiedSearchPublicPlugin data, storage: this.storage, usageCollection: this.usageCollection, + isScreenshotMode: Boolean(screenshotMode?.isScreenshotMode()), }); uiActions.addTriggerAction( diff --git a/src/plugins/unified_search/public/query_string_input/_query_bar.scss b/src/plugins/unified_search/public/query_string_input/_query_bar.scss index f8c2f067d9ec5..7b9a735d1556f 100644 --- a/src/plugins/unified_search/public/query_string_input/_query_bar.scss +++ b/src/plugins/unified_search/public/query_string_input/_query_bar.scss @@ -1,61 +1,34 @@ .kbnQueryBar__wrap { - max-width: 100%; + width: 100%; z-index: $euiZContentMenu; -} + height: $euiFormControlHeight; + display: flex; -// Uses the append style, but no bordering -.kqlQueryBar__languageSwitcherButton { - border-right: none !important; - border-left: $euiFormInputGroupBorder; + > [aria-expanded='true'] { + // Using filter allows it to adhere the children's bounds + filter: drop-shadow(0 5.7px 12px rgba($euiShadowColor, shadowOpacity(.05))); + } } .kbnQueryBar__textareaWrap { + position: relative; overflow: visible !important; // Override EUI form control display: flex; flex: 1 1 100%; - position: relative; - background-color: $euiFormBackgroundColor; - border-radius: $euiFormControlBorderRadius; - - &.kbnQueryBar__textareaWrap--hasPrepend { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - - &.kbnQueryBar__textareaWrap--hasAppend { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } } .kbnQueryBar__textarea { z-index: $euiZContentMenu; resize: none !important; // When in the group, it will autosize - height: $euiFormControlHeight - 2px; + height: $euiFormControlHeight; // Unlike most inputs within layout control groups, the text area still needs a border // for multi-line content. These adjusts help it sit above the control groups // shadow to line up correctly. - padding: $euiSizeS; - box-shadow: 0 0 0 1px $euiFormBorderColor; - padding-bottom: $euiSizeS + 1px; + padding: ($euiSizeS + 2px) $euiSizeS $euiSizeS; // Firefox adds margin to textarea margin: 0; - &.kbnQueryBar__textarea--hasPrepend { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - &.kbnQueryBar__textarea--hasAppend { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - - &:not(.kbnQueryBar__textarea--autoHeight):not(:invalid) { - @include euiYScrollWithShadows; - } - &:not(.kbnQueryBar__textarea--autoHeight) { - white-space: nowrap; overflow-y: hidden; overflow-x: hidden; } @@ -65,18 +38,35 @@ overflow-x: auto; overflow-y: auto; white-space: normal; - box-shadow: 0 0 0 1px $euiFormBorderColor; + + } + + &.kbnQueryBar__textarea--isSuggestionsVisible { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + } + + &--isClearable { + @include euiFormControlWithIcon($isIconOptional: false, $side: 'right'); } @include euiFormControlWithIcon($isIconOptional: true); + ~ .euiFormControlLayoutIcons { // By default form control layout icon is vertically centered, but our textarea // can expand to be multi-line, so we position it with padding that matches // the parent textarea padding z-index: $euiZContentMenu + 1; - top: $euiSizeS + 3px; + top: $euiSizeM; bottom: unset; } + + &--withPrepend { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + margin-left: -1px; + width: calc(100% + 1px); + } } .kbnQueryBar__datePickerWrapper { @@ -92,32 +82,3 @@ } } } - -@include euiBreakpoint('xs', 's') { - .kbnQueryBar--withDatePicker { - > :first-child { - // Change the order of the query bar and date picker so that the date picker is top and the query bar still aligns with filters - order: 1; - // EUI Flexbox adds too much margin between responded items, this just moves it up - margin-top: $euiSizeS * -1; - } - } -} - -// IE specific fix for the datepicker to not collapse -@include euiBreakpoint('m', 'l', 'xl') { - .kbnQueryBar__datePickerWrapper { - max-width: 40vw; - // sass-lint:disable-block no-important - flex-grow: 0 !important; - flex-basis: auto !important; - - &.kbnQueryBar__datePickerWrapper-isHidden { - // sass-lint:disable-block no-important - margin-right: -$euiSizeXS !important; - width: 0; - overflow: hidden; - max-width: 0; - } - } -} diff --git a/src/plugins/unified_search/public/query_string_input/add_filter_popover.tsx b/src/plugins/unified_search/public/query_string_input/add_filter_popover.tsx new file mode 100644 index 0000000000000..b86d7d7f02498 --- /dev/null +++ b/src/plugins/unified_search/public/query_string_input/add_filter_popover.tsx @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiFlexItem, + EuiButtonIcon, + EuiPopover, + EuiButtonIconProps, + EuiToolTip, +} from '@elastic/eui'; +import { Filter } from '@kbn/es-query'; +import type { DataView } from '@kbn/data-views-plugin/public'; +import { FilterEditorWrapper } from './filter_editor_wrapper'; + +interface AddFilterPopoverProps { + indexPatterns?: Array; + filters: Filter[]; + timeRangeForSuggestionsOverride?: boolean; + onFiltersUpdated?: (filters: Filter[]) => void; + buttonProps?: Partial; +} + +export const AddFilterPopover = React.memo(function AddFilterPopover({ + indexPatterns, + filters, + timeRangeForSuggestionsOverride, + onFiltersUpdated, + buttonProps, +}: AddFilterPopoverProps) { + const [isAddFilterPopoverOpen, setIsAddFilterPopoverOpen] = useState(false); + + const buttonIconLabel = i18n.translate('unifiedSearch.filter.filterBar.addFilterButtonLabel', { + defaultMessage: 'Add filter', + }); + + const button = ( + + setIsAddFilterPopoverOpen(!isAddFilterPopoverOpen)} + size="m" + {...buttonProps} + /> + + ); + + return ( + + setIsAddFilterPopoverOpen(false)} + anchorPosition="downLeft" + panelPaddingSize="none" + initialFocus=".filterEditor__hiddenItem" + ownFocus + repositionOnScroll + > + setIsAddFilterPopoverOpen(false)} + /> + + + ); +}); diff --git a/src/plugins/unified_search/public/query_string_input/filter_editor_wrapper.tsx b/src/plugins/unified_search/public/query_string_input/filter_editor_wrapper.tsx new file mode 100644 index 0000000000000..dd106607353f2 --- /dev/null +++ b/src/plugins/unified_search/public/query_string_input/filter_editor_wrapper.tsx @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState, useEffect } from 'react'; +import { Filter, buildEmptyFilter } from '@kbn/es-query'; +import { METRIC_TYPE } from '@kbn/analytics'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { UI_SETTINGS } from '@kbn/data-plugin/common'; +import { IDataPluginServices } from '@kbn/data-plugin/public'; +import type { DataView } from '@kbn/data-views-plugin/public'; +import { FILTER_EDITOR_WIDTH } from '../filter_bar/filter_item/filter_item'; +import { FilterEditor } from '../filter_bar/filter_editor'; +import { fetchIndexPatterns } from './fetch_index_patterns'; + +interface FilterEditorWrapperProps { + indexPatterns?: Array; + filters: Filter[]; + timeRangeForSuggestionsOverride?: boolean; + closePopover?: () => void; + onFiltersUpdated?: (filters: Filter[]) => void; +} + +export const FilterEditorWrapper = React.memo(function FilterEditorWrapper({ + indexPatterns, + filters, + timeRangeForSuggestionsOverride, + closePopover, + onFiltersUpdated, +}: FilterEditorWrapperProps) { + const kibana = useKibana(); + const { uiSettings, data, usageCollection, appName } = kibana.services; + const reportUiCounter = usageCollection?.reportUiCounter.bind(usageCollection, appName); + const [dataViews, setDataviews] = useState([]); + const [newFilter, setNewFilter] = useState(undefined); + const isPinned = uiSettings!.get(UI_SETTINGS.FILTERS_PINNED_BY_DEFAULT); + + useEffect(() => { + const fetchDataViews = async () => { + const stringPatterns = indexPatterns?.filter( + (indexPattern) => typeof indexPattern === 'string' + ) as string[]; + const objectPatterns = indexPatterns?.filter( + (indexPattern) => typeof indexPattern !== 'string' + ) as DataView[]; + + const objectPatternsFromStrings = (await fetchIndexPatterns( + data.dataViews, + stringPatterns + )) as DataView[]; + setDataviews([...objectPatterns, ...objectPatternsFromStrings]); + const [dataView] = [...objectPatterns, ...objectPatternsFromStrings]; + const index = dataView && dataView.id; + const emptyFilter = buildEmptyFilter(isPinned, index); + setNewFilter(emptyFilter); + }; + if (indexPatterns) { + fetchDataViews(); + } + }, [data.dataViews, indexPatterns, isPinned]); + + function onAdd(filter: Filter) { + reportUiCounter?.(METRIC_TYPE.CLICK, `filter:added`); + closePopover?.(); + const updatedFilters = [...filters, filter]; + onFiltersUpdated?.(updatedFilters); + } + + return ( +
+ {newFilter && ( + closePopover?.()} + key={JSON.stringify(newFilter)} + timeRangeForSuggestionsOverride={timeRangeForSuggestionsOverride} + mode="add" + /> + )} +
+ ); +}); diff --git a/src/plugins/unified_search/public/query_string_input/language_switcher.test.tsx b/src/plugins/unified_search/public/query_string_input/language_switcher.test.tsx index 0223fc85a3ddb..591fe94360793 100644 --- a/src/plugins/unified_search/public/query_string_input/language_switcher.test.tsx +++ b/src/plugins/unified_search/public/query_string_input/language_switcher.test.tsx @@ -11,7 +11,7 @@ import { QueryLanguageSwitcher, QueryLanguageSwitcherProps } from './language_sw import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { coreMock } from '@kbn/core/public/mocks'; import { mountWithIntl } from '@kbn/test-jest-helpers'; -import { EuiButtonEmpty, EuiIcon, EuiPopover } from '@elastic/eui'; +import { EuiButtonIcon, EuiIcon, EuiPopover } from '@elastic/eui'; const startMock = coreMock.createStart(); describe('LanguageSwitcher', () => { @@ -28,7 +28,7 @@ describe('LanguageSwitcher', () => { ); } - it('should toggle off if language is lucene', () => { + it('should select the lucene context menu if language is lucene', () => { const component = mountWithIntl( wrapInContext({ language: 'lucene', @@ -37,12 +37,14 @@ describe('LanguageSwitcher', () => { }, }) ); - component.find(EuiButtonEmpty).simulate('click'); + component.find(EuiButtonIcon).simulate('click'); expect(component.find(EuiPopover).prop('isOpen')).toBe(true); - expect(component.find('[data-test-subj="languageToggle"]').get(0).props.checked).toBeFalsy(); + expect(component.find('[data-test-subj="luceneLanguageMenuItem"]').get(0).props.icon).toBe( + 'check' + ); }); - it('should toggle on if language is kuery', () => { + it('should select the kql context menu if language is kuery', () => { const component = mountWithIntl( wrapInContext({ language: 'kuery', @@ -51,12 +53,14 @@ describe('LanguageSwitcher', () => { }, }) ); - component.find(EuiButtonEmpty).simulate('click'); + component.find(EuiButtonIcon).simulate('click'); expect(component.find(EuiPopover).prop('isOpen')).toBe(true); - expect(component.find('[data-test-subj="languageToggle"]').get(0).props.checked).toBeTruthy(); + expect(component.find('[data-test-subj="kqlLanguageMenuItem"]').get(0).props.icon).toBe( + 'check' + ); }); - it('should toggle off if language is text', () => { + it('should select the lucene context menu if language is text', () => { const component = mountWithIntl( wrapInContext({ language: 'text', @@ -65,9 +69,11 @@ describe('LanguageSwitcher', () => { }, }) ); - component.find(EuiButtonEmpty).simulate('click'); + component.find(EuiButtonIcon).simulate('click'); expect(component.find(EuiPopover).prop('isOpen')).toBe(true); - expect(component.find('[data-test-subj="languageToggle"]').get(0).props.checked).toBeFalsy(); + expect(component.find('[data-test-subj="luceneLanguageMenuItem"]').get(0).props.icon).toBe( + 'check' + ); }); it('it set language on nonKql mode text', () => { const onSelectLanguage = jest.fn(); @@ -79,11 +85,13 @@ describe('LanguageSwitcher', () => { onSelectLanguage, }) ); - component.find(EuiButtonEmpty).simulate('click'); + component.find(EuiButtonIcon).simulate('click'); expect(component.find(EuiPopover).prop('isOpen')).toBe(true); - expect(component.find('[data-test-subj="languageToggle"]').get(0).props.checked).toBeTruthy(); + expect(component.find('[data-test-subj="kqlLanguageMenuItem"]').get(0).props.icon).toBe( + 'check' + ); - component.find('[data-test-subj="languageToggle"]').at(1).simulate('click'); + component.find('[data-test-subj="luceneLanguageMenuItem"]').at(1).simulate('click'); expect(onSelectLanguage).toHaveBeenCalledWith('text'); }); @@ -97,8 +105,8 @@ describe('LanguageSwitcher', () => { onSelectLanguage, }) ); - component.find(EuiButtonEmpty).simulate('click'); - component.find('[data-test-subj="languageToggle"]').at(1).simulate('click'); + component.find(EuiButtonIcon).simulate('click'); + component.find('[data-test-subj="luceneLanguageMenuItem"]').at(1).simulate('click'); expect(onSelectLanguage).toHaveBeenCalledWith('lucene'); }); @@ -114,10 +122,10 @@ describe('LanguageSwitcher', () => { }) ); - expect(component.find(EuiIcon).prop('type')).toBe('boxesVertical'); + expect(component.find(EuiIcon).prop('type')).toBe('filter'); - component.find(EuiButtonEmpty).simulate('click'); - component.find('[data-test-subj="languageToggle"]').at(1).simulate('click'); + component.find(EuiButtonIcon).simulate('click'); + component.find('[data-test-subj="kqlLanguageMenuItem"]').at(1).simulate('click'); expect(onSelectLanguage).toHaveBeenCalledWith('kuery'); }); @@ -132,13 +140,12 @@ describe('LanguageSwitcher', () => { onSelectLanguage, }) ); - - expect(component.find('[data-test-subj="switchQueryLanguageButton"]').at(0).text()).toBe( - 'Lucene' + component.find(EuiButtonIcon).simulate('click'); + expect(component.find('[data-test-subj="luceneLanguageMenuItem"]').get(0).props.icon).toBe( + 'check' ); - component.find(EuiButtonEmpty).simulate('click'); - component.find('[data-test-subj="languageToggle"]').at(1).simulate('click'); + component.find('[data-test-subj="kqlLanguageMenuItem"]').at(1).simulate('click'); expect(onSelectLanguage).toHaveBeenCalledWith('kuery'); }); diff --git a/src/plugins/unified_search/public/query_string_input/language_switcher.tsx b/src/plugins/unified_search/public/query_string_input/language_switcher.tsx index db42339e464c3..a48901ef17f86 100644 --- a/src/plugins/unified_search/public/query_string_input/language_switcher.tsx +++ b/src/plugins/unified_search/public/query_string_input/language_switcher.tsx @@ -7,17 +7,13 @@ */ import { - EuiButtonEmpty, - EuiForm, - EuiFormRow, - EuiIcon, - EuiLink, EuiPopover, EuiPopoverTitle, - EuiSpacer, - EuiSwitch, - EuiText, PopoverAnchorPosition, + EuiContextMenuItem, + toSentenceCase, + EuiHorizontalRule, + EuiButtonIcon, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; @@ -29,7 +25,7 @@ export interface QueryLanguageSwitcherProps { onSelectLanguage: (newLanguage: string) => void; anchorPosition?: PopoverAnchorPosition; nonKqlMode?: 'lucene' | 'text'; - nonKqlModeHelpText?: string; + isOnTopBarMenu?: boolean; } export const QueryLanguageSwitcher = React.memo(function QueryLanguageSwitcher({ @@ -37,124 +33,78 @@ export const QueryLanguageSwitcher = React.memo(function QueryLanguageSwitcher({ anchorPosition, onSelectLanguage, nonKqlMode = 'lucene', - nonKqlModeHelpText, + isOnTopBarMenu, }: QueryLanguageSwitcherProps) { const kibana = useKibana(); const kueryQuerySyntaxDocs = kibana.services.docLinks!.links.query.kueryQuerySyntax; const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const luceneLabel = ( - - ); - const kqlLabel = ( - - ); - - const kqlFullName = ( - - ); - - const kqlModeTitle = i18n.translate('unifiedSearch.query.queryBar.languageSwitcher.toText', { - defaultMessage: 'Switch to Kibana Query Language for search', - }); const button = ( - setIsPopoverOpen(!isPopoverOpen)} className="euiFormControlLayout__append kqlQueryBar__languageSwitcherButton" data-test-subj={'switchQueryLanguageButton'} - > - {language === 'kuery' ? ( - kqlLabel - ) : nonKqlMode === 'lucene' ? ( - luceneLabel - ) : ( - - )} - + aria-label={i18n.translate('unifiedSearch.switchLanguage.buttonText', { + defaultMessage: 'Switch language button.', + })} + /> + ); + + const languageMenuItem = ( +
+ { + onSelectLanguage('kuery'); + }} + > + KQL + + { + onSelectLanguage(nonKqlMode); + }} + > + {toSentenceCase(nonKqlMode)} + + + + Documentation + +
); - return ( + const languageQueryStringComponent = ( setIsPopoverOpen(false)} repositionOnScroll - ownFocus={true} - initialFocus={'[role="switch"]'} + panelPaddingSize="none" > - + -
- -

- - {kqlFullName} - - ), - nonKqlModeHelpText: - nonKqlModeHelpText || - i18n.translate( - 'unifiedSearch.query.queryBar.syntaxOptionsDescription.nonKqlModeHelpText', - { - defaultMessage: 'Kibana uses Lucene.', - } - ), - }} - /> -

-
- - - - - - - ) : ( - - ) - } - checked={language === 'kuery'} - onChange={() => { - const newLanguage = language === 'kuery' ? nonKqlMode : 'kuery'; - onSelectLanguage(newLanguage); - }} - data-test-subj="languageToggle" - /> - - -
+ {languageMenuItem}
); + + return Boolean(isOnTopBarMenu) ? languageMenuItem : languageQueryStringComponent; }); diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_menu.test.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_menu.test.tsx new file mode 100644 index 0000000000000..3d41a9145dffa --- /dev/null +++ b/src/plugins/unified_search/public/query_string_input/query_bar_menu.test.tsx @@ -0,0 +1,278 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { I18nProvider } from '@kbn/i18n-react'; +import { act } from 'react-dom/test-utils'; +import { mountWithIntl as mount } from '@kbn/test-jest-helpers'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { coreMock } from '@kbn/core/public/mocks'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { Filter } from '@kbn/es-query'; +import { QueryBarMenuProps, QueryBarMenu } from './query_bar_menu'; +import { EuiPopover } from '@elastic/eui'; + +describe('Querybar Menu component', () => { + const createMockWebStorage = () => ({ + clear: jest.fn(), + getItem: jest.fn(), + key: jest.fn(), + removeItem: jest.fn(), + setItem: jest.fn(), + length: 0, + }); + + const createMockStorage = () => ({ + storage: createMockWebStorage(), + get: jest.fn(), + set: jest.fn(), + remove: jest.fn(), + clear: jest.fn(), + }); + const getStorage = (v: string) => { + const storage = createMockStorage(); + storage.get.mockReturnValue(v); + return storage; + }; + + const startMock = coreMock.createStart(); + let dataMock = dataPluginMock.createStartContract(); + function wrapQueryBarMenuComponentInContext(testProps: QueryBarMenuProps, storageValue: string) { + dataMock = { + ...dataMock, + dataViews: { + ...dataMock.dataViews, + getIdsWithTitle: jest.fn(), + }, + }; + const services = { + data: dataMock, + storage: getStorage(storageValue), + uiSettings: startMock.uiSettings, + }; + + return ( + + + + + + ); + } + let props: QueryBarMenuProps; + beforeEach(() => { + props = { + language: 'kuery', + onQueryChange: jest.fn(), + onQueryBarSubmit: jest.fn(), + toggleFilterBarMenuPopover: jest.fn(), + openQueryBarMenu: false, + savedQueryService: { + ...dataMock.query.savedQueries, + findSavedQueries: jest.fn().mockResolvedValue({ + queries: [ + { + id: '8a0b7cd0-b0c4-11ec-92b2-73d62e0d28a9', + attributes: { + title: 'Test', + description: '', + query: { + query: 'category.keyword : "Men\'s Shoes" ', + language: 'kuery', + }, + filters: [], + }, + }, + ], + }), + }, + }; + }); + it('should not render the popover if the openQueryBarMenu prop is false', async () => { + await act(async () => { + const component = mount(wrapQueryBarMenuComponentInContext(props, 'kuery')); + expect(component.find(EuiPopover).prop('isOpen')).toBe(false); + }); + }); + + it('should render the popover if the openQueryBarMenu prop is true', async () => { + const newProps = { + ...props, + openQueryBarMenu: true, + }; + await act(async () => { + const component = mount(wrapQueryBarMenuComponentInContext(newProps, 'kuery')); + expect(component.find(EuiPopover).prop('isOpen')).toBe(true); + }); + }); + + it('should render the context menu by default', async () => { + const newProps = { + ...props, + openQueryBarMenu: true, + }; + const component = mount(wrapQueryBarMenuComponentInContext(newProps, 'kuery')); + expect(component.find('[data-test-subj="queryBarMenuPanel"]')).toBeTruthy(); + }); + + it('should render the saved saved queries panels if the showQueryInput prop is true but disabled', async () => { + const newProps = { + ...props, + openQueryBarMenu: true, + showQueryInput: true, + showFilterBar: true, + }; + const component = mount(wrapQueryBarMenuComponentInContext(newProps, 'kuery')); + const saveFilterSetButton = component.find( + '[data-test-subj="saved-query-management-save-button"]' + ); + const loadFilterSetButton = component.find( + '[data-test-subj="saved-query-management-load-button"]' + ); + expect(saveFilterSetButton.length).toBeTruthy(); + expect(saveFilterSetButton.first().prop('disabled')).toBe(true); + expect(loadFilterSetButton.length).toBeTruthy(); + expect(loadFilterSetButton.first().prop('disabled')).toBe(true); + }); + + it('should render the saved queries panels if the showFilterBar is true but disabled', async () => { + const newProps = { + ...props, + openQueryBarMenu: true, + showFilterBar: true, + }; + const component = mount(wrapQueryBarMenuComponentInContext(newProps, 'kuery')); + const applyToAllFiltersButton = component.find( + '[data-test-subj="filter-sets-applyToAllFilters"]' + ); + const removeAllFiltersButton = component.find( + '[data-test-subj="filter-sets-removeAllFilters"]' + ); + expect(applyToAllFiltersButton.length).toBeTruthy(); + expect(applyToAllFiltersButton.first().prop('disabled')).toBe(true); + expect(removeAllFiltersButton.length).toBeTruthy(); + expect(removeAllFiltersButton.first().prop('disabled')).toBe(true); + }); + + it('should enable the clear all button if query is given', async () => { + const newProps = { + ...props, + openQueryBarMenu: true, + showFilterBar: true, + query: { + query: 'category.keyword : "Men\'s Shoes" ', + language: 'kuery', + }, + }; + const component = mount(wrapQueryBarMenuComponentInContext(newProps, 'kuery')); + const removeAllFiltersButton = component.find( + '[data-test-subj="filter-sets-removeAllFilters"]' + ); + expect(removeAllFiltersButton.first().prop('disabled')).toBe(false); + }); + + it('should enable the apply to all button if filter is given', async () => { + const newProps = { + ...props, + openQueryBarMenu: true, + showFilterBar: true, + filters: [ + { + meta: { + index: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'category.keyword', + params: { + query: "Men's Accessories", + }, + }, + query: { + match_phrase: { + 'category.keyword': "Men's Accessories", + }, + }, + $state: { + store: 'appState', + }, + }, + ] as Filter[], + }; + const component = mount(wrapQueryBarMenuComponentInContext(newProps, 'kuery')); + const applyToAllFiltersButton = component.find( + '[data-test-subj="filter-sets-applyToAllFilters"]' + ); + expect(applyToAllFiltersButton.first().prop('disabled')).toBe(false); + }); + + it('should render the language switcher panel', async () => { + const newProps = { + ...props, + openQueryBarMenu: true, + showFilterBar: true, + showQueryInput: true, + }; + const component = mount(wrapQueryBarMenuComponentInContext(newProps, 'kuery')); + const languageSwitcher = component.find('[data-test-subj="switchQueryLanguageButton"]'); + expect(languageSwitcher.length).toBeTruthy(); + }); + + it('should render the save query quick buttons', async () => { + const newProps = { + ...props, + openQueryBarMenu: true, + showSaveQuery: true, + filters: [ + { + meta: { + index: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'category.keyword', + params: { + query: "Men's Accessories", + }, + }, + query: { + match_phrase: { + 'category.keyword': "Men's Accessories", + }, + }, + $state: { + store: 'appState', + }, + }, + ] as Filter[], + savedQuery: { + id: '8a0b7cd0-b0c4-11ec-92b2-73d62e0d28a9', + attributes: { + title: 'Test', + description: '', + query: { + query: 'category.keyword : "Men\'s Shoes" ', + language: 'kuery', + }, + filters: [], + }, + }, + }; + const component = mount(wrapQueryBarMenuComponentInContext(newProps, 'kuery')); + const saveChangesButton = component.find( + '[data-test-subj="saved-query-management-save-changes-button"]' + ); + expect(saveChangesButton.length).toBeTruthy(); + const saveChangesAsNewButton = component.find( + '[data-test-subj="saved-query-management-save-as-new-button"]' + ); + expect(saveChangesAsNewButton.length).toBeTruthy(); + }); +}); diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_menu.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_menu.tsx new file mode 100644 index 0000000000000..810d0a64d0251 --- /dev/null +++ b/src/plugins/unified_search/public/query_string_input/query_bar_menu.tsx @@ -0,0 +1,186 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState, useEffect } from 'react'; +import { + EuiButtonIcon, + EuiContextMenu, + EuiContextMenuPanel, + EuiPopover, + useGeneratedHtmlId, + EuiButtonIconProps, + EuiToolTip, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import type { Filter, Query } from '@kbn/es-query'; +import type { DataView } from '@kbn/data-views-plugin/public'; +import type { TimeRange, SavedQueryService, SavedQuery } from '@kbn/data-plugin/public'; +import { QueryBarMenuPanels } from './query_bar_menu_panels'; +import { FilterEditorWrapper } from './filter_editor_wrapper'; + +export interface QueryBarMenuProps { + language: string; + onQueryChange: (payload: { dateRange: TimeRange; query?: Query }) => void; + onQueryBarSubmit: (payload: { dateRange: TimeRange; query?: Query }) => void; + toggleFilterBarMenuPopover: (value: boolean) => void; + openQueryBarMenu: boolean; + nonKqlMode?: 'lucene' | 'text'; + dateRangeFrom?: string; + dateRangeTo?: string; + savedQueryService: SavedQueryService; + saveAsNewQueryFormComponent?: JSX.Element; + saveFormComponent?: JSX.Element; + manageFilterSetComponent?: JSX.Element; + onFiltersUpdated?: (filters: Filter[]) => void; + filters?: Filter[]; + query?: Query; + savedQuery?: SavedQuery; + onClearSavedQuery?: () => void; + showQueryInput?: boolean; + showFilterBar?: boolean; + showSaveQuery?: boolean; + timeRangeForSuggestionsOverride?: boolean; + indexPatterns?: Array; + buttonProps?: Partial; +} + +export function QueryBarMenu({ + language, + nonKqlMode, + dateRangeFrom, + dateRangeTo, + onQueryChange, + onQueryBarSubmit, + savedQueryService, + saveAsNewQueryFormComponent, + saveFormComponent, + manageFilterSetComponent, + openQueryBarMenu, + toggleFilterBarMenuPopover, + onFiltersUpdated, + filters, + query, + savedQuery, + onClearSavedQuery, + showQueryInput, + showFilterBar, + showSaveQuery, + indexPatterns, + timeRangeForSuggestionsOverride, + buttonProps, +}: QueryBarMenuProps) { + const [renderedComponent, setRenderedComponent] = useState('menu'); + + useEffect(() => { + if (openQueryBarMenu) { + setRenderedComponent('menu'); + } + }, [openQueryBarMenu]); + + const normalContextMenuPopoverId = useGeneratedHtmlId({ + prefix: 'normalContextMenuPopover', + }); + const onButtonClick = () => { + toggleFilterBarMenuPopover(!openQueryBarMenu); + }; + + const closePopover = () => { + toggleFilterBarMenuPopover(false); + }; + + const buttonLabel = i18n.translate('unifiedSearch.filter.options.filterSetButtonLabel', { + defaultMessage: 'Saved query menu', + }); + + const button = ( + + + + ); + + const panels = QueryBarMenuPanels({ + filters, + savedQuery, + language, + dateRangeFrom, + dateRangeTo, + query, + showSaveQuery, + showFilterBar, + showQueryInput, + savedQueryService, + saveAsNewQueryFormComponent, + manageFilterSetComponent, + nonKqlMode, + closePopover, + onQueryBarSubmit, + onFiltersUpdated, + onClearSavedQuery, + onQueryChange, + setRenderedComponent, + }); + + const renderComponent = () => { + switch (renderedComponent) { + case 'menu': + default: + return ( + + ); + case 'saveForm': + return ( + {saveFormComponent}
]} /> + ); + case 'saveAsNewForm': + return ( + {saveAsNewQueryFormComponent}
]} + /> + ); + case 'addFilter': + return ( + , + ]} + /> + ); + } + }; + + return ( + <> + + {renderComponent()} + + + ); +} diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_menu_panels.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_menu_panels.tsx new file mode 100644 index 0000000000000..548d7f24d5da7 --- /dev/null +++ b/src/plugins/unified_search/public/query_string_input/query_bar_menu_panels.tsx @@ -0,0 +1,500 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState, useRef, useEffect, useCallback } from 'react'; +import { i18n } from '@kbn/i18n'; +import { isEqual } from 'lodash'; +import { + EuiContextMenuPanelDescriptor, + EuiText, + EuiFlexGroup, + EuiFlexItem, + EuiButton, +} from '@elastic/eui'; +import { + Filter, + Query, + enableFilter, + disableFilter, + toggleFilterNegated, + pinFilter, + unpinFilter, +} from '@kbn/es-query'; +import { METRIC_TYPE } from '@kbn/analytics'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { KIBANA_USER_QUERY_LANGUAGE_KEY, UI_SETTINGS } from '@kbn/data-plugin/common'; +import type { + IDataPluginServices, + TimeRange, + SavedQueryService, + SavedQuery, +} from '@kbn/data-plugin/public'; +import { fromUser } from './from_user'; +import { QueryLanguageSwitcher } from './language_switcher'; + +interface QueryBarMenuPanelProps { + filters?: Filter[]; + savedQuery?: SavedQuery; + language: string; + dateRangeFrom?: string; + dateRangeTo?: string; + query?: Query; + showSaveQuery?: boolean; + showQueryInput?: boolean; + showFilterBar?: boolean; + savedQueryService: SavedQueryService; + saveAsNewQueryFormComponent?: JSX.Element; + manageFilterSetComponent?: JSX.Element; + nonKqlMode?: 'lucene' | 'text'; + closePopover: () => void; + onQueryBarSubmit: (payload: { dateRange: TimeRange; query?: Query }) => void; + onFiltersUpdated?: (filters: Filter[]) => void; + onClearSavedQuery?: () => void; + onQueryChange: (payload: { dateRange: TimeRange; query?: Query }) => void; + setRenderedComponent: (component: string) => void; +} + +export function QueryBarMenuPanels({ + filters, + savedQuery, + language, + dateRangeFrom, + dateRangeTo, + query, + showSaveQuery, + showFilterBar, + showQueryInput, + savedQueryService, + saveAsNewQueryFormComponent, + manageFilterSetComponent, + nonKqlMode, + closePopover, + onQueryBarSubmit, + onFiltersUpdated, + onClearSavedQuery, + onQueryChange, + setRenderedComponent, +}: QueryBarMenuPanelProps) { + const kibana = useKibana(); + const { appName, usageCollection, uiSettings, http, storage } = kibana.services; + const reportUiCounter = usageCollection?.reportUiCounter.bind(usageCollection, appName); + const cancelPendingListingRequest = useRef<() => void>(() => {}); + + const [savedQueries, setSavedQueries] = useState([] as SavedQuery[]); + const [hasFiltersOrQuery, setHasFiltersOrQuery] = useState(false); + const [savedQueryHasChanged, setSavedQueryHasChanged] = useState(false); + + useEffect(() => { + const fetchSavedQueries = async () => { + cancelPendingListingRequest.current(); + let requestGotCancelled = false; + cancelPendingListingRequest.current = () => { + requestGotCancelled = true; + }; + + const { queries: savedQueryItems } = await savedQueryService.findSavedQueries(''); + + if (requestGotCancelled) return; + + setSavedQueries(savedQueryItems.reverse().slice(0, 5)); + }; + if (showQueryInput && showFilterBar) { + fetchSavedQueries(); + } + }, [savedQueryService, savedQuery, showQueryInput, showFilterBar]); + + useEffect(() => { + if (savedQuery) { + let filtersHaveChanged = filters?.length !== savedQuery.attributes?.filters?.length; + if (filters?.length === savedQuery.attributes?.filters?.length) { + filtersHaveChanged = Boolean( + filters?.some( + (filter, index) => + !isEqual(filter.query, savedQuery.attributes?.filters?.[index]?.query) + ) + ); + } + if (filtersHaveChanged || !isEqual(query, savedQuery?.attributes.query)) { + setSavedQueryHasChanged(true); + } else { + setSavedQueryHasChanged(false); + } + } + }, [filters, query, savedQuery, savedQuery?.attributes.filters, savedQuery?.attributes.query]); + + useEffect(() => { + const hasFilters = Boolean(filters && filters.length > 0); + const hasQuery = Boolean(query && query.query); + setHasFiltersOrQuery(hasFilters || hasQuery); + }, [filters, onClearSavedQuery, query, savedQuery]); + + const getDateRange = () => { + const defaultTimeSetting = uiSettings!.get(UI_SETTINGS.TIMEPICKER_TIME_DEFAULTS); + return { + from: dateRangeFrom || defaultTimeSetting.from, + to: dateRangeTo || defaultTimeSetting.to, + }; + }; + + const handleSaveAsNew = useCallback(() => { + setRenderedComponent('saveAsNewForm'); + }, [setRenderedComponent]); + + const handleSave = useCallback(() => { + setRenderedComponent('saveForm'); + }, [setRenderedComponent]); + + const onEnableAll = () => { + reportUiCounter?.(METRIC_TYPE.CLICK, `filter:enable_all`); + const enabledFilters = filters?.map(enableFilter); + if (enabledFilters) { + onFiltersUpdated?.(enabledFilters); + } + }; + + const onDisableAll = () => { + reportUiCounter?.(METRIC_TYPE.CLICK, `filter:disable_all`); + const disabledFilters = filters?.map(disableFilter); + if (disabledFilters) { + onFiltersUpdated?.(disabledFilters); + } + }; + + const onToggleAllNegated = () => { + reportUiCounter?.(METRIC_TYPE.CLICK, `filter:invert_all`); + const negatedFilters = filters?.map(toggleFilterNegated); + if (negatedFilters) { + onFiltersUpdated?.(negatedFilters); + } + }; + + const onRemoveAll = () => { + reportUiCounter?.(METRIC_TYPE.CLICK, `filter:remove_all`); + onFiltersUpdated?.([]); + }; + + const onPinAll = () => { + reportUiCounter?.(METRIC_TYPE.CLICK, `filter:pin_all`); + const pinnedFilters = filters?.map(pinFilter); + if (pinnedFilters) { + onFiltersUpdated?.(pinnedFilters); + } + }; + + const onUnpinAll = () => { + reportUiCounter?.(METRIC_TYPE.CLICK, `filter:unpin_all`); + const unPinnedFilters = filters?.map(unpinFilter); + if (unPinnedFilters) { + onFiltersUpdated?.(unPinnedFilters); + } + }; + + const onQueryStringChange = (value: string) => { + onQueryChange({ + query: { query: value, language }, + dateRange: getDateRange(), + }); + }; + + const onSelectLanguage = (lang: string) => { + http.post('/api/kibana/kql_opt_in_stats', { + body: JSON.stringify({ opt_in: lang === 'kuery' }), + }); + + const storageKey = KIBANA_USER_QUERY_LANGUAGE_KEY; + storage.set(storageKey!, lang); + + const newQuery = { query: '', language: lang }; + onQueryStringChange(newQuery.query); + onQueryBarSubmit({ + query: { query: fromUser(newQuery.query), language: newQuery.language }, + dateRange: getDateRange(), + }); + }; + + const luceneLabel = i18n.translate('unifiedSearch.query.queryBar.luceneLanguageName', { + defaultMessage: 'Lucene', + }); + const kqlLabel = i18n.translate('unifiedSearch.query.queryBar.kqlLanguageName', { + defaultMessage: 'KQL', + }); + + const filtersRelatedPanels = [ + { + name: i18n.translate('unifiedSearch.filter.options.addFilterButtonLabel', { + defaultMessage: 'Add filter', + }), + icon: 'plus', + onClick: () => { + setRenderedComponent('addFilter'); + }, + }, + { + name: i18n.translate('unifiedSearch.filter.options.applyAllFiltersButtonLabel', { + defaultMessage: 'Apply to all', + }), + icon: 'filter', + panel: 2, + disabled: !Boolean(filters && filters.length > 0), + 'data-test-subj': 'filter-sets-applyToAllFilters', + }, + ]; + + const queryAndFiltersRelatedPanels = [ + { + name: savedQuery + ? i18n.translate('unifiedSearch.filter.options.loadOtherFilterSetLabel', { + defaultMessage: 'Load other saved query', + }) + : i18n.translate('unifiedSearch.filter.options.loadCurrentFilterSetLabel', { + defaultMessage: 'Load saved query', + }), + panel: 4, + width: 350, + icon: 'filter', + 'data-test-subj': 'saved-query-management-load-button', + disabled: !savedQueries.length, + }, + { + name: savedQuery + ? i18n.translate('unifiedSearch.filter.options.saveAsNewFilterSetLabel', { + defaultMessage: 'Save as new', + }) + : i18n.translate('unifiedSearch.filter.options.saveFilterSetLabel', { + defaultMessage: 'Save saved query', + }), + icon: 'save', + disabled: + !Boolean(showSaveQuery) || !hasFiltersOrQuery || (savedQuery && !savedQueryHasChanged), + panel: 1, + 'data-test-subj': 'saved-query-management-save-button', + }, + { isSeparator: true }, + ]; + + const items = []; + // apply to all actions are only shown when there are filters + if (showFilterBar) { + items.push(...filtersRelatedPanels); + } + // clear all actions are only shown when there are filters or query + if (showFilterBar || showQueryInput) { + items.push( + { + name: i18n.translate('unifiedSearch.filter.options.clearllFiltersButtonLabel', { + defaultMessage: 'Clear all', + }), + disabled: !hasFiltersOrQuery && !Boolean(savedQuery), + icon: 'crossInACircleFilled', + 'data-test-subj': 'filter-sets-removeAllFilters', + onClick: () => { + closePopover(); + onQueryBarSubmit({ + query: { query: '', language }, + dateRange: getDateRange(), + }); + onRemoveAll(); + onClearSavedQuery?.(); + }, + }, + { isSeparator: true } + ); + } + // saved queries actions are only shown when the showQueryInput and showFilterBar is true + if (showQueryInput && showFilterBar) { + items.push(...queryAndFiltersRelatedPanels); + } + + // language menu appears when the showQueryInput is true + if (showQueryInput) { + items.push({ + name: `Language: ${language === 'kuery' ? kqlLabel : luceneLabel}`, + panel: 3, + 'data-test-subj': 'switchQueryLanguageButton', + }); + } + + const panels = [ + { + id: 0, + title: ( + <> + + + + + {savedQuery + ? savedQuery.attributes.title + : i18n.translate('unifiedSearch.search.searchBar.savedQuery', { + defaultMessage: 'Saved query', + })} + + + + {savedQuery && savedQueryHasChanged && Boolean(showSaveQuery) && hasFiltersOrQuery && ( + + + + + {i18n.translate( + 'unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonText', + { + defaultMessage: 'Save changes', + } + )} + + + + + {i18n.translate( + 'unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonText', + { + defaultMessage: 'Save as new', + } + )} + + + + + )} + + + ), + items, + }, + { + id: 1, + title: i18n.translate('unifiedSearch.filter.options.saveCurrentFilterSetLabel', { + defaultMessage: 'Save current saved query', + }), + disabled: !Boolean(showSaveQuery), + content:
{saveAsNewQueryFormComponent}
, + }, + { + id: 2, + initialFocusedItemIndex: 1, + title: i18n.translate('unifiedSearch.filter.options.applyAllFiltersButtonLabel', { + defaultMessage: 'Apply to all', + }), + items: [ + { + name: i18n.translate('unifiedSearch.filter.options.enableAllFiltersButtonLabel', { + defaultMessage: 'Enable all', + }), + icon: 'eye', + 'data-test-subj': 'filter-sets-enableAllFilters', + onClick: () => { + closePopover(); + onEnableAll(); + }, + }, + { + name: i18n.translate('unifiedSearch.filter.options.disableAllFiltersButtonLabel', { + defaultMessage: 'Disable all', + }), + 'data-test-subj': 'filter-sets-disableAllFilters', + icon: 'eyeClosed', + onClick: () => { + closePopover(); + onDisableAll(); + }, + }, + { + name: i18n.translate('unifiedSearch.filter.options.invertNegatedFiltersButtonLabel', { + defaultMessage: 'Invert inclusion', + }), + 'data-test-subj': 'filter-sets-invertAllFilters', + icon: 'invert', + onClick: () => { + closePopover(); + onToggleAllNegated(); + }, + }, + { + name: i18n.translate('unifiedSearch.filter.options.pinAllFiltersButtonLabel', { + defaultMessage: 'Pin all', + }), + 'data-test-subj': 'filter-sets-pinAllFilters', + icon: 'pin', + onClick: () => { + closePopover(); + onPinAll(); + }, + }, + { + name: i18n.translate('unifiedSearch.filter.options.unpinAllFiltersButtonLabel', { + defaultMessage: 'Unpin all', + }), + 'data-test-subj': 'filter-sets-unpinAllFilters', + icon: 'pin', + onClick: () => { + closePopover(); + onUnpinAll(); + }, + }, + ], + }, + { + id: 3, + title: i18n.translate('unifiedSearch.filter.options.filterLanguageLabel', { + defaultMessage: 'Filter language', + }), + content: ( + + ), + }, + { + id: 4, + title: i18n.translate('unifiedSearch.filter.options.loadCurrentFilterSetLabel', { + defaultMessage: 'Load saved query', + }), + width: 400, + content:
{manageFilterSetComponent}
, + }, + ] as EuiContextMenuPanelDescriptor[]; + + return panels; +} diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index de1fa659aa133..0bff12ac78798 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -11,6 +11,7 @@ import classNames from 'classnames'; import React, { useCallback, useMemo, useRef, useState } from 'react'; import deepEqual from 'fast-deep-equal'; import useObservable from 'react-use/lib/useObservable'; +import type { Filter } from '@kbn/es-query'; import { EMPTY } from 'rxjs'; import { map } from 'rxjs/operators'; import { @@ -20,8 +21,9 @@ import { EuiFieldText, usePrettyDuration, EuiIconProps, - EuiSuperUpdateButton, OnRefreshProps, + useIsWithinBreakpoints, + EuiSuperUpdateButton, } from '@elastic/eui'; import { IDataPluginServices, @@ -30,20 +32,21 @@ import { Query, getQueryLog, } from '@kbn/data-plugin/public'; +import { i18n } from '@kbn/i18n'; import { DataView } from '@kbn/data-views-plugin/public'; import type { PersistedLog } from '@kbn/data-plugin/public'; import { useKibana, withKibana } from '@kbn/kibana-react-plugin/public'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; import QueryStringInputUI from './query_string_input'; import { NoDataPopover } from './no_data_popover'; -import { shallowEqual } from '../utils'; +import { shallowEqual } from '../utils/shallow_equal'; +import { AddFilterPopover } from './add_filter_popover'; +import { DataViewPicker, DataViewPickerProps } from '../dataview_picker'; +import { FilterButtonGroup } from '../filter_bar/filter_button_group/filter_button_group'; const SuperDatePicker = React.memo( EuiSuperDatePicker as any ) as unknown as typeof EuiSuperDatePicker; -const SuperUpdateButton = React.memo( - EuiSuperUpdateButton as any -) as unknown as typeof EuiSuperUpdateButton; const QueryStringInput = withKibana(QueryStringInputUI); @@ -63,7 +66,6 @@ export interface QueryBarTopRowProps { isLoading?: boolean; isRefreshPaused?: boolean; nonKqlMode?: 'lucene' | 'text'; - nonKqlModeHelpText?: string; onChange: (payload: { dateRange: TimeRange; query?: Query }) => void; onRefresh?: (payload: { dateRange: TimeRange }) => void; onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; @@ -74,10 +76,18 @@ export interface QueryBarTopRowProps { refreshInterval?: number; screenTitle?: string; showQueryInput?: boolean; + showAddFilter?: boolean; showDatePicker?: boolean; showAutoRefreshOnly?: boolean; timeHistory?: TimeHistoryContract; timeRangeForSuggestionsOverride?: boolean; + filters: Filter[]; + onFiltersUpdated?: (filters: Filter[]) => void; + dataViewPickerComponentProps?: DataViewPickerProps; + filterBar?: React.ReactNode; + showDatePickerAsBadge?: boolean; + showSubmitButton?: boolean; + isScreenshotMode?: boolean; } const SharingMetaFields = React.memo(function SharingMetaFields({ @@ -114,7 +124,13 @@ const SharingMetaFields = React.memo(function SharingMetaFields({ export const QueryBarTopRow = React.memo( function QueryBarTopRow(props: QueryBarTopRowProps) { - const { showQueryInput = true, showDatePicker = true, showAutoRefreshOnly = false } = props; + const isMobile = useIsWithinBreakpoints(['xs', 's']); + const { + showQueryInput = true, + showDatePicker = true, + showAutoRefreshOnly = false, + showSubmitButton = true, + } = props; const [isDateRangeInvalid, setIsDateRangeInvalid] = useState(false); const [isQueryInputFocused, setIsQueryInputFocused] = useState(false); @@ -283,14 +299,27 @@ export const QueryBarTopRow = React.memo( return Boolean(showDatePicker || showAutoRefreshOnly); } + function renderFilterMenuOnly(): boolean { + return !Boolean(props.showAddFilter) && Boolean(props.prepend); + } + + function shouldRenderUpdatebutton(): boolean { + return ( + Boolean(showSubmitButton) && + Boolean(showQueryInput || showDatePicker || showAutoRefreshOnly) + ); + } + + function shouldShowDatePickerAsBadge(): boolean { + return Boolean(props.showDatePickerAsBadge) && !shouldRenderQueryInput(); + } + function renderDatePicker() { if (!shouldRenderDatePicker()) { return null; } - const wrapperClasses = classNames('kbnQueryBar__datePickerWrapper', { - 'kbnQueryBar__datePickerWrapper-isHidden': isQueryInputFocused, - }); + const wrapperClasses = classNames('kbnQueryBar__datePickerWrapper'); return ( @@ -308,23 +337,52 @@ export const QueryBarTopRow = React.memo( dateFormat={uiSettings.get('dateFormat')} isAutoRefreshOnly={showAutoRefreshOnly} className="kbnQueryBar__datePicker" + isQuickSelectOnly={isMobile ? false : isQueryInputFocused} + width={isMobile ? 'full' : 'auto'} + compressed={shouldShowDatePickerAsBadge()} /> ); } function renderUpdateButton() { + if (!shouldRenderUpdatebutton()) { + return null; + } + + const buttonLabelUpdate = i18n.translate('unifiedSearch.queryBarTopRow.submitButton.update', { + defaultMessage: 'Needs updating', + }); + const buttonLabelRefresh = i18n.translate( + 'unifiedSearch.queryBarTopRow.submitButton.refresh', + { + defaultMessage: 'Refresh query', + } + ); + const button = props.customSubmitButton ? ( React.cloneElement(props.customSubmitButton, { onClick: onClickSubmitButton }) ) : ( - + + + ); if (!shouldRenderDatePicker()) { @@ -332,61 +390,124 @@ export const QueryBarTopRow = React.memo( } return ( - - - {renderDatePicker()} - {button} - - + + + + {renderDatePicker()} + {button} + + + ); } - function renderQueryInput() { - if (!shouldRenderQueryInput()) return; + function renderDataViewsPicker() { + if (!props.dataViewPickerComponentProps) return; return ( - - + ); } - const classes = classNames('kbnQueryBar', { - 'kbnQueryBar--withDatePicker': showDatePicker, - }); + function renderAddButton() { + return ( + Boolean(props.showAddFilter) && ( + + + + ) + ); + } + + function renderFilterButtonGroup() { + return ( + (Boolean(props.showAddFilter) || Boolean(props.prepend)) && ( + + + + ) + ); + } + + function renderQueryInput() { + return ( + + {!renderFilterMenuOnly() && renderFilterButtonGroup()} + {shouldRenderQueryInput() && ( + + + + )} + + ); + } + + const isScreenshotMode = props.isScreenshotMode === true; return ( - - {renderQueryInput()} + <> - {renderUpdateButton()} - + {!isScreenshotMode && ( + <> + + {renderDataViewsPicker()} + + {renderQueryInput()} + + {shouldShowDatePickerAsBadge() && props.filterBar} + {renderUpdateButton()} + + {!shouldShowDatePickerAsBadge() && props.filterBar} + + )} + ); }, ({ query: prevQuery, ...prevProps }, { query: nextQuery, ...nextProps }) => { diff --git a/src/plugins/unified_search/public/query_string_input/query_string_input.test.tsx b/src/plugins/unified_search/public/query_string_input/query_string_input.test.tsx index b4eed13da7f58..7437bf5fd4ece 100644 --- a/src/plugins/unified_search/public/query_string_input/query_string_input.test.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_string_input.test.tsx @@ -110,7 +110,6 @@ describe('QueryStringInput', () => { ); await waitFor(() => getByText(kqlQuery.query)); - await waitFor(() => getByText('KQL')); }); it('Should pass the query language to the language switcher', () => { diff --git a/src/plugins/unified_search/public/query_string_input/query_string_input.tsx b/src/plugins/unified_search/public/query_string_input/query_string_input.tsx index a9f4127809ab7..31ff302f9e699 100644 --- a/src/plugins/unified_search/public/query_string_input/query_string_input.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_string_input.tsx @@ -24,9 +24,10 @@ import { EuiTextArea, htmlIdGenerator, PopoverAnchorPosition, + toSentenceCase, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { compact, debounce, isEqual, isFunction } from 'lodash'; +import { compact, debounce, isEmpty, isEqual, isFunction } from 'lodash'; import { Toast } from '@kbn/core/public'; import { IDataPluginServices, Query, getQueryLog } from '@kbn/data-plugin/public'; import { DataView } from '@kbn/data-views-plugin/public'; @@ -41,6 +42,7 @@ import { QueryLanguageSwitcher } from './language_switcher'; import type { SuggestionsListSize } from '../typeahead/suggestions_component'; import { SuggestionsComponent } from '../typeahead'; import { onRaf } from '../utils'; +import { FilterButtonGroup } from '../filter_bar/filter_button_group/filter_button_group'; import { QuerySuggestion, QuerySuggestionTypes } from '../autocomplete'; import { getTheme, getAutocomplete } from '../services'; @@ -72,7 +74,6 @@ export interface QueryStringInputProps { * this params add another option text, which is just a simple keyword search mode, the way a simple search box works */ nonKqlMode?: 'lucene' | 'text'; - nonKqlModeHelpText?: string; /** * @param autoSubmit if user selects a value, in that case kuery will be auto submitted */ @@ -124,6 +125,8 @@ const KEY_CODES = { export default class QueryStringInputUI extends PureComponent { static defaultProps = { storageKey: KIBANA_USER_QUERY_LANGUAGE_KEY, + iconType: 'search', + isClearable: true, }; public state: State = { @@ -678,31 +681,59 @@ export default class QueryStringInputUI extends PureComponent { this.handleAutoHeight(); }; + getSearchInputPlaceholder = () => { + let placeholder = ''; + if (!this.props.query.language || this.props.query.language === 'text') { + placeholder = i18n.translate('unifiedSearch.query.queryBar.searchInputPlaceholderForText', { + defaultMessage: 'Filter your data', + }); + } else { + const language = + this.props.query.language === 'kuery' ? 'KQL' : toSentenceCase(this.props.query.language); + + placeholder = i18n.translate('unifiedSearch.query.queryBar.searchInputPlaceholder', { + defaultMessage: 'Filter your data using {language} syntax', + values: { language }, + }); + } + + return placeholder; + }; + public render() { const isSuggestionsVisible = this.state.isSuggestionsVisible && { 'aria-controls': 'kbnTypeahead__items', 'aria-owns': 'kbnTypeahead__items', }; const ariaCombobox = { ...isSuggestionsVisible, role: 'combobox' }; - const containerClassName = classNames( - 'euiFormControlLayout euiFormControlLayout--group kbnQueryBar__wrap', - this.props.className - ); - const inputClassName = classNames( - 'kbnQueryBar__textarea', - this.props.iconType ? 'kbnQueryBar__textarea--withIcon' : null, - this.props.prepend ? 'kbnQueryBar__textarea--hasPrepend' : null, - !this.props.disableLanguageSwitcher ? 'kbnQueryBar__textarea--hasAppend' : null - ); - const inputWrapClassName = classNames( - 'euiFormControlLayout__childrenWrapper kbnQueryBar__textareaWrap', - this.props.prepend ? 'kbnQueryBar__textareaWrap--hasPrepend' : null, - !this.props.disableLanguageSwitcher ? 'kbnQueryBar__textareaWrap--hasAppend' : null + + const simpleLanguageSwitcher = this.props.disableLanguageSwitcher ? null : ( + ); + const prependElement = + this.props.prepend || simpleLanguageSwitcher ? ( + + ) : undefined; + + const containerClassName = classNames('kbnQueryBar__wrap', this.props.className); + const inputClassName = classNames('kbnQueryBar__textarea', { + 'kbnQueryBar__textarea--withIcon': this.props.iconType, + 'kbnQueryBar__textarea--isClearable': this.props.isClearable, + 'kbnQueryBar__textarea--withPrepend': prependElement, + 'kbnQueryBar__textarea--isSuggestionsVisible': + isSuggestionsVisible && !isEmpty(this.state.suggestions), + }); + const inputWrapClassName = classNames('kbnQueryBar__textareaWrap'); return (
- {this.props.prepend} + {prependElement} +
{ >
{
- {this.props.disableLanguageSwitcher ? null : ( - - )}
); } diff --git a/src/plugins/unified_search/public/saved_query_form/save_query_form.tsx b/src/plugins/unified_search/public/saved_query_form/save_query_form.tsx index 53c5ec3310da2..008c61b909ce1 100644 --- a/src/plugins/unified_search/public/saved_query_form/save_query_form.tsx +++ b/src/plugins/unified_search/public/saved_query_form/save_query_form.tsx @@ -7,20 +7,7 @@ */ import React, { useEffect, useState, useCallback } from 'react'; -import { - EuiButtonEmpty, - EuiModal, - EuiButton, - EuiModalHeader, - EuiModalHeaderTitle, - EuiModalBody, - EuiModalFooter, - EuiForm, - EuiFormRow, - EuiFieldText, - EuiSwitch, - EuiText, -} from '@elastic/eui'; +import { EuiButton, EuiForm, EuiFormRow, EuiFieldText, EuiSwitch } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { sortBy, isEqual } from 'lodash'; import { SavedQuery, SavedQueryService } from '@kbn/data-plugin/public'; @@ -51,8 +38,6 @@ export function SaveQueryForm({ showTimeFilterOption = true, }: Props) { const [title, setTitle] = useState(savedQuery?.attributes.title ?? ''); - const [enabledSaveButton, setEnabledSaveButton] = useState(Boolean(savedQuery)); - const [description, setDescription] = useState(savedQuery?.attributes.description ?? ''); const [savedQueries, setSavedQueries] = useState([]); const [shouldIncludeFilters, setShouldIncludeFilters] = useState( Boolean(savedQuery?.attributes.filters ?? true) @@ -72,10 +57,10 @@ export function SaveQueryForm({ } ); - const savedQueryDescriptionText = i18n.translate( - 'unifiedSearch.search.searchBar.savedQueryDescriptionText', + const titleExistsErrorText = i18n.translate( + 'unifiedSearch.search.searchBar.savedQueryForm.titleExistsText', { - defaultMessage: 'Save query text and filters that you want to use again.', + defaultMessage: 'Name is required.', } ); @@ -98,36 +83,40 @@ export function SaveQueryForm({ errors.push(titleConflictErrorText); } + if (!title) { + errors.push(titleExistsErrorText); + } + if (!isEqual(errors, formErrors)) { setFormErrors(errors); return false; } return !formErrors.length; - }, [savedQueries, savedQuery, title, titleConflictErrorText, formErrors]); + }, [savedQueries, formErrors, title, savedQuery, titleConflictErrorText, titleExistsErrorText]); const onClickSave = useCallback(() => { if (validate()) { onSave({ id: savedQuery?.id, title, - description, + description: '', shouldIncludeFilters, shouldIncludeTimefilter, }); + onClose(); } }, [ validate, onSave, + onClose, savedQuery?.id, title, - description, shouldIncludeFilters, shouldIncludeTimefilter, ]); const onInputChange = useCallback((event) => { - setEnabledSaveButton(Boolean(event.target.value)); setFormErrors([]); setTitle(event.target.value); }, []); @@ -143,18 +132,16 @@ export function SaveQueryForm({ const saveQueryForm = ( - - {savedQueryDescriptionText} - - - { - setDescription(event.target.value); - }} - data-test-subj="saveQueryFormDescription" - /> - {showFilterOption && ( - + + )} - - ); - - return ( - - - - {i18n.translate('unifiedSearch.search.searchBar.savedQueryFormTitle', { - defaultMessage: 'Save query', - })} - - - - {saveQueryForm} - - - - {i18n.translate('unifiedSearch.search.searchBar.savedQueryFormCancelButtonText', { - defaultMessage: 'Cancel', - })} - + {i18n.translate('unifiedSearch.search.searchBar.savedQueryFormSaveButtonText', { - defaultMessage: 'Save', + defaultMessage: 'Save saved query', })} - - + + ); + + return <>{saveQueryForm}; } diff --git a/src/plugins/unified_search/public/saved_query_management/_index.scss b/src/plugins/unified_search/public/saved_query_management/_index.scss index 0580e857e8494..0c90d7817b685 100644 --- a/src/plugins/unified_search/public/saved_query_management/_index.scss +++ b/src/plugins/unified_search/public/saved_query_management/_index.scss @@ -1,2 +1 @@ -@import './saved_query_management_component'; -@import './saved_query_list_item'; +@import './saved_query_management_list'; diff --git a/src/plugins/unified_search/public/saved_query_management/_saved_query_list_item.scss b/src/plugins/unified_search/public/saved_query_management/_saved_query_list_item.scss deleted file mode 100644 index 714ba82dfb476..0000000000000 --- a/src/plugins/unified_search/public/saved_query_management/_saved_query_list_item.scss +++ /dev/null @@ -1,21 +0,0 @@ -.kbnSavedQueryListItem { - margin-top: 0; - color: $euiLinkColor; -} - -// Can't actually target the button with classes, but styles to override -// are just user agent styles -.kbnSavedQueryListItem-selected button { - font-weight: $euiFontWeightBold; -} - -// This will ensure the info icon and tooltip shows even if the label gets truncated -.kbnSavedQueryListItem__label { - display: flex; - align-items: center; -} - -.kbnSavedQueryListItem__labelText { - @include euiTextTruncate; - margin-right: $euiSizeXS; -} diff --git a/src/plugins/unified_search/public/saved_query_management/_saved_query_management_component.scss b/src/plugins/unified_search/public/saved_query_management/_saved_query_management_component.scss deleted file mode 100644 index 928cb5a34d6de..0000000000000 --- a/src/plugins/unified_search/public/saved_query_management/_saved_query_management_component.scss +++ /dev/null @@ -1,26 +0,0 @@ -.kbnSavedQueryManagement__popover { - max-width: $euiFormMaxWidth; -} - -.kbnSavedQueryManagement__listWrapper { - // Addition height will ensure one item is "cutoff" to indicate more below the scroll - max-height: $euiFormMaxWidth + $euiSize; - overflow-y: hidden; -} - -.kbnSavedQueryManagement__pagination { - justify-content: center; - padding: ($euiSizeM / 2) $euiSizeM $euiSizeM; -} - -.kbnSavedQueryManagement__text { - padding: $euiSizeM $euiSizeM ($euiSizeM / 2) $euiSizeM; -} - -.kbnSavedQueryManagement__list { - @include euiYScrollWithShadows; - max-height: inherit; // Fixes overflow for applied max-height - // Left/Right padding is calculated to match the left alignment of the - // popover text and buttons - padding: ($euiSizeM / 2) $euiSizeXS !important; // Override flush -} diff --git a/src/plugins/unified_search/public/saved_query_management/_saved_query_management_list.scss b/src/plugins/unified_search/public/saved_query_management/_saved_query_management_list.scss new file mode 100644 index 0000000000000..7ce304310ae56 --- /dev/null +++ b/src/plugins/unified_search/public/saved_query_management/_saved_query_management_list.scss @@ -0,0 +1,17 @@ +.kbnSavedQueryManagement__listWrapper { + // Addition height will ensure one item is "cutoff" to indicate more below the scroll + max-height: $euiFormMaxWidth + $euiSize; + overflow-y: hidden; +} + +.kbnSavedQueryManagement__text { + padding: $euiSizeM $euiSizeM ($euiSizeM / 2) $euiSizeM; +} + +.kbnSavedQueryManagement__list { + @include euiYScrollWithShadows; + max-height: inherit; // Fixes overflow for applied max-height + // Left/Right padding is calculated to match the left alignment of the + // popover text and buttons + padding: ($euiSizeM / 2) $euiSizeXS !important; // Override flush +} diff --git a/src/plugins/unified_search/public/saved_query_management/index.ts b/src/plugins/unified_search/public/saved_query_management/index.ts index 4ead1907cd23b..134b24a4fb85c 100644 --- a/src/plugins/unified_search/public/saved_query_management/index.ts +++ b/src/plugins/unified_search/public/saved_query_management/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { SavedQueryManagementComponent } from './saved_query_management_component'; +export { SavedQueryManagementList } from './saved_query_management_list'; diff --git a/src/plugins/unified_search/public/saved_query_management/saved_query_list_item.tsx b/src/plugins/unified_search/public/saved_query_management/saved_query_list_item.tsx deleted file mode 100644 index 71fbd8aad6e48..0000000000000 --- a/src/plugins/unified_search/public/saved_query_management/saved_query_list_item.tsx +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { EuiListGroupItem, EuiConfirmModal, EuiIconTip } from '@elastic/eui'; - -import React, { Fragment, useState } from 'react'; -import classNames from 'classnames'; -import { i18n } from '@kbn/i18n'; -import { SavedQuery } from '@kbn/data-plugin/public'; - -interface Props { - savedQuery: SavedQuery; - isSelected: boolean; - showWriteOperations: boolean; - onSelect: (savedQuery: SavedQuery) => void; - onDelete: (savedQuery: SavedQuery) => void; -} - -export const SavedQueryListItem = ({ - savedQuery, - isSelected, - onSelect, - onDelete, - showWriteOperations, -}: Props) => { - const [showDeletionConfirmationModal, setShowDeletionConfirmationModal] = useState(false); - - const selectButtonAriaLabelText = isSelected - ? i18n.translate( - 'unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemSelectedButtonAriaLabel', - { - defaultMessage: - 'Saved query button selected {savedQueryName}. Press to clear any changes.', - values: { savedQueryName: savedQuery.attributes.title }, - } - ) - : i18n.translate( - 'unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemButtonAriaLabel', - { - defaultMessage: 'Saved query button {savedQueryName}', - values: { savedQueryName: savedQuery.attributes.title }, - } - ); - - const selectButtonDataTestSubj = isSelected - ? `load-saved-query-${savedQuery.attributes.title}-button saved-query-list-item-selected` - : `load-saved-query-${savedQuery.attributes.title}-button`; - - const classes = classNames('kbnSavedQueryListItem', { - 'kbnSavedQueryListItem-selected': isSelected, - }); - - const label = ( - - {savedQuery.attributes.title}{' '} - {savedQuery.attributes.description && ( - - )} - - ); - - return ( - - { - onSelect(savedQuery); - }} - aria-label={selectButtonAriaLabelText} - label={label} - iconType={isSelected ? 'check' : undefined} - extraAction={ - showWriteOperations - ? { - color: 'danger', - onClick: () => setShowDeletionConfirmationModal(true), - iconType: 'trash', - iconSize: 's', - 'aria-label': i18n.translate( - 'unifiedSearch.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel', - { - defaultMessage: 'Delete saved query {savedQueryName}', - values: { savedQueryName: savedQuery.attributes.title }, - } - ), - title: i18n.translate( - 'unifiedSearch.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel', - { - defaultMessage: 'Delete saved query {savedQueryName}', - values: { savedQueryName: savedQuery.attributes.title }, - } - ), - 'data-test-subj': `delete-saved-query-${savedQuery.attributes.title}-button`, - } - : undefined - } - /> - - {showDeletionConfirmationModal && ( - { - onDelete(savedQuery); - setShowDeletionConfirmationModal(false); - }} - buttonColor="danger" - onCancel={() => { - setShowDeletionConfirmationModal(false); - }} - /> - )} - - ); -}; diff --git a/src/plugins/unified_search/public/saved_query_management/saved_query_management_component.tsx b/src/plugins/unified_search/public/saved_query_management/saved_query_management_component.tsx deleted file mode 100644 index 07d3a9d799a66..0000000000000 --- a/src/plugins/unified_search/public/saved_query_management/saved_query_management_component.tsx +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { - EuiPopover, - EuiPopoverTitle, - EuiPopoverFooter, - EuiButtonEmpty, - EuiButton, - EuiFlexGroup, - EuiFlexItem, - EuiListGroup, - EuiPagination, - EuiText, - EuiSpacer, - EuiIcon, -} from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; -import React, { useCallback, useEffect, useState, Fragment, useRef } from 'react'; -import { sortBy } from 'lodash'; -import { SavedQuery, SavedQueryService } from '@kbn/data-plugin/public'; -import { SavedQueryListItem } from './saved_query_list_item'; - -const perPage = 50; -interface Props { - showSaveQuery?: boolean; - loadedSavedQuery?: SavedQuery; - savedQueryService: SavedQueryService; - onSave: () => void; - onSaveAsNew: () => void; - onLoad: (savedQuery: SavedQuery) => void; - onClearSavedQuery: () => void; -} - -export function SavedQueryManagementComponent({ - showSaveQuery, - loadedSavedQuery, - onSave, - onSaveAsNew, - onLoad, - onClearSavedQuery, - savedQueryService, -}: Props) { - const [isOpen, setIsOpen] = useState(false); - const [savedQueries, setSavedQueries] = useState([] as SavedQuery[]); - const [count, setTotalCount] = useState(0); - const [activePage, setActivePage] = useState(0); - const cancelPendingListingRequest = useRef<() => void>(() => {}); - - useEffect(() => { - const fetchCountAndSavedQueries = async () => { - cancelPendingListingRequest.current(); - let requestGotCancelled = false; - cancelPendingListingRequest.current = () => { - requestGotCancelled = true; - }; - - const { total: savedQueryCount, queries: savedQueryItems } = - await savedQueryService.findSavedQueries('', perPage, activePage + 1); - - if (requestGotCancelled) return; - - const sortedSavedQueryItems = sortBy(savedQueryItems, 'attributes.title'); - setTotalCount(savedQueryCount); - setSavedQueries(sortedSavedQueryItems); - }; - if (isOpen) { - fetchCountAndSavedQueries(); - } - }, [isOpen, activePage, savedQueryService]); - - const handleTogglePopover = useCallback( - () => setIsOpen((currentState) => !currentState), - [setIsOpen] - ); - - const handleClosePopover = useCallback(() => setIsOpen(false), []); - - const handleSave = useCallback(() => { - handleClosePopover(); - onSave(); - }, [handleClosePopover, onSave]); - - const handleSaveAsNew = useCallback(() => { - handleClosePopover(); - onSaveAsNew(); - }, [handleClosePopover, onSaveAsNew]); - - const handleSelect = useCallback( - (savedQueryToSelect) => { - handleClosePopover(); - onLoad(savedQueryToSelect); - }, - [handleClosePopover, onLoad] - ); - - const handleDelete = useCallback( - (savedQueryToDelete: SavedQuery) => { - const onDeleteSavedQuery = async (savedQuery: SavedQuery) => { - cancelPendingListingRequest.current(); - setSavedQueries( - savedQueries.filter((currentSavedQuery) => currentSavedQuery.id !== savedQuery.id) - ); - - if (loadedSavedQuery && loadedSavedQuery.id === savedQuery.id) { - onClearSavedQuery(); - } - - await savedQueryService.deleteSavedQuery(savedQuery.id); - setActivePage(0); - }; - - onDeleteSavedQuery(savedQueryToDelete); - handleClosePopover(); - }, - [handleClosePopover, loadedSavedQuery, onClearSavedQuery, savedQueries, savedQueryService] - ); - - const savedQueryDescriptionText = i18n.translate( - 'unifiedSearch.search.searchBar.savedQueryDescriptionText', - { - defaultMessage: 'Save query text and filters that you want to use again.', - } - ); - - const noSavedQueriesDescriptionText = - i18n.translate('unifiedSearch.search.searchBar.savedQueryNoSavedQueriesText', { - defaultMessage: 'There are no saved queries.', - }) + - ' ' + - savedQueryDescriptionText; - - const savedQueryPopoverTitleText = i18n.translate( - 'unifiedSearch.search.searchBar.savedQueryPopoverTitleText', - { - defaultMessage: 'Saved Queries', - } - ); - - const goToPage = (pageNumber: number) => { - setActivePage(pageNumber); - }; - - const savedQueryPopoverButton = ( - - - - - ); - - const savedQueryRows = () => { - const savedQueriesWithoutCurrent = savedQueries.filter((savedQuery) => { - if (!loadedSavedQuery) return true; - return savedQuery.id !== loadedSavedQuery.id; - }); - const savedQueriesReordered = - loadedSavedQuery && savedQueriesWithoutCurrent.length !== savedQueries.length - ? [loadedSavedQuery, ...savedQueriesWithoutCurrent] - : [...savedQueriesWithoutCurrent]; - return savedQueriesReordered.map((savedQuery) => ( - - )); - }; - - return ( - - -
- - {savedQueryPopoverTitleText} - - {savedQueries.length > 0 ? ( - - -

{savedQueryDescriptionText}

-
-
- - {savedQueryRows()} - -
- -
- ) : ( - - -

{noSavedQueriesDescriptionText}

-
- -
- )} - - - {showSaveQuery && loadedSavedQuery && ( - - - - {i18n.translate( - 'unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonText', - { - defaultMessage: 'Save changes', - } - )} - - - - - {i18n.translate( - 'unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonText', - { - defaultMessage: 'Save as new', - } - )} - - - - )} - {showSaveQuery && !loadedSavedQuery && ( - - - {i18n.translate( - 'unifiedSearch.search.searchBar.savedQueryPopoverSaveButtonText', - { - defaultMessage: 'Save current query', - } - )} - - - )} - - - {loadedSavedQuery && ( - - {i18n.translate( - 'unifiedSearch.search.searchBar.savedQueryPopoverClearButtonText', - { - defaultMessage: 'Clear', - } - )} - - )} - - - -
-
-
- ); -} diff --git a/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.test.tsx b/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.test.tsx new file mode 100644 index 0000000000000..c7db17ea934d5 --- /dev/null +++ b/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.test.tsx @@ -0,0 +1,165 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { EuiSelectable } from '@elastic/eui'; +import { I18nProvider } from '@kbn/i18n-react'; +import { act } from 'react-dom/test-utils'; +import { findTestSubject } from '@elastic/eui/lib/test'; +import { mountWithIntl as mount } from '@kbn/test-jest-helpers'; +import { ReactWrapper } from 'enzyme'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { coreMock, applicationServiceMock } from '@kbn/core/public/mocks'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { + SavedQueryManagementListProps, + SavedQueryManagementList, +} from './saved_query_management_list'; + +describe('Saved query management list component', () => { + const startMock = coreMock.createStart(); + const dataMock = dataPluginMock.createStartContract(); + const applicationMock = applicationServiceMock.createStartContract(); + const application = { + ...applicationMock, + capabilities: { + ...applicationMock.capabilities, + savedObjectsManagement: { edit: true }, + }, + }; + function wrapSavedQueriesListComponentInContext(testProps: SavedQueryManagementListProps) { + const services = { + uiSettings: startMock.uiSettings, + http: startMock.http, + application, + }; + + return ( + + + + + + ); + } + + function flushEffect(component: ReactWrapper) { + return act(async () => { + await component; + await new Promise((r) => setImmediate(r)); + component.update(); + }); + } + let props: SavedQueryManagementListProps; + beforeEach(() => { + props = { + onLoad: jest.fn(), + onClearSavedQuery: jest.fn(), + onClose: jest.fn(), + showSaveQuery: true, + hasFiltersOrQuery: false, + savedQueryService: { + ...dataMock.query.savedQueries, + findSavedQueries: jest.fn().mockResolvedValue({ + queries: [ + { + id: '8a0b7cd0-b0c4-11ec-92b2-73d62e0d28a9', + attributes: { + title: 'Test', + description: '', + query: { + query: 'category.keyword : "Men\'s Shoes" ', + language: 'kuery', + }, + filters: [], + }, + }, + ], + }), + deleteSavedQuery: jest.fn(), + }, + }; + }); + it('should render the list component if saved queries exist', async () => { + const component = mount(wrapSavedQueriesListComponentInContext(props)); + await flushEffect(component); + expect(component.find('[data-test-subj="saved-query-management-list"]').length).toBe(1); + }); + + it('should not rendet the list component if not saved queries exist', async () => { + const newProps = { + ...props, + savedQueryService: { + ...dataMock.query.savedQueries, + findSavedQueries: jest.fn().mockResolvedValue({ + queries: [], + }), + }, + }; + const component = mount(wrapSavedQueriesListComponentInContext(newProps)); + await flushEffect(component); + expect(component.find('[data-test-subj="saved-query-management-empty"]').length).toBeTruthy(); + }); + + it('should render the saved queries on the selectable component', async () => { + const component = mount(wrapSavedQueriesListComponentInContext(props)); + await flushEffect(component); + expect(component.find(EuiSelectable).prop('options').length).toBe(1); + expect(component.find(EuiSelectable).prop('options')[0].label).toBe('Test'); + }); + + it('should call the onLoad function', async () => { + const onLoadSpy = jest.fn(); + const newProps = { + ...props, + onLoad: onLoadSpy, + }; + const component = mount(wrapSavedQueriesListComponentInContext(newProps)); + await flushEffect(component); + component.find('[data-test-subj="load-saved-query-Test-button"]').first().simulate('click'); + expect( + component.find('[data-test-subj="saved-query-management-apply-changes-button"]').length + ).toBeTruthy(); + component + .find('[data-test-subj="saved-query-management-apply-changes-button"]') + .first() + .simulate('click'); + expect(onLoadSpy).toBeCalled(); + }); + + it('should render the button with the correct text', async () => { + const component = mount(wrapSavedQueriesListComponentInContext(props)); + await flushEffect(component); + expect( + component + .find('[data-test-subj="saved-query-management-apply-changes-button"]') + .first() + .text() + ).toBe('Apply saved query'); + + const newProps = { + ...props, + hasFiltersOrQuery: true, + }; + const updatedComponent = mount(wrapSavedQueriesListComponentInContext(newProps)); + await flushEffect(component); + expect( + updatedComponent + .find('[data-test-subj="saved-query-management-apply-changes-button"]') + .first() + .text() + ).toBe('Replace with selected saved query'); + }); + + it('should render the modal on delete', async () => { + const component = mount(wrapSavedQueriesListComponentInContext(props)); + await flushEffect(component); + findTestSubject(component, 'delete-saved-query-Test-button').simulate('click'); + expect(component.find('[data-test-subj="confirmModalConfirmButton"]').length).toBeTruthy(); + }); +}); diff --git a/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.tsx b/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.tsx new file mode 100644 index 0000000000000..127aa804f77f8 --- /dev/null +++ b/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.tsx @@ -0,0 +1,385 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPanel, + EuiSelectable, + EuiText, + EuiPopoverFooter, + EuiButtonIcon, + EuiButtonEmpty, + EuiConfirmModal, + usePrettyDuration, + ShortDate, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import React, { useCallback, useEffect, useState, useRef } from 'react'; +import { css } from '@emotion/react'; +import { sortBy } from 'lodash'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { IDataPluginServices, SavedQuery, SavedQueryService } from '@kbn/data-plugin/public'; +import type { SavedQueryAttributes } from '@kbn/data-plugin/common'; + +export interface SavedQueryManagementListProps { + showSaveQuery?: boolean; + loadedSavedQuery?: SavedQuery; + savedQueryService: SavedQueryService; + onLoad: (savedQuery: SavedQuery) => void; + onClearSavedQuery: () => void; + onClose: () => void; + hasFiltersOrQuery: boolean; +} + +interface SelectableProps { + key?: string; + label: string; + value?: string; + checked?: 'on' | 'off' | undefined; +} + +interface DurationRange { + end: ShortDate; + label?: string; + start: ShortDate; +} + +const commonDurationRanges: DurationRange[] = [ + { start: 'now/d', end: 'now/d', label: 'Today' }, + { start: 'now/w', end: 'now/w', label: 'This week' }, + { start: 'now/M', end: 'now/M', label: 'This month' }, + { start: 'now/y', end: 'now/y', label: 'This year' }, + { start: 'now-1d/d', end: 'now-1d/d', label: 'Yesterday' }, + { start: 'now/w', end: 'now', label: 'Week to date' }, + { start: 'now/M', end: 'now', label: 'Month to date' }, + { start: 'now/y', end: 'now', label: 'Year to date' }, +]; + +const itemTitle = (attributes: SavedQueryAttributes, format: string) => { + let label = attributes.title; + const prettifier = usePrettyDuration; + + if (attributes.description) { + label += `; ${attributes.description}`; + } + + if (attributes.timefilter) { + label += `; ${prettifier({ + timeFrom: attributes.timefilter?.from, + timeTo: attributes.timefilter?.to, + quickRanges: commonDurationRanges, + dateFormat: format, + })}`; + } + + return label; +}; + +const itemLabel = (attributes: SavedQueryAttributes) => { + let label: React.ReactNode = attributes.title; + + if (attributes.description) { + label = ( + <> + {label} + + ); + } + + if (attributes.timefilter) { + label = ( + <> + {label} + + ); + } + + return label; +}; + +export function SavedQueryManagementList({ + showSaveQuery, + loadedSavedQuery, + onLoad, + onClearSavedQuery, + savedQueryService, + onClose, + hasFiltersOrQuery, +}: SavedQueryManagementListProps) { + const kibana = useKibana(); + const [savedQueries, setSavedQueries] = useState([] as SavedQuery[]); + const [selectedSavedQuery, setSelectedSavedQuery] = useState(null as SavedQuery | null); + const [toBeDeletedSavedQuery, setToBeDeletedSavedQuery] = useState(null as SavedQuery | null); + const [showDeletionConfirmationModal, setShowDeletionConfirmationModal] = useState(false); + const cancelPendingListingRequest = useRef<() => void>(() => {}); + const { uiSettings, http, application } = kibana.services; + const format = uiSettings.get('dateFormat'); + + useEffect(() => { + const fetchCountAndSavedQueries = async () => { + cancelPendingListingRequest.current(); + let requestGotCancelled = false; + cancelPendingListingRequest.current = () => { + requestGotCancelled = true; + }; + + const { queries: savedQueryItems } = await savedQueryService.findSavedQueries(); + + if (requestGotCancelled) return; + + const sortedSavedQueryItems = sortBy(savedQueryItems, 'attributes.title'); + setSavedQueries(sortedSavedQueryItems); + }; + fetchCountAndSavedQueries(); + }, [savedQueryService]); + + const handleLoad = useCallback(() => { + if (selectedSavedQuery) { + onLoad(selectedSavedQuery); + onClose(); + } + }, [onLoad, selectedSavedQuery, onClose]); + + const handleSelect = useCallback((savedQueryToSelect) => { + setSelectedSavedQuery(savedQueryToSelect); + }, []); + + const handleDelete = useCallback((savedQueryToDelete: SavedQuery) => { + setShowDeletionConfirmationModal(true); + setToBeDeletedSavedQuery(savedQueryToDelete); + }, []); + + const onDelete = useCallback( + (savedQueryToDelete: string) => { + const onDeleteSavedQuery = async (savedQueryId: string) => { + cancelPendingListingRequest.current(); + setSavedQueries( + savedQueries.filter((currentSavedQuery) => currentSavedQuery.id !== savedQueryId) + ); + + if (loadedSavedQuery && loadedSavedQuery.id === savedQueryId) { + onClearSavedQuery(); + } + + await savedQueryService.deleteSavedQuery(savedQueryId); + }; + + onDeleteSavedQuery(savedQueryToDelete); + }, + [loadedSavedQuery, onClearSavedQuery, savedQueries, savedQueryService] + ); + + const savedQueryDescriptionText = i18n.translate( + 'unifiedSearch.search.searchBar.savedQueryDescriptionText', + { + defaultMessage: 'Save query text and filters that you want to use again.', + } + ); + + const noSavedQueriesDescriptionText = + i18n.translate('unifiedSearch.search.searchBar.savedQueryNoSavedQueriesText', { + defaultMessage: 'No saved queries.', + }) + + ' ' + + savedQueryDescriptionText; + + const savedQueriesOptions = () => { + const savedQueriesWithoutCurrent = savedQueries.filter((savedQuery) => { + if (!loadedSavedQuery) return true; + return savedQuery.id !== loadedSavedQuery.id; + }); + const savedQueriesReordered = + loadedSavedQuery && savedQueriesWithoutCurrent.length !== savedQueries.length + ? [loadedSavedQuery, ...savedQueriesWithoutCurrent] + : [...savedQueriesWithoutCurrent]; + + return savedQueriesReordered.map((savedQuery) => { + return { + key: savedQuery.id, + label: itemLabel(savedQuery.attributes), + title: itemTitle(savedQuery.attributes, format), + 'data-test-subj': `load-saved-query-${savedQuery.attributes.title}-button`, + value: savedQuery.id, + checked: + (loadedSavedQuery && savedQuery.id === loadedSavedQuery.id) || + (selectedSavedQuery && savedQuery.id === selectedSavedQuery.id) + ? 'on' + : undefined, + append: !!showSaveQuery && ( + handleDelete(savedQuery)} + color="danger" + /> + ), + }; + }) as unknown as SelectableProps[]; + }; + + const canEditSavedObjects = application.capabilities.savedObjectsManagement.edit; + + const listComponent = ( + <> + {savedQueries.length > 0 ? ( + <> +
+ + aria-label="Basic example" + options={savedQueriesOptions()} + searchable + singleSelection="always" + onChange={(choices) => { + const choice = choices.find(({ checked }) => checked) as unknown as { + value: string; + }; + if (choice) { + handleSelect(savedQueries.find((savedQuery) => savedQuery.id === choice.value)); + } + }} + searchProps={{ + compressed: true, + placeholder: i18n.translate( + 'unifiedSearch.query.queryBar.indexPattern.findFilterSet', + { + defaultMessage: 'Find a saved query', + } + ), + }} + listProps={{ + isVirtualized: true, + }} + > + {(list, search) => ( + <> + + {search} + + {list} + + )} + +
+ + ) : ( + <> + +

{noSavedQueriesDescriptionText}

+
+ + )} + + + {canEditSavedObjects && ( + + + Manage + + + )} + + + {hasFiltersOrQuery + ? i18n.translate( + 'unifiedSearch.search.searchBar.savedQueryPopoverReplaceFilterSetLabel', + { + defaultMessage: 'Replace with selected saved query', + } + ) + : i18n.translate( + 'unifiedSearch.search.searchBar.savedQueryPopoverApplyFilterSetLabel', + { + defaultMessage: 'Apply saved query', + } + )} + + + + + {showDeletionConfirmationModal && toBeDeletedSavedQuery && ( + { + onDelete(toBeDeletedSavedQuery.id); + setShowDeletionConfirmationModal(false); + }} + buttonColor="danger" + onCancel={() => { + setShowDeletionConfirmationModal(false); + }} + /> + )} + + ); + + return listComponent; +} diff --git a/src/plugins/unified_search/public/search_bar/create_search_bar.tsx b/src/plugins/unified_search/public/search_bar/create_search_bar.tsx index 3d8aa26af22af..c73aa258863ed 100644 --- a/src/plugins/unified_search/public/search_bar/create_search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/create_search_bar.tsx @@ -26,6 +26,7 @@ interface StatefulSearchBarDeps { data: Omit; storage: IStorageWrapper; usageCollection?: UsageCollectionSetup; + isScreenshotMode?: boolean; } export type StatefulSearchBarProps = SearchBarOwnProps & { @@ -110,7 +111,13 @@ const overrideDefaultBehaviors = (props: StatefulSearchBarProps) => { return props.useDefaultBehaviors ? {} : props; }; -export function createSearchBar({ core, storage, data, usageCollection }: StatefulSearchBarDeps) { +export function createSearchBar({ + core, + storage, + data, + usageCollection, + isScreenshotMode = false, +}: StatefulSearchBarDeps) { // App name should come from the core application service. // Until it's available, we'll ask the user to provide it for the pre-wired component. return (props: StatefulSearchBarProps) => { @@ -191,11 +198,13 @@ export function createSearchBar({ core, storage, data, usageCollection }: Statef onSaved={defaultOnSavedQueryUpdated(props, setSavedQuery)} iconType={props.iconType} nonKqlMode={props.nonKqlMode} - nonKqlModeHelpText={props.nonKqlModeHelpText} customSubmitButton={props.customSubmitButton} isClearable={props.isClearable} placeholder={props.placeholder} {...overrideDefaultBehaviors(props)} + dataViewPickerComponentProps={props.dataViewPickerComponentProps} + displayStyle={props.displayStyle} + isScreenshotMode={isScreenshotMode} /> ); diff --git a/src/plugins/unified_search/public/search_bar/index.tsx b/src/plugins/unified_search/public/search_bar/index.tsx index f8c9de7ec7d87..40421a50a5fe2 100644 --- a/src/plugins/unified_search/public/search_bar/index.tsx +++ b/src/plugins/unified_search/public/search_bar/index.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { injectI18n } from '@kbn/i18n-react'; import { withKibana } from '@kbn/kibana-react-plugin/public'; import type { SearchBarProps } from './search_bar'; +import '../index.scss'; const Fallback = () =>
; diff --git a/src/plugins/unified_search/public/search_bar/search_bar.styles.ts b/src/plugins/unified_search/public/search_bar/search_bar.styles.ts new file mode 100644 index 0000000000000..36d06d1cb9c7f --- /dev/null +++ b/src/plugins/unified_search/public/search_bar/search_bar.styles.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { UseEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; + +export const searchBarStyles = ({ euiTheme }: UseEuiTheme) => { + return { + uniSearchBar: css` + padding: ${euiTheme.size.s}; + `, + detached: css` + border-bottom: ${euiTheme.border.thin}; + `, + inPage: css` + padding: 0; + `, + hidden: css` + display: none; + `, + }; +}; diff --git a/src/plugins/unified_search/public/search_bar/search_bar.test.tsx b/src/plugins/unified_search/public/search_bar/search_bar.test.tsx index 14310b69809e0..fe5e03ab7fb37 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.test.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.test.tsx @@ -16,24 +16,21 @@ import { coreMock } from '@kbn/core/public/mocks'; const startMock = coreMock.createStart(); import { mount } from 'enzyme'; -import { IIndexPattern } from '@kbn/data-plugin/public'; +import { DataView } from '@kbn/data-views-plugin/public'; +import { EuiThemeProvider } from '@elastic/eui'; const mockTimeHistory = { get: () => { return []; }, + add: jest.fn(), + get$: () => { + return { + pipe: () => {}, + }; + }, }; -jest.mock('../filter_bar', () => { - return { - FilterBar: () =>
, - }; -}); - -jest.mock('../query_string_input/query_bar_top_row', () => { - return () =>
; -}); - const noop = jest.fn(); const createMockWebStorage = () => ({ @@ -66,7 +63,7 @@ const mockIndexPattern = { searchable: true, }, ], -} as IIndexPattern; +} as DataView; const kqlQuery = { query: 'response:200', @@ -88,24 +85,45 @@ function wrapSearchBarInContext(testProps: any) { storage: createMockStorage(), data: { query: { - savedQueries: {}, + savedQueries: { + findSavedQueries: () => + Promise.resolve({ + queries: [ + { + id: 'testwewe', + attributes: { + title: 'Saved query 1', + description: '', + query: { + query: 'category.keyword : "Men\'s Shoes" ', + language: 'kuery', + }, + filters: [], + }, + }, + ], + }), + }, }, }, }; return ( - - - - - + + + + + + + ); } describe('SearchBar', () => { - const SEARCH_BAR_ROOT = '.globalQueryBar'; - const FILTER_BAR = '.filterBar'; - const QUERY_BAR = '.queryBar'; + const SEARCH_BAR_ROOT = '.uniSearchBar'; + const FILTER_BAR = '[data-test-subj="unifiedFilterBar"]'; + const QUERY_BAR = '.kbnQueryBar'; + const QUERY_INPUT = '[data-test-subj="unifiedQueryInput"]'; beforeEach(() => { jest.clearAllMocks(); @@ -118,22 +136,9 @@ describe('SearchBar', () => { }) ); - expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); - expect(component.find(FILTER_BAR).length).toBe(0); - expect(component.find(QUERY_BAR).length).toBe(1); - }); - - it('Should render empty when timepicker is off and no options provided', () => { - const component = mount( - wrapSearchBarInContext({ - indexPatterns: [mockIndexPattern], - showDatePicker: false, - }) - ); - - expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); - expect(component.find(FILTER_BAR).length).toBe(0); - expect(component.find(QUERY_BAR).length).toBe(0); + expect(component.find(SEARCH_BAR_ROOT)).toBeTruthy(); + expect(component.find(FILTER_BAR).length).toBeFalsy(); + expect(component.find(QUERY_BAR).length).toBeTruthy(); }); it('Should render filter bar, when required fields are provided', () => { @@ -141,14 +146,16 @@ describe('SearchBar', () => { wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], showDatePicker: false, + showQueryInput: true, + showFilterBar: true, onFiltersUpdated: noop, filters: [], }) ); - expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); - expect(component.find(FILTER_BAR).length).toBe(1); - expect(component.find(QUERY_BAR).length).toBe(0); + expect(component.find(SEARCH_BAR_ROOT)).toBeTruthy(); + expect(component.find(FILTER_BAR).length).toBeTruthy(); + expect(component.find(QUERY_BAR).length).toBeTruthy(); }); it('Should NOT render filter bar, if disabled', () => { @@ -162,9 +169,9 @@ describe('SearchBar', () => { }) ); - expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); - expect(component.find(FILTER_BAR).length).toBe(0); - expect(component.find(QUERY_BAR).length).toBe(0); + expect(component.find(SEARCH_BAR_ROOT)).toBeTruthy(); + expect(component.find(FILTER_BAR).length).toBeFalsy(); + expect(component.find(QUERY_BAR).length).toBeTruthy(); }); it('Should render query bar, when required fields are provided', () => { @@ -177,12 +184,12 @@ describe('SearchBar', () => { }) ); - expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); - expect(component.find(FILTER_BAR).length).toBe(0); - expect(component.find(QUERY_BAR).length).toBe(1); + expect(component.find(SEARCH_BAR_ROOT)).toBeTruthy(); + expect(component.find(FILTER_BAR).length).toBeFalsy(); + expect(component.find(QUERY_BAR).length).toBeTruthy(); }); - it('Should NOT render query bar, if disabled', () => { + it('Should NOT render the input query input, if disabled', () => { const component = mount( wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], @@ -190,12 +197,13 @@ describe('SearchBar', () => { onQuerySubmit: noop, query: kqlQuery, showQueryBar: false, + showQueryInput: false, }) ); - expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); - expect(component.find(FILTER_BAR).length).toBe(0); - expect(component.find(QUERY_BAR).length).toBe(0); + expect(component.find(SEARCH_BAR_ROOT)).toBeTruthy(); + expect(component.find(FILTER_BAR).length).toBeFalsy(); + expect(component.find(QUERY_INPUT).length).toBeFalsy(); }); it('Should render query bar and filter bar', () => { @@ -203,6 +211,7 @@ describe('SearchBar', () => { wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], screenTitle: 'test screen', + showQueryInput: true, onQuerySubmit: noop, query: kqlQuery, filters: [], @@ -210,8 +219,9 @@ describe('SearchBar', () => { }) ); - expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); - expect(component.find(FILTER_BAR).length).toBe(1); - expect(component.find(QUERY_BAR).length).toBe(1); + expect(component.find(SEARCH_BAR_ROOT)).toBeTruthy(); + expect(component.find(FILTER_BAR).length).toBeTruthy(); + expect(component.find(QUERY_BAR).length).toBeTruthy(); + expect(component.find(QUERY_INPUT).length).toBeTruthy(); }); }); diff --git a/src/plugins/unified_search/public/search_bar/search_bar.tsx b/src/plugins/unified_search/public/search_bar/search_bar.tsx index c829ab66bb60a..8c5abc1bf4c2c 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.tsx @@ -11,22 +11,25 @@ import { InjectedIntl, injectI18n } from '@kbn/i18n-react'; import classNames from 'classnames'; import React, { Component } from 'react'; import { get, isEqual } from 'lodash'; -import { EuiIconProps } from '@elastic/eui'; +import { EuiIconProps, withEuiTheme, WithEuiThemeProps } from '@elastic/eui'; import memoizeOne from 'memoize-one'; import { METRIC_TYPE } from '@kbn/analytics'; import { Query, Filter } from '@kbn/es-query'; import { withKibana, KibanaReactContextValue } from '@kbn/kibana-react-plugin/public'; - import type { TimeHistoryContract, SavedQuery } from '@kbn/data-plugin/public'; import type { SavedQueryAttributes } from '@kbn/data-plugin/common'; import { IDataPluginServices } from '@kbn/data-plugin/public'; import { TimeRange } from '@kbn/data-plugin/common'; import { DataView } from '@kbn/data-views-plugin/public'; -import { FilterBar } from '../filter_bar'; -import QueryBarTopRow from '../query_string_input/query_bar_top_row'; + import { SavedQueryMeta, SaveQueryForm } from '../saved_query_form'; -import { SavedQueryManagementComponent } from '../saved_query_management'; +import { SavedQueryManagementList } from '../saved_query_management'; +import { QueryBarMenu } from '../query_string_input/query_bar_menu'; +import type { DataViewPickerProps } from '../dataview_picker'; +import QueryBarTopRow from '../query_string_input/query_bar_top_row'; +import { FilterBar, FilterItems } from '../filter_bar'; +import { searchBarStyles } from './search_bar.styles'; export interface SearchBarInjectedDeps { kibana: KibanaReactContextValue; @@ -77,19 +80,21 @@ export interface SearchBarOwnProps { isClearable?: boolean; iconType?: EuiIconProps['type']; nonKqlMode?: 'lucene' | 'text'; - nonKqlModeHelpText?: string; - // defines padding; use 'inPage' to avoid extra padding; use 'detached' if the searchBar appears at the very top of the view, without any wrapper + // defines padding and border; use 'inPage' to avoid any padding or border; + // use 'detached' if the searchBar appears at the very top of the view, without any wrapper displayStyle?: 'inPage' | 'detached'; // super update button background fill control fillSubmitButton?: boolean; + dataViewPickerComponentProps?: DataViewPickerProps; + showSubmitButton?: boolean; + isScreenshotMode?: boolean; } export type SearchBarProps = SearchBarOwnProps & SearchBarInjectedDeps; interface State { isFiltersVisible: boolean; - showSaveQueryModal: boolean; - showSaveNewQueryModal: boolean; + openQueryBarMenu: boolean; showSavedQueryPopover: boolean; currentProps?: SearchBarProps; query?: Query; @@ -97,11 +102,12 @@ interface State { dateRangeTo: string; } -class SearchBarUI extends Component { +class SearchBarUI extends Component { public static defaultProps = { showQueryBar: true, showFilterBar: true, showDatePicker: true, + showSubmitButton: true, showAutoRefreshOnly: false, }; @@ -168,8 +174,7 @@ class SearchBarUI extends Component { */ public state = { isFiltersVisible: true, - showSaveQueryModal: false, - showSaveNewQueryModal: false, + openQueryBarMenu: false, showSavedQueryPopover: false, currentProps: this.props, query: this.props.query ? { ...this.props.query } : undefined, @@ -193,13 +198,6 @@ class SearchBarUI extends Component { this.renderSavedQueryManagement.clear(); } - private shouldRenderQueryBar() { - const showDatePicker = this.props.showDatePicker || this.props.showAutoRefreshOnly; - const showQueryInput = - this.props.showQueryInput && this.props.indexPatterns && this.state.query; - return this.props.showQueryBar && (showDatePicker || showQueryInput); - } - private shouldRenderFilterBar() { return ( this.props.showFilterBar && @@ -266,11 +264,6 @@ class SearchBarUI extends Component { `Your query "${response.attributes.title}" was saved` ); - this.setState({ - showSaveQueryModal: false, - showSaveNewQueryModal: false, - }); - if (this.props.onSaved) { this.props.onSaved(response); } @@ -282,18 +275,6 @@ class SearchBarUI extends Component { } }; - public onInitiateSave = () => { - this.setState({ - showSaveQueryModal: true, - }); - }; - - public onInitiateSaveNew = () => { - this.setState({ - showSaveNewQueryModal: true, - }); - }; - public onQueryBarChange = (queryAndDateRange: { dateRange: TimeRange; query?: Query }) => { this.setState({ query: queryAndDateRange.query, @@ -305,6 +286,12 @@ class SearchBarUI extends Component { } }; + public toggleFilterBarMenuPopover = (value: boolean) => { + this.setState({ + openQueryBarMenu: value, + }); + }; + public onQueryBarSubmit = (queryAndDateRange: { dateRange?: TimeRange; query?: Query }) => { this.setState( { @@ -349,12 +336,107 @@ class SearchBarUI extends Component { } }; + private shouldShowDatePickerAsBadge() { + return this.shouldRenderFilterBar() && !this.props.showQueryInput; + } + public render() { + const { theme } = this.props; + const isScreenshotMode = this.props.isScreenshotMode === true; + const styles = searchBarStyles(theme); + const cssStyles = [ + styles.uniSearchBar, + this.props.displayStyle && styles[this.props.displayStyle], + isScreenshotMode && styles.hidden, + ]; + + const classes = classNames('uniSearchBar', { + [`uniSearchBar--hidden`]: isScreenshotMode, + [`uniSearchBar--${this.props.displayStyle}`]: this.props.displayStyle, + }); + const timeRangeForSuggestionsOverride = this.props.showDatePicker ? undefined : false; - let queryBar; - if (this.shouldRenderQueryBar()) { - queryBar = ( + const saveAsNewQueryFormComponent = ( + this.onSave(savedQueryMeta, true)} + onClose={() => this.setState({ openQueryBarMenu: false })} + showFilterOption={this.props.showFilterBar} + showTimeFilterOption={this.shouldRenderTimeFilterInSavedQueryForm()} + /> + ); + + const saveQueryFormComponent = ( + this.setState({ openQueryBarMenu: false })} + showFilterOption={this.props.showFilterBar} + showTimeFilterOption={this.shouldRenderTimeFilterInSavedQueryForm()} + /> + ); + + const queryBarMenu = ( + + ); + + let filterBar; + if (this.shouldRenderFilterBar()) { + filterBar = this.shouldShowDatePickerAsBadge() ? ( + + ) : ( + + ); + } + + return ( +
{ indexPatterns={this.props.indexPatterns} isLoading={this.props.isLoading} fillSubmitButton={this.props.fillSubmitButton || false} - prepend={ - this.props.showFilterBar && this.state.query - ? this.renderSavedQueryManagement( - this.props.onClearSavedQuery, - this.props.showSaveQuery, - this.props.savedQuery - ) - : undefined - } + prepend={this.props.showFilterBar || this.props.showQueryInput ? queryBarMenu : undefined} showDatePicker={this.props.showDatePicker} dateRangeFrom={this.state.dateRangeFrom} dateRangeTo={this.state.dateRangeTo} @@ -379,6 +453,7 @@ class SearchBarUI extends Component { refreshInterval={this.props.refreshInterval} showAutoRefreshOnly={this.props.showAutoRefreshOnly} showQueryInput={this.props.showQueryInput} + showAddFilter={this.props.showFilterBar} onRefresh={this.props.onRefresh} onRefreshChange={this.props.onRefreshChange} onChange={this.onQueryBarChange} @@ -386,70 +461,31 @@ class SearchBarUI extends Component { customSubmitButton={ this.props.customSubmitButton ? this.props.customSubmitButton : undefined } + showSubmitButton={this.props.showSubmitButton} dataTestSubj={this.props.dataTestSubj} indicateNoData={this.props.indicateNoData} placeholder={this.props.placeholder} isClearable={this.props.isClearable} iconType={this.props.iconType} nonKqlMode={this.props.nonKqlMode} - nonKqlModeHelpText={this.props.nonKqlModeHelpText} timeRangeForSuggestionsOverride={timeRangeForSuggestionsOverride} + filters={this.props.filters!} + onFiltersUpdated={this.props.onFiltersUpdated} + dataViewPickerComponentProps={this.props.dataViewPickerComponentProps} + showDatePickerAsBadge={this.shouldShowDatePickerAsBadge()} + filterBar={filterBar} + isScreenshotMode={this.props.isScreenshotMode} /> - ); - } - - let filterBar; - if (this.shouldRenderFilterBar()) { - const filterGroupClasses = classNames('globalFilterGroup__wrapper', { - 'globalFilterGroup__wrapper-isVisible': this.state.isFiltersVisible, - }); - - filterBar = ( -
- -
- ); - } - - const globalQueryBarClasses = classNames('globalQueryBar', { - 'globalQueryBar--inPage': this.props.displayStyle === 'inPage', - }); - - return ( -
- {queryBar} - {filterBar} - - {this.state.showSaveQueryModal ? ( - this.setState({ showSaveQueryModal: false })} - showFilterOption={this.props.showFilterBar} - showTimeFilterOption={this.shouldRenderTimeFilterInSavedQueryForm()} - /> - ) : null} - {this.state.showSaveNewQueryModal ? ( - this.onSave(savedQueryMeta, true)} - onClose={() => this.setState({ showSaveNewQueryModal: false })} - showFilterOption={this.props.showFilterBar} - showTimeFilterOption={this.shouldRenderTimeFilterInSavedQueryForm()} - /> - ) : null}
); } + private hasFiltersOrQuery() { + const hasFilters = Boolean(this.props.filters && this.props.filters.length > 0); + const hasQuery = Boolean(this.state.query && this.state.query.query); + return hasFilters || hasQuery; + } + private renderSavedQueryManagement = memoizeOne( ( onClearSavedQuery: SearchBarOwnProps['onClearSavedQuery'], @@ -457,14 +493,14 @@ class SearchBarUI extends Component { savedQuery: SearchBarOwnProps['savedQuery'] ) => { const savedQueryManagement = onClearSavedQuery && ( - this.setState({ openQueryBarMenu: false })} + hasFiltersOrQuery={this.hasFiltersOrQuery()} /> ); @@ -475,4 +511,4 @@ class SearchBarUI extends Component { // Needed for React.lazy // eslint-disable-next-line import/no-default-export -export default injectI18n(withKibana(SearchBarUI)); +export default injectI18n(withEuiTheme(withKibana(SearchBarUI))); diff --git a/src/plugins/unified_search/public/typeahead/_suggestion.scss b/src/plugins/unified_search/public/typeahead/_suggestion.scss index 9e0c163f9c94c..a59e53a102d6c 100644 --- a/src/plugins/unified_search/public/typeahead/_suggestion.scss +++ b/src/plugins/unified_search/public/typeahead/_suggestion.scss @@ -15,12 +15,16 @@ $kbnTypeaheadTypes: ( @include euiBottomShadowFlat; border-top-left-radius: $euiBorderRadius; border-top-right-radius: $euiBorderRadius; + // Clips the shadow so it doesn't show above the input (below) + clip-path: polygon(-50px -50px, calc(100% + 50px) -50px, calc(100% + 50px) 100%, -50px 100%); } .kbnTypeahead__popover--bottom { - @include euiBottomShadow($adjustBorders: true); + @include euiBottomShadow; border-bottom-left-radius: $euiBorderRadius; border-bottom-right-radius: $euiBorderRadius; + // Clips the shadow so it doesn't show above the input (top) + clip-path: polygon(-50px 1px, calc(100% + 50px) 1px, calc(100% + 50px) calc(100% + 50px), -50px calc(100% + 50px)); } .kbnTypeahead { @@ -59,7 +63,6 @@ $kbnTypeaheadTypes: ( .kbnTypeahead__item:first-child { border-bottom: none; - border-radius: $euiBorderRadius $euiBorderRadius 0 0; } .kbnTypeahead__item.active { diff --git a/src/plugins/unified_search/public/typeahead/suggestions_component.tsx b/src/plugins/unified_search/public/typeahead/suggestions_component.tsx index 75e446cf2d6e8..ebeddfaaff81f 100644 --- a/src/plugins/unified_search/public/typeahead/suggestions_component.tsx +++ b/src/plugins/unified_search/public/typeahead/suggestions_component.tsx @@ -9,8 +9,7 @@ import React, { PureComponent, ReactNode } from 'react'; import { isEmpty } from 'lodash'; import classNames from 'classnames'; - -import styled from 'styled-components'; +import { css } from '@emotion/react'; import useRafState from 'react-use/lib/useRafState'; import { QuerySuggestion } from '../autocomplete'; @@ -146,15 +145,6 @@ export default class SuggestionsComponent extends PureComponent ` - position: absolute; - z-index: 4001; - left: ${props.left}px; - width: ${props.width}px; - ${props.verticalListPosition}`} -`; - const ResizableSuggestionsListDiv: React.FC<{ inputContainer: HTMLElement; suggestionsSize?: SuggestionsListSize; @@ -174,12 +164,16 @@ const ResizableSuggestionsListDiv: React.FC<{ ? `top: ${pageYOffset + containerRect.bottom - SUGGESTIONS_LIST_REQUIRED_TOP_OFFSET}px;` : `bottom: ${documentHeight - (pageYOffset + containerRect.top)}px;`; + const divPosition = css` + position: absolute; + z-index: 4001; + left: ${containerRect.left}px; + width: ${containerRect.width}px; + ${verticalListPosition} + `; + return ( - +
- +
); }); diff --git a/src/plugins/unified_search/public/types.ts b/src/plugins/unified_search/public/types.ts index 29cf59f41a871..fa0fc9e826e37 100755 --- a/src/plugins/unified_search/public/types.ts +++ b/src/plugins/unified_search/public/types.ts @@ -8,6 +8,7 @@ import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import type { ScreenshotModePluginStart } from '@kbn/screenshot-mode-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { UiActionsSetup, UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; @@ -29,6 +30,7 @@ export interface UnifiedSearchStartDependencies { fieldFormats: FieldFormatsStart; data: DataPublicPluginStart; uiActions: UiActionsStart; + screenshotMode?: ScreenshotModePluginStart; } /** diff --git a/src/plugins/vis_default_editor/kibana.json b/src/plugins/vis_default_editor/kibana.json index e6a56bd65fcc7..240eb7ccab6fa 100644 --- a/src/plugins/vis_default_editor/kibana.json +++ b/src/plugins/vis_default_editor/kibana.json @@ -3,7 +3,16 @@ "version": "kibana", "ui": true, "optionalPlugins": ["visualizations"], - "requiredBundles": ["unifiedSearch", "kibanaUtils", "kibanaReact", "data", "fieldFormats", "discover", "esUiShared"], + "requiredBundles": [ + "unifiedSearch", + "kibanaUtils", + "kibanaReact", + "data", + "fieldFormats", + "discover", + "esUiShared", + "visualizations" + ], "owner": { "name": "Vis Editors", "githubTeam": "kibana-vis-editors" diff --git a/src/plugins/vis_default_editor/public/components/options/legend_size_settings.test.tsx b/src/plugins/vis_default_editor/public/components/options/legend_size_settings.test.tsx new file mode 100644 index 0000000000000..3eeb93e6155df --- /dev/null +++ b/src/plugins/vis_default_editor/public/components/options/legend_size_settings.test.tsx @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { LegendSizeSettings } from './legend_size_settings'; +import { LegendSize, DEFAULT_LEGEND_SIZE } from '@kbn/visualizations-plugin/public'; +import { EuiSuperSelect } from '@elastic/eui'; +import { shallow } from 'enzyme'; + +describe('legend size settings', () => { + it('select is disabled if not vertical legend', () => { + const instance = shallow( + {}} + isVerticalLegend={false} + showAutoOption={true} + /> + ); + + expect(instance.find(EuiSuperSelect).props().disabled).toBeTruthy(); + }); + + it('reflects current setting in select', () => { + const CURRENT_SIZE = LegendSize.SMALL; + + const instance = shallow( + {}} + isVerticalLegend={true} + showAutoOption={true} + /> + ); + + expect(instance.find(EuiSuperSelect).props().valueOfSelected).toBe(CURRENT_SIZE); + }); + + it('allows user to select a new option', () => { + const onSizeChange = jest.fn(); + + const instance = shallow( + + ); + + const onChange = instance.find(EuiSuperSelect).props().onChange; + + onChange(LegendSize.EXTRA_LARGE); + onChange(DEFAULT_LEGEND_SIZE); + + expect(onSizeChange).toHaveBeenNthCalledWith(1, LegendSize.EXTRA_LARGE); + expect(onSizeChange).toHaveBeenNthCalledWith(2, undefined); + }); + + it('hides "auto" option if visualization not using it', () => { + const getOptions = (showAutoOption: boolean) => + shallow( + {}} + isVerticalLegend={true} + showAutoOption={showAutoOption} + /> + ) + .find(EuiSuperSelect) + .props().options; + + const autoOption = expect.objectContaining({ value: LegendSize.AUTO }); + + expect(getOptions(true)).toContainEqual(autoOption); + expect(getOptions(false)).not.toContainEqual(autoOption); + }); +}); diff --git a/src/plugins/vis_default_editor/public/components/options/legend_size_settings.tsx b/src/plugins/vis_default_editor/public/components/options/legend_size_settings.tsx index 768db7d3dd78e..bbe47295c99e6 100644 --- a/src/plugins/vis_default_editor/public/components/options/legend_size_settings.tsx +++ b/src/plugins/vis_default_editor/public/components/options/legend_size_settings.tsx @@ -10,27 +10,11 @@ import React, { useCallback, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiFormRow, EuiSuperSelect, EuiToolTip } from '@elastic/eui'; +import { LegendSize, DEFAULT_LEGEND_SIZE } from '@kbn/visualizations-plugin/public'; -enum LegendSizes { - AUTO = '0', - SMALL = '80', - MEDIUM = '130', - LARGE = '180', - EXTRA_LARGE = '230', -} - -const legendSizeOptions: Array<{ value: LegendSizes; inputDisplay: string }> = [ +const legendSizeOptions: Array<{ value: LegendSize; inputDisplay: string }> = [ { - value: LegendSizes.AUTO, - inputDisplay: i18n.translate( - 'visDefaultEditor.options.legendSizeSetting.legendSizeOptions.auto', - { - defaultMessage: 'Auto', - } - ), - }, - { - value: LegendSizes.SMALL, + value: LegendSize.SMALL, inputDisplay: i18n.translate( 'visDefaultEditor.options.legendSizeSetting.legendSizeOptions.small', { @@ -39,7 +23,7 @@ const legendSizeOptions: Array<{ value: LegendSizes; inputDisplay: string }> = [ ), }, { - value: LegendSizes.MEDIUM, + value: LegendSize.MEDIUM, inputDisplay: i18n.translate( 'visDefaultEditor.options.legendSizeSetting.legendSizeOptions.medium', { @@ -48,7 +32,7 @@ const legendSizeOptions: Array<{ value: LegendSizes; inputDisplay: string }> = [ ), }, { - value: LegendSizes.LARGE, + value: LegendSize.LARGE, inputDisplay: i18n.translate( 'visDefaultEditor.options.legendSizeSetting.legendSizeOptions.large', { @@ -57,7 +41,7 @@ const legendSizeOptions: Array<{ value: LegendSizes; inputDisplay: string }> = [ ), }, { - value: LegendSizes.EXTRA_LARGE, + value: LegendSize.EXTRA_LARGE, inputDisplay: i18n.translate( 'visDefaultEditor.options.legendSizeSetting.legendSizeOptions.extraLarge', { @@ -68,15 +52,17 @@ const legendSizeOptions: Array<{ value: LegendSizes; inputDisplay: string }> = [ ]; interface LegendSizeSettingsProps { - legendSize?: number; - onLegendSizeChange: (size?: number) => void; + legendSize?: LegendSize; + onLegendSizeChange: (size?: LegendSize) => void; isVerticalLegend: boolean; + showAutoOption: boolean; } export const LegendSizeSettings = ({ legendSize, onLegendSizeChange, isVerticalLegend, + showAutoOption, }: LegendSizeSettingsProps) => { useEffect(() => { if (legendSize && !isVerticalLegend) { @@ -85,16 +71,31 @@ export const LegendSizeSettings = ({ }, [isVerticalLegend, legendSize, onLegendSizeChange]); const onLegendSizeOptionChange = useCallback( - (option) => onLegendSizeChange(Number(option) || undefined), + (option) => onLegendSizeChange(option === DEFAULT_LEGEND_SIZE ? undefined : option), [onLegendSizeChange] ); + const options = showAutoOption + ? [ + { + value: LegendSize.AUTO, + inputDisplay: i18n.translate( + 'visDefaultEditor.options.legendSizeSetting.legendSizeOptions.auto', + { + defaultMessage: 'Auto', + } + ), + }, + ...legendSizeOptions, + ] + : legendSizeOptions; + const legendSizeSelect = ( diff --git a/src/plugins/vis_type_markdown/public/markdown_vis.ts b/src/plugins/vis_type_markdown/public/markdown_vis.ts index 7fcf9fb6311e6..a4b4010064e78 100644 --- a/src/plugins/vis_type_markdown/public/markdown_vis.ts +++ b/src/plugins/vis_type_markdown/public/markdown_vis.ts @@ -58,6 +58,8 @@ export const markdownVisDefinition: VisTypeDefinition = { options: { showTimePicker: false, showFilterBar: false, + showQueryBar: true, + showQueryInput: false, }, inspectorAdapters: {}, }; diff --git a/src/plugins/vis_types/heatmap/public/editor/components/heatmap.tsx b/src/plugins/vis_types/heatmap/public/editor/components/heatmap.tsx index f592ee3933c1c..3c06e65e2cff4 100644 --- a/src/plugins/vis_types/heatmap/public/editor/components/heatmap.tsx +++ b/src/plugins/vis_types/heatmap/public/editor/components/heatmap.tsx @@ -26,7 +26,7 @@ import { LegendSizeSettings, } from '@kbn/vis-default-editor-plugin/public'; import { colorSchemas } from '@kbn/charts-plugin/public'; -import { VisEditorOptionsProps } from '@kbn/visualizations-plugin/public'; +import { LegendSize, VisEditorOptionsProps } from '@kbn/visualizations-plugin/public'; import { HeatmapVisParams, HeatmapTypeProps, ValueAxis } from '../../types'; import { LabelsPanel } from './labels_panel'; import { legendPositions, scaleTypes } from '../collections'; @@ -42,6 +42,9 @@ const HeatmapOptions = (props: HeatmapOptionsProps) => { const isColorsNumberInvalid = stateParams.colorsNumber < 2 || stateParams.colorsNumber > 10; const [isColorRangesValid, setIsColorRangesValid] = useState(false); + const legendSize = stateParams.legendSize; + const [hadAutoLegendSize] = useState(() => legendSize === LegendSize.AUTO); + const setValueAxisScale = useCallback( (paramName: T, value: ValueAxis['scale'][T]) => setValue('valueAxes', [ @@ -91,12 +94,13 @@ const HeatmapOptions = (props: HeatmapOptionsProps) => { setValue={setValue} /> )} diff --git a/src/plugins/vis_types/heatmap/public/types.ts b/src/plugins/vis_types/heatmap/public/types.ts index 8301d246e9f63..9d41a132f00b1 100644 --- a/src/plugins/vis_types/heatmap/public/types.ts +++ b/src/plugins/vis_types/heatmap/public/types.ts @@ -9,6 +9,7 @@ import { UiCounterMetricType } from '@kbn/analytics'; import type { Position } from '@elastic/charts'; import type { ChartsPluginSetup, Style, Labels, ColorSchemas } from '@kbn/charts-plugin/public'; import { Range } from '@kbn/expressions-plugin/public'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; export interface HeatmapTypeProps { showElasticChartsOptions?: boolean; @@ -23,7 +24,7 @@ export interface HeatmapVisParams { legendPosition: Position; truncateLegend?: boolean; maxLegendLines?: number; - legendSize?: number; + legendSize?: LegendSize; lastRangeIsRightOpen: boolean; percentageMode: boolean; valueAxes: ValueAxis[]; diff --git a/src/plugins/vis_types/metric/public/to_ast.ts b/src/plugins/vis_types/metric/public/to_ast.ts index 22176341f45ab..322ea561abeb4 100644 --- a/src/plugins/vis_types/metric/public/to_ast.ts +++ b/src/plugins/vis_types/metric/public/to_ast.ts @@ -83,7 +83,7 @@ export const toExpressionAst: VisToExpressionAst = (vis, params) => { ) ); - if (colorsRange && colorsRange.length) { + if (colorsRange && colorsRange.length > 1) { const stopsWithColors = getStopsWithColorsFromRanges(colorsRange, colorSchema, invertColors); const palette = buildExpressionFunction('palette', { ...stopsWithColors, diff --git a/src/plugins/vis_types/pie/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_types/pie/public/__snapshots__/to_ast.test.ts.snap index 904dff6ee1192..5b8bd613609f9 100644 --- a/src/plugins/vis_types/pie/public/__snapshots__/to_ast.test.ts.snap +++ b/src/plugins/vis_types/pie/public/__snapshots__/to_ast.test.ts.snap @@ -93,6 +93,9 @@ Object { "legendPosition": Array [ "right", ], + "legendSize": Array [ + "large", + ], "metric": Array [ Object { "chain": Array [ diff --git a/src/plugins/vis_types/pie/public/editor/components/pie.tsx b/src/plugins/vis_types/pie/public/editor/components/pie.tsx index f1f335f186ffd..cd1e565861d78 100644 --- a/src/plugins/vis_types/pie/public/editor/components/pie.tsx +++ b/src/plugins/vis_types/pie/public/editor/components/pie.tsx @@ -31,7 +31,7 @@ import { LongLegendOptions, LegendSizeSettings, } from '@kbn/vis-default-editor-plugin/public'; -import { VisEditorOptionsProps } from '@kbn/visualizations-plugin/public'; +import { LegendSize, VisEditorOptionsProps } from '@kbn/visualizations-plugin/public'; import { PartitionVisParams, LabelPositions, @@ -97,6 +97,9 @@ const PieOptions = (props: PieOptionsProps) => { const hasSplitChart = Boolean(aggs?.aggs?.find((agg) => agg.schema === 'split' && agg.enabled)); const segments = aggs?.aggs?.filter((agg) => agg.schema === 'segment' && agg.enabled) ?? []; + const legendSize = stateParams.legendSize; + const [hadAutoLegendSize] = useState(() => legendSize === LegendSize.AUTO); + const getLegendDisplay = useCallback( (isVisible: boolean) => (isVisible ? LegendDisplay.SHOW : LegendDisplay.HIDE), [] @@ -234,12 +237,13 @@ const PieOptions = (props: PieOptionsProps) => { setValue={setValue} /> )} diff --git a/src/plugins/vis_types/pie/public/sample_vis.test.mocks.ts b/src/plugins/vis_types/pie/public/sample_vis.test.mocks.ts index f8836f208d916..4c638689ca310 100644 --- a/src/plugins/vis_types/pie/public/sample_vis.test.mocks.ts +++ b/src/plugins/vis_types/pie/public/sample_vis.test.mocks.ts @@ -7,6 +7,7 @@ */ import { LegendDisplay } from '@kbn/expression-partition-vis-plugin/common'; +import { LegendSize } from '@kbn/visualizations-plugin/common'; export const samplePieVis = { type: { @@ -142,6 +143,7 @@ export const samplePieVis = { addTooltip: true, legendDisplay: LegendDisplay.SHOW, legendPosition: 'right', + legendSize: LegendSize.LARGE, isDonut: true, labels: { show: true, diff --git a/src/plugins/vis_types/pie/public/to_ast.ts b/src/plugins/vis_types/pie/public/to_ast.ts index aaac3040d7bd3..7a131dbb76b9c 100644 --- a/src/plugins/vis_types/pie/public/to_ast.ts +++ b/src/plugins/vis_types/pie/public/to_ast.ts @@ -62,14 +62,14 @@ export const toExpressionAst: VisToExpressionAst = async (vi addTooltip: vis.params.addTooltip, legendDisplay: vis.params.legendDisplay, legendPosition: vis.params.legendPosition, - nestedLegend: vis.params?.nestedLegend ?? false, + nestedLegend: vis.params.nestedLegend ?? false, truncateLegend: vis.params.truncateLegend, maxLegendLines: vis.params.maxLegendLines, legendSize: vis.params.legendSize, - distinctColors: vis.params?.distinctColors, + distinctColors: vis.params.distinctColors, isDonut: vis.params.isDonut ?? false, emptySizeRatio: vis.params.emptySizeRatio, - palette: preparePalette(vis.params?.palette), + palette: preparePalette(vis.params.palette), labels: prepareLabels(vis.params.labels), metric: schemas.metric.map(prepareDimension), buckets: schemas.segment?.map(prepareDimension), diff --git a/src/plugins/vis_types/table/public/components/table_vis_columns.tsx b/src/plugins/vis_types/table/public/components/table_vis_columns.tsx index 9aa30f95f1809..25bd6b0b9031c 100644 --- a/src/plugins/vis_types/table/public/components/table_vis_columns.tsx +++ b/src/plugins/vis_types/table/public/components/table_vis_columns.tsx @@ -35,7 +35,7 @@ export const createGridColumns = ( ) => { const onFilterClick = (data: FilterCellData, negate: boolean) => { fireEvent({ - name: 'filterBucket', + name: 'filter', data: { data: [ { diff --git a/src/plugins/vis_types/table/public/table_vis_fn.test.ts b/src/plugins/vis_types/table/public/table_vis_fn.test.ts index 98336d6cc67d4..87da839578117 100644 --- a/src/plugins/vis_types/table/public/table_vis_fn.test.ts +++ b/src/plugins/vis_types/table/public/table_vis_fn.test.ts @@ -79,6 +79,7 @@ describe('interpreter/functions#table', () => { logDatatable: (name: string, datatable: Datatable) => { loggedTable = datatable; }, + reset: () => {}, }, }, }; diff --git a/src/plugins/vis_types/timelion/public/components/timelion_vis_component.tsx b/src/plugins/vis_types/timelion/public/components/timelion_vis_component.tsx index 8137d8defd18c..b31256dd4fbb7 100644 --- a/src/plugins/vis_types/timelion/public/components/timelion_vis_component.tsx +++ b/src/plugins/vis_types/timelion/public/components/timelion_vis_component.tsx @@ -16,6 +16,7 @@ import { TooltipType, LegendPositionConfig, LayoutDirection, + Placement, } from '@elastic/charts'; import { EuiTitle } from '@elastic/eui'; import { RangeFilterParams } from '@kbn/es-query'; @@ -57,6 +58,7 @@ interface TimelionVisComponentProps { onBrushEvent: (rangeFilterParams: RangeFilterParams) => void; renderComplete: IInterpreterRenderHandlers['done']; ariaLabel?: string; + syncTooltips?: boolean; } const DefaultYAxis = () => ( @@ -101,6 +103,7 @@ export const TimelionVisComponent = ({ renderComplete, onBrushEvent, ariaLabel, + syncTooltips, }: TimelionVisComponentProps) => { const kibana = useKibana(); const chartRef = useRef(null); @@ -201,6 +204,9 @@ export const TimelionVisComponent = ({ legendPosition={legend.legendPosition} onRenderChange={onRenderChange} onPointerUpdate={handleCursorUpdate} + externalPointerEvents={{ + tooltip: { visible: syncTooltips, placement: Placement.Right }, + }} theme={chartTheme} baseTheme={chartBaseTheme} tooltip={{ @@ -208,7 +214,6 @@ export const TimelionVisComponent = ({ headerFormatter: ({ value }) => tickFormat(value), type: TooltipType.VerticalCursor, }} - externalPointerEvents={{ tooltip: { visible: false } }} ariaLabel={ariaLabel} ariaUseDefaultSummary={!ariaLabel} /> diff --git a/src/plugins/vis_types/timelion/public/timelion_vis_fn.ts b/src/plugins/vis_types/timelion/public/timelion_vis_fn.ts index a7afb338c8edc..6f92d02682b53 100644 --- a/src/plugins/vis_types/timelion/public/timelion_vis_fn.ts +++ b/src/plugins/vis_types/timelion/public/timelion_vis_fn.ts @@ -21,6 +21,7 @@ export interface TimelionRenderValue { visData?: TimelionSuccessResponse; visType: 'timelion'; visParams: TimelionVisParams; + syncTooltips: boolean; } export interface TimelionVisParams { @@ -68,7 +69,13 @@ export const getTimelionVisualizationConfig = ( async fn( input, args, - { getSearchSessionId, getExecutionContext, variables, abortSignal: expressionAbortSignal } + { + getSearchSessionId, + getExecutionContext, + variables, + abortSignal: expressionAbortSignal, + isSyncTooltipsEnabled, + } ) { const { getTimelionRequestHandler } = await import('./async_services'); const visParams = { @@ -106,6 +113,7 @@ export const getTimelionVisualizationConfig = ( visParams, visType: TIMELION_VIS_NAME, visData, + syncTooltips: isSyncTooltipsEnabled?.() ?? false, }, }; }, diff --git a/src/plugins/vis_types/timelion/public/timelion_vis_renderer.tsx b/src/plugins/vis_types/timelion/public/timelion_vis_renderer.tsx index 5e2c40d51217c..0d148a4c56c96 100644 --- a/src/plugins/vis_types/timelion/public/timelion_vis_renderer.tsx +++ b/src/plugins/vis_types/timelion/public/timelion_vis_renderer.tsx @@ -28,7 +28,7 @@ export const getTimelionVisRenderer: ( name: 'timelion_vis', displayName: 'Timelion visualization', reuseDomNode: true, - render: (domNode, { visData, visParams }, handlers) => { + render: (domNode, { visData, visParams, syncTooltips }, handlers) => { handlers.onDestroy(() => { unmountComponentAtNode(domNode); }); @@ -69,6 +69,7 @@ export const getTimelionVisRenderer: ( seriesList={seriesList} renderComplete={handlers.done} onBrushEvent={onBrushEvent} + syncTooltips={syncTooltips} /> )} diff --git a/src/plugins/vis_types/timelion/public/timelion_vis_type.tsx b/src/plugins/vis_types/timelion/public/timelion_vis_type.tsx index f32a485ac2565..f8d7415f6aefe 100644 --- a/src/plugins/vis_types/timelion/public/timelion_vis_type.tsx +++ b/src/plugins/vis_types/timelion/public/timelion_vis_type.tsx @@ -66,8 +66,9 @@ export function getTimelionVisDefinition(dependencies: TimelionVisDependencies) }, options: { showIndexSelection: false, - showQueryBar: false, + showQueryBar: true, showFilterBar: false, + showQueryInput: false, }, requiresSearch: true, }; diff --git a/src/plugins/vis_types/timelion/server/lib/build_target.js b/src/plugins/vis_types/timelion/server/lib/build_target.js index b7967bbb48e41..98e73be3419d3 100644 --- a/src/plugins/vis_types/timelion/server/lib/build_target.js +++ b/src/plugins/vis_types/timelion/server/lib/build_target.js @@ -6,22 +6,30 @@ * Side Public License, v 1. */ -import moment from 'moment'; +import moment from 'moment-timezone'; import splitInterval from './split_interval'; export default function (tlConfig) { - const min = moment(tlConfig.time.from); - const max = moment(tlConfig.time.to); - - const intervalParts = splitInterval(tlConfig.time.interval); + const targetSeries = []; + // The code between this call and the reset in the finally block is not allowed to get async, + // otherwise the timezone setting can leak out of this function. + const defaultTimezone = moment().zoneName(); + try { + moment.tz.setDefault(tlConfig.time.timezone); + const min = moment(tlConfig.time.from); + const max = moment(tlConfig.time.to); - let current = min.startOf(intervalParts.unit); + const intervalParts = splitInterval(tlConfig.time.interval); - const targetSeries = []; + let current = min.startOf(intervalParts.unit); - while (current.valueOf() < max.valueOf()) { - targetSeries.push(current.valueOf()); - current = current.add(intervalParts.count, intervalParts.unit); + while (current.valueOf() < max.valueOf()) { + targetSeries.push(current.valueOf()); + current = current.add(intervalParts.count, intervalParts.unit); + } + } finally { + // reset default moment timezone + moment.tz.setDefault(defaultTimezone); } return targetSeries; diff --git a/src/plugins/vis_types/timeseries/public/application/components/splits/__snapshots__/terms.test.js.snap b/src/plugins/vis_types/timeseries/public/application/components/splits/__snapshots__/terms.test.js.snap index b7c5095535fbc..fdfe472f46689 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/splits/__snapshots__/terms.test.js.snap +++ b/src/plugins/vis_types/timeseries/public/application/components/splits/__snapshots__/terms.test.js.snap @@ -90,6 +90,7 @@ exports[`src/legacy/core_plugins/metrics/public/components/splits/terms.test.js @@ -113,6 +114,7 @@ exports[`src/legacy/core_plugins/metrics/public/components/splits/terms.test.js diff --git a/src/plugins/vis_types/timeseries/public/application/components/splits/terms.js b/src/plugins/vis_types/timeseries/public/application/components/splits/terms.js index bfc31f7e46d48..071e91ffee981 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/splits/terms.js +++ b/src/plugins/vis_types/timeseries/public/application/components/splits/terms.js @@ -95,6 +95,8 @@ export const SplitByTermsUI = ({ (selectedOptions) => { onChange({ terms_field: selectedOptions.length === 1 ? selectedOptions[0] : selectedOptions, + terms_include: undefined, + terms_exclude: undefined, }); }, [onChange] @@ -174,7 +176,7 @@ export const SplitByTermsUI = ({ } > @@ -191,7 +193,7 @@ export const SplitByTermsUI = ({ } > diff --git a/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx b/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx index 6784943b11188..181ea661b69f8 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx @@ -37,6 +37,7 @@ interface TimeseriesVisualizationProps { visData: TimeseriesVisData; uiState: PersistedState; syncColors: boolean; + syncTooltips: boolean; } function TimeseriesVisualization({ @@ -46,6 +47,7 @@ function TimeseriesVisualization({ uiState, getConfig, syncColors, + syncTooltips, }: TimeseriesVisualizationProps) { const [indexPattern, setIndexPattern] = useState(null); const [palettesService, setPalettesService] = useState(null); @@ -123,7 +125,7 @@ function TimeseriesVisualization({ const data = getClickFilterData(points, tables, model); const event = { - name: 'filterBucket', + name: 'filter', data: { data, negate: false, @@ -188,6 +190,7 @@ function TimeseriesVisualization({ onFilterClick={handleFilterClick} onUiState={handleUiState} syncColors={syncColors} + syncTooltips={syncTooltips} palettesService={palettesService} indexPattern={indexPattern} fieldFormatMap={indexPattern?.fieldFormatMap} diff --git a/src/plugins/vis_types/timeseries/public/application/components/vis_types/index.ts b/src/plugins/vis_types/timeseries/public/application/components/vis_types/index.ts index bbc4dcddf6128..68be45ff6eec0 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/vis_types/index.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/index.ts @@ -61,6 +61,7 @@ export interface TimeseriesVisProps { visData: TimeseriesVisData; getConfig: IUiSettingsClient['get']; syncColors: boolean; + syncTooltips: boolean; palettesService: PaletteRegistry; indexPattern?: FetchedIndexPattern['indexPattern']; /** @deprecated please use indexPattern.fieldFormatMap instead **/ diff --git a/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js index 007e763997e13..e79c3466b0bb2 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js @@ -162,6 +162,7 @@ class TimeseriesVisualization extends Component { onBrush, onFilterClick, syncColors, + syncTooltips, palettesService, fieldFormatMap, getConfig, @@ -272,6 +273,7 @@ class TimeseriesVisualization extends Component { xAxisFormatter={this.xAxisFormatter(interval)} annotations={this.prepareAnnotations()} syncColors={syncColors} + syncTooltips={syncTooltips} palettesService={palettesService} interval={interval} useLegacyTimeAxis={getConfig(LEGACY_TIME_AXIS, false)} diff --git a/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js index 3fff0f2cce6cb..aab3b45b34447 100644 --- a/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js +++ b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js @@ -20,6 +20,7 @@ import { LineAnnotation, TooltipType, StackMode, + Placement, } from '@elastic/charts'; import { EuiIcon } from '@elastic/eui'; import { getTimezone } from '../../../lib/get_timezone'; @@ -73,6 +74,7 @@ export const TimeSeries = ({ xAxisFormatter, annotations, syncColors, + syncTooltips, palettesService, interval, isLastBucketDropped, @@ -213,7 +215,9 @@ export const TimeSeries = ({ boundary: document.getElementById('app-fixed-viewport') ?? undefined, headerFormatter: tooltipFormatter, }} - externalPointerEvents={{ tooltip: { visible: false } }} + externalPointerEvents={{ + tooltip: { visible: syncTooltips, placement: Placement.Right }, + }} /> {annotations.map(({ id, data, icon, color }) => { diff --git a/src/plugins/vis_types/timeseries/public/metrics_fn.ts b/src/plugins/vis_types/timeseries/public/metrics_fn.ts index 58b1f581b4a65..9562e19f3f0ec 100644 --- a/src/plugins/vis_types/timeseries/public/metrics_fn.ts +++ b/src/plugins/vis_types/timeseries/public/metrics_fn.ts @@ -26,6 +26,7 @@ export interface TimeseriesRenderValue { visData: TimeseriesVisData | {}; visParams: TimeseriesVisParams; syncColors: boolean; + syncTooltips: boolean; } export type TimeseriesExpressionFunctionDefinition = ExpressionFunctionDefinition< @@ -60,6 +61,7 @@ export const createMetricsFn = (): TimeseriesExpressionFunctionDefinition => ({ { getSearchSessionId, isSyncColorsEnabled, + isSyncTooltipsEnabled, getExecutionContext, inspectorAdapters, abortSignal: expressionAbortSignal, @@ -68,6 +70,7 @@ export const createMetricsFn = (): TimeseriesExpressionFunctionDefinition => ({ const visParams: TimeseriesVisParams = JSON.parse(args.params); const uiState = JSON.parse(args.uiState); const syncColors = isSyncColorsEnabled?.() ?? false; + const syncTooltips = isSyncTooltipsEnabled?.() ?? false; const response = await metricsRequestHandler({ input, @@ -86,6 +89,7 @@ export const createMetricsFn = (): TimeseriesExpressionFunctionDefinition => ({ visParams, visData: response, syncColors, + syncTooltips, }, }; }, diff --git a/src/plugins/vis_types/timeseries/public/timeseries_vis_renderer.tsx b/src/plugins/vis_types/timeseries/public/timeseries_vis_renderer.tsx index 5c4e124c92d31..3e2b5a9c9e6a3 100644 --- a/src/plugins/vis_types/timeseries/public/timeseries_vis_renderer.tsx +++ b/src/plugins/vis_types/timeseries/public/timeseries_vis_renderer.tsx @@ -50,7 +50,7 @@ export const getTimeseriesVisRenderer: (deps: { handlers.onDestroy(() => { unmountComponentAtNode(domNode); }); - const { visParams: model, visData, syncColors } = config; + const { visParams: model, visData, syncColors, syncTooltips } = config; const showNoResult = !checkIfDataExists(visData, model); @@ -70,6 +70,7 @@ export const getTimeseriesVisRenderer: (deps: { model={model} visData={visData as TimeseriesVisData} syncColors={syncColors} + syncTooltips={syncTooltips} uiState={handlers.uiState! as PersistedState} /> diff --git a/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_terms.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_terms.js index 07e8ef4a0e5fd..d093019fee3d8 100644 --- a/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_terms.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_terms.js @@ -37,15 +37,16 @@ export function splitByTerms(req, panel, series, esQueryConfig, seriesIndex) { ); } else { overwrite(doc, `aggs.${series.id}.${termsType}.field`, termsIds[0]); + if (series.terms_include) { + overwrite(doc, `aggs.${series.id}.${termsType}.include`, series.terms_include); + } + if (series.terms_exclude) { + overwrite(doc, `aggs.${series.id}.${termsType}.exclude`, series.terms_exclude); + } } overwrite(doc, `aggs.${series.id}.${termsType}.size`, series.terms_size); - if (series.terms_include) { - overwrite(doc, `aggs.${series.id}.${termsType}.include`, series.terms_include); - } - if (series.terms_exclude) { - overwrite(doc, `aggs.${series.id}.${termsType}.exclude`, series.terms_exclude); - } + if (metric && metric.type !== 'count' && ~basicAggs.indexOf(metric.type)) { const sortAggKey = `${orderByTerms}-SORT`; const fn = bucketTransform[metric.type]; diff --git a/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_terms.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_terms.test.js index 984eb385ca4a6..0795e3390eb2f 100644 --- a/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_terms.test.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_terms.test.js @@ -111,6 +111,37 @@ describe('splitByTerms', () => { }); }); + test('should ignore "include/exclude" for multi terms', () => { + series.terms_include = 'a'; + series.terms_exclude = 'b'; + series.terms_field = ['c', 'd']; + const next = jest.fn((doc) => doc); + const doc = splitByTerms(req, panel, series, config, seriesIndex)(next)({}); + + expect(doc).toMatchInlineSnapshot(` + Object { + "aggs": Object { + "test": Object { + "multi_terms": Object { + "order": Object { + "_count": "desc", + }, + "size": 10, + "terms": Array [ + Object { + "field": "c", + }, + Object { + "field": "d", + }, + ], + }, + }, + }, + } + `); + }); + test('calls next and does not add a terms agg', () => { series.split_mode = 'everything'; const next = jest.fn((doc) => doc); diff --git a/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/math.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/math.js index 46dde6653647d..d14757f3e229b 100644 --- a/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/math.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/math.js @@ -7,16 +7,18 @@ */ import { convertIntervalToUnit } from '../../helpers/unit_to_seconds'; +import { getLastMetric } from '../../helpers/get_last_metric'; import { SERIES_SEPARATOR } from '../../../../../common/constants'; const percentileValueMatch = /\[([0-9\.]+)\]$/; import { startsWith, flatten, values, first, last } from 'lodash'; import { getDefaultDecoration, getSiblingAggValue, getSplits, mapEmptyToZero } from '../../helpers'; import { evaluate } from '@kbn/tinymath'; +import { TSVB_METRIC_TYPES } from '../../../../../common/enums'; export function mathAgg(resp, panel, series, meta, extractFields) { return (next) => async (results) => { - const mathMetric = last(series.metrics); + const mathMetric = getLastMetric(series); if (mathMetric.type !== 'math') return next(results); // Filter the results down to only the ones that match the series.id. Sometimes // there will be data from other series mixed in. @@ -41,7 +43,13 @@ export function mathAgg(resp, panel, series, meta, extractFields) { }); } else { const percentileMatch = v.field.match(percentileValueMatch); - const m = percentileMatch ? { ...metric, percent: percentileMatch[1] } : { ...metric }; + const m = percentileMatch + ? { + ...metric, + [metric.type === TSVB_METRIC_TYPES.PERCENTILE ? 'percent' : 'value']: + percentileMatch[1], + } + : { ...metric }; acc[v.name] = mapEmptyToZero(m, split.timeseries.buckets); } return acc; diff --git a/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/math.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/math.test.js index 14c66866d323a..3ff2ee0cba353 100644 --- a/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/math.test.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/math.test.js @@ -108,6 +108,91 @@ describe('math(resp, panel, series)', () => { }); }); + test('works with percentiles and percentile rank', async () => { + series.metrics = [ + { + id: 'percentile_cpu', + type: 'percentile', + field: 'cpu', + percentiles: [{ value: 50, id: 'p50' }], + }, + { + id: 'rank_cpu', + type: 'percentile_rank', + field: 'cpu', + percentiles: [{ value: 500, id: 'p500' }], + }, + { + id: 'mathagg', + type: 'math', + script: 'divide(params.a, params.b)', + variables: [ + { name: 'a', field: 'percentile_cpu[50.0]' }, + { name: 'b', field: 'rank_cpu[500.0]' }, + ], + }, + ]; + resp.aggregations.test.buckets[0].timeseries.buckets[0].percentile_cpu = { + values: { '50.0': 0.25 }, + }; + resp.aggregations.test.buckets[0].timeseries.buckets[0].rank_cpu = { + values: { '500.0': 0.125 }, + }; + resp.aggregations.test.buckets[0].timeseries.buckets[1].percentile_cpu = { + values: { '50.0': 0.25 }, + }; + resp.aggregations.test.buckets[0].timeseries.buckets[1].rank_cpu = { + values: { '500.0': 0.25 }, + }; + + const next = await mathAgg(resp, panel, series)((results) => results); + const results = await stdMetric(resp, panel, series)(next)([]); + + expect(results).toHaveLength(1); + + expect(results[0]).toEqual({ + id: 'test╰┄►example-01', + label: 'example-01', + color: 'rgb(255, 0, 0)', + stack: false, + seriesId: 'test', + lines: { show: true, fill: 0, lineWidth: 1, steps: false }, + points: { show: true, radius: 1, lineWidth: 1 }, + bars: { fill: 0, lineWidth: 1, show: false }, + data: [ + [1, 2], + [2, 1], + ], + }); + }); + + test('handles math even if there is a series agg', async () => { + series.metrics.push({ + id: 'myid', + type: 'series_agg', + function: 'sum', + }); + const next = await mathAgg(resp, panel, series)((results) => results); + const results = await stdMetric(resp, panel, series)(next)([]); + + expect(results).toHaveLength(1); + + expect(results[0]).toEqual({ + id: 'test╰┄►example-01', + label: 'example-01', + color: 'rgb(255, 0, 0)', + stack: false, + seriesId: 'test', + lines: { show: true, fill: 0, lineWidth: 1, steps: false }, + points: { show: true, radius: 1, lineWidth: 1 }, + bars: { fill: 0, lineWidth: 1, show: false }, + data: [ + [1, 2], + [2, 1], + ], + }); + }); + test('turns division by zero into null values', async () => { resp.aggregations.test.buckets[0].timeseries.buckets[0].mincpu = 0; const next = await mathAgg(resp, panel, series)((results) => results); diff --git a/src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.js.snap b/src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.js.snap index c70c4406a34f2..f49b0bb75951f 100644 --- a/src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.js.snap +++ b/src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`VegaVisualizations VegaVisualization - basics should show vega graph (may fail in dev env) 1`] = `"
"`; +exports[`VegaVisualizations VegaVisualization - basics should show vega graph (may fail in dev env) 1`] = `"
"`; -exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 1`] = `"
  • \\"width\\" and \\"height\\" params are ignored because \\"autosize\\" is enabled. Set \\"autosize\\": \\"none\\" to disable
"`; +exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 1`] = `"
  • \\"width\\" and \\"height\\" params are ignored because \\"autosize\\" is enabled. Set \\"autosize\\": \\"none\\" to disable
"`; -exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 2`] = `"
  • \\"width\\" and \\"height\\" params are ignored because \\"autosize\\" is enabled. Set \\"autosize\\": \\"none\\" to disable
"`; +exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 2`] = `"
  • \\"width\\" and \\"height\\" params are ignored because \\"autosize\\" is enabled. Set \\"autosize\\": \\"none\\" to disable
"`; diff --git a/src/plugins/vis_types/vega/public/components/vega_vis.scss b/src/plugins/vis_types/vega/public/components/vega_vis.scss index ce66dc1d7f928..cee2cd16ad0a3 100644 --- a/src/plugins/vis_types/vega/public/components/vega_vis.scss +++ b/src/plugins/vis_types/vega/public/components/vega_vis.scss @@ -14,15 +14,21 @@ // flex-direction determined by js } +.vgaVis--autoresize { + @include euiScrollBar; + max-width: 100%; + max-height: 100%; + overflow: auto; + .vgaVis__view { + overflow: hidden; + } +} + .vgaVis__view { z-index: 0; flex: 1 1 100%; display: block; - max-width: 100%; - max-height: 100%; - width: 100%; - height: 100%; canvas { display: block; diff --git a/src/plugins/vis_types/vega/public/data_model/search_api.ts b/src/plugins/vis_types/vega/public/data_model/search_api.ts index 042bc34dea0f1..530449da9aa26 100644 --- a/src/plugins/vis_types/vega/public/data_model/search_api.ts +++ b/src/plugins/vis_types/vega/public/data_model/search_api.ts @@ -30,7 +30,7 @@ export const extendSearchParamsWithRuntimeFields = async ( let runtimeMappings = requestParams.body?.runtime_mappings; if (!runtimeMappings) { - const indexPattern = (await indexPatterns.find(indexPatternString)).find( + const indexPattern = (await indexPatterns.find(indexPatternString, 1)).find( (index) => index.title === indexPatternString ); runtimeMappings = indexPattern?.getRuntimeMappings(); diff --git a/src/plugins/vis_types/vega/public/lib/extract_index_pattern.ts b/src/plugins/vis_types/vega/public/lib/extract_index_pattern.ts index 7a52ca254f7ea..c8335030aac19 100644 --- a/src/plugins/vis_types/vega/public/lib/extract_index_pattern.ts +++ b/src/plugins/vis_types/vega/public/lib/extract_index_pattern.ts @@ -26,7 +26,7 @@ export const extractIndexPatternsFromSpec = async (spec: VegaSpec) => { await Promise.all( data.reduce>>((accumulator, currentValue) => { if (currentValue.url?.index) { - accumulator.push(dataViews.find(currentValue.url.index)); + accumulator.push(dataViews.find(currentValue.url.index, 1)); } return accumulator; diff --git a/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js b/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js index e518894c5511a..90112e294d56b 100644 --- a/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js +++ b/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js @@ -98,6 +98,11 @@ export class VegaBaseView { this._initialized = true; try { + if (this._parser.useResize) { + this._$parentEl.addClass('vgaVis--autoresize'); + } else { + this._$parentEl.removeClass('vgaVis--autoresize'); + } this._$parentEl.empty().addClass(`vgaVis`).css('flex-direction', this._parser.containerDir); // bypass the onWarn warning checks - in some cases warnings may still need to be shown despite being disabled @@ -110,10 +115,7 @@ export class VegaBaseView { return; } - this._$container = $('
') - // Force a height here because css is not loaded in mocha test - .css('height', '100%') - .appendTo(this._$parentEl); + this._$container = $('
').appendTo(this._$parentEl); this._$controls = $( `
` ).appendTo(this._$parentEl); @@ -160,7 +162,7 @@ export class VegaBaseView { let idxObj; if (index) { - [idxObj] = await dataViews.find(index); + [idxObj] = await dataViews.find(index, 1); if (!idxObj) { throw new Error( i18n.translate('visTypeVega.vegaParser.baseView.indexNotFoundErrorMessage', { @@ -262,9 +264,9 @@ export class VegaBaseView { } } - async resize(dimensions) { + async resize() { if (this._parser.useResize && this._view) { - this.updateVegaSize(this._view, dimensions); + this.updateVegaSize(this._view); await this._view.runAsync(); // The derived class should create this method diff --git a/src/plugins/vis_types/vega/public/vega_view/vega_view.js b/src/plugins/vis_types/vega/public/vega_view/vega_view.js index 11b7ca125a4bd..a5241c9f0f0db 100644 --- a/src/plugins/vis_types/vega/public/vega_view/vega_view.js +++ b/src/plugins/vis_types/vega/public/vega_view/vega_view.js @@ -18,6 +18,8 @@ export class VegaView extends VegaBaseView { if (this._parser.useResize) this.updateVegaSize(view); view.initialize(this._$container.get(0), this._$controls.get(0)); + // resize again to take controls into account + if (this._parser.useResize) this.updateVegaSize(view); if (this._parser.useHover) view.hover(); diff --git a/src/plugins/vis_types/vislib/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_types/vislib/public/__snapshots__/to_ast.test.ts.snap index 233940d97d38a..6d20088dbff32 100644 --- a/src/plugins/vis_types/vislib/public/__snapshots__/to_ast.test.ts.snap +++ b/src/plugins/vis_types/vislib/public/__snapshots__/to_ast.test.ts.snap @@ -8,7 +8,7 @@ Object { "area", ], "visConfig": Array [ - "{\\"type\\":\\"area\\",\\"grid\\":{\\"categoryLines\\":false,\\"style\\":{\\"color\\":\\"#eee\\"}},\\"categoryAxes\\":[{\\"id\\":\\"CategoryAxis-1\\",\\"type\\":\\"category\\",\\"position\\":\\"bottom\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\"},\\"labels\\":{\\"show\\":true,\\"truncate\\":100},\\"title\\":{}}],\\"valueAxes\\":[{\\"id\\":\\"ValueAxis-1\\",\\"name\\":\\"LeftAxis-1\\",\\"type\\":\\"value\\",\\"position\\":\\"left\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\",\\"mode\\":\\"normal\\"},\\"labels\\":{\\"show\\":true,\\"rotate\\":0,\\"filter\\":false,\\"truncate\\":100},\\"title\\":{\\"text\\":\\"Sum of total_quantity\\"}}],\\"seriesParams\\":[{\\"show\\":\\"true\\",\\"type\\":\\"area\\",\\"mode\\":\\"stacked\\",\\"data\\":{\\"label\\":\\"Sum of total_quantity\\",\\"id\\":\\"1\\"},\\"drawLinesBetweenPoints\\":true,\\"showCircles\\":true,\\"circlesRadius\\":5,\\"interpolate\\":\\"linear\\",\\"valueAxis\\":\\"ValueAxis-1\\"}],\\"addTooltip\\":true,\\"addLegend\\":true,\\"legendPosition\\":\\"top\\",\\"times\\":[],\\"addTimeMarker\\":false,\\"truncateLegend\\":true,\\"maxLegendLines\\":1,\\"thresholdLine\\":{\\"show\\":false,\\"value\\":10,\\"width\\":1,\\"style\\":\\"full\\",\\"color\\":\\"#E7664C\\"},\\"palette\\":{\\"name\\":\\"default\\"},\\"labels\\":{},\\"dimensions\\":{\\"x\\":{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"date\\",\\"params\\":{\\"pattern\\":\\"HH:mm:ss.SSS\\"}},\\"params\\":{}},\\"y\\":[{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"number\\",\\"params\\":{\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}],\\"series\\":[{\\"accessor\\":2,\\"format\\":{\\"id\\":\\"terms\\",\\"params\\":{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\",\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}]}}", + "{\\"type\\":\\"area\\",\\"grid\\":{\\"categoryLines\\":false,\\"style\\":{\\"color\\":\\"#eee\\"}},\\"categoryAxes\\":[{\\"id\\":\\"CategoryAxis-1\\",\\"type\\":\\"category\\",\\"position\\":\\"bottom\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\"},\\"labels\\":{\\"show\\":true,\\"truncate\\":100},\\"title\\":{}}],\\"valueAxes\\":[{\\"id\\":\\"ValueAxis-1\\",\\"name\\":\\"LeftAxis-1\\",\\"type\\":\\"value\\",\\"position\\":\\"left\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\",\\"mode\\":\\"normal\\"},\\"labels\\":{\\"show\\":true,\\"rotate\\":0,\\"filter\\":false,\\"truncate\\":100},\\"title\\":{\\"text\\":\\"Sum of total_quantity\\"}}],\\"seriesParams\\":[{\\"show\\":\\"true\\",\\"type\\":\\"area\\",\\"mode\\":\\"stacked\\",\\"data\\":{\\"label\\":\\"Sum of total_quantity\\",\\"id\\":\\"1\\"},\\"drawLinesBetweenPoints\\":true,\\"showCircles\\":true,\\"circlesRadius\\":5,\\"interpolate\\":\\"linear\\",\\"valueAxis\\":\\"ValueAxis-1\\"}],\\"addTooltip\\":true,\\"addLegend\\":true,\\"legendPosition\\":\\"top\\",\\"legendSize\\":\\"small\\",\\"times\\":[],\\"addTimeMarker\\":false,\\"truncateLegend\\":true,\\"maxLegendLines\\":1,\\"thresholdLine\\":{\\"show\\":false,\\"value\\":10,\\"width\\":1,\\"style\\":\\"full\\",\\"color\\":\\"#E7664C\\"},\\"palette\\":{\\"name\\":\\"default\\"},\\"labels\\":{},\\"dimensions\\":{\\"x\\":{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"date\\",\\"params\\":{\\"pattern\\":\\"HH:mm:ss.SSS\\"}},\\"params\\":{}},\\"y\\":[{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"number\\",\\"params\\":{\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}],\\"series\\":[{\\"accessor\\":2,\\"format\\":{\\"id\\":\\"terms\\",\\"params\\":{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\",\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}]}}", ], }, "getArgument": [Function], diff --git a/src/plugins/vis_types/vislib/public/__snapshots__/to_ast_pie.test.ts.snap b/src/plugins/vis_types/vislib/public/__snapshots__/to_ast_pie.test.ts.snap index 1eedae99ffedb..80e52d95be5c9 100644 --- a/src/plugins/vis_types/vislib/public/__snapshots__/to_ast_pie.test.ts.snap +++ b/src/plugins/vis_types/vislib/public/__snapshots__/to_ast_pie.test.ts.snap @@ -5,7 +5,7 @@ Object { "addArgument": [Function], "arguments": Object { "visConfig": Array [ - "{\\"type\\":\\"pie\\",\\"addTooltip\\":true,\\"legendDisplay\\":\\"show\\",\\"legendPosition\\":\\"right\\",\\"isDonut\\":true,\\"labels\\":{\\"show\\":true,\\"values\\":true,\\"last_level\\":true,\\"truncate\\":100},\\"dimensions\\":{\\"metric\\":{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"number\\"},\\"params\\":{}},\\"buckets\\":[{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"terms\\",\\"params\\":{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\",\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}]}}", + "{\\"type\\":\\"pie\\",\\"addTooltip\\":true,\\"legendDisplay\\":\\"show\\",\\"legendPosition\\":\\"right\\",\\"legendSize\\":\\"large\\",\\"isDonut\\":true,\\"labels\\":{\\"show\\":true,\\"values\\":true,\\"last_level\\":true,\\"truncate\\":100},\\"dimensions\\":{\\"metric\\":{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"number\\"},\\"params\\":{}},\\"buckets\\":[{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"terms\\",\\"params\\":{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\",\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}]}}", ], }, "getArgument": [Function], diff --git a/src/plugins/vis_types/vislib/public/vislib/components/legend/legend.test.tsx b/src/plugins/vis_types/vislib/public/vislib/components/legend/legend.test.tsx index 7f948917764df..5c4c4e3c2c145 100644 --- a/src/plugins/vis_types/vislib/public/vislib/components/legend/legend.test.tsx +++ b/src/plugins/vis_types/vislib/public/vislib/components/legend/legend.test.tsx @@ -201,7 +201,7 @@ describe('VisLegend Component', () => { }); expect(fireEvent).toHaveBeenCalledWith({ - name: 'filterBucket', + name: 'filter', data: { data: ['valuesA'], negate: false }, }); expect(fireEvent).toHaveBeenCalledTimes(1); @@ -216,7 +216,7 @@ describe('VisLegend Component', () => { }); expect(fireEvent).toHaveBeenCalledWith({ - name: 'filterBucket', + name: 'filter', data: { data: ['valuesA'], negate: true }, }); expect(fireEvent).toHaveBeenCalledTimes(1); diff --git a/src/plugins/vis_types/vislib/public/vislib/components/legend/legend.tsx b/src/plugins/vis_types/vislib/public/vislib/components/legend/legend.tsx index 577a76dd84454..fedeb03cdde28 100644 --- a/src/plugins/vis_types/vislib/public/vislib/components/legend/legend.tsx +++ b/src/plugins/vis_types/vislib/public/vislib/components/legend/legend.tsx @@ -87,7 +87,7 @@ export class VisLegend extends PureComponent { filter = ({ values: data }: LegendItem, negate: boolean) => { this.props.fireEvent({ - name: 'filterBucket', + name: 'filter', data: { data, negate, diff --git a/src/plugins/vis_types/vislib/public/vislib/lib/handler.js b/src/plugins/vis_types/vislib/public/vislib/lib/handler.js index fe8388e025b94..177febfb2812c 100644 --- a/src/plugins/vis_types/vislib/public/vislib/lib/handler.js +++ b/src/plugins/vis_types/vislib/public/vislib/lib/handler.js @@ -95,7 +95,7 @@ export class Handler { }); case 'click': return self.vis.emit(eventType, { - name: 'filterBucket', + name: 'filter', data: eventPayload, }); } diff --git a/src/plugins/vis_types/xy/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_types/xy/public/__snapshots__/to_ast.test.ts.snap index 7ee1b0d2b2053..048b07dbf34ed 100644 --- a/src/plugins/vis_types/xy/public/__snapshots__/to_ast.test.ts.snap +++ b/src/plugins/vis_types/xy/public/__snapshots__/to_ast.test.ts.snap @@ -32,6 +32,9 @@ Object { "legendPosition": Array [ "top", ], + "legendSize": Array [ + "small", + ], "maxLegendLines": Array [ 1, ], diff --git a/src/plugins/vis_types/xy/public/components/xy_settings.tsx b/src/plugins/vis_types/xy/public/components/xy_settings.tsx index 836a46236e228..f934a2c203196 100644 --- a/src/plugins/vis_types/xy/public/components/xy_settings.tsx +++ b/src/plugins/vis_types/xy/public/components/xy_settings.tsx @@ -51,6 +51,7 @@ type XYSettingsProps = Pick< | 'orderBucketsBySum' > & { onPointerUpdate: SettingsProps['onPointerUpdate']; + externalPointerEvents: SettingsProps['externalPointerEvents']; xDomain?: DomainRange; adjustedXDomain?: DomainRange; showLegend: boolean; @@ -91,6 +92,7 @@ export const XYSettings: FC = ({ showLegend, onElementClick, onPointerUpdate, + externalPointerEvents, onBrushEnd, onRenderChange, legendAction, @@ -163,6 +165,7 @@ export const XYSettings: FC = ({ ) [stateParams.seriesParams, aggs.aggs] ); + const legendSize = stateParams.legendSize; + + const [hadAutoLegendSize] = useState(() => legendSize === LegendSize.AUTO); + const handleLegendSizeChange = useCallback((size) => setValue('legendSize', size), [setValue]); return ( @@ -64,12 +69,13 @@ export function PointSeriesOptions(props: ValidationVisOptionsProps) setValue={setValue} /> {vis.data.aggs!.aggs.some( diff --git a/src/plugins/vis_types/xy/public/expression_functions/xy_vis_fn.ts b/src/plugins/vis_types/xy/public/expression_functions/xy_vis_fn.ts index 9ed19e2549389..08319e8e9a11b 100644 --- a/src/plugins/vis_types/xy/public/expression_functions/xy_vis_fn.ts +++ b/src/plugins/vis_types/xy/public/expression_functions/xy_vis_fn.ts @@ -13,7 +13,12 @@ import type { Datatable, Render, } from '@kbn/expressions-plugin/common'; -import { prepareLogTable, Dimension } from '@kbn/visualizations-plugin/public'; +import { + prepareLogTable, + Dimension, + DEFAULT_LEGEND_SIZE, + LegendSize, +} from '@kbn/visualizations-plugin/public'; import type { ChartType } from '../../common'; import type { VisParams, XYVisConfig } from '../types'; @@ -23,6 +28,7 @@ export interface RenderValue { visType: ChartType; visConfig: VisParams; syncColors: boolean; + syncTooltips: boolean; } export type VisTypeXyExpressionFunctionDefinition = ExpressionFunctionDefinition< @@ -72,10 +78,19 @@ export const visTypeXyVisFn = (): VisTypeXyExpressionFunctionDefinition => ({ }), }, legendSize: { - types: ['number'], + types: ['string'], + default: DEFAULT_LEGEND_SIZE, help: i18n.translate('visTypeXy.function.args.args.legendSize.help', { - defaultMessage: 'Specifies the legend size in pixels.', + defaultMessage: 'Specifies the legend size.', }), + options: [ + LegendSize.AUTO, + LegendSize.SMALL, + LegendSize.MEDIUM, + LegendSize.LARGE, + LegendSize.EXTRA_LARGE, + ], + strict: true, }, addLegend: { types: ['boolean'], @@ -348,6 +363,7 @@ export const visTypeXyVisFn = (): VisTypeXyExpressionFunctionDefinition => ({ visConfig, visData: context, syncColors: handlers?.isSyncColorsEnabled?.() ?? false, + syncTooltips: handlers?.isSyncTooltipsEnabled?.() ?? false, }, }; }, diff --git a/src/plugins/vis_types/xy/public/sample_vis.test.mocks.ts b/src/plugins/vis_types/xy/public/sample_vis.test.mocks.ts index 436a284b1657a..3c1d87d2efc3c 100644 --- a/src/plugins/vis_types/xy/public/sample_vis.test.mocks.ts +++ b/src/plugins/vis_types/xy/public/sample_vis.test.mocks.ts @@ -5,6 +5,9 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + +import { LegendSize } from '@kbn/visualizations-plugin/common'; + export const sampleAreaVis = { type: { name: 'area', @@ -282,6 +285,7 @@ export const sampleAreaVis = { addTooltip: true, addLegend: true, legendPosition: 'top', + legendSize: LegendSize.SMALL, times: [], addTimeMarker: false, truncateLegend: true, diff --git a/src/plugins/vis_types/xy/public/types/param.ts b/src/plugins/vis_types/xy/public/types/param.ts index 708eb1cbdd196..a491efad97fcb 100644 --- a/src/plugins/vis_types/xy/public/types/param.ts +++ b/src/plugins/vis_types/xy/public/types/param.ts @@ -15,6 +15,7 @@ import type { FakeParams, HistogramParams, DateHistogramParams, + LegendSize, } from '@kbn/visualizations-plugin/public'; import type { ChartType, XyVisType } from '../../common'; import type { @@ -124,7 +125,7 @@ export interface VisParams { addTimeMarker: boolean; truncateLegend: boolean; maxLegendLines: number; - legendSize?: number; + legendSize?: LegendSize; categoryAxes: CategoryAxis[]; orderBucketsBySum?: boolean; labels: Labels; @@ -165,7 +166,7 @@ export interface XYVisConfig { addTimeMarker: boolean; truncateLegend: boolean; maxLegendLines: number; - legendSize?: number; + legendSize?: LegendSize; orderBucketsBySum?: boolean; labels: ExpressionValueLabel; thresholdLine: ExpressionValueThresholdLine; diff --git a/src/plugins/vis_types/xy/public/vis_component.tsx b/src/plugins/vis_types/xy/public/vis_component.tsx index 3bc7eb4a77c52..a744841601a67 100644 --- a/src/plugins/vis_types/xy/public/vis_component.tsx +++ b/src/plugins/vis_types/xy/public/vis_component.tsx @@ -20,6 +20,7 @@ import { AccessorFn, Accessor, XYBrushEvent, + Placement, } from '@elastic/charts'; import { compact } from 'lodash'; @@ -32,7 +33,11 @@ import { useActiveCursor, } from '@kbn/charts-plugin/public'; import { Datatable, IInterpreterRenderHandlers } from '@kbn/expressions-plugin/public'; -import type { PersistedState } from '@kbn/visualizations-plugin/public'; +import { + DEFAULT_LEGEND_SIZE, + LegendSizeToPixels, + PersistedState, +} from '@kbn/visualizations-plugin/public'; import { VisParams } from './types'; import { getAdjustedDomain, @@ -65,6 +70,7 @@ export interface VisComponentProps { fireEvent: IInterpreterRenderHandlers['event']; renderComplete: IInterpreterRenderHandlers['done']; syncColors: boolean; + syncTooltips: boolean; useLegacyTimeAxis: boolean; } @@ -210,7 +216,7 @@ const VisComponent = (props: VisComponentProps) => { [props.uiState] ); - const { visData, visParams, syncColors } = props; + const { visData, visParams, syncColors, syncTooltips } = props; const isDarkMode = getThemeService().useDarkMode(); const config = getConfig(visData, visParams, props.useLegacyTimeAxis, isDarkMode); @@ -355,8 +361,11 @@ const VisComponent = (props: VisComponentProps) => { maxLegendLines={visParams.maxLegendLines} showLegend={showLegend} onPointerUpdate={handleCursorUpdate} + externalPointerEvents={{ + tooltip: { visible: syncTooltips, placement: Placement.Right }, + }} legendPosition={legendPosition} - legendSize={visParams.legendSize} + legendSize={LegendSizeToPixels[visParams.legendSize ?? DEFAULT_LEGEND_SIZE]} xDomain={xDomain} adjustedXDomain={adjustedXDomain} legendColorPicker={legendColorPicker} diff --git a/src/plugins/vis_types/xy/public/vis_renderer.tsx b/src/plugins/vis_types/xy/public/vis_renderer.tsx index abdb4c0cca6cf..271d9147b280b 100644 --- a/src/plugins/vis_types/xy/public/vis_renderer.tsx +++ b/src/plugins/vis_types/xy/public/vis_renderer.tsx @@ -38,7 +38,7 @@ export const getXYVisRenderer: (deps: { name: visName, displayName: 'XY visualization', reuseDomNode: true, - render: async (domNode, { visData, visConfig, visType, syncColors }, handlers) => { + render: async (domNode, { visData, visConfig, visType, syncColors, syncTooltips }, handlers) => { const showNoResult = shouldShowNoResultsMessage(visData, visType); handlers.onDestroy(() => unmountComponentAtNode(domNode)); @@ -53,6 +53,7 @@ export const getXYVisRenderer: (deps: { fireEvent={handlers.event} uiState={handlers.uiState as PersistedState} syncColors={syncColors} + syncTooltips={syncTooltips} useLegacyTimeAxis={uiSettings.get(LEGACY_TIME_AXIS, false)} /> diff --git a/src/plugins/visualizations/common/constants.ts b/src/plugins/visualizations/common/constants.ts index 0b840c8ff13fc..ea695e6bdca02 100644 --- a/src/plugins/visualizations/common/constants.ts +++ b/src/plugins/visualizations/common/constants.ts @@ -26,3 +26,21 @@ export const VisualizeConstants = { EDIT_BY_VALUE_PATH: '/edit_by_value', APP_ID: 'visualize', }; + +export enum LegendSize { + AUTO = 'auto', + SMALL = 'small', + MEDIUM = 'medium', + LARGE = 'large', + EXTRA_LARGE = 'xlarge', +} + +export const LegendSizeToPixels = { + [LegendSize.AUTO]: undefined, + [LegendSize.SMALL]: 80, + [LegendSize.MEDIUM]: 130, + [LegendSize.LARGE]: 180, + [LegendSize.EXTRA_LARGE]: 230, +} as const; + +export const DEFAULT_LEGEND_SIZE = LegendSize.MEDIUM; diff --git a/src/plugins/visualizations/common/index.ts b/src/plugins/visualizations/common/index.ts index d784fcfd09eb9..1dd9a0e90477c 100644 --- a/src/plugins/visualizations/common/index.ts +++ b/src/plugins/visualizations/common/index.ts @@ -13,3 +13,4 @@ export * from './types'; export * from './utils'; export * from './expression_functions'; +export { LegendSize, LegendSizeToPixels, DEFAULT_LEGEND_SIZE } from './constants'; diff --git a/src/plugins/visualizations/common/types.ts b/src/plugins/visualizations/common/types.ts index 978e424477be6..8aa03470b2094 100644 --- a/src/plugins/visualizations/common/types.ts +++ b/src/plugins/visualizations/common/types.ts @@ -6,10 +6,9 @@ * Side Public License, v 1. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { SavedObjectAttributes } from '@kbn/core/server'; import type { SerializableRecord } from '@kbn/utility-types'; -import { AggConfigSerialized, SerializedSearchSourceFields } from '@kbn/data-plugin/common'; +import type { AggConfigSerialized, SerializedSearchSourceFields } from '@kbn/data-plugin/common'; +import type { SavedObjectAttributes } from '@kbn/core/types'; export interface VisParams { [key: string]: any; diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx index efb60c16b4bf9..8cff4ccdeb4e9 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx @@ -93,6 +93,7 @@ export class VisualizeEmbeddable private filters?: Filter[]; private searchSessionId?: string; private syncColors?: boolean; + private syncTooltips?: boolean; private embeddableTitle?: string; private visCustomizations?: Pick; private subscriptions: Subscription[] = []; @@ -135,6 +136,7 @@ export class VisualizeEmbeddable this.deps = deps; this.timefilter = timefilter; this.syncColors = this.input.syncColors; + this.syncTooltips = this.input.syncTooltips; this.searchSessionId = this.input.searchSessionId; this.query = this.input.query; this.embeddableTitle = this.getTitle(); @@ -257,6 +259,11 @@ export class VisualizeEmbeddable dirty = true; } + if (this.syncTooltips !== this.input.syncTooltips) { + this.syncTooltips = this.input.syncTooltips; + dirty = true; + } + if (this.embeddableTitle !== this.getTitle()) { this.embeddableTitle = this.getTitle(); dirty = true; @@ -418,6 +425,7 @@ export class VisualizeEmbeddable }, searchSessionId: this.input.searchSessionId, syncColors: this.input.syncColors, + syncTooltips: this.input.syncTooltips, uiState: this.vis.uiState, interactive: !this.input.disableTriggers, inspectorAdapters: this.inspectorAdapters, diff --git a/src/plugins/visualizations/public/index.ts b/src/plugins/visualizations/public/index.ts index 22217e9de9abe..67b13c8236708 100644 --- a/src/plugins/visualizations/public/index.ts +++ b/src/plugins/visualizations/public/index.ts @@ -56,6 +56,9 @@ export { VISUALIZE_ENABLE_LABS_SETTING, SAVED_OBJECTS_LIMIT_SETTING, SAVED_OBJECTS_PER_PAGE_SETTING, + LegendSize, + LegendSizeToPixels, + DEFAULT_LEGEND_SIZE, } from '../common/constants'; export type { SavedVisState, VisParams, Dimension } from '../common'; export { prepareLogTable } from '../common'; diff --git a/src/plugins/visualizations/public/vis_types/base_vis_type.ts b/src/plugins/visualizations/public/vis_types/base_vis_type.ts index 80295e5af2e40..bb197e219f439 100644 --- a/src/plugins/visualizations/public/vis_types/base_vis_type.ts +++ b/src/plugins/visualizations/public/vis_types/base_vis_type.ts @@ -18,6 +18,7 @@ const defaultOptions: VisTypeOptions = { showQueryBar: true, showFilterBar: true, showIndexSelection: true, + showQueryInput: true, hierarchicalData: false, // we should get rid of this i guess ? }; diff --git a/src/plugins/visualizations/public/vis_types/types.ts b/src/plugins/visualizations/public/vis_types/types.ts index 0e7e44b6ea38e..383a238621e1e 100644 --- a/src/plugins/visualizations/public/vis_types/types.ts +++ b/src/plugins/visualizations/public/vis_types/types.ts @@ -20,6 +20,7 @@ export interface VisTypeOptions { showQueryBar: boolean; showFilterBar: boolean; showIndexSelection: boolean; + showQueryInput: boolean; hierarchicalData: boolean; } diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx b/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx index a6c1710afbed8..e42ee1d0cd6c0 100644 --- a/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx +++ b/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx @@ -161,7 +161,8 @@ const TopNav = ({ return vis.type.options.showTimePicker && hasTimeField; }; const showFilterBar = vis.type.options.showFilterBar; - const showQueryInput = vis.type.requiresSearch && vis.type.options.showQueryBar; + const showQueryInput = + vis.type.requiresSearch && vis.type.options.showQueryBar && vis.type.options.showQueryInput; useEffect(() => { return () => { diff --git a/src/plugins/visualizations/server/embeddable/make_visualize_embeddable_factory.test.ts b/src/plugins/visualizations/server/embeddable/make_visualize_embeddable_factory.test.ts index 42fcc34984b4d..d8a1fc51cd6db 100644 --- a/src/plugins/visualizations/server/embeddable/make_visualize_embeddable_factory.test.ts +++ b/src/plugins/visualizations/server/embeddable/make_visualize_embeddable_factory.test.ts @@ -9,9 +9,8 @@ import semverGte from 'semver/functions/gte'; import { makeVisualizeEmbeddableFactory } from './make_visualize_embeddable_factory'; import { getAllMigrations } from '../migrations/visualization_saved_object_migrations'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { SerializedSearchSourceFields } from '@kbn/data-plugin/public'; -import { GetMigrationFunctionObjectFn } from '@kbn/kibana-utils-plugin/common'; +import type { SerializedSearchSourceFields } from '@kbn/data-plugin/common'; +import type { GetMigrationFunctionObjectFn } from '@kbn/kibana-utils-plugin/common'; describe('embeddable migrations', () => { test('should have same versions registered as saved object migrations versions (>7.13.0)', () => { diff --git a/src/plugins/visualizations/server/embeddable/make_visualize_embeddable_factory.ts b/src/plugins/visualizations/server/embeddable/make_visualize_embeddable_factory.ts index e9dd45e1f84fc..1d8a00ab2e33b 100644 --- a/src/plugins/visualizations/server/embeddable/make_visualize_embeddable_factory.ts +++ b/src/plugins/visualizations/server/embeddable/make_visualize_embeddable_factory.ts @@ -7,10 +7,9 @@ */ import { flow, mapValues } from 'lodash'; -import { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/server'; +import type { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/server'; import type { SerializableRecord } from '@kbn/utility-types'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { SerializedSearchSourceFields } from '@kbn/data-plugin/public'; +import type { SerializedSearchSourceFields } from '@kbn/data-plugin/common'; import { mergeMigrationFunctionMaps, MigrateFunctionsObject, @@ -27,6 +26,7 @@ import { commonAddDropLastBucketIntoTSVBModel714Above, commonRemoveMarkdownLessFromTSVB, commonUpdatePieVisApi, + commonPreserveOldLegendSizeDefault, } from '../migrations/visualization_common_migrations'; import { SerializedVis } from '../../common'; @@ -98,6 +98,11 @@ const byValueUpdatePieVisApi = (state: SerializableRecord) => ({ savedVis: commonUpdatePieVisApi(state.savedVis), }); +const byValuePreserveOldLegendSizeDefault = (state: SerializableRecord) => ({ + ...state, + savedVis: commonPreserveOldLegendSizeDefault(state.savedVis), +}); + const getEmbeddedVisualizationSearchSourceMigrations = ( searchSourceMigrations: MigrateFunctionsObject ) => @@ -145,6 +150,7 @@ export const makeVisualizeEmbeddableFactory = '7.17.0': (state) => flow(byValueAddDropLastBucketIntoTSVBModel714Above)(state), '8.0.0': (state) => flow(byValueRemoveMarkdownLessFromTSVB)(state), '8.1.0': (state) => flow(byValueUpdatePieVisApi)(state), + '8.3.0': (state) => flow(byValuePreserveOldLegendSizeDefault)(state), } ), }; diff --git a/src/plugins/visualizations/server/migrations/visualization_common_migrations.ts b/src/plugins/visualizations/server/migrations/visualization_common_migrations.ts index aec452e356abe..57d8142822882 100644 --- a/src/plugins/visualizations/server/migrations/visualization_common_migrations.ts +++ b/src/plugins/visualizations/server/migrations/visualization_common_migrations.ts @@ -215,3 +215,34 @@ export const commonUpdatePieVisApi = (visState: any) => { return visState; }; + +export const commonPreserveOldLegendSizeDefault = (visState: any) => { + const visualizationTypesWithLegends = [ + 'pie', + 'area', + 'histogram', + 'horizontal_bar', + 'line', + 'heatmap', + ]; + + const pixelsToLegendSize: Record = { + undefined: 'auto', + '80': 'small', + '130': 'medium', + '180': 'large', + '230': 'xlarge', + }; + + if (visualizationTypesWithLegends.includes(visState?.type)) { + return { + ...visState, + params: { + ...visState.params, + legendSize: pixelsToLegendSize[visState.params?.legendSize], + }, + }; + } + + return visState; +}; diff --git a/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.test.ts b/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.test.ts index 99dbf548e6f44..626dc14e05396 100644 --- a/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.test.ts +++ b/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.test.ts @@ -2470,6 +2470,31 @@ describe('migration visualization', () => { }); }); + it('should not apply search source migrations within visualization when searchSourceJSON is not an object', () => { + const visualizationDoc = { + attributes: { + kibanaSavedObjectMeta: { + searchSourceJSON: '5', + }, + }, + } as SavedObjectUnsanitizedDoc; + + const versionToTest = '1.2.4'; + const visMigrations = getAllMigrations({ + [versionToTest]: (state) => ({ ...state, migrated: true }), + }); + + expect( + visMigrations[versionToTest](visualizationDoc, {} as SavedObjectMigrationContext) + ).toEqual({ + attributes: { + kibanaSavedObjectMeta: { + searchSourceJSON: '5', + }, + }, + }); + }); + describe('8.1.0 pie - labels and addLegend migration', () => { const getDoc = (addLegend: boolean, lastLevel: boolean = false) => ({ attributes: { @@ -2539,4 +2564,63 @@ describe('migration visualization', () => { expect(otherParams.addLegend).toBeUndefined(); }); }); + + describe('8.3.0 - preserves default legend size for existing visualizations', () => { + const getDoc = (type: string, legendSize: number | undefined) => ({ + attributes: { + title: 'Some Vis with a Legend', + description: '', + visState: JSON.stringify({ + type, + title: 'Pie vis', + params: { + legendSize, + }, + }), + }, + }); + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['8.3.0']( + doc as Parameters[0], + savedObjectMigrationContext + ); + + const autoLegendSize = 'auto'; + const largeLegendSize = 'large'; + const largeLegendSizePx = 180; + + test.each([ + ['pie', undefined, autoLegendSize], + ['area', undefined, autoLegendSize], + ['histogram', undefined, autoLegendSize], + ['horizontal_bar', undefined, autoLegendSize], + ['line', undefined, autoLegendSize], + ['heatmap', undefined, autoLegendSize], + ['pie', largeLegendSizePx, largeLegendSize], + ['area', largeLegendSizePx, largeLegendSize], + ['histogram', largeLegendSizePx, largeLegendSize], + ['horizontal_bar', largeLegendSizePx, largeLegendSize], + ['line', largeLegendSizePx, largeLegendSize], + ['heatmap', largeLegendSizePx, largeLegendSize], + ])( + 'given a %s visualization with current legend size of %s -- sets legend size to %s', + ( + visualizationType: string, + currentLegendSize: number | undefined, + expectedLegendSize: string + ) => { + const visState = JSON.parse( + migrate(getDoc(visualizationType, currentLegendSize)).attributes.visState + ); + + expect(visState.params.legendSize).toBe(expectedLegendSize); + } + ); + + test.each(['metric', 'gauge', 'table'])('leaves visualization without legend alone: %s', () => { + const visState = JSON.parse(migrate(getDoc('table', undefined)).attributes.visState); + + expect(visState.params.legendSize).toBeUndefined(); + }); + }); }); diff --git a/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts b/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts index 4b729afa62307..bb2d68cfd35d9 100644 --- a/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts +++ b/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts @@ -11,7 +11,11 @@ import type { SavedObjectMigrationFn, SavedObjectMigrationMap } from '@kbn/core/ import { mergeSavedObjectMigrationMaps } from '@kbn/core/server'; import { MigrateFunctionsObject, MigrateFunction } from '@kbn/kibana-utils-plugin/common'; -import { DEFAULT_QUERY_LANGUAGE, SerializedSearchSourceFields } from '@kbn/data-plugin/common'; +import { + DEFAULT_QUERY_LANGUAGE, + isSerializedSearchSource, + SerializedSearchSourceFields, +} from '@kbn/data-plugin/common'; import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/common'; import { commonAddSupportOfDualIndexSelectionModeInTSVB, @@ -24,6 +28,7 @@ import { commonAddDropLastBucketIntoTSVBModel714Above, commonRemoveMarkdownLessFromTSVB, commonUpdatePieVisApi, + commonPreserveOldLegendSizeDefault, } from './visualization_common_migrations'; import { VisualizationSavedObjectAttributes } from '../../common'; @@ -1154,6 +1159,30 @@ export const updatePieVisApi: SavedObjectMigrationFn = (doc) => { return doc; }; +const preserveOldLegendSizeDefault: SavedObjectMigrationFn = (doc) => { + const visStateJSON = get(doc, 'attributes.visState'); + let visState; + + if (visStateJSON) { + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + + const newVisState = commonPreserveOldLegendSizeDefault(visState); + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(newVisState), + }, + }; + } + + return doc; +}; + const visualizationSavedObjectTypeMigrations = { /** * We need to have this migration twice, once with a version prior to 7.0.0 once with a version @@ -1210,32 +1239,37 @@ const visualizationSavedObjectTypeMigrations = { '7.17.0': flow(addDropLastBucketIntoTSVBModel714Above), '8.0.0': flow(removeMarkdownLessFromTSVB), '8.1.0': flow(updatePieVisApi), + '8.3.0': preserveOldLegendSizeDefault, }; /** * This creates a migration map that applies search source migrations to legacy visualization SOs */ -const getVisualizationSearchSourceMigrations = (searchSourceMigrations: MigrateFunctionsObject) => +const getVisualizationSearchSourceMigrations = ( + searchSourceMigrations: MigrateFunctionsObject +): MigrateFunctionsObject => mapValues( searchSourceMigrations, (migrate: MigrateFunction): MigrateFunction => (state) => { - const _state = state as unknown as { attributes: VisualizationSavedObjectAttributes }; - - const parsedSearchSourceJSON = _state.attributes.kibanaSavedObjectMeta.searchSourceJSON; - - if (!parsedSearchSourceJSON) return _state; - - return { - ..._state, - attributes: { - ..._state.attributes, - kibanaSavedObjectMeta: { - ..._state.attributes.kibanaSavedObjectMeta, - searchSourceJSON: JSON.stringify(migrate(JSON.parse(parsedSearchSourceJSON))), + const _state = state as { attributes: VisualizationSavedObjectAttributes }; + + const parsedSearchSourceJSON = JSON.parse( + _state.attributes.kibanaSavedObjectMeta.searchSourceJSON + ); + if (isSerializedSearchSource(parsedSearchSourceJSON)) { + return { + ..._state, + attributes: { + ..._state.attributes, + kibanaSavedObjectMeta: { + ..._state.attributes.kibanaSavedObjectMeta, + searchSourceJSON: JSON.stringify(migrate(parsedSearchSourceJSON)), + }, }, - }, - }; + }; + } + return _state; } ); @@ -1244,7 +1278,5 @@ export const getAllMigrations = ( ): SavedObjectMigrationMap => mergeSavedObjectMigrationMaps( visualizationSavedObjectTypeMigrations, - getVisualizationSearchSourceMigrations( - searchSourceMigrations - ) as unknown as SavedObjectMigrationMap + getVisualizationSearchSourceMigrations(searchSourceMigrations) as SavedObjectMigrationMap ); diff --git a/src/setup_node_env/ensure_node_preserve_symlinks.js b/src/setup_node_env/ensure_node_preserve_symlinks.js index 3899564203622..5ec286801bdc4 100644 --- a/src/setup_node_env/ensure_node_preserve_symlinks.js +++ b/src/setup_node_env/ensure_node_preserve_symlinks.js @@ -89,10 +89,18 @@ } if (spawnResult.signal !== null) { - return 128 + spawnResult.signal; + console.log( + 'ensure_node_preserve_symlinks wrapper: process exitted with signal', + spawnResult.signal + ); + return 1; } if (spawnResult.error) { + console.log( + 'ensure_node_preserve_symlinks wrapper: process exitted with error', + spawnResult.error + ); return 1; } diff --git a/test/accessibility/apps/discover.ts b/test/accessibility/apps/discover.ts index 867e146e64ca3..5a3e881b86471 100644 --- a/test/accessibility/apps/discover.ts +++ b/test/accessibility/apps/discover.ts @@ -11,6 +11,7 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'discover', 'header', 'share', 'timePicker']); const a11y = getService('a11y'); + const savedQueryManagementComponent = getService('savedQueryManagementComponent'); const inspector = getService('inspector'); const testSubjects = getService('testSubjects'); const TEST_COLUMN_NAMES = ['dayOfWeek', 'DestWeather']; @@ -93,11 +94,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('a11y test on saved queries list panel', async () => { + await savedQueryManagementComponent.loadSavedQuery('test'); await PageObjects.discover.clickSavedQueriesPopOver(); - await testSubjects.moveMouseTo( - 'saved-query-list-item load-saved-query-test-button saved-query-list-item-selected saved-query-list-item-selected' - ); - await testSubjects.find('delete-saved-query-test-button'); + await testSubjects.click('saved-query-management-load-button'); + await savedQueryManagementComponent.deleteSavedQuery('test'); await a11y.testAppSnapshot(); }); }); diff --git a/test/accessibility/apps/filter_panel.ts b/test/accessibility/apps/filter_panel.ts index deb1e9512cd81..b479c62f48975 100644 --- a/test/accessibility/apps/filter_panel.ts +++ b/test/accessibility/apps/filter_panel.ts @@ -43,38 +43,47 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // the following tests filter panel options which changes UI it('a11y test on filter panel options panel', async () => { await filterBar.addFilter('DestCountry', 'is', 'AU'); - await testSubjects.click('showFilterActions'); + await testSubjects.click('showQueryBarMenu'); await a11y.testAppSnapshot(); }); it('a11y test on disable all filter options view', async () => { - await testSubjects.click('disableAllFilters'); + await testSubjects.click('filter-sets-applyToAllFilters'); + await testSubjects.click('filter-sets-disableAllFilters'); await a11y.testAppSnapshot(); }); - it('a11y test on pin filters view', async () => { - await testSubjects.click('showFilterActions'); - await testSubjects.click('enableAllFilters'); - await testSubjects.click('showFilterActions'); - await testSubjects.click('pinAllFilters'); + it('a11y test on enable all filters view', async () => { + await testSubjects.click('showQueryBarMenu'); + await testSubjects.click('filter-sets-applyToAllFilters'); + await testSubjects.click('filter-sets-enableAllFilters'); + await a11y.testAppSnapshot(); + }); + + it('a11y test on pin all filters view', async () => { + await testSubjects.click('showQueryBarMenu'); + await testSubjects.click('filter-sets-applyToAllFilters'); + await testSubjects.click('filter-sets-pinAllFilters'); await a11y.testAppSnapshot(); }); it('a11y test on unpin all filters view', async () => { - await testSubjects.click('showFilterActions'); - await testSubjects.click('unpinAllFilters'); + await testSubjects.click('showQueryBarMenu'); + await testSubjects.click('filter-sets-applyToAllFilters'); + await testSubjects.click('filter-sets-unpinAllFilters'); await a11y.testAppSnapshot(); }); it('a11y test on invert inclusion of all filters view', async () => { - await testSubjects.click('showFilterActions'); - await testSubjects.click('invertInclusionAllFilters'); + await testSubjects.click('showQueryBarMenu'); + await testSubjects.click('filter-sets-applyToAllFilters'); + await testSubjects.click('filter-sets-invertAllFilters'); await a11y.testAppSnapshot(); }); it('a11y test on remove all filtes view', async () => { - await testSubjects.click('showFilterActions'); - await testSubjects.click('removeAllFilters'); + await testSubjects.click('showQueryBarMenu'); + await testSubjects.click('filter-sets-removeAllFilters'); await a11y.testAppSnapshot(); }); }); diff --git a/test/accessibility/config.ts b/test/accessibility/config.ts index 59194fcb67826..9ed89694db5d8 100644 --- a/test/accessibility/config.ts +++ b/test/accessibility/config.ts @@ -11,7 +11,7 @@ import { services } from './services'; import { pageObjects } from './page_objects'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); return { ...functionalConfig.getAll(), diff --git a/test/analytics/__fixtures__/plugins/analytics_ftr_helpers/public/custom_shipper.ts b/test/analytics/__fixtures__/plugins/analytics_ftr_helpers/public/custom_shipper.ts index 31c9302e5ece8..97bf37749c256 100644 --- a/test/analytics/__fixtures__/plugins/analytics_ftr_helpers/public/custom_shipper.ts +++ b/test/analytics/__fixtures__/plugins/analytics_ftr_helpers/public/custom_shipper.ts @@ -7,17 +7,25 @@ */ import { Subject } from 'rxjs'; +import type { AnalyticsClientInitContext } from '@kbn/analytics-client'; import type { Event, IShipper } from '@kbn/core/public'; export class CustomShipper implements IShipper { public static shipperName = 'FTR-helpers-shipper'; - constructor(private readonly events$: Subject) {} + constructor( + private readonly events$: Subject, + private readonly initContext: AnalyticsClientInitContext + ) {} public reportEvents(events: Event[]) { + this.initContext.logger.info( + `Reporting ${events.length} events to ${CustomShipper.shipperName}: ${JSON.stringify(events)}` + ); events.forEach((event) => { this.events$.next(event); }); } optIn(isOptedIn: boolean) {} + shutdown() {} } diff --git a/test/analytics/__fixtures__/plugins/analytics_ftr_helpers/server/custom_shipper.ts b/test/analytics/__fixtures__/plugins/analytics_ftr_helpers/server/custom_shipper.ts index 0feb1f2d13e7c..c76f30c94572e 100644 --- a/test/analytics/__fixtures__/plugins/analytics_ftr_helpers/server/custom_shipper.ts +++ b/test/analytics/__fixtures__/plugins/analytics_ftr_helpers/server/custom_shipper.ts @@ -7,17 +7,25 @@ */ import { Subject } from 'rxjs'; +import type { AnalyticsClientInitContext } from '@kbn/analytics-client'; import type { IShipper, Event } from '@kbn/core/server'; export class CustomShipper implements IShipper { public static shipperName = 'FTR-helpers-shipper'; - constructor(private readonly events$: Subject) {} + constructor( + private readonly events$: Subject, + private readonly initContext: AnalyticsClientInitContext + ) {} public reportEvents(events: Event[]) { + this.initContext.logger.info( + `Reporting ${events.length} events to ${CustomShipper.shipperName}: ${JSON.stringify(events)}` + ); events.forEach((event) => { this.events$.next(event); }); } optIn(isOptedIn: boolean) {} + shutdown() {} } diff --git a/test/analytics/__fixtures__/plugins/analytics_plugin_a/public/custom_shipper.ts b/test/analytics/__fixtures__/plugins/analytics_plugin_a/public/custom_shipper.ts index e94e34126c7d4..075f5bcdb99a1 100644 --- a/test/analytics/__fixtures__/plugins/analytics_plugin_a/public/custom_shipper.ts +++ b/test/analytics/__fixtures__/plugins/analytics_plugin_a/public/custom_shipper.ts @@ -40,4 +40,5 @@ export class CustomShipper implements IShipper { extendContext(newContext: EventContext) { this.actions$.next({ action: 'extendContext', meta: newContext }); } + shutdown() {} } diff --git a/test/analytics/__fixtures__/plugins/analytics_plugin_a/server/custom_shipper.ts b/test/analytics/__fixtures__/plugins/analytics_plugin_a/server/custom_shipper.ts index 7d0b2bb1b6db2..dd3b99a154968 100644 --- a/test/analytics/__fixtures__/plugins/analytics_plugin_a/server/custom_shipper.ts +++ b/test/analytics/__fixtures__/plugins/analytics_plugin_a/server/custom_shipper.ts @@ -40,4 +40,5 @@ export class CustomShipper implements IShipper { extendContext(newContext: EventContext) { this.actions$.next({ action: 'extendContext', meta: newContext }); } + shutdown() {} } diff --git a/test/analytics/config.ts b/test/analytics/config.ts index 1ecac5af0d01a..ecb9792b0dff1 100644 --- a/test/analytics/config.ts +++ b/test/analytics/config.ts @@ -19,7 +19,7 @@ import { services } from './services'; */ export default async function ({ readConfigFile }: FtrConfigProviderContext) { const commonConfig = await readConfigFile(require.resolve('../common/config')); - const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); return { testFiles: [require.resolve('./tests')], @@ -34,7 +34,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { ...functionalConfig.get('kbnTestServer'), serverArgs: [ ...functionalConfig.get('kbnTestServer.serverArgs'), - // Disabling telemetry so it doesn't call opt-in before the tests run. + // Disabling telemetry, so it doesn't call opt-in before the tests run. '--telemetry.enabled=false', `--plugin-path=${path.resolve(__dirname, './__fixtures__/plugins/analytics_plugin_a')}`, `--plugin-path=${path.resolve(__dirname, './__fixtures__/plugins/analytics_ftr_helpers')}`, diff --git a/test/analytics/services/kibana_ebt.ts b/test/analytics/services/kibana_ebt.ts index fd64cbbbc0105..281794e899a3c 100644 --- a/test/analytics/services/kibana_ebt.ts +++ b/test/analytics/services/kibana_ebt.ts @@ -12,24 +12,27 @@ import '@kbn/analytics-ftr-helpers-plugin/public/types'; export function KibanaEBTServerProvider({ getService }: FtrProviderContext) { const supertest = getService('supertest'); + const setOptIn = async (optIn: boolean) => { + await supertest + .post(`/internal/analytics_ftr_helpers/opt_in`) + .set('kbn-xsrf', 'xxx') + .query({ consent: optIn }) + .expect(200); + }; + return { /** * Change the opt-in state of the Kibana EBT client. * @param optIn `true` to opt-in, `false` to opt-out. */ - setOptIn: async (optIn: boolean) => { - await supertest - .post(`/internal/analytics_ftr_helpers/opt_in`) - .set('kbn-xsrf', 'xxx') - .query({ consent: optIn }) - .expect(200); - }, + setOptIn, /** * Returns the last events of the specified types. * @param numberOfEvents - number of events to return * @param eventTypes (Optional) array of event types to return */ getLastEvents: async (takeNumberOfEvents: number, eventTypes: string[] = []) => { + await setOptIn(true); const resp = await supertest .get(`/internal/analytics_ftr_helpers/events`) .query({ takeNumberOfEvents, eventTypes: JSON.stringify(eventTypes) }) @@ -45,6 +48,10 @@ export function KibanaEBTUIProvider({ getService, getPageObjects }: FtrProviderC const { common } = getPageObjects(['common']); const browser = getService('browser'); + const setOptIn = async (optIn: boolean) => { + await browser.execute((isOptIn) => window.__analytics_ftr_helpers__.setOptIn(isOptIn), optIn); + }; + return { /** * Change the opt-in state of the Kibana EBT client. @@ -52,7 +59,7 @@ export function KibanaEBTUIProvider({ getService, getPageObjects }: FtrProviderC */ setOptIn: async (optIn: boolean) => { await common.navigateToApp('home'); - await browser.execute((isOptIn) => window.__analytics_ftr_helpers__.setOptIn(isOptIn), optIn); + await setOptIn(optIn); }, /** * Returns the last events of the specified types. @@ -60,6 +67,7 @@ export function KibanaEBTUIProvider({ getService, getPageObjects }: FtrProviderC * @param eventTypes (Optional) array of event types to return */ getLastEvents: async (numberOfEvents: number, eventTypes: string[] = []) => { + await setOptIn(true); const events = await browser.execute( ({ eventTypes: _eventTypes, numberOfEvents: _numberOfEvents }) => window.__analytics_ftr_helpers__.getLastEvents(_numberOfEvents, _eventTypes), diff --git a/test/analytics/tests/analytics_from_the_browser.ts b/test/analytics/tests/analytics_from_the_browser.ts index 7acabf2112c5d..c05492fe30961 100644 --- a/test/analytics/tests/analytics_from_the_browser.ts +++ b/test/analytics/tests/analytics_from_the_browser.ts @@ -72,6 +72,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(context).to.have.property('user_agent'); expect(context.user_agent).to.be.a('string'); + // Some context providers emit very early. We are OK with that. + const initialContext = actions[2].meta[0].context; + const reportEventContext = actions[2].meta[1].context; expect(reportEventContext).to.have.property('user_agent'); expect(reportEventContext.user_agent).to.be.a('string'); @@ -85,7 +88,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { { timestamp: actions[2].meta[0].timestamp, event_type: 'test-plugin-lifecycle', - context: {}, + context: initialContext, properties: { plugin: 'analyticsPluginA', step: 'setup' }, }, { @@ -103,7 +106,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { { timestamp: actions[2].meta[0].timestamp, event_type: 'test-plugin-lifecycle', - context: {}, + context: initialContext, properties: { plugin: 'analyticsPluginA', step: 'setup' }, }, { diff --git a/test/analytics/tests/analytics_from_the_server.ts b/test/analytics/tests/analytics_from_the_server.ts index e5e3573b20fcd..820f7e51adc96 100644 --- a/test/analytics/tests/analytics_from_the_server.ts +++ b/test/analytics/tests/analytics_from_the_server.ts @@ -63,11 +63,19 @@ export default function ({ getService }: FtrProviderContext) { await ebtServerHelper.setOptIn(true); const actions = await getActions(3); + // Validating the remote PID because that's the only field that it's added by the FTR plugin. const context = actions[1].meta; expect(context).to.have.property('pid'); expect(context.pid).to.be.a('number'); + // Some context providers emit very early. We are OK with that. + const initialContext = actions[2].meta[0].context; + + const reportEventContext = actions[2].meta[1].context; + expect(context).to.have.property('pid'); + expect(context.pid).to.be.a('number'); + expect(actions).to.eql([ { action: 'optIn', meta: true }, { action: 'extendContext', meta: context }, @@ -77,13 +85,13 @@ export default function ({ getService }: FtrProviderContext) { { timestamp: actions[2].meta[0].timestamp, event_type: 'test-plugin-lifecycle', - context: {}, + context: initialContext, properties: { plugin: 'analyticsPluginA', step: 'setup' }, }, { timestamp: actions[2].meta[1].timestamp, event_type: 'test-plugin-lifecycle', - context, + context: reportEventContext, properties: { plugin: 'analyticsPluginA', step: 'start' }, }, ], @@ -96,13 +104,13 @@ export default function ({ getService }: FtrProviderContext) { { timestamp: actions[2].meta[0].timestamp, event_type: 'test-plugin-lifecycle', - context: {}, + context: initialContext, properties: { plugin: 'analyticsPluginA', step: 'setup' }, }, { timestamp: actions[2].meta[1].timestamp, event_type: 'test-plugin-lifecycle', - context, + context: reportEventContext, properties: { plugin: 'analyticsPluginA', step: 'start' }, }, ]); diff --git a/test/analytics/tests/instrumented_events/from_the_browser/click.ts b/test/analytics/tests/instrumented_events/from_the_browser/click.ts new file mode 100644 index 0000000000000..7b9816ba13e4e --- /dev/null +++ b/test/analytics/tests/instrumented_events/from_the_browser/click.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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../services'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const ebtUIHelper = getService('kibana_ebt_ui'); + const { common } = getPageObjects(['common']); + + describe('General "click"', () => { + beforeEach(async () => { + await common.navigateToApp('home'); + // Just click on the top div and expect it's still there... we're just testing the click event generation + await common.clickAndValidate('kibanaChrome', 'kibanaChrome'); + }); + + it('should emit a "click" event', async () => { + const [event] = await ebtUIHelper.getLastEvents(1, ['click']); + expect(event.event_type).to.eql('click'); + expect(event.properties.target).to.be.an('array'); + const targets = event.properties.target as string[]; + expect(targets.includes('DIV')).to.be(true); + expect(targets.includes('id=kibana-body')).to.be(true); + expect(targets.includes('data-test-subj=kibanaChrome')).to.be(true); + }); + }); +} diff --git a/test/analytics/tests/instrumented_events/from_the_browser/core_context_providers.ts b/test/analytics/tests/instrumented_events/from_the_browser/core_context_providers.ts new file mode 100644 index 0000000000000..b6f691f419dab --- /dev/null +++ b/test/analytics/tests/instrumented_events/from_the_browser/core_context_providers.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 and the Server Side Public License, v 1; you may 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 { Event } from '@kbn/core/public'; +import { FtrProviderContext } from '../../../services'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const deployment = getService('deployment'); + const ebtUIHelper = getService('kibana_ebt_ui'); + const { common } = getPageObjects(['common']); + + describe('Core Context Providers', () => { + let event: Event; + before(async () => { + await common.navigateToApp('home'); + [event] = await ebtUIHelper.getLastEvents(1, ['Loaded Kibana']); // Get the loaded Kibana event + }); + + it('should have the properties provided by the "cluster info" context provider', () => { + expect(event.context).to.have.property('cluster_uuid'); + expect(event.context.cluster_uuid).to.be.a('string'); + expect(event.context).to.have.property('cluster_name'); + expect(event.context.cluster_name).to.be.a('string'); + expect(event.context).to.have.property('cluster_version'); + expect(event.context.cluster_version).to.be.a('string'); + }); + + it('should have the properties provided by the "build info" context provider', () => { + expect(event.context).to.have.property('isDev'); + expect(event.context.isDev).to.be.a('boolean'); + expect(event.context).to.have.property('isDistributable'); + expect(event.context.isDistributable).to.be.a('boolean'); + expect(event.context).to.have.property('version'); + expect(event.context.version).to.be.a('string'); + expect(event.context).to.have.property('branch'); + expect(event.context.branch).to.be.a('string'); + expect(event.context).to.have.property('buildNum'); + expect(event.context.buildNum).to.be.a('number'); + expect(event.context).to.have.property('buildSha'); + expect(event.context.buildSha).to.be.a('string'); + }); + + it('should have the properties provided by the "session-id" context provider', () => { + expect(event.context).to.have.property('session_id'); + expect(event.context.session_id).to.be.a('string'); + }); + + it('should have the properties provided by the "browser info" context provider', () => { + expect(event.context).to.have.property('user_agent'); + expect(event.context.user_agent).to.be.a('string'); + expect(event.context).to.have.property('preferred_language'); + expect(event.context.preferred_language).to.be.a('string'); + expect(event.context).to.have.property('preferred_languages'); + expect(event.context.preferred_languages).to.be.an('array'); + (event.context.preferred_languages as unknown[]).forEach((lang) => + expect(lang).to.be.a('string') + ); + }); + + it('should have the properties provided by the "execution_context" context provider', () => { + expect(event.context).to.have.property('pageName'); + expect(event.context.pageName).to.be.a('string'); + expect(event.context).to.have.property('applicationId'); + expect(event.context.applicationId).to.be.a('string'); + expect(event.context).not.to.have.property('entityId'); // In the Home app it's not available. + expect(event.context).not.to.have.property('page'); // In the Home app it's not available. + }); + + it('should have the properties provided by the "license info" context provider', async () => { + await common.clickAndValidate('kibanaChrome', 'kibanaChrome'); + [event] = await ebtUIHelper.getLastEvents(1, ['click']); // Get a later event to ensure license has been obtained already. + expect(event.context).to.have.property('license_id'); + expect(event.context.license_id).to.be.a('string'); + expect(event.context).to.have.property('license_status'); + expect(event.context.license_status).to.be.a('string'); + expect(event.context).to.have.property('license_type'); + expect(event.context.license_type).to.be.a('string'); + }); + + it('should have the properties provided by the "Cloud Deployment ID" context provider', async () => { + if (await deployment.isCloud()) { + expect(event.context).to.have.property('cloudId'); + expect(event.context.cloudId).to.be.a('string'); + } else { + expect(event.context).not.to.have.property('cloudId'); + } + }); + }); +} diff --git a/test/analytics/tests/instrumented_events/from_the_browser/index.ts b/test/analytics/tests/instrumented_events/from_the_browser/index.ts index daf21180d2328..2fe99373d5214 100644 --- a/test/analytics/tests/instrumented_events/from_the_browser/index.ts +++ b/test/analytics/tests/instrumented_events/from_the_browser/index.ts @@ -8,13 +8,11 @@ import { FtrProviderContext } from '../../../services'; -export default function ({ getService }: FtrProviderContext) { +export default function ({ loadTestFile }: FtrProviderContext) { describe('from the browser', () => { - beforeEach(async () => { - await getService('kibana_ebt_ui').setOptIn(true); - }); - // Add tests for UI-instrumented events here: - // loadTestFile(require.resolve('./some_event')); + loadTestFile(require.resolve('./click')); + loadTestFile(require.resolve('./loaded_kibana')); + loadTestFile(require.resolve('./core_context_providers')); }); } diff --git a/test/analytics/tests/instrumented_events/from_the_browser/loaded_kibana.ts b/test/analytics/tests/instrumented_events/from_the_browser/loaded_kibana.ts new file mode 100644 index 0000000000000..c7d3291cb03d4 --- /dev/null +++ b/test/analytics/tests/instrumented_events/from_the_browser/loaded_kibana.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../services'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const ebtUIHelper = getService('kibana_ebt_ui'); + const { common } = getPageObjects(['common']); + const browser = getService('browser'); + + describe('Loaded Kibana', () => { + beforeEach(async () => { + await common.navigateToApp('home'); + }); + + it('should emit the "Loaded Kibana" event', async () => { + const [event] = await ebtUIHelper.getLastEvents(1, ['Loaded Kibana']); + expect(event.event_type).to.eql('Loaded Kibana'); + expect(event.properties).to.have.property('kibana_version'); + expect(event.properties.kibana_version).to.be.a('string'); + + if (browser.isChromium) { + expect(event.properties).to.have.property('memory_js_heap_size_limit'); + expect(event.properties.memory_js_heap_size_limit).to.be.a('number'); + expect(event.properties).to.have.property('memory_js_heap_size_total'); + expect(event.properties.memory_js_heap_size_total).to.be.a('number'); + expect(event.properties).to.have.property('memory_js_heap_size_used'); + expect(event.properties.memory_js_heap_size_used).to.be.a('number'); + } + }); + }); +} diff --git a/test/analytics/tests/instrumented_events/from_the_server/core_context_providers.ts b/test/analytics/tests/instrumented_events/from_the_server/core_context_providers.ts new file mode 100644 index 0000000000000..743a32fcc58ac --- /dev/null +++ b/test/analytics/tests/instrumented_events/from_the_server/core_context_providers.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; +import { Event } from '@kbn/core/public'; +import { FtrProviderContext } from '../../../services'; + +export default function ({ getService }: FtrProviderContext) { + const deployment = getService('deployment'); + const ebtServerHelper = getService('kibana_ebt_server'); + + describe('Core Context Providers', () => { + let event: Event; + before(async () => { + // Wait for the 2nd "status_changed" event. At that point all the context providers should be set up. + [, event] = await ebtServerHelper.getLastEvents(2, ['core-overall_status_changed']); + }); + + it('should have the properties provided by the "kibana info" context provider', () => { + expect(event.context).to.have.property('kibana_uuid'); + expect(event.context.kibana_uuid).to.be.a('string'); + expect(event.context).to.have.property('pid'); + expect(event.context.pid).to.be.a('number'); + }); + + it('should have the properties provided by the "build info" context provider', () => { + expect(event.context).to.have.property('isDev'); + expect(event.context.isDev).to.be.a('boolean'); + expect(event.context).to.have.property('isDistributable'); + expect(event.context.isDistributable).to.be.a('boolean'); + expect(event.context).to.have.property('version'); + expect(event.context.version).to.be.a('string'); + expect(event.context).to.have.property('branch'); + expect(event.context.branch).to.be.a('string'); + expect(event.context).to.have.property('buildNum'); + expect(event.context.buildNum).to.be.a('number'); + expect(event.context).to.have.property('buildSha'); + expect(event.context.buildSha).to.be.a('string'); + }); + + it('should have the properties provided by the "cluster info" context provider', () => { + expect(event.context).to.have.property('cluster_uuid'); + expect(event.context.cluster_uuid).to.be.a('string'); + expect(event.context).to.have.property('cluster_name'); + expect(event.context.cluster_name).to.be.a('string'); + expect(event.context).to.have.property('cluster_version'); + expect(event.context.cluster_version).to.be.a('string'); + }); + + it('should have the properties provided by the "status info" context provider', () => { + expect(event.context).to.have.property('overall_status_level'); + expect(event.context.overall_status_level).to.be.a('string'); + expect(event.context).to.have.property('overall_status_summary'); + expect(event.context.overall_status_summary).to.be.a('string'); + }); + + it('should have the properties provided by the "license info" context provider', () => { + expect(event.context).to.have.property('license_id'); + expect(event.context.license_id).to.be.a('string'); + expect(event.context).to.have.property('license_status'); + expect(event.context.license_status).to.be.a('string'); + expect(event.context).to.have.property('license_type'); + expect(event.context.license_type).to.be.a('string'); + }); + + it('should have the properties provided by the "Cloud Deployment ID" context provider', async () => { + if (await deployment.isCloud()) { + expect(event.context).to.have.property('cloudId'); + expect(event.context.cloudId).to.be.a('string'); + } else { + expect(event.context).not.to.have.property('cloudId'); + } + }); + }); +} diff --git a/test/analytics/tests/instrumented_events/from_the_server/core_overall_status_changed.ts b/test/analytics/tests/instrumented_events/from_the_server/core_overall_status_changed.ts new file mode 100644 index 0000000000000..fa94e2b69fc3f --- /dev/null +++ b/test/analytics/tests/instrumented_events/from_the_server/core_overall_status_changed.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 and the Server Side Public License, v 1; you may 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 { Event } from '@kbn/analytics-client'; +import { FtrProviderContext } from '../../../services'; + +export default function ({ getService }: FtrProviderContext) { + const ebtServerHelper = getService('kibana_ebt_server'); + + describe('core-overall_status_changed', () => { + let initialEvent: Event; + let secondEvent: Event; + + before(async () => { + [initialEvent, secondEvent] = await ebtServerHelper.getLastEvents(2, [ + 'core-overall_status_changed', + ]); + }); + + it('should emit the initial "degraded" event with the context set to `initializing`', () => { + expect(initialEvent.event_type).to.eql('core-overall_status_changed'); + expect(initialEvent.context).to.have.property('overall_status_level', 'initializing'); + expect(initialEvent.context).to.have.property( + 'overall_status_summary', + 'Kibana is starting up' + ); + expect(initialEvent.properties).to.have.property('overall_status_level', 'degraded'); + expect(initialEvent.properties.overall_status_summary).to.be.a('string'); + }); + + it('should emit the 2nd event as `available` with the context set to the previous values', () => { + expect(secondEvent.event_type).to.eql('core-overall_status_changed'); + expect(secondEvent.context).to.have.property( + 'overall_status_level', + initialEvent.properties.overall_status_level + ); + expect(secondEvent.context).to.have.property( + 'overall_status_summary', + initialEvent.properties.overall_status_summary + ); + expect(secondEvent.properties.overall_status_level).to.be.a('string'); // Ideally we would test it as `available`, but we can't do that as it may result flaky for many side effects in the CI. + expect(secondEvent.properties.overall_status_summary).to.be.a('string'); + }); + }); +} diff --git a/test/analytics/tests/instrumented_events/from_the_server/index.ts b/test/analytics/tests/instrumented_events/from_the_server/index.ts index 8961b9e92994c..d8150b0519fde 100644 --- a/test/analytics/tests/instrumented_events/from_the_server/index.ts +++ b/test/analytics/tests/instrumented_events/from_the_server/index.ts @@ -8,13 +8,11 @@ import { FtrProviderContext } from '../../../services'; -export default function ({ getService }: FtrProviderContext) { +export default function ({ loadTestFile }: FtrProviderContext) { describe('from the server', () => { - beforeEach(async () => { - await getService('kibana_ebt_server').setOptIn(true); - }); - - // Add tests for UI-instrumented events here: - // loadTestFile(require.resolve('./some_event')); + // Add tests for Server-instrumented events here: + loadTestFile(require.resolve('./core_context_providers')); + loadTestFile(require.resolve('./kibana_started')); + loadTestFile(require.resolve('./core_overall_status_changed')); }); } diff --git a/test/analytics/tests/instrumented_events/from_the_server/kibana_started.ts b/test/analytics/tests/instrumented_events/from_the_server/kibana_started.ts new file mode 100644 index 0000000000000..86917b937cbab --- /dev/null +++ b/test/analytics/tests/instrumented_events/from_the_server/kibana_started.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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../services'; + +export default function ({ getService }: FtrProviderContext) { + const ebtServerHelper = getService('kibana_ebt_server'); + + describe('kibana_started', () => { + it('should emit the "kibana_started" event', async () => { + const [event] = await ebtServerHelper.getLastEvents(1, ['kibana_started']); + expect(event.event_type).to.eql('kibana_started'); + expect(event.properties.uptime_per_step.constructor.start).to.be.a('number'); + expect(event.properties.uptime_per_step.constructor.end).to.be.a('number'); + expect(event.properties.uptime_per_step.preboot.start).to.be.a('number'); + expect(event.properties.uptime_per_step.preboot.end).to.be.a('number'); + expect(event.properties.uptime_per_step.setup.start).to.be.a('number'); + expect(event.properties.uptime_per_step.setup.end).to.be.a('number'); + expect(event.properties.uptime_per_step.start.start).to.be.a('number'); + expect(event.properties.uptime_per_step.start.end).to.be.a('number'); + }); + }); +} diff --git a/test/api_integration/apis/saved_objects/lib/saved_objects_test_utils.ts b/test/api_integration/apis/saved_objects/lib/saved_objects_test_utils.ts index d915fc75ed0c3..4e6fc6158e881 100644 --- a/test/api_integration/apis/saved_objects/lib/saved_objects_test_utils.ts +++ b/test/api_integration/apis/saved_objects/lib/saved_objects_test_utils.ts @@ -14,5 +14,6 @@ export async function getKibanaVersion(getService: FtrProviderContext['getServic const kibanaVersion = await kibanaServer.version.get(); expect(typeof kibanaVersion).to.eql('string'); expect(kibanaVersion.length).to.be.greaterThan(0); - return kibanaVersion; + // mimic SavedObjectsService.stripVersionQualifier() + return kibanaVersion.split('-')[0]; } diff --git a/test/api_integration/config.js b/test/api_integration/config.js index 4988094dad7a2..7f3f4b45298d1 100644 --- a/test/api_integration/config.js +++ b/test/api_integration/config.js @@ -10,7 +10,7 @@ import { services } from './services'; export default async function ({ readConfigFile }) { const commonConfig = await readConfigFile(require.resolve('../common/config')); - const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); return { rootTags: ['runOutsideOfCiGroups'], diff --git a/test/common/fixtures/plugins/newsfeed/server/plugin.ts b/test/common/fixtures/plugins/newsfeed/server/plugin.ts index 85dadcfa8d7d2..5eb27325e535c 100644 --- a/test/common/fixtures/plugins/newsfeed/server/plugin.ts +++ b/test/common/fixtures/plugins/newsfeed/server/plugin.ts @@ -59,7 +59,7 @@ export class NewsFeedSimulatorPlugin implements Plugin { title: { en: 'Staging too!' }, description: { en: 'Hello world' }, link_text: { en: 'Generic feed-viewer could go here' }, - link_url: { en: 'https://feeds-staging.elastic.co' }, + link_url: { en: 'https://feeds.elastic.co' }, languages: null, badge: null, image_url: null, @@ -71,7 +71,7 @@ export class NewsFeedSimulatorPlugin implements Plugin { title: { en: 'This item is expired!' }, description: { en: 'This should not show up.' }, link_text: { en: 'Generic feed-viewer could go here' }, - link_url: { en: 'https://feeds-staging.elastic.co' }, + link_url: { en: 'https://feeds.elastic.co' }, languages: null, badge: null, image_url: null, diff --git a/test/examples/bfetch_explorer/index.ts b/test/examples/bfetch_explorer/index.ts index 247cef07a487e..b487704663c62 100644 --- a/test/examples/bfetch_explorer/index.ts +++ b/test/examples/bfetch_explorer/index.ts @@ -14,7 +14,6 @@ export default function ({ getService, getPageObjects, loadTestFile }: FtrProvid const PageObjects = getPageObjects(['common', 'header']); describe('bfetch explorer', function () { - this.tags('ciGroup11'); before(async () => { await browser.setWindowSize(1300, 900); await PageObjects.common.navigateToApp('bfetch-explorer', { insertTimestamp: false }); diff --git a/test/examples/config.js b/test/examples/config.js index 6d1f1ec472350..25537a22e19ac 100644 --- a/test/examples/config.js +++ b/test/examples/config.js @@ -12,7 +12,7 @@ import fs from 'fs'; import { KIBANA_ROOT } from '@kbn/test'; export default async function ({ readConfigFile }) { - const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); // Find all folders in /examples and /x-pack/examples since we treat all them as plugin folder const examplesFiles = fs.readdirSync(resolve(KIBANA_ROOT, 'examples')); diff --git a/test/examples/data_view_field_editor_example/index.ts b/test/examples/data_view_field_editor_example/index.ts index 0f8517cb3ed29..fcb8300749f0c 100644 --- a/test/examples/data_view_field_editor_example/index.ts +++ b/test/examples/data_view_field_editor_example/index.ts @@ -20,7 +20,6 @@ export default function ({ const PageObjects = getPageObjects(['common', 'header', 'settings']); describe('data view field editor example', function () { - this.tags('ciGroup11'); before(async () => { await esArchiver.emptyKibanaIndex(); await browser.setWindowSize(1300, 900); diff --git a/test/examples/embeddables/index.ts b/test/examples/embeddables/index.ts index 364c4001383a7..6cd95c699e7b8 100644 --- a/test/examples/embeddables/index.ts +++ b/test/examples/embeddables/index.ts @@ -18,7 +18,6 @@ export default function ({ const PageObjects = getPageObjects(['common', 'header']); describe('embeddable explorer', function () { - this.tags('ciGroup11'); before(async () => { await browser.setWindowSize(1300, 900); await PageObjects.common.navigateToApp('embeddableExplorer'); diff --git a/test/examples/expressions_explorer/index.ts b/test/examples/expressions_explorer/index.ts index 34f3c77cb0d3e..d7a47b63bd012 100644 --- a/test/examples/expressions_explorer/index.ts +++ b/test/examples/expressions_explorer/index.ts @@ -18,7 +18,6 @@ export default function ({ const PageObjects = getPageObjects(['common', 'header']); describe('expressions explorer', function () { - this.tags('ciGroup11'); before(async () => { await browser.setWindowSize(1300, 900); await PageObjects.common.navigateToApp('expressionsExplorer'); diff --git a/test/examples/field_formats/index.ts b/test/examples/field_formats/index.ts index f9692c910fda0..aebd92728b1af 100644 --- a/test/examples/field_formats/index.ts +++ b/test/examples/field_formats/index.ts @@ -16,7 +16,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('Field formats example', function () { before(async () => { - this.tags('ciGroup11'); await PageObjects.common.navigateToApp('fieldFormatsExample'); }); diff --git a/test/examples/hello_world/index.ts b/test/examples/hello_world/index.ts index 1ffb7ff6d69af..604d014401b8f 100644 --- a/test/examples/hello_world/index.ts +++ b/test/examples/hello_world/index.ts @@ -17,7 +17,6 @@ export default function ({ getService, getPageObjects, loadTestFile }: FtrProvid describe('Hello world', function () { before(async () => { - this.tags('ciGroup11'); await PageObjects.common.navigateToApp('helloWorld'); }); diff --git a/test/examples/partial_results/index.ts b/test/examples/partial_results/index.ts index 84ccff4cd35b7..6dc76f6a8856c 100644 --- a/test/examples/partial_results/index.ts +++ b/test/examples/partial_results/index.ts @@ -16,7 +16,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('Partial Results Example', function () { before(async () => { - this.tags('ciGroup11'); await PageObjects.common.navigateToApp('partialResultsExample'); const element = await testSubjects.find('example-help'); diff --git a/test/examples/routing/index.ts b/test/examples/routing/index.ts index 949d8cfc7547a..0012283d8535f 100644 --- a/test/examples/routing/index.ts +++ b/test/examples/routing/index.ts @@ -17,7 +17,6 @@ export default function ({ getService, getPageObjects, loadTestFile }: FtrProvid describe('routing examples', function () { before(async () => { - this.tags('ciGroup11'); await PageObjects.common.navigateToApp('routingExample'); }); diff --git a/test/examples/state_sync/index.ts b/test/examples/state_sync/index.ts index a33c014f4dd9d..6f70794497bef 100644 --- a/test/examples/state_sync/index.ts +++ b/test/examples/state_sync/index.ts @@ -17,7 +17,6 @@ export default function ({ const browser = getService('browser'); describe('state sync examples', function () { - this.tags('ciGroup11'); before(async () => { await browser.setWindowSize(1300, 900); }); diff --git a/test/examples/ui_actions/index.ts b/test/examples/ui_actions/index.ts index b04d361cc86ec..400af962053ba 100644 --- a/test/examples/ui_actions/index.ts +++ b/test/examples/ui_actions/index.ts @@ -18,7 +18,6 @@ export default function ({ const PageObjects = getPageObjects(['common', 'header']); describe('ui actions explorer', function () { - this.tags('ciGroup11'); before(async () => { await browser.setWindowSize(1300, 900); await PageObjects.common.navigateToApp('uiActionsExplorer'); diff --git a/test/functional/apps/bundles/config.ts b/test/functional/apps/bundles/config.ts new file mode 100644 index 0000000000000..e487d31dcb657 --- /dev/null +++ b/test/functional/apps/bundles/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/bundles/index.js b/test/functional/apps/bundles/index.js index c3ce4201a470c..105aa155a9f1f 100644 --- a/test/functional/apps/bundles/index.js +++ b/test/functional/apps/bundles/index.js @@ -14,7 +14,7 @@ export default function ({ getService }) { const supertest = getService('supertest'); describe('bundle compression', function () { - this.tags(['ciGroup11', 'skipCoverage']); + this.tags('skipCoverage'); let buildNum; before(async () => { diff --git a/test/functional/apps/console/config.ts b/test/functional/apps/console/config.ts new file mode 100644 index 0000000000000..e487d31dcb657 --- /dev/null +++ b/test/functional/apps/console/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/console/index.js b/test/functional/apps/console/index.js index c3d0553514cb5..1944e10b5239f 100644 --- a/test/functional/apps/console/index.js +++ b/test/functional/apps/console/index.js @@ -10,8 +10,6 @@ export default function ({ getService, loadTestFile }) { const browser = getService('browser'); describe('console app', function () { - this.tags('ciGroup1'); - before(async function () { await browser.setWindowSize(1300, 1100); }); diff --git a/test/functional/apps/context/config.ts b/test/functional/apps/context/config.ts new file mode 100644 index 0000000000000..e487d31dcb657 --- /dev/null +++ b/test/functional/apps/context/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/context/index.ts b/test/functional/apps/context/index.ts index 1320a22aad09b..20e1bcc2a3cb4 100644 --- a/test/functional/apps/context/index.ts +++ b/test/functional/apps/context/index.ts @@ -15,8 +15,6 @@ export default function ({ getService, getPageObjects, loadTestFile }: FtrProvid const kibanaServer = getService('kibanaServer'); describe('context app', function () { - this.tags('ciGroup1'); - before(async () => { await browser.setWindowSize(1200, 800); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); diff --git a/test/functional/apps/dashboard/README.md b/test/functional/apps/dashboard/README.md new file mode 100644 index 0000000000000..5e87a8b210bdd --- /dev/null +++ b/test/functional/apps/dashboard/README.md @@ -0,0 +1,7 @@ +# What are all these groups? + +These tests take a while so they have been broken up into groups with their own `config.ts` and `index.ts` file, causing each of these groups to be independent bundles of tests which can be run on some worker in CI without taking an incredible amount of time. + +Want to change the groups to something more logical? Have fun! Just make sure that each group executes on CI in less than 10 minutes or so. We don't currently have any mechanism for validating this right now, you just need to look at the times in the log output on CI, but we'll be working on tooling for making this information more accessible soon. + +- Kibana Operations \ No newline at end of file diff --git a/test/functional/apps/dashboard/group1/config.ts b/test/functional/apps/dashboard/group1/config.ts new file mode 100644 index 0000000000000..a70a190ca63f8 --- /dev/null +++ b/test/functional/apps/dashboard/group1/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/dashboard/create_and_add_embeddables.ts b/test/functional/apps/dashboard/group1/create_and_add_embeddables.ts similarity index 99% rename from test/functional/apps/dashboard/create_and_add_embeddables.ts rename to test/functional/apps/dashboard/group1/create_and_add_embeddables.ts index 30100f0e1aa07..c96e596a88ecf 100644 --- a/test/functional/apps/dashboard/create_and_add_embeddables.ts +++ b/test/functional/apps/dashboard/group1/create_and_add_embeddables.ts @@ -10,7 +10,7 @@ import expect from '@kbn/expect'; import { VisualizeConstants } from '@kbn/visualizations-plugin/common/constants'; import { VISUALIZE_ENABLE_LABS_SETTING } from '@kbn/visualizations-plugin/common/constants'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); diff --git a/test/functional/apps/dashboard/dashboard_back_button.ts b/test/functional/apps/dashboard/group1/dashboard_back_button.ts similarity index 96% rename from test/functional/apps/dashboard/dashboard_back_button.ts rename to test/functional/apps/dashboard/group1/dashboard_back_button.ts index d532444befdab..1fd9614d2421a 100644 --- a/test/functional/apps/dashboard/dashboard_back_button.ts +++ b/test/functional/apps/dashboard/group1/dashboard_back_button.ts @@ -7,7 +7,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); diff --git a/test/functional/apps/dashboard/dashboard_error_handling.ts b/test/functional/apps/dashboard/group1/dashboard_error_handling.ts similarity index 97% rename from test/functional/apps/dashboard/dashboard_error_handling.ts rename to test/functional/apps/dashboard/group1/dashboard_error_handling.ts index 58304359458c7..e950b8aef975d 100644 --- a/test/functional/apps/dashboard/dashboard_error_handling.ts +++ b/test/functional/apps/dashboard/group1/dashboard_error_handling.ts @@ -7,7 +7,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['dashboard', 'header', 'common']); diff --git a/test/functional/apps/dashboard/dashboard_options.ts b/test/functional/apps/dashboard/group1/dashboard_options.ts similarity index 96% rename from test/functional/apps/dashboard/dashboard_options.ts rename to test/functional/apps/dashboard/group1/dashboard_options.ts index 282674d0cec98..096f8595072bf 100644 --- a/test/functional/apps/dashboard/dashboard_options.ts +++ b/test/functional/apps/dashboard/group1/dashboard_options.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); diff --git a/test/functional/apps/dashboard/dashboard_query_bar.ts b/test/functional/apps/dashboard/group1/dashboard_query_bar.ts similarity index 96% rename from test/functional/apps/dashboard/dashboard_query_bar.ts rename to test/functional/apps/dashboard/group1/dashboard_query_bar.ts index 5092cadaf9d26..290cc62dca58f 100644 --- a/test/functional/apps/dashboard/dashboard_query_bar.ts +++ b/test/functional/apps/dashboard/group1/dashboard_query_bar.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); diff --git a/test/functional/apps/dashboard/dashboard_unsaved_listing.ts b/test/functional/apps/dashboard/group1/dashboard_unsaved_listing.ts similarity index 99% rename from test/functional/apps/dashboard/dashboard_unsaved_listing.ts rename to test/functional/apps/dashboard/group1/dashboard_unsaved_listing.ts index a1db57784b5f8..6b55a44ff9e79 100644 --- a/test/functional/apps/dashboard/dashboard_unsaved_listing.ts +++ b/test/functional/apps/dashboard/group1/dashboard_unsaved_listing.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'settings', 'common']); diff --git a/test/functional/apps/dashboard/dashboard_unsaved_state.ts b/test/functional/apps/dashboard/group1/dashboard_unsaved_state.ts similarity index 99% rename from test/functional/apps/dashboard/dashboard_unsaved_state.ts rename to test/functional/apps/dashboard/group1/dashboard_unsaved_state.ts index 5afe3b9937433..2447a122a77aa 100644 --- a/test/functional/apps/dashboard/dashboard_unsaved_state.ts +++ b/test/functional/apps/dashboard/group1/dashboard_unsaved_state.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'settings', 'common']); diff --git a/test/functional/apps/dashboard/data_shared_attributes.ts b/test/functional/apps/dashboard/group1/data_shared_attributes.ts similarity index 98% rename from test/functional/apps/dashboard/data_shared_attributes.ts rename to test/functional/apps/dashboard/group1/data_shared_attributes.ts index a94cf1b6063a9..d4070c700a925 100644 --- a/test/functional/apps/dashboard/data_shared_attributes.ts +++ b/test/functional/apps/dashboard/group1/data_shared_attributes.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); diff --git a/test/functional/apps/dashboard/edit_embeddable_redirects.ts b/test/functional/apps/dashboard/group1/edit_embeddable_redirects.ts similarity index 98% rename from test/functional/apps/dashboard/edit_embeddable_redirects.ts rename to test/functional/apps/dashboard/group1/edit_embeddable_redirects.ts index 763488cc21ab1..aca22d84e6843 100644 --- a/test/functional/apps/dashboard/edit_embeddable_redirects.ts +++ b/test/functional/apps/dashboard/group1/edit_embeddable_redirects.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'settings', 'common']); diff --git a/test/functional/apps/dashboard/edit_visualizations.js b/test/functional/apps/dashboard/group1/edit_visualizations.js similarity index 100% rename from test/functional/apps/dashboard/edit_visualizations.js rename to test/functional/apps/dashboard/group1/edit_visualizations.js diff --git a/test/functional/apps/dashboard/embed_mode.ts b/test/functional/apps/dashboard/group1/embed_mode.ts similarity index 94% rename from test/functional/apps/dashboard/embed_mode.ts rename to test/functional/apps/dashboard/group1/embed_mode.ts index 7e53bff7387ca..482c976d98689 100644 --- a/test/functional/apps/dashboard/embed_mode.ts +++ b/test/functional/apps/dashboard/group1/embed_mode.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); @@ -59,7 +59,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.missingOrFail('top-nav'); await testSubjects.missingOrFail('queryInput'); await testSubjects.missingOrFail('superDatePickerToggleQuickMenuButton'); - await testSubjects.existOrFail('showFilterActions'); + await testSubjects.existOrFail('showQueryBarMenu'); const currentUrl = await browser.getCurrentUrl(); const newUrl = [currentUrl].concat(urlParamExtensions).join('&'); @@ -70,7 +70,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.existOrFail('top-nav'); await testSubjects.existOrFail('queryInput'); await testSubjects.existOrFail('superDatePickerToggleQuickMenuButton'); - await testSubjects.missingOrFail('showFilterActions'); }); after(async function () { diff --git a/test/functional/apps/dashboard/embeddable_data_grid.ts b/test/functional/apps/dashboard/group1/embeddable_data_grid.ts similarity index 97% rename from test/functional/apps/dashboard/embeddable_data_grid.ts rename to test/functional/apps/dashboard/group1/embeddable_data_grid.ts index 060c467656662..85277e63d6f6c 100644 --- a/test/functional/apps/dashboard/embeddable_data_grid.ts +++ b/test/functional/apps/dashboard/group1/embeddable_data_grid.ts @@ -7,7 +7,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const dashboardAddPanel = getService('dashboardAddPanel'); diff --git a/test/functional/apps/dashboard/embeddable_rendering.ts b/test/functional/apps/dashboard/group1/embeddable_rendering.ts similarity index 99% rename from test/functional/apps/dashboard/embeddable_rendering.ts rename to test/functional/apps/dashboard/group1/embeddable_rendering.ts index 840826be46532..5274a2c12e878 100644 --- a/test/functional/apps/dashboard/embeddable_rendering.ts +++ b/test/functional/apps/dashboard/group1/embeddable_rendering.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; /** * This tests both that one of each visualization can be added to a dashboard (as opposed to opening an existing diff --git a/test/functional/apps/dashboard/empty_dashboard.ts b/test/functional/apps/dashboard/group1/empty_dashboard.ts similarity index 97% rename from test/functional/apps/dashboard/empty_dashboard.ts rename to test/functional/apps/dashboard/group1/empty_dashboard.ts index a7524eaa94b8a..e559c0ef81f60 100644 --- a/test/functional/apps/dashboard/empty_dashboard.ts +++ b/test/functional/apps/dashboard/group1/empty_dashboard.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); diff --git a/test/functional/apps/dashboard/group1/index.ts b/test/functional/apps/dashboard/group1/index.ts new file mode 100644 index 0000000000000..597102433ef45 --- /dev/null +++ b/test/functional/apps/dashboard/group1/index.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const browser = getService('browser'); + const esArchiver = getService('esArchiver'); + + async function loadCurrentData() { + await browser.setWindowSize(1300, 900); + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/dashboard/current/data'); + } + + async function unloadCurrentData() { + await esArchiver.unload('test/functional/fixtures/es_archiver/dashboard/current/data'); + } + + describe('dashboard app - group 1', function () { + before(loadCurrentData); + after(unloadCurrentData); + + // This has to be first since the other tests create some embeddables as side affects and our counting assumes + // a fresh index. + loadTestFile(require.resolve('./empty_dashboard')); + loadTestFile(require.resolve('./url_field_formatter')); + loadTestFile(require.resolve('./embeddable_rendering')); + loadTestFile(require.resolve('./embeddable_data_grid')); + loadTestFile(require.resolve('./create_and_add_embeddables')); + loadTestFile(require.resolve('./edit_embeddable_redirects')); + loadTestFile(require.resolve('./dashboard_unsaved_state')); + loadTestFile(require.resolve('./dashboard_unsaved_listing')); + loadTestFile(require.resolve('./edit_visualizations')); + loadTestFile(require.resolve('./dashboard_options')); + loadTestFile(require.resolve('./data_shared_attributes')); + loadTestFile(require.resolve('./share')); + loadTestFile(require.resolve('./embed_mode')); + loadTestFile(require.resolve('./dashboard_back_button')); + loadTestFile(require.resolve('./dashboard_error_handling')); + loadTestFile(require.resolve('./legacy_urls')); + loadTestFile(require.resolve('./saved_search_embeddable')); + + // Note: This one must be last because it unloads some data for one of its tests! + // No, this isn't ideal, but loading/unloading takes so much time and these are all bunched + // to improve efficiency... + loadTestFile(require.resolve('./dashboard_query_bar')); + }); +} diff --git a/test/functional/apps/dashboard/legacy_urls.ts b/test/functional/apps/dashboard/group1/legacy_urls.ts similarity index 98% rename from test/functional/apps/dashboard/legacy_urls.ts rename to test/functional/apps/dashboard/group1/legacy_urls.ts index 1e4138e63d393..e11da2d82fe47 100644 --- a/test/functional/apps/dashboard/legacy_urls.ts +++ b/test/functional/apps/dashboard/group1/legacy_urls.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects([ diff --git a/test/functional/apps/dashboard/saved_search_embeddable.ts b/test/functional/apps/dashboard/group1/saved_search_embeddable.ts similarity index 98% rename from test/functional/apps/dashboard/saved_search_embeddable.ts rename to test/functional/apps/dashboard/group1/saved_search_embeddable.ts index 02050eec30227..e0ecc40d2486b 100644 --- a/test/functional/apps/dashboard/saved_search_embeddable.ts +++ b/test/functional/apps/dashboard/group1/saved_search_embeddable.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const dashboardAddPanel = getService('dashboardAddPanel'); diff --git a/test/functional/apps/dashboard/share.ts b/test/functional/apps/dashboard/group1/share.ts similarity index 95% rename from test/functional/apps/dashboard/share.ts rename to test/functional/apps/dashboard/group1/share.ts index 7fe8048ab7c04..871ab5bed1488 100644 --- a/test/functional/apps/dashboard/share.ts +++ b/test/functional/apps/dashboard/group1/share.ts @@ -7,7 +7,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); diff --git a/test/functional/apps/dashboard/url_field_formatter.ts b/test/functional/apps/dashboard/group1/url_field_formatter.ts similarity index 95% rename from test/functional/apps/dashboard/url_field_formatter.ts rename to test/functional/apps/dashboard/group1/url_field_formatter.ts index 8e9dd7b66e79f..be454549af378 100644 --- a/test/functional/apps/dashboard/url_field_formatter.ts +++ b/test/functional/apps/dashboard/group1/url_field_formatter.ts @@ -7,8 +7,8 @@ */ import expect from '@kbn/expect'; -import { WebElementWrapper } from '../../services/lib/web_element_wrapper'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { WebElementWrapper } from '../../../services/lib/web_element_wrapper'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const { common, dashboard, settings, timePicker, visChart } = getPageObjects([ diff --git a/test/functional/apps/dashboard/group2/config.ts b/test/functional/apps/dashboard/group2/config.ts new file mode 100644 index 0000000000000..a70a190ca63f8 --- /dev/null +++ b/test/functional/apps/dashboard/group2/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/dashboard/dashboard_filter_bar.ts b/test/functional/apps/dashboard/group2/dashboard_filter_bar.ts similarity index 99% rename from test/functional/apps/dashboard/dashboard_filter_bar.ts rename to test/functional/apps/dashboard/group2/dashboard_filter_bar.ts index 3f74c4bc2f0dc..966b453409433 100644 --- a/test/functional/apps/dashboard/dashboard_filter_bar.ts +++ b/test/functional/apps/dashboard/group2/dashboard_filter_bar.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const dataGrid = getService('dataGrid'); diff --git a/test/functional/apps/dashboard/dashboard_filtering.ts b/test/functional/apps/dashboard/group2/dashboard_filtering.ts similarity index 99% rename from test/functional/apps/dashboard/dashboard_filtering.ts rename to test/functional/apps/dashboard/group2/dashboard_filtering.ts index 9522c47f907fc..09acbd5965020 100644 --- a/test/functional/apps/dashboard/dashboard_filtering.ts +++ b/test/functional/apps/dashboard/group2/dashboard_filtering.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; /** * Test the querying capabilities of dashboard, and make sure visualizations show the expected results, especially diff --git a/test/functional/apps/dashboard/dashboard_grid.ts b/test/functional/apps/dashboard/group2/dashboard_grid.ts similarity index 96% rename from test/functional/apps/dashboard/dashboard_grid.ts rename to test/functional/apps/dashboard/group2/dashboard_grid.ts index 25e901fd25d8b..90e2187e19eb4 100644 --- a/test/functional/apps/dashboard/dashboard_grid.ts +++ b/test/functional/apps/dashboard/group2/dashboard_grid.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); diff --git a/test/functional/apps/dashboard/dashboard_saved_query.ts b/test/functional/apps/dashboard/group2/dashboard_saved_query.ts similarity index 82% rename from test/functional/apps/dashboard/dashboard_saved_query.ts rename to test/functional/apps/dashboard/group2/dashboard_saved_query.ts index 658afb9c641b2..1dad54234e8a3 100644 --- a/test/functional/apps/dashboard/dashboard_saved_query.ts +++ b/test/functional/apps/dashboard/group2/dashboard_saved_query.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); @@ -40,22 +40,30 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.clickNewDashboard(); }); - it('should show the saved query management component when there are no saved queries', async () => { - await savedQueryManagementComponent.openSavedQueryManagementComponent(); - const descriptionText = await testSubjects.getVisibleText('saved-query-management-popover'); - expect(descriptionText).to.eql( - 'Saved Queries\nThere are no saved queries. Save query text and filters that you want to use again.\nSave current query' - ); + it('should show the saved query management load button as disabled when there are no saved queries', async () => { + await testSubjects.click('showQueryBarMenu'); + const loadFilterSetBtn = await testSubjects.find('saved-query-management-load-button'); + const isDisabled = await loadFilterSetBtn.getAttribute('disabled'); + expect(isDisabled).to.equal('true'); }); it('should allow a query to be saved via the saved objects management component', async () => { await queryBar.setQuery('response:200'); + await queryBar.clickQuerySubmitButton(); + await testSubjects.click('showQueryBarMenu'); await savedQueryManagementComponent.saveNewQuery( 'OkResponse', '200 responses for .jpg over 24 hours', true, true ); + const contextMenuPanelTitleButton = await testSubjects.exists( + 'contextMenuPanelTitleButton' + ); + if (contextMenuPanelTitleButton) { + await testSubjects.click('contextMenuPanelTitleButton'); + } + await savedQueryManagementComponent.savedQueryExistOrFail('OkResponse'); await savedQueryManagementComponent.savedQueryTextExist('response:200'); }); @@ -81,6 +89,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await queryBar.setQuery('response:404'); await savedQueryManagementComponent.updateCurrentlyLoadedQuery('OkResponse', false, false); await savedQueryManagementComponent.savedQueryExistOrFail('OkResponse'); + const contextMenuPanelTitleButton = await testSubjects.exists( + 'contextMenuPanelTitleButton' + ); + if (contextMenuPanelTitleButton) { + await testSubjects.click('contextMenuPanelTitleButton'); + } await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); expect(await queryBar.getQueryString()).to.eql(''); await savedQueryManagementComponent.loadSavedQuery('OkResponse'); @@ -88,9 +102,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('allows saving the currently loaded query as a new query', async () => { + await queryBar.setQuery('response:400'); await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( 'OkResponseCopy', - '200 responses', + '400 responses', false, false ); diff --git a/test/functional/apps/dashboard/dashboard_snapshots.ts b/test/functional/apps/dashboard/group2/dashboard_snapshots.ts similarity index 96% rename from test/functional/apps/dashboard/dashboard_snapshots.ts rename to test/functional/apps/dashboard/group2/dashboard_snapshots.ts index 9cb52c5dd5511..56dcfe2388bc2 100644 --- a/test/functional/apps/dashboard/dashboard_snapshots.ts +++ b/test/functional/apps/dashboard/group2/dashboard_snapshots.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, @@ -84,7 +84,7 @@ export default function ({ ); await PageObjects.dashboard.clickExitFullScreenLogoButton(); - expect(percentDifference).to.be.lessThan(0.022); + expect(percentDifference).to.be.lessThan(0.029); }); }); } diff --git a/test/functional/apps/dashboard/embeddable_library.ts b/test/functional/apps/dashboard/group2/embeddable_library.ts similarity index 97% rename from test/functional/apps/dashboard/embeddable_library.ts rename to test/functional/apps/dashboard/group2/embeddable_library.ts index 2abf75f6385ac..ca52eaecaf46e 100644 --- a/test/functional/apps/dashboard/embeddable_library.ts +++ b/test/functional/apps/dashboard/group2/embeddable_library.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'settings', 'common']); diff --git a/test/functional/apps/dashboard/full_screen_mode.ts b/test/functional/apps/dashboard/group2/full_screen_mode.ts similarity index 98% rename from test/functional/apps/dashboard/full_screen_mode.ts rename to test/functional/apps/dashboard/group2/full_screen_mode.ts index 74fa2168a1461..35d9ed8a2a15c 100644 --- a/test/functional/apps/dashboard/full_screen_mode.ts +++ b/test/functional/apps/dashboard/group2/full_screen_mode.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); diff --git a/test/functional/apps/dashboard/group2/index.ts b/test/functional/apps/dashboard/group2/index.ts new file mode 100644 index 0000000000000..004c85f2da760 --- /dev/null +++ b/test/functional/apps/dashboard/group2/index.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const browser = getService('browser'); + const esArchiver = getService('esArchiver'); + + async function loadCurrentData() { + await browser.setWindowSize(1300, 900); + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/dashboard/current/data'); + } + + async function unloadCurrentData() { + await esArchiver.unload('test/functional/fixtures/es_archiver/dashboard/current/data'); + } + + describe('dashboard app - group 2', function () { + before(loadCurrentData); + after(unloadCurrentData); + + loadTestFile(require.resolve('./full_screen_mode')); + loadTestFile(require.resolve('./dashboard_filter_bar')); + loadTestFile(require.resolve('./dashboard_filtering')); + loadTestFile(require.resolve('./panel_expand_toggle')); + loadTestFile(require.resolve('./dashboard_grid')); + loadTestFile(require.resolve('./view_edit')); + loadTestFile(require.resolve('./dashboard_saved_query')); + // Order of test suites *shouldn't* be important but there's a bug for the view_edit test above + // https://github.com/elastic/kibana/issues/46752 + // The dashboard_snapshot test below requires the timestamped URL which breaks the view_edit test. + // If we don't use the timestamp in the URL, the colors in the charts will be different. + loadTestFile(require.resolve('./dashboard_snapshots')); + loadTestFile(require.resolve('./embeddable_library')); + }); +} diff --git a/test/functional/apps/dashboard/panel_expand_toggle.ts b/test/functional/apps/dashboard/group2/panel_expand_toggle.ts similarity index 97% rename from test/functional/apps/dashboard/panel_expand_toggle.ts rename to test/functional/apps/dashboard/group2/panel_expand_toggle.ts index 272ec3824e233..f33280ba7bb79 100644 --- a/test/functional/apps/dashboard/panel_expand_toggle.ts +++ b/test/functional/apps/dashboard/group2/panel_expand_toggle.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); diff --git a/test/functional/apps/dashboard/view_edit.ts b/test/functional/apps/dashboard/group2/view_edit.ts similarity index 99% rename from test/functional/apps/dashboard/view_edit.ts rename to test/functional/apps/dashboard/group2/view_edit.ts index a73924a8ae75f..dfd62eeaa6cb3 100644 --- a/test/functional/apps/dashboard/view_edit.ts +++ b/test/functional/apps/dashboard/group2/view_edit.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const queryBar = getService('queryBar'); diff --git a/test/functional/apps/dashboard/bwc_shared_urls.ts b/test/functional/apps/dashboard/group3/bwc_shared_urls.ts similarity index 99% rename from test/functional/apps/dashboard/bwc_shared_urls.ts rename to test/functional/apps/dashboard/group3/bwc_shared_urls.ts index 569cd8e2a67d5..01b1c8379089e 100644 --- a/test/functional/apps/dashboard/bwc_shared_urls.ts +++ b/test/functional/apps/dashboard/group3/bwc_shared_urls.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['dashboard', 'header']); diff --git a/test/functional/apps/dashboard/group3/config.ts b/test/functional/apps/dashboard/group3/config.ts new file mode 100644 index 0000000000000..a70a190ca63f8 --- /dev/null +++ b/test/functional/apps/dashboard/group3/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/dashboard/copy_panel_to.ts b/test/functional/apps/dashboard/group3/copy_panel_to.ts similarity index 98% rename from test/functional/apps/dashboard/copy_panel_to.ts rename to test/functional/apps/dashboard/group3/copy_panel_to.ts index 9a61b289ee1f3..1f40f780a5398 100644 --- a/test/functional/apps/dashboard/copy_panel_to.ts +++ b/test/functional/apps/dashboard/group3/copy_panel_to.ts @@ -7,7 +7,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const dashboardVisualizations = getService('dashboardVisualizations'); diff --git a/test/functional/apps/dashboard/dashboard_state.ts b/test/functional/apps/dashboard/group3/dashboard_state.ts similarity index 97% rename from test/functional/apps/dashboard/dashboard_state.ts rename to test/functional/apps/dashboard/group3/dashboard_state.ts index d931475766776..c5306f4ab4ff3 100644 --- a/test/functional/apps/dashboard/dashboard_state.ts +++ b/test/functional/apps/dashboard/group3/dashboard_state.ts @@ -10,8 +10,8 @@ import expect from '@kbn/expect'; import chroma from 'chroma-js'; import { DEFAULT_PANEL_WIDTH } from '@kbn/dashboard-plugin/public/application/embeddable/dashboard_constants'; -import { PIE_CHART_VIS_NAME, AREA_CHART_VIS_NAME } from '../../page_objects/dashboard_page'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { PIE_CHART_VIS_NAME, AREA_CHART_VIS_NAME } from '../../../page_objects/dashboard_page'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects([ @@ -48,13 +48,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { isNewChartsLibraryEnabled = await PageObjects.visChart.isNewChartsLibraryEnabled(); await PageObjects.dashboard.initTests(); await PageObjects.dashboard.preserveCrossAppState(); + await browser.setLocalStorageItem('data.newDataViewMenu', 'true'); if (isNewChartsLibraryEnabled) { await kibanaServer.uiSettings.update({ 'visualization:visualize:legacyPieChartsLibrary': false, }); - await browser.refresh(); } + await browser.refresh(); }); after(async function () { diff --git a/test/functional/apps/dashboard/dashboard_time_picker.ts b/test/functional/apps/dashboard/group3/dashboard_time_picker.ts similarity index 97% rename from test/functional/apps/dashboard/dashboard_time_picker.ts rename to test/functional/apps/dashboard/group3/dashboard_time_picker.ts index 6f876185fd8dd..37f6e4f2ef5df 100644 --- a/test/functional/apps/dashboard/dashboard_time_picker.ts +++ b/test/functional/apps/dashboard/group3/dashboard_time_picker.ts @@ -8,8 +8,8 @@ import expect from '@kbn/expect'; -import { PIE_CHART_VIS_NAME } from '../../page_objects/dashboard_page'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { PIE_CHART_VIS_NAME } from '../../../page_objects/dashboard_page'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const dashboardExpect = getService('dashboardExpect'); diff --git a/test/functional/apps/dashboard/group3/index.ts b/test/functional/apps/dashboard/group3/index.ts new file mode 100644 index 0000000000000..f3a10500fe4e6 --- /dev/null +++ b/test/functional/apps/dashboard/group3/index.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 and the Server Side Public License, v 1; you may 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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const browser = getService('browser'); + const esArchiver = getService('esArchiver'); + + async function loadLogstash() { + await browser.setWindowSize(1200, 900); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + } + + async function unloadLogstash() { + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + } + + describe('dashboard app - group 3', function () { + before(loadLogstash); + after(unloadLogstash); + + loadTestFile(require.resolve('./dashboard_time_picker')); + loadTestFile(require.resolve('./bwc_shared_urls')); + loadTestFile(require.resolve('./panel_replacing')); + loadTestFile(require.resolve('./panel_cloning')); + loadTestFile(require.resolve('./copy_panel_to')); + loadTestFile(require.resolve('./panel_context_menu')); + loadTestFile(require.resolve('./dashboard_state')); + }); +} diff --git a/test/functional/apps/dashboard/panel_cloning.ts b/test/functional/apps/dashboard/group3/panel_cloning.ts similarity index 96% rename from test/functional/apps/dashboard/panel_cloning.ts rename to test/functional/apps/dashboard/group3/panel_cloning.ts index a2cadd89f486a..4de65419d2ecb 100644 --- a/test/functional/apps/dashboard/panel_cloning.ts +++ b/test/functional/apps/dashboard/group3/panel_cloning.ts @@ -7,8 +7,8 @@ */ import expect from '@kbn/expect'; -import { PIE_CHART_VIS_NAME } from '../../page_objects/dashboard_page'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { PIE_CHART_VIS_NAME } from '../../../page_objects/dashboard_page'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const dashboardPanelActions = getService('dashboardPanelActions'); diff --git a/test/functional/apps/dashboard/panel_context_menu.ts b/test/functional/apps/dashboard/group3/panel_context_menu.ts similarity index 98% rename from test/functional/apps/dashboard/panel_context_menu.ts rename to test/functional/apps/dashboard/group3/panel_context_menu.ts index 8c82c162f5e86..f78cd27614b3b 100644 --- a/test/functional/apps/dashboard/panel_context_menu.ts +++ b/test/functional/apps/dashboard/group3/panel_context_menu.ts @@ -8,8 +8,8 @@ import expect from '@kbn/expect'; import { VisualizeConstants } from '@kbn/visualizations-plugin/common/constants'; -import { PIE_CHART_VIS_NAME } from '../../page_objects/dashboard_page'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { PIE_CHART_VIS_NAME } from '../../../page_objects/dashboard_page'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); diff --git a/test/functional/apps/dashboard/panel_replacing.ts b/test/functional/apps/dashboard/group3/panel_replacing.ts similarity index 97% rename from test/functional/apps/dashboard/panel_replacing.ts rename to test/functional/apps/dashboard/group3/panel_replacing.ts index b9ba731beee29..e6ff8c4f940bb 100644 --- a/test/functional/apps/dashboard/panel_replacing.ts +++ b/test/functional/apps/dashboard/group3/panel_replacing.ts @@ -11,8 +11,8 @@ import { PIE_CHART_VIS_NAME, AREA_CHART_VIS_NAME, LINE_CHART_VIS_NAME, -} from '../../page_objects/dashboard_page'; -import { FtrProviderContext } from '../../ftr_provider_context'; +} from '../../../page_objects/dashboard_page'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); diff --git a/test/functional/apps/dashboard/group4/config.ts b/test/functional/apps/dashboard/group4/config.ts new file mode 100644 index 0000000000000..a70a190ca63f8 --- /dev/null +++ b/test/functional/apps/dashboard/group4/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/dashboard/dashboard_clone.ts b/test/functional/apps/dashboard/group4/dashboard_clone.ts similarity index 97% rename from test/functional/apps/dashboard/dashboard_clone.ts rename to test/functional/apps/dashboard/group4/dashboard_clone.ts index 5fc0b0c28c914..26738760b28e4 100644 --- a/test/functional/apps/dashboard/dashboard_clone.ts +++ b/test/functional/apps/dashboard/group4/dashboard_clone.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); diff --git a/test/functional/apps/dashboard/dashboard_listing.ts b/test/functional/apps/dashboard/group4/dashboard_listing.ts similarity index 99% rename from test/functional/apps/dashboard/dashboard_listing.ts rename to test/functional/apps/dashboard/group4/dashboard_listing.ts index 9182a0d318228..4a9827ce02f91 100644 --- a/test/functional/apps/dashboard/dashboard_listing.ts +++ b/test/functional/apps/dashboard/group4/dashboard_listing.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['dashboard', 'header', 'common']); diff --git a/test/functional/apps/dashboard/dashboard_save.ts b/test/functional/apps/dashboard/group4/dashboard_save.ts similarity index 98% rename from test/functional/apps/dashboard/dashboard_save.ts rename to test/functional/apps/dashboard/group4/dashboard_save.ts index 4ab8633a5619b..f20817c65d25d 100644 --- a/test/functional/apps/dashboard/dashboard_save.ts +++ b/test/functional/apps/dashboard/group4/dashboard_save.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['dashboard', 'header', 'visualize']); diff --git a/test/functional/apps/dashboard/dashboard_time.ts b/test/functional/apps/dashboard/group4/dashboard_time.ts similarity index 98% rename from test/functional/apps/dashboard/dashboard_time.ts rename to test/functional/apps/dashboard/group4/dashboard_time.ts index 2c0394474adab..2ff91185be60a 100644 --- a/test/functional/apps/dashboard/dashboard_time.ts +++ b/test/functional/apps/dashboard/group4/dashboard_time.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; const dashboardName = 'Dashboard Test Time'; diff --git a/test/functional/apps/dashboard/group4/index.ts b/test/functional/apps/dashboard/group4/index.ts new file mode 100644 index 0000000000000..8c9a291d2e577 --- /dev/null +++ b/test/functional/apps/dashboard/group4/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const browser = getService('browser'); + const esArchiver = getService('esArchiver'); + + async function loadLogstash() { + await browser.setWindowSize(1200, 900); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + } + + async function unloadLogstash() { + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + } + + describe('dashboard app - group 4', function () { + before(loadLogstash); + after(unloadLogstash); + + loadTestFile(require.resolve('./dashboard_save')); + loadTestFile(require.resolve('./dashboard_time')); + loadTestFile(require.resolve('./dashboard_listing')); + loadTestFile(require.resolve('./dashboard_clone')); + }); +} diff --git a/test/functional/apps/dashboard/group5/config.ts b/test/functional/apps/dashboard/group5/config.ts new file mode 100644 index 0000000000000..a70a190ca63f8 --- /dev/null +++ b/test/functional/apps/dashboard/group5/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/dashboard/group5/index.ts b/test/functional/apps/dashboard/group5/index.ts new file mode 100644 index 0000000000000..14f4a6366477d --- /dev/null +++ b/test/functional/apps/dashboard/group5/index.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const browser = getService('browser'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + + async function loadLogstash() { + await browser.setWindowSize(1200, 900); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + } + + async function unloadLogstash() { + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + } + + describe('dashboard app - group 5', function () { + // TODO: Remove when vislib is removed + // https://github.com/elastic/kibana/issues/56143 + describe('new charts library', function () { + before(async () => { + await loadLogstash(); + await kibanaServer.uiSettings.update({ + 'visualization:visualize:legacyPieChartsLibrary': false, + }); + await browser.refresh(); + }); + + after(async () => { + await unloadLogstash(); + await kibanaServer.uiSettings.update({ + 'visualization:visualize:legacyPieChartsLibrary': true, + }); + await browser.refresh(); + }); + + loadTestFile(require.resolve('../group3/dashboard_state')); + }); + }); +} diff --git a/test/functional/apps/dashboard/index.ts b/test/functional/apps/dashboard/index.ts deleted file mode 100644 index 4f69504ef7d5c..0000000000000 --- a/test/functional/apps/dashboard/index.ts +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ getService, loadTestFile }: FtrProviderContext) { - const browser = getService('browser'); - const esArchiver = getService('esArchiver'); - const kibanaServer = getService('kibanaServer'); - - async function loadCurrentData() { - await browser.setWindowSize(1300, 900); - await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/dashboard/current/data'); - } - - async function unloadCurrentData() { - await esArchiver.unload('test/functional/fixtures/es_archiver/dashboard/current/data'); - } - - async function loadLogstash() { - await browser.setWindowSize(1200, 900); - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); - } - - async function unloadLogstash() { - await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); - } - - describe('dashboard app', function () { - // This has to be first since the other tests create some embeddables as side affects and our counting assumes - // a fresh index. - describe('using current data', function () { - this.tags('ciGroup2'); - before(loadCurrentData); - after(unloadCurrentData); - - loadTestFile(require.resolve('./empty_dashboard')); - loadTestFile(require.resolve('./url_field_formatter')); - loadTestFile(require.resolve('./embeddable_rendering')); - loadTestFile(require.resolve('./embeddable_data_grid')); - loadTestFile(require.resolve('./create_and_add_embeddables')); - loadTestFile(require.resolve('./edit_embeddable_redirects')); - loadTestFile(require.resolve('./dashboard_unsaved_state')); - loadTestFile(require.resolve('./dashboard_unsaved_listing')); - loadTestFile(require.resolve('./edit_visualizations')); - loadTestFile(require.resolve('./dashboard_options')); - loadTestFile(require.resolve('./data_shared_attributes')); - loadTestFile(require.resolve('./share')); - loadTestFile(require.resolve('./embed_mode')); - loadTestFile(require.resolve('./dashboard_back_button')); - loadTestFile(require.resolve('./dashboard_error_handling')); - loadTestFile(require.resolve('./legacy_urls')); - loadTestFile(require.resolve('./saved_search_embeddable')); - - // Note: This one must be last because it unloads some data for one of its tests! - // No, this isn't ideal, but loading/unloading takes so much time and these are all bunched - // to improve efficiency... - loadTestFile(require.resolve('./dashboard_query_bar')); - }); - - describe('using current data', function () { - this.tags('ciGroup3'); - before(loadCurrentData); - after(unloadCurrentData); - - loadTestFile(require.resolve('./full_screen_mode')); - loadTestFile(require.resolve('./dashboard_filter_bar')); - loadTestFile(require.resolve('./dashboard_filtering')); - loadTestFile(require.resolve('./panel_expand_toggle')); - loadTestFile(require.resolve('./dashboard_grid')); - loadTestFile(require.resolve('./view_edit')); - loadTestFile(require.resolve('./dashboard_saved_query')); - // Order of test suites *shouldn't* be important but there's a bug for the view_edit test above - // https://github.com/elastic/kibana/issues/46752 - // The dashboard_snapshot test below requires the timestamped URL which breaks the view_edit test. - // If we don't use the timestamp in the URL, the colors in the charts will be different. - loadTestFile(require.resolve('./dashboard_snapshots')); - loadTestFile(require.resolve('./embeddable_library')); - }); - - // Each of these tests call initTests themselves, the way it was originally written. The above tests only load - // the data once to save on time. Eventually, all of these tests should just use current data and we can reserve - // legacy data only for specifically testing BWC situations. - describe('using legacy data', function () { - this.tags('ciGroup4'); - before(loadLogstash); - after(unloadLogstash); - - loadTestFile(require.resolve('./dashboard_time_picker')); - loadTestFile(require.resolve('./bwc_shared_urls')); - loadTestFile(require.resolve('./panel_replacing')); - loadTestFile(require.resolve('./panel_cloning')); - loadTestFile(require.resolve('./copy_panel_to')); - loadTestFile(require.resolve('./panel_context_menu')); - loadTestFile(require.resolve('./dashboard_state')); - }); - - describe('using legacy data', function () { - this.tags('ciGroup5'); - before(loadLogstash); - after(unloadLogstash); - - loadTestFile(require.resolve('./dashboard_save')); - loadTestFile(require.resolve('./dashboard_time')); - loadTestFile(require.resolve('./dashboard_listing')); - loadTestFile(require.resolve('./dashboard_clone')); - }); - - // TODO: Remove when vislib is removed - // https://github.com/elastic/kibana/issues/56143 - describe('new charts library', function () { - this.tags('ciGroup5'); - - before(async () => { - await loadLogstash(); - await kibanaServer.uiSettings.update({ - 'visualization:visualize:legacyPieChartsLibrary': false, - }); - await browser.refresh(); - }); - - after(async () => { - await unloadLogstash(); - await kibanaServer.uiSettings.update({ - 'visualization:visualize:legacyPieChartsLibrary': true, - }); - await browser.refresh(); - }); - - loadTestFile(require.resolve('./dashboard_state')); - }); - }); -} diff --git a/test/functional/apps/dashboard_elements/config.ts b/test/functional/apps/dashboard_elements/config.ts new file mode 100644 index 0000000000000..e487d31dcb657 --- /dev/null +++ b/test/functional/apps/dashboard_elements/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/dashboard_elements/controls/options_list.ts b/test/functional/apps/dashboard_elements/controls/options_list.ts index a4da1c217f92b..de3144d98beab 100644 --- a/test/functional/apps/dashboard_elements/controls/options_list.ts +++ b/test/functional/apps/dashboard_elements/controls/options_list.ts @@ -286,7 +286,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboard.waitForRenderComplete(); await header.waitUntilLoadingHasFinished(); await ensureAvailableOptionsEql(allAvailableOptions); - await filterBar.removeAllFilters(); + await filterBar.removeFilter('sound.keyword'); }); it('Does not apply time range to options list control', async () => { @@ -406,6 +406,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('Options List dashboard no validation', async () => { before(async () => { + await filterBar.removeAllFilters(); + await queryBar.clickQuerySubmitButton(); await dashboardControls.optionsListOpenPopover(controlId); await dashboardControls.optionsListPopoverSelectOption('meow'); await dashboardControls.optionsListPopoverSelectOption('bark'); @@ -431,6 +433,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await filterBar.removeAllFilters(); + await queryBar.clickQuerySubmitButton(); await dashboardControls.clearAllControls(); }); }); diff --git a/test/functional/apps/dashboard_elements/index.ts b/test/functional/apps/dashboard_elements/index.ts index 6bd3e4e04a9c9..10b0c6e5ecff5 100644 --- a/test/functional/apps/dashboard_elements/index.ts +++ b/test/functional/apps/dashboard_elements/index.ts @@ -29,9 +29,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await esArchiver.unload('test/functional/fixtures/es_archiver/long_window_logstash'); }); - describe('dashboard elements ciGroup10', function () { - this.tags('ciGroup10'); - + describe('dashboard elements', function () { loadTestFile(require.resolve('./input_control_vis')); loadTestFile(require.resolve('./controls')); loadTestFile(require.resolve('./_markdown_vis')); diff --git a/test/functional/apps/discover/_field_data.ts b/test/functional/apps/discover/_field_data.ts index 28f147eeab55f..d13baf9948171 100644 --- a/test/functional/apps/discover/_field_data.ts +++ b/test/functional/apps/discover/_field_data.ts @@ -89,10 +89,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); await retry.try(async function tryingForTime() { expect(await PageObjects.discover.getDocHeader()).to.contain('relatedContent'); - }); - const field = await PageObjects.discover.getDocTableIndex(1); - expect(field).to.contain('og:description'); + const field = await PageObjects.discover.getDocTableIndex(1); + expect(field).to.contain('og:description'); + }); const marks = await PageObjects.discover.getMarks(); expect(marks.length).to.be(0); diff --git a/test/functional/apps/discover/_indexpattern_without_timefield.ts b/test/functional/apps/discover/_indexpattern_without_timefield.ts index 2d5892fa6e6ca..6c936f63e999d 100644 --- a/test/functional/apps/discover/_indexpattern_without_timefield.ts +++ b/test/functional/apps/discover/_indexpattern_without_timefield.ts @@ -91,7 +91,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await browser.goBack(); await PageObjects.discover.waitForDocTableLoadingComplete(); return ( - (await testSubjects.getVisibleText('indexPattern-switch-link')) === 'without-timefield' + (await testSubjects.getVisibleText('discover-dataView-switch-link')) === + 'without-timefield' ); } ); diff --git a/test/functional/apps/discover/_saved_queries.ts b/test/functional/apps/discover/_saved_queries.ts index 79d49131df138..d56b5032a430b 100644 --- a/test/functional/apps/discover/_saved_queries.ts +++ b/test/functional/apps/discover/_saved_queries.ts @@ -144,12 +144,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('saved query management component functionality', function () { before(async () => await setUpQueriesWithFilters()); - it('should show the saved query management component when there are no saved queries', async () => { + it('should show the saved query management load button as disabled when there are no saved queries', async () => { await savedQueryManagementComponent.openSavedQueryManagementComponent(); - const descriptionText = await testSubjects.getVisibleText('saved-query-management-popover'); - expect(descriptionText).to.eql( - 'Saved Queries\nThere are no saved queries. Save query text and filters that you want to use again.\nSave current query' - ); + const loadFilterSetBtn = await testSubjects.find('saved-query-management-load-button'); + const isDisabled = await loadFilterSetBtn.getAttribute('disabled'); + expect(isDisabled).to.equal('true'); }); it('should allow a query to be saved via the saved objects management component', async () => { @@ -189,9 +188,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('allows saving changes to a currently loaded query via the saved query management component', async () => { + await savedQueryManagementComponent.closeSavedQueryManagementComponent(); await queryBar.setQuery('response:404'); await savedQueryManagementComponent.updateCurrentlyLoadedQuery('OkResponse', false, false); await savedQueryManagementComponent.savedQueryExistOrFail('OkResponse'); + const contextMenuPanelTitleButton = await testSubjects.exists( + 'contextMenuPanelTitleButton' + ); + if (contextMenuPanelTitleButton) { + await testSubjects.click('contextMenuPanelTitleButton'); + } await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); expect(await queryBar.getQueryString()).to.eql(''); await savedQueryManagementComponent.loadSavedQuery('OkResponse'); @@ -199,9 +205,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('allows saving the currently loaded query as a new query', async () => { + await queryBar.setQuery('response:400'); await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( 'OkResponseCopy', - '200 responses', + '400 responses', false, false ); @@ -215,6 +222,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('does not allow saving a query with a non-unique name', async () => { + // this check allows this test to run stand alone, also should fix occacional flakiness + const savedQueryExists = await savedQueryManagementComponent.savedQueryExist('OkResponse'); + if (!savedQueryExists) { + await savedQueryManagementComponent.saveNewQuery( + 'OkResponse', + '200 responses for .jpg over 24 hours', + true, + true + ); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); + } + await queryBar.setQuery('response:400'); await savedQueryManagementComponent.saveNewQueryWithNameError('OkResponse'); }); @@ -232,17 +251,22 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('allows clearing if non default language was remembered in localstorage', async () => { + await savedQueryManagementComponent.openSavedQueryManagementComponent(); await queryBar.switchQueryLanguage('lucene'); await PageObjects.common.navigateToApp('discover'); // makes sure discovered is reloaded without any state in url + await savedQueryManagementComponent.openSavedQueryManagementComponent(); await queryBar.expectQueryLanguageOrFail('lucene'); // make sure lucene is remembered after refresh (comes from localstorage) await savedQueryManagementComponent.loadSavedQuery('OkResponse'); + await savedQueryManagementComponent.openSavedQueryManagementComponent(); await queryBar.expectQueryLanguageOrFail('kql'); await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); + await savedQueryManagementComponent.openSavedQueryManagementComponent(); await queryBar.expectQueryLanguageOrFail('lucene'); }); it('changing language removes saved query', async () => { await savedQueryManagementComponent.loadSavedQuery('OkResponse'); + await savedQueryManagementComponent.openSavedQueryManagementComponent(); await queryBar.switchQueryLanguage('lucene'); expect(await queryBar.getQueryString()).to.eql(''); }); diff --git a/test/functional/apps/discover/config.ts b/test/functional/apps/discover/config.ts new file mode 100644 index 0000000000000..e487d31dcb657 --- /dev/null +++ b/test/functional/apps/discover/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/discover/index.ts b/test/functional/apps/discover/index.ts index e2895f3ca56b4..20f8f017b084f 100644 --- a/test/functional/apps/discover/index.ts +++ b/test/functional/apps/discover/index.ts @@ -13,8 +13,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { const config = getService('config'); describe('discover app', function () { - this.tags('ciGroup6'); - before(async function () { await browser.setWindowSize(1300, 800); }); diff --git a/test/functional/apps/getting_started/config.ts b/test/functional/apps/getting_started/config.ts new file mode 100644 index 0000000000000..e487d31dcb657 --- /dev/null +++ b/test/functional/apps/getting_started/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/getting_started/index.ts b/test/functional/apps/getting_started/index.ts index c88999e23be3d..a0506ce52e028 100644 --- a/test/functional/apps/getting_started/index.ts +++ b/test/functional/apps/getting_started/index.ts @@ -13,8 +13,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); describe('Getting Started ', function () { - this.tags(['ciGroup5']); - before(async function () { await browser.setWindowSize(1200, 800); }); diff --git a/test/functional/apps/home/_navigation.ts b/test/functional/apps/home/_navigation.ts index 016cead53f0c4..1d9d02d5e94b5 100644 --- a/test/functional/apps/home/_navigation.ts +++ b/test/functional/apps/home/_navigation.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); - const PageObjects = getPageObjects(['common', 'header', 'home', 'timePicker']); + const PageObjects = getPageObjects(['common', 'header', 'home', 'timePicker', 'unifiedSearch']); const appsMenu = getService('appsMenu'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); @@ -37,6 +37,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // Navigate to discover app await appsMenu.clickLink('Discover'); + await PageObjects.unifiedSearch.closeTourPopoverByLocalStorage(); const discoverUrl = await browser.getCurrentUrl(); await PageObjects.timePicker.setDefaultAbsoluteRange(); const modifiedTimeDiscoverUrl = await browser.getCurrentUrl(); diff --git a/test/functional/apps/home/config.ts b/test/functional/apps/home/config.ts new file mode 100644 index 0000000000000..e487d31dcb657 --- /dev/null +++ b/test/functional/apps/home/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/home/index.js b/test/functional/apps/home/index.js index 992c5b1f2f474..ada4aa54073cf 100644 --- a/test/functional/apps/home/index.js +++ b/test/functional/apps/home/index.js @@ -9,9 +9,7 @@ export default function ({ getService, loadTestFile }) { const browser = getService('browser'); - describe('homepage app', function () { - this.tags('ciGroup5'); - + describe('home app', function () { before(function () { return browser.setWindowSize(1200, 800); }); diff --git a/test/functional/apps/management/config.ts b/test/functional/apps/management/config.ts new file mode 100644 index 0000000000000..e487d31dcb657 --- /dev/null +++ b/test/functional/apps/management/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/management/index.ts b/test/functional/apps/management/index.ts index a4271ae73d9e2..840d04d0d1aed 100644 --- a/test/functional/apps/management/index.ts +++ b/test/functional/apps/management/index.ts @@ -21,33 +21,24 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await esArchiver.unload('test/functional/fixtures/es_archiver/makelogs'); }); - describe('', function () { - this.tags('ciGroup9'); - - loadTestFile(require.resolve('./_create_index_pattern_wizard')); - loadTestFile(require.resolve('./_index_pattern_create_delete')); - loadTestFile(require.resolve('./_index_pattern_results_sort')); - loadTestFile(require.resolve('./_index_pattern_popularity')); - loadTestFile(require.resolve('./_kibana_settings')); - loadTestFile(require.resolve('./_scripted_fields_preview')); - loadTestFile(require.resolve('./_mgmt_import_saved_objects')); - loadTestFile(require.resolve('./_index_patterns_empty')); - loadTestFile(require.resolve('./_scripted_fields')); - loadTestFile(require.resolve('./_runtime_fields')); - loadTestFile(require.resolve('./_field_formatter')); - loadTestFile(require.resolve('./_legacy_url_redirect')); - loadTestFile(require.resolve('./_exclude_index_pattern')); - }); - - describe('', function () { - this.tags('ciGroup8'); - - loadTestFile(require.resolve('./_index_pattern_filter')); - loadTestFile(require.resolve('./_scripted_fields_filter')); - loadTestFile(require.resolve('./_import_objects')); - loadTestFile(require.resolve('./_test_huge_fields')); - loadTestFile(require.resolve('./_handle_alias')); - loadTestFile(require.resolve('./_handle_version_conflict')); - }); + loadTestFile(require.resolve('./_create_index_pattern_wizard')); + loadTestFile(require.resolve('./_index_pattern_create_delete')); + loadTestFile(require.resolve('./_index_pattern_results_sort')); + loadTestFile(require.resolve('./_index_pattern_popularity')); + loadTestFile(require.resolve('./_kibana_settings')); + loadTestFile(require.resolve('./_scripted_fields_preview')); + loadTestFile(require.resolve('./_mgmt_import_saved_objects')); + loadTestFile(require.resolve('./_index_patterns_empty')); + loadTestFile(require.resolve('./_scripted_fields')); + loadTestFile(require.resolve('./_runtime_fields')); + loadTestFile(require.resolve('./_field_formatter')); + loadTestFile(require.resolve('./_legacy_url_redirect')); + loadTestFile(require.resolve('./_exclude_index_pattern')); + loadTestFile(require.resolve('./_index_pattern_filter')); + loadTestFile(require.resolve('./_scripted_fields_filter')); + loadTestFile(require.resolve('./_import_objects')); + loadTestFile(require.resolve('./_test_huge_fields')); + loadTestFile(require.resolve('./_handle_alias')); + loadTestFile(require.resolve('./_handle_version_conflict')); }); } diff --git a/test/functional/apps/saved_objects_management/config.ts b/test/functional/apps/saved_objects_management/config.ts new file mode 100644 index 0000000000000..e487d31dcb657 --- /dev/null +++ b/test/functional/apps/saved_objects_management/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/saved_objects_management/index.ts b/test/functional/apps/saved_objects_management/index.ts index 12e0cc8863f12..c70b2c6d25b17 100644 --- a/test/functional/apps/saved_objects_management/index.ts +++ b/test/functional/apps/saved_objects_management/index.ts @@ -10,7 +10,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function savedObjectsManagementApp({ loadTestFile }: FtrProviderContext) { describe('saved objects management', function savedObjectsManagementAppTestSuite() { - this.tags('ciGroup7'); loadTestFile(require.resolve('./inspect_saved_objects')); loadTestFile(require.resolve('./show_relationships')); }); diff --git a/test/functional/apps/status_page/config.ts b/test/functional/apps/status_page/config.ts new file mode 100644 index 0000000000000..e487d31dcb657 --- /dev/null +++ b/test/functional/apps/status_page/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/status_page/index.ts b/test/functional/apps/status_page/index.ts index 509abeb4f0346..971f9c4984c99 100644 --- a/test/functional/apps/status_page/index.ts +++ b/test/functional/apps/status_page/index.ts @@ -14,8 +14,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common']); describe('status page', function () { - this.tags('ciGroup1'); - beforeEach(async () => { await PageObjects.common.navigateToApp('status_page'); }); diff --git a/test/functional/apps/visualize/README.md b/test/functional/apps/visualize/README.md new file mode 100644 index 0000000000000..5e87a8b210bdd --- /dev/null +++ b/test/functional/apps/visualize/README.md @@ -0,0 +1,7 @@ +# What are all these groups? + +These tests take a while so they have been broken up into groups with their own `config.ts` and `index.ts` file, causing each of these groups to be independent bundles of tests which can be run on some worker in CI without taking an incredible amount of time. + +Want to change the groups to something more logical? Have fun! Just make sure that each group executes on CI in less than 10 minutes or so. We don't currently have any mechanism for validating this right now, you just need to look at the times in the log output on CI, but we'll be working on tooling for making this information more accessible soon. + +- Kibana Operations \ No newline at end of file diff --git a/test/functional/apps/visualize/_chart_types.ts b/test/functional/apps/visualize/group1/_chart_types.ts similarity index 96% rename from test/functional/apps/visualize/_chart_types.ts rename to test/functional/apps/visualize/group1/_chart_types.ts index 1afc372f75b0e..4b5922e21a51c 100644 --- a/test/functional/apps/visualize/_chart_types.ts +++ b/test/functional/apps/visualize/group1/_chart_types.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/apps/visualize/_data_table.ts b/test/functional/apps/visualize/group1/_data_table.ts similarity index 99% rename from test/functional/apps/visualize/_data_table.ts rename to test/functional/apps/visualize/group1/_data_table.ts index e165e40e83dc1..9b95c5b69fd41 100644 --- a/test/functional/apps/visualize/_data_table.ts +++ b/test/functional/apps/visualize/group1/_data_table.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/apps/visualize/_data_table_nontimeindex.ts b/test/functional/apps/visualize/group1/_data_table_nontimeindex.ts similarity index 98% rename from test/functional/apps/visualize/_data_table_nontimeindex.ts rename to test/functional/apps/visualize/group1/_data_table_nontimeindex.ts index 1549f2aac0735..43407d3a899ea 100644 --- a/test/functional/apps/visualize/_data_table_nontimeindex.ts +++ b/test/functional/apps/visualize/group1/_data_table_nontimeindex.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/apps/visualize/_data_table_notimeindex_filters.ts b/test/functional/apps/visualize/group1/_data_table_notimeindex_filters.ts similarity index 97% rename from test/functional/apps/visualize/_data_table_notimeindex_filters.ts rename to test/functional/apps/visualize/group1/_data_table_notimeindex_filters.ts index 51ceef947bfac..d62bdd86ecf9b 100644 --- a/test/functional/apps/visualize/_data_table_notimeindex_filters.ts +++ b/test/functional/apps/visualize/group1/_data_table_notimeindex_filters.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/apps/visualize/_embedding_chart.ts b/test/functional/apps/visualize/group1/_embedding_chart.ts similarity index 98% rename from test/functional/apps/visualize/_embedding_chart.ts rename to test/functional/apps/visualize/group1/_embedding_chart.ts index 9531eafc33bed..a07e3a36e2aea 100644 --- a/test/functional/apps/visualize/_embedding_chart.ts +++ b/test/functional/apps/visualize/group1/_embedding_chart.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const filterBar = getService('filterBar'); diff --git a/test/functional/apps/visualize/group1/config.ts b/test/functional/apps/visualize/group1/config.ts new file mode 100644 index 0000000000000..a70a190ca63f8 --- /dev/null +++ b/test/functional/apps/visualize/group1/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/visualize/group1/index.ts b/test/functional/apps/visualize/group1/index.ts new file mode 100644 index 0000000000000..fa3379b632cc1 --- /dev/null +++ b/test/functional/apps/visualize/group1/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const browser = getService('browser'); + const log = getService('log'); + const esArchiver = getService('esArchiver'); + + describe('visualize app - group1', () => { + before(async () => { + log.debug('Starting visualize before method'); + await browser.setWindowSize(1280, 800); + await esArchiver.load('test/functional/fixtures/es_archiver/empty_kibana'); + + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/long_window_logstash'); + }); + + loadTestFile(require.resolve('./_embedding_chart')); + loadTestFile(require.resolve('./_data_table')); + loadTestFile(require.resolve('./_data_table_nontimeindex')); + loadTestFile(require.resolve('./_data_table_notimeindex_filters')); + loadTestFile(require.resolve('./_chart_types')); + }); +} diff --git a/test/functional/apps/visualize/_experimental_vis.ts b/test/functional/apps/visualize/group2/_experimental_vis.ts similarity index 97% rename from test/functional/apps/visualize/_experimental_vis.ts rename to test/functional/apps/visualize/group2/_experimental_vis.ts index 8e33285f909be..26460192a6b96 100644 --- a/test/functional/apps/visualize/_experimental_vis.ts +++ b/test/functional/apps/visualize/group2/_experimental_vis.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/apps/visualize/_gauge_chart.ts b/test/functional/apps/visualize/group2/_gauge_chart.ts similarity index 97% rename from test/functional/apps/visualize/_gauge_chart.ts rename to test/functional/apps/visualize/group2/_gauge_chart.ts index 6dd460d4ac32b..08425fcd78b5f 100644 --- a/test/functional/apps/visualize/_gauge_chart.ts +++ b/test/functional/apps/visualize/group2/_gauge_chart.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); @@ -102,6 +102,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should show correct values for fields with fieldFormatters', async () => { + await filterBar.removeAllFilters(); const expectedTexts = ['2,904', 'win 8: Count', '0B', 'win 8: Min bytes']; await PageObjects.visEditor.selectAggregation('Terms'); @@ -117,8 +118,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(expectedTexts).to.eql(metricValue); }); }); - - afterEach(async () => await filterBar.removeAllFilters()); }); }); } diff --git a/test/functional/apps/visualize/_heatmap_chart.ts b/test/functional/apps/visualize/group2/_heatmap_chart.ts similarity index 98% rename from test/functional/apps/visualize/_heatmap_chart.ts rename to test/functional/apps/visualize/group2/_heatmap_chart.ts index 54cec19be97ff..1c82b66273251 100644 --- a/test/functional/apps/visualize/_heatmap_chart.ts +++ b/test/functional/apps/visualize/group2/_heatmap_chart.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/apps/visualize/_histogram_request_start.ts b/test/functional/apps/visualize/group2/_histogram_request_start.ts similarity index 98% rename from test/functional/apps/visualize/_histogram_request_start.ts rename to test/functional/apps/visualize/group2/_histogram_request_start.ts index 28ebb25744d3f..a12474d9ebc2e 100644 --- a/test/functional/apps/visualize/_histogram_request_start.ts +++ b/test/functional/apps/visualize/group2/_histogram_request_start.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/apps/visualize/_inspector.ts b/test/functional/apps/visualize/group2/_inspector.ts similarity index 98% rename from test/functional/apps/visualize/_inspector.ts rename to test/functional/apps/visualize/group2/_inspector.ts index f83eae2fc00bc..7b306f7817f5c 100644 --- a/test/functional/apps/visualize/_inspector.ts +++ b/test/functional/apps/visualize/group2/_inspector.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/apps/visualize/_metric_chart.ts b/test/functional/apps/visualize/group2/_metric_chart.ts similarity index 97% rename from test/functional/apps/visualize/_metric_chart.ts rename to test/functional/apps/visualize/group2/_metric_chart.ts index 7853a3a845bfc..d28835ea556e3 100644 --- a/test/functional/apps/visualize/_metric_chart.ts +++ b/test/functional/apps/visualize/group2/_metric_chart.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); @@ -171,14 +171,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); describe('with filters', function () { - it('should prevent filtering without buckets', async function () { + it('should allow filtering without buckets', async function () { let filterCount = 0; await retry.try(async function tryingForTime() { // click first metric bucket await PageObjects.visEditor.clickMetricByIndex(0); filterCount = await filterBar.getFilterCount(); }); - expect(filterCount).to.equal(0); + await filterBar.removeAllFilters(); + expect(filterCount).to.equal(1); }); it('should allow filtering with buckets', async function () { diff --git a/test/functional/apps/visualize/group2/config.ts b/test/functional/apps/visualize/group2/config.ts new file mode 100644 index 0000000000000..a70a190ca63f8 --- /dev/null +++ b/test/functional/apps/visualize/group2/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/visualize/group2/index.ts b/test/functional/apps/visualize/group2/index.ts new file mode 100644 index 0000000000000..ea5ad24e2f873 --- /dev/null +++ b/test/functional/apps/visualize/group2/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const browser = getService('browser'); + const log = getService('log'); + const esArchiver = getService('esArchiver'); + + describe('visualize app', () => { + before(async () => { + log.debug('Starting visualize before method'); + await browser.setWindowSize(1280, 800); + await esArchiver.load('test/functional/fixtures/es_archiver/empty_kibana'); + + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/long_window_logstash'); + }); + + loadTestFile(require.resolve('./_inspector')); + loadTestFile(require.resolve('./_experimental_vis')); + loadTestFile(require.resolve('./_gauge_chart')); + loadTestFile(require.resolve('./_heatmap_chart')); + loadTestFile(require.resolve('./_histogram_request_start')); + loadTestFile(require.resolve('./_metric_chart')); + }); +} diff --git a/test/functional/apps/visualize/_add_to_dashboard.ts b/test/functional/apps/visualize/group3/_add_to_dashboard.ts similarity index 99% rename from test/functional/apps/visualize/_add_to_dashboard.ts rename to test/functional/apps/visualize/group3/_add_to_dashboard.ts index 9e8984675eccd..32d329cd181da 100644 --- a/test/functional/apps/visualize/_add_to_dashboard.ts +++ b/test/functional/apps/visualize/group3/_add_to_dashboard.ts @@ -7,7 +7,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const dashboardExpect = getService('dashboardExpect'); diff --git a/test/functional/apps/visualize/_lab_mode.ts b/test/functional/apps/visualize/group3/_lab_mode.ts similarity index 97% rename from test/functional/apps/visualize/_lab_mode.ts rename to test/functional/apps/visualize/group3/_lab_mode.ts index 2af593f2acc4a..ba1b8ae33e2ce 100644 --- a/test/functional/apps/visualize/_lab_mode.ts +++ b/test/functional/apps/visualize/group3/_lab_mode.ts @@ -9,7 +9,7 @@ import expect from '@kbn/expect'; import { VISUALIZE_ENABLE_LABS_SETTING } from '@kbn/visualizations-plugin/common/constants'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/apps/visualize/_linked_saved_searches.ts b/test/functional/apps/visualize/group3/_linked_saved_searches.ts similarity index 98% rename from test/functional/apps/visualize/_linked_saved_searches.ts rename to test/functional/apps/visualize/group3/_linked_saved_searches.ts index 6fa8acac8e781..e64a3f18bde95 100644 --- a/test/functional/apps/visualize/_linked_saved_searches.ts +++ b/test/functional/apps/visualize/group3/_linked_saved_searches.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); diff --git a/test/functional/apps/visualize/_pie_chart.ts b/test/functional/apps/visualize/group3/_pie_chart.ts similarity index 95% rename from test/functional/apps/visualize/_pie_chart.ts rename to test/functional/apps/visualize/group3/_pie_chart.ts index 48d49d3007b68..23b008c690cba 100644 --- a/test/functional/apps/visualize/_pie_chart.ts +++ b/test/functional/apps/visualize/group3/_pie_chart.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); @@ -401,7 +401,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ['360,000', '47', 'US', '4'], ['360,000', '47', 'BD', '3'], ['360,000', '47', 'BR', '2'], - ]; + ].map((row) => + // the count of records is not shown for every split level in the new charting library + isNewChartsLibraryEnabled ? [row[0], ...row.slice(2)] : row + ); await inspector.open(); await inspector.setTablePageSize(50); @@ -447,6 +450,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should still showing pie chart when a subseries have zero data', async function () { + if (isNewChartsLibraryEnabled) { + // TODO: it seems that adding a filter agg which has no results to a pie chart breaks it and instead it shows "no data" + return; + } + await PageObjects.visualize.navigateToNewAggBasedVisualization(); log.debug('clickPieChart'); await PageObjects.visualize.clickPieChart(); @@ -518,7 +526,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ['osx', '1,322', 'US', '130'], ['osx', '1,322', 'ID', '56'], ['osx', '1,322', 'BR', '30'], - ]; + ].map((row) => + // the count of records is not shown for every split level in the new charting library + isNewChartsLibraryEnabled ? [row[0], ...row.slice(2)] : row + ); await inspector.open(); await inspector.setTablePageSize(50); await inspector.expectTableData(expectedTableData); @@ -532,7 +543,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ['win xp', '526', 'CN', '526'], ['ios', '478', 'CN', '478'], ['osx', '228', 'CN', '228'], - ]; + ].map((row) => + // the count of records is not shown for every split level in the new charting library + isNewChartsLibraryEnabled ? [row[0], ...row.slice(2)] : row + ); await PageObjects.visChart.filterLegend('CN'); await PageObjects.header.waitUntilLoadingHasFinished(); await inspector.open(); diff --git a/test/functional/apps/visualize/_shared_item.ts b/test/functional/apps/visualize/group3/_shared_item.ts similarity index 95% rename from test/functional/apps/visualize/_shared_item.ts rename to test/functional/apps/visualize/group3/_shared_item.ts index 3f9016ca2ff82..0a84ae1962c63 100644 --- a/test/functional/apps/visualize/_shared_item.ts +++ b/test/functional/apps/visualize/group3/_shared_item.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/apps/visualize/_visualize_listing.ts b/test/functional/apps/visualize/group3/_visualize_listing.ts similarity index 98% rename from test/functional/apps/visualize/_visualize_listing.ts rename to test/functional/apps/visualize/group3/_visualize_listing.ts index 30b19b52af258..ad370939f2260 100644 --- a/test/functional/apps/visualize/_visualize_listing.ts +++ b/test/functional/apps/visualize/group3/_visualize_listing.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'visEditor']); diff --git a/test/functional/apps/visualize/group3/config.ts b/test/functional/apps/visualize/group3/config.ts new file mode 100644 index 0000000000000..a70a190ca63f8 --- /dev/null +++ b/test/functional/apps/visualize/group3/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/visualize/group3/index.ts b/test/functional/apps/visualize/group3/index.ts new file mode 100644 index 0000000000000..93eff60575cb3 --- /dev/null +++ b/test/functional/apps/visualize/group3/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const browser = getService('browser'); + const log = getService('log'); + const esArchiver = getService('esArchiver'); + + describe('visualize app', () => { + before(async () => { + log.debug('Starting visualize before method'); + await browser.setWindowSize(1280, 800); + await esArchiver.load('test/functional/fixtures/es_archiver/empty_kibana'); + + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/long_window_logstash'); + }); + + loadTestFile(require.resolve('./_pie_chart')); + loadTestFile(require.resolve('./_shared_item')); + loadTestFile(require.resolve('./_lab_mode')); + loadTestFile(require.resolve('./_linked_saved_searches')); + loadTestFile(require.resolve('./_visualize_listing')); + loadTestFile(require.resolve('./_add_to_dashboard.ts')); + }); +} diff --git a/test/functional/apps/visualize/_tsvb_chart.ts b/test/functional/apps/visualize/group4/_tsvb_chart.ts similarity index 97% rename from test/functional/apps/visualize/_tsvb_chart.ts rename to test/functional/apps/visualize/group4/_tsvb_chart.ts index ec3852b309d3f..013c0473a59b9 100644 --- a/test/functional/apps/visualize/_tsvb_chart.ts +++ b/test/functional/apps/visualize/group4/_tsvb_chart.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); @@ -111,6 +111,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await visualBuilder.selectAggType('derivative', 1); await visualBuilder.setFieldForAggregation('Max of machine.ram', 1); + await visChart.waitForVisualizationRenderingStabilized(); const value = await visualBuilder.getMetricValue(); expect(value).to.eql('0'); @@ -197,6 +198,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await visualBuilder.selectAggType('derivative', 1); await visualBuilder.setFieldForAggregation('Max of machine.ram', 1); + await visChart.waitForVisualizationRenderingStabilized(); const value = await visualBuilder.getGaugeCount(); expect(value).to.eql('0'); @@ -226,6 +228,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await visualBuilder.clickSeriesOption(); await visualBuilder.changeDataFormatter('number'); + await visChart.waitForVisualizationRenderingStabilized(); const gaugeLabel = await visualBuilder.getGaugeLabel(); const gaugeCount = await visualBuilder.getGaugeCount(); @@ -239,6 +242,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await visualBuilder.clickPanelOptions('gauge'); await visualBuilder.setMetricsDataTimerangeMode('Last value'); + await visChart.waitForVisualizationRenderingStabilized(); const gaugeLabel = await visualBuilder.getGaugeLabel(); const gaugeCount = await visualBuilder.getGaugeCount(); @@ -351,6 +355,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await visualBuilder.selectAggType('derivative', 1); await visualBuilder.setFieldForAggregation('Max of machine.ram', 1); + await visChart.waitForVisualizationRenderingStabilized(); const value = await visualBuilder.getTopNCount(); expect(value).to.eql('0'); @@ -579,6 +584,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ]; await visualBuilder.clickSeriesOption(); await visualBuilder.changeDataFormatter('number'); + await visChart.waitForVisualizationRenderingStabilized(); const legendItems = await visualBuilder.getLegendItemsContent(); expect(legendItems).to.eql(expectedLegendItems); @@ -615,6 +621,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await visualBuilder.clickTopN(); await visualBuilder.checkTopNTabIsPresent(); + await visChart.waitForVisualizationRenderingStabilized(); const topNLabel = await visualBuilder.getTopNLabel(); const topNCount = await visualBuilder.getTopNCount(); @@ -626,6 +633,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await visualBuilder.clickGauge(); await visualBuilder.checkGaugeTabIsPresent(); + await visChart.waitForVisualizationRenderingStabilized(); const gaugeLabel = await visualBuilder.getGaugeLabel(); const gaugeCount = await visualBuilder.getGaugeCount(); diff --git a/test/functional/apps/visualize/group4/config.ts b/test/functional/apps/visualize/group4/config.ts new file mode 100644 index 0000000000000..a70a190ca63f8 --- /dev/null +++ b/test/functional/apps/visualize/group4/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/visualize/group4/index.ts b/test/functional/apps/visualize/group4/index.ts new file mode 100644 index 0000000000000..3476489706415 --- /dev/null +++ b/test/functional/apps/visualize/group4/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const browser = getService('browser'); + const log = getService('log'); + const esArchiver = getService('esArchiver'); + + describe('visualize app', () => { + before(async () => { + log.debug('Starting visualize before method'); + await browser.setWindowSize(1280, 800); + await esArchiver.load('test/functional/fixtures/es_archiver/empty_kibana'); + + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/long_window_logstash'); + }); + + loadTestFile(require.resolve('./_tsvb_chart')); + }); +} diff --git a/test/functional/apps/visualize/_tsvb_time_series.ts b/test/functional/apps/visualize/group5/_tsvb_time_series.ts similarity index 99% rename from test/functional/apps/visualize/_tsvb_time_series.ts rename to test/functional/apps/visualize/group5/_tsvb_time_series.ts index 3f6661aaecf00..409b2b3610f5c 100644 --- a/test/functional/apps/visualize/_tsvb_time_series.ts +++ b/test/functional/apps/visualize/group5/_tsvb_time_series.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const { visualize, visualBuilder, timeToVisualize, dashboard, common } = getPageObjects([ diff --git a/test/functional/apps/visualize/group5/config.ts b/test/functional/apps/visualize/group5/config.ts new file mode 100644 index 0000000000000..a70a190ca63f8 --- /dev/null +++ b/test/functional/apps/visualize/group5/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/visualize/group5/index.ts b/test/functional/apps/visualize/group5/index.ts new file mode 100644 index 0000000000000..eafa39962ff56 --- /dev/null +++ b/test/functional/apps/visualize/group5/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const browser = getService('browser'); + const log = getService('log'); + const esArchiver = getService('esArchiver'); + + describe('visualize app', () => { + before(async () => { + log.debug('Starting visualize before method'); + await browser.setWindowSize(1280, 800); + await esArchiver.load('test/functional/fixtures/es_archiver/empty_kibana'); + + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/long_window_logstash'); + }); + + loadTestFile(require.resolve('./_tsvb_time_series')); + }); +} diff --git a/test/functional/apps/visualize/_tag_cloud.ts b/test/functional/apps/visualize/group6/_tag_cloud.ts similarity index 99% rename from test/functional/apps/visualize/_tag_cloud.ts rename to test/functional/apps/visualize/group6/_tag_cloud.ts index 9380f40e0d36c..93a7fb22a2ca8 100644 --- a/test/functional/apps/visualize/_tag_cloud.ts +++ b/test/functional/apps/visualize/group6/_tag_cloud.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); diff --git a/test/functional/apps/visualize/_tsvb_markdown.ts b/test/functional/apps/visualize/group6/_tsvb_markdown.ts similarity index 99% rename from test/functional/apps/visualize/_tsvb_markdown.ts rename to test/functional/apps/visualize/group6/_tsvb_markdown.ts index 98ed05d854f0c..80756b1eacbfb 100644 --- a/test/functional/apps/visualize/_tsvb_markdown.ts +++ b/test/functional/apps/visualize/group6/_tsvb_markdown.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const { visualBuilder, timePicker, visualize, visChart } = getPageObjects([ diff --git a/test/functional/apps/visualize/_tsvb_table.ts b/test/functional/apps/visualize/group6/_tsvb_table.ts similarity index 99% rename from test/functional/apps/visualize/_tsvb_table.ts rename to test/functional/apps/visualize/group6/_tsvb_table.ts index ed668e4bca8e5..e7e24885cb406 100644 --- a/test/functional/apps/visualize/_tsvb_table.ts +++ b/test/functional/apps/visualize/group6/_tsvb_table.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const { visualBuilder, visualize, visChart, settings } = getPageObjects([ diff --git a/test/functional/apps/visualize/_vega_chart.ts b/test/functional/apps/visualize/group6/_vega_chart.ts similarity index 97% rename from test/functional/apps/visualize/_vega_chart.ts rename to test/functional/apps/visualize/group6/_vega_chart.ts index 6640b37b4a28a..1d802065ad137 100644 --- a/test/functional/apps/visualize/_vega_chart.ts +++ b/test/functional/apps/visualize/group6/_vega_chart.ts @@ -9,7 +9,7 @@ import { unzip } from 'lodash'; import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; const getTestSpec = (expression: string) => ` { @@ -220,7 +220,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { describe('Vega extension functions', () => { beforeEach(async () => { - await filterBar.removeAllFilters(); + const filtersCount = await filterBar.getFilterCount(); + if (filtersCount > 0) { + await filterBar.removeAllFilters(); + } }); const fillSpecAndGo = async (newSpec: string) => { diff --git a/test/functional/apps/visualize/group6/config.ts b/test/functional/apps/visualize/group6/config.ts new file mode 100644 index 0000000000000..a70a190ca63f8 --- /dev/null +++ b/test/functional/apps/visualize/group6/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/visualize/group6/index.ts b/test/functional/apps/visualize/group6/index.ts new file mode 100644 index 0000000000000..05fe3b232d370 --- /dev/null +++ b/test/functional/apps/visualize/group6/index.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const browser = getService('browser'); + const log = getService('log'); + const esArchiver = getService('esArchiver'); + + describe('visualize app', () => { + before(async () => { + log.debug('Starting visualize before method'); + await browser.setWindowSize(1280, 800); + await esArchiver.load('test/functional/fixtures/es_archiver/empty_kibana'); + + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/long_window_logstash'); + }); + + loadTestFile(require.resolve('./_tag_cloud')); + loadTestFile(require.resolve('./_tsvb_markdown')); + loadTestFile(require.resolve('./_tsvb_table')); + loadTestFile(require.resolve('./_vega_chart')); + }); +} diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts deleted file mode 100644 index d68fb4b253123..0000000000000 --- a/test/functional/apps/visualize/index.ts +++ /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 and the Server Side Public License, v 1; you may 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 { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ getService, loadTestFile }: FtrProviderContext) { - const browser = getService('browser'); - const log = getService('log'); - const esArchiver = getService('esArchiver'); - const kibanaServer = getService('kibanaServer'); - - describe('visualize app', () => { - before(async () => { - log.debug('Starting visualize before method'); - await browser.setWindowSize(1280, 800); - await esArchiver.load('test/functional/fixtures/es_archiver/empty_kibana'); - - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/long_window_logstash'); - }); - - // TODO: Remove when vislib is removed - describe('new charts library visualize ciGroup7', function () { - this.tags('ciGroup7'); - - before(async () => { - await kibanaServer.uiSettings.update({ - 'visualization:visualize:legacyPieChartsLibrary': false, - 'visualization:visualize:legacyHeatmapChartsLibrary': false, - }); - await browser.refresh(); - }); - - after(async () => { - await kibanaServer.uiSettings.update({ - 'visualization:visualize:legacyPieChartsLibrary': true, - 'visualization:visualize:legacyHeatmapChartsLibrary': true, - }); - await browser.refresh(); - }); - - // Test replaced vislib chart types - loadTestFile(require.resolve('./_area_chart')); - loadTestFile(require.resolve('./_line_chart_split_series')); - loadTestFile(require.resolve('./_line_chart_split_chart')); - loadTestFile(require.resolve('./_point_series_options')); - loadTestFile(require.resolve('./_vertical_bar_chart')); - loadTestFile(require.resolve('./_vertical_bar_chart_nontimeindex')); - loadTestFile(require.resolve('./_pie_chart')); - loadTestFile(require.resolve('./_timelion')); - loadTestFile(require.resolve('./_heatmap_chart')); - }); - - describe('visualize ciGroup9', function () { - this.tags('ciGroup9'); - - loadTestFile(require.resolve('./_embedding_chart')); - loadTestFile(require.resolve('./_data_table')); - loadTestFile(require.resolve('./_data_table_nontimeindex')); - loadTestFile(require.resolve('./_data_table_notimeindex_filters')); - loadTestFile(require.resolve('./_chart_types')); - }); - - describe('visualize ciGroup10', function () { - this.tags('ciGroup10'); - - loadTestFile(require.resolve('./_inspector')); - loadTestFile(require.resolve('./_experimental_vis')); - loadTestFile(require.resolve('./_gauge_chart')); - loadTestFile(require.resolve('./_heatmap_chart')); - loadTestFile(require.resolve('./_histogram_request_start')); - loadTestFile(require.resolve('./_metric_chart')); - }); - - describe('visualize ciGroup1', function () { - this.tags('ciGroup1'); - - loadTestFile(require.resolve('./_pie_chart')); - loadTestFile(require.resolve('./_shared_item')); - loadTestFile(require.resolve('./_lab_mode')); - loadTestFile(require.resolve('./_linked_saved_searches')); - loadTestFile(require.resolve('./_visualize_listing')); - loadTestFile(require.resolve('./_add_to_dashboard.ts')); - }); - - describe('visualize ciGroup8', function () { - this.tags('ciGroup8'); - - loadTestFile(require.resolve('./_tsvb_chart')); - }); - - describe('visualize ciGroup11', function () { - this.tags('ciGroup11'); - - loadTestFile(require.resolve('./_tsvb_time_series')); - }); - - describe('visualize ciGroup12', function () { - this.tags('ciGroup12'); - - loadTestFile(require.resolve('./_tag_cloud')); - loadTestFile(require.resolve('./_tsvb_markdown')); - loadTestFile(require.resolve('./_tsvb_table')); - loadTestFile(require.resolve('./_vega_chart')); - }); - }); -} diff --git a/test/functional/apps/visualize/_area_chart.ts b/test/functional/apps/visualize/replaced_vislib_chart_types/_area_chart.ts similarity index 99% rename from test/functional/apps/visualize/_area_chart.ts rename to test/functional/apps/visualize/replaced_vislib_chart_types/_area_chart.ts index 76bb1d2f58d05..5fbb264910dca 100644 --- a/test/functional/apps/visualize/_area_chart.ts +++ b/test/functional/apps/visualize/replaced_vislib_chart_types/_area_chart.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/apps/visualize/_line_chart_split_chart.ts b/test/functional/apps/visualize/replaced_vislib_chart_types/_line_chart_split_chart.ts similarity index 99% rename from test/functional/apps/visualize/_line_chart_split_chart.ts rename to test/functional/apps/visualize/replaced_vislib_chart_types/_line_chart_split_chart.ts index 0e44c30499ed3..77ddc3bbac1a4 100644 --- a/test/functional/apps/visualize/_line_chart_split_chart.ts +++ b/test/functional/apps/visualize/replaced_vislib_chart_types/_line_chart_split_chart.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/apps/visualize/_line_chart_split_series.ts b/test/functional/apps/visualize/replaced_vislib_chart_types/_line_chart_split_series.ts similarity index 99% rename from test/functional/apps/visualize/_line_chart_split_series.ts rename to test/functional/apps/visualize/replaced_vislib_chart_types/_line_chart_split_series.ts index d10b4ebd9b312..a46c46fda48ad 100644 --- a/test/functional/apps/visualize/_line_chart_split_series.ts +++ b/test/functional/apps/visualize/replaced_vislib_chart_types/_line_chart_split_series.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/apps/visualize/_point_series_options.ts b/test/functional/apps/visualize/replaced_vislib_chart_types/_point_series_options.ts similarity index 99% rename from test/functional/apps/visualize/_point_series_options.ts rename to test/functional/apps/visualize/replaced_vislib_chart_types/_point_series_options.ts index a2d2831c87933..1a11d19064ce7 100644 --- a/test/functional/apps/visualize/_point_series_options.ts +++ b/test/functional/apps/visualize/replaced_vislib_chart_types/_point_series_options.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/apps/visualize/_timelion.ts b/test/functional/apps/visualize/replaced_vislib_chart_types/_timelion.ts similarity index 99% rename from test/functional/apps/visualize/_timelion.ts rename to test/functional/apps/visualize/replaced_vislib_chart_types/_timelion.ts index e10ba03a0e19f..dc80083a67697 100644 --- a/test/functional/apps/visualize/_timelion.ts +++ b/test/functional/apps/visualize/replaced_vislib_chart_types/_timelion.ts @@ -7,7 +7,7 @@ */ import expect from '@kbn/expect'; -import type { FtrProviderContext } from '../../ftr_provider_context'; +import type { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const { timePicker, visChart, visEditor, visualize, timelion, common } = getPageObjects([ diff --git a/test/functional/apps/visualize/_vertical_bar_chart.ts b/test/functional/apps/visualize/replaced_vislib_chart_types/_vertical_bar_chart.ts similarity index 99% rename from test/functional/apps/visualize/_vertical_bar_chart.ts rename to test/functional/apps/visualize/replaced_vislib_chart_types/_vertical_bar_chart.ts index 7c4f989724ad9..b8d5cd64bbc1f 100644 --- a/test/functional/apps/visualize/_vertical_bar_chart.ts +++ b/test/functional/apps/visualize/replaced_vislib_chart_types/_vertical_bar_chart.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/apps/visualize/_vertical_bar_chart_nontimeindex.ts b/test/functional/apps/visualize/replaced_vislib_chart_types/_vertical_bar_chart_nontimeindex.ts similarity index 99% rename from test/functional/apps/visualize/_vertical_bar_chart_nontimeindex.ts rename to test/functional/apps/visualize/replaced_vislib_chart_types/_vertical_bar_chart_nontimeindex.ts index eadc7c58af5a5..4f00bac7792c4 100644 --- a/test/functional/apps/visualize/_vertical_bar_chart_nontimeindex.ts +++ b/test/functional/apps/visualize/replaced_vislib_chart_types/_vertical_bar_chart_nontimeindex.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); diff --git a/test/functional/apps/visualize/replaced_vislib_chart_types/config.ts b/test/functional/apps/visualize/replaced_vislib_chart_types/config.ts new file mode 100644 index 0000000000000..a70a190ca63f8 --- /dev/null +++ b/test/functional/apps/visualize/replaced_vislib_chart_types/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 and the Server Side Public License, v 1; you may 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/visualize/replaced_vislib_chart_types/index.ts b/test/functional/apps/visualize/replaced_vislib_chart_types/index.ts new file mode 100644 index 0000000000000..5794edef68555 --- /dev/null +++ b/test/functional/apps/visualize/replaced_vislib_chart_types/index.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const browser = getService('browser'); + const log = getService('log'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + + // TODO: Remove when vislib is removed + describe('visualize app - new charts library visualize', () => { + before(async () => { + log.debug('Starting visualize before method'); + await browser.setWindowSize(1280, 800); + await esArchiver.load('test/functional/fixtures/es_archiver/empty_kibana'); + + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/long_window_logstash'); + }); + + before(async () => { + await kibanaServer.uiSettings.update({ + 'visualization:visualize:legacyPieChartsLibrary': false, + 'visualization:visualize:legacyHeatmapChartsLibrary': false, + }); + await browser.refresh(); + }); + + after(async () => { + await kibanaServer.uiSettings.update({ + 'visualization:visualize:legacyPieChartsLibrary': true, + 'visualization:visualize:legacyHeatmapChartsLibrary': true, + }); + await browser.refresh(); + }); + + // Test replaced vislib chart types + loadTestFile(require.resolve('./_area_chart')); + loadTestFile(require.resolve('./_line_chart_split_series')); + loadTestFile(require.resolve('./_line_chart_split_chart')); + loadTestFile(require.resolve('./_point_series_options')); + loadTestFile(require.resolve('./_vertical_bar_chart')); + loadTestFile(require.resolve('./_vertical_bar_chart_nontimeindex')); + loadTestFile(require.resolve('./_timelion')); + loadTestFile(require.resolve('../group3/_pie_chart')); + loadTestFile(require.resolve('../group2/_heatmap_chart')); + }); +} diff --git a/test/functional/config.base.js b/test/functional/config.base.js new file mode 100644 index 0000000000000..40b50da505951 --- /dev/null +++ b/test/functional/config.base.js @@ -0,0 +1,399 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { pageObjects } from './page_objects'; +import { services } from './services'; + +export default async function ({ readConfigFile }) { + const commonConfig = await readConfigFile(require.resolve('../common/config')); + + return { + pageObjects, + services, + + servers: commonConfig.get('servers'), + + esTestCluster: { + ...commonConfig.get('esTestCluster'), + serverArgs: [`xpack.security.enabled=${process.env.ES_SECURITY_ENABLED ? 'true' : 'false'}`], + }, + + kbnTestServer: { + ...commonConfig.get('kbnTestServer'), + serverArgs: [ + ...commonConfig.get('kbnTestServer.serverArgs'), + '--telemetry.optIn=false', + '--savedObjects.maxImportPayloadBytes=10485760', + + // to be re-enabled once kibana/issues/102552 is completed + '--xpack.reporting.enabled=false', + ], + }, + + uiSettings: { + defaults: { + 'accessibility:disableAnimations': true, + 'dateFormat:tz': 'UTC', + 'visualization:visualize:legacyPieChartsLibrary': true, + 'visualization:useLegacyTimeAxis': true, + }, + }, + + apps: { + kibana: { + pathname: '/app/kibana', + }, + status_page: { + pathname: '/status', + }, + discover: { + pathname: '/app/discover', + hash: '/', + }, + context: { + pathname: '/app/discover', + hash: '/context', + }, + visualize: { + pathname: '/app/visualize', + hash: '/', + }, + dashboard: { + pathname: '/app/dashboards', + hash: '/list', + }, + management: { + pathname: '/app/management', + }, + /** @obsolete "management" should be instead of "settings" **/ + settings: { + pathname: '/app/management', + }, + console: { + pathname: '/app/dev_tools', + hash: '/console', + }, + home: { + pathname: '/app/home', + hash: '/', + }, + observabilityCases: { + pathname: '/app/observability/cases', + }, + fleet: { + pathname: '/app/fleet', + }, + integrations: { + pathname: '/app/integrations', + }, + }, + junit: { + reportName: 'Chrome UI Functional Tests', + }, + browser: { + type: 'chrome', + }, + + security: { + roles: { + test_logstash_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['logstash*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + test_shakespeare_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['shakes*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + test_testhuge_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['testhuge*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + test_alias_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['alias*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + test_field_formatters: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['field_formats_management_functional_tests*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + //for sample data - can remove but not add sample data.( not ml)- for ml use built in role. + kibana_sample_admin: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['kibana_sample*'], + privileges: ['read', 'view_index_metadata', 'manage', 'create_index', 'index'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + version_test: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['version-test'], + privileges: ['read', 'view_index_metadata', 'manage', 'create_index', 'index'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + context_encoded_param: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['context-encoded-param'], + privileges: ['read', 'view_index_metadata', 'manage', 'create_index', 'index'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_sample_read: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['kibana_sample*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_date_nanos: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['date-nanos'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_date_nanos_custom: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['date_nanos_custom_timestamp'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_date_nanos_mixed: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['date_nanos_mixed', 'timestamp-*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_date_nested: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['date-nested'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + kibana_message_with_newline: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['newline-test'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_timefield: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['without-timefield', 'with-timefield'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_large_strings: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['testlargestring'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + long_window_logstash: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['long-window-logstash-*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + 'test-index-unmapped-fields': { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['test-index-unmapped-fields'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + animals: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['animals-*', 'dogbreeds'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + test_alias1_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['alias1'], + privileges: ['read', 'view_index_metadata'], + }, + ], + }, + }, + }, + defaultRoles: ['test_logstash_reader', 'kibana_admin'], + }, + }; +} diff --git a/test/functional/config.ccs.ts b/test/functional/config.ccs.ts index 8137635b474da..e5a3736d6fbc3 100644 --- a/test/functional/config.ccs.ts +++ b/test/functional/config.ccs.ts @@ -12,15 +12,15 @@ import { RemoteEsProvider } from './services/remote_es/remote_es'; // eslint-disable-next-line import/no-default-export export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const functionalConfig = await readConfigFile(require.resolve('./config')); + const baseConfig = await readConfigFile(require.resolve('./config.base.js')); return { - ...functionalConfig.getAll(), + ...baseConfig.getAll(), testFiles: [require.resolve('./apps/discover')], services: { - ...functionalConfig.get('services'), + ...baseConfig.get('services'), remoteEs: RemoteEsProvider, remoteEsArchiver: RemoteEsArchiverProvider, }, @@ -30,7 +30,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { }, security: { - ...functionalConfig.get('security'), + ...baseConfig.get('security'), remoteEsRoles: { ccs_remote_search: { indices: [ @@ -41,17 +41,15 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { ], }, }, - defaultRoles: [...(functionalConfig.get('security.defaultRoles') ?? []), 'ccs_remote_search'], + defaultRoles: [...(baseConfig.get('security.defaultRoles') ?? []), 'ccs_remote_search'], }, esTestCluster: { - ...functionalConfig.get('esTestCluster'), + ...baseConfig.get('esTestCluster'), ccs: { remoteClusterUrl: process.env.REMOTE_CLUSTER_URL ?? - `http://elastic:changeme@localhost:${ - functionalConfig.get('servers.elasticsearch.port') + 1 - }`, + `http://elastic:changeme@localhost:${baseConfig.get('servers.elasticsearch.port') + 1}`, }, }, }; diff --git a/test/functional/config.coverage.js b/test/functional/config.coverage.js deleted file mode 100644 index 8b0a59ac88b1b..0000000000000 --- a/test/functional/config.coverage.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 default async function ({ readConfigFile }) { - const defaultConfig = await readConfigFile(require.resolve('./config')); - - return { - ...defaultConfig.getAll(), - - suiteTags: { - exclude: ['skipCoverage'], - }, - - junit: { - reportName: 'Code Coverage for Functional Tests', - }, - }; -} diff --git a/test/functional/config.edge.js b/test/functional/config.edge.js index 89dc4c39bca4c..86e213fd31e9f 100644 --- a/test/functional/config.edge.js +++ b/test/functional/config.edge.js @@ -7,10 +7,10 @@ */ export default async function ({ readConfigFile }) { - const defaultConfig = await readConfigFile(require.resolve('./config')); + const firefoxConfig = await readConfigFile(require.resolve('./config.firefox.js')); return { - ...defaultConfig.getAll(), + ...firefoxConfig.getAll(), browser: { type: 'msedge', diff --git a/test/functional/config.firefox.js b/test/functional/config.firefox.js index 145add328c9ca..79a757b1f1116 100644 --- a/test/functional/config.firefox.js +++ b/test/functional/config.firefox.js @@ -7,16 +7,26 @@ */ export default async function ({ readConfigFile }) { - const defaultConfig = await readConfigFile(require.resolve('./config')); + const baseConfig = await readConfigFile(require.resolve('./config.base.js')); return { - ...defaultConfig.getAll(), + ...baseConfig.getAll(), + + testFiles: [ + require.resolve('./apps/console'), + require.resolve('./apps/dashboard/group4/dashboard_save'), + require.resolve('./apps/dashboard_elements'), + require.resolve('./apps/discover'), + require.resolve('./apps/home'), + require.resolve('./apps/visualize/group5'), + ], browser: { type: 'firefox', }, suiteTags: { + include: ['includeFirefox'], exclude: ['skipFirefox'], }, diff --git a/test/functional/config.js b/test/functional/config.js deleted file mode 100644 index 9221bef808e01..0000000000000 --- a/test/functional/config.js +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { pageObjects } from './page_objects'; -import { services } from './services'; - -export default async function ({ readConfigFile }) { - const commonConfig = await readConfigFile(require.resolve('../common/config')); - - return { - testFiles: [ - require.resolve('./apps/status_page'), - require.resolve('./apps/bundles'), - require.resolve('./apps/console'), - require.resolve('./apps/context'), - require.resolve('./apps/dashboard'), - require.resolve('./apps/dashboard_elements'), - require.resolve('./apps/discover'), - require.resolve('./apps/getting_started'), - require.resolve('./apps/home'), - require.resolve('./apps/management'), - require.resolve('./apps/saved_objects_management'), - require.resolve('./apps/visualize'), - ], - pageObjects, - services, - - servers: commonConfig.get('servers'), - - esTestCluster: { - ...commonConfig.get('esTestCluster'), - serverArgs: [`xpack.security.enabled=${process.env.ES_SECURITY_ENABLED ? 'true' : 'false'}`], - }, - - kbnTestServer: { - ...commonConfig.get('kbnTestServer'), - serverArgs: [ - ...commonConfig.get('kbnTestServer.serverArgs'), - '--telemetry.optIn=false', - '--savedObjects.maxImportPayloadBytes=10485760', - - // to be re-enabled once kibana/issues/102552 is completed - '--xpack.reporting.enabled=false', - ], - }, - - uiSettings: { - defaults: { - 'accessibility:disableAnimations': true, - 'dateFormat:tz': 'UTC', - 'visualization:visualize:legacyPieChartsLibrary': true, - 'visualization:useLegacyTimeAxis': true, - }, - }, - - apps: { - kibana: { - pathname: '/app/kibana', - }, - status_page: { - pathname: '/status', - }, - discover: { - pathname: '/app/discover', - hash: '/', - }, - context: { - pathname: '/app/discover', - hash: '/context', - }, - visualize: { - pathname: '/app/visualize', - hash: '/', - }, - dashboard: { - pathname: '/app/dashboards', - hash: '/list', - }, - management: { - pathname: '/app/management', - }, - /** @obsolete "management" should be instead of "settings" **/ - settings: { - pathname: '/app/management', - }, - console: { - pathname: '/app/dev_tools', - hash: '/console', - }, - home: { - pathname: '/app/home', - hash: '/', - }, - observabilityCases: { - pathname: '/app/observability/cases', - }, - fleet: { - pathname: '/app/fleet', - }, - integrations: { - pathname: '/app/integrations', - }, - }, - junit: { - reportName: 'Chrome UI Functional Tests', - }, - browser: { - type: 'chrome', - }, - - security: { - roles: { - test_logstash_reader: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['logstash*'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - test_shakespeare_reader: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['shakes*'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - test_testhuge_reader: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['testhuge*'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - test_alias_reader: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['alias*'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - test_field_formatters: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['field_formats_management_functional_tests*'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - //for sample data - can remove but not add sample data.( not ml)- for ml use built in role. - kibana_sample_admin: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['kibana_sample*'], - privileges: ['read', 'view_index_metadata', 'manage', 'create_index', 'index'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - version_test: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['version-test'], - privileges: ['read', 'view_index_metadata', 'manage', 'create_index', 'index'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - context_encoded_param: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['context-encoded-param'], - privileges: ['read', 'view_index_metadata', 'manage', 'create_index', 'index'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - kibana_sample_read: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['kibana_sample*'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - kibana_date_nanos: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['date-nanos'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - kibana_date_nanos_custom: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['date_nanos_custom_timestamp'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - kibana_date_nanos_mixed: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['date_nanos_mixed', 'timestamp-*'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - kibana_date_nested: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['date-nested'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - kibana_message_with_newline: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['newline-test'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - kibana_timefield: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['without-timefield', 'with-timefield'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - kibana_large_strings: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['testlargestring'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - long_window_logstash: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['long-window-logstash-*'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - 'test-index-unmapped-fields': { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['test-index-unmapped-fields'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - animals: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['animals-*', 'dogbreeds'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - test_alias1_reader: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['alias1'], - privileges: ['read', 'view_index_metadata'], - }, - ], - }, - }, - }, - defaultRoles: ['test_logstash_reader', 'kibana_admin'], - }, - }; -} diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index 28ac88674b4a6..206cc82912c36 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -281,6 +281,7 @@ export class CommonPageObject extends FtrService { } if (appName === 'discover') { await this.browser.setLocalStorageItem('data.autocompleteFtuePopover', 'true'); + await this.browser.setLocalStorageItem('data.newDataViewMenu', 'true'); } return currentUrl; }); diff --git a/test/functional/page_objects/discover_page.ts b/test/functional/page_objects/discover_page.ts index ce25370493823..5691b4f5609c7 100644 --- a/test/functional/page_objects/discover_page.ts +++ b/test/functional/page_objects/discover_page.ts @@ -22,6 +22,9 @@ export class DiscoverPageObject extends FtrService { private readonly config = this.ctx.getService('config'); private readonly dataGrid = this.ctx.getService('dataGrid'); private readonly kibanaServer = this.ctx.getService('kibanaServer'); + private readonly queryBar = this.ctx.getService('queryBar'); + + private readonly unifiedSearch = this.ctx.getPageObject('unifiedSearch'); private readonly defaultFindTimeout = this.config.get('timeouts.find'); @@ -365,8 +368,7 @@ export class DiscoverPageObject extends FtrService { public async clickIndexPatternActions() { await this.retry.try(async () => { - await this.testSubjects.click('discoverIndexPatternActions'); - await this.testSubjects.existOrFail('discover-addRuntimeField-popover'); + await this.testSubjects.click('discover-dataView-switch-link'); }); } @@ -494,7 +496,7 @@ export class DiscoverPageObject extends FtrService { } public async selectIndexPattern(indexPattern: string) { - await this.testSubjects.click('indexPattern-switch-link'); + await this.testSubjects.click('discover-dataView-switch-link'); await this.find.setValue('[data-test-subj="indexPattern-switcher"] input', indexPattern); await this.find.clickByCssSelector( `[data-test-subj="indexPattern-switcher"] [title="${indexPattern}"]` @@ -557,6 +559,7 @@ export class DiscoverPageObject extends FtrService { await this.retry.waitFor('Discover app on screen', async () => { return await this.isDiscoverAppOnScreen(); }); + await this.unifiedSearch.closeTourPopoverByLocalStorage(); } public async showAllFilterActions() { @@ -564,10 +567,13 @@ export class DiscoverPageObject extends FtrService { } public async clickSavedQueriesPopOver() { - await this.testSubjects.click('saved-query-management-popover-button'); + await this.testSubjects.click('showQueryBarMenu'); } public async clickCurrentSavedQuery() { + await this.queryBar.setQuery('Cancelled : true'); + await this.queryBar.clickQuerySubmitButton(); + await this.testSubjects.click('showQueryBarMenu'); await this.testSubjects.click('saved-query-management-save-button'); } @@ -630,7 +636,7 @@ export class DiscoverPageObject extends FtrService { public async getCurrentlySelectedDataView() { await this.testSubjects.existOrFail('discover-sidebar'); - const button = await this.testSubjects.find('indexPattern-switch-link'); + const button = await this.testSubjects.find('discover-dataView-switch-link'); return button.getAttribute('title'); } } diff --git a/test/functional/page_objects/home_page.ts b/test/functional/page_objects/home_page.ts index 1e3e6a9634f4c..4acd8a6e10e95 100644 --- a/test/functional/page_objects/home_page.ts +++ b/test/functional/page_objects/home_page.ts @@ -78,6 +78,11 @@ export class HomePageObject extends FtrService { }); } + async launchSampleDiscover(id: string) { + await this.launchSampleDataSet(id); + await this.find.clickByLinkText('Discover'); + } + async launchSampleDashboard(id: string) { await this.launchSampleDataSet(id); await this.find.clickByLinkText('Dashboard'); diff --git a/test/functional/page_objects/index.ts b/test/functional/page_objects/index.ts index 826c4b78d1d0f..bdfe91efef900 100644 --- a/test/functional/page_objects/index.ts +++ b/test/functional/page_objects/index.ts @@ -31,6 +31,7 @@ import { SavedObjectsPageObject } from './management/saved_objects_page'; import { LegacyDataTableVisPageObject } from './legacy/data_table_vis'; import { IndexPatternFieldEditorPageObject } from './management/indexpattern_field_editor_page'; import { DashboardPageControls } from './dashboard_page_controls'; +import { UnifiedSearchPageObject } from './unified_search_page'; export const pageObjects = { common: CommonPageObject, @@ -58,4 +59,5 @@ export const pageObjects = { vegaChart: VegaChartPageObject, savedObjects: SavedObjectsPageObject, indexPatternFieldEditorObjects: IndexPatternFieldEditorPageObject, + unifiedSearch: UnifiedSearchPageObject, }; diff --git a/test/functional/page_objects/unified_search_page.ts b/test/functional/page_objects/unified_search_page.ts new file mode 100644 index 0000000000000..b1bcd0662f77e --- /dev/null +++ b/test/functional/page_objects/unified_search_page.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 and the Server Side Public License, v 1; you may 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 { FtrService } from '../ftr_provider_context'; + +export class UnifiedSearchPageObject extends FtrService { + private readonly browser = this.ctx.getService('browser'); + private readonly testSubjects = this.ctx.getService('testSubjects'); + + public async closeTour() { + const tourPopoverIsOpen = await this.testSubjects.exists('dataViewPickerTourLink'); + if (tourPopoverIsOpen) { + await this.testSubjects.click('dataViewPickerTourLink'); + } + } + + public async closeTourPopoverByLocalStorage() { + await this.browser.setLocalStorageItem('data.newDataViewMenu', 'true'); + await this.browser.refresh(); + } +} diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index f0ea135bd9ba6..f96e4088da78f 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -374,11 +374,13 @@ export class VisualBuilderPageObject extends FtrService { } public async getTopNLabel() { + await this.visChart.waitForVisualizationRenderingStabilized(); const topNLabel = await this.find.byCssSelector('.tvbVisTopN__label'); return await topNLabel.getVisibleText(); } public async getTopNCount() { + await this.visChart.waitForVisualizationRenderingStabilized(); const gaugeCount = await this.find.byCssSelector('.tvbVisTopN__value'); return await gaugeCount.getVisibleText(); } @@ -891,6 +893,7 @@ export class VisualBuilderPageObject extends FtrService { } public async getChartDebugState(chartData?: DebugState) { + await this.header.waitUntilLoadingHasFinished(); return chartData ?? (await this.elasticChart.getChartDebugData())!; } @@ -908,7 +911,6 @@ export class VisualBuilderPageObject extends FtrService { chartData?: DebugState, itemType: 'areas' | 'bars' | 'annotations' = 'areas' ) { - await this.header.waitUntilLoadingHasFinished(); return (await this.getChartDebugState(chartData))?.[itemType]; } diff --git a/test/functional/page_objects/visualize_page.ts b/test/functional/page_objects/visualize_page.ts index 20aec8ba5d984..e087d50f21003 100644 --- a/test/functional/page_objects/visualize_page.ts +++ b/test/functional/page_objects/visualize_page.ts @@ -39,6 +39,7 @@ export class VisualizePageObject extends FtrService { private readonly elasticChart = this.ctx.getService('elasticChart'); private readonly common = this.ctx.getPageObject('common'); private readonly header = this.ctx.getPageObject('header'); + private readonly unifiedSearch = this.ctx.getPageObject('unifiedSearch'); private readonly visEditor = this.ctx.getPageObject('visEditor'); private readonly visChart = this.ctx.getPageObject('visChart'); @@ -154,6 +155,10 @@ export class VisualizePageObject extends FtrService { public async clickVisType(type: string) { await this.testSubjects.click(`visType-${type}`); await this.header.waitUntilLoadingHasFinished(); + + if (type === 'lens') { + await this.unifiedSearch.closeTour(); + } } public async clickAreaChart() { diff --git a/test/functional/screenshots/baseline/area_chart.png b/test/functional/screenshots/baseline/area_chart.png index 851f53499e94f..7bbaa256f0360 100644 Binary files a/test/functional/screenshots/baseline/area_chart.png and b/test/functional/screenshots/baseline/area_chart.png differ diff --git a/test/functional/services/common/screenshots.ts b/test/functional/services/common/screenshots.ts index d5f901300941f..5fca603407760 100644 --- a/test/functional/services/common/screenshots.ts +++ b/test/functional/services/common/screenshots.ts @@ -22,7 +22,6 @@ const writeFileAsync = promisify(writeFile); export class ScreenshotsService extends FtrService { private readonly log = this.ctx.getService('log'); private readonly config = this.ctx.getService('config'); - private readonly testMetadata = this.ctx.getService('testMetadata'); private readonly browser = this.ctx.getService('browser'); private readonly SESSION_DIRECTORY = resolve(this.config.get('screenshots.directory'), 'session'); @@ -54,13 +53,7 @@ export class ScreenshotsService extends FtrService { const baselinePath = resolve(this.BASELINE_DIRECTORY, `${name}.png`); const failurePath = resolve(this.FAILURE_DIRECTORY, `${name}.png`); - await this.capture({ - path: sessionPath, - name, - el, - baselinePath, - failurePath, - }); + await this.capture(sessionPath, el); if (updateBaselines) { this.log.debug('Updating baseline snapshot'); @@ -82,42 +75,20 @@ export class ScreenshotsService extends FtrService { async take(name: string, el?: WebElementWrapper, subDirectories: string[] = []) { const path = resolve(this.SESSION_DIRECTORY, ...subDirectories, `${name}.png`); - await this.capture({ path, name, el }); + await this.capture(path, el); } async takeForFailure(name: string, el?: WebElementWrapper) { const path = resolve(this.FAILURE_DIRECTORY, `${name}.png`); - await this.capture({ - path, - name: `failure[${name}]`, - el, - }); + await this.capture(path, el); } - private async capture({ - path, - el, - name, - baselinePath, - failurePath, - }: { - path: string; - name: string; - el?: WebElementWrapper; - baselinePath?: string; - failurePath?: string; - }) { + private async capture(path: string, el?: WebElementWrapper) { try { this.log.info(`Taking screenshot "${path}"`); const screenshot = await (el ? el.takeScreenshot() : this.browser.takeScreenshot()); await mkdirAsync(dirname(path), { recursive: true }); await writeFileAsync(path, screenshot, 'base64'); - this.testMetadata.addScreenshot({ - name, - base64Png: Buffer.isBuffer(screenshot) ? screenshot.toString('base64') : screenshot, - baselinePath, - failurePath, - }); } catch (err) { this.log.error('SCREENSHOT FAILED'); this.log.error(err); diff --git a/test/functional/services/dashboard/visualizations.ts b/test/functional/services/dashboard/visualizations.ts index 8688d375f7a7b..48828798a4efa 100644 --- a/test/functional/services/dashboard/visualizations.ts +++ b/test/functional/services/dashboard/visualizations.ts @@ -17,6 +17,7 @@ export class DashboardVisualizationsService extends FtrService { private readonly visualize = this.ctx.getPageObject('visualize'); private readonly visEditor = this.ctx.getPageObject('visEditor'); private readonly header = this.ctx.getPageObject('header'); + private readonly unifiedSearch = this.ctx.getPageObject('unifiedSearch'); private readonly discover = this.ctx.getPageObject('discover'); private readonly timePicker = this.ctx.getPageObject('timePicker'); @@ -43,6 +44,7 @@ export class DashboardVisualizationsService extends FtrService { }) { this.log.debug(`createSavedSearch(${name})`); await this.header.clickDiscover(true); + await this.unifiedSearch.closeTourPopoverByLocalStorage(); await this.timePicker.setHistoricalDataRange(); if (query) { diff --git a/test/functional/services/filter_bar.ts b/test/functional/services/filter_bar.ts index eee1a1027f541..7178013d5b9fd 100644 --- a/test/functional/services/filter_bar.ts +++ b/test/functional/services/filter_bar.ts @@ -64,8 +64,8 @@ export class FilterBarService extends FtrService { * Removes all filters */ public async removeAllFilters(): Promise { - await this.testSubjects.click('showFilterActions'); - await this.testSubjects.click('removeAllFilters'); + await this.testSubjects.click('showQueryBarMenu'); + await this.testSubjects.click('filter-sets-removeAllFilters'); await this.header.waitUntilLoadingHasFinished(); await this.common.waitUntilUrlIncludes('filters:!()'); } diff --git a/test/functional/services/query_bar.ts b/test/functional/services/query_bar.ts index ec5fc039101a5..ca6c161accc39 100644 --- a/test/functional/services/query_bar.ts +++ b/test/functional/services/query_bar.ts @@ -16,7 +16,6 @@ export class QueryBarService extends FtrService { private readonly common = this.ctx.getPageObject('common'); private readonly header = this.ctx.getPageObject('header'); private readonly find = this.ctx.getService('find'); - private readonly browser = this.ctx.getService('browser'); async getQueryString(): Promise { return await this.testSubjects.getAttribute('queryInput', 'value'); @@ -60,20 +59,19 @@ export class QueryBarService extends FtrService { public async switchQueryLanguage(lang: 'kql' | 'lucene'): Promise { await this.testSubjects.click('switchQueryLanguageButton'); - const kqlToggle = await this.testSubjects.find('languageToggle'); - const currentLang = - (await kqlToggle.getAttribute('aria-checked')) === 'true' ? 'kql' : 'lucene'; - if (lang !== currentLang) { - await kqlToggle.click(); + await this.testSubjects.click(`${lang}LanguageMenuItem`); + const contextMenuPanelTitleButton = await this.testSubjects.exists( + 'contextMenuPanelTitleButton' + ); + if (contextMenuPanelTitleButton) { + await this.testSubjects.click('contextMenuPanelTitleButton'); } - - await this.browser.pressKeys(this.browser.keys.ESCAPE); // close popover await this.expectQueryLanguageOrFail(lang); // make sure lang is switched } public async expectQueryLanguageOrFail(lang: 'kql' | 'lucene'): Promise { const queryLanguageButton = await this.testSubjects.find('switchQueryLanguageButton'); - expect((await queryLanguageButton.getVisibleText()).toLowerCase()).to.eql(lang); + expect((await queryLanguageButton.getVisibleText()).toLowerCase()).to.eql(`language: ${lang}`); } /** diff --git a/test/functional/services/saved_query_management_component.ts b/test/functional/services/saved_query_management_component.ts index a216f8cb0469e..7822ed8f77a89 100644 --- a/test/functional/services/saved_query_management_component.ts +++ b/test/functional/services/saved_query_management_component.ts @@ -19,7 +19,7 @@ export class SavedQueryManagementComponentService extends FtrService { public async getCurrentlyLoadedQueryID() { await this.openSavedQueryManagementComponent(); try { - return await this.testSubjects.getVisibleText('~saved-query-list-item-selected'); + return await this.testSubjects.getVisibleText('savedQueryTitle'); } catch { return undefined; } @@ -53,7 +53,12 @@ export class SavedQueryManagementComponentService extends FtrService { return saveQueryFormSaveButtonStatus === false; }); - await this.testSubjects.click('savedQueryFormCancelButton'); + const contextMenuPanelTitleButton = await this.testSubjects.exists( + 'contextMenuPanelTitleButton' + ); + if (contextMenuPanelTitleButton) { + await this.testSubjects.click('contextMenuPanelTitleButton'); + } } public async saveCurrentlyLoadedAsNewQuery( @@ -63,7 +68,7 @@ export class SavedQueryManagementComponentService extends FtrService { includeTimeFilter: boolean ) { await this.openSavedQueryManagementComponent(); - await this.testSubjects.click('saved-query-management-save-as-new-button'); + await this.testSubjects.click('saved-query-management-save-button'); await this.submitSaveQueryForm(name, description, includeFilters, includeTimeFilter); } @@ -79,12 +84,12 @@ export class SavedQueryManagementComponentService extends FtrService { public async loadSavedQuery(title: string) { await this.openSavedQueryManagementComponent(); + await this.testSubjects.click('saved-query-management-load-button'); await this.testSubjects.click(`~load-saved-query-${title}-button`); + await this.testSubjects.click('saved-query-management-apply-changes-button'); await this.retry.try(async () => { await this.openSavedQueryManagementComponent(); - const selectedSavedQueryText = await this.testSubjects.getVisibleText( - '~saved-query-list-item-selected' - ); + const selectedSavedQueryText = await this.testSubjects.getVisibleText('savedQueryTitle'); expect(selectedSavedQueryText).to.eql(title); }); await this.closeSavedQueryManagementComponent(); @@ -92,13 +97,24 @@ export class SavedQueryManagementComponentService extends FtrService { public async deleteSavedQuery(title: string) { await this.openSavedQueryManagementComponent(); - await this.testSubjects.click(`~delete-saved-query-${title}-button`); + const shouldClickLoadMenu = await this.testSubjects.exists( + 'saved-query-management-load-button' + ); + if (shouldClickLoadMenu) { + await this.testSubjects.click('saved-query-management-load-button'); + } + await this.testSubjects.click(`~load-saved-query-${title}-button`); + await this.retry.waitFor('delete saved query', async () => { + await this.testSubjects.click(`delete-saved-query-${title}-button`); + const exists = await this.testSubjects.exists('confirmModalTitleText'); + return exists === true; + }); await this.common.clickConfirmOnModal(); } async clearCurrentlyLoadedQuery() { await this.openSavedQueryManagementComponent(); - await this.testSubjects.click('saved-query-management-clear-button'); + await this.testSubjects.click('filter-sets-removeAllFilters'); await this.closeSavedQueryManagementComponent(); const queryString = await this.queryBar.getQueryString(); expect(queryString).to.be.empty(); @@ -113,7 +129,6 @@ export class SavedQueryManagementComponentService extends FtrService { if (title) { await this.testSubjects.setValue('saveQueryFormTitle', title); } - await this.testSubjects.setValue('saveQueryFormDescription', description); const currentIncludeFiltersValue = (await this.testSubjects.getAttribute( @@ -138,6 +153,7 @@ export class SavedQueryManagementComponentService extends FtrService { async savedQueryExist(title: string) { await this.openSavedQueryManagementComponent(); + await this.testSubjects.click('saved-query-management-load-button'); const exists = await this.testSubjects.exists(`~load-saved-query-${title}-button`); await this.closeSavedQueryManagementComponent(); return exists; @@ -145,6 +161,13 @@ export class SavedQueryManagementComponentService extends FtrService { async savedQueryExistOrFail(title: string) { await this.openSavedQueryManagementComponent(); + await this.retry.waitFor('load saved query', async () => { + const shouldClickLoadMenu = await this.testSubjects.exists( + 'saved-query-management-load-button' + ); + return shouldClickLoadMenu === true; + }); + await this.testSubjects.click('saved-query-management-load-button'); await this.testSubjects.existOrFail(`~load-saved-query-${title}-button`); } @@ -163,24 +186,19 @@ export class SavedQueryManagementComponentService extends FtrService { } async openSavedQueryManagementComponent() { - const isOpenAlready = await this.testSubjects.exists('saved-query-management-popover'); + const isOpenAlready = await this.testSubjects.exists('queryBarMenuPanel'); if (isOpenAlready) return; - await this.testSubjects.click('saved-query-management-popover-button'); - - await this.retry.waitFor('saved query management popover to have any text', async () => { - const queryText = await this.testSubjects.getVisibleText('saved-query-management-popover'); - return queryText.length > 0; - }); + await this.testSubjects.click('showQueryBarMenu'); } async closeSavedQueryManagementComponent() { - const isOpenAlready = await this.testSubjects.exists('saved-query-management-popover'); + const isOpenAlready = await this.testSubjects.exists('queryBarMenuPanel'); if (!isOpenAlready) return; await this.retry.try(async () => { - await this.testSubjects.click('saved-query-management-popover-button'); - await this.testSubjects.missingOrFail('saved-query-management-popover'); + await this.testSubjects.click('showQueryBarMenu'); + await this.testSubjects.missingOrFail('queryBarMenuPanel'); }); } @@ -197,7 +215,9 @@ export class SavedQueryManagementComponentService extends FtrService { async saveNewQueryMissingOrFail() { await this.openSavedQueryManagementComponent(); - await this.testSubjects.missingOrFail('saved-query-management-save-button'); + const saveFilterSetBtn = await this.testSubjects.find('saved-query-management-save-button'); + const isDisabled = await saveFilterSetBtn.getAttribute('disabled'); + expect(isDisabled).to.equal('true'); } async updateCurrentlyLoadedQueryMissingOrFail() { diff --git a/test/interactive_setup_api_integration/tests/enrollment_flow.ts b/test/interactive_setup_api_integration/tests/enrollment_flow.ts index f35509f49480a..a9cec78e391c1 100644 --- a/test/interactive_setup_api_integration/tests/enrollment_flow.ts +++ b/test/interactive_setup_api_integration/tests/enrollment_flow.ts @@ -20,7 +20,7 @@ export default function (context: FtrProviderContext) { const config = context.getService('config'); describe('Interactive setup APIs - Enrollment flow', function () { - this.tags(['skipCloud', 'ciGroup11']); + this.tags('skipCloud'); let kibanaVerificationCode: string; let elasticsearchCaFingerprint: string; diff --git a/test/interactive_setup_api_integration/tests/manual_configuration_flow.ts b/test/interactive_setup_api_integration/tests/manual_configuration_flow.ts index 94a06363367ad..156834d503e8f 100644 --- a/test/interactive_setup_api_integration/tests/manual_configuration_flow.ts +++ b/test/interactive_setup_api_integration/tests/manual_configuration_flow.ts @@ -19,7 +19,7 @@ export default function (context: FtrProviderContext) { const config = context.getService('config'); describe('Interactive setup APIs - Manual configuration flow', function () { - this.tags(['skipCloud', 'ciGroup11']); + this.tags('skipCloud'); let kibanaVerificationCode: string; let elasticsearchCaCertificate: string; diff --git a/test/interactive_setup_api_integration/tests/manual_configuration_flow_without_tls.ts b/test/interactive_setup_api_integration/tests/manual_configuration_flow_without_tls.ts index a3964c5fd5aa6..6035b6571a1fc 100644 --- a/test/interactive_setup_api_integration/tests/manual_configuration_flow_without_tls.ts +++ b/test/interactive_setup_api_integration/tests/manual_configuration_flow_without_tls.ts @@ -18,7 +18,7 @@ export default function (context: FtrProviderContext) { const config = context.getService('config'); describe('Interactive setup APIs - Manual configuration flow without TLS', function () { - this.tags(['skipCloud', 'ciGroup11']); + this.tags('skipCloud'); let kibanaVerificationCode: string; before(async () => { diff --git a/test/interactive_setup_functional/manual_configuration_without_security.config.ts b/test/interactive_setup_functional/manual_configuration_without_security.config.ts index 953b33d4e2077..48c917e853b5a 100644 --- a/test/interactive_setup_functional/manual_configuration_without_security.config.ts +++ b/test/interactive_setup_functional/manual_configuration_without_security.config.ts @@ -13,7 +13,7 @@ import type { FtrConfigProviderContext } from '@kbn/test'; import { getDataPath } from '@kbn/utils'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); const testEndpointsPlugin = resolve( __dirname, diff --git a/test/interactive_setup_functional/tests/enrollment_token.ts b/test/interactive_setup_functional/tests/enrollment_token.ts index 20d9da6539692..5af4ed0fc3f2e 100644 --- a/test/interactive_setup_functional/tests/enrollment_token.ts +++ b/test/interactive_setup_functional/tests/enrollment_token.ts @@ -22,7 +22,7 @@ export default function ({ getService }: FtrProviderContext) { const log = getService('log'); describe('Interactive Setup Functional Tests (Enrollment token)', function () { - this.tags(['skipCloud', 'ciGroup11']); + this.tags('skipCloud'); const elasticsearchConfig = config.get('servers.elasticsearch'); let verificationCode: string; diff --git a/test/interactive_setup_functional/tests/manual_configuration.ts b/test/interactive_setup_functional/tests/manual_configuration.ts index 68c5068acd267..3f41cf0659567 100644 --- a/test/interactive_setup_functional/tests/manual_configuration.ts +++ b/test/interactive_setup_functional/tests/manual_configuration.ts @@ -19,7 +19,7 @@ export default function ({ getService }: FtrProviderContext) { const log = getService('log'); describe('Interactive Setup Functional Tests (Manual configuration)', function () { - this.tags(['skipCloud', 'ciGroup11']); + this.tags('skipCloud'); let verificationCode: string; before(async function () { diff --git a/test/interactive_setup_functional/tests/manual_configuration_without_security.ts b/test/interactive_setup_functional/tests/manual_configuration_without_security.ts index 20f7bac890da4..f8925e4add106 100644 --- a/test/interactive_setup_functional/tests/manual_configuration_without_security.ts +++ b/test/interactive_setup_functional/tests/manual_configuration_without_security.ts @@ -19,7 +19,7 @@ export default function ({ getService, getPageObject }: FtrProviderContext) { const log = getService('log'); describe('Interactive Setup Functional Tests (Manual configuration without Security)', function () { - this.tags(['skipCloud', 'ciGroup11']); + this.tags('skipCloud'); let verificationCode: string; before(async function () { diff --git a/test/interactive_setup_functional/tests/manual_configuration_without_tls.ts b/test/interactive_setup_functional/tests/manual_configuration_without_tls.ts index 04e31b4ae8454..23595150d55a1 100644 --- a/test/interactive_setup_functional/tests/manual_configuration_without_tls.ts +++ b/test/interactive_setup_functional/tests/manual_configuration_without_tls.ts @@ -19,7 +19,7 @@ export default function ({ getService }: FtrProviderContext) { const log = getService('log'); describe('Interactive Setup Functional Tests (Manual configuration without TLS)', function () { - this.tags(['skipCloud', 'ciGroup11']); + this.tags('skipCloud'); let verificationCode: string; before(async function () { diff --git a/test/interpreter_functional/config.ts b/test/interpreter_functional/config.ts index 3f9c846a51429..0f81089433b33 100644 --- a/test/interpreter_functional/config.ts +++ b/test/interpreter_functional/config.ts @@ -11,7 +11,7 @@ import fs from 'fs'; import { FtrConfigProviderContext } from '@kbn/test'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); // Find all folders in ./plugins since we treat all them as plugin folder const allFiles = fs.readdirSync(path.resolve(__dirname, 'plugins')); diff --git a/test/new_visualize_flow/config.ts b/test/new_visualize_flow/config.ts index a6bd97464e2d0..381d316fa1ad0 100644 --- a/test/new_visualize_flow/config.ts +++ b/test/new_visualize_flow/config.ts @@ -9,7 +9,7 @@ import { FtrConfigProviderContext } from '@kbn/test'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const commonConfig = await readConfigFile(require.resolve('../functional/config.js')); + const commonConfig = await readConfigFile(require.resolve('../functional/config.base.js')); return { testFiles: [require.resolve('./index.ts')], diff --git a/test/new_visualize_flow/index.ts b/test/new_visualize_flow/index.ts index 7cb55069d7d9b..35c90edf9447d 100644 --- a/test/new_visualize_flow/index.ts +++ b/test/new_visualize_flow/index.ts @@ -11,7 +11,6 @@ import { FtrProviderContext } from '../functional/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function ({ loadTestFile, getService }: FtrProviderContext) { describe('New Visualize Flow', function () { - this.tags('ciGroup11'); const esArchiver = getService('esArchiver'); before(async () => { await esArchiver.loadIfNeeded( diff --git a/test/plugin_functional/config.ts b/test/plugin_functional/config.ts index 4a96b2b402898..b2dbc762ab657 100644 --- a/test/plugin_functional/config.ts +++ b/test/plugin_functional/config.ts @@ -11,7 +11,7 @@ import path from 'path'; import fs from 'fs'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); // Find all folders in ./plugins since we treat all them as plugin folder const allFiles = fs.readdirSync(path.resolve(__dirname, 'plugins')); diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index b7adbfa17301c..d295be040db7a 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -91,6 +91,20 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'unifiedSearch.autocomplete.valueSuggestions.tiers (array)', 'unifiedSearch.autocomplete.valueSuggestions.timeout (duration)', 'data.search.aggs.shardDelay.enabled (boolean)', + 'data.search.sessions.cleanupInterval (duration)', + 'data.search.sessions.defaultExpiration (duration)', + 'data.search.sessions.enabled (boolean)', + 'data.search.sessions.expireInterval (duration)', + 'data.search.sessions.management.expiresSoonWarning (duration)', + 'data.search.sessions.management.maxSessions (number)', + 'data.search.sessions.management.refreshInterval (duration)', + 'data.search.sessions.management.refreshTimeout (duration)', + 'data.search.sessions.maxUpdateRetries (number)', + 'data.search.sessions.monitoringTaskTimeout (duration)', + 'data.search.sessions.notTouchedInProgressTimeout (duration)', + 'data.search.sessions.notTouchedTimeout (duration)', + 'data.search.sessions.pageSize (number)', + 'data.search.sessions.trackingInterval (duration)', 'enterpriseSearch.host (string)', 'home.disableWelcomeScreen (boolean)', 'map.emsFileApiUrl (string)', @@ -125,7 +139,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'newsfeed.fetchInterval (duration)', 'newsfeed.mainInterval (duration)', 'newsfeed.service.pathTemplate (string)', - 'newsfeed.service.urlRoot (any)', + 'newsfeed.service.urlRoot (string)', 'telemetry.allowChangingOptInStatus (boolean)', 'telemetry.banner (boolean)', 'telemetry.enabled (boolean)', @@ -151,23 +165,11 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'xpack.cloud.deployment_url (string)', 'xpack.cloud.full_story.enabled (boolean)', 'xpack.cloud.full_story.org_id (any)', + // No PII. Just the list of event types we want to forward to FullStory. + 'xpack.cloud.full_story.eventTypesAllowlist (array)', 'xpack.cloud.id (string)', 'xpack.cloud.organization_url (string)', 'xpack.cloud.profile_url (string)', - 'xpack.data_enhanced.search.sessions.cleanupInterval (duration)', - 'xpack.data_enhanced.search.sessions.defaultExpiration (duration)', - 'xpack.data_enhanced.search.sessions.enabled (boolean)', - 'xpack.data_enhanced.search.sessions.expireInterval (duration)', - 'xpack.data_enhanced.search.sessions.management.expiresSoonWarning (duration)', - 'xpack.data_enhanced.search.sessions.management.maxSessions (number)', - 'xpack.data_enhanced.search.sessions.management.refreshInterval (duration)', - 'xpack.data_enhanced.search.sessions.management.refreshTimeout (duration)', - 'xpack.data_enhanced.search.sessions.maxUpdateRetries (number)', - 'xpack.data_enhanced.search.sessions.monitoringTaskTimeout (duration)', - 'xpack.data_enhanced.search.sessions.notTouchedInProgressTimeout (duration)', - 'xpack.data_enhanced.search.sessions.notTouchedTimeout (duration)', - 'xpack.data_enhanced.search.sessions.pageSize (number)', - 'xpack.data_enhanced.search.sessions.trackingInterval (duration)', 'xpack.discoverEnhanced.actions.exploreDataInChart.enabled (boolean)', 'xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled (boolean)', 'xpack.fleet.agents.enabled (boolean)', @@ -182,14 +184,13 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'xpack.maps.showMapsInspectorAdapter (boolean)', 'xpack.observability.unsafe.alertingExperience.enabled (boolean)', 'xpack.observability.unsafe.cases.enabled (boolean)', - 'xpack.observability.unsafe.overviewNext.enabled (boolean)', 'xpack.observability.unsafe.rules.enabled (boolean)', 'xpack.osquery.actionEnabled (boolean)', 'xpack.osquery.packs (boolean)', 'xpack.osquery.savedQueries (boolean)', 'xpack.remote_clusters.ui.enabled (boolean)', /** - * NOTE: The Reporting plugin is currently disabled in functional tests (see test/functional/config.js). + * NOTE: The Reporting plugin is currently disabled in functional tests (see test/functional/config.base.js). * It will be re-enabled once #102552 is completed. */ // 'xpack.reporting.roles.allow (array)', diff --git a/test/scripts/jenkins_storybook.sh b/test/scripts/jenkins_storybook.sh index e03494e13677d..a07a634d2dba2 100755 --- a/test/scripts/jenkins_storybook.sh +++ b/test/scripts/jenkins_storybook.sh @@ -10,7 +10,7 @@ yarn storybook --site ci_composite yarn storybook --site custom_integrations yarn storybook --site dashboard yarn storybook --site dashboard_enhanced -yarn storybook --site data_enhanced +yarn storybook --site data yarn storybook --site embeddable yarn storybook --site expression_error yarn storybook --site expression_image diff --git a/test/server_integration/config.base.js b/test/server_integration/config.base.js new file mode 100644 index 0000000000000..71006c258c423 --- /dev/null +++ b/test/server_integration/config.base.js @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + createKibanaSupertestProvider, + KibanaSupertestWithoutAuthProvider, + ElasticsearchSupertestProvider, +} from './services'; + +export default async function ({ readConfigFile }) { + const commonConfig = await readConfigFile(require.resolve('../common/config')); + const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); + + return { + services: { + ...commonConfig.get('services'), + supertest: createKibanaSupertestProvider(), + supertestWithoutAuth: KibanaSupertestWithoutAuthProvider, + esSupertest: ElasticsearchSupertestProvider, + }, + servers: commonConfig.get('servers'), + junit: { + reportName: 'Integration Tests', + }, + esTestCluster: { + ...functionalConfig.get('esTestCluster'), + serverArgs: ['xpack.security.enabled=false'], + }, + kbnTestServer: { + ...functionalConfig.get('kbnTestServer'), + serverArgs: [ + ...functionalConfig.get('kbnTestServer.serverArgs'), + '--elasticsearch.healthCheck.delay=3600000', + '--server.xsrf.disableProtection=true', + ], + }, + }; +} diff --git a/test/server_integration/config.js b/test/server_integration/config.js deleted file mode 100644 index 0ebb5c48033b8..0000000000000 --- a/test/server_integration/config.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { - createKibanaSupertestProvider, - KibanaSupertestWithoutAuthProvider, - ElasticsearchSupertestProvider, -} from './services'; - -export default async function ({ readConfigFile }) { - const commonConfig = await readConfigFile(require.resolve('../common/config')); - const functionalConfig = await readConfigFile(require.resolve('../functional/config')); - - return { - services: { - ...commonConfig.get('services'), - supertest: createKibanaSupertestProvider(), - supertestWithoutAuth: KibanaSupertestWithoutAuthProvider, - esSupertest: ElasticsearchSupertestProvider, - }, - servers: commonConfig.get('servers'), - junit: { - reportName: 'Integration Tests', - }, - esTestCluster: { - ...functionalConfig.get('esTestCluster'), - serverArgs: ['xpack.security.enabled=false'], - }, - kbnTestServer: { - ...functionalConfig.get('kbnTestServer'), - serverArgs: [ - ...functionalConfig.get('kbnTestServer.serverArgs'), - '--elasticsearch.healthCheck.delay=3600000', - '--server.xsrf.disableProtection=true', - ], - }, - }; -} diff --git a/test/server_integration/http/platform/config.status.ts b/test/server_integration/http/platform/config.status.ts index 20ffc917f8244..8be9b24a56817 100644 --- a/test/server_integration/http/platform/config.status.ts +++ b/test/server_integration/http/platform/config.status.ts @@ -20,7 +20,7 @@ import { FtrConfigProviderContext } from '@kbn/test'; */ // eslint-disable-next-line import/no-default-export export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const httpConfig = await readConfigFile(require.resolve('../../config')); + const httpConfig = await readConfigFile(require.resolve('../../config.base.js')); // Find all folders in __fixtures__/plugins since we treat all them as plugin folder const allFiles = fs.readdirSync(path.resolve(__dirname, '../../__fixtures__/plugins')); @@ -52,6 +52,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { ...httpConfig.get('kbnTestServer.runOptions'), // Don't wait for Kibana to be completely ready so that we can test the status timeouts wait: /Kibana is now unavailable/, + alwaysUseSource: true, }, }, }; diff --git a/test/server_integration/http/platform/config.ts b/test/server_integration/http/platform/config.ts index f3cdf426e9cbb..028ff67b43022 100644 --- a/test/server_integration/http/platform/config.ts +++ b/test/server_integration/http/platform/config.ts @@ -10,7 +10,7 @@ import { FtrConfigProviderContext } from '@kbn/test'; // eslint-disable-next-line import/no-default-export export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const httpConfig = await readConfigFile(require.resolve('../../config')); + const httpConfig = await readConfigFile(require.resolve('../../config.base.js')); return { testFiles: [require.resolve('./cache'), require.resolve('./headers')], diff --git a/test/server_integration/http/ssl/config.js b/test/server_integration/http/ssl/config.js index 260ba7424d676..14d9a27a00f44 100644 --- a/test/server_integration/http/ssl/config.js +++ b/test/server_integration/http/ssl/config.js @@ -11,7 +11,7 @@ import { CA_CERT_PATH, KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils'; import { createKibanaSupertestProvider } from '../../services'; export default async function ({ readConfigFile }) { - const httpConfig = await readConfigFile(require.resolve('../../config')); + const httpConfig = await readConfigFile(require.resolve('../../config.base.js')); const certificateAuthorities = [readFileSync(CA_CERT_PATH)]; return { diff --git a/test/server_integration/http/ssl_redirect/config.js b/test/server_integration/http/ssl_redirect/config.js index aed7f23593820..47568b16bf6ba 100644 --- a/test/server_integration/http/ssl_redirect/config.js +++ b/test/server_integration/http/ssl_redirect/config.js @@ -13,7 +13,7 @@ import { CA_CERT_PATH, KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils'; import { createKibanaSupertestProvider } from '../../services'; export default async function ({ readConfigFile }) { - const httpConfig = await readConfigFile(require.resolve('../../config')); + const httpConfig = await readConfigFile(require.resolve('../../config.base.js')); const certificateAuthorities = [readFileSync(CA_CERT_PATH)]; const redirectPort = httpConfig.get('servers.kibana.port') + 1234; diff --git a/test/server_integration/http/ssl_with_p12/config.js b/test/server_integration/http/ssl_with_p12/config.js index 5d2de9e9a9b54..51beb51b20ba4 100644 --- a/test/server_integration/http/ssl_with_p12/config.js +++ b/test/server_integration/http/ssl_with_p12/config.js @@ -11,7 +11,7 @@ import { CA_CERT_PATH, KBN_P12_PATH, KBN_P12_PASSWORD } from '@kbn/dev-utils'; import { createKibanaSupertestProvider } from '../../services'; export default async function ({ readConfigFile }) { - const httpConfig = await readConfigFile(require.resolve('../../config')); + const httpConfig = await readConfigFile(require.resolve('../../config.base.js')); const certificateAuthorities = [readFileSync(CA_CERT_PATH)]; return { diff --git a/test/server_integration/http/ssl_with_p12_intermediate/config.js b/test/server_integration/http/ssl_with_p12_intermediate/config.js index ffcb9da0fa047..a6eb6c82485d9 100644 --- a/test/server_integration/http/ssl_with_p12_intermediate/config.js +++ b/test/server_integration/http/ssl_with_p12_intermediate/config.js @@ -11,7 +11,7 @@ import { CA1_CERT_PATH, CA2_CERT_PATH, EE_P12_PATH, EE_P12_PASSWORD } from '../. import { createKibanaSupertestProvider } from '../../services'; export default async function ({ readConfigFile }) { - const httpConfig = await readConfigFile(require.resolve('../../config')); + const httpConfig = await readConfigFile(require.resolve('../../config.base.js')); const certificateAuthorities = [readFileSync(CA1_CERT_PATH), readFileSync(CA2_CERT_PATH)]; return { diff --git a/test/ui_capabilities/newsfeed_err/config.ts b/test/ui_capabilities/newsfeed_err/config.ts index e9548b41b67a0..4b6eb99a5627c 100644 --- a/test/ui_capabilities/newsfeed_err/config.ts +++ b/test/ui_capabilities/newsfeed_err/config.ts @@ -7,22 +7,20 @@ */ import { FtrConfigProviderContext } from '@kbn/test'; -// @ts-ignore untyped module -import getFunctionalConfig from '../../functional/config'; // eslint-disable-next-line import/no-default-export export default async ({ readConfigFile }: FtrConfigProviderContext) => { - const functionalConfig = await getFunctionalConfig({ readConfigFile }); + const baseConfig = await readConfigFile(require.resolve('../../functional/config.base.js')); return { - ...functionalConfig, + ...baseConfig.getAll(), testFiles: [require.resolve('./test')], kbnTestServer: { - ...functionalConfig.kbnTestServer, + ...baseConfig.get('kbnTestServer'), serverArgs: [ - ...functionalConfig.kbnTestServer.serverArgs, + ...baseConfig.get('kbnTestServer.serverArgs'), `--newsfeed.service.pathTemplate=/api/_newsfeed-FTS-external-service-simulators/kibana/crash.json`, ], }, diff --git a/test/ui_capabilities/newsfeed_err/test.ts b/test/ui_capabilities/newsfeed_err/test.ts index 52c1c0644299a..538d790ac5724 100644 --- a/test/ui_capabilities/newsfeed_err/test.ts +++ b/test/ui_capabilities/newsfeed_err/test.ts @@ -15,8 +15,6 @@ export default function uiCapabilitiesTests({ getService, getPageObjects }: FtrP const PageObjects = getPageObjects(['common', 'newsfeed']); describe('Newsfeed icon button handle errors', function () { - this.tags('ciGroup5'); - before(async () => { await PageObjects.newsfeed.resetPage(); }); diff --git a/test/visual_regression/config.ts b/test/visual_regression/config.ts index 3c11a4c2689ba..294848246e7c8 100644 --- a/test/visual_regression/config.ts +++ b/test/visual_regression/config.ts @@ -10,7 +10,7 @@ import { FtrConfigProviderContext } from '@kbn/test'; import { services } from './services'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); return { ...functionalConfig.getAll(), diff --git a/test/visual_regression/tests/discover/index.ts b/test/visual_regression/tests/discover/index.ts index fe634c02400a4..9142a430f963b 100644 --- a/test/visual_regression/tests/discover/index.ts +++ b/test/visual_regression/tests/discover/index.ts @@ -16,8 +16,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { const browser = getService('browser'); describe('discover app', function () { - this.tags('ciGroup5'); - before(function () { return browser.setWindowSize(SCREEN_WIDTH, 1000); }); diff --git a/test/visual_regression/tests/vega/index.ts b/test/visual_regression/tests/vega/index.ts index 71f22a3058d91..9ab4e199439a4 100644 --- a/test/visual_regression/tests/vega/index.ts +++ b/test/visual_regression/tests/vega/index.ts @@ -16,8 +16,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { const browser = getService('browser'); describe('vega app', function () { - this.tags('ciGroup5'); - before(function () { return browser.setWindowSize(SCREEN_WIDTH, 1000); }); diff --git a/tsconfig.base.json b/tsconfig.base.json index ac35847201b7f..78023a603276a 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -295,8 +295,6 @@ "@kbn/cross-cluster-replication-plugin/*": ["x-pack/plugins/cross_cluster_replication/*"], "@kbn/dashboard-enhanced-plugin": ["x-pack/plugins/dashboard_enhanced"], "@kbn/dashboard-enhanced-plugin/*": ["x-pack/plugins/dashboard_enhanced/*"], - "@kbn/data-enhanced-plugin": ["x-pack/plugins/data_enhanced"], - "@kbn/data-enhanced-plugin/*": ["x-pack/plugins/data_enhanced/*"], "@kbn/data-visualizer-plugin": ["x-pack/plugins/data_visualizer"], "@kbn/data-visualizer-plugin/*": ["x-pack/plugins/data_visualizer/*"], "@kbn/discover-enhanced-plugin": ["x-pack/plugins/discover_enhanced"], diff --git a/tsconfig.types.json b/tsconfig.types.json index 54a26b6aca404..0217a2cde12bb 100644 --- a/tsconfig.types.json +++ b/tsconfig.types.json @@ -7,17 +7,11 @@ "declaration": true, "emitDeclarationOnly": true, "declarationMap": true, + "rootDir": "./src" }, "include": [ "src/core/server/index.ts", "src/core/public/index.ts", - "src/plugins/data/server/index.ts", - "src/plugins/data/public/index.ts", - "src/plugins/embeddable/server/index.ts", - "src/plugins/embeddable/public/index.ts", - "src/plugins/expressions/server/index.ts", - "src/plugins/expressions/public/index.ts", - "src/plugins/ui_actions/public/index.ts", "typings" ] } diff --git a/versions.json b/versions.json index 6dfe620c9fd7e..3223c4e8c62dd 100644 --- a/versions.json +++ b/versions.json @@ -8,16 +8,11 @@ "currentMinor": true }, { - "version": "8.2.0", + "version": "8.2.1", "branch": "8.2", "currentMajor": true, "previousMinor": true }, - { - "version": "8.1.3", - "branch": "8.1", - "currentMajor": true - }, { "version": "7.17.3", "branch": "7.17", diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index a632593f8b96b..b1464f5cfbe2e 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -14,7 +14,6 @@ "xpack.dashboard": "plugins/dashboard_enhanced", "xpack.discover": "plugins/discover_enhanced", "xpack.crossClusterReplication": "plugins/cross_cluster_replication", - "xpack.data": "plugins/data_enhanced", "xpack.embeddableEnhanced": "plugins/embeddable_enhanced", "xpack.endpoint": "plugins/endpoint", "xpack.enterpriseSearch": "plugins/enterprise_search", diff --git a/x-pack/README.md b/x-pack/README.md index d104dffff3d28..dad469295ca0f 100644 --- a/x-pack/README.md +++ b/x-pack/README.md @@ -20,7 +20,7 @@ For information on testing, see [the Elastic functional test development guide]( #### Running functional tests -The functional UI tests, the API integration tests, and the SAML API integration tests are all run against a live browser, Kibana, and Elasticsearch install. Each set of tests is specified with a unique config that describes how to start the Elasticsearch server, the Kibana server, and what tests to run against them. The sets of tests that exist today are *functional UI tests* ([specified by this config](test/functional/config.js)), *API integration tests* ([specified by this config](test/api_integration/config.ts)), and *SAML API integration tests* ([specified by this config](test/security_api_integration/saml.config.ts)). +The functional UI tests, the API integration tests, and the SAML API integration tests are all run against a live browser, Kibana, and Elasticsearch install. Each set of tests is specified with a unique config that describes how to start the Elasticsearch server, the Kibana server, and what tests to run against them. The sets of tests that exist today are *functional UI tests* ([specified by this config](test/functional/config.base.js)), *API integration tests* ([specified by this config](test/api_integration/config.ts)), and *SAML API integration tests* ([specified by this config](test/security_api_integration/saml.config.ts)). The script runs all sets of tests sequentially like so: * builds Elasticsearch and X-Pack diff --git a/x-pack/plugins/actions/server/actions_client.mock.ts b/x-pack/plugins/actions/server/actions_client.mock.ts index 419babe97c0f4..246c8fa35fc15 100644 --- a/x-pack/plugins/actions/server/actions_client.mock.ts +++ b/x-pack/plugins/actions/server/actions_client.mock.ts @@ -19,6 +19,7 @@ const createActionsClientMock = () => { update: jest.fn(), getAll: jest.fn(), getBulk: jest.fn(), + getOAuthAccessToken: jest.fn(), execute: jest.fn(), enqueueExecution: jest.fn(), ephemeralEnqueuedExecution: jest.fn(), diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index afee13b8c9bca..787b4e450a9e0 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -18,11 +18,14 @@ import { actionsConfigMock } from './actions_config.mock'; import { getActionsConfigurationUtilities } from './actions_config'; import { licenseStateMock } from './lib/license_state.mock'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; -import { httpServerMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import { + httpServerMock, + loggingSystemMock, + elasticsearchServiceMock, + savedObjectsClientMock, +} from '@kbn/core/server/mocks'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; - -import { elasticsearchServiceMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; import { actionExecutorMock } from './lib/action_executor.mock'; import uuid from 'uuid'; import { ActionsAuthorization } from './authorization/actions_authorization'; @@ -37,6 +40,9 @@ import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/s import { Logger } from '@kbn/core/server'; import { connectorTokenClientMock } from './builtin_action_types/lib/connector_token_client.mock'; import { inMemoryMetricsMock } from './monitoring/in_memory_metrics.mock'; +import { getOAuthJwtAccessToken } from './builtin_action_types/lib/get_oauth_jwt_access_token'; +import { getOAuthClientCredentialsAccessToken } from './builtin_action_types/lib/get_oauth_client_credentials_access_token'; +import { OAuthParams } from './routes/get_oauth_access_token'; jest.mock('@kbn/core/server/saved_objects/service/lib/utils', () => ({ SavedObjectsUtils: { @@ -60,6 +66,13 @@ jest.mock('./authorization/get_authorization_mode_by_source', () => { }; }); +jest.mock('./builtin_action_types/lib/get_oauth_jwt_access_token', () => ({ + getOAuthJwtAccessToken: jest.fn(), +})); +jest.mock('./builtin_action_types/lib/get_oauth_client_credentials_access_token', () => ({ + getOAuthClientCredentialsAccessToken: jest.fn(), +})); + const defaultKibanaIndex = '.kibana'; const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); @@ -73,6 +86,7 @@ const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); const logger = loggingSystemMock.create().get() as jest.Mocked; const mockTaskManager = taskManagerMock.createSetup(); +const configurationUtilities = actionsConfigMock.create(); let actionsClient: ActionsClient; let mockedLicenseState: jest.Mocked; @@ -115,6 +129,10 @@ beforeEach(() => { usageCounter: mockUsageCounter, connectorTokenClient, }); + (getOAuthJwtAccessToken as jest.Mock).mockResolvedValue(`Bearer jwttokentokentoken`); + (getOAuthClientCredentialsAccessToken as jest.Mock).mockResolvedValue( + `Bearer clienttokentokentoken` + ); }); describe('create()', () => { @@ -1274,6 +1292,292 @@ describe('getBulk()', () => { }); }); +describe('getOAuthAccessToken()', () => { + function getOAuthAccessToken( + requestBody: OAuthParams + ): ReturnType { + actionsClient = new ActionsClient({ + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + defaultKibanaIndex, + actionExecutor, + executionEnqueuer, + ephemeralExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + preconfiguredActions: [ + { + id: 'testPreconfigured', + actionTypeId: '.slack', + secrets: {}, + isPreconfigured: true, + isDeprecated: false, + name: 'test', + config: { + foo: 'bar', + }, + }, + ], + connectorTokenClient: connectorTokenClientMock.create(), + }); + return actionsClient.getOAuthAccessToken(requestBody, logger, configurationUtilities); + } + + describe('authorization', () => { + test('ensures user is authorised to get the type of action', async () => { + await getOAuthAccessToken({ + type: 'jwt', + options: { + tokenUrl: 'https://testurl.service-now.com/oauth_token.do', + config: { + clientId: 'abc', + jwtKeyId: 'def', + userIdentifierValue: 'userA', + }, + secrets: { + clientSecret: 'iamasecret', + privateKey: 'xyz', + }, + }, + }); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('update'); + }); + + test('throws when user is not authorised to create the type of action', async () => { + authorization.ensureAuthorized.mockRejectedValue(new Error(`Unauthorized to update actions`)); + + await expect( + getOAuthAccessToken({ + type: 'jwt', + options: { + tokenUrl: 'https://testurl.service-now.com/oauth_token.do', + config: { + clientId: 'abc', + jwtKeyId: 'def', + userIdentifierValue: 'userA', + }, + secrets: { + clientSecret: 'iamasecret', + privateKey: 'xyz', + }, + }, + }) + ).rejects.toMatchInlineSnapshot(`[Error: Unauthorized to update actions]`); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('update'); + }); + }); + + test('throws when tokenUrl is not using http or https', async () => { + await expect( + getOAuthAccessToken({ + type: 'jwt', + options: { + tokenUrl: 'ftp://testurl.service-now.com/oauth_token.do', + config: { + clientId: 'abc', + jwtKeyId: 'def', + userIdentifierValue: 'userA', + }, + secrets: { + clientSecret: 'iamasecret', + privateKey: 'xyz', + }, + }, + }) + ).rejects.toMatchInlineSnapshot(`[Error: Token URL must use http or https]`); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('update'); + }); + + test('throws when tokenUrl does not contain hostname', async () => { + await expect( + getOAuthAccessToken({ + type: 'jwt', + options: { + tokenUrl: '/path/to/myfile', + config: { + clientId: 'abc', + jwtKeyId: 'def', + userIdentifierValue: 'userA', + }, + secrets: { + clientSecret: 'iamasecret', + privateKey: 'xyz', + }, + }, + }) + ).rejects.toMatchInlineSnapshot(`[Error: Token URL must contain hostname]`); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('update'); + }); + + test('throws when tokenUrl is not in allowed hosts', async () => { + configurationUtilities.ensureUriAllowed.mockImplementationOnce(() => { + throw new Error('URI not allowed'); + }); + + await expect( + getOAuthAccessToken({ + type: 'jwt', + options: { + tokenUrl: 'https://testurl.service-now.com/oauth_token.do', + config: { + clientId: 'abc', + jwtKeyId: 'def', + userIdentifierValue: 'userA', + }, + secrets: { + clientSecret: 'iamasecret', + privateKey: 'xyz', + }, + }, + }) + ).rejects.toMatchInlineSnapshot(`[Error: URI not allowed]`); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('update'); + expect(configurationUtilities.ensureUriAllowed).toHaveBeenCalledWith( + `https://testurl.service-now.com/oauth_token.do` + ); + }); + + test('calls getOAuthJwtAccessToken when type="jwt"', async () => { + const result = await getOAuthAccessToken({ + type: 'jwt', + options: { + tokenUrl: 'https://testurl.service-now.com/oauth_token.do', + config: { + clientId: 'abc', + jwtKeyId: 'def', + userIdentifierValue: 'userA', + }, + secrets: { + clientSecret: 'iamasecret', + privateKey: 'xyz', + }, + }, + }); + expect(result).toEqual({ + accessToken: 'Bearer jwttokentokentoken', + }); + expect(getOAuthJwtAccessToken as jest.Mock).toHaveBeenCalledWith({ + logger, + configurationUtilities, + credentials: { + config: { + clientId: 'abc', + jwtKeyId: 'def', + userIdentifierValue: 'userA', + }, + secrets: { + clientSecret: 'iamasecret', + privateKey: 'xyz', + }, + }, + tokenUrl: 'https://testurl.service-now.com/oauth_token.do', + }); + expect(getOAuthClientCredentialsAccessToken).not.toHaveBeenCalled(); + expect(logger.debug).toHaveBeenCalledWith( + `Successfully retrieved access token using JWT OAuth with tokenUrl https://testurl.service-now.com/oauth_token.do and config {\"clientId\":\"abc\",\"jwtKeyId\":\"def\",\"userIdentifierValue\":\"userA\"}` + ); + }); + + test('calls getOAuthClientCredentialsAccessToken when type="client"', async () => { + const result = await getOAuthAccessToken({ + type: 'client', + options: { + tokenUrl: 'https://login.microsoftonline.com/98765/oauth2/v2.0/token', + scope: 'https://graph.microsoft.com/.default', + config: { + clientId: 'abc', + tenantId: 'def', + }, + secrets: { + clientSecret: 'iamasecret', + }, + }, + }); + expect(result).toEqual({ + accessToken: 'Bearer clienttokentokentoken', + }); + expect(getOAuthClientCredentialsAccessToken as jest.Mock).toHaveBeenCalledWith({ + logger, + configurationUtilities, + credentials: { + config: { + clientId: 'abc', + tenantId: 'def', + }, + secrets: { + clientSecret: 'iamasecret', + }, + }, + tokenUrl: 'https://login.microsoftonline.com/98765/oauth2/v2.0/token', + oAuthScope: 'https://graph.microsoft.com/.default', + }); + expect(getOAuthJwtAccessToken).not.toHaveBeenCalled(); + expect(logger.debug).toHaveBeenCalledWith( + `Successfully retrieved access token using Client Credentials OAuth with tokenUrl https://login.microsoftonline.com/98765/oauth2/v2.0/token, scope https://graph.microsoft.com/.default and config {\"clientId\":\"abc\",\"tenantId\":\"def\"}` + ); + }); + + test('throws when getOAuthJwtAccessToken throws error', async () => { + (getOAuthJwtAccessToken as jest.Mock).mockRejectedValue(new Error(`Something went wrong!`)); + + await expect( + getOAuthAccessToken({ + type: 'jwt', + options: { + tokenUrl: 'https://testurl.service-now.com/oauth_token.do', + config: { + clientId: 'abc', + jwtKeyId: 'def', + userIdentifierValue: 'userA', + }, + secrets: { + clientSecret: 'iamasecret', + privateKey: 'xyz', + }, + }, + }) + ).rejects.toMatchInlineSnapshot(`[Error: Failed to retrieve access token]`); + + expect(getOAuthJwtAccessToken as jest.Mock).toHaveBeenCalled(); + expect(logger.debug).toHaveBeenCalledWith( + `Failed to retrieve access token using JWT OAuth with tokenUrl https://testurl.service-now.com/oauth_token.do and config {\"clientId\":\"abc\",\"jwtKeyId\":\"def\",\"userIdentifierValue\":\"userA\"} - Something went wrong!` + ); + }); + + test('throws when getOAuthClientCredentialsAccessToken throws error', async () => { + (getOAuthClientCredentialsAccessToken as jest.Mock).mockRejectedValue( + new Error(`Something went wrong!`) + ); + + await expect( + getOAuthAccessToken({ + type: 'client', + options: { + tokenUrl: 'https://login.microsoftonline.com/98765/oauth2/v2.0/token', + scope: 'https://graph.microsoft.com/.default', + config: { + clientId: 'abc', + tenantId: 'def', + }, + secrets: { + clientSecret: 'iamasecret', + }, + }, + }) + ).rejects.toMatchInlineSnapshot(`[Error: Failed to retrieve access token]`); + + expect(getOAuthClientCredentialsAccessToken as jest.Mock).toHaveBeenCalled(); + expect(logger.debug).toHaveBeenCalledWith( + `Failed to retrieved access token using Client Credentials OAuth with tokenUrl https://login.microsoftonline.com/98765/oauth2/v2.0/token, scope https://graph.microsoft.com/.default and config {\"clientId\":\"abc\",\"tenantId\":\"def\"} - Something went wrong!` + ); + }); +}); + describe('delete()', () => { describe('authorization', () => { test('ensures user is authorised to delete actions', async () => { diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index dacf6de36bd37..89156bb56b51a 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -6,6 +6,7 @@ */ import Boom from '@hapi/boom'; +import url from 'url'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { UsageCounter } from '@kbn/usage-collection-plugin/server'; @@ -18,6 +19,7 @@ import { SavedObject, KibanaRequest, SavedObjectsUtils, + Logger, } from '@kbn/core/server'; import { AuditLogger } from '@kbn/security-plugin/server'; import { RunNowResult } from '@kbn/task-manager-plugin/server'; @@ -46,6 +48,22 @@ import { import { connectorAuditEvent, ConnectorAuditAction } from './lib/audit_events'; import { trackLegacyRBACExemption } from './lib/track_legacy_rbac_exemption'; import { isConnectorDeprecated } from './lib/is_conector_deprecated'; +import { ActionsConfigurationUtilities } from './actions_config'; +import { + OAuthClientCredentialsParams, + OAuthJwtParams, + OAuthParams, +} from './routes/get_oauth_access_token'; +import { + getOAuthJwtAccessToken, + GetOAuthJwtConfig, + GetOAuthJwtSecrets, +} from './builtin_action_types/lib/get_oauth_jwt_access_token'; +import { + getOAuthClientCredentialsAccessToken, + GetOAuthClientCredentialsConfig, + GetOAuthClientCredentialsSecrets, +} from './builtin_action_types/lib/get_oauth_client_credentials_access_token'; // We are assuming there won't be many actions. This is why we will load // all the actions in advance and assume the total count to not go over 10000. @@ -448,6 +466,98 @@ export class ActionsClient { return actionResults; } + public async getOAuthAccessToken( + { type, options }: OAuthParams, + logger: Logger, + configurationUtilities: ActionsConfigurationUtilities + ) { + // Verify that user has edit access + await this.authorization.ensureAuthorized('update'); + + // Verify that token url is allowed by allowed hosts config + try { + configurationUtilities.ensureUriAllowed(options.tokenUrl); + } catch (err) { + throw Boom.badRequest(err.message); + } + + // Verify that token url contains a hostname and uses https + const parsedUrl = url.parse( + options.tokenUrl, + false /* parseQueryString */, + true /* slashesDenoteHost */ + ); + + if (!parsedUrl.hostname) { + throw Boom.badRequest(`Token URL must contain hostname`); + } + + if (parsedUrl.protocol !== 'https:' && parsedUrl.protocol !== 'http:') { + throw Boom.badRequest(`Token URL must use http or https`); + } + + let accessToken: string | null = null; + if (type === 'jwt') { + const tokenOpts = options as OAuthJwtParams; + + try { + accessToken = await getOAuthJwtAccessToken({ + logger, + configurationUtilities, + credentials: { + config: tokenOpts.config as GetOAuthJwtConfig, + secrets: tokenOpts.secrets as GetOAuthJwtSecrets, + }, + tokenUrl: tokenOpts.tokenUrl, + }); + + logger.debug( + `Successfully retrieved access token using JWT OAuth with tokenUrl ${ + tokenOpts.tokenUrl + } and config ${JSON.stringify(tokenOpts.config)}` + ); + } catch (err) { + logger.debug( + `Failed to retrieve access token using JWT OAuth with tokenUrl ${ + tokenOpts.tokenUrl + } and config ${JSON.stringify(tokenOpts.config)} - ${err.message}` + ); + throw Boom.badRequest(`Failed to retrieve access token`); + } + } else if (type === 'client') { + const tokenOpts = options as OAuthClientCredentialsParams; + try { + accessToken = await getOAuthClientCredentialsAccessToken({ + logger, + configurationUtilities, + credentials: { + config: tokenOpts.config as GetOAuthClientCredentialsConfig, + secrets: tokenOpts.secrets as GetOAuthClientCredentialsSecrets, + }, + tokenUrl: tokenOpts.tokenUrl, + oAuthScope: tokenOpts.scope, + }); + + logger.debug( + `Successfully retrieved access token using Client Credentials OAuth with tokenUrl ${ + tokenOpts.tokenUrl + }, scope ${tokenOpts.scope} and config ${JSON.stringify(tokenOpts.config)}` + ); + } catch (err) { + logger.debug( + `Failed to retrieved access token using Client Credentials OAuth with tokenUrl ${ + tokenOpts.tokenUrl + }, scope ${tokenOpts.scope} and config ${JSON.stringify(tokenOpts.config)} - ${ + err.message + }` + ); + throw Boom.badRequest(`Failed to retrieve access token`); + } + } + + return { accessToken }; + } + /** * Delete action */ diff --git a/x-pack/plugins/actions/server/actions_config.test.ts b/x-pack/plugins/actions/server/actions_config.test.ts index 470e6ce8cdc8e..a6b68d907cb44 100644 --- a/x-pack/plugins/actions/server/actions_config.test.ts +++ b/x-pack/plugins/actions/server/actions_config.test.ts @@ -129,6 +129,17 @@ describe('isUriAllowed', () => { ).toEqual(true); }); + test('returns true for network path references', () => { + const config: ActionsConfig = { + ...defaultActionsConfig, + allowedHosts: ['my-domain.com'], + enabledActionTypes: [], + }; + expect(getActionsConfigurationUtilities(config).isUriAllowed('//my-domain.com/foo')).toEqual( + true + ); + }); + test('throws when the hostname in the requested uri is not in the allowedHosts', () => { const config: ActionsConfig = defaultActionsConfig; expect( diff --git a/x-pack/plugins/actions/server/actions_config.ts b/x-pack/plugins/actions/server/actions_config.ts index 35e08bb5cfe66..49f1d1fd5445e 100644 --- a/x-pack/plugins/actions/server/actions_config.ts +++ b/x-pack/plugins/actions/server/actions_config.ts @@ -76,7 +76,7 @@ function isAllowed({ allowedHosts }: ActionsConfig, hostname: string | null): bo function isHostnameAllowedInUri(config: ActionsConfig, uri: string): boolean { return pipe( - tryCatch(() => url.parse(uri)), + tryCatch(() => url.parse(uri, false /* parseQueryString */, true /* slashesDenoteHost */)), map((parsedUrl) => parsedUrl.hostname), mapNullable((hostname) => isAllowed(config, hostname)), getOrElse(() => false) diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/connector_token_client.mock.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/connector_token_client.mock.ts index 71d0a2f4466a4..4b38f6fcb53e6 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/connector_token_client.mock.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/connector_token_client.mock.ts @@ -14,6 +14,7 @@ const createConnectorTokenClientMock = () => { get: jest.fn(), update: jest.fn(), deleteConnectorTokens: jest.fn(), + updateOrReplace: jest.fn(), }; return mocked; }; diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/connector_token_client.test.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/connector_token_client.test.ts index e3e9f3b362ae9..54765b9e01b8f 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/connector_token_client.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/connector_token_client.test.ts @@ -357,3 +357,144 @@ describe('delete()', () => { `); }); }); + +describe('updateOrReplace()', () => { + test('creates new SO if current token is null', async () => { + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'connector_token', + attributes: { + connectorId: '123', + tokenType: 'access_token', + token: 'testtokenvalue', + expiresAt: new Date().toISOString(), + }, + references: [], + }); + await connectorTokenClient.updateOrReplace({ + connectorId: '1', + token: null, + newToken: 'newToken', + expiresInSec: 1000, + deleteExisting: false, + }); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1); + expect((unsecuredSavedObjectsClient.create.mock.calls[0][1] as ConnectorToken).token).toBe( + 'newToken' + ); + + expect(unsecuredSavedObjectsClient.find).not.toHaveBeenCalled(); + expect(unsecuredSavedObjectsClient.delete).not.toHaveBeenCalled(); + }); + + test('creates new SO and deletes all existing tokens for connector if current token is null and deleteExisting is true', async () => { + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'connector_token', + attributes: { + connectorId: '123', + tokenType: 'access_token', + token: 'testtokenvalue', + expiresAt: new Date().toISOString(), + }, + references: [], + }); + unsecuredSavedObjectsClient.find.mockResolvedValueOnce({ + total: 1, + per_page: 10, + page: 1, + saved_objects: [ + { + id: '1', + type: 'connector_token', + attributes: { + connectorId: '123', + tokenType: 'access_token', + createdAt: new Date().toISOString(), + expiresAt: new Date().toISOString(), + }, + score: 1, + references: [], + }, + { + id: '2', + type: 'connector_token', + attributes: { + connectorId: '123', + tokenType: 'access_token', + createdAt: new Date().toISOString(), + expiresAt: new Date().toISOString(), + }, + score: 1, + references: [], + }, + ], + }); + await connectorTokenClient.updateOrReplace({ + connectorId: '1', + token: null, + newToken: 'newToken', + expiresInSec: 1000, + deleteExisting: true, + }); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1); + expect((unsecuredSavedObjectsClient.create.mock.calls[0][1] as ConnectorToken).token).toBe( + 'newToken' + ); + + expect(unsecuredSavedObjectsClient.find).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.delete).toHaveBeenCalledTimes(2); + }); + + test('updates existing SO if current token exists', async () => { + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'connector_token', + attributes: { + connectorId: '123', + tokenType: 'access_token', + token: 'testtokenvalue', + createdAt: new Date().toISOString(), + }, + references: [], + }); + unsecuredSavedObjectsClient.checkConflicts.mockResolvedValueOnce({ + errors: [], + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'connector_token', + attributes: { + connectorId: '123', + tokenType: 'access_token', + token: 'testtokenvalue', + expiresAt: new Date().toISOString(), + }, + references: [], + }); + await connectorTokenClient.updateOrReplace({ + connectorId: '1', + token: { + id: '3', + connectorId: '123', + tokenType: 'access_token', + token: 'testtokenvalue', + createdAt: new Date().toISOString(), + expiresAt: new Date().toISOString(), + }, + newToken: 'newToken', + expiresInSec: 1000, + deleteExisting: true, + }); + + expect(unsecuredSavedObjectsClient.find).not.toHaveBeenCalled(); + expect(unsecuredSavedObjectsClient.delete).not.toHaveBeenCalled(); + + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.checkConflicts).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1); + expect((unsecuredSavedObjectsClient.create.mock.calls[0][1] as ConnectorToken).token).toBe( + 'newToken' + ); + }); +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/connector_token_client.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/connector_token_client.ts index 949ec855ee3f0..6ce91fad94546 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/connector_token_client.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/connector_token_client.ts @@ -33,6 +33,13 @@ export interface UpdateOptions { tokenType?: string; } +interface UpdateOrReplaceOptions { + connectorId: string; + token: ConnectorToken | null; + newToken: string; + expiresInSec: number; + deleteExisting: boolean; +} export class ConnectorTokenClient { private readonly logger: Logger; private readonly unsecuredSavedObjectsClient: SavedObjectsClientContract; @@ -245,4 +252,36 @@ export class ConnectorTokenClient { throw err; } } + + public async updateOrReplace({ + connectorId, + token, + newToken, + expiresInSec, + deleteExisting, + }: UpdateOrReplaceOptions) { + expiresInSec = expiresInSec ?? 3600; + if (token === null) { + if (deleteExisting) { + await this.deleteConnectorTokens({ + connectorId, + tokenType: 'access_token', + }); + } + + await this.create({ + connectorId, + token: newToken, + expiresAtMillis: new Date(Date.now() + expiresInSec * 1000).toISOString(), + tokenType: 'access_token', + }); + } else { + await this.update({ + id: token.id!.toString(), + token: newToken, + expiresAtMillis: new Date(Date.now() + expiresInSec * 1000).toISOString(), + tokenType: 'access_token', + }); + } + } } diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/create_jwt_assertion.test.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/create_jwt_assertion.test.ts index d68cc9a5e4398..d679564c82472 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/create_jwt_assertion.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/create_jwt_assertion.test.ts @@ -17,6 +17,8 @@ import { createJWTAssertion } from './create_jwt_assertion'; const jwtSign = jwt.sign as jest.Mock; const mockLogger = loggingSystemMock.create().get() as jest.Mocked; +Date.now = jest.fn(() => 0); + describe('createJWTAssertion', () => { test('creating a JWT token from provided claims with default values', () => { jwtSign.mockReturnValueOnce('123456qwertyjwttoken'); @@ -27,6 +29,28 @@ describe('createJWTAssertion', () => { subject: 'test@gmail.com', }); + expect(jwtSign).toHaveBeenCalledWith( + { aud: '1', exp: 3600, iat: 0, iss: 'someappid', sub: 'test@gmail.com' }, + { key: 'test', passphrase: '123456' }, + { algorithm: 'RS256' } + ); + expect(assertion).toMatchInlineSnapshot('"123456qwertyjwttoken"'); + }); + + test('creating a JWT token when private key password is null', () => { + jwtSign.mockReturnValueOnce('123456qwertyjwttoken'); + + const assertion = createJWTAssertion(mockLogger, 'test', null, { + audience: '1', + issuer: 'someappid', + subject: 'test@gmail.com', + }); + + expect(jwtSign).toHaveBeenCalledWith( + { aud: '1', exp: 3600, iat: 0, iss: 'someappid', sub: 'test@gmail.com' }, + 'test', + { algorithm: 'RS256' } + ); expect(assertion).toMatchInlineSnapshot('"123456qwertyjwttoken"'); }); diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/create_jwt_assertion.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/create_jwt_assertion.ts index f4723e59b418c..9dde4790c152d 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/create_jwt_assertion.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/create_jwt_assertion.ts @@ -12,18 +12,17 @@ export interface JWTClaims { audience: string; subject: string; issuer: string; - expireInMilisecons?: number; + expireInMilliseconds?: number; keyId?: string; } export function createJWTAssertion( logger: Logger, privateKey: string, - privateKeyPassword: string, - reservedClaims: JWTClaims, - customClaims?: Record + privateKeyPassword: string | null, + reservedClaims: JWTClaims ): string { - const { subject, audience, issuer, expireInMilisecons, keyId } = reservedClaims; + const { subject, audience, issuer, expireInMilliseconds, keyId } = reservedClaims; const iat = Math.floor(Date.now() / 1000); const headerObj = { algorithm: 'RS256' as Algorithm, ...(keyId ? { keyid: keyId } : {}) }; @@ -33,17 +32,18 @@ export function createJWTAssertion( aud: audience, // audience claim identifies the recipients that the JWT is intended for iss: issuer, // issuer claim identifies the principal that issued the JWT iat, // issued at claim identifies the time at which the JWT was issued - exp: iat + (expireInMilisecons ?? 3600), // expiration time claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing - ...(customClaims ?? {}), + exp: iat + (expireInMilliseconds ?? 3600), // expiration time claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing }; try { const jwtToken = jwt.sign( - JSON.stringify(payloadObj), - { - key: privateKey, - passphrase: privateKeyPassword, - }, + payloadObj, + privateKeyPassword + ? { + key: privateKey, + passphrase: privateKeyPassword, + } + : privateKey, headerObj ); return jwtToken; diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/get_oauth_client_credentials_access_token.test.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/get_oauth_client_credentials_access_token.test.ts new file mode 100644 index 0000000000000..2efa79cf09c48 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/get_oauth_client_credentials_access_token.test.ts @@ -0,0 +1,306 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { asyncForEach } from '@kbn/std'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { actionsConfigMock } from '../../actions_config.mock'; +import { connectorTokenClientMock } from './connector_token_client.mock'; +import { getOAuthClientCredentialsAccessToken } from './get_oauth_client_credentials_access_token'; +import { requestOAuthClientCredentialsToken } from './request_oauth_client_credentials_token'; + +jest.mock('./request_oauth_client_credentials_token', () => ({ + requestOAuthClientCredentialsToken: jest.fn(), +})); + +const logger = loggingSystemMock.create().get() as jest.Mocked; +const configurationUtilities = actionsConfigMock.create(); +const connectorTokenClient = connectorTokenClientMock.create(); + +describe('getOAuthClientCredentialsAccessToken', () => { + const getOAuthClientCredentialsAccessTokenOpts = { + connectorId: '123', + logger, + configurationUtilities, + credentials: { + config: { + clientId: 'clientId', + tenantId: 'tenantId', + }, + secrets: { + clientSecret: 'clientSecret', + }, + }, + oAuthScope: 'https://graph.microsoft.com/.default', + tokenUrl: 'https://login.microsoftonline.com/98765/oauth2/v2.0/token', + connectorTokenClient, + }; + + beforeEach(() => { + jest.resetAllMocks(); + jest.clearAllMocks(); + }); + + test('uses stored access token if it exists', async () => { + connectorTokenClient.get.mockResolvedValueOnce({ + hasErrors: false, + connectorToken: { + id: '1', + connectorId: '123', + tokenType: 'access_token', + token: 'testtokenvalue', + createdAt: new Date().toISOString(), + expiresAt: new Date(Date.now() + 10000000000).toISOString(), + }, + }); + const accessToken = await getOAuthClientCredentialsAccessToken( + getOAuthClientCredentialsAccessTokenOpts + ); + + expect(accessToken).toEqual('testtokenvalue'); + expect(requestOAuthClientCredentialsToken as jest.Mock).not.toHaveBeenCalled(); + }); + + test('creates new assertion if stored access token does not exist', async () => { + connectorTokenClient.get.mockResolvedValueOnce({ + hasErrors: false, + connectorToken: null, + }); + (requestOAuthClientCredentialsToken as jest.Mock).mockResolvedValueOnce({ + tokenType: 'access_token', + accessToken: 'brandnewaccesstoken', + expiresIn: 1000, + }); + + const accessToken = await getOAuthClientCredentialsAccessToken( + getOAuthClientCredentialsAccessTokenOpts + ); + + expect(accessToken).toEqual('access_token brandnewaccesstoken'); + expect(requestOAuthClientCredentialsToken as jest.Mock).toHaveBeenCalledWith( + 'https://login.microsoftonline.com/98765/oauth2/v2.0/token', + logger, + { + scope: 'https://graph.microsoft.com/.default', + clientId: 'clientId', + clientSecret: 'clientSecret', + }, + + configurationUtilities + ); + expect(connectorTokenClient.updateOrReplace).toHaveBeenCalledWith({ + connectorId: '123', + token: null, + newToken: 'access_token brandnewaccesstoken', + expiresInSec: 1000, + deleteExisting: false, + }); + }); + + test('creates new assertion if stored access token exists but is expired', async () => { + const createdAt = new Date().toISOString(); + const expiresAt = new Date(Date.now() - 100).toISOString(); + connectorTokenClient.get.mockResolvedValueOnce({ + hasErrors: false, + connectorToken: { + id: '1', + connectorId: '123', + tokenType: 'access_token', + token: 'testtokenvalue', + createdAt, + expiresAt, + }, + }); + (requestOAuthClientCredentialsToken as jest.Mock).mockResolvedValueOnce({ + tokenType: 'access_token', + accessToken: 'brandnewaccesstoken', + expiresIn: 1000, + }); + + const accessToken = await getOAuthClientCredentialsAccessToken( + getOAuthClientCredentialsAccessTokenOpts + ); + + expect(accessToken).toEqual('access_token brandnewaccesstoken'); + expect(requestOAuthClientCredentialsToken as jest.Mock).toHaveBeenCalledWith( + 'https://login.microsoftonline.com/98765/oauth2/v2.0/token', + logger, + { + scope: 'https://graph.microsoft.com/.default', + clientId: 'clientId', + clientSecret: 'clientSecret', + }, + + configurationUtilities + ); + expect(connectorTokenClient.updateOrReplace).toHaveBeenCalledWith({ + connectorId: '123', + token: { + id: '1', + connectorId: '123', + tokenType: 'access_token', + token: 'testtokenvalue', + createdAt, + expiresAt, + }, + newToken: 'access_token brandnewaccesstoken', + expiresInSec: 1000, + deleteExisting: false, + }); + }); + + test('returns null and logs warning if any required fields are missing', async () => { + await asyncForEach(['clientId', 'tenantId'], async (configField: string) => { + const accessToken = await getOAuthClientCredentialsAccessToken({ + ...getOAuthClientCredentialsAccessTokenOpts, + credentials: { + config: { + ...getOAuthClientCredentialsAccessTokenOpts.credentials.config, + [configField]: null, + }, + secrets: getOAuthClientCredentialsAccessTokenOpts.credentials.secrets, + }, + }); + expect(accessToken).toBeNull(); + expect(logger.warn).toHaveBeenCalledWith( + `Missing required fields for requesting OAuth Client Credentials access token` + ); + }); + + await asyncForEach(['clientSecret'], async (secretsField: string) => { + const accessToken = await getOAuthClientCredentialsAccessToken({ + ...getOAuthClientCredentialsAccessTokenOpts, + credentials: { + config: getOAuthClientCredentialsAccessTokenOpts.credentials.config, + secrets: { + ...getOAuthClientCredentialsAccessTokenOpts.credentials.secrets, + [secretsField]: null, + }, + }, + }); + expect(accessToken).toBeNull(); + expect(logger.warn).toHaveBeenCalledWith( + `Missing required fields for requesting OAuth Client Credentials access token` + ); + }); + }); + + test('throws error if requestOAuthClientCredentialsToken throws error', async () => { + connectorTokenClient.get.mockResolvedValueOnce({ + hasErrors: false, + connectorToken: null, + }); + (requestOAuthClientCredentialsToken as jest.Mock).mockRejectedValueOnce( + new Error('requestOAuthClientCredentialsToken error!!') + ); + + await expect( + getOAuthClientCredentialsAccessToken(getOAuthClientCredentialsAccessTokenOpts) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"requestOAuthClientCredentialsToken error!!"`); + }); + + test('logs warning if connectorTokenClient.updateOrReplace throws error', async () => { + connectorTokenClient.get.mockResolvedValueOnce({ + hasErrors: false, + connectorToken: null, + }); + (requestOAuthClientCredentialsToken as jest.Mock).mockResolvedValueOnce({ + tokenType: 'access_token', + accessToken: 'brandnewaccesstoken', + expiresIn: 1000, + }); + connectorTokenClient.updateOrReplace.mockRejectedValueOnce(new Error('updateOrReplace error')); + + const accessToken = await getOAuthClientCredentialsAccessToken( + getOAuthClientCredentialsAccessTokenOpts + ); + + expect(accessToken).toEqual('access_token brandnewaccesstoken'); + expect(logger.warn).toHaveBeenCalledWith( + `Not able to update connector token for connectorId: 123 due to error: updateOrReplace error` + ); + }); + + test('gets access token if connectorId is not provided', async () => { + (requestOAuthClientCredentialsToken as jest.Mock).mockResolvedValueOnce({ + tokenType: 'access_token', + accessToken: 'brandnewaccesstoken', + expiresIn: 1000, + }); + + const accessToken = await getOAuthClientCredentialsAccessToken({ + logger, + configurationUtilities, + credentials: { + config: { + clientId: 'clientId', + tenantId: 'tenantId', + }, + secrets: { + clientSecret: 'clientSecret', + }, + }, + oAuthScope: 'https://graph.microsoft.com/.default', + tokenUrl: 'https://login.microsoftonline.com/98765/oauth2/v2.0/token', + connectorTokenClient, + }); + + expect(connectorTokenClient.get).not.toHaveBeenCalled(); + expect(connectorTokenClient.updateOrReplace).not.toHaveBeenCalled(); + expect(accessToken).toEqual('access_token brandnewaccesstoken'); + expect(requestOAuthClientCredentialsToken as jest.Mock).toHaveBeenCalledWith( + 'https://login.microsoftonline.com/98765/oauth2/v2.0/token', + logger, + { + scope: 'https://graph.microsoft.com/.default', + clientId: 'clientId', + clientSecret: 'clientSecret', + }, + + configurationUtilities + ); + }); + + test('gets access token if connectorTokenClient is not provided', async () => { + (requestOAuthClientCredentialsToken as jest.Mock).mockResolvedValueOnce({ + tokenType: 'access_token', + accessToken: 'brandnewaccesstoken', + expiresIn: 1000, + }); + + const accessToken = await getOAuthClientCredentialsAccessToken({ + connectorId: '123', + logger, + configurationUtilities, + credentials: { + config: { + clientId: 'clientId', + tenantId: 'tenantId', + }, + secrets: { + clientSecret: 'clientSecret', + }, + }, + oAuthScope: 'https://graph.microsoft.com/.default', + tokenUrl: 'https://login.microsoftonline.com/98765/oauth2/v2.0/token', + }); + + expect(connectorTokenClient.get).not.toHaveBeenCalled(); + expect(connectorTokenClient.updateOrReplace).not.toHaveBeenCalled(); + expect(accessToken).toEqual('access_token brandnewaccesstoken'); + expect(requestOAuthClientCredentialsToken as jest.Mock).toHaveBeenCalledWith( + 'https://login.microsoftonline.com/98765/oauth2/v2.0/token', + logger, + { + scope: 'https://graph.microsoft.com/.default', + clientId: 'clientId', + clientSecret: 'clientSecret', + }, + + configurationUtilities + ); + }); +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/get_oauth_client_credentials_access_token.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/get_oauth_client_credentials_access_token.ts new file mode 100644 index 0000000000000..803cce2db7668 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/get_oauth_client_credentials_access_token.ts @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { Logger } from '@kbn/core/server'; +import { ActionsConfigurationUtilities } from '../../actions_config'; +import { ConnectorToken, ConnectorTokenClientContract } from '../../types'; +import { requestOAuthClientCredentialsToken } from './request_oauth_client_credentials_token'; + +export interface GetOAuthClientCredentialsConfig { + clientId: string; + tenantId: string; +} + +export interface GetOAuthClientCredentialsSecrets { + clientSecret: string; +} + +interface GetOAuthClientCredentialsAccessTokenOpts { + connectorId?: string; + tokenUrl: string; + oAuthScope: string; + logger: Logger; + configurationUtilities: ActionsConfigurationUtilities; + credentials: { + config: GetOAuthClientCredentialsConfig; + secrets: GetOAuthClientCredentialsSecrets; + }; + connectorTokenClient?: ConnectorTokenClientContract; +} + +export const getOAuthClientCredentialsAccessToken = async ({ + connectorId, + logger, + tokenUrl, + oAuthScope, + configurationUtilities, + credentials, + connectorTokenClient, +}: GetOAuthClientCredentialsAccessTokenOpts) => { + const { clientId, tenantId } = credentials.config; + const { clientSecret } = credentials.secrets; + + if (!clientId || !clientSecret || !tenantId) { + logger.warn(`Missing required fields for requesting OAuth Client Credentials access token`); + return null; + } + + let accessToken: string; + let connectorToken: ConnectorToken | null = null; + let hasErrors: boolean = false; + + if (connectorId && connectorTokenClient) { + // Check if there is a token stored for this connector + const { connectorToken: token, hasErrors: errors } = await connectorTokenClient.get({ + connectorId, + }); + connectorToken = token; + hasErrors = errors; + } + + if (connectorToken === null || Date.parse(connectorToken.expiresAt) <= Date.now()) { + // request access token with jwt assertion + const tokenResult = await requestOAuthClientCredentialsToken( + tokenUrl, + logger, + { + scope: oAuthScope, + clientId, + clientSecret, + }, + configurationUtilities + ); + accessToken = `${tokenResult.tokenType} ${tokenResult.accessToken}`; + + // try to update connector_token SO + if (connectorId && connectorTokenClient) { + try { + await connectorTokenClient.updateOrReplace({ + connectorId, + token: connectorToken, + newToken: accessToken, + expiresInSec: tokenResult.expiresIn, + deleteExisting: hasErrors, + }); + } catch (err) { + logger.warn( + `Not able to update connector token for connectorId: ${connectorId} due to error: ${err.message}` + ); + } + } + } else { + // use existing valid token + accessToken = connectorToken.token; + } + return accessToken; +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/get_oauth_jwt_access_token.test.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/get_oauth_jwt_access_token.test.ts new file mode 100644 index 0000000000000..b48456ddd2a8c --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/get_oauth_jwt_access_token.test.ts @@ -0,0 +1,350 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { asyncForEach } from '@kbn/std'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { actionsConfigMock } from '../../actions_config.mock'; +import { connectorTokenClientMock } from './connector_token_client.mock'; +import { getOAuthJwtAccessToken } from './get_oauth_jwt_access_token'; +import { createJWTAssertion } from './create_jwt_assertion'; +import { requestOAuthJWTToken } from './request_oauth_jwt_token'; + +jest.mock('./create_jwt_assertion', () => ({ + createJWTAssertion: jest.fn(), +})); +jest.mock('./request_oauth_jwt_token', () => ({ + requestOAuthJWTToken: jest.fn(), +})); + +const logger = loggingSystemMock.create().get() as jest.Mocked; +const configurationUtilities = actionsConfigMock.create(); +const connectorTokenClient = connectorTokenClientMock.create(); + +describe('getOAuthJwtAccessToken', () => { + const getOAuthJwtAccessTokenOpts = { + connectorId: '123', + logger, + configurationUtilities, + credentials: { + config: { + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'userIdentifierValue', + }, + secrets: { + clientSecret: 'clientSecret', + privateKey: 'privateKey', + privateKeyPassword: 'privateKeyPassword', + }, + }, + tokenUrl: 'https://dev23432523.service-now.com/oauth_token.do', + connectorTokenClient, + }; + + beforeEach(() => { + jest.resetAllMocks(); + jest.clearAllMocks(); + }); + + test('uses stored access token if it exists', async () => { + connectorTokenClient.get.mockResolvedValueOnce({ + hasErrors: false, + connectorToken: { + id: '1', + connectorId: '123', + tokenType: 'access_token', + token: 'testtokenvalue', + createdAt: new Date().toISOString(), + expiresAt: new Date(Date.now() + 10000000000).toISOString(), + }, + }); + const accessToken = await getOAuthJwtAccessToken(getOAuthJwtAccessTokenOpts); + + expect(accessToken).toEqual('testtokenvalue'); + expect(createJWTAssertion as jest.Mock).not.toHaveBeenCalled(); + expect(requestOAuthJWTToken as jest.Mock).not.toHaveBeenCalled(); + }); + + test('creates new assertion if stored access token does not exist', async () => { + connectorTokenClient.get.mockResolvedValueOnce({ + hasErrors: false, + connectorToken: null, + }); + (createJWTAssertion as jest.Mock).mockReturnValueOnce('newassertion'); + (requestOAuthJWTToken as jest.Mock).mockResolvedValueOnce({ + tokenType: 'access_token', + accessToken: 'brandnewaccesstoken', + expiresIn: 1000, + }); + + const accessToken = await getOAuthJwtAccessToken(getOAuthJwtAccessTokenOpts); + + expect(accessToken).toEqual('access_token brandnewaccesstoken'); + expect(createJWTAssertion as jest.Mock).toHaveBeenCalledWith( + logger, + 'privateKey', + 'privateKeyPassword', + { + audience: 'clientId', + issuer: 'clientId', + subject: 'userIdentifierValue', + keyId: 'jwtKeyId', + } + ); + expect(requestOAuthJWTToken as jest.Mock).toHaveBeenCalledWith( + 'https://dev23432523.service-now.com/oauth_token.do', + { clientId: 'clientId', clientSecret: 'clientSecret', assertion: 'newassertion' }, + logger, + configurationUtilities + ); + expect(connectorTokenClient.updateOrReplace).toHaveBeenCalledWith({ + connectorId: '123', + token: null, + newToken: 'access_token brandnewaccesstoken', + expiresInSec: 1000, + deleteExisting: false, + }); + }); + + test('creates new assertion if stored access token exists but is expired', async () => { + const createdAt = new Date().toISOString(); + const expiresAt = new Date(Date.now() - 100).toISOString(); + connectorTokenClient.get.mockResolvedValueOnce({ + hasErrors: false, + connectorToken: { + id: '1', + connectorId: '123', + tokenType: 'access_token', + token: 'testtokenvalue', + createdAt, + expiresAt, + }, + }); + (createJWTAssertion as jest.Mock).mockReturnValueOnce('newassertion'); + (requestOAuthJWTToken as jest.Mock).mockResolvedValueOnce({ + tokenType: 'access_token', + accessToken: 'brandnewaccesstoken', + expiresIn: 1000, + }); + + const accessToken = await getOAuthJwtAccessToken(getOAuthJwtAccessTokenOpts); + + expect(accessToken).toEqual('access_token brandnewaccesstoken'); + expect(createJWTAssertion as jest.Mock).toHaveBeenCalledWith( + logger, + 'privateKey', + 'privateKeyPassword', + { + audience: 'clientId', + issuer: 'clientId', + subject: 'userIdentifierValue', + keyId: 'jwtKeyId', + } + ); + expect(requestOAuthJWTToken as jest.Mock).toHaveBeenCalledWith( + 'https://dev23432523.service-now.com/oauth_token.do', + { clientId: 'clientId', clientSecret: 'clientSecret', assertion: 'newassertion' }, + logger, + configurationUtilities + ); + expect(connectorTokenClient.updateOrReplace).toHaveBeenCalledWith({ + connectorId: '123', + token: { + id: '1', + connectorId: '123', + tokenType: 'access_token', + token: 'testtokenvalue', + createdAt, + expiresAt, + }, + newToken: 'access_token brandnewaccesstoken', + expiresInSec: 1000, + deleteExisting: false, + }); + }); + + test('returns null and logs warning if any required fields are missing', async () => { + await asyncForEach( + ['clientId', 'jwtKeyId', 'userIdentifierValue'], + async (configField: string) => { + const accessToken = await getOAuthJwtAccessToken({ + ...getOAuthJwtAccessTokenOpts, + credentials: { + config: { ...getOAuthJwtAccessTokenOpts.credentials.config, [configField]: null }, + secrets: getOAuthJwtAccessTokenOpts.credentials.secrets, + }, + }); + expect(accessToken).toBeNull(); + expect(logger.warn).toHaveBeenCalledWith( + `Missing required fields for requesting OAuth JWT access token` + ); + } + ); + + await asyncForEach(['clientSecret', 'privateKey'], async (secretsField: string) => { + const accessToken = await getOAuthJwtAccessToken({ + ...getOAuthJwtAccessTokenOpts, + credentials: { + config: getOAuthJwtAccessTokenOpts.credentials.config, + secrets: { ...getOAuthJwtAccessTokenOpts.credentials.secrets, [secretsField]: null }, + }, + }); + expect(accessToken).toBeNull(); + expect(logger.warn).toHaveBeenCalledWith( + `Missing required fields for requesting OAuth JWT access token` + ); + }); + }); + + test('throws error if createJWTAssertion throws error', async () => { + connectorTokenClient.get.mockResolvedValueOnce({ + hasErrors: false, + connectorToken: null, + }); + (createJWTAssertion as jest.Mock).mockImplementationOnce(() => { + throw new Error('createJWTAssertion error!!'); + }); + + await expect( + getOAuthJwtAccessToken(getOAuthJwtAccessTokenOpts) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"createJWTAssertion error!!"`); + }); + + test('throws error if requestOAuthJWTToken throws error', async () => { + connectorTokenClient.get.mockResolvedValueOnce({ + hasErrors: false, + connectorToken: null, + }); + (createJWTAssertion as jest.Mock).mockReturnValueOnce('newassertion'); + (requestOAuthJWTToken as jest.Mock).mockRejectedValueOnce( + new Error('requestOAuthJWTToken error!!') + ); + + await expect( + getOAuthJwtAccessToken(getOAuthJwtAccessTokenOpts) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"requestOAuthJWTToken error!!"`); + }); + + test('logs warning if connectorTokenClient.updateOrReplace throws error', async () => { + connectorTokenClient.get.mockResolvedValueOnce({ + hasErrors: false, + connectorToken: null, + }); + (createJWTAssertion as jest.Mock).mockReturnValueOnce('newassertion'); + (requestOAuthJWTToken as jest.Mock).mockResolvedValueOnce({ + tokenType: 'access_token', + accessToken: 'brandnewaccesstoken', + expiresIn: 1000, + }); + connectorTokenClient.updateOrReplace.mockRejectedValueOnce(new Error('updateOrReplace error')); + + const accessToken = await getOAuthJwtAccessToken(getOAuthJwtAccessTokenOpts); + + expect(accessToken).toEqual('access_token brandnewaccesstoken'); + expect(logger.warn).toHaveBeenCalledWith( + `Not able to update connector token for connectorId: 123 due to error: updateOrReplace error` + ); + }); + + test('gets access token if connectorId is not provided', async () => { + (createJWTAssertion as jest.Mock).mockReturnValueOnce('newassertion'); + (requestOAuthJWTToken as jest.Mock).mockResolvedValueOnce({ + tokenType: 'access_token', + accessToken: 'brandnewaccesstoken', + expiresIn: 1000, + }); + + const accessToken = await getOAuthJwtAccessToken({ + logger, + configurationUtilities, + credentials: { + config: { + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'userIdentifierValue', + }, + secrets: { + clientSecret: 'clientSecret', + privateKey: 'privateKey', + privateKeyPassword: 'privateKeyPassword', + }, + }, + tokenUrl: 'https://dev23432523.service-now.com/oauth_token.do', + connectorTokenClient, + }); + + expect(connectorTokenClient.get).not.toHaveBeenCalled(); + expect(connectorTokenClient.updateOrReplace).not.toHaveBeenCalled(); + expect(accessToken).toEqual('access_token brandnewaccesstoken'); + expect(createJWTAssertion as jest.Mock).toHaveBeenCalledWith( + logger, + 'privateKey', + 'privateKeyPassword', + { + audience: 'clientId', + issuer: 'clientId', + subject: 'userIdentifierValue', + keyId: 'jwtKeyId', + } + ); + expect(requestOAuthJWTToken as jest.Mock).toHaveBeenCalledWith( + 'https://dev23432523.service-now.com/oauth_token.do', + { clientId: 'clientId', clientSecret: 'clientSecret', assertion: 'newassertion' }, + logger, + configurationUtilities + ); + }); + + test('gets access token if connectorTokenClient is not provided', async () => { + (createJWTAssertion as jest.Mock).mockReturnValueOnce('newassertion'); + (requestOAuthJWTToken as jest.Mock).mockResolvedValueOnce({ + tokenType: 'access_token', + accessToken: 'brandnewaccesstoken', + expiresIn: 1000, + }); + + const accessToken = await getOAuthJwtAccessToken({ + connectorId: '123', + logger, + configurationUtilities, + credentials: { + config: { + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'userIdentifierValue', + }, + secrets: { + clientSecret: 'clientSecret', + privateKey: 'privateKey', + privateKeyPassword: 'privateKeyPassword', + }, + }, + tokenUrl: 'https://dev23432523.service-now.com/oauth_token.do', + }); + + expect(connectorTokenClient.get).not.toHaveBeenCalled(); + expect(connectorTokenClient.updateOrReplace).not.toHaveBeenCalled(); + expect(accessToken).toEqual('access_token brandnewaccesstoken'); + expect(createJWTAssertion as jest.Mock).toHaveBeenCalledWith( + logger, + 'privateKey', + 'privateKeyPassword', + { + audience: 'clientId', + issuer: 'clientId', + subject: 'userIdentifierValue', + keyId: 'jwtKeyId', + } + ); + expect(requestOAuthJWTToken as jest.Mock).toHaveBeenCalledWith( + 'https://dev23432523.service-now.com/oauth_token.do', + { clientId: 'clientId', clientSecret: 'clientSecret', assertion: 'newassertion' }, + logger, + configurationUtilities + ); + }); +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/get_oauth_jwt_access_token.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/get_oauth_jwt_access_token.ts new file mode 100644 index 0000000000000..a4867d99556e7 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/get_oauth_jwt_access_token.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 } from '@kbn/core/server'; +import { ActionsConfigurationUtilities } from '../../actions_config'; +import { ConnectorToken, ConnectorTokenClientContract } from '../../types'; +import { createJWTAssertion } from './create_jwt_assertion'; +import { requestOAuthJWTToken } from './request_oauth_jwt_token'; + +export interface GetOAuthJwtConfig { + clientId: string; + jwtKeyId: string; + userIdentifierValue: string; +} + +export interface GetOAuthJwtSecrets { + clientSecret: string; + privateKey: string; + privateKeyPassword: string | null; +} + +interface GetOAuthJwtAccessTokenOpts { + connectorId?: string; + tokenUrl: string; + logger: Logger; + configurationUtilities: ActionsConfigurationUtilities; + credentials: { + config: GetOAuthJwtConfig; + secrets: GetOAuthJwtSecrets; + }; + connectorTokenClient?: ConnectorTokenClientContract; +} + +export const getOAuthJwtAccessToken = async ({ + connectorId, + logger, + tokenUrl, + configurationUtilities, + credentials, + connectorTokenClient, +}: GetOAuthJwtAccessTokenOpts) => { + const { clientId, jwtKeyId, userIdentifierValue } = credentials.config; + const { clientSecret, privateKey, privateKeyPassword } = credentials.secrets; + + if (!clientId || !clientSecret || !jwtKeyId || !privateKey || !userIdentifierValue) { + logger.warn(`Missing required fields for requesting OAuth JWT access token`); + return null; + } + + let accessToken: string; + let connectorToken: ConnectorToken | null = null; + let hasErrors: boolean = false; + + if (connectorId && connectorTokenClient) { + // Check if there is a token stored for this connector + const { connectorToken: token, hasErrors: errors } = await connectorTokenClient.get({ + connectorId, + }); + connectorToken = token; + hasErrors = errors; + } + + if (connectorToken === null || Date.parse(connectorToken.expiresAt) <= Date.now()) { + // generate a new assertion + const assertion = createJWTAssertion(logger, privateKey, privateKeyPassword, { + audience: clientId, + issuer: clientId, + subject: userIdentifierValue, + keyId: jwtKeyId, + }); + + // request access token with jwt assertion + const tokenResult = await requestOAuthJWTToken( + tokenUrl, + { + clientId, + clientSecret, + assertion, + }, + logger, + configurationUtilities + ); + accessToken = `${tokenResult.tokenType} ${tokenResult.accessToken}`; + + // try to update connector_token SO + if (connectorId && connectorTokenClient) { + try { + await connectorTokenClient.updateOrReplace({ + connectorId, + token: connectorToken, + newToken: accessToken, + expiresInSec: tokenResult.expiresIn, + deleteExisting: hasErrors, + }); + } catch (err) { + logger.warn( + `Not able to update connector token for connectorId: ${connectorId} due to error: ${err.message}` + ); + } + } + } else { + // use existing valid token + accessToken = connectorToken.token; + } + return accessToken; +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.test.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.test.ts index 8c36113032461..fbf0d90541659 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.test.ts @@ -12,8 +12,8 @@ jest.mock('nodemailer', () => ({ jest.mock('./send_email_graph_api', () => ({ sendEmailGraphApi: jest.fn(), })); -jest.mock('./request_oauth_client_credentials_token', () => ({ - requestOAuthClientCredentialsToken: jest.fn(), +jest.mock('./get_oauth_client_credentials_access_token', () => ({ + getOAuthClientCredentialsAccessToken: jest.fn(), })); import { Logger } from '@kbn/core/server'; @@ -24,10 +24,9 @@ import { ProxySettings } from '../../types'; import { actionsConfigMock } from '../../actions_config.mock'; import { CustomHostSettings } from '../../config'; import { sendEmailGraphApi } from './send_email_graph_api'; -import { requestOAuthClientCredentialsToken } from './request_oauth_client_credentials_token'; +import { getOAuthClientCredentialsAccessToken } from './get_oauth_client_credentials_access_token'; import { ConnectorTokenClient } from './connector_token_client'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; -import { connectorTokenClientMock } from './connector_token_client.mock'; const createTransportMock = nodemailer.createTransport as jest.Mock; const sendMailMockResult = { result: 'does not matter' }; @@ -92,314 +91,38 @@ describe('send_email module', () => { test('uses OAuth 2.0 Client Credentials authentication for email using "exchange_server" service', async () => { const sendEmailGraphApiMock = sendEmailGraphApi as jest.Mock; - const requestOAuthClientCredentialsTokenMock = requestOAuthClientCredentialsToken as jest.Mock; + const getOAuthClientCredentialsAccessTokenMock = + getOAuthClientCredentialsAccessToken as jest.Mock; const sendEmailOptions = getSendEmailOptions({ transport: { service: 'exchange_server', clientId: '123456', + tenantId: '98765', clientSecret: 'sdfhkdsjhfksdjfh', }, }); - requestOAuthClientCredentialsTokenMock.mockReturnValueOnce({ - tokenType: 'Bearer', - accessToken: 'dfjsdfgdjhfgsjdf', - expiresIn: 123, - }); + getOAuthClientCredentialsAccessTokenMock.mockReturnValueOnce(`Bearer dfjsdfgdjhfgsjdf`); const date = new Date(); date.setDate(date.getDate() + 5); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'connector_token', - references: [], - attributes: { - connectorId: '123', - expiresAt: date.toISOString(), - tokenType: 'access_token', - token: '11111111', - }, - }); - sendEmailGraphApiMock.mockReturnValue({ status: 202, }); - unsecuredSavedObjectsClient.find.mockResolvedValueOnce({ - total: 0, - saved_objects: [], - per_page: 500, - page: 1, - }); await sendEmail(mockLogger, sendEmailOptions, connectorTokenClient); - requestOAuthClientCredentialsTokenMock.mock.calls[0].pop(); - expect(requestOAuthClientCredentialsTokenMock.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "https://login.microsoftonline.com/undefined/oauth2/v2.0/token", - Object { - "context": Array [], - "debug": [MockFunction], - "error": [MockFunction], - "fatal": [MockFunction], - "get": [MockFunction], - "info": [MockFunction], - "log": [MockFunction], - "trace": [MockFunction], - "warn": [MockFunction], - }, - Object { - "clientId": "123456", - "clientSecret": "sdfhkdsjhfksdjfh", - "scope": "https://graph.microsoft.com/.default", - }, - ] - `); - - delete sendEmailGraphApiMock.mock.calls[0][0].options.configurationUtilities; - sendEmailGraphApiMock.mock.calls[0].pop(); - expect(sendEmailGraphApiMock.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "graphApiUrl": undefined, - "headers": Object { - "Authorization": "Bearer dfjsdfgdjhfgsjdf", - "Content-Type": "application/json", - }, - "messageHTML": "

a message

- ", - "options": Object { - "connectorId": "1", - "content": Object { - "message": "a message", - "subject": "a subject", - }, - "hasAuth": true, - "routing": Object { - "bcc": Array [], - "cc": Array [ - "bob@example.com", - "robert@example.com", - ], - "from": "fred@example.com", - "to": Array [ - "jim@example.com", - ], - }, - "transport": Object { - "clientId": "123456", - "clientSecret": "sdfhkdsjhfksdjfh", - "password": "changeme", - "service": "exchange_server", - "user": "elastic", - }, - }, - }, - Object { - "context": Array [], - "debug": [MockFunction], - "error": [MockFunction], - "fatal": [MockFunction], - "get": [MockFunction], - "info": [MockFunction], - "log": [MockFunction], - "trace": [MockFunction], - "warn": [MockFunction], - }, - ] - `); - - expect(unsecuredSavedObjectsClient.create.mock.calls.length).toBe(1); - }); - - test('uses existing "access_token" from "connector_token" SO for authentication for email using "exchange_server" service', async () => { - const sendEmailGraphApiMock = sendEmailGraphApi as jest.Mock; - const requestOAuthClientCredentialsTokenMock = requestOAuthClientCredentialsToken as jest.Mock; - const sendEmailOptions = getSendEmailOptions({ - transport: { - service: 'exchange_server', - clientId: '123456', - clientSecret: 'sdfhkdsjhfksdjfh', - }, - }); - requestOAuthClientCredentialsTokenMock.mockReturnValueOnce({ - tokenType: 'Bearer', - accessToken: 'dfjsdfgdjhfgsjdf', - expiresIn: 123, - }); - - sendEmailGraphApiMock.mockReturnValue({ - status: 202, - }); - const date = new Date(); - date.setDate(date.getDate() + 5); - - unsecuredSavedObjectsClient.checkConflicts.mockResolvedValueOnce({ - errors: [], - }); - unsecuredSavedObjectsClient.find.mockResolvedValueOnce({ - total: 2, - saved_objects: [ - { - id: '1', - score: 1, - type: 'connector_token', - references: [], - attributes: { - connectorId: '123', - expiresAt: date.toISOString(), - tokenType: 'access_token', - }, - }, - ], - per_page: 500, - page: 1, - }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'connector_token', - references: [], - attributes: { - token: '11111111', + expect(getOAuthClientCredentialsAccessTokenMock).toHaveBeenCalledWith({ + configurationUtilities: sendEmailOptions.configurationUtilities, + connectorId: '1', + connectorTokenClient, + credentials: { + config: { clientId: '123456', tenantId: '98765' }, + secrets: { clientSecret: 'sdfhkdsjhfksdjfh' }, }, + logger: mockLogger, + oAuthScope: 'https://graph.microsoft.com/.default', + tokenUrl: 'https://login.microsoftonline.com/98765/oauth2/v2.0/token', }); - await sendEmail(mockLogger, sendEmailOptions, connectorTokenClient); - expect(requestOAuthClientCredentialsTokenMock.mock.calls.length).toBe(0); - - delete sendEmailGraphApiMock.mock.calls[0][0].options.configurationUtilities; - sendEmailGraphApiMock.mock.calls[0].pop(); - expect(sendEmailGraphApiMock.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "graphApiUrl": undefined, - "headers": Object { - "Authorization": "11111111", - "Content-Type": "application/json", - }, - "messageHTML": "

a message

- ", - "options": Object { - "connectorId": "1", - "content": Object { - "message": "a message", - "subject": "a subject", - }, - "hasAuth": true, - "routing": Object { - "bcc": Array [], - "cc": Array [ - "bob@example.com", - "robert@example.com", - ], - "from": "fred@example.com", - "to": Array [ - "jim@example.com", - ], - }, - "transport": Object { - "clientId": "123456", - "clientSecret": "sdfhkdsjhfksdjfh", - "password": "changeme", - "service": "exchange_server", - "user": "elastic", - }, - }, - }, - Object { - "context": Array [], - "debug": [MockFunction], - "error": [MockFunction], - "fatal": [MockFunction], - "get": [MockFunction], - "info": [MockFunction], - "log": [MockFunction], - "trace": [MockFunction], - "warn": [MockFunction], - }, - ] - `); - - expect(unsecuredSavedObjectsClient.create.mock.calls.length).toBe(0); - }); - - test('request the new token and update existing "access_token" when it is expired for "exchange_server" email service', async () => { - const sendEmailGraphApiMock = sendEmailGraphApi as jest.Mock; - const requestOAuthClientCredentialsTokenMock = requestOAuthClientCredentialsToken as jest.Mock; - const sendEmailOptions = getSendEmailOptions({ - transport: { - service: 'exchange_server', - clientId: '123456', - clientSecret: 'sdfhkdsjhfksdjfh', - }, - }); - requestOAuthClientCredentialsTokenMock.mockReturnValueOnce({ - tokenType: 'Bearer', - accessToken: 'dfjsdfgdjhfgsjdf', - expiresIn: 123, - }); - - sendEmailGraphApiMock.mockReturnValue({ - status: 202, - }); - const date = new Date(); - date.setDate(date.getDate() - 5); - - unsecuredSavedObjectsClient.find.mockResolvedValueOnce({ - total: 2, - saved_objects: [ - { - id: '1', - score: 1, - type: 'connector_token', - references: [], - attributes: { - connectorId: '123', - expiresAt: date.toISOString(), - tokenType: 'access_token', - }, - }, - ], - per_page: 500, - page: 1, - }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'connector_token', - references: [], - attributes: { - token: '11111111', - }, - }); - - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'connector_token', - references: [], - attributes: { - connectorId: '123', - expiresAt: date.toISOString(), - tokenType: 'access_token', - token: '11111111', - }, - }); - unsecuredSavedObjectsClient.checkConflicts.mockResolvedValueOnce({ - errors: [], - }); - - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'connector_token', - references: [], - attributes: { - connectorId: '123', - expiresAt: date.toISOString(), - tokenType: 'access_token', - token: '11111111', - }, - }); - - await sendEmail(mockLogger, sendEmailOptions, connectorTokenClient); - expect(requestOAuthClientCredentialsTokenMock.mock.calls.length).toBe(1); - delete sendEmailGraphApiMock.mock.calls[0][0].options.configurationUtilities; sendEmailGraphApiMock.mock.calls[0].pop(); expect(sendEmailGraphApiMock.mock.calls[0]).toMatchInlineSnapshot(` @@ -435,6 +158,7 @@ describe('send_email module', () => { "clientSecret": "sdfhkdsjhfksdjfh", "password": "changeme", "service": "exchange_server", + "tenantId": "98765", "user": "elastic", }, }, @@ -452,209 +176,42 @@ describe('send_email module', () => { }, ] `); - - expect(unsecuredSavedObjectsClient.create.mock.calls.length).toBe(1); }); - test('sending email for "exchange_server" wont fail if connectorTokenClient throw the errors, just log warning message', async () => { + test('throws error if null access token returned when using OAuth 2.0 Client Credentials authentication', async () => { const sendEmailGraphApiMock = sendEmailGraphApi as jest.Mock; - const requestOAuthClientCredentialsTokenMock = requestOAuthClientCredentialsToken as jest.Mock; + const getOAuthClientCredentialsAccessTokenMock = + getOAuthClientCredentialsAccessToken as jest.Mock; const sendEmailOptions = getSendEmailOptions({ transport: { service: 'exchange_server', clientId: '123456', + tenantId: '98765', clientSecret: 'sdfhkdsjhfksdjfh', }, }); - requestOAuthClientCredentialsTokenMock.mockReturnValueOnce({ - tokenType: 'Bearer', - accessToken: 'dfjsdfgdjhfgsjdf', - expiresIn: 123, - }); - - sendEmailGraphApiMock.mockReturnValue({ - status: 202, - }); - const date = new Date(); - date.setDate(date.getDate() + 5); - - unsecuredSavedObjectsClient.find.mockResolvedValueOnce({ - total: 0, - saved_objects: [], - per_page: 500, - page: 1, - }); - unsecuredSavedObjectsClient.create.mockRejectedValueOnce(new Error('Fail')); - - await sendEmail(mockLogger, sendEmailOptions, connectorTokenClient); - expect(requestOAuthClientCredentialsTokenMock.mock.calls.length).toBe(1); - expect(unsecuredSavedObjectsClient.create.mock.calls.length).toBe(1); - expect(mockLogger.warn.mock.calls[0]).toMatchObject([ - `Not able to update connector token for connectorId: 1 due to error: Fail`, - ]); + getOAuthClientCredentialsAccessTokenMock.mockReturnValueOnce(null); - delete sendEmailGraphApiMock.mock.calls[0][0].options.configurationUtilities; - sendEmailGraphApiMock.mock.calls[0].pop(); - expect(sendEmailGraphApiMock.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "graphApiUrl": undefined, - "headers": Object { - "Authorization": "Bearer dfjsdfgdjhfgsjdf", - "Content-Type": "application/json", - }, - "messageHTML": "

a message

- ", - "options": Object { - "connectorId": "1", - "content": Object { - "message": "a message", - "subject": "a subject", - }, - "hasAuth": true, - "routing": Object { - "bcc": Array [], - "cc": Array [ - "bob@example.com", - "robert@example.com", - ], - "from": "fred@example.com", - "to": Array [ - "jim@example.com", - ], - }, - "transport": Object { - "clientId": "123456", - "clientSecret": "sdfhkdsjhfksdjfh", - "password": "changeme", - "service": "exchange_server", - "user": "elastic", - }, - }, - }, - Object { - "context": Array [], - "debug": [MockFunction], - "error": [MockFunction] { - "calls": Array [ - Array [ - "Failed to create connector_token for connectorId \\"1\\" and tokenType: \\"access_token\\". Error: Fail", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, - "fatal": [MockFunction], - "get": [MockFunction], - "info": [MockFunction], - "log": [MockFunction], - "trace": [MockFunction], - "warn": [MockFunction] { - "calls": Array [ - Array [ - "Not able to update connector token for connectorId: 1 due to error: Fail", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ] - `); - }); + await expect(() => + sendEmail(mockLogger, sendEmailOptions, connectorTokenClient) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unable to retrieve access token for connectorId: 1"` + ); - test('delete duplication tokens if connectorTokenClient get method has the errors, like decription error', async () => { - const sendEmailGraphApiMock = sendEmailGraphApi as jest.Mock; - const requestOAuthClientCredentialsTokenMock = requestOAuthClientCredentialsToken as jest.Mock; - const sendEmailOptions = getSendEmailOptions({ - transport: { - service: 'exchange_server', - clientId: '123456', - clientSecret: 'sdfhkdsjhfksdjfh', + expect(getOAuthClientCredentialsAccessTokenMock).toHaveBeenCalledWith({ + configurationUtilities: sendEmailOptions.configurationUtilities, + connectorId: '1', + connectorTokenClient, + credentials: { + config: { clientId: '123456', tenantId: '98765' }, + secrets: { clientSecret: 'sdfhkdsjhfksdjfh' }, }, + logger: mockLogger, + oAuthScope: 'https://graph.microsoft.com/.default', + tokenUrl: 'https://login.microsoftonline.com/98765/oauth2/v2.0/token', }); - requestOAuthClientCredentialsTokenMock.mockReturnValueOnce({ - tokenType: 'Bearer', - accessToken: 'dfjsdfgdjhfgsjdf', - expiresIn: 123, - }); - - sendEmailGraphApiMock.mockReturnValue({ - status: 202, - }); - const date = new Date(); - date.setDate(date.getDate() + 5); - - const connectorTokenClientM = connectorTokenClientMock.create(); - connectorTokenClientM.get.mockResolvedValueOnce({ - hasErrors: true, - connectorToken: null, - }); - - await sendEmail(mockLogger, sendEmailOptions, connectorTokenClientM); - expect(requestOAuthClientCredentialsTokenMock.mock.calls.length).toBe(1); - expect(connectorTokenClientM.deleteConnectorTokens.mock.calls.length).toBe(1); - delete sendEmailGraphApiMock.mock.calls[0][0].options.configurationUtilities; - sendEmailGraphApiMock.mock.calls[0].pop(); - expect(sendEmailGraphApiMock.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "graphApiUrl": undefined, - "headers": Object { - "Authorization": "Bearer dfjsdfgdjhfgsjdf", - "Content-Type": "application/json", - }, - "messageHTML": "

a message

- ", - "options": Object { - "connectorId": "1", - "content": Object { - "message": "a message", - "subject": "a subject", - }, - "hasAuth": true, - "routing": Object { - "bcc": Array [], - "cc": Array [ - "bob@example.com", - "robert@example.com", - ], - "from": "fred@example.com", - "to": Array [ - "jim@example.com", - ], - }, - "transport": Object { - "clientId": "123456", - "clientSecret": "sdfhkdsjhfksdjfh", - "password": "changeme", - "service": "exchange_server", - "user": "elastic", - }, - }, - }, - Object { - "context": Array [], - "debug": [MockFunction], - "error": [MockFunction], - "fatal": [MockFunction], - "get": [MockFunction], - "info": [MockFunction], - "log": [MockFunction], - "trace": [MockFunction], - "warn": [MockFunction], - }, - ] - `); + expect(sendEmailGraphApiMock).not.toHaveBeenCalled(); }); test('handles unauthenticated email using not secure host/port', async () => { diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts index a664b22f9df34..f2b059e51e0d6 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts @@ -14,9 +14,9 @@ import { ActionsConfigurationUtilities } from '../../actions_config'; import { CustomHostSettings } from '../../config'; import { getNodeSSLOptions, getSSLSettingsFromConfig } from './get_node_ssl_options'; import { sendEmailGraphApi } from './send_email_graph_api'; -import { requestOAuthClientCredentialsToken } from './request_oauth_client_credentials_token'; import { ConnectorTokenClientContract, ProxySettings } from '../../types'; import { AdditionalEmailServices } from '../../../common'; +import { getOAuthClientCredentialsAccessToken } from './get_oauth_client_credentials_access_token'; // an email "service" which doesn't actually send, just returns what it would send export const JSON_TRANSPORT_SERVICE = '__json'; @@ -86,58 +86,28 @@ async function sendEmailWithExchange( const { transport, configurationUtilities, connectorId } = options; const { clientId, clientSecret, tenantId, oauthTokenUrl } = transport; - let accessToken: string; - - const { connectorToken, hasErrors } = await connectorTokenClient.get({ connectorId }); - if (connectorToken === null || Date.parse(connectorToken.expiresAt) <= Date.now()) { - // request new access token for microsoft exchange online server with Graph API scope - const tokenResult = await requestOAuthClientCredentialsToken( - oauthTokenUrl ?? `${EXCHANGE_ONLINE_SERVER_HOST}/${tenantId}/oauth2/v2.0/token`, - logger, - { - scope: GRAPH_API_OAUTH_SCOPE, - clientId, - clientSecret, + const accessToken = await getOAuthClientCredentialsAccessToken({ + connectorId, + logger, + configurationUtilities, + credentials: { + config: { + clientId: clientId as string, + tenantId: tenantId as string, + }, + secrets: { + clientSecret: clientSecret as string, }, - configurationUtilities - ); - accessToken = `${tokenResult.tokenType} ${tokenResult.accessToken}`; + }, + oAuthScope: GRAPH_API_OAUTH_SCOPE, + tokenUrl: oauthTokenUrl ?? `${EXCHANGE_ONLINE_SERVER_HOST}/${tenantId}/oauth2/v2.0/token`, + connectorTokenClient, + }); - // try to update connector_token SO - try { - if (connectorToken === null) { - if (hasErrors) { - // delete existing access tokens - await connectorTokenClient.deleteConnectorTokens({ - connectorId, - tokenType: 'access_token', - }); - } - await connectorTokenClient.create({ - connectorId, - token: accessToken, - // convert MS Exchange expiresIn from seconds to milliseconds - expiresAtMillis: new Date(Date.now() + tokenResult.expiresIn * 1000).toISOString(), - tokenType: 'access_token', - }); - } else { - await connectorTokenClient.update({ - id: connectorToken.id!.toString(), - token: accessToken, - // convert MS Exchange expiresIn from seconds to milliseconds - expiresAtMillis: new Date(Date.now() + tokenResult.expiresIn * 1000).toISOString(), - tokenType: 'access_token', - }); - } - } catch (err) { - logger.warn( - `Not able to update connector token for connectorId: ${connectorId} due to error: ${err.message}` - ); - } - } else { - // use existing valid token - accessToken = connectorToken.token; + if (!accessToken) { + throw new Error(`Unable to retrieve access token for connectorId: ${connectorId}`); } + const headers = { 'Content-Type': 'application/json', Authorization: accessToken, diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/create_service_wrapper.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/create_service_wrapper.test.ts new file mode 100644 index 0000000000000..37ada260c75a9 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/create_service_wrapper.test.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import axios from 'axios'; +import { createServiceWrapper } from './create_service_wrapper'; +import { Logger } from '@kbn/core/server'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { actionsConfigMock } from '../../actions_config.mock'; +import { connectorTokenClientMock } from '../lib/connector_token_client.mock'; +import { snExternalServiceConfig } from './config'; + +const logger = loggingSystemMock.create().get() as jest.Mocked; +const connectorTokenClient = connectorTokenClientMock.create(); +const configurationUtilities = actionsConfigMock.create(); + +jest.mock('axios'); +axios.create = jest.fn(() => axios); + +describe('createServiceWrapper', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('creates axios instance with apiUrl', () => { + const createServiceFn = jest.fn(); + const credentials = { + config: { + apiUrl: 'https://test-sn.service-now.com', + }, + secrets: { + username: 'username', + password: 'password', + }, + }; + const serviceConfig = snExternalServiceConfig['.servicenow']; + createServiceWrapper({ + connectorId: '123', + credentials, + logger, + configurationUtilities, + serviceConfig, + connectorTokenClient, + createServiceFn, + }); + + expect(createServiceFn).toHaveBeenCalledWith({ + credentials, + logger, + configurationUtilities, + serviceConfig, + axiosInstance: axios, + }); + }); + + test('handles apiUrl with trailing slash', () => { + const createServiceFn = jest.fn(); + const credentials = { + config: { + apiUrl: 'https://test-sn.service-now.com/', + }, + secrets: { + username: 'username', + password: 'password', + }, + }; + const serviceConfig = snExternalServiceConfig['.servicenow']; + createServiceWrapper({ + connectorId: '123', + credentials, + logger, + configurationUtilities, + serviceConfig, + connectorTokenClient, + createServiceFn, + }); + + expect(createServiceFn).toHaveBeenCalledWith({ + credentials, + logger, + configurationUtilities, + serviceConfig, + axiosInstance: axios, + }); + }); +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/create_service_wrapper.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/create_service_wrapper.ts new file mode 100644 index 0000000000000..cd431027a720f --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/create_service_wrapper.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Logger } from '@kbn/core/server'; +import { ExternalService, ExternalServiceCredentials, SNProductsConfigValue } from './types'; + +import { ServiceNowPublicConfigurationType, ServiceFactory } from './types'; +import { ActionsConfigurationUtilities } from '../../actions_config'; +import { getAxiosInstance } from './utils'; +import { ConnectorTokenClientContract } from '../../types'; + +interface CreateServiceWrapperOpts { + connectorId: string; + credentials: ExternalServiceCredentials; + logger: Logger; + configurationUtilities: ActionsConfigurationUtilities; + serviceConfig: SNProductsConfigValue; + connectorTokenClient: ConnectorTokenClientContract; + createServiceFn: ServiceFactory; +} + +export function createServiceWrapper({ + connectorId, + credentials, + logger, + configurationUtilities, + serviceConfig, + connectorTokenClient, + createServiceFn, +}: CreateServiceWrapperOpts): T { + const { config } = credentials; + const { apiUrl: url } = config as ServiceNowPublicConfigurationType; + const urlWithoutTrailingSlash = url.endsWith('/') ? url.slice(0, -1) : url; + const axiosInstance = getAxiosInstance({ + connectorId, + logger, + configurationUtilities, + credentials, + snServiceUrl: urlWithoutTrailingSlash, + connectorTokenClient, + }); + + return createServiceFn({ + credentials, + logger, + configurationUtilities, + serviceConfig, + axiosInstance, + }); +} diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.ts index e22c65a3694bb..17a3b1982f487 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.ts @@ -39,6 +39,7 @@ import { ExternalServiceApiITOM, ExternalServiceITOM, ServiceNowPublicConfigurationBaseType, + ExternalService, } from './types'; import { ServiceNowITOMActionTypeId, @@ -53,6 +54,7 @@ import { apiSIR } from './api_sir'; import { throwIfSubActionIsNotSupported } from './utils'; import { createExternalServiceITOM } from './service_itom'; import { apiITOM } from './api_itom'; +import { createServiceWrapper } from './create_service_wrapper'; export { ServiceNowITSMActionTypeId, @@ -97,6 +99,7 @@ export function getServiceNowITSMActionType( secrets: schema.object(ExternalIncidentServiceSecretConfiguration, { validate: curry(validate.secrets)(configurationUtilities), }), + connector: validate.connector, params: ExecutorParamsSchemaITSM, }, executor: curry(executor)({ @@ -124,6 +127,7 @@ export function getServiceNowSIRActionType( secrets: schema.object(ExternalIncidentServiceSecretConfiguration, { validate: curry(validate.secrets)(configurationUtilities), }), + connector: validate.connector, params: ExecutorParamsSchemaSIR, }, executor: curry(executor)({ @@ -151,6 +155,7 @@ export function getServiceNowITOMActionType( secrets: schema.object(ExternalIncidentServiceSecretConfiguration, { validate: curry(validate.secrets)(configurationUtilities), }), + connector: validate.connector, params: ExecutorParamsSchemaITOM, }, executor: curry(executorITOM)({ @@ -184,20 +189,24 @@ async function executor( ExecutorParams > ): Promise> { - const { actionId, config, params, secrets } = execOptions; + const { actionId, config, params, secrets, services } = execOptions; const { subAction, subActionParams } = params; + const connectorTokenClient = services.connectorTokenClient; const externalServiceConfig = snExternalServiceConfig[actionTypeId]; let data: ServiceNowExecutorResultData | null = null; - const externalService = createService( - { + const externalService = createServiceWrapper({ + connectorId: actionId, + credentials: { config, secrets, }, logger, configurationUtilities, - externalServiceConfig - ); + serviceConfig: externalServiceConfig, + connectorTokenClient, + createServiceFn: createService, + }); const apiAsRecord = api as unknown as Record; throwIfSubActionIsNotSupported({ api: apiAsRecord, subAction, supportedSubActions, logger }); @@ -260,18 +269,22 @@ async function executorITOM( ): Promise> { const { actionId, config, params, secrets } = execOptions; const { subAction, subActionParams } = params; + const connectorTokenClient = execOptions.services.connectorTokenClient; const externalServiceConfig = snExternalServiceConfig[actionTypeId]; let data: ServiceNowExecutorResultData | null = null; - const externalService = createService( - { + const externalService = createServiceWrapper({ + connectorId: actionId, + credentials: { config, secrets, }, logger, configurationUtilities, - externalServiceConfig - ) as ExternalServiceITOM; + serviceConfig: externalServiceConfig, + connectorTokenClient, + createServiceFn: createService, + }); const apiAsRecord = api as unknown as Record; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts index e41eea24834c7..5f5ea6ab0ff93 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts @@ -10,6 +10,10 @@ import { DEFAULT_ALERTS_GROUPING_KEY } from './config'; export const ExternalIncidentServiceConfigurationBase = { apiUrl: schema.string(), + isOAuth: schema.boolean({ defaultValue: false }), + userIdentifierValue: schema.nullable(schema.string()), // required if isOAuth = true + clientId: schema.nullable(schema.string()), // required if isOAuth = true + jwtKeyId: schema.nullable(schema.string()), // required if isOAuth = true }; export const ExternalIncidentServiceConfiguration = { @@ -26,8 +30,11 @@ export const ExternalIncidentServiceConfigurationSchema = schema.object( ); export const ExternalIncidentServiceSecretConfiguration = { - password: schema.string(), - username: schema.string(), + password: schema.nullable(schema.string()), // required if isOAuth = false + username: schema.nullable(schema.string()), // required if isOAuth = false + clientSecret: schema.nullable(schema.string()), // required if isOAuth = true + privateKey: schema.nullable(schema.string()), // required if isOAuth = true + privateKeyPassword: schema.nullable(schema.string()), }; export const ExternalIncidentServiceSecretConfigurationSchema = schema.object( diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.test.ts index c179277956aa9..68fa57e93f31b 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.test.ts @@ -147,64 +147,240 @@ describe('ServiceNow service', () => { let service: ExternalService; beforeEach(() => { - service = createExternalService( - { + jest.clearAllMocks(); + service = createExternalService({ + credentials: { // The trailing slash at the end of the url is intended. // All API calls need to have the trailing slash removed. - config: { apiUrl: 'https://example.com/' }, + config: { apiUrl: 'https://example.com/', isOAuth: false }, secrets: { username: 'admin', password: 'admin' }, }, logger, configurationUtilities, - snExternalServiceConfig['.servicenow'] - ); - }); - - beforeEach(() => { - jest.clearAllMocks(); + serviceConfig: snExternalServiceConfig['.servicenow'], + axiosInstance: axios, + }); }); describe('createExternalService', () => { test('throws without url', () => { expect(() => - createExternalService( - { - config: { apiUrl: null }, + createExternalService({ + credentials: { + config: { apiUrl: null, isOAuth: false }, secrets: { username: 'admin', password: 'admin' }, }, logger, configurationUtilities, - snExternalServiceConfig['.servicenow'] - ) + serviceConfig: snExternalServiceConfig['.servicenow'], + axiosInstance: axios, + }) ).toThrow(); }); - test('throws without username', () => { - expect(() => - createExternalService( - { - config: { apiUrl: 'test.com' }, - secrets: { username: '', password: 'admin' }, - }, - logger, - configurationUtilities, - snExternalServiceConfig['.servicenow'] - ) - ).toThrow(); + test('throws when isOAuth is false and basic auth required values are falsy', () => { + const badBasicCredentials = [ + { + config: { apiUrl: 'test.com', isOAuth: false }, + secrets: { username: '', password: 'admin' }, + }, + { + config: { apiUrl: 'test.com', isOAuth: false }, + secrets: { username: null, password: 'admin' }, + }, + { + config: { apiUrl: 'test.com', isOAuth: false }, + secrets: { password: 'admin' }, + }, + { + config: { apiUrl: 'test.com', isOAuth: false }, + secrets: { username: 'admin', password: '' }, + }, + { + config: { apiUrl: 'test.com', isOAuth: false }, + secrets: { username: 'admin', password: null }, + }, + { + config: { apiUrl: 'test.com', isOAuth: false }, + secrets: { username: 'admin' }, + }, + ]; + + badBasicCredentials.forEach((badCredentials) => { + expect(() => + createExternalService({ + credentials: badCredentials, + logger, + configurationUtilities, + serviceConfig: snExternalServiceConfig['.servicenow'], + axiosInstance: axios, + }) + ).toThrow(); + }); }); - test('throws without password', () => { - expect(() => - createExternalService( - { - config: { apiUrl: 'test.com' }, - secrets: { username: '', password: undefined }, + test('throws when isOAuth is true and OAuth required values are falsy', () => { + const badOAuthCredentials = [ + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: '', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', }, - logger, - configurationUtilities, - snExternalServiceConfig['.servicenow'] - ) - ).toThrow(); + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: null, + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: '', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: null, + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: '', + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: null, + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: '', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: null, privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret', privateKey: '' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret', privateKey: null }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret' }, + }, + ]; + + badOAuthCredentials.forEach((badCredentials) => { + expect(() => + createExternalService({ + credentials: badCredentials, + logger, + configurationUtilities, + serviceConfig: snExternalServiceConfig['.servicenow'], + axiosInstance: axios, + }) + ).toThrow(); + }); }); }); @@ -233,15 +409,16 @@ describe('ServiceNow service', () => { }); test('it should call request with correct arguments when table changes', async () => { - service = createExternalService( - { - config: { apiUrl: 'https://example.com/' }, + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, secrets: { username: 'admin', password: 'admin' }, }, logger, configurationUtilities, - { ...snExternalServiceConfig['.servicenow'], table: 'sn_si_incident' } - ); + serviceConfig: { ...snExternalServiceConfig['.servicenow'], table: 'sn_si_incident' }, + axiosInstance: axios, + }); requestMock.mockImplementation(() => ({ data: { result: { sys_id: '1', number: 'INC01' } }, @@ -298,15 +475,16 @@ describe('ServiceNow service', () => { }); test('it should call request with correct arguments when table changes', async () => { - service = createExternalService( - { - config: { apiUrl: 'https://example.com/' }, + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, secrets: { username: 'admin', password: 'admin' }, }, logger, configurationUtilities, - snExternalServiceConfig['.servicenow-sir'] - ); + serviceConfig: snExternalServiceConfig['.servicenow-sir'], + axiosInstance: axios, + }); const res = await createIncident(service); @@ -382,15 +560,16 @@ describe('ServiceNow service', () => { // old connectors describe('table API', () => { beforeEach(() => { - service = createExternalService( - { - config: { apiUrl: 'https://example.com/' }, + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, secrets: { username: 'admin', password: 'admin' }, }, logger, configurationUtilities, - { ...snExternalServiceConfig['.servicenow'], useImportAPI: false } - ); + serviceConfig: { ...snExternalServiceConfig['.servicenow'], useImportAPI: false }, + axiosInstance: axios, + }); }); test('it creates the incident correctly', async () => { @@ -418,15 +597,16 @@ describe('ServiceNow service', () => { }); test('it should call request with correct arguments when table changes', async () => { - service = createExternalService( - { - config: { apiUrl: 'https://example.com/' }, + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, secrets: { username: 'admin', password: 'admin' }, }, logger, configurationUtilities, - { ...snExternalServiceConfig['.servicenow-sir'], useImportAPI: false } - ); + serviceConfig: { ...snExternalServiceConfig['.servicenow-sir'], useImportAPI: false }, + axiosInstance: axios, + }); mockIncidentResponse(false); @@ -468,15 +648,16 @@ describe('ServiceNow service', () => { }); test('it should call request with correct arguments when table changes', async () => { - service = createExternalService( - { - config: { apiUrl: 'https://example.com/' }, + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, secrets: { username: 'admin', password: 'admin' }, }, logger, configurationUtilities, - snExternalServiceConfig['.servicenow-sir'] - ); + serviceConfig: snExternalServiceConfig['.servicenow-sir'], + axiosInstance: axios, + }); const res = await updateIncident(service); expect(requestMock).toHaveBeenNthCalledWith(1, { @@ -554,15 +735,16 @@ describe('ServiceNow service', () => { // old connectors describe('table API', () => { beforeEach(() => { - service = createExternalService( - { - config: { apiUrl: 'https://example.com/' }, + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, secrets: { username: 'admin', password: 'admin' }, }, logger, configurationUtilities, - { ...snExternalServiceConfig['.servicenow'], useImportAPI: false } - ); + serviceConfig: { ...snExternalServiceConfig['.servicenow'], useImportAPI: false }, + axiosInstance: axios, + }); }); test('it updates the incident correctly', async () => { @@ -591,15 +773,16 @@ describe('ServiceNow service', () => { }); test('it should call request with correct arguments when table changes', async () => { - service = createExternalService( - { - config: { apiUrl: 'https://example.com/' }, + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, secrets: { username: 'admin', password: 'admin' }, }, logger, configurationUtilities, - { ...snExternalServiceConfig['.servicenow-sir'], useImportAPI: false } - ); + serviceConfig: { ...snExternalServiceConfig['.servicenow-sir'], useImportAPI: false }, + axiosInstance: axios, + }); mockIncidentResponse(false); @@ -646,15 +829,16 @@ describe('ServiceNow service', () => { }); test('it should call request with correct arguments when table changes', async () => { - service = createExternalService( - { - config: { apiUrl: 'https://example.com/' }, + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, secrets: { username: 'admin', password: 'admin' }, }, logger, configurationUtilities, - { ...snExternalServiceConfig['.servicenow'], table: 'sn_si_incident' } - ); + serviceConfig: { ...snExternalServiceConfig['.servicenow'], table: 'sn_si_incident' }, + axiosInstance: axios, + }); requestMock.mockImplementation(() => ({ data: { result: serviceNowCommonFields }, @@ -714,15 +898,16 @@ describe('ServiceNow service', () => { }); test('it should call request with correct arguments when table changes', async () => { - service = createExternalService( - { - config: { apiUrl: 'https://example.com/' }, + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, secrets: { username: 'admin', password: 'admin' }, }, logger, configurationUtilities, - { ...snExternalServiceConfig['.servicenow'], table: 'sn_si_incident' } - ); + serviceConfig: { ...snExternalServiceConfig['.servicenow'], table: 'sn_si_incident' }, + axiosInstance: axios, + }); requestMock.mockImplementation(() => ({ data: { result: serviceNowChoices }, @@ -818,15 +1003,16 @@ describe('ServiceNow service', () => { }); test('it does not log if useOldApi = true', async () => { - service = createExternalService( - { - config: { apiUrl: 'https://example.com/' }, + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, secrets: { username: 'admin', password: 'admin' }, }, logger, configurationUtilities, - { ...snExternalServiceConfig['.servicenow'], useImportAPI: false } - ); + serviceConfig: { ...snExternalServiceConfig['.servicenow'], useImportAPI: false }, + axiosInstance: axios, + }); await service.checkIfApplicationIsInstalled(); expect(requestMock).not.toHaveBeenCalled(); expect(logger.debug).not.toHaveBeenCalled(); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.ts index 8e606ca5f8ef7..5a1b1f604cb81 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.ts @@ -5,11 +5,9 @@ * 2.0. */ -import axios, { AxiosResponse } from 'axios'; +import { AxiosResponse } from 'axios'; -import { Logger } from '@kbn/core/server'; import { - ExternalServiceCredentials, ExternalService, ExternalServiceParamsCreate, ExternalServiceParamsUpdate, @@ -17,29 +15,41 @@ import { ImportSetApiResponseError, ServiceNowIncident, GetApplicationInfoResponse, - SNProductsConfigValue, ServiceFactory, } from './types'; import * as i18n from './translations'; import { ServiceNowPublicConfigurationType, ServiceNowSecretConfigurationType } from './types'; import { request } from '../lib/axios_utils'; -import { ActionsConfigurationUtilities } from '../../actions_config'; import { createServiceError, getPushedDate, prepareIncident } from './utils'; export const SYS_DICTIONARY_ENDPOINT = `api/now/table/sys_dictionary`; -export const createExternalService: ServiceFactory = ( - { config, secrets }: ExternalServiceCredentials, - logger: Logger, - configurationUtilities: ActionsConfigurationUtilities, - { table, importSetTable, useImportAPI, appScope }: SNProductsConfigValue -): ExternalService => { - const { apiUrl: url, usesTableApi: usesTableApiConfigValue } = - config as ServiceNowPublicConfigurationType; - const { username, password } = secrets as ServiceNowSecretConfigurationType; - - if (!url || !username || !password) { +export const createExternalService: ServiceFactory = ({ + credentials, + logger, + configurationUtilities, + serviceConfig, + axiosInstance, +}): ExternalService => { + const { config, secrets } = credentials; + const { table, importSetTable, useImportAPI, appScope } = serviceConfig; + const { + apiUrl: url, + usesTableApi: usesTableApiConfigValue, + isOAuth, + clientId, + jwtKeyId, + userIdentifierValue, + } = config as ServiceNowPublicConfigurationType; + const { username, password, clientSecret, privateKey } = + secrets as ServiceNowSecretConfigurationType; + + if ( + !url || + (!isOAuth && (!username || !password)) || + (isOAuth && (!clientSecret || !privateKey || !clientId || !jwtKeyId || !userIdentifierValue)) + ) { throw Error(`[Action]${i18n.SERVICENOW}: Wrong configuration.`); } @@ -54,10 +64,6 @@ export const createExternalService: ServiceFactory = ( */ const getVersionUrl = () => `${urlWithoutTrailingSlash}/api/${appScope}/elastic_api/health`; - const axiosInstance = axios.create({ - auth: { username, password }, - }); - const useTableApi = !useImportAPI || usesTableApiConfigValue; const getCreateIncidentUrl = () => (useTableApi ? tableApiIncidentUrl : importSetTableUrl); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_itom.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_itom.test.ts index 57e7a7506171a..855cff79d608e 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_itom.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_itom.test.ts @@ -35,15 +35,16 @@ describe('ServiceNow SIR service', () => { let service: ExternalServiceITOM; beforeEach(() => { - service = createExternalServiceITOM( - { - config: { apiUrl: 'https://example.com/' }, + service = createExternalServiceITOM({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, secrets: { username: 'admin', password: 'admin' }, }, logger, configurationUtilities, - snExternalServiceConfig['.servicenow-itom'] - ) as ExternalServiceITOM; + serviceConfig: snExternalServiceConfig['.servicenow-itom'], + axiosInstance: axios, + }) as ExternalServiceITOM; }); beforeEach(() => { diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_itom.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_itom.ts index 65c9c70545acc..3e33564fe7364 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_itom.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_itom.ts @@ -4,41 +4,28 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import axios from 'axios'; -import { Logger } from '@kbn/core/server'; -import { - ExternalServiceCredentials, - SNProductsConfigValue, - ServiceFactory, - ExternalServiceITOM, - ExecutorSubActionAddEventParams, -} from './types'; +import { ServiceFactory, ExternalServiceITOM, ExecutorSubActionAddEventParams } from './types'; -import { ServiceNowSecretConfigurationType } from './types'; import { request } from '../lib/axios_utils'; -import { ActionsConfigurationUtilities } from '../../actions_config'; import { createExternalService } from './service'; import { createServiceError } from './utils'; const getAddEventURL = (url: string) => `${url}/api/global/em/jsonv2`; -export const createExternalServiceITOM: ServiceFactory = ( - credentials: ExternalServiceCredentials, - logger: Logger, - configurationUtilities: ActionsConfigurationUtilities, - serviceConfig: SNProductsConfigValue -): ExternalServiceITOM => { - const snService = createExternalService( +export const createExternalServiceITOM: ServiceFactory = ({ + credentials, + logger, + configurationUtilities, + serviceConfig, + axiosInstance, +}): ExternalServiceITOM => { + const snService = createExternalService({ credentials, logger, configurationUtilities, - serviceConfig - ); - - const { username, password } = credentials.secrets as ServiceNowSecretConfigurationType; - const axiosInstance = axios.create({ - auth: { username, password }, + serviceConfig, + axiosInstance, }); const addEvent = async (params: ExecutorSubActionAddEventParams) => { diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_sir.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_sir.test.ts index cbd47fb2552c7..59c71ac6887c8 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_sir.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_sir.test.ts @@ -92,15 +92,16 @@ describe('ServiceNow SIR service', () => { let service: ExternalServiceSIR; beforeEach(() => { - service = createExternalServiceSIR( - { - config: { apiUrl: 'https://example.com/' }, + service = createExternalServiceSIR({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, secrets: { username: 'admin', password: 'admin' }, }, logger, configurationUtilities, - snExternalServiceConfig['.servicenow-sir'] - ) as ExternalServiceSIR; + serviceConfig: snExternalServiceConfig['.servicenow-sir'], + axiosInstance: axios, + }) as ExternalServiceSIR; }); beforeEach(() => { diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_sir.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_sir.ts index 41c02a57643e3..0ecd838c93bd2 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_sir.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_sir.ts @@ -5,21 +5,9 @@ * 2.0. */ -import axios from 'axios'; +import { Observable, ExternalServiceSIR, ObservableResponse, ServiceFactory } from './types'; -import { Logger } from '@kbn/core/server'; -import { - ExternalServiceCredentials, - SNProductsConfigValue, - Observable, - ExternalServiceSIR, - ObservableResponse, - ServiceFactory, -} from './types'; - -import { ServiceNowSecretConfigurationType } from './types'; import { request } from '../lib/axios_utils'; -import { ActionsConfigurationUtilities } from '../../actions_config'; import { createExternalService } from './service'; import { createServiceError } from './utils'; @@ -29,22 +17,19 @@ const getAddObservableToIncidentURL = (url: string, incidentID: string) => const getBulkAddObservableToIncidentURL = (url: string, incidentID: string) => `${url}/api/x_elas2_sir_int/elastic_api/incident/${incidentID}/observables/bulk`; -export const createExternalServiceSIR: ServiceFactory = ( - credentials: ExternalServiceCredentials, - logger: Logger, - configurationUtilities: ActionsConfigurationUtilities, - serviceConfig: SNProductsConfigValue -): ExternalServiceSIR => { - const snService = createExternalService( +export const createExternalServiceSIR: ServiceFactory = ({ + credentials, + logger, + configurationUtilities, + serviceConfig, + axiosInstance, +}): ExternalServiceSIR => { + const snService = createExternalService({ credentials, logger, configurationUtilities, - serviceConfig - ); - - const { username, password } = credentials.secrets as ServiceNowSecretConfigurationType; - const axiosInstance = axios.create({ - auth: { username, password }, + serviceConfig, + axiosInstance, }); const _addObservable = async (data: Observable | Observable[], url: string) => { diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/translations.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/translations.ts index 8b2bb9423d012..b007e57773989 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/translations.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/translations.ts @@ -30,3 +30,42 @@ export const ALLOWED_HOSTS_ERROR = (message: string) => message, }, }); + +export const CREDENTIALS_ERROR = i18n.translate( + 'xpack.actions.builtin.configuration.apiCredentialsError', + { + defaultMessage: 'Either basic auth or OAuth credentials must be specified', + } +); + +export const BASIC_AUTH_CREDENTIALS_ERROR = i18n.translate( + 'xpack.actions.builtin.configuration.apiBasicAuthCredentialsError', + { + defaultMessage: 'username and password must both be specified', + } +); + +export const OAUTH_CREDENTIALS_ERROR = i18n.translate( + 'xpack.actions.builtin.configuration.apiOAuthCredentialsError', + { + defaultMessage: 'clientSecret and privateKey must both be specified', + } +); + +export const VALIDATE_OAUTH_MISSING_FIELD_ERROR = (field: string, isOAuth: boolean) => + i18n.translate('xpack.actions.builtin.configuration.apiValidateMissingOAuthFieldError', { + defaultMessage: '{field} must be provided when isOAuth = {isOAuth}', + values: { + field, + isOAuth: isOAuth ? 'true' : 'false', + }, + }); + +export const VALIDATE_OAUTH_POPULATED_FIELD_ERROR = (field: string, isOAuth: boolean) => + i18n.translate('xpack.actions.builtin.configuration.apiValidateOAuthFieldError', { + defaultMessage: '{field} should not be provided with isOAuth = {isOAuth}', + values: { + field, + isOAuth: isOAuth ? 'true' : 'false', + }, + }); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/types.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/types.ts index 4475832e1a7f7..ff3a92e935818 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/types.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/types.ts @@ -7,7 +7,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { AxiosError, AxiosResponse } from 'axios'; +import { AxiosError, AxiosInstance, AxiosResponse } from 'axios'; import { TypeOf } from '@kbn/config-schema'; import { Logger } from '@kbn/core/server'; import { @@ -78,6 +78,7 @@ export interface ExternalServiceCredentials { export interface ExternalServiceValidation { config: (configurationUtilities: ActionsConfigurationUtilities, configObject: any) => void; secrets: (configurationUtilities: ActionsConfigurationUtilities, secrets: any) => void; + connector: (config: any, secrets: any) => string | null; } export interface ExternalServiceIncidentResponse { @@ -277,12 +278,21 @@ export interface ExternalServiceSIR extends ExternalService { ) => Promise; } -export type ServiceFactory = ( - credentials: ExternalServiceCredentials, - logger: Logger, - configurationUtilities: ActionsConfigurationUtilities, - serviceConfig: SNProductsConfigValue -) => T; +interface ServiceFactoryOpts { + credentials: ExternalServiceCredentials; + logger: Logger; + configurationUtilities: ActionsConfigurationUtilities; + serviceConfig: SNProductsConfigValue; + axiosInstance: AxiosInstance; +} + +export type ServiceFactory = ({ + credentials, + logger, + configurationUtilities, + serviceConfig, + axiosInstance, +}: ServiceFactoryOpts) => T; /** * ITOM diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.test.ts index 7d66949d4473f..64a80977709e5 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { AxiosError } from 'axios'; +import axios, { AxiosError } from 'axios'; import { Logger } from '@kbn/core/server'; import { loggingSystemMock } from '@kbn/core/server/mocks'; @@ -14,10 +14,30 @@ import { createServiceError, getPushedDate, throwIfSubActionIsNotSupported, + getAxiosInstance, } from './utils'; +import { connectorTokenClientMock } from '../lib/connector_token_client.mock'; +import { actionsConfigMock } from '../../actions_config.mock'; +import { getOAuthJwtAccessToken } from '../lib/get_oauth_jwt_access_token'; -const logger = loggingSystemMock.create().get() as jest.Mocked; +jest.mock('../lib/get_oauth_jwt_access_token', () => ({ + getOAuthJwtAccessToken: jest.fn(), +})); + +jest.mock('axios', () => ({ + create: jest.fn(), +})); +const createAxiosInstanceMock = axios.create as jest.Mock; +const axiosInstanceMock = { + interceptors: { + request: { eject: jest.fn(), use: jest.fn() }, + response: { eject: jest.fn(), use: jest.fn() }, + }, +}; +const connectorTokenClient = connectorTokenClientMock.create(); +const logger = loggingSystemMock.create().get() as jest.Mocked; +const configurationUtilities = actionsConfigMock.create(); /** * The purpose of this test is to * prevent developers from accidentally @@ -131,4 +151,113 @@ describe('utils', () => { ).not.toThrow(); }); }); + + describe('getAxiosInstance', () => { + beforeEach(() => { + jest.clearAllMocks(); + createAxiosInstanceMock.mockReturnValue(axiosInstanceMock); + }); + + test('creates axios instance with basic auth when isOAuth is false and username and password are defined', () => { + getAxiosInstance({ + connectorId: '123', + logger, + configurationUtilities, + credentials: { + config: { + apiUrl: 'https://servicenow', + usesTableApi: true, + isOAuth: false, + clientId: null, + jwtKeyId: null, + userIdentifierValue: null, + }, + secrets: { + clientSecret: null, + privateKey: null, + privateKeyPassword: null, + username: 'username', + password: 'password', + }, + }, + snServiceUrl: 'https://dev23432523.service-now.com', + connectorTokenClient, + }); + + expect(createAxiosInstanceMock).toHaveBeenCalledTimes(1); + expect(createAxiosInstanceMock).toHaveBeenCalledWith({ + auth: { password: 'password', username: 'username' }, + }); + }); + + test('creates axios instance with interceptor when isOAuth is true and OAuth fields are defined', async () => { + connectorTokenClient.get.mockResolvedValueOnce({ + hasErrors: false, + connectorToken: { + id: '1', + connectorId: '123', + tokenType: 'access_token', + token: 'testtokenvalue', + createdAt: new Date().toISOString(), + expiresAt: new Date(Date.now() + 10000000000).toISOString(), + }, + }); + getAxiosInstance({ + connectorId: '123', + logger, + configurationUtilities, + credentials: { + config: { + apiUrl: 'https://servicenow', + usesTableApi: true, + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'userIdentifierValue', + }, + secrets: { + clientSecret: 'clientSecret', + privateKey: 'privateKey', + privateKeyPassword: null, + username: null, + password: null, + }, + }, + snServiceUrl: 'https://dev23432523.service-now.com', + connectorTokenClient, + }); + + expect(createAxiosInstanceMock).toHaveBeenCalledTimes(1); + expect(createAxiosInstanceMock).toHaveBeenCalledWith(); + expect(axiosInstanceMock.interceptors.request.use).toHaveBeenCalledTimes(1); + + (getOAuthJwtAccessToken as jest.Mock).mockResolvedValueOnce('Bearer tokentokentoken'); + + const mockRequestCallback = (axiosInstanceMock.interceptors.request.use as jest.Mock).mock + .calls[0][0]; + expect(await mockRequestCallback({ headers: {} })).toEqual({ + headers: { Authorization: 'Bearer tokentokentoken' }, + }); + + expect(getOAuthJwtAccessToken as jest.Mock).toHaveBeenCalledWith({ + connectorId: '123', + logger, + configurationUtilities, + credentials: { + config: { + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'userIdentifierValue', + }, + secrets: { + clientSecret: 'clientSecret', + privateKey: 'privateKey', + privateKeyPassword: null, + }, + }, + tokenUrl: 'https://dev23432523.service-now.com/oauth_token.do', + connectorTokenClient, + }); + }); + }); }); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.ts index f18d09cdaedb9..538967269b1ea 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.ts @@ -5,11 +5,23 @@ * 2.0. */ +import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'; import { Logger } from '@kbn/core/server'; -import { Incident, PartialIncident, ResponseError, ServiceNowError } from './types'; +import { + ExternalServiceCredentials, + Incident, + PartialIncident, + ResponseError, + ServiceNowError, + ServiceNowPublicConfigurationType, + ServiceNowSecretConfigurationType, +} from './types'; import { FIELD_PREFIX } from './config'; import { addTimeZoneToDate, getErrorMessage } from '../lib/axios_utils'; import * as i18n from './translations'; +import { ActionsConfigurationUtilities } from '../../actions_config'; +import { ConnectorTokenClientContract } from '../../types'; +import { getOAuthJwtAccessToken } from '../lib/get_oauth_jwt_access_token'; export const prepareIncident = (useOldApi: boolean, incident: PartialIncident): PartialIncident => useOldApi @@ -69,3 +81,67 @@ export const throwIfSubActionIsNotSupported = ({ throw new Error(errorMessage); } }; + +export interface GetAxiosInstanceOpts { + connectorId?: string; + logger: Logger; + configurationUtilities: ActionsConfigurationUtilities; + credentials: ExternalServiceCredentials; + snServiceUrl: string; + connectorTokenClient?: ConnectorTokenClientContract; +} + +export const getAxiosInstance = ({ + connectorId, + logger, + configurationUtilities, + credentials, + snServiceUrl, + connectorTokenClient, +}: GetAxiosInstanceOpts): AxiosInstance => { + const { config, secrets } = credentials; + const { isOAuth } = config as ServiceNowPublicConfigurationType; + const { username, password } = secrets as ServiceNowSecretConfigurationType; + + let axiosInstance; + + if (!isOAuth && username && password) { + axiosInstance = axios.create({ + auth: { username, password }, + }); + } else { + axiosInstance = axios.create(); + axiosInstance.interceptors.request.use( + async (axiosConfig: AxiosRequestConfig) => { + const accessToken = await getOAuthJwtAccessToken({ + connectorId, + logger, + configurationUtilities, + credentials: { + config: { + clientId: config.clientId as string, + jwtKeyId: config.jwtKeyId as string, + userIdentifierValue: config.userIdentifierValue as string, + }, + secrets: { + clientSecret: secrets.clientSecret as string, + privateKey: secrets.privateKey as string, + privateKeyPassword: secrets.privateKeyPassword + ? (secrets.privateKeyPassword as string) + : null, + }, + }, + tokenUrl: `${snServiceUrl}/oauth_token.do`, + connectorTokenClient, + }); + axiosConfig.headers.Authorization = accessToken; + return axiosConfig; + }, + (error) => { + Promise.reject(error); + } + ); + } + + return axiosInstance; +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/validators.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/validators.test.ts new file mode 100644 index 0000000000000..547c025fcdb61 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/validators.test.ts @@ -0,0 +1,401 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { validateCommonConfig, validateCommonSecrets, validateCommonConnector } from './validators'; +import { actionsConfigMock } from '../../actions_config.mock'; + +const configurationUtilities = actionsConfigMock.create(); + +describe('validateCommonConfig', () => { + test('config validation fails when apiUrl is not allowed', () => { + expect( + validateCommonConfig( + { + ...configurationUtilities, + ensureUriAllowed: (_) => { + throw new Error(`target url is not present in allowedHosts`); + }, + }, + { + apiUrl: 'example.com/do-something', + usesTableApi: true, + isOAuth: false, + userIdentifierValue: null, + clientId: null, + jwtKeyId: null, + } + ) + ).toEqual(`error configuring connector action: target url is not present in allowedHosts`); + }); + describe('when isOAuth = true', () => { + test('config validation fails when userIdentifierValue is null', () => { + expect( + validateCommonConfig(configurationUtilities, { + apiUrl: 'https://url', + usesTableApi: true, + isOAuth: true, + userIdentifierValue: null, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + }) + ).toEqual(`userIdentifierValue must be provided when isOAuth = true`); + }); + test('config validation fails when clientId is null', () => { + expect( + validateCommonConfig(configurationUtilities, { + apiUrl: 'https://url', + usesTableApi: true, + isOAuth: true, + userIdentifierValue: 'userIdentifierValue', + clientId: null, + jwtKeyId: 'jwtKeyId', + }) + ).toEqual(`clientId must be provided when isOAuth = true`); + }); + test('config validation fails when jwtKeyId is null', () => { + expect( + validateCommonConfig(configurationUtilities, { + apiUrl: 'https://url', + usesTableApi: true, + isOAuth: true, + userIdentifierValue: 'userIdentifierValue', + clientId: 'clientId', + jwtKeyId: null, + }) + ).toEqual(`jwtKeyId must be provided when isOAuth = true`); + }); + }); + + describe('when isOAuth = false', () => { + test('connector validation fails when username is null', () => { + expect( + validateCommonConnector( + { + apiUrl: 'https://url', + usesTableApi: true, + isOAuth: false, + userIdentifierValue: null, + clientId: null, + jwtKeyId: null, + }, + { + password: 'password', + username: null, + clientSecret: null, + privateKey: null, + privateKeyPassword: null, + } + ) + ).toEqual(`username must be provided when isOAuth = false`); + }); + test('connector validation fails when password is null', () => { + expect( + validateCommonConnector( + { + apiUrl: 'https://url', + usesTableApi: true, + isOAuth: false, + userIdentifierValue: null, + clientId: null, + jwtKeyId: null, + }, + { + password: null, + username: 'username', + clientSecret: null, + privateKey: null, + privateKeyPassword: null, + } + ) + ).toEqual(`password must be provided when isOAuth = false`); + }); + test('connector validation fails when any oauth related field is defined', () => { + expect( + validateCommonConnector( + { + apiUrl: 'https://url', + usesTableApi: true, + isOAuth: false, + userIdentifierValue: null, + clientId: null, + jwtKeyId: null, + }, + { + password: 'password', + username: 'username', + clientSecret: 'clientSecret', + privateKey: null, + privateKeyPassword: null, + } + ) + ).toEqual( + `clientId, clientSecret, userIdentifierValue, jwtKeyId and privateKey should not be provided with isOAuth = false` + ); + }); + }); +}); + +describe('validateCommonSecrets', () => { + test('secrets validation fails when no credentials are defined', () => { + expect( + validateCommonSecrets(configurationUtilities, { + password: null, + username: null, + clientSecret: null, + privateKey: null, + privateKeyPassword: null, + }) + ).toEqual(`Either basic auth or OAuth credentials must be specified`); + }); + + test('secrets validation fails when username is defined and password is not', () => { + expect( + validateCommonSecrets(configurationUtilities, { + password: null, + username: 'admin', + clientSecret: null, + privateKey: null, + privateKeyPassword: null, + }) + ).toEqual(`username and password must both be specified`); + }); + + test('secrets validation fails when password is defined and username is not', () => { + expect( + validateCommonSecrets(configurationUtilities, { + password: 'password', + username: null, + clientSecret: null, + privateKey: null, + privateKeyPassword: null, + }) + ).toEqual(`username and password must both be specified`); + }); + + test('secrets validation fails when clientSecret is defined and privateKey is not', () => { + expect( + validateCommonSecrets(configurationUtilities, { + password: null, + username: null, + clientSecret: 'secret', + privateKey: null, + privateKeyPassword: null, + }) + ).toEqual(`clientSecret and privateKey must both be specified`); + }); + + test('secrets validation fails when privateKey is defined and clientSecret is not', () => { + expect( + validateCommonSecrets(configurationUtilities, { + password: null, + username: null, + clientSecret: null, + privateKey: 'private', + privateKeyPassword: null, + }) + ).toEqual(`clientSecret and privateKey must both be specified`); + }); +}); + +describe('validateCommonConnector', () => { + describe('when isOAuth = true', () => { + test('connector validation fails when userIdentifierValue is null', () => { + expect( + validateCommonConnector( + { + apiUrl: 'https://url', + usesTableApi: true, + isOAuth: true, + userIdentifierValue: null, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + }, + { + password: null, + username: null, + clientSecret: 'clientSecret', + privateKey: 'privateKey', + privateKeyPassword: null, + } + ) + ).toEqual(`userIdentifierValue must be provided when isOAuth = true`); + }); + test('connector validation fails when clientId is null', () => { + expect( + validateCommonConnector( + { + apiUrl: 'https://url', + usesTableApi: true, + isOAuth: true, + userIdentifierValue: 'userIdentifierValue', + clientId: null, + jwtKeyId: 'jwtKeyId', + }, + { + password: null, + username: null, + clientSecret: 'clientSecret', + privateKey: 'privateKey', + privateKeyPassword: null, + } + ) + ).toEqual(`clientId must be provided when isOAuth = true`); + }); + test('connector validation fails when jwtKeyId is null', () => { + expect( + validateCommonConnector( + { + apiUrl: 'https://url', + usesTableApi: true, + isOAuth: true, + userIdentifierValue: 'userIdentifierValue', + clientId: 'clientId', + jwtKeyId: null, + }, + { + password: null, + username: null, + clientSecret: 'clientSecret', + privateKey: 'privateKey', + privateKeyPassword: null, + } + ) + ).toEqual(`jwtKeyId must be provided when isOAuth = true`); + }); + test('connector validation fails when clientSecret is null', () => { + expect( + validateCommonConnector( + { + apiUrl: 'https://url', + usesTableApi: true, + isOAuth: true, + userIdentifierValue: 'userIdentifierValue', + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + }, + { + password: null, + username: null, + clientSecret: null, + privateKey: 'privateKey', + privateKeyPassword: null, + } + ) + ).toEqual(`clientSecret must be provided when isOAuth = true`); + }); + test('connector validation fails when privateKey is null', () => { + expect( + validateCommonConnector( + { + apiUrl: 'https://url', + usesTableApi: true, + isOAuth: true, + userIdentifierValue: 'userIdentifierValue', + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + }, + { + password: null, + username: null, + clientSecret: 'clientSecret', + privateKey: null, + privateKeyPassword: null, + } + ) + ).toEqual(`privateKey must be provided when isOAuth = true`); + }); + test('connector validation fails when username and password are not null', () => { + expect( + validateCommonConnector( + { + apiUrl: 'https://url', + usesTableApi: true, + isOAuth: true, + userIdentifierValue: 'userIdentifierValue', + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + }, + { + password: 'password', + username: 'username', + clientSecret: 'clientSecret', + privateKey: 'privateKey', + privateKeyPassword: null, + } + ) + ).toEqual(`Username and password should not be provided with isOAuth = true`); + }); + }); + + describe('when isOAuth = false', () => { + test('connector validation fails when username is null', () => { + expect( + validateCommonConnector( + { + apiUrl: 'https://url', + usesTableApi: true, + isOAuth: false, + userIdentifierValue: null, + clientId: null, + jwtKeyId: null, + }, + { + password: 'password', + username: null, + clientSecret: null, + privateKey: null, + privateKeyPassword: null, + } + ) + ).toEqual(`username must be provided when isOAuth = false`); + }); + test('connector validation fails when password is null', () => { + expect( + validateCommonConnector( + { + apiUrl: 'https://url', + usesTableApi: true, + isOAuth: false, + userIdentifierValue: null, + clientId: null, + jwtKeyId: null, + }, + { + password: null, + username: 'username', + clientSecret: null, + privateKey: null, + privateKeyPassword: null, + } + ) + ).toEqual(`password must be provided when isOAuth = false`); + }); + test('connector validation fails when any oauth related field is defined', () => { + expect( + validateCommonConnector( + { + apiUrl: 'https://url', + usesTableApi: true, + isOAuth: false, + userIdentifierValue: null, + clientId: null, + jwtKeyId: null, + }, + { + password: 'password', + username: 'username', + clientSecret: 'clientSecret', + privateKey: null, + privateKeyPassword: null, + } + ) + ).toEqual( + `clientId, clientSecret, userIdentifierValue, jwtKeyId and privateKey should not be provided with isOAuth = false` + ); + }); + }); +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/validators.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/validators.ts index f074e28863642..87ea4922fa5cc 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/validators.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/validators.ts @@ -16,21 +16,107 @@ import * as i18n from './translations'; export const validateCommonConfig = ( configurationUtilities: ActionsConfigurationUtilities, - configObject: ServiceNowPublicConfigurationType + config: ServiceNowPublicConfigurationType ) => { + const { isOAuth, apiUrl, userIdentifierValue, clientId, jwtKeyId } = config; + try { - configurationUtilities.ensureUriAllowed(configObject.apiUrl); + configurationUtilities.ensureUriAllowed(apiUrl); } catch (allowedListError) { return i18n.ALLOWED_HOSTS_ERROR(allowedListError.message); } + + if (isOAuth) { + if (userIdentifierValue == null) { + return i18n.VALIDATE_OAUTH_MISSING_FIELD_ERROR('userIdentifierValue', true); + } + + if (clientId == null) { + return i18n.VALIDATE_OAUTH_MISSING_FIELD_ERROR('clientId', true); + } + + if (jwtKeyId == null) { + return i18n.VALIDATE_OAUTH_MISSING_FIELD_ERROR('jwtKeyId', true); + } + } }; export const validateCommonSecrets = ( configurationUtilities: ActionsConfigurationUtilities, secrets: ServiceNowSecretConfigurationType -) => {}; +) => { + const { username, password, clientSecret, privateKey } = secrets; + + if (!username && !password && !clientSecret && !privateKey) { + return i18n.CREDENTIALS_ERROR; + } + + if (username || password) { + // Username and password must be set and set together + if (!username || !password) { + return i18n.BASIC_AUTH_CREDENTIALS_ERROR; + } + } else if (clientSecret || privateKey) { + // Client secret and private key must be set and set together + if (!clientSecret || !privateKey) { + return i18n.OAUTH_CREDENTIALS_ERROR; + } + } +}; + +export const validateCommonConnector = ( + config: ServiceNowPublicConfigurationType, + secrets: ServiceNowSecretConfigurationType +): string | null => { + const { isOAuth, userIdentifierValue, clientId, jwtKeyId } = config; + const { username, password, clientSecret, privateKey } = secrets; + + if (isOAuth) { + if (userIdentifierValue == null) { + return i18n.VALIDATE_OAUTH_MISSING_FIELD_ERROR('userIdentifierValue', true); + } + + if (clientId == null) { + return i18n.VALIDATE_OAUTH_MISSING_FIELD_ERROR('clientId', true); + } + + if (jwtKeyId == null) { + return i18n.VALIDATE_OAUTH_MISSING_FIELD_ERROR('jwtKeyId', true); + } + + if (clientSecret == null) { + return i18n.VALIDATE_OAUTH_MISSING_FIELD_ERROR('clientSecret', true); + } + + if (privateKey == null) { + return i18n.VALIDATE_OAUTH_MISSING_FIELD_ERROR('privateKey', true); + } + + if (username || password) { + return i18n.VALIDATE_OAUTH_POPULATED_FIELD_ERROR('Username and password', true); + } + } else { + if (username == null) { + return i18n.VALIDATE_OAUTH_MISSING_FIELD_ERROR('username', false); + } + + if (password == null) { + return i18n.VALIDATE_OAUTH_MISSING_FIELD_ERROR('password', false); + } + + if (clientSecret || clientId || userIdentifierValue || jwtKeyId || privateKey) { + return i18n.VALIDATE_OAUTH_POPULATED_FIELD_ERROR( + 'clientId, clientSecret, userIdentifierValue, jwtKeyId and privateKey', + false + ); + } + } + + return null; +}; export const validate: ExternalServiceValidation = { config: validateCommonConfig, secrets: validateCommonSecrets, + connector: validateCommonConnector, }; diff --git a/x-pack/plugins/actions/server/lib/action_executor.test.ts b/x-pack/plugins/actions/server/lib/action_executor.test.ts index 093236c939aa1..12898cea5a482 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts @@ -521,7 +521,7 @@ test('logs a warning when alert executor throws an error', async () => { executorMock.mockRejectedValue(new Error('this action execution is intended to fail')); await actionExecutor.execute(executeParams); expect(loggerMock.warn).toBeCalledWith( - 'action execution failure: test:1: action-1: an error occurred while running the action executor: this action execution is intended to fail' + 'action execution failure: test:1: action-1: an error occurred while running the action: this action execution is intended to fail' ); }); diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index fe77b72f47aa3..b9ed252c6afc2 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -19,6 +19,7 @@ import { validateConnector, } from './validate_with_schema'; import { + ActionType, ActionTypeExecutorResult, ActionTypeRegistryContract, GetServicesFunction, @@ -30,6 +31,7 @@ import { ActionsClient } from '../actions_client'; import { ActionExecutionSource } from './action_execution_source'; import { RelatedSavedObjects } from './related_saved_objects'; import { createActionEventLogRecordObject } from './create_action_event_log_record_object'; +import { ActionExecutionError, ActionExecutionErrorReason } from './errors/action_execution_error'; // 1,000,000 nanoseconds in 1 millisecond const Millis2Nanos = 1000 * 1000; @@ -157,24 +159,6 @@ export class ActionExecutor { } const actionType = actionTypeRegistry.get(actionTypeId); - let validatedParams: Record; - let validatedConfig: Record; - let validatedSecrets: Record; - try { - validatedParams = validateParams(actionType, params); - validatedConfig = validateConfig(actionType, config); - validatedSecrets = validateSecrets(actionType, secrets); - if (actionType.validate?.connector) { - validateConnector(actionType, { - config, - secrets, - }); - } - } catch (err) { - span?.setOutcome('failure'); - return { status: 'error', actionId, message: err.message, retry: false }; - } - const actionLabel = `${actionTypeId}:${actionId}: ${name}`; logger.debug(`executing action ${actionLabel}`); @@ -221,6 +205,14 @@ export class ActionExecutor { let rawResult: ActionTypeExecutorResult; try { + const { validatedParams, validatedConfig, validatedSecrets } = validateAction({ + actionId, + actionType, + params, + config, + secrets, + }); + rawResult = await actionType.executor({ actionId, services, @@ -231,14 +223,19 @@ export class ActionExecutor { taskInfo, }); } catch (err) { - rawResult = { - actionId, - status: 'error', - message: 'an error occurred while running the action executor', - serviceMessage: err.message, - retry: false, - }; + if (err.reason === ActionExecutionErrorReason.Validation) { + rawResult = err.result; + } else { + rawResult = { + actionId, + status: 'error', + message: 'an error occurred while running the action', + serviceMessage: err.message, + retry: false, + }; + } } + eventLogger.stopTiming(event); // allow null-ish return to indicate success @@ -411,3 +408,38 @@ function actionErrorToMessage(result: ActionTypeExecutorResult): string return message; } + +interface ValidateActionOpts { + actionId: string; + actionType: ActionType; + params: Record; + config: unknown; + secrets: unknown; +} + +function validateAction({ actionId, actionType, params, config, secrets }: ValidateActionOpts) { + let validatedParams: Record; + let validatedConfig: Record; + let validatedSecrets: Record; + + try { + validatedParams = validateParams(actionType, params); + validatedConfig = validateConfig(actionType, config); + validatedSecrets = validateSecrets(actionType, secrets); + if (actionType.validate?.connector) { + validateConnector(actionType, { + config, + secrets, + }); + } + + return { validatedParams, validatedConfig, validatedSecrets }; + } catch (err) { + throw new ActionExecutionError(err.message, ActionExecutionErrorReason.Validation, { + actionId, + status: 'error', + message: err.message, + retry: false, + }); + } +} diff --git a/x-pack/plugins/actions/server/lib/errors/action_execution_error.ts b/x-pack/plugins/actions/server/lib/errors/action_execution_error.ts new file mode 100644 index 0000000000000..ad43008ef8e20 --- /dev/null +++ b/x-pack/plugins/actions/server/lib/errors/action_execution_error.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 { ActionTypeExecutorResult } from '../../types'; + +export enum ActionExecutionErrorReason { + Validation = 'validation', +} + +export class ActionExecutionError extends Error { + public readonly reason: ActionExecutionErrorReason; + public readonly result: ActionTypeExecutorResult; + + constructor( + message: string, + reason: ActionExecutionErrorReason, + result: ActionTypeExecutorResult + ) { + super(message); + this.reason = reason; + this.result = result; + } +} diff --git a/x-pack/plugins/actions/server/plugin.test.ts b/x-pack/plugins/actions/server/plugin.test.ts index d89a3c96b01b9..3f3895ec5b69f 100644 --- a/x-pack/plugins/actions/server/plugin.test.ts +++ b/x-pack/plugins/actions/server/plugin.test.ts @@ -109,7 +109,7 @@ describe('Actions Plugin', () => { httpServerMock.createKibanaRequest(), httpServerMock.createResponseFactory() )) as unknown as ActionsApiRequestHandlerContext; - actionsContextHandler!.getActionsClient(); + expect(actionsContextHandler!.getActionsClient()).toBeDefined(); }); it('should throw error when ESO plugin is missing encryption key', async () => { diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 1fad2a6189693..c097b94a85950 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -311,12 +311,13 @@ export class ActionsPlugin implements Plugin(), - this.licenseState, + defineRoutes({ + router: core.http.createRouter(), + licenseState: this.licenseState, + logger: this.logger, actionsConfigUtils, - this.usageCounter - ); + usageCounter: this.usageCounter, + }); // Cleanup failed execution task definition if (this.actionsConfig.cleanupFailedExecutionsTask.enabled) { diff --git a/x-pack/plugins/actions/server/routes/get_oauth_access_token.test.ts b/x-pack/plugins/actions/server/routes/get_oauth_access_token.test.ts new file mode 100644 index 0000000000000..888e87dbdf1f4 --- /dev/null +++ b/x-pack/plugins/actions/server/routes/get_oauth_access_token.test.ts @@ -0,0 +1,227 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getOAuthAccessToken } from './get_oauth_access_token'; +import { Logger } from '@kbn/core/server'; +import { httpServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './legacy/_mock_handler_arguments'; +import { verifyAccessAndContext } from './verify_access_and_context'; +import { actionsConfigMock } from '../actions_config.mock'; +import { actionsClientMock } from '../actions_client.mock'; + +jest.mock('./verify_access_and_context', () => ({ + verifyAccessAndContext: jest.fn(), +})); + +const logger = loggingSystemMock.create().get() as jest.Mocked; +const configurationUtilities = actionsConfigMock.create(); + +beforeEach(() => { + jest.resetAllMocks(); + (verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler); +}); + +describe('getOAuthAccessToken', () => { + it('returns jwt access token for given jwt oauth config', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getOAuthAccessToken(router, licenseState, logger, configurationUtilities); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/actions/connector/_oauth_access_token"`); + + const actionsClient = actionsClientMock.create(); + actionsClient.getOAuthAccessToken.mockResolvedValueOnce({ + accessToken: 'Bearer jwttokentokentoken', + }); + + const requestBody = { + type: 'jwt', + options: { + tokenUrl: 'https://testurl.service-now.com/oauth_token.do', + config: { + clientId: 'abc', + jwtKeyId: 'def', + userIdentifierValue: 'userA', + }, + secrets: { + clientSecret: 'iamasecret', + privateKey: 'xyz', + }, + }, + }; + + const [context, req, res] = mockHandlerArguments( + { actionsClient }, + { + body: requestBody, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Object { + "accessToken": "Bearer jwttokentokentoken", + }, + } + `); + + expect(actionsClient.getOAuthAccessToken).toHaveBeenCalledTimes(1); + expect(actionsClient.getOAuthAccessToken.mock.calls[0]).toEqual([ + requestBody, + logger, + configurationUtilities, + ]); + + expect(res.ok).toHaveBeenCalledWith({ + body: { + accessToken: 'Bearer jwttokentokentoken', + }, + }); + }); + + it('returns client credentials access token for given client credentials oauth config', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getOAuthAccessToken(router, licenseState, logger, configurationUtilities); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/actions/connector/_oauth_access_token"`); + + const actionsClient = actionsClientMock.create(); + actionsClient.getOAuthAccessToken.mockResolvedValueOnce({ + accessToken: 'Bearer clienttokentokentoken', + }); + + const requestBody = { + type: 'client', + options: { + tokenUrl: 'https://login.microsoftonline.com/98765/oauth2/v2.0/token', + scope: 'https://graph.microsoft.com/.default', + config: { + clientId: 'abc', + tenantId: 'def', + }, + secrets: { + clientSecret: 'iamasecret', + }, + }, + }; + + const [context, req, res] = mockHandlerArguments( + { actionsClient }, + { + body: requestBody, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Object { + "accessToken": "Bearer clienttokentokentoken", + }, + } + `); + + expect(actionsClient.getOAuthAccessToken).toHaveBeenCalledTimes(1); + expect(actionsClient.getOAuthAccessToken.mock.calls[0]).toEqual([ + requestBody, + logger, + configurationUtilities, + ]); + + expect(res.ok).toHaveBeenCalledWith({ + body: { + accessToken: 'Bearer clienttokentokentoken', + }, + }); + }); + + it('ensures the license allows getting servicenow access token', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getOAuthAccessToken(router, licenseState, logger, configurationUtilities); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/actions/connector/_oauth_access_token"`); + + const [context, req, res] = mockHandlerArguments( + {}, + { + body: { + type: 'jwt', + options: { + tokenUrl: 'https://testurl.service-now.com/oauth_token.do', + config: { + clientId: 'abc', + jwtKeyId: 'def', + userIdentifierValue: 'userA', + }, + secrets: { + clientSecret: 'iamasecret', + privateKey: 'xyz', + }, + }, + }, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); + }); + + it('ensures the license check prevents getting service now access token', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + (verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => { + throw new Error('OMG'); + }); + + getOAuthAccessToken(router, licenseState, logger, configurationUtilities); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/actions/connector/_oauth_access_token"`); + + const [context, req, res] = mockHandlerArguments( + {}, + { + body: { + type: 'client', + options: { + tokenUrl: 'https://login.microsoftonline.com/98765/oauth2/v2.0/token', + scope: 'https://graph.microsoft.com/.default', + config: { + clientId: 'abc', + tenantId: 'def', + }, + secrets: { + clientSecret: 'iamasecret', + }, + }, + }, + }, + ['ok'] + ); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); + }); +}); diff --git a/x-pack/plugins/actions/server/routes/get_oauth_access_token.ts b/x-pack/plugins/actions/server/routes/get_oauth_access_token.ts new file mode 100644 index 0000000000000..e1b612d321bcd --- /dev/null +++ b/x-pack/plugins/actions/server/routes/get_oauth_access_token.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { IRouter, Logger } from '@kbn/core/server'; +import { ILicenseState } from '../lib'; +import { INTERNAL_BASE_ACTION_API_PATH } from '../../common'; +import { ActionsRequestHandlerContext } from '../types'; +import { verifyAccessAndContext } from './verify_access_and_context'; +import { ActionsConfigurationUtilities } from '../actions_config'; + +const oauthJwtBodySchema = schema.object({ + tokenUrl: schema.string(), + config: schema.object({ + clientId: schema.string(), + jwtKeyId: schema.string(), + userIdentifierValue: schema.string(), + }), + secrets: schema.object({ + clientSecret: schema.string(), + privateKey: schema.string(), + privateKeyPassword: schema.maybe(schema.string()), + }), +}); + +export type OAuthJwtParams = TypeOf; + +const oauthClientCredentialsBodySchema = schema.object({ + tokenUrl: schema.string(), + scope: schema.string(), + config: schema.object({ + clientId: schema.string(), + tenantId: schema.string(), + }), + secrets: schema.object({ + clientSecret: schema.string(), + }), +}); + +export type OAuthClientCredentialsParams = TypeOf; + +const bodySchema = schema.object({ + type: schema.oneOf([schema.literal('jwt'), schema.literal('client')]), + options: schema.conditional( + schema.siblingRef('type'), + schema.literal('jwt'), + oauthJwtBodySchema, + oauthClientCredentialsBodySchema + ), +}); + +export type OAuthParams = TypeOf; + +export const getOAuthAccessToken = ( + router: IRouter, + licenseState: ILicenseState, + logger: Logger, + configurationUtilities: ActionsConfigurationUtilities +) => { + router.post( + { + path: `${INTERNAL_BASE_ACTION_API_PATH}/connector/_oauth_access_token`, + validate: { + body: bodySchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const actionsClient = (await context.actions).getActionsClient(); + + return res.ok({ + body: await actionsClient.getOAuthAccessToken(req.body, logger, configurationUtilities), + }); + }) + ) + ); +}; diff --git a/x-pack/plugins/actions/server/routes/index.ts b/x-pack/plugins/actions/server/routes/index.ts index ab90141ae1c80..2822aa3668900 100644 --- a/x-pack/plugins/actions/server/routes/index.ts +++ b/x-pack/plugins/actions/server/routes/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { IRouter } from '@kbn/core/server'; +import { IRouter, Logger } from '@kbn/core/server'; import { UsageCounter } from '@kbn/usage-collection-plugin/server'; import { ILicenseState } from '../lib'; import { ActionsRequestHandlerContext } from '../types'; @@ -17,15 +17,21 @@ import { getAllActionRoute } from './get_all'; import { connectorTypesRoute } from './connector_types'; import { updateActionRoute } from './update'; import { getWellKnownEmailServiceRoute } from './get_well_known_email_service'; +import { getOAuthAccessToken } from './get_oauth_access_token'; import { defineLegacyRoutes } from './legacy'; import { ActionsConfigurationUtilities } from '../actions_config'; -export function defineRoutes( - router: IRouter, - licenseState: ILicenseState, - actionsConfigUtils: ActionsConfigurationUtilities, - usageCounter?: UsageCounter -) { +export interface RouteOptions { + router: IRouter; + licenseState: ILicenseState; + logger: Logger; + actionsConfigUtils: ActionsConfigurationUtilities; + usageCounter?: UsageCounter; +} + +export function defineRoutes(opts: RouteOptions) { + const { router, licenseState, logger, actionsConfigUtils, usageCounter } = opts; + defineLegacyRoutes(router, licenseState, usageCounter); createActionRoute(router, licenseState); @@ -36,5 +42,6 @@ export function defineRoutes( connectorTypesRoute(router, licenseState); executeActionRoute(router, licenseState); + getOAuthAccessToken(router, licenseState, logger, actionsConfigUtils); getWellKnownEmailServiceRoute(router, licenseState); } diff --git a/x-pack/plugins/actions/server/saved_objects/actions_migrations.test.ts b/x-pack/plugins/actions/server/saved_objects/actions_migrations.test.ts index d350e40c1b362..12bf3984907b4 100644 --- a/x-pack/plugins/actions/server/saved_objects/actions_migrations.test.ts +++ b/x-pack/plugins/actions/server/saved_objects/actions_migrations.test.ts @@ -168,7 +168,7 @@ describe('successful migrations', () => { test('set usesTableApi config property for .servicenow', () => { const migration716 = getActionsMigrations(encryptedSavedObjectsSetup)['7.16.0']; - const action = getMockDataForServiceNow(); + const action = getMockDataForServiceNow716({ usesTableApi: true }); const migratedAction = migration716(action, context); expect(migratedAction).toEqual({ @@ -185,7 +185,7 @@ describe('successful migrations', () => { test('set usesTableApi config property for .servicenow-sir', () => { const migration716 = getActionsMigrations(encryptedSavedObjectsSetup)['7.16.0']; - const action = getMockDataForServiceNow({ actionTypeId: '.servicenow-sir' }); + const action = getMockDataForServiceNow716({ actionTypeId: '.servicenow-sir' }); const migratedAction = migration716(action, context); expect(migratedAction).toEqual({ @@ -215,6 +215,52 @@ describe('successful migrations', () => { expect(migration800(action, context)).toEqual(action); }); }); + + describe('8.3.0', () => { + test('set isOAuth config property for .servicenow', () => { + const migration830 = getActionsMigrations(encryptedSavedObjectsSetup)['8.3.0']; + const action = getMockDataForServiceNow83(); + const migratedAction = migration830(action, context); + + expect(migratedAction.attributes.config).toEqual({ + apiUrl: 'https://example.com', + usesTableApi: true, + isOAuth: false, + }); + }); + + test('set isOAuth config property for .servicenow-sir', () => { + const migration830 = getActionsMigrations(encryptedSavedObjectsSetup)['8.3.0']; + const action = getMockDataForServiceNow83({ actionTypeId: '.servicenow-sir' }); + const migratedAction = migration830(action, context); + + expect(migratedAction.attributes.config).toEqual({ + apiUrl: 'https://example.com', + usesTableApi: true, + isOAuth: false, + }); + }); + + test('set isOAuth config property for .servicenow-itom', () => { + const migration830 = getActionsMigrations(encryptedSavedObjectsSetup)['8.3.0']; + const action = getMockDataForServiceNow83({ actionTypeId: '.servicenow-itom' }); + const migratedAction = migration830(action, context); + + expect(migratedAction.attributes.config).toEqual({ + apiUrl: 'https://example.com', + usesTableApi: true, + isOAuth: false, + }); + }); + + test('it does not set isOAuth config for other connectors', () => { + const migration830 = getActionsMigrations(encryptedSavedObjectsSetup)['8.3.0']; + const action = getMockData(); + const migratedAction = migration830(action, context); + + expect(migratedAction).toEqual(action); + }); + }); }); describe('handles errors during migrations', () => { @@ -348,7 +394,7 @@ function getMockData( }; } -function getMockDataForServiceNow( +function getMockDataForServiceNow716( overwrites: Record = {} ): SavedObjectUnsanitizedDoc> { return { @@ -363,3 +409,11 @@ function getMockDataForServiceNow( type: 'action', }; } + +function getMockDataForServiceNow83( + overwrites: Record = {} +): SavedObjectUnsanitizedDoc> { + return getMockDataForServiceNow716({ + config: { apiUrl: 'https://example.com', usesTableApi: true }, + }); +} diff --git a/x-pack/plugins/actions/server/saved_objects/actions_migrations.ts b/x-pack/plugins/actions/server/saved_objects/actions_migrations.ts index 4c113a09b81b5..f785fa9ee4ac9 100644 --- a/x-pack/plugins/actions/server/saved_objects/actions_migrations.ts +++ b/x-pack/plugins/actions/server/saved_objects/actions_migrations.ts @@ -78,12 +78,22 @@ export function getActionsMigrations( (doc) => doc // no-op ); + const migrationActions830 = createEsoMigration( + encryptedSavedObjects, + (doc): doc is SavedObjectUnsanitizedDoc => + doc.attributes.actionTypeId === '.servicenow' || + doc.attributes.actionTypeId === '.servicenow-sir' || + doc.attributes.actionTypeId === '.servicenow-itom', + pipeMigrations(addIsOAuthToServiceNowConnectors) + ); + return { '7.10.0': executeMigrationWithErrorHandling(migrationActionsTen, '7.10.0'), '7.11.0': executeMigrationWithErrorHandling(migrationActionsEleven, '7.11.0'), '7.14.0': executeMigrationWithErrorHandling(migrationActionsFourteen, '7.14.0'), '7.16.0': executeMigrationWithErrorHandling(migrationActionsSixteen, '7.16.0'), '8.0.0': executeMigrationWithErrorHandling(migrationActions800, '8.0.0'), + '8.3.0': executeMigrationWithErrorHandling(migrationActions830, '8.3.0'), }; } @@ -219,6 +229,29 @@ const addUsesTableApiToServiceNowConnectors = ( }; }; +const addIsOAuthToServiceNowConnectors = ( + doc: SavedObjectUnsanitizedDoc +): SavedObjectUnsanitizedDoc => { + if ( + doc.attributes.actionTypeId !== '.servicenow' && + doc.attributes.actionTypeId !== '.servicenow-sir' && + doc.attributes.actionTypeId !== '.servicenow-itom' + ) { + return doc; + } + + return { + ...doc, + attributes: { + ...doc.attributes, + config: { + ...doc.attributes.config, + isOAuth: false, + }, + }, + }; +}; + function pipeMigrations(...migrations: ActionMigration[]): ActionMigration { return (doc: SavedObjectUnsanitizedDoc) => migrations.reduce((migratedDoc, nextMigration) => nextMigration(migratedDoc), doc); diff --git a/x-pack/plugins/actions/tsconfig.json b/x-pack/plugins/actions/tsconfig.json index aad127a6ca94c..6fced06e2057f 100644 --- a/x-pack/plugins/actions/tsconfig.json +++ b/x-pack/plugins/actions/tsconfig.json @@ -15,6 +15,7 @@ ], "references": [ { "path": "../../../src/core/tsconfig.json" }, + { "path": "../../../src/plugins/data/tsconfig.json" }, { "path": "../spaces/tsconfig.json" }, { "path": "../security/tsconfig.json" }, { "path": "../licensing/tsconfig.json" }, diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md index c77e600df5f0e..eed17e563860b 100644 --- a/x-pack/plugins/alerting/README.md +++ b/x-pack/plugins/alerting/README.md @@ -104,7 +104,7 @@ The following table describes the properties of the `options` object. ### Executor -This is the primary function for a rule type. Whenever the rule needs to execute, this function will perform the execution. It receives a variety of parameters. The following table describes the properties the executor receives. +This is the primary function for a rule type. Whenever the rule needs to execute, this function will perform the execution. This function is for running a custom query that returns documents meeting a condition and report them to the framework using the `services.alertFactory`. The function receives a variety of parameters. The following table describes the properties the executor receives. **executor(options)** diff --git a/x-pack/plugins/alerting/common/rule.ts b/x-pack/plugins/alerting/common/rule.ts index c8f282bf695d7..4509a004c6e58 100644 --- a/x-pack/plugins/alerting/common/rule.ts +++ b/x-pack/plugins/alerting/common/rule.ts @@ -75,6 +75,7 @@ export interface RuleAggregations { ruleEnabledStatus: { enabled: number; disabled: number }; ruleMutedStatus: { muted: number; unmuted: number }; ruleSnoozedStatus: { snoozed: number }; + ruleTags: string[]; } export interface MappedParamsProperties { diff --git a/x-pack/plugins/alerting/kibana.json b/x-pack/plugins/alerting/kibana.json index fc45f22d9c9a6..f4e6a917b19d3 100644 --- a/x-pack/plugins/alerting/kibana.json +++ b/x-pack/plugins/alerting/kibana.json @@ -17,8 +17,9 @@ "features", "kibanaUtils", "licensing", + "spaces", "taskManager" ], - "optionalPlugins": ["usageCollection", "spaces", "security", "monitoringCollection"], + "optionalPlugins": ["usageCollection", "security", "monitoringCollection"], "extraPublicDirs": ["common", "common/parse_duration"] } diff --git a/x-pack/plugins/alerting/server/alerting_authorization_client_factory.ts b/x-pack/plugins/alerting/server/alerting_authorization_client_factory.ts index 3a8a0a072cc16..0ee4cce387d18 100644 --- a/x-pack/plugins/alerting/server/alerting_authorization_client_factory.ts +++ b/x-pack/plugins/alerting/server/alerting_authorization_client_factory.ts @@ -16,8 +16,8 @@ export interface AlertingAuthorizationClientFactoryOpts { ruleTypeRegistry: RuleTypeRegistry; securityPluginSetup?: SecurityPluginSetup; securityPluginStart?: SecurityPluginStart; - getSpace: (request: KibanaRequest) => Promise; - getSpaceId: (request: KibanaRequest) => string | undefined; + getSpace: (request: KibanaRequest) => Promise; + getSpaceId: (request: KibanaRequest) => string; features: FeaturesPluginStart; } @@ -26,8 +26,8 @@ export class AlertingAuthorizationClientFactory { private ruleTypeRegistry!: RuleTypeRegistry; private securityPluginStart?: SecurityPluginStart; private features!: FeaturesPluginStart; - private getSpace!: (request: KibanaRequest) => Promise; - private getSpaceId!: (request: KibanaRequest) => string | undefined; + private getSpace!: (request: KibanaRequest) => Promise; + private getSpaceId!: (request: KibanaRequest) => string; public initialize(options: AlertingAuthorizationClientFactoryOpts) { if (this.isInitialized) { diff --git a/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts b/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts index b9acfed7dcc65..26aa7a706db0a 100644 --- a/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts +++ b/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts @@ -32,6 +32,7 @@ export enum ReadOperations { GetAlertSummary = 'getAlertSummary', GetExecutionLog = 'getExecutionLog', Find = 'find', + GetAuthorizedAlertsIndices = 'getAuthorizedAlertsIndices', } export enum WriteOperations { diff --git a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts index 70ec1524fec16..d2040f8e63f3a 100644 --- a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts +++ b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts @@ -7,7 +7,7 @@ import { random, mean } from 'lodash'; import { SanitizedRule, AlertSummary } from '../types'; -import { IValidatedEvent } from '@kbn/event-log-plugin/server'; +import { IValidatedEvent, millisToNanos, nanosToMillis } from '@kbn/event-log-plugin/server'; import { EVENT_LOG_ACTIONS, EVENT_LOG_PROVIDER, LEGACY_EVENT_LOG_ACTIONS } from '../plugin'; import { alertSummaryFromEventLog } from './alert_summary_from_event_log'; @@ -643,7 +643,7 @@ export class EventsFactory { event: { provider: EVENT_LOG_PROVIDER, action: EVENT_LOG_ACTIONS.execute, - duration: random(2000, 180000) * 1000 * 1000, + duration: millisToNanos(random(2000, 180000)), }, }; @@ -710,7 +710,7 @@ export class EventsFactory { return this.events .filter((ev) => ev?.event?.action === 'execute' && ev?.event?.duration !== undefined) .reduce((res: Record, ev) => { - res[ev?.['@timestamp']!] = ev?.event?.duration! / (1000 * 1000); + res[ev?.['@timestamp']!] = nanosToMillis(ev?.event?.duration!); return res; }, {}); } diff --git a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts index ff9bc776cfb95..54ac23bf94f2a 100644 --- a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts +++ b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts @@ -6,12 +6,10 @@ */ import { mean } from 'lodash'; -import { IEvent } from '@kbn/event-log-plugin/server'; +import { IEvent, nanosToMillis } from '@kbn/event-log-plugin/server'; import { SanitizedRule, AlertSummary, AlertStatus } from '../types'; import { EVENT_LOG_ACTIONS, EVENT_LOG_PROVIDER, LEGACY_EVENT_LOG_ACTIONS } from '../plugin'; -const Millis2Nanos = 1000 * 1000; - export interface AlertSummaryFromEventLogParams { rule: SanitizedRule<{ bar: boolean }>; events: IEvent[]; @@ -111,7 +109,7 @@ export function alertSummaryFromEventLog(params: AlertSummaryFromEventLogParams) } if (event?.event?.duration) { - const eventDirationMillis = event.event.duration / Millis2Nanos; + const eventDirationMillis = nanosToMillis(event.event.duration); eventDurations.push(eventDirationMillis); eventDurationsWithTimestamp[event['@timestamp']!] = eventDirationMillis; } diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.mock.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.mock.ts new file mode 100644 index 0000000000000..bcbb72518e4e4 --- /dev/null +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.mock.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 { PublicMethodsOf } from '@kbn/utility-types'; +import { AlertingEventLogger } from './alerting_event_logger'; + +const createAlertingEventLoggerMock = () => { + const mock: jest.Mocked> = { + initialize: jest.fn(), + start: jest.fn(), + getEvent: jest.fn(), + getStartAndDuration: jest.fn(), + setRuleName: jest.fn(), + setExecutionSucceeded: jest.fn(), + setExecutionFailed: jest.fn(), + logTimeout: jest.fn(), + logAlert: jest.fn(), + logAction: jest.fn(), + done: jest.fn(), + }; + return mock; +}; + +export const alertingEventLoggerMock = { + create: createAlertingEventLoggerMock, +}; diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts new file mode 100644 index 0000000000000..c980d61bb08fe --- /dev/null +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts @@ -0,0 +1,1115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { eventLoggerMock } from '@kbn/event-log-plugin/server/event_logger.mock'; +import { IEvent, SAVED_OBJECT_REL_PRIMARY } from '@kbn/event-log-plugin/server'; +import { + AlertingEventLogger, + RuleContextOpts, + initializeExecuteRecord, + createExecuteStartRecord, + createExecuteTimeoutRecord, + createAlertRecord, + createActionExecuteRecord, + updateEvent, +} from './alerting_event_logger'; +import { UntypedNormalizedRuleType } from '../../rule_type_registry'; +import { + ActionsCompletion, + RecoveredActionGroup, + RuleExecutionStatusErrorReasons, + RuleExecutionStatusWarningReasons, +} from '../../types'; +import { RuleRunMetrics } from '../rule_run_metrics_store'; +import { EVENT_LOG_ACTIONS } from '../../plugin'; + +const mockNow = '2020-01-01T02:00:00.000Z'; +const eventLogger = eventLoggerMock.create(); + +const ruleType: jest.Mocked = { + id: 'test', + name: 'My test rule', + actionGroups: [{ id: 'default', name: 'Default' }, RecoveredActionGroup], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + recoveryActionGroup: RecoveredActionGroup, + executor: jest.fn(), + producer: 'alerts', + ruleTaskTimeout: '1m', +}; + +const context: RuleContextOpts = { + ruleId: '123', + ruleType, + consumer: 'test-consumer', + spaceId: 'test-space', + executionId: 'abcd-efgh-ijklmnop', + taskScheduledAt: new Date('2020-01-01T00:00:00.000Z'), +}; + +const contextWithScheduleDelay = { ...context, taskScheduleDelay: 7200000 }; +const contextWithName = { ...contextWithScheduleDelay, ruleName: 'my-super-cool-rule' }; + +const alert = { + action: EVENT_LOG_ACTIONS.activeInstance, + id: 'aaabbb', + message: `.test-rule-type:123: 'my rule' active alert: 'aaabbb' in actionGroup: 'aGroup'; actionSubGroup: 'bSubGroup'`, + group: 'aGroup', + subgroup: 'bSubgroup', + state: { + start: '2020-01-01T02:00:00.000Z', + end: '2020-01-01T03:00:00.000Z', + duration: '2343252346', + }, +}; + +const action = { + id: 'abc', + typeId: '.email', + alertId: '123', + alertGroup: 'aGroup', + alertSubgroup: 'bSubgroup', +}; + +describe('AlertingEventLogger', () => { + let alertingEventLogger: AlertingEventLogger; + + beforeAll(() => { + jest.useFakeTimers('modern'); + jest.setSystemTime(new Date(mockNow)); + }); + + beforeEach(() => { + jest.resetAllMocks(); + alertingEventLogger = new AlertingEventLogger(eventLogger); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + describe('initialize()', () => { + test('initialization should succeed if alertingEventLogger has not been initialized', () => { + expect(() => alertingEventLogger.initialize(context)).not.toThrow(); + }); + + test('initialization should fail if alertingEventLogger has already been initialized', () => { + alertingEventLogger.initialize(context); + expect(() => alertingEventLogger.initialize(context)).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger already initialized"` + ); + }); + }); + + describe('start()', () => { + test('should throw error if alertingEventLogger has not been initialized', () => { + expect(() => alertingEventLogger.start()).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should throw error if alertingEventLogger rule context is null', () => { + alertingEventLogger.initialize(null as unknown as RuleContextOpts); + expect(() => alertingEventLogger.start()).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should throw error if alertingEventLogger rule context is undefined', () => { + alertingEventLogger.initialize(undefined as unknown as RuleContextOpts); + expect(() => alertingEventLogger.start()).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should call eventLogger "startTiming" and "logEvent"', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + + expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); + expect(eventLogger.logEvent).toHaveBeenCalledTimes(1); + + expect(eventLogger.startTiming).toHaveBeenCalledWith( + initializeExecuteRecord(contextWithScheduleDelay), + new Date(mockNow) + ); + expect(eventLogger.logEvent).toHaveBeenCalledWith( + createExecuteStartRecord(contextWithScheduleDelay, new Date(mockNow)) + ); + }); + + test('should initialize the "execute" event', () => { + mockEventLoggerStartTiming(); + + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + expect(alertingEventLogger.getEvent()).toEqual({ + ...event, + event: { + ...event.event, + start: new Date(mockNow).toISOString(), + }, + }); + }); + }); + + describe('setRuleName()', () => { + test('should throw error if alertingEventLogger has not been initialized', () => { + expect(() => alertingEventLogger.setRuleName('')).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should throw error if event is null', () => { + alertingEventLogger.initialize(context); + expect(() => alertingEventLogger.setRuleName('')).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should update event with rule name correctly', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.setRuleName('my-super-cool-rule'); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + expect(alertingEventLogger.getEvent()).toEqual({ + ...event, + rule: { + ...event.rule, + name: 'my-super-cool-rule', + }, + }); + }); + }); + + describe('setExecutionSucceeded()', () => { + test('should throw error if alertingEventLogger has not been initialized', () => { + expect(() => + alertingEventLogger.setExecutionSucceeded('') + ).toThrowErrorMatchingInlineSnapshot(`"AlertingEventLogger not initialized"`); + }); + + test('should throw error if event is null', () => { + alertingEventLogger.initialize(context); + expect(() => + alertingEventLogger.setExecutionSucceeded('') + ).toThrowErrorMatchingInlineSnapshot(`"AlertingEventLogger not initialized"`); + }); + + test('should update execute event correctly', () => { + mockEventLoggerStartTiming(); + + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.setRuleName('my-super-cool-rule'); + alertingEventLogger.setExecutionSucceeded('success!'); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + expect(alertingEventLogger.getEvent()).toEqual({ + ...event, + event: { + ...event.event, + start: new Date(mockNow).toISOString(), + outcome: 'success', + }, + rule: { + ...event.rule, + name: 'my-super-cool-rule', + }, + message: 'success!', + }); + }); + }); + + describe('setExecutionFailed()', () => { + test('should throw error if alertingEventLogger has not been initialized', () => { + expect(() => + alertingEventLogger.setExecutionFailed('', '') + ).toThrowErrorMatchingInlineSnapshot(`"AlertingEventLogger not initialized"`); + }); + + test('should throw error if event is null', () => { + alertingEventLogger.initialize(context); + expect(() => + alertingEventLogger.setExecutionFailed('', '') + ).toThrowErrorMatchingInlineSnapshot(`"AlertingEventLogger not initialized"`); + }); + + test('should update execute event correctly', () => { + mockEventLoggerStartTiming(); + + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.setExecutionFailed('rule failed!', 'something went wrong!'); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + expect(alertingEventLogger.getEvent()).toEqual({ + ...event, + event: { + ...event.event, + start: new Date(mockNow).toISOString(), + outcome: 'failure', + }, + error: { + message: 'something went wrong!', + }, + message: 'rule failed!', + }); + }); + }); + + describe('logTimeout()', () => { + test('should throw error if alertingEventLogger has not been initialized', () => { + expect(() => alertingEventLogger.logTimeout()).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should throw error if alertingEventLogger rule context is null', () => { + alertingEventLogger.initialize(null as unknown as RuleContextOpts); + expect(() => alertingEventLogger.logTimeout()).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should throw error if alertingEventLogger rule context is undefined', () => { + alertingEventLogger.initialize(undefined as unknown as RuleContextOpts); + expect(() => alertingEventLogger.logTimeout()).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should log timeout event', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.logTimeout(); + + const event = createExecuteTimeoutRecord(contextWithName); + + expect(eventLogger.logEvent).toHaveBeenCalledWith(event); + }); + }); + + describe('logAlert()', () => { + test('should throw error if alertingEventLogger has not been initialized', () => { + expect(() => alertingEventLogger.logAlert(alert)).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should throw error if alertingEventLogger rule context is null', () => { + alertingEventLogger.initialize(null as unknown as RuleContextOpts); + expect(() => alertingEventLogger.logAlert(alert)).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should throw error if alertingEventLogger rule context is undefined', () => { + alertingEventLogger.initialize(undefined as unknown as RuleContextOpts); + expect(() => alertingEventLogger.logAlert(alert)).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should log timeout event', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.logAlert(alert); + + const event = createAlertRecord(contextWithName, alert); + + expect(eventLogger.logEvent).toHaveBeenCalledWith(event); + }); + }); + + describe('logAction()', () => { + test('should throw error if alertingEventLogger has not been initialized', () => { + expect(() => alertingEventLogger.logAction(action)).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should throw error if alertingEventLogger rule context is null', () => { + alertingEventLogger.initialize(null as unknown as RuleContextOpts); + expect(() => alertingEventLogger.logAction(action)).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should throw error if alertingEventLogger rule context is undefined', () => { + alertingEventLogger.initialize(undefined as unknown as RuleContextOpts); + expect(() => alertingEventLogger.logAction(action)).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should log timeout event', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.logAction(action); + + const event = createActionExecuteRecord(contextWithName, action); + + expect(eventLogger.logEvent).toHaveBeenCalledWith(event); + }); + }); + + describe('done()', () => { + test('should throw error if alertingEventLogger has not been initialized', () => { + expect(() => alertingEventLogger.done({})).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should throw error if alertingEventLogger rule context is null', () => { + alertingEventLogger.initialize(null as unknown as RuleContextOpts); + expect(() => alertingEventLogger.done({})).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should throw error if alertingEventLogger rule context is undefined', () => { + alertingEventLogger.initialize(undefined as unknown as RuleContextOpts); + expect(() => alertingEventLogger.done({})).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should throw error if event is null', () => { + alertingEventLogger.initialize(context); + expect(() => alertingEventLogger.done({})).toThrowErrorMatchingInlineSnapshot( + `"AlertingEventLogger not initialized"` + ); + }); + + test('should log event if no status or metrics are provided', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.done({}); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + + expect(eventLogger.logEvent).toHaveBeenCalledWith(event); + }); + + test('should set fields from execution status if provided', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.done({ + status: { lastExecutionDate: new Date('2022-05-05T15:59:54.480Z'), status: 'active' }, + }); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + const loggedEvent = { + ...event, + kibana: { + ...event?.kibana, + alerting: { + status: 'active', + }, + }, + }; + expect(alertingEventLogger.getEvent()).toEqual(loggedEvent); + expect(eventLogger.logEvent).toHaveBeenCalledWith(loggedEvent); + }); + + test('should set fields from execution status if execution status is error', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.done({ + status: { + lastExecutionDate: new Date('2022-05-05T15:59:54.480Z'), + status: 'error', + error: { + reason: RuleExecutionStatusErrorReasons.Execute, + message: 'something went wrong', + }, + }, + }); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + const loggedEvent = { + ...event, + event: { + ...event?.event, + outcome: 'failure', + reason: RuleExecutionStatusErrorReasons.Execute, + }, + error: { + message: 'something went wrong', + }, + kibana: { + ...event?.kibana, + alerting: { + status: 'error', + }, + }, + message: 'test:123: execution failed', + }; + + expect(alertingEventLogger.getEvent()).toEqual(loggedEvent); + expect(eventLogger.logEvent).toHaveBeenCalledWith(loggedEvent); + }); + + test('should set fields from execution status if execution status is error and uses "unknown" if no reason is provided', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.done({ + status: { + lastExecutionDate: new Date('2022-05-05T15:59:54.480Z'), + status: 'error', + error: { + reason: undefined as unknown as RuleExecutionStatusErrorReasons, + message: 'something went wrong', + }, + }, + }); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + const loggedEvent = { + ...event, + event: { + ...event?.event, + outcome: 'failure', + reason: 'unknown', + }, + error: { + message: 'something went wrong', + }, + kibana: { + ...event?.kibana, + alerting: { + status: 'error', + }, + }, + message: 'test:123: execution failed', + }; + + expect(alertingEventLogger.getEvent()).toEqual(loggedEvent); + expect(eventLogger.logEvent).toHaveBeenCalledWith(loggedEvent); + }); + + test('should set fields from execution status if execution status is error and does not overwrite existing error message', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.done({ + status: { + lastExecutionDate: new Date('2022-05-05T15:59:54.480Z'), + status: 'error', + error: { + reason: undefined as unknown as RuleExecutionStatusErrorReasons, + message: 'something went wrong', + }, + }, + }); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + alertingEventLogger.setExecutionFailed( + 'i am an existing error message', + 'i am an existing error message!' + ); + const loggedEvent = { + ...event, + event: { + ...event?.event, + outcome: 'failure', + reason: 'unknown', + }, + error: { + message: 'i am an existing error message!', + }, + kibana: { + ...event?.kibana, + alerting: { + status: 'error', + }, + }, + message: 'i am an existing error message', + }; + + expect(alertingEventLogger.getEvent()).toEqual(loggedEvent); + expect(eventLogger.logEvent).toHaveBeenCalledWith(loggedEvent); + }); + + test('should set fields from execution status if execution status is warning', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.done({ + status: { + lastExecutionDate: new Date('2022-05-05T15:59:54.480Z'), + status: 'warning', + warning: { + reason: RuleExecutionStatusWarningReasons.MAX_EXECUTABLE_ACTIONS, + message: 'something funky happened', + }, + }, + }); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + const loggedEvent = { + ...event, + event: { + ...event?.event, + reason: RuleExecutionStatusWarningReasons.MAX_EXECUTABLE_ACTIONS, + }, + kibana: { + ...event?.kibana, + alerting: { + status: 'warning', + }, + }, + message: 'something funky happened', + }; + + expect(alertingEventLogger.getEvent()).toEqual(loggedEvent); + expect(eventLogger.logEvent).toHaveBeenCalledWith(loggedEvent); + }); + + test('should set fields from execution status if execution status is warning and uses "unknown" if no reason is provided', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.done({ + status: { + lastExecutionDate: new Date('2022-05-05T15:59:54.480Z'), + status: 'warning', + warning: { + reason: undefined as unknown as RuleExecutionStatusWarningReasons, + message: 'something funky happened', + }, + }, + }); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + const loggedEvent = { + ...event, + event: { + ...event?.event, + reason: 'unknown', + }, + kibana: { + ...event?.kibana, + alerting: { + status: 'warning', + }, + }, + message: 'something funky happened', + }; + + expect(alertingEventLogger.getEvent()).toEqual(loggedEvent); + expect(eventLogger.logEvent).toHaveBeenCalledWith(loggedEvent); + }); + + test('should set fields from execution status if execution status is warning and uses existing message if no message is provided', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.done({ + status: { + lastExecutionDate: new Date('2022-05-05T15:59:54.480Z'), + status: 'warning', + warning: { + reason: RuleExecutionStatusWarningReasons.MAX_EXECUTABLE_ACTIONS, + message: undefined as unknown as string, + }, + }, + }); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + alertingEventLogger.setExecutionSucceeded('success!'); + const loggedEvent = { + ...event, + event: { + ...event?.event, + reason: 'maxExecutableActions', + outcome: 'success', + }, + kibana: { + ...event?.kibana, + alerting: { + status: 'warning', + }, + }, + message: 'success!', + }; + + expect(alertingEventLogger.getEvent()).toEqual(loggedEvent); + expect(eventLogger.logEvent).toHaveBeenCalledWith(loggedEvent); + }); + + test('should set fields from execution metrics if provided', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.done({ + metrics: { + numberOfTriggeredActions: 1, + numberOfGeneratedActions: 2, + numberOfActiveAlerts: 3, + numberOfNewAlerts: 4, + numberOfRecoveredAlerts: 5, + numSearches: 6, + esSearchDurationMs: 3300, + totalSearchDurationMs: 10333, + triggeredActionsStatus: ActionsCompletion.COMPLETE, + }, + }); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + const loggedEvent = { + ...event, + kibana: { + ...event.kibana, + alert: { + ...event.kibana?.alert, + rule: { + ...event.kibana?.alert?.rule, + execution: { + ...event.kibana?.alert?.rule?.execution, + metrics: { + number_of_triggered_actions: 1, + number_of_generated_actions: 2, + number_of_active_alerts: 3, + number_of_new_alerts: 4, + number_of_recovered_alerts: 5, + total_number_of_alerts: 8, + number_of_searches: 6, + es_search_duration_ms: 3300, + total_search_duration_ms: 10333, + }, + }, + }, + }, + }, + }; + + expect(alertingEventLogger.getEvent()).toEqual(loggedEvent); + expect(eventLogger.logEvent).toHaveBeenCalledWith(loggedEvent); + }); + + test('should set fields to 0 execution metrics are provided but undefined', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.done({ + metrics: {} as unknown as RuleRunMetrics, + }); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + const loggedEvent = { + ...event, + kibana: { + ...event.kibana, + alert: { + ...event.kibana?.alert, + rule: { + ...event.kibana?.alert?.rule, + execution: { + ...event.kibana?.alert?.rule?.execution, + metrics: { + number_of_triggered_actions: 0, + number_of_generated_actions: 0, + number_of_active_alerts: 0, + number_of_new_alerts: 0, + number_of_recovered_alerts: 0, + total_number_of_alerts: 0, + number_of_searches: 0, + es_search_duration_ms: 0, + total_search_duration_ms: 0, + }, + }, + }, + }, + }, + }; + + expect(alertingEventLogger.getEvent()).toEqual(loggedEvent); + expect(eventLogger.logEvent).toHaveBeenCalledWith(loggedEvent); + }); + }); +}); + +describe('createExecuteStartRecord', () => { + test('should create execute-start record', () => { + const executeRecord = initializeExecuteRecord(contextWithScheduleDelay); + const record = createExecuteStartRecord(contextWithScheduleDelay); + + expect(record).toEqual({ + ...executeRecord, + event: { + ...executeRecord.event, + action: 'execute-start', + }, + message: `rule execution start: "123"`, + }); + }); + + test('should create execute-start record with given start time', () => { + const executeRecord = initializeExecuteRecord(contextWithScheduleDelay); + const record = createExecuteStartRecord( + contextWithScheduleDelay, + new Date('2022-01-01T02:00:00.000Z') + ); + + expect(record).toEqual({ + ...executeRecord, + event: { + ...executeRecord.event, + action: 'execute-start', + start: '2022-01-01T02:00:00.000Z', + }, + message: `rule execution start: "123"`, + }); + }); +}); + +describe('initializeExecuteRecord', () => { + test('should populate initial set of fields in event log record', () => { + const record = initializeExecuteRecord(contextWithScheduleDelay); + + expect(record.event).toBeDefined(); + expect(record.kibana).toBeDefined(); + expect(record.kibana?.alert).toBeDefined(); + expect(record.kibana?.alert?.rule).toBeDefined(); + expect(record.kibana?.alert?.rule?.execution).toBeDefined(); + expect(record.kibana?.saved_objects).toBeDefined(); + expect(record.kibana?.space_ids).toBeDefined(); + expect(record.kibana?.task).toBeDefined(); + expect(record.rule).toBeDefined(); + + // these fields should be explicitly set + expect(record.event?.action).toEqual('execute'); + expect(record.event?.kind).toEqual('alert'); + expect(record.event?.category).toEqual([contextWithScheduleDelay.ruleType.producer]); + expect(record.kibana?.alert?.rule?.rule_type_id).toEqual(contextWithScheduleDelay.ruleType.id); + expect(record.kibana?.alert?.rule?.consumer).toEqual(contextWithScheduleDelay.consumer); + expect(record.kibana?.alert?.rule?.execution?.uuid).toEqual( + contextWithScheduleDelay.executionId + ); + expect(record.kibana?.saved_objects).toEqual([ + { + id: contextWithScheduleDelay.ruleId, + type: 'alert', + type_id: contextWithScheduleDelay.ruleType.id, + rel: SAVED_OBJECT_REL_PRIMARY, + }, + ]); + expect(record.kibana?.space_ids).toEqual([contextWithScheduleDelay.spaceId]); + expect(record.kibana?.task?.scheduled).toEqual( + contextWithScheduleDelay.taskScheduledAt.toISOString() + ); + expect(record.kibana?.task?.schedule_delay).toEqual( + contextWithScheduleDelay.taskScheduleDelay * 1000000 + ); + expect(record?.rule?.id).toEqual(contextWithScheduleDelay.ruleId); + expect(record?.rule?.license).toEqual(contextWithScheduleDelay.ruleType.minimumLicenseRequired); + expect(record?.rule?.category).toEqual(contextWithScheduleDelay.ruleType.id); + expect(record?.rule?.ruleset).toEqual(contextWithScheduleDelay.ruleType.producer); + + // these fields should not be set by this function + expect(record['@timestamp']).toBeUndefined(); + expect(record.event?.provider).toBeUndefined(); + expect(record.event?.start).toBeUndefined(); + expect(record.event?.outcome).toBeUndefined(); + expect(record.event?.end).toBeUndefined(); + expect(record.event?.duration).toBeUndefined(); + expect(record.kibana?.alert?.rule?.execution?.metrics).toBeUndefined(); + expect(record.kibana?.alerting).toBeUndefined(); + expect(record.kibana?.server_uuid).toBeUndefined(); + expect(record.kibana?.version).toBeUndefined(); + expect(record?.rule?.name).toBeUndefined(); + expect(record?.message).toBeUndefined(); + expect(record?.ecs).toBeUndefined(); + }); +}); + +describe('createExecuteTimeoutRecord', () => { + test('should populate expected fields in event log record', () => { + const record = createExecuteTimeoutRecord(contextWithName); + + expect(record.event).toBeDefined(); + expect(record.kibana).toBeDefined(); + expect(record.kibana?.alert).toBeDefined(); + expect(record.kibana?.alert?.rule).toBeDefined(); + expect(record.kibana?.alert?.rule?.execution).toBeDefined(); + expect(record.kibana?.saved_objects).toBeDefined(); + expect(record.kibana?.space_ids).toBeDefined(); + expect(record.rule).toBeDefined(); + + // these fields should be explicitly set + expect(record.event?.action).toEqual('execute-timeout'); + expect(record.event?.kind).toEqual('alert'); + expect(record.message).toEqual( + `rule: test:123: 'my-super-cool-rule' execution cancelled due to timeout - exceeded rule type timeout of 1m` + ); + expect(record.event?.category).toEqual([contextWithName.ruleType.producer]); + expect(record.kibana?.alert?.rule?.rule_type_id).toEqual(contextWithName.ruleType.id); + expect(record.kibana?.alert?.rule?.consumer).toEqual(contextWithName.consumer); + expect(record.kibana?.alert?.rule?.execution?.uuid).toEqual(contextWithName.executionId); + expect(record.kibana?.saved_objects).toEqual([ + { + id: contextWithName.ruleId, + type: 'alert', + type_id: contextWithName.ruleType.id, + rel: SAVED_OBJECT_REL_PRIMARY, + }, + ]); + expect(record.kibana?.space_ids).toEqual([contextWithName.spaceId]); + expect(record?.rule?.id).toEqual(contextWithName.ruleId); + expect(record?.rule?.license).toEqual(contextWithName.ruleType.minimumLicenseRequired); + expect(record?.rule?.category).toEqual(contextWithName.ruleType.id); + expect(record?.rule?.ruleset).toEqual(contextWithName.ruleType.producer); + expect(record?.rule?.name).toEqual(contextWithName.ruleName); + + // these fields should not be set by this function + expect(record['@timestamp']).toBeUndefined(); + expect(record.event?.provider).toBeUndefined(); + expect(record.event?.start).toBeUndefined(); + expect(record.event?.outcome).toBeUndefined(); + expect(record.event?.end).toBeUndefined(); + expect(record.event?.duration).toBeUndefined(); + expect(record.kibana?.alert?.rule?.execution?.metrics).toBeUndefined(); + expect(record.kibana?.alerting).toBeUndefined(); + expect(record.kibana?.server_uuid).toBeUndefined(); + expect(record.kibana?.task).toBeUndefined(); + expect(record.kibana?.version).toBeUndefined(); + expect(record?.ecs).toBeUndefined(); + }); +}); + +describe('createAlertRecord', () => { + test('should populate expected fields in event log record', () => { + const record = createAlertRecord(contextWithName, alert); + + // these fields should be explicitly set + expect(record.event?.action).toEqual('active-instance'); + expect(record.event?.kind).toEqual('alert'); + expect(record.event?.category).toEqual([contextWithName.ruleType.producer]); + expect(record.event?.start).toEqual(alert.state.start); + expect(record.event?.end).toEqual(alert.state.end); + expect(record.event?.duration).toEqual(alert.state.duration); + expect(record.message).toEqual( + `.test-rule-type:123: 'my rule' active alert: 'aaabbb' in actionGroup: 'aGroup'; actionSubGroup: 'bSubGroup'` + ); + expect(record.kibana?.alert?.rule?.rule_type_id).toEqual(contextWithName.ruleType.id); + expect(record.kibana?.alert?.rule?.consumer).toEqual(contextWithName.consumer); + expect(record.kibana?.alert?.rule?.execution?.uuid).toEqual(contextWithName.executionId); + expect(record.kibana?.alerting?.instance_id).toEqual(alert.id); + expect(record.kibana?.alerting?.action_group_id).toEqual(alert.group); + expect(record.kibana?.alerting?.action_subgroup).toEqual(alert.subgroup); + expect(record.kibana?.saved_objects).toEqual([ + { + id: contextWithName.ruleId, + type: 'alert', + type_id: contextWithName.ruleType.id, + rel: SAVED_OBJECT_REL_PRIMARY, + }, + ]); + expect(record.kibana?.space_ids).toEqual([contextWithName.spaceId]); + expect(record?.rule?.id).toEqual(contextWithName.ruleId); + expect(record?.rule?.license).toEqual(contextWithName.ruleType.minimumLicenseRequired); + expect(record?.rule?.category).toEqual(contextWithName.ruleType.id); + expect(record?.rule?.ruleset).toEqual(contextWithName.ruleType.producer); + expect(record?.rule?.name).toEqual(contextWithName.ruleName); + + // these fields should not be set by this function + expect(record['@timestamp']).toBeUndefined(); + expect(record.event?.provider).toBeUndefined(); + expect(record.event?.outcome).toBeUndefined(); + expect(record.kibana?.alert?.rule?.execution?.metrics).toBeUndefined(); + expect(record.kibana?.server_uuid).toBeUndefined(); + expect(record.kibana?.task).toBeUndefined(); + expect(record.kibana?.version).toBeUndefined(); + expect(record?.ecs).toBeUndefined(); + }); +}); + +describe('createActionExecuteRecord', () => { + test('should populate expected fields in event log record', () => { + const record = createActionExecuteRecord(contextWithName, action); + + expect(record.event).toBeDefined(); + expect(record.kibana).toBeDefined(); + expect(record.kibana?.alert).toBeDefined(); + expect(record.kibana?.alert?.rule).toBeDefined(); + expect(record.kibana?.alert?.rule?.execution).toBeDefined(); + expect(record.kibana?.saved_objects).toBeDefined(); + expect(record.kibana?.space_ids).toBeDefined(); + expect(record.rule).toBeDefined(); + + // these fields should be explicitly set + expect(record.event?.action).toEqual('execute-action'); + expect(record.event?.kind).toEqual('alert'); + expect(record.event?.category).toEqual([contextWithName.ruleType.producer]); + expect(record.message).toEqual( + `alert: test:123: 'my-super-cool-rule' instanceId: '123' scheduled actionGroup(subgroup): 'aGroup(bSubgroup)' action: .email:abc` + ); + expect(record.kibana?.alert?.rule?.rule_type_id).toEqual(contextWithName.ruleType.id); + expect(record.kibana?.alert?.rule?.consumer).toEqual(contextWithName.consumer); + expect(record.kibana?.alert?.rule?.execution?.uuid).toEqual(contextWithName.executionId); + expect(record.kibana?.alerting?.instance_id).toEqual(action.alertId); + expect(record.kibana?.alerting?.action_group_id).toEqual(action.alertGroup); + expect(record.kibana?.alerting?.action_subgroup).toEqual(action.alertSubgroup); + expect(record.kibana?.saved_objects).toEqual([ + { + id: contextWithName.ruleId, + type: 'alert', + type_id: contextWithName.ruleType.id, + rel: SAVED_OBJECT_REL_PRIMARY, + }, + { + id: action.id, + type: 'action', + type_id: action.typeId, + }, + ]); + expect(record.kibana?.space_ids).toEqual([contextWithName.spaceId]); + expect(record?.rule?.id).toEqual(contextWithName.ruleId); + expect(record?.rule?.license).toEqual(contextWithName.ruleType.minimumLicenseRequired); + expect(record?.rule?.category).toEqual(contextWithName.ruleType.id); + expect(record?.rule?.ruleset).toEqual(contextWithName.ruleType.producer); + expect(record?.rule?.name).toEqual(contextWithName.ruleName); + + // these fields should not be set by this function + expect(record['@timestamp']).toBeUndefined(); + expect(record.event?.provider).toBeUndefined(); + expect(record.event?.start).toBeUndefined(); + expect(record.event?.outcome).toBeUndefined(); + expect(record.event?.end).toBeUndefined(); + expect(record.event?.duration).toBeUndefined(); + expect(record.kibana?.alert?.rule?.execution?.metrics).toBeUndefined(); + expect(record.kibana?.server_uuid).toBeUndefined(); + expect(record.kibana?.task).toBeUndefined(); + expect(record.kibana?.version).toBeUndefined(); + expect(record?.ecs).toBeUndefined(); + }); +}); + +describe('updateEvent', () => { + let event: IEvent; + let expectedEvent: IEvent; + beforeEach(() => { + event = initializeExecuteRecord(contextWithScheduleDelay); + expectedEvent = initializeExecuteRecord(contextWithScheduleDelay); + }); + + test('throws error if event is null', () => { + expect(() => updateEvent(null as unknown as IEvent, {})).toThrowErrorMatchingInlineSnapshot( + `"Cannot update event because it is not initialized."` + ); + }); + + test('throws error if event is undefined', () => { + expect(() => + updateEvent(undefined as unknown as IEvent, {}) + ).toThrowErrorMatchingInlineSnapshot(`"Cannot update event because it is not initialized."`); + }); + + test('updates event message if provided', () => { + updateEvent(event, { message: 'tell me something good' }); + expect(event).toEqual({ + ...expectedEvent, + message: 'tell me something good', + }); + }); + + test('updates event outcome if provided', () => { + updateEvent(event, { outcome: 'yay' }); + expect(event).toEqual({ + ...expectedEvent, + event: { + ...expectedEvent?.event, + outcome: 'yay', + }, + }); + }); + + test('updates event error if provided', () => { + updateEvent(event, { error: 'oh no' }); + expect(event).toEqual({ + ...expectedEvent, + error: { + message: 'oh no', + }, + }); + }); + + test('updates event rule name if provided', () => { + updateEvent(event, { ruleName: 'test rule' }); + expect(event).toEqual({ + ...expectedEvent, + rule: { + ...expectedEvent?.rule, + name: 'test rule', + }, + }); + }); + + test('updates event status if provided', () => { + updateEvent(event, { status: 'ok' }); + expect(event).toEqual({ + ...expectedEvent, + kibana: { + ...expectedEvent?.kibana, + alerting: { + status: 'ok', + }, + }, + }); + }); + + test('updates event reason if provided', () => { + updateEvent(event, { reason: 'my-reason' }); + expect(event).toEqual({ + ...expectedEvent, + event: { + ...expectedEvent?.event, + reason: 'my-reason', + }, + }); + }); + + test('updates all fields if provided', () => { + updateEvent(event, { + message: 'tell me something good', + outcome: 'yay', + error: 'oh no', + ruleName: 'test rule', + status: 'ok', + reason: 'my-reason', + }); + expect(event).toEqual({ + ...expectedEvent, + message: 'tell me something good', + kibana: { + ...expectedEvent?.kibana, + alerting: { + status: 'ok', + }, + }, + event: { + ...expectedEvent?.event, + outcome: 'yay', + reason: 'my-reason', + }, + error: { + message: 'oh no', + }, + rule: { + ...expectedEvent?.rule, + name: 'test rule', + }, + }); + }); +}); + +function mockEventLoggerStartTiming() { + eventLogger.startTiming.mockImplementationOnce((event: IEvent, startTime?: Date) => { + if (event == null) return; + event.event = event.event || {}; + + const start = startTime ?? new Date(); + event.event.start = start.toISOString(); + }); +} diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts new file mode 100644 index 0000000000000..74a8a26f531f2 --- /dev/null +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts @@ -0,0 +1,389 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IEvent, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '@kbn/event-log-plugin/server'; +import { EVENT_LOG_ACTIONS } from '../../plugin'; +import { UntypedNormalizedRuleType } from '../../rule_type_registry'; +import { AlertInstanceState, RuleExecutionStatus } from '../../types'; +import { createAlertEventLogRecordObject } from '../create_alert_event_log_record_object'; +import { RuleRunMetrics } from '../rule_run_metrics_store'; + +// 1,000,000 nanoseconds in 1 millisecond +const Millis2Nanos = 1000 * 1000; + +export interface RuleContextOpts { + ruleId: string; + ruleType: UntypedNormalizedRuleType; + consumer: string; + namespace?: string; + spaceId: string; + executionId: string; + taskScheduledAt: Date; + ruleName?: string; +} + +type RuleContext = RuleContextOpts & { + taskScheduleDelay: number; +}; + +interface DoneOpts { + status?: RuleExecutionStatus; + metrics?: RuleRunMetrics | null; +} + +interface AlertOpts { + action: string; + id: string; + message: string; + group?: string; + subgroup?: string; + state?: AlertInstanceState; +} + +interface ActionOpts { + id: string; + typeId: string; + alertId: string; + alertGroup?: string; + alertSubgroup?: string; +} + +export class AlertingEventLogger { + private eventLogger: IEventLogger; + private isInitialized = false; + private startTime?: Date; + private ruleContext?: RuleContextOpts; + + // this is the "execute" event that will be updated over the lifecycle of this class + private event: IEvent; + + constructor(eventLogger: IEventLogger) { + this.eventLogger = eventLogger; + } + + // For testing purposes + public getEvent(): IEvent { + return this.event; + } + + public initialize(context: RuleContextOpts) { + if (this.isInitialized) { + throw new Error('AlertingEventLogger already initialized'); + } + this.isInitialized = true; + this.ruleContext = context; + } + + public start() { + if (!this.isInitialized || !this.ruleContext) { + throw new Error('AlertingEventLogger not initialized'); + } + + this.startTime = new Date(); + + const context = { + ...this.ruleContext, + taskScheduleDelay: this.startTime.getTime() - this.ruleContext.taskScheduledAt.getTime(), + }; + + // Initialize the "execute" event + this.event = initializeExecuteRecord(context); + this.eventLogger.startTiming(this.event, this.startTime); + + // Create and log "execute-start" event + const executeStartEvent = createExecuteStartRecord(context, this.startTime); + this.eventLogger.logEvent(executeStartEvent); + } + + public getStartAndDuration(): { start?: Date; duration?: string | number } { + return { start: this.startTime, duration: this.event?.event?.duration }; + } + + public setRuleName(ruleName: string) { + if (!this.isInitialized || !this.event || !this.ruleContext) { + throw new Error('AlertingEventLogger not initialized'); + } + + this.ruleContext.ruleName = ruleName; + updateEvent(this.event, { ruleName }); + } + + public setExecutionSucceeded(message: string) { + if (!this.isInitialized || !this.event) { + throw new Error('AlertingEventLogger not initialized'); + } + + updateEvent(this.event, { message, outcome: 'success' }); + } + + public setExecutionFailed(message: string, errorMessage: string) { + if (!this.isInitialized || !this.event) { + throw new Error('AlertingEventLogger not initialized'); + } + + updateEvent(this.event, { message, outcome: 'failure', error: errorMessage }); + } + + public logTimeout() { + if (!this.isInitialized || !this.ruleContext) { + throw new Error('AlertingEventLogger not initialized'); + } + + this.eventLogger.logEvent(createExecuteTimeoutRecord(this.ruleContext)); + } + + public logAlert(alert: AlertOpts) { + if (!this.isInitialized || !this.ruleContext) { + throw new Error('AlertingEventLogger not initialized'); + } + + this.eventLogger.logEvent(createAlertRecord(this.ruleContext, alert)); + } + + public logAction(action: ActionOpts) { + if (!this.isInitialized || !this.ruleContext) { + throw new Error('AlertingEventLogger not initialized'); + } + + this.eventLogger.logEvent(createActionExecuteRecord(this.ruleContext, action)); + } + + public done({ status, metrics }: DoneOpts) { + if (!this.isInitialized || !this.event || !this.ruleContext) { + throw new Error('AlertingEventLogger not initialized'); + } + + this.eventLogger.stopTiming(this.event); + + if (status) { + updateEvent(this.event, { status: status.status }); + + if (status.error) { + updateEvent(this.event, { + outcome: 'failure', + reason: status.error?.reason || 'unknown', + error: this.event?.error?.message || status.error.message, + ...(this.event.message + ? {} + : { + message: `${this.ruleContext.ruleType.id}:${this.ruleContext.ruleId}: execution failed`, + }), + }); + } else { + if (status.warning) { + updateEvent(this.event, { + reason: status.warning?.reason || 'unknown', + message: status.warning?.message || this.event?.message, + }); + } + } + } + + if (metrics) { + updateEvent(this.event, { metrics }); + } + + this.eventLogger.logEvent(this.event); + } +} + +export function createExecuteStartRecord(context: RuleContext, startTime?: Date) { + const event = initializeExecuteRecord(context); + return { + ...event, + event: { + ...event.event, + action: EVENT_LOG_ACTIONS.executeStart, + ...(startTime ? { start: startTime.toISOString() } : {}), + }, + message: `rule execution start: "${context.ruleId}"`, + }; +} + +export function createAlertRecord(context: RuleContextOpts, alert: AlertOpts) { + return createAlertEventLogRecordObject({ + ruleId: context.ruleId, + ruleType: context.ruleType, + consumer: context.consumer, + namespace: context.namespace, + spaceId: context.spaceId, + executionId: context.executionId, + action: alert.action, + state: alert.state, + instanceId: alert.id, + group: alert.group, + subgroup: alert.subgroup, + message: alert.message, + savedObjects: [ + { + id: context.ruleId, + type: 'alert', + typeId: context.ruleType.id, + relation: SAVED_OBJECT_REL_PRIMARY, + }, + ], + ruleName: context.ruleName, + }); +} + +export function createActionExecuteRecord(context: RuleContextOpts, action: ActionOpts) { + return createAlertEventLogRecordObject({ + ruleId: context.ruleId, + ruleType: context.ruleType, + consumer: context.consumer, + namespace: context.namespace, + spaceId: context.spaceId, + executionId: context.executionId, + action: EVENT_LOG_ACTIONS.executeAction, + instanceId: action.alertId, + group: action.alertGroup, + subgroup: action.alertSubgroup, + message: `alert: ${context.ruleType.id}:${context.ruleId}: '${context.ruleName}' instanceId: '${ + action.alertId + }' scheduled ${ + action.alertSubgroup + ? `actionGroup(subgroup): '${action.alertGroup}(${action.alertSubgroup})'` + : `actionGroup: '${action.alertGroup}'` + } action: ${action.typeId}:${action.id}`, + savedObjects: [ + { + id: context.ruleId, + type: 'alert', + typeId: context.ruleType.id, + relation: SAVED_OBJECT_REL_PRIMARY, + }, + { + type: 'action', + id: action.id, + typeId: action.typeId, + }, + ], + ruleName: context.ruleName, + }); +} + +export function createExecuteTimeoutRecord(context: RuleContextOpts) { + return createAlertEventLogRecordObject({ + ruleId: context.ruleId, + ruleType: context.ruleType, + consumer: context.consumer, + namespace: context.namespace, + spaceId: context.spaceId, + executionId: context.executionId, + action: EVENT_LOG_ACTIONS.executeTimeout, + message: `rule: ${context.ruleType.id}:${context.ruleId}: '${ + context.ruleName ?? '' + }' execution cancelled due to timeout - exceeded rule type timeout of ${ + context.ruleType.ruleTaskTimeout + }`, + savedObjects: [ + { + id: context.ruleId, + type: 'alert', + typeId: context.ruleType.id, + relation: SAVED_OBJECT_REL_PRIMARY, + }, + ], + ruleName: context.ruleName, + }); +} + +export function initializeExecuteRecord(context: RuleContext) { + return createAlertEventLogRecordObject({ + ruleId: context.ruleId, + ruleType: context.ruleType, + consumer: context.consumer, + namespace: context.namespace, + spaceId: context.spaceId, + executionId: context.executionId, + action: EVENT_LOG_ACTIONS.execute, + task: { + scheduled: context.taskScheduledAt.toISOString(), + scheduleDelay: Millis2Nanos * context.taskScheduleDelay, + }, + savedObjects: [ + { + id: context.ruleId, + type: 'alert', + typeId: context.ruleType.id, + relation: SAVED_OBJECT_REL_PRIMARY, + }, + ], + }); +} + +interface UpdateEventOpts { + message?: string; + outcome?: string; + error?: string; + ruleName?: string; + status?: string; + reason?: string; + metrics?: RuleRunMetrics; +} +export function updateEvent(event: IEvent, opts: UpdateEventOpts) { + const { message, outcome, error, ruleName, status, reason, metrics } = opts; + if (!event) { + throw new Error('Cannot update event because it is not initialized.'); + } + if (message) { + event.message = message; + } + + if (outcome) { + event.event = event.event || {}; + event.event.outcome = outcome; + } + + if (error) { + event.error = event.error || {}; + event.error.message = error; + } + + if (ruleName) { + event.rule = { + ...event.rule, + name: ruleName, + }; + } + + if (status) { + event.kibana = event.kibana || {}; + event.kibana.alerting = event.kibana.alerting || {}; + event.kibana.alerting.status = status; + } + + if (reason) { + event.event = event.event || {}; + event.event.reason = reason; + } + + if (metrics) { + event.kibana = event.kibana || {}; + event.kibana.alert = event.kibana.alert || {}; + event.kibana.alert.rule = event.kibana.alert.rule || {}; + event.kibana.alert.rule.execution = event.kibana.alert.rule.execution || {}; + event.kibana.alert.rule.execution.metrics = { + number_of_triggered_actions: metrics.numberOfTriggeredActions + ? metrics.numberOfTriggeredActions + : 0, + number_of_generated_actions: metrics.numberOfGeneratedActions + ? metrics.numberOfGeneratedActions + : 0, + number_of_active_alerts: metrics.numberOfActiveAlerts ? metrics.numberOfActiveAlerts : 0, + number_of_new_alerts: metrics.numberOfNewAlerts ? metrics.numberOfNewAlerts : 0, + number_of_recovered_alerts: metrics.numberOfRecoveredAlerts + ? metrics.numberOfRecoveredAlerts + : 0, + total_number_of_alerts: + (metrics.numberOfActiveAlerts ?? 0) + (metrics.numberOfRecoveredAlerts ?? 0), + number_of_searches: metrics.numSearches ? metrics.numSearches : 0, + es_search_duration_ms: metrics.esSearchDurationMs ? metrics.esSearchDurationMs : 0, + total_search_duration_ms: metrics.totalSearchDurationMs ? metrics.totalSearchDurationMs : 0, + }; + } +} diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts index 51b43793efd45..cd7eda500d15b 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts @@ -71,7 +71,7 @@ export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecor category: [ruleType.producer], ...(state?.start ? { start: state.start as string } : {}), ...(state?.end ? { end: state.end as string } : {}), - ...(state?.duration !== undefined ? { duration: state.duration as number } : {}), + ...(state?.duration !== undefined ? { duration: state.duration as string } : {}), }, kibana: { alert: { diff --git a/x-pack/plugins/alerting/server/plugin.test.ts b/x-pack/plugins/alerting/server/plugin.test.ts index 29739d2ca53d9..5eba1353df216 100644 --- a/x-pack/plugins/alerting/server/plugin.test.ts +++ b/x-pack/plugins/alerting/server/plugin.test.ts @@ -21,6 +21,8 @@ import { eventLogMock } from '@kbn/event-log-plugin/server/mocks'; import { actionsMock } from '@kbn/actions-plugin/server/mocks'; import { dataPluginMock } from '@kbn/data-plugin/server/mocks'; import { monitoringCollectionMock } from '@kbn/monitoring-collection-plugin/server/mocks'; +import { PluginSetup as DataPluginSetup } from '@kbn/data-plugin/server'; +import { spacesMock } from '@kbn/spaces-plugin/server/mocks'; const generateAlertingConfig = (): AlertingConfig => ({ healthCheck: { @@ -65,6 +67,7 @@ describe('Alerting Plugin', () => { actions: actionsMock.createSetup(), statusService: statusServiceMock.createSetupContract(), monitoringCollection: monitoringCollectionMock.createSetup(), + data: dataPluginMock.createSetupContract() as unknown as DataPluginSetup, }; let plugin: AlertingPlugin; @@ -206,12 +209,14 @@ describe('Alerting Plugin', () => { actions: actionsMock.createSetup(), statusService: statusServiceMock.createSetupContract(), monitoringCollection: monitoringCollectionMock.createSetup(), + data: dataPluginMock.createSetupContract() as unknown as DataPluginSetup, }); const startContract = plugin.start(coreMock.createStart(), { actions: actionsMock.createStart(), encryptedSavedObjects: encryptedSavedObjectsMock.createStart(), features: mockFeatures(), + spaces: spacesMock.createStart(), licensing: licensingMock.createStart(), eventLog: eventLogMock.createStart(), taskManager: taskManagerMock.createStart(), @@ -244,12 +249,14 @@ describe('Alerting Plugin', () => { actions: actionsMock.createSetup(), statusService: statusServiceMock.createSetupContract(), monitoringCollection: monitoringCollectionMock.createSetup(), + data: dataPluginMock.createSetupContract() as unknown as DataPluginSetup, }); const startContract = plugin.start(coreMock.createStart(), { actions: actionsMock.createStart(), encryptedSavedObjects: encryptedSavedObjectsMock.createStart(), features: mockFeatures(), + spaces: spacesMock.createStart(), licensing: licensingMock.createStart(), eventLog: eventLogMock.createStart(), taskManager: taskManagerMock.createStart(), @@ -293,12 +300,14 @@ describe('Alerting Plugin', () => { actions: actionsMock.createSetup(), statusService: statusServiceMock.createSetupContract(), monitoringCollection: monitoringCollectionMock.createSetup(), + data: dataPluginMock.createSetupContract() as unknown as DataPluginSetup, }); const startContract = plugin.start(coreMock.createStart(), { actions: actionsMock.createStart(), encryptedSavedObjects: encryptedSavedObjectsMock.createStart(), features: mockFeatures(), + spaces: spacesMock.createStart(), licensing: licensingMock.createStart(), eventLog: eventLogMock.createStart(), taskManager: taskManagerMock.createStart(), diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index cfd2701e1f885..063c221ea98db 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -10,6 +10,7 @@ import { BehaviorSubject } from 'rxjs'; import { pick } from 'lodash'; import { UsageCollectionSetup, UsageCounter } from '@kbn/usage-collection-plugin/server'; import { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/server'; +import { PluginSetup as DataPluginSetup } from '@kbn/data-plugin/server'; import { EncryptedSavedObjectsPluginSetup, EncryptedSavedObjectsPluginStart, @@ -140,6 +141,7 @@ export interface AlertingPluginsSetup { eventLog: IEventLogService; statusService: StatusServiceSetup; monitoringCollection: MonitoringCollectionSetup; + data: DataPluginSetup; } export interface AlertingPluginsStart { @@ -149,7 +151,7 @@ export interface AlertingPluginsStart { features: FeaturesPluginStart; eventLog: IEventLogClientService; licensing: LicensingPluginStart; - spaces?: SpacesPluginStart; + spaces: SpacesPluginStart; security?: SecurityPluginStart; data: DataPluginStart; } @@ -247,12 +249,16 @@ export class AlertingPlugin { // Usage counter for telemetry this.usageCounter = plugins.usageCollection?.createUsageCounter(ALERTS_FEATURE_ID); + const getSearchSourceMigrations = plugins.data.search.searchSource.getAllMigrations.bind( + plugins.data.search.searchSource + ); setupSavedObjects( core.savedObjects, plugins.encryptedSavedObjects, this.ruleTypeRegistry, this.logger, - plugins.actions.isPreconfiguredConnector + plugins.actions.isPreconfiguredConnector, + getSearchSourceMigrations ); initializeApiKeyInvalidator( @@ -374,10 +380,10 @@ export class AlertingPlugin { securityPluginSetup: security, securityPluginStart: plugins.security, async getSpace(request: KibanaRequest) { - return plugins.spaces?.spacesService.getActiveSpace(request); + return plugins.spaces.spacesService.getActiveSpace(request); }, getSpaceId(request: KibanaRequest) { - return plugins.spaces?.spacesService.getSpaceId(request); + return plugins.spaces.spacesService.getSpaceId(request); }, features: plugins.features, }); diff --git a/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts b/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts index 7123f1bf4ad6c..8c24b457df565 100644 --- a/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts +++ b/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts @@ -60,6 +60,7 @@ describe('aggregateRulesRoute', () => { ruleSnoozedStatus: { snoozed: 4, }, + ruleTags: ['a', 'b', 'c'], }; rulesClient.aggregate.mockResolvedValueOnce(aggregateResult); @@ -94,6 +95,11 @@ describe('aggregateRulesRoute', () => { "rule_snoozed_status": Object { "snoozed": 4, }, + "rule_tags": Array [ + "a", + "b", + "c", + ], }, } `); @@ -129,6 +135,7 @@ describe('aggregateRulesRoute', () => { rule_snoozed_status: { snoozed: 4, }, + rule_tags: ['a', 'b', 'c'], }, }); }); diff --git a/x-pack/plugins/alerting/server/routes/aggregate_rules.ts b/x-pack/plugins/alerting/server/routes/aggregate_rules.ts index 312def72dd65e..c48c74fc28754 100644 --- a/x-pack/plugins/alerting/server/routes/aggregate_rules.ts +++ b/x-pack/plugins/alerting/server/routes/aggregate_rules.ts @@ -50,6 +50,7 @@ const rewriteBodyRes: RewriteResponseCase = ({ ruleEnabledStatus, ruleMutedStatus, ruleSnoozedStatus, + ruleTags, ...rest }) => ({ ...rest, @@ -57,6 +58,7 @@ const rewriteBodyRes: RewriteResponseCase = ({ rule_enabled_status: ruleEnabledStatus, rule_muted_status: ruleMutedStatus, rule_snoozed_status: ruleSnoozedStatus, + rule_tags: ruleTags, }); export const aggregateRulesRoute = ( diff --git a/x-pack/plugins/alerting/server/rules_client/lib/validate_attributes.ts b/x-pack/plugins/alerting/server/rules_client/lib/validate_attributes.ts index ad17ede1b99ad..208709a4b8b35 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/validate_attributes.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/validate_attributes.ts @@ -7,7 +7,7 @@ import { KueryNode } from '@kbn/es-query'; import { get, isEmpty } from 'lodash'; -import mappings from '../../saved_objects/mappings.json'; +import { alertMappings } from '../../saved_objects/mappings'; const astFunctionType = ['is', 'range', 'nested']; @@ -92,7 +92,7 @@ export const iterateFilterKureyNode = ({ } else if (ast.type === 'literal' && ast.value && typeof ast.value === 'string') { const key = ast.value.replace('.attributes', ''); const mappingKey = 'properties.' + key.split('.').join('.properties.'); - const field = get(mappings, mappingKey); + const field = get(alertMappings, mappingKey); if (field != null && field.type === 'nested') { localNestedKeys = ast.value; } diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index d925315928f2c..e229b15fcd1cd 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -133,6 +133,12 @@ export interface RuleAggregation { doc_count: number; }>; }; + tags: { + buckets: Array<{ + key: string; + doc_count: number; + }>; + }; } export interface ConstructorOptions { @@ -144,7 +150,7 @@ export interface ConstructorOptions { ruleTypeRegistry: RuleTypeRegistry; minimumScheduleInterval: AlertingRulesConfig['minimumScheduleInterval']; encryptedSavedObjectsClient: EncryptedSavedObjectsClient; - spaceId?: string; + spaceId: string; namespace?: string; getUserName: () => Promise; createAPIKey: (name: string) => Promise; @@ -200,6 +206,7 @@ export interface AggregateResult { ruleEnabledStatus?: { enabled: number; disabled: number }; ruleMutedStatus?: { muted: number; unmuted: number }; ruleSnoozedStatus?: { snoozed: number }; + ruleTags?: string[]; } export interface FindResult { @@ -281,7 +288,7 @@ const alertingAuthorizationFilterOpts: AlertingAuthorizationFilterOpts = { export class RulesClient { private readonly logger: Logger; private readonly getUserName: () => Promise; - private readonly spaceId?: string; + private readonly spaceId: string; private readonly namespace?: string; private readonly taskManager: TaskManagerStartContract; private readonly unsecuredSavedObjectsClient: SavedObjectsClientContract; @@ -921,6 +928,9 @@ export class RulesClient { muted: { terms: { field: 'alert.attributes.muteAll' }, }, + tags: { + terms: { field: 'alert.attributes.tags', order: { _key: 'asc' } }, + }, snoozed: { date_range: { field: 'alert.attributes.snoozeEndTime', @@ -990,6 +1000,9 @@ export class RulesClient { snoozed: snoozedBuckets.reduce((acc, bucket) => acc + bucket.doc_count, 0), }; + const tagsBuckets = resp.aggregations.tags?.buckets || []; + ret.ruleTags = tagsBuckets.map((bucket) => bucket.key); + return ret; } diff --git a/x-pack/plugins/alerting/server/rules_client/tests/aggregate.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/aggregate.test.ts index b74059e4be3d6..1a3d203162bd6 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/aggregate.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/aggregate.test.ts @@ -112,6 +112,22 @@ describe('aggregate()', () => { }, ], }, + tags: { + buckets: [ + { + key: 'a', + doc_count: 10, + }, + { + key: 'b', + doc_count: 20, + }, + { + key: 'c', + doc_count: 30, + }, + ], + }, }, }); @@ -160,6 +176,11 @@ describe('aggregate()', () => { "ruleSnoozedStatus": Object { "snoozed": 2, }, + "ruleTags": Array [ + "a", + "b", + "c", + ], } `); expect(unsecuredSavedObjectsClient.find).toHaveBeenCalledTimes(1); @@ -187,6 +208,9 @@ describe('aggregate()', () => { ranges: [{ from: 'now' }], }, }, + tags: { + terms: { field: 'alert.attributes.tags', order: { _key: 'asc' } }, + }, }, }, ]); @@ -221,6 +245,9 @@ describe('aggregate()', () => { ranges: [{ from: 'now' }], }, }, + tags: { + terms: { field: 'alert.attributes.tags', order: { _key: 'asc' } }, + }, }, }, ]); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts index 0eee41402000f..541e55f5c8d90 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts @@ -296,7 +296,7 @@ const findResults = { kind: 'action', start: '2022-03-23T17:37:07.105Z', end: '2022-03-23T17:37:07.105Z', - duration: 0, + duration: '0', outcome: 'failure', }, kibana: { @@ -345,7 +345,7 @@ const findResults = { kind: 'action', start: '2022-03-23T17:37:07.101Z', end: '2022-03-23T17:37:07.102Z', - duration: 1000000, + duration: '1000000', outcome: 'failure', }, kibana: { @@ -394,7 +394,7 @@ const findResults = { kind: 'action', start: '2022-03-23T17:37:07.098Z', end: '2022-03-23T17:37:07.098Z', - duration: 0, + duration: '0', outcome: 'failure', }, kibana: { @@ -443,7 +443,7 @@ const findResults = { kind: 'action', start: '2022-03-23T17:37:07.095Z', end: '2022-03-23T17:37:07.096Z', - duration: 1000000, + duration: '1000000', outcome: 'failure', }, kibana: { @@ -492,7 +492,7 @@ const findResults = { kind: 'action', start: '2022-03-23T17:37:07.084Z', end: '2022-03-23T17:37:07.086Z', - duration: 2000000, + duration: '2000000', outcome: 'failure', }, kibana: { @@ -543,7 +543,7 @@ const findResults = { start: '2022-03-23T17:23:05.131Z', outcome: 'failure', end: '2022-03-23T17:23:05.248Z', - duration: 117000000, + duration: '117000000', reason: 'execute', }, kibana: { diff --git a/x-pack/plugins/alerting/server/rules_client_factory.ts b/x-pack/plugins/alerting/server/rules_client_factory.ts index 19184d3baa329..d7dc9612b603a 100644 --- a/x-pack/plugins/alerting/server/rules_client_factory.ts +++ b/x-pack/plugins/alerting/server/rules_client_factory.ts @@ -26,7 +26,7 @@ export interface RulesClientFactoryOpts { ruleTypeRegistry: RuleTypeRegistry; securityPluginSetup?: SecurityPluginSetup; securityPluginStart?: SecurityPluginStart; - getSpaceId: (request: KibanaRequest) => string | undefined; + getSpaceId: (request: KibanaRequest) => string; spaceIdToNamespace: SpaceIdToNamespaceFunction; encryptedSavedObjectsClient: EncryptedSavedObjectsClient; actions: ActionsPluginStartContract; @@ -44,7 +44,7 @@ export class RulesClientFactory { private ruleTypeRegistry!: RuleTypeRegistry; private securityPluginSetup?: SecurityPluginSetup; private securityPluginStart?: SecurityPluginStart; - private getSpaceId!: (request: KibanaRequest) => string | undefined; + private getSpaceId!: (request: KibanaRequest) => string; private spaceIdToNamespace!: SpaceIdToNamespaceFunction; private encryptedSavedObjectsClient!: EncryptedSavedObjectsClient; private actions!: ActionsPluginStartContract; diff --git a/x-pack/plugins/alerting/server/saved_objects/index.ts b/x-pack/plugins/alerting/server/saved_objects/index.ts index 47e172005ccbe..6566fee15d4a8 100644 --- a/x-pack/plugins/alerting/server/saved_objects/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/index.ts @@ -10,10 +10,10 @@ import type { SavedObject, SavedObjectsExportTransformContext, SavedObjectsServiceSetup, - SavedObjectsTypeMappingDefinition, } from '@kbn/core/server'; import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; -import mappings from './mappings.json'; +import { MigrateFunctionsObject } from '@kbn/kibana-utils-plugin/common'; +import { alertMappings } from './mappings'; import { getMigrations } from './migrations'; import { transformRulesForExport } from './transform_rule_for_export'; import { RawRule } from '../types'; @@ -52,15 +52,16 @@ export function setupSavedObjects( encryptedSavedObjects: EncryptedSavedObjectsPluginSetup, ruleTypeRegistry: RuleTypeRegistry, logger: Logger, - isPreconfigured: (connectorId: string) => boolean + isPreconfigured: (connectorId: string) => boolean, + getSearchSourceMigrations: () => MigrateFunctionsObject ) { savedObjects.registerType({ name: 'alert', hidden: true, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', - migrations: getMigrations(encryptedSavedObjects, isPreconfigured), - mappings: mappings.alert as SavedObjectsTypeMappingDefinition, + migrations: getMigrations(encryptedSavedObjects, getSearchSourceMigrations(), isPreconfigured), + mappings: alertMappings, management: { displayName: 'rule', importableAndExportable: true, diff --git a/x-pack/plugins/alerting/server/saved_objects/mappings.json b/x-pack/plugins/alerting/server/saved_objects/mappings.json deleted file mode 100644 index a027dd389575e..0000000000000 --- a/x-pack/plugins/alerting/server/saved_objects/mappings.json +++ /dev/null @@ -1,185 +0,0 @@ -{ - "alert": { - "properties": { - "enabled": { - "type": "boolean" - }, - "name": { - "type": "text", - "fields": { - "keyword": { - "type": "keyword" - } - } - }, - "tags": { - "type": "keyword" - }, - "alertTypeId": { - "type": "keyword" - }, - "schedule": { - "properties": { - "interval": { - "type": "keyword" - } - } - }, - "consumer": { - "type": "keyword" - }, - "legacyId": { - "type": "keyword" - }, - "actions": { - "type": "nested", - "properties": { - "group": { - "type": "keyword" - }, - "actionRef": { - "type": "keyword" - }, - "actionTypeId": { - "type": "keyword" - }, - "params": { - "enabled": false, - "type": "object" - } - } - }, - "params": { - "type": "flattened", - "ignore_above": 4096 - }, - "mapped_params": { - "properties": { - "risk_score": { - "type": "float" - }, - "severity": { - "type": "keyword" - } - } - }, - "scheduledTaskId": { - "type": "keyword" - }, - "createdBy": { - "type": "keyword" - }, - "updatedBy": { - "type": "keyword" - }, - "createdAt": { - "type": "date" - }, - "updatedAt": { - "type": "date" - }, - "apiKey": { - "type": "binary" - }, - "apiKeyOwner": { - "type": "keyword" - }, - "throttle": { - "type": "keyword" - }, - "notifyWhen": { - "type": "keyword" - }, - "muteAll": { - "type": "boolean" - }, - "mutedInstanceIds": { - "type": "keyword" - }, - "meta": { - "properties": { - "versionApiKeyLastmodified": { - "type": "keyword" - } - } - }, - "monitoring": { - "properties": { - "execution": { - "properties": { - "history": { - "properties": { - "duration": { - "type": "long" - }, - "success": { - "type": "boolean" - }, - "timestamp": { - "type": "date" - } - } - }, - "calculated_metrics": { - "properties": { - "p50": { - "type": "long" - }, - "p95": { - "type": "long" - }, - "p99": { - "type": "long" - }, - "success_ratio": { - "type": "float" - } - } - } - } - } - } - }, - "executionStatus": { - "properties": { - "numberOfTriggeredActions": { - "type": "long" - }, - "status": { - "type": "keyword" - }, - "lastExecutionDate": { - "type": "date" - }, - "lastDuration": { - "type": "long" - }, - "error": { - "properties": { - "reason": { - "type": "keyword" - }, - "message": { - "type": "keyword" - } - } - }, - "warning": { - "properties": { - "reason": { - "type": "keyword" - }, - "message": { - "type": "keyword" - } - } - } - } - }, - "snoozeEndTime": { - "type": "date", - "format": "strict_date_time" - } - } - } -} diff --git a/x-pack/plugins/alerting/server/saved_objects/mappings.ts b/x-pack/plugins/alerting/server/saved_objects/mappings.ts new file mode 100644 index 0000000000000..5e2803222ecba --- /dev/null +++ b/x-pack/plugins/alerting/server/saved_objects/mappings.ts @@ -0,0 +1,193 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectsTypeMappingDefinition } from '@kbn/core/server'; + +export const alertMappings: SavedObjectsTypeMappingDefinition = { + properties: { + enabled: { + type: 'boolean', + }, + name: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + normalizer: 'lowercase', + }, + }, + }, + tags: { + type: 'keyword', + }, + alertTypeId: { + type: 'keyword', + }, + schedule: { + properties: { + interval: { + type: 'keyword', + }, + }, + }, + consumer: { + type: 'keyword', + }, + legacyId: { + type: 'keyword', + }, + actions: { + type: 'nested', + properties: { + group: { + type: 'keyword', + }, + actionRef: { + type: 'keyword', + }, + actionTypeId: { + type: 'keyword', + }, + params: { + enabled: false, + type: 'object', + }, + }, + }, + params: { + type: 'flattened', + ignore_above: 4096, + }, + mapped_params: { + properties: { + risk_score: { + type: 'float', + }, + severity: { + type: 'keyword', + }, + }, + }, + scheduledTaskId: { + type: 'keyword', + }, + createdBy: { + type: 'keyword', + }, + updatedBy: { + type: 'keyword', + }, + createdAt: { + type: 'date', + }, + updatedAt: { + type: 'date', + }, + apiKey: { + type: 'binary', + }, + apiKeyOwner: { + type: 'keyword', + }, + throttle: { + type: 'keyword', + }, + notifyWhen: { + type: 'keyword', + }, + muteAll: { + type: 'boolean', + }, + mutedInstanceIds: { + type: 'keyword', + }, + meta: { + properties: { + versionApiKeyLastmodified: { + type: 'keyword', + }, + }, + }, + monitoring: { + properties: { + execution: { + properties: { + history: { + properties: { + duration: { + type: 'long', + }, + success: { + type: 'boolean', + }, + timestamp: { + type: 'date', + }, + }, + }, + calculated_metrics: { + properties: { + p50: { + type: 'long', + }, + p95: { + type: 'long', + }, + p99: { + type: 'long', + }, + success_ratio: { + type: 'float', + }, + }, + }, + }, + }, + }, + }, + executionStatus: { + properties: { + numberOfTriggeredActions: { + type: 'long', + }, + status: { + type: 'keyword', + }, + lastExecutionDate: { + type: 'date', + }, + lastDuration: { + type: 'long', + }, + error: { + properties: { + reason: { + type: 'keyword', + }, + message: { + type: 'keyword', + }, + }, + }, + warning: { + properties: { + reason: { + type: 'keyword', + }, + message: { + type: 'keyword', + }, + }, + }, + }, + }, + snoozeEndTime: { + type: 'date', + format: 'strict_date_time', + }, + }, +}; diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts b/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts index 921412d4e79e8..c83d0a95dfdcb 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts @@ -8,7 +8,7 @@ import uuid from 'uuid'; import { getMigrations, isAnyActionSupportIncidents } from './migrations'; import { RawRule } from '../types'; -import { SavedObjectUnsanitizedDoc } from '@kbn/core/server'; +import { SavedObjectMigrationContext, SavedObjectUnsanitizedDoc } from '@kbn/core/server'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; import { migrationMocks } from '@kbn/core/server/mocks'; import { RuleType, ruleTypeMappings } from '@kbn/securitysolution-rules'; @@ -25,7 +25,7 @@ describe('successful migrations', () => { }); describe('7.10.0', () => { test('marks alerts as legacy', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0']; + const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0']; const alert = getMockData({}); expect(migration710(alert, migrationContext)).toMatchObject({ ...alert, @@ -39,7 +39,7 @@ describe('successful migrations', () => { }); test('migrates the consumer for metrics', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0']; + const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0']; const alert = getMockData({ consumer: 'metrics', }); @@ -56,7 +56,7 @@ describe('successful migrations', () => { }); test('migrates the consumer for siem', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0']; + const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0']; const alert = getMockData({ consumer: 'securitySolution', }); @@ -73,7 +73,7 @@ describe('successful migrations', () => { }); test('migrates the consumer for alerting', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0']; + const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0']; const alert = getMockData({ consumer: 'alerting', }); @@ -90,7 +90,7 @@ describe('successful migrations', () => { }); test('migrates PagerDuty actions to set a default dedupkey of the AlertId', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0']; + const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0']; const alert = getMockData({ actions: [ { @@ -127,7 +127,7 @@ describe('successful migrations', () => { }); test('skips PagerDuty actions with a specified dedupkey', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0']; + const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0']; const alert = getMockData({ actions: [ { @@ -165,7 +165,7 @@ describe('successful migrations', () => { }); test('skips PagerDuty actions with an eventAction of "trigger"', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0']; + const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0']; const alert = getMockData({ actions: [ { @@ -204,7 +204,7 @@ describe('successful migrations', () => { }); test('creates execution status', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0']; + const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0']; const alert = getMockData(); const dateStart = Date.now(); const migratedAlert = migration710(alert, migrationContext); @@ -232,7 +232,7 @@ describe('successful migrations', () => { describe('7.11.0', () => { test('add updatedAt field to alert - set to SavedObject updated_at attribute', () => { - const migration711 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.0']; + const migration711 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.11.0']; const alert = getMockData({}, true); expect(migration711(alert, migrationContext)).toEqual({ ...alert, @@ -245,7 +245,7 @@ describe('successful migrations', () => { }); test('add updatedAt field to alert - set to createdAt when SavedObject updated_at is not defined', () => { - const migration711 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.0']; + const migration711 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.11.0']; const alert = getMockData({}); expect(migration711(alert, migrationContext)).toEqual({ ...alert, @@ -258,7 +258,7 @@ describe('successful migrations', () => { }); test('add notifyWhen=onActiveAlert when throttle is null', () => { - const migration711 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.0']; + const migration711 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.11.0']; const alert = getMockData({}); expect(migration711(alert, migrationContext)).toEqual({ ...alert, @@ -271,7 +271,7 @@ describe('successful migrations', () => { }); test('add notifyWhen=onActiveAlert when throttle is set', () => { - const migration711 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.0']; + const migration711 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.11.0']; const alert = getMockData({ throttle: '5m' }); expect(migration711(alert, migrationContext)).toEqual({ ...alert, @@ -286,7 +286,9 @@ describe('successful migrations', () => { describe('7.11.2', () => { test('transforms connectors that support incident correctly', () => { - const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2']; + const migration7112 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.11.2' + ]; const alert = getMockData({ actions: [ { @@ -428,7 +430,9 @@ describe('successful migrations', () => { }); test('it transforms only subAction=pushToService', () => { - const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2']; + const migration7112 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.11.2' + ]; const alert = getMockData({ actions: [ { @@ -447,7 +451,9 @@ describe('successful migrations', () => { }); test('it does not transforms other connectors', () => { - const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2']; + const migration7112 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.11.2' + ]; const alert = getMockData({ actions: [ { @@ -526,7 +532,9 @@ describe('successful migrations', () => { }); test('it does not transforms alerts when the right structure connectors is already applied', () => { - const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2']; + const migration7112 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.11.2' + ]; const alert = getMockData({ actions: [ { @@ -563,7 +571,9 @@ describe('successful migrations', () => { }); test('if incident attribute is an empty object, copy back the related attributes from subActionParams back to incident', () => { - const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2']; + const migration7112 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.11.2' + ]; const alert = getMockData({ actions: [ { @@ -625,7 +635,9 @@ describe('successful migrations', () => { }); test('custom action does not get migrated/loss', () => { - const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2']; + const migration7112 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.11.2' + ]; const alert = getMockData({ actions: [ { @@ -654,7 +666,7 @@ describe('successful migrations', () => { describe('7.13.0', () => { test('security solution alerts get migrated and remove null values', () => { - const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0']; + const migration713 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.13.0']; const alert = getMockData({ alertTypeId: 'siem.signals', params: { @@ -748,7 +760,7 @@ describe('successful migrations', () => { }); test('non-null values in security solution alerts are not modified', () => { - const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0']; + const migration713 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.13.0']; const alert = getMockData({ alertTypeId: 'siem.signals', params: { @@ -815,7 +827,7 @@ describe('successful migrations', () => { }); test('security solution threshold alert with string in threshold.field is migrated to array', () => { - const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0']; + const migration713 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.13.0']; const alert = getMockData({ alertTypeId: 'siem.signals', params: { @@ -846,7 +858,7 @@ describe('successful migrations', () => { }); test('security solution threshold alert with empty string in threshold.field is migrated to empty array', () => { - const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0']; + const migration713 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.13.0']; const alert = getMockData({ alertTypeId: 'siem.signals', params: { @@ -877,7 +889,7 @@ describe('successful migrations', () => { }); test('security solution threshold alert with array in threshold.field and cardinality is left alone', () => { - const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0']; + const migration713 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.13.0']; const alert = getMockData({ alertTypeId: 'siem.signals', params: { @@ -919,7 +931,7 @@ describe('successful migrations', () => { }); test('security solution ML alert with string in machineLearningJobId is converted to an array', () => { - const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0']; + const migration713 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.13.0']; const alert = getMockData({ alertTypeId: 'siem.signals', params: { @@ -945,7 +957,7 @@ describe('successful migrations', () => { }); test('security solution ML alert with an array in machineLearningJobId is preserved', () => { - const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0']; + const migration713 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.13.0']; const alert = getMockData({ alertTypeId: 'siem.signals', params: { @@ -973,7 +985,9 @@ describe('successful migrations', () => { describe('7.14.1', () => { test('security solution author field is migrated to array if it is undefined', () => { - const migration7141 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.14.1']; + const migration7141 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.14.1' + ]; const alert = getMockData({ alertTypeId: 'siem.signals', params: {}, @@ -991,7 +1005,9 @@ describe('successful migrations', () => { }); test('security solution author field does not override existing values if they exist', () => { - const migration7141 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.14.1']; + const migration7141 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.14.1' + ]; const alert = getMockData({ alertTypeId: 'siem.signals', params: { @@ -1015,7 +1031,9 @@ describe('successful migrations', () => { describe('7.15.0', () => { test('security solution is migrated to saved object references if it has 1 exceptionsList', () => { - const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0']; + const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.15.0' + ]; const alert = getMockData({ alertTypeId: 'siem.signals', params: { @@ -1044,7 +1062,9 @@ describe('successful migrations', () => { }); test('security solution is migrated to saved object references if it has 2 exceptionsLists', () => { - const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0']; + const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.15.0' + ]; const alert = getMockData({ alertTypeId: 'siem.signals', params: { @@ -1084,7 +1104,9 @@ describe('successful migrations', () => { }); test('security solution is migrated to saved object references if it has 3 exceptionsLists', () => { - const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0']; + const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.15.0' + ]; const alert = getMockData({ alertTypeId: 'siem.signals', params: { @@ -1135,7 +1157,9 @@ describe('successful migrations', () => { }); test('security solution does not change anything if exceptionsList is missing', () => { - const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0']; + const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.15.0' + ]; const alert = getMockData({ alertTypeId: 'siem.signals', params: { @@ -1147,7 +1171,9 @@ describe('successful migrations', () => { }); test('security solution will keep existing references if we do not have an exceptionsList but we do already have references', () => { - const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0']; + const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.15.0' + ]; const alert = { ...getMockData({ alertTypeId: 'siem.signals', @@ -1177,7 +1203,9 @@ describe('successful migrations', () => { }); test('security solution keep any foreign references if they exist but still migrate other references', () => { - const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0']; + const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.15.0' + ]; const alert = { ...getMockData({ alertTypeId: 'siem.signals', @@ -1242,7 +1270,9 @@ describe('successful migrations', () => { }); test('security solution is idempotent and if re-run on the same migrated data will keep the same items', () => { - const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0']; + const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.15.0' + ]; const alert = { ...getMockData({ alertTypeId: 'siem.signals', @@ -1282,7 +1312,9 @@ describe('successful migrations', () => { }); test('security solution will migrate with only missing data if we have partially migrated data', () => { - const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0']; + const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.15.0' + ]; const alert = { ...getMockData({ alertTypeId: 'siem.signals', @@ -1331,7 +1363,9 @@ describe('successful migrations', () => { }); test('security solution will not migrate if exception list if it is invalid data', () => { - const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0']; + const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.15.0' + ]; const alert = { ...getMockData({ alertTypeId: 'siem.signals', @@ -1345,7 +1379,9 @@ describe('successful migrations', () => { }); test('security solution will migrate valid data if it is mixed with invalid data', () => { - const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0']; + const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.15.0' + ]; const alert = { ...getMockData({ alertTypeId: 'siem.signals', @@ -1387,7 +1423,9 @@ describe('successful migrations', () => { }); test('security solution will not migrate if exception list is invalid data but will keep existing references', () => { - const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0']; + const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.15.0' + ]; const alert = { ...getMockData({ alertTypeId: 'siem.signals', @@ -1419,7 +1457,7 @@ describe('successful migrations', () => { describe('7.16.0', () => { test('add legacyId field to alert - set to SavedObject id attribute', () => { - const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration716 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.16.0']; const alert = getMockData({}, true); expect(migration716(alert, migrationContext)).toEqual({ ...alert, @@ -1434,7 +1472,7 @@ describe('successful migrations', () => { isPreconfigured.mockReset(); isPreconfigured.mockReturnValueOnce(true); isPreconfigured.mockReturnValueOnce(false); - const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration716 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.16.0']; const rule = { ...getMockData({ actions: [ @@ -1510,7 +1548,7 @@ describe('successful migrations', () => { isPreconfigured.mockReturnValueOnce(true); isPreconfigured.mockReturnValueOnce(false); isPreconfigured.mockReturnValueOnce(false); - const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration716 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.16.0']; const rule = { ...getMockData({ actions: [ @@ -1593,7 +1631,7 @@ describe('successful migrations', () => { test('does nothing to rules with no references', () => { isPreconfigured.mockReset(); - const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration716 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.16.0']; const rule = { ...getMockData({ actions: [ @@ -1629,7 +1667,7 @@ describe('successful migrations', () => { test('does nothing to rules with no action references', () => { isPreconfigured.mockReset(); - const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration716 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.16.0']; const rule = { ...getMockData({ actions: [ @@ -1671,7 +1709,7 @@ describe('successful migrations', () => { test('does nothing to rules with references but no actions', () => { isPreconfigured.mockReset(); - const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration716 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.16.0']; const rule = { ...getMockData({ actions: [], @@ -1699,7 +1737,9 @@ describe('successful migrations', () => { }); test('security solution is migrated to saved object references if it has a "ruleAlertId"', () => { - const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.16.0' + ]; const alert = getMockData({ alertTypeId: 'siem.notifications', params: { @@ -1724,7 +1764,9 @@ describe('successful migrations', () => { }); test('security solution does not migrate anything if its type is not siem.notifications', () => { - const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.16.0' + ]; const alert = getMockData({ alertTypeId: 'other-type', params: { @@ -1741,7 +1783,9 @@ describe('successful migrations', () => { }); }); test('security solution does not change anything if "ruleAlertId" is missing', () => { - const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.16.0' + ]; const alert = getMockData({ alertTypeId: 'siem.notifications', params: {}, @@ -1757,7 +1801,9 @@ describe('successful migrations', () => { }); test('security solution will keep existing references if we do not have a "ruleAlertId" but we do already have references', () => { - const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.16.0' + ]; const alert = { ...getMockData({ alertTypeId: 'siem.notifications', @@ -1789,7 +1835,9 @@ describe('successful migrations', () => { }); test('security solution will keep any foreign references if they exist but still migrate other "ruleAlertId" references', () => { - const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.16.0' + ]; const alert = { ...getMockData({ alertTypeId: 'siem.notifications', @@ -1828,7 +1876,9 @@ describe('successful migrations', () => { }); test('security solution is idempotent and if re-run on the same migrated data will keep the same items "ruleAlertId" references', () => { - const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.16.0' + ]; const alert = { ...getMockData({ alertTypeId: 'siem.notifications', @@ -1862,7 +1912,9 @@ describe('successful migrations', () => { }); test('security solution will not migrate "ruleAlertId" if it is invalid data', () => { - const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.16.0' + ]; const alert = { ...getMockData({ alertTypeId: 'siem.notifications', @@ -1882,7 +1934,9 @@ describe('successful migrations', () => { }); test('security solution will not migrate "ruleAlertId" if it is invalid data but will keep existing references', () => { - const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.16.0' + ]; const alert = { ...getMockData({ alertTypeId: 'siem.notifications', @@ -1916,7 +1970,9 @@ describe('successful migrations', () => { }); test('geo-containment alert migration extracts boundary and index references', () => { - const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.16.0' + ]; const alert = { ...getMockData({ alertTypeId: '.geo-containment', @@ -1944,7 +2000,9 @@ describe('successful migrations', () => { }); test('geo-containment alert migration should preserve foreign references', () => { - const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.16.0' + ]; const alert = { ...getMockData({ alertTypeId: '.geo-containment', @@ -1984,7 +2042,9 @@ describe('successful migrations', () => { }); test('geo-containment alert migration ignores other alert-types', () => { - const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.16.0' + ]; const alert = { ...getMockData({ alertTypeId: '.foo', @@ -2008,13 +2068,13 @@ describe('successful migrations', () => { describe('8.0.0', () => { test('no op migration for rules SO', () => { - const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0']; + const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.0.0']; const alert = getMockData({}, true); expect(migration800(alert, migrationContext)).toEqual(alert); }); test('add threatIndicatorPath default value to threat match rules if missing', () => { - const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0']; + const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.0.0']; const alert = getMockData( { params: { type: 'threat_match' }, alertTypeId: 'siem.signals' }, true @@ -2025,7 +2085,7 @@ describe('successful migrations', () => { }); test('doesnt change threatIndicatorPath value in threat match rules if value is present', () => { - const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0']; + const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.0.0']; const alert = getMockData( { params: { type: 'threat_match', threatIndicatorPath: 'custom.indicator.path' }, @@ -2039,7 +2099,7 @@ describe('successful migrations', () => { }); test('doesnt change threatIndicatorPath value in other rules', () => { - const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0']; + const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.0.0']; const alert = getMockData({ params: { type: 'eql' }, alertTypeId: 'siem.signals' }, true); expect(migration800(alert, migrationContext).attributes.params.threatIndicatorPath).toEqual( undefined @@ -2047,7 +2107,7 @@ describe('successful migrations', () => { }); test('doesnt change threatIndicatorPath value if not a siem.signals rule', () => { - const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0']; + const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.0.0']; const alert = getMockData( { params: { type: 'threat_match' }, alertTypeId: 'not.siem.signals' }, true @@ -2058,7 +2118,7 @@ describe('successful migrations', () => { }); test('doesnt change AAD rule params if not a siem.signals rule', () => { - const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0']; + const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.0.0']; const alert = getMockData( { params: { outputIndex: 'output-index', type: 'query' }, alertTypeId: 'not.siem.signals' }, true @@ -2073,7 +2133,9 @@ describe('successful migrations', () => { test.each(Object.keys(ruleTypeMappings) as RuleType[])( 'changes AAD rule params accordingly if rule is a siem.signals %p rule', (ruleType) => { - const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0']; + const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '8.0.0' + ]; const alert = getMockData( { params: { outputIndex: 'output-index', type: ruleType }, alertTypeId: 'siem.signals' }, true @@ -2118,7 +2180,7 @@ describe('successful migrations', () => { ); test('Does not update rule tags if rule has already been enabled', () => { - const migrations = getMigrations(encryptedSavedObjectsSetup, isPreconfigured); + const migrations = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured); const migration800 = migrations['8.0.0']; const migration801 = migrations['8.0.1']; @@ -2141,7 +2203,7 @@ describe('successful migrations', () => { }); test('Does not update rule tags if rule was already disabled before upgrading to 8.0', () => { - const migrations = getMigrations(encryptedSavedObjectsSetup, isPreconfigured); + const migrations = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured); const migration800 = migrations['8.0.0']; const migration801 = migrations['8.0.1']; @@ -2161,7 +2223,7 @@ describe('successful migrations', () => { }); test('Updates rule tags if rule was auto-disabled in 8.0 upgrade and not reenabled', () => { - const migrations = getMigrations(encryptedSavedObjectsSetup, isPreconfigured); + const migrations = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured); const migration800 = migrations['8.0.0']; const migration801 = migrations['8.0.1']; @@ -2181,7 +2243,7 @@ describe('successful migrations', () => { }); test('Updates rule tags correctly if tags are undefined', () => { - const migrations = getMigrations(encryptedSavedObjectsSetup, isPreconfigured); + const migrations = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured); const migration801 = migrations['8.0.1']; const alert = { @@ -2204,7 +2266,7 @@ describe('successful migrations', () => { }); test('Updates rule tags correctly if tags are null', () => { - const migrations = getMigrations(encryptedSavedObjectsSetup, isPreconfigured); + const migrations = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured); const migration801 = migrations['8.0.1']; const alert = { @@ -2231,7 +2293,9 @@ describe('successful migrations', () => { describe('8.2.0', () => { test('migrates params to mapped_params', () => { - const migration820 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.2.0']; + const migration820 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '8.2.0' + ]; const alert = getMockData( { params: { @@ -2254,8 +2318,29 @@ describe('successful migrations', () => { }); describe('8.3.0', () => { + test('migrates es_query alert params', () => { + const migration830 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '8.3.0' + ]; + const alert = getMockData( + { + params: { esQuery: '{ "query": "test-query" }' }, + alertTypeId: '.es-query', + }, + true + ); + const migratedAlert820 = migration830(alert, migrationContext); + + expect(migratedAlert820.attributes.params).toEqual({ + esQuery: '{ "query": "test-query" }', + searchType: 'esQuery', + }); + }); + test('removes internal tags', () => { - const migration830 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.3.0']; + const migration830 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '8.3.0' + ]; const alert = getMockData( { tags: [ @@ -2274,7 +2359,9 @@ describe('successful migrations', () => { }); test('do not remove internal tags if rule is not Security solution rule', () => { - const migration830 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.3.0']; + const migration830 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '8.3.0' + ]; const alert = getMockData( { tags: ['__internal_immutable:false', 'tag-1'], @@ -2290,7 +2377,9 @@ describe('successful migrations', () => { describe('Metrics Inventory Threshold rule', () => { test('Migrates incorrect action group spelling', () => { - const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0']; + const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '8.0.0' + ]; const actions = [ { @@ -2317,7 +2406,9 @@ describe('successful migrations', () => { }); test('Works with the correct action group spelling', () => { - const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0']; + const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '8.0.0' + ]; const actions = [ { @@ -2346,6 +2437,72 @@ describe('successful migrations', () => { }); }); +describe('search source migration', () => { + it('should apply migration within es query alert rule', () => { + const esQueryRuleSavedObject = { + attributes: { + params: { + searchConfiguration: { + some: 'prop', + migrated: false, + }, + }, + }, + } as SavedObjectUnsanitizedDoc; + + const versionToTest = '9.1.3'; + const migrations = getMigrations( + encryptedSavedObjectsSetup, + { + [versionToTest]: (state) => ({ ...state, migrated: true }), + }, + isPreconfigured + ); + + expect( + migrations[versionToTest](esQueryRuleSavedObject, {} as SavedObjectMigrationContext) + ).toEqual({ + attributes: { + params: { + searchConfiguration: { + some: 'prop', + migrated: true, + }, + }, + }, + }); + }); + + it('should not apply migration within es query alert rule when searchConfiguration not an object', () => { + const esQueryRuleSavedObject = { + attributes: { + params: { + searchConfiguration: 5, + }, + }, + } as SavedObjectUnsanitizedDoc; + + const versionToTest = '9.1.4'; + const migrations = getMigrations( + encryptedSavedObjectsSetup, + { + [versionToTest]: (state) => ({ ...state, migrated: true }), + }, + isPreconfigured + ); + + expect( + migrations[versionToTest](esQueryRuleSavedObject, {} as SavedObjectMigrationContext) + ).toEqual({ + attributes: { + params: { + searchConfiguration: 5, + }, + }, + }); + }); +}); + describe('handles errors during migrations', () => { beforeEach(() => { jest.resetAllMocks(); @@ -2355,7 +2512,7 @@ describe('handles errors during migrations', () => { }); describe('7.10.0 throws if migration fails', () => { test('should show the proper exception', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0']; + const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0']; const alert = getMockData({ consumer: 'alerting', }); @@ -2380,7 +2537,7 @@ describe('handles errors during migrations', () => { describe('7.11.0 throws if migration fails', () => { test('should show the proper exception', () => { - const migration711 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.0']; + const migration711 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.11.0']; const alert = getMockData({ consumer: 'alerting', }); @@ -2405,7 +2562,9 @@ describe('handles errors during migrations', () => { describe('7.11.2 throws if migration fails', () => { test('should show the proper exception', () => { - const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2']; + const migration7112 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.11.2' + ]; const alert = getMockData({ consumer: 'alerting', }); @@ -2430,7 +2589,9 @@ describe('handles errors during migrations', () => { describe('7.13.0 throws if migration fails', () => { test('should show the proper exception', () => { - const migration7130 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0']; + const migration7130 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.13.0' + ]; const alert = getMockData({ consumer: 'alerting', }); @@ -2455,7 +2616,9 @@ describe('handles errors during migrations', () => { describe('7.16.0 throws if migration fails', () => { test('should show the proper exception', () => { - const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0']; + const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '7.16.0' + ]; const rule = getMockData(); expect(() => { migration7160(rule, migrationContext); @@ -2475,6 +2638,53 @@ describe('handles errors during migrations', () => { ); }); }); + + describe('8.3.0 throws if migration fails', () => { + test('should show the proper exception on search source migration', () => { + encryptedSavedObjectsSetup.createMigration.mockImplementation(({ migration }) => migration); + const mockRule = getMockData(); + const rule = { + ...mockRule, + attributes: { + ...mockRule.attributes, + params: { + searchConfiguration: { + some: 'prop', + migrated: false, + }, + }, + }, + }; + + const versionToTest = '8.3.0'; + const migration830 = getMigrations( + encryptedSavedObjectsSetup, + { + [versionToTest]: () => { + throw new Error(`Can't migrate search source!`); + }, + }, + isPreconfigured + )[versionToTest]; + + expect(() => { + migration830(rule, migrationContext); + }).toThrowError(`Can't migrate search source!`); + expect(migrationContext.log.error).toHaveBeenCalledWith( + `encryptedSavedObject ${versionToTest} migration failed for alert ${rule.id} with error: Can't migrate search source!`, + { + migrations: { + alertDocument: { + ...rule, + attributes: { + ...rule.attributes, + }, + }, + }, + } + ); + }); + }); }); function getUpdatedAt(): string { diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations.ts b/x-pack/plugins/alerting/server/saved_objects/migrations.ts index 69d88e196dcfd..b3f8d873d8ef0 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations.ts @@ -7,6 +7,7 @@ import { isRuleType, ruleTypeMappings } from '@kbn/securitysolution-rules'; import { isString } from 'lodash/fp'; +import { gte } from 'semver'; import { LogMeta, SavedObjectMigrationMap, @@ -19,12 +20,16 @@ import { } from '@kbn/core/server'; import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; import type { IsMigrationNeededPredicate } from '@kbn/encrypted-saved-objects-plugin/server'; -import { RawRule, RawRuleAction, RawRuleExecutionStatus } from '../types'; +import { MigrateFunctionsObject, MigrateFunction } from '@kbn/kibana-utils-plugin/common'; +import { mergeSavedObjectMigrationMaps } from '@kbn/core/server'; +import { isSerializedSearchSource, SerializedSearchSourceFields } from '@kbn/data-plugin/common'; import { extractRefsFromGeoContainmentAlert } from './geo_containment/migrations'; +import { RawRule, RawRuleAction, RawRuleExecutionStatus } from '../types'; import { getMappedParams } from '../rules_client/lib/mapped_params_utils'; const SIEM_APP_ID = 'securitySolution'; const SIEM_SERVER_APP_ID = 'siem'; +const MINIMUM_SS_MIGRATION_VERSION = '8.3.0'; export const LEGACY_LAST_MODIFIED_VERSION = 'pre-7.10.0'; export const FILEBEAT_7X_INDICATOR_PATH = 'threatintel.indicator'; @@ -59,6 +64,9 @@ export const isAnyActionSupportIncidents = (doc: SavedObjectUnsanitizedDoc): boolean => doc.attributes.alertTypeId === 'siem.signals'; +export const isEsQueryRuleType = (doc: SavedObjectUnsanitizedDoc) => + doc.attributes.alertTypeId === '.es-query'; + export const isDetectionEngineAADRuleType = (doc: SavedObjectUnsanitizedDoc): boolean => (Object.values(ruleTypeMappings) as string[]).includes(doc.attributes.alertTypeId); @@ -75,6 +83,7 @@ export const isSecuritySolutionLegacyNotification = ( export function getMigrations( encryptedSavedObjects: EncryptedSavedObjectsPluginSetup, + searchSourceMigrations: MigrateFunctionsObject, isPreconfigured: (connectorId: string) => boolean ): SavedObjectMigrationMap { const migrationWhenRBACWasIntroduced = createEsoMigration( @@ -155,22 +164,25 @@ export function getMigrations( const migrationRules830 = createEsoMigration( encryptedSavedObjects, (doc: SavedObjectUnsanitizedDoc): doc is SavedObjectUnsanitizedDoc => true, - pipeMigrations(removeInternalTags) + pipeMigrations(addSearchType, removeInternalTags) ); - return { - '7.10.0': executeMigrationWithErrorHandling(migrationWhenRBACWasIntroduced, '7.10.0'), - '7.11.0': executeMigrationWithErrorHandling(migrationAlertUpdatedAtAndNotifyWhen, '7.11.0'), - '7.11.2': executeMigrationWithErrorHandling(migrationActions7112, '7.11.2'), - '7.13.0': executeMigrationWithErrorHandling(migrationSecurityRules713, '7.13.0'), - '7.14.1': executeMigrationWithErrorHandling(migrationSecurityRules714, '7.14.1'), - '7.15.0': executeMigrationWithErrorHandling(migrationSecurityRules715, '7.15.0'), - '7.16.0': executeMigrationWithErrorHandling(migrateRules716, '7.16.0'), - '8.0.0': executeMigrationWithErrorHandling(migrationRules800, '8.0.0'), - '8.0.1': executeMigrationWithErrorHandling(migrationRules801, '8.0.1'), - '8.2.0': executeMigrationWithErrorHandling(migrationRules820, '8.2.0'), - '8.3.0': executeMigrationWithErrorHandling(migrationRules830, '8.3.0'), - }; + return mergeSavedObjectMigrationMaps( + { + '7.10.0': executeMigrationWithErrorHandling(migrationWhenRBACWasIntroduced, '7.10.0'), + '7.11.0': executeMigrationWithErrorHandling(migrationAlertUpdatedAtAndNotifyWhen, '7.11.0'), + '7.11.2': executeMigrationWithErrorHandling(migrationActions7112, '7.11.2'), + '7.13.0': executeMigrationWithErrorHandling(migrationSecurityRules713, '7.13.0'), + '7.14.1': executeMigrationWithErrorHandling(migrationSecurityRules714, '7.14.1'), + '7.15.0': executeMigrationWithErrorHandling(migrationSecurityRules715, '7.15.0'), + '7.16.0': executeMigrationWithErrorHandling(migrateRules716, '7.16.0'), + '8.0.0': executeMigrationWithErrorHandling(migrationRules800, '8.0.0'), + '8.0.1': executeMigrationWithErrorHandling(migrationRules801, '8.0.1'), + '8.2.0': executeMigrationWithErrorHandling(migrationRules820, '8.2.0'), + '8.3.0': executeMigrationWithErrorHandling(migrationRules830, '8.3.0'), + }, + getSearchSourceMigrations(encryptedSavedObjects, searchSourceMigrations) + ); } function executeMigrationWithErrorHandling( @@ -697,6 +709,23 @@ function addSecuritySolutionAADRuleTypes( : doc; } +function addSearchType(doc: SavedObjectUnsanitizedDoc) { + const searchType = doc.attributes.params.searchType; + + return isEsQueryRuleType(doc) && !searchType + ? { + ...doc, + attributes: { + ...doc.attributes, + params: { + ...doc.attributes.params, + searchType: 'esQuery', + }, + }, + } + : doc; +} + function addSecuritySolutionAADRuleTypeTags( doc: SavedObjectUnsanitizedDoc ): SavedObjectUnsanitizedDoc { @@ -902,3 +931,56 @@ function pipeMigrations(...migrations: AlertMigration[]): AlertMigration { return (doc: SavedObjectUnsanitizedDoc) => migrations.reduce((migratedDoc, nextMigration) => nextMigration(migratedDoc), doc); } + +function mapSearchSourceMigrationFunc( + migrateSerializedSearchSourceFields: MigrateFunction +): MigrateFunction { + return (doc) => { + const _doc = doc as { attributes: RawRule }; + + const serializedSearchSource = _doc.attributes.params.searchConfiguration; + + if (isSerializedSearchSource(serializedSearchSource)) { + return { + ..._doc, + attributes: { + ..._doc.attributes, + params: { + ..._doc.attributes.params, + searchConfiguration: migrateSerializedSearchSourceFields(serializedSearchSource), + }, + }, + }; + } + return _doc; + }; +} + +/** + * This creates a migration map that applies search source migrations to legacy es query rules. + * It doesn't modify existing migrations. The following migrations will occur at minimum version of 8.3+. + */ +function getSearchSourceMigrations( + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup, + searchSourceMigrations: MigrateFunctionsObject +) { + const filteredMigrations: SavedObjectMigrationMap = {}; + for (const versionKey in searchSourceMigrations) { + if (gte(versionKey, MINIMUM_SS_MIGRATION_VERSION)) { + const migrateSearchSource = mapSearchSourceMigrationFunc( + searchSourceMigrations[versionKey] + ) as unknown as AlertMigration; + + filteredMigrations[versionKey] = executeMigrationWithErrorHandling( + createEsoMigration( + encryptedSavedObjects, + (doc: SavedObjectUnsanitizedDoc): doc is SavedObjectUnsanitizedDoc => + isEsQueryRuleType(doc), + pipeMigrations(migrateSearchSource) + ), + versionKey + ); + } + } + return filteredMigrations; +} diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts index 81f7fa7da02d2..b4ae05fd341b0 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts @@ -13,7 +13,6 @@ import { actionsMock, renderActionParameterTemplatesDefault, } from '@kbn/actions-plugin/server/mocks'; -import { eventLoggerMock } from '@kbn/event-log-plugin/server/event_logger.mock'; import { KibanaRequest } from '@kbn/core/server'; import { asSavedObjectExecutionSource } from '@kbn/actions-plugin/server'; import { InjectActionParamsOpts } from './inject_action_params'; @@ -26,11 +25,14 @@ import { RuleTypeState, } from '../types'; import { RuleRunMetricsStore } from '../lib/rule_run_metrics_store'; +import { alertingEventLoggerMock } from '../lib/alerting_event_logger/alerting_event_logger.mock'; jest.mock('./inject_action_params', () => ({ injectActionParams: jest.fn(), })); +const alertingEventLogger = alertingEventLoggerMock.create(); + const ruleType: NormalizedRuleType< RuleTypeParams, RuleTypeParams, @@ -60,7 +62,6 @@ const ruleType: NormalizedRuleType< const actionsClient = actionsClientMock.create(); const mockActionsPlugin = actionsMock.createStart(); -const mockEventLogger = eventLoggerMock.create(); const createExecutionHandlerParams: jest.Mocked< CreateExecutionHandlerOptions< RuleTypeParams, @@ -83,7 +84,7 @@ const createExecutionHandlerParams: jest.Mocked< kibanaBaseUrl: 'http://localhost:5601', ruleType, logger: loggingSystemMock.create().get(), - eventLogger: mockEventLogger, + alertingEventLogger, actions: [ { id: '1', @@ -178,63 +179,13 @@ describe('Create Execution Handler', () => { ] `); - expect(mockEventLogger.logEvent).toHaveBeenCalledTimes(1); - expect(mockEventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "event": Object { - "action": "execute-action", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "consumer": "rule-consumer", - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - "rule_type_id": "test", - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "2", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": "test1", - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - Object { - "id": "1", - "namespace": "test1", - "type": "action", - "type_id": "test", - }, - ], - "space_ids": Array [ - "test1", - ], - }, - "message": "alert: test:1: 'name-of-alert' instanceId: '2' scheduled actionGroup: 'default' action: test:1", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "name-of-alert", - "ruleset": "alerts", - }, - }, - ], - ] - `); + expect(alertingEventLogger.logAction).toHaveBeenCalledTimes(1); + expect(alertingEventLogger.logAction).toHaveBeenNthCalledWith(1, { + id: '1', + typeId: 'test', + alertId: '2', + alertGroup: 'default', + }); expect(jest.requireMock('./inject_action_params').injectActionParams).toHaveBeenCalledWith({ ruleId: '1', diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts index ce212a3cbff1b..0383289ab91df 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts @@ -5,10 +5,8 @@ * 2.0. */ import { asSavedObjectExecutionSource } from '@kbn/actions-plugin/server'; -import { SAVED_OBJECT_REL_PRIMARY } from '@kbn/event-log-plugin/server'; import { isEphemeralTaskRejectedDueToCapacityError } from '@kbn/task-manager-plugin/server'; import { transformActionParams } from './transform_action_params'; -import { EVENT_LOG_ACTIONS } from '../plugin'; import { injectActionParams } from './inject_action_params'; import { ActionsCompletion, @@ -17,9 +15,6 @@ import { RuleTypeParams, RuleTypeState, } from '../types'; - -import { UntypedNormalizedRuleType } from '../rule_type_registry'; -import { createAlertEventLogRecordObject } from '../lib/create_alert_event_log_record_object'; import { CreateExecutionHandlerOptions, ExecutionHandlerOptions } from './types'; export type ExecutionHandler = ( @@ -47,7 +42,7 @@ export function createExecutionHandler< apiKey, ruleType, kibanaBaseUrl, - eventLogger, + alertingEventLogger, request, ruleParams, supportsEphemeralTasks, @@ -117,8 +112,6 @@ export function createExecutionHandler< ruleRunMetricsStore.incrementNumberOfGeneratedActions(actions.length); - const ruleLabel = `${ruleType.id}:${ruleId}: '${ruleName}'`; - const actionsClient = await actionsPlugin.getActionsClientWithRequest(request); let ephemeralActionsToSchedule = maxEphemeralActionsPerRule; @@ -189,8 +182,6 @@ export function createExecutionHandler< ], }; - // TODO would be nice to add the action name here, but it's not available - const actionLabel = `${actionTypeId}:${action.id}`; if (supportsEphemeralTasks && ephemeralActionsToSchedule > 0) { ephemeralActionsToSchedule--; try { @@ -204,39 +195,13 @@ export function createExecutionHandler< await actionsClient.enqueueExecution(enqueueOptions); } - const event = createAlertEventLogRecordObject({ - ruleId, - ruleType: ruleType as UntypedNormalizedRuleType, - consumer: ruleConsumer, - action: EVENT_LOG_ACTIONS.executeAction, - executionId, - spaceId, - instanceId: alertId, - group: actionGroup, - subgroup: actionSubgroup, - ruleName, - savedObjects: [ - { - type: 'alert', - id: ruleId, - typeId: ruleType.id, - relation: SAVED_OBJECT_REL_PRIMARY, - }, - { - type: 'action', - id: action.id, - typeId: actionTypeId, - }, - ], - ...namespace, - message: `alert: ${ruleLabel} instanceId: '${alertId}' scheduled ${ - actionSubgroup - ? `actionGroup(subgroup): '${actionGroup}(${actionSubgroup})'` - : `actionGroup: '${actionGroup}'` - } action: ${actionLabel}`, + alertingEventLogger.logAction({ + id: action.id, + typeId: actionTypeId, + alertId, + alertGroup: actionGroup, + alertSubgroup: actionSubgroup, }); - - eventLogger.logEvent(event); } }; } diff --git a/x-pack/plugins/alerting/server/task_runner/fixtures.ts b/x-pack/plugins/alerting/server/task_runner/fixtures.ts index 74a121e9c8026..5e4594cda6c04 100644 --- a/x-pack/plugins/alerting/server/task_runner/fixtures.ts +++ b/x-pack/plugins/alerting/server/task_runner/fixtures.ts @@ -5,14 +5,8 @@ * 2.0. */ -import { isNil } from 'lodash'; import { TaskStatus } from '@kbn/task-manager-plugin/server'; -import { - Rule, - RuleExecutionStatusWarningReasons, - RuleTypeParams, - RecoveredActionGroup, -} from '../../common'; +import { Rule, RuleTypeParams, RecoveredActionGroup } from '../../common'; import { getDefaultRuleMonitoring } from './task_runner'; import { UntypedNormalizedRuleType } from '../rule_type_registry'; import { EVENT_LOG_ACTIONS } from '../plugin'; @@ -28,7 +22,7 @@ export const DATE_1969 = '1969-12-31T00:00:00.000Z'; export const DATE_1970 = '1970-01-01T00:00:00.000Z'; export const DATE_1970_5_MIN = '1969-12-31T23:55:00.000Z'; export const DATE_9999 = '9999-12-31T12:34:56.789Z'; -export const MOCK_DURATION = 86400000000000; +export const MOCK_DURATION = '86400000000000'; export const SAVED_OBJECT = { id: '1', @@ -108,6 +102,8 @@ export const ruleType: jest.Mocked = { recoveryActionGroup: RecoveredActionGroup, executor: jest.fn(), producer: 'alerts', + cancelAlertsOnRuleTimeout: true, + ruleTaskTimeout: '5m', }; export const mockRunNowResponse = { @@ -182,178 +178,45 @@ export const mockTaskInstance = () => ({ ownerId: null, }); -export const generateAlertSO = (id: string) => ({ - id, - rel: 'primary', - type: 'alert', - type_id: RULE_TYPE_ID, -}); +export const generateAlertOpts = ({ action, group, subgroup, state, id }: GeneratorParams = {}) => { + id = id ?? '1'; + let message: string = ''; + switch (action) { + case EVENT_LOG_ACTIONS.newInstance: + message = `test:1: 'rule-name' created new alert: '${id}'`; + break; + case EVENT_LOG_ACTIONS.activeInstance: + message = subgroup + ? `test:1: 'rule-name' active alert: '${id}' in actionGroup(subgroup): 'default(${subgroup})'` + : `test:1: 'rule-name' active alert: '${id}' in actionGroup: 'default'`; + break; + case EVENT_LOG_ACTIONS.recoveredInstance: + message = `test:1: 'rule-name' alert '${id}' has recovered`; + break; + } + return { + action, + id, + message, + state, + ...(group ? { group } : {}), + ...(subgroup ? { subgroup } : {}), + }; +}; -export const generateActionSO = (id: string) => ({ +export const generateActionOpts = ({ + subgroup, id, - namespace: undefined, - type: 'action', - type_id: 'action', -}); - -export const generateEventLog = ({ - action, - task, - duration, - consumer, - start, - end, - outcome, - reason, - instanceId, - actionSubgroup, - actionGroupId, - actionId, - status, - numberOfTriggeredActions, - numberOfGeneratedActions, - numberOfActiveAlerts, - numberOfRecoveredAlerts, - numberOfNewAlerts, - savedObjects = [generateAlertSO('1')], + alertGroup, + alertId, }: GeneratorParams = {}) => ({ - ...(status === 'error' && { - error: { - message: generateErrorMessage(String(reason)), - }, - }), - event: { - action, - ...(!isNil(duration) && { duration }), - ...(start && { start }), - ...(end && { end }), - ...(outcome && { outcome }), - ...(reason && { reason }), - category: ['alerts'], - kind: 'alert', - }, - kibana: { - alert: { - rule: { - ...(consumer && { consumer }), - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - ...((!isNil(numberOfTriggeredActions) || !isNil(numberOfGeneratedActions)) && { - metrics: { - number_of_triggered_actions: numberOfTriggeredActions, - number_of_generated_actions: numberOfGeneratedActions, - number_of_active_alerts: numberOfActiveAlerts ?? 0, - number_of_new_alerts: numberOfNewAlerts ?? 0, - number_of_recovered_alerts: numberOfRecoveredAlerts ?? 0, - total_number_of_alerts: - ((numberOfActiveAlerts ?? 0) as number) + - ((numberOfRecoveredAlerts ?? 0) as number), - number_of_searches: 3, - es_search_duration_ms: 33, - total_search_duration_ms: 23423, - }, - }), - }, - rule_type_id: 'test', - }, - }, - ...((actionSubgroup || actionGroupId || instanceId || status) && { - alerting: { - ...(actionSubgroup && { action_subgroup: actionSubgroup }), - ...(actionGroupId && { action_group_id: actionGroupId }), - ...(instanceId && { instance_id: instanceId }), - ...(status && { status }), - }, - }), - saved_objects: savedObjects, - space_ids: ['default'], - ...(task && { - task: { - schedule_delay: 0, - scheduled: DATE_1970, - }, - }), - }, - message: generateMessage({ - action, - instanceId, - actionGroupId, - actionSubgroup, - reason, - status, - actionId, - }), - rule: { - category: 'test', - id: '1', - license: 'basic', - ...(hasRuleName({ action, status }) && { name: RULE_NAME }), - ruleset: 'alerts', - }, + id: id ?? '1', + typeId: 'action', + alertId: alertId ?? '1', + alertGroup: alertGroup ?? 'default', + ...(subgroup ? { alertSubgroup: subgroup } : {}), }); -const generateMessage = ({ - action, - instanceId, - actionGroupId, - actionSubgroup, - actionId, - reason, - status, -}: GeneratorParams) => { - if (action === EVENT_LOG_ACTIONS.executeStart) { - return `rule execution start: "${mockTaskInstance().params.alertId}"`; - } - - if (action === EVENT_LOG_ACTIONS.newInstance) { - return `${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}' created new alert: '${instanceId}'`; - } - - if (action === EVENT_LOG_ACTIONS.activeInstance) { - if (actionSubgroup) { - return `${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}' active alert: '${instanceId}' in actionGroup(subgroup): 'default(${actionSubgroup})'`; - } - return `${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}' active alert: '${instanceId}' in actionGroup: '${actionGroupId}'`; - } - - if (action === EVENT_LOG_ACTIONS.recoveredInstance) { - return `${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}' alert '${instanceId}' has recovered`; - } - - if (action === EVENT_LOG_ACTIONS.executeAction) { - if (actionSubgroup) { - return `alert: ${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}' instanceId: '${instanceId}' scheduled actionGroup(subgroup): 'default(${actionSubgroup})' action: action:${actionId}`; - } - return `alert: ${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}' instanceId: '${instanceId}' scheduled actionGroup: '${actionGroupId}' action: action:${actionId}`; - } - - if (action === EVENT_LOG_ACTIONS.execute) { - if (status === 'error' && reason === 'execute') { - return `rule execution failure: ${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}'`; - } - if (status === 'error') { - return `${RULE_TYPE_ID}:${RULE_ID}: execution failed`; - } - if (actionGroupId === 'recovered') { - return `rule-name' instanceId: '${instanceId}' scheduled actionGroup: '${actionGroupId}' action: action:${actionId}`; - } - if ( - status === 'warning' && - reason === RuleExecutionStatusWarningReasons.MAX_EXECUTABLE_ACTIONS - ) { - return `The maximum number of actions for this rule type was reached; excess actions were not triggered.`; - } - return `rule executed: ${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}'`; - } -}; - -const generateErrorMessage = (reason: string) => { - if (reason === 'disabled') { - return 'Rule failed to execute because rule ran after it was disabled.'; - } - return GENERIC_ERROR_MESSAGE; -}; - export const generateRunnerResult = ({ successRatio = 1, history = Array(false), @@ -424,6 +287,3 @@ export const generateAlertInstance = ({ id, duration, start }: GeneratorParams = }, }, }); -const hasRuleName = ({ action, status }: GeneratorParams) => { - return action !== 'execute-start' && status !== 'error'; -}; diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index 111c0a7689504..7d95f63f3c43c 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -45,9 +45,8 @@ import { ExecuteOptions } from '@kbn/actions-plugin/server/create_execute_functi import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock'; import moment from 'moment'; import { - generateActionSO, - generateAlertSO, - generateEventLog, + generateAlertOpts, + generateActionOpts, mockDate, mockedRuleTypeSavedObject, mockRunNowResponse, @@ -71,6 +70,11 @@ import { EVENT_LOG_ACTIONS } from '../plugin'; import { IN_MEMORY_METRICS } from '../monitoring'; import { translations } from '../constants/translations'; import { dataPluginMock } from '@kbn/data-plugin/server/mocks'; +import { + AlertingEventLogger, + RuleContextOpts, +} from '../lib/alerting_event_logger/alerting_event_logger'; +import { alertingEventLoggerMock } from '../lib/alerting_event_logger/alerting_event_logger.mock'; jest.mock('uuid', () => ({ v4: () => '5f6aa57d-3e22-484e-bae8-cbed868f4d28', @@ -80,17 +84,30 @@ jest.mock('../lib/wrap_scoped_cluster_client', () => ({ createWrappedScopedClusterClientFactory: jest.fn(), })); +jest.mock('../lib/alerting_event_logger/alerting_event_logger'); + let fakeTimer: sinon.SinonFakeTimers; const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); +const alertingEventLogger = alertingEventLoggerMock.create(); describe('Task Runner', () => { let mockedTaskInstance: ConcreteTaskInstance; + let alertingEventLoggerInitializer: RuleContextOpts; beforeAll(() => { fakeTimer = sinon.useFakeTimers(); mockedTaskInstance = mockTaskInstance(); + + alertingEventLoggerInitializer = { + consumer: mockedTaskInstance.params.consumer, + executionId: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + ruleId: mockedTaskInstance.params.alertId, + ruleType, + spaceId: mockedTaskInstance.params.spaceId, + taskScheduledAt: mockedTaskInstance.scheduledAt, + }; }); afterAll(() => fakeTimer.restore()); @@ -186,6 +203,9 @@ describe('Task Runner', () => { ); mockedRuleTypeSavedObject.monitoring!.execution.history = []; mockedRuleTypeSavedObject.monitoring!.execution.calculated_metrics.success_ratio = 0; + + alertingEventLogger.getStartAndDuration.mockImplementation(() => ({ start: new Date() })); + (AlertingEventLogger as jest.Mock).mockImplementation(() => alertingEventLogger); }); test('successfully executes the task', async () => { @@ -201,10 +221,13 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalledTimes(1); + rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); expect(runnerResult).toEqual(generateRunnerResult({ state: true, history: [true] })); + expect(ruleType.executor).toHaveBeenCalledTimes(1); const call = ruleType.executor.mock.calls[0][0]; expect(call.params).toEqual({ bar: true }); @@ -247,16 +270,7 @@ describe('Task Runner', () => { 'ruleRunMetrics for test:1: {"numSearches":3,"totalSearchDurationMs":23423,"esSearchDurationMs":33,"numberOfTriggeredActions":0,"numberOfGeneratedActions":0,"numberOfActiveAlerts":0,"numberOfRecoveredAlerts":0,"numberOfNewAlerts":0,"triggeredActionsStatus":"complete"}' ); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent).toHaveBeenCalledWith( - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); + testAlertingEventLogCalls({ status: 'ok' }); expect( taskRunnerFactoryInitializerParams.internalSavedObjectsRepository.update @@ -309,6 +323,8 @@ describe('Task Runner', () => { customTaskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalledTimes(1); + rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); @@ -331,66 +347,38 @@ describe('Task Runner', () => { 'ruleRunMetrics for test:1: {"numSearches":3,"totalSearchDurationMs":23423,"esSearchDurationMs":33,"numberOfTriggeredActions":1,"numberOfGeneratedActions":1,"numberOfActiveAlerts":1,"numberOfRecoveredAlerts":0,"numberOfNewAlerts":1,"triggeredActionsStatus":"complete"}' ); - const eventLogger = customTaskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(5); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + testAlertingEventLogCalls({ + activeAlerts: 1, + generatedActions: 1, + newAlerts: 1, + triggeredActions: 1, + status: 'active', + logAlert: 2, + logAction: 1, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ - duration: 0, - start: DATE_1970, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.newInstance, - actionSubgroup: 'subDefault', - actionGroupId: 'default', - instanceId: '1', - consumer: 'bar', + group: 'default', + subgroup: 'subDefault', + state: { start: DATE_1970, duration: '0' }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 3, - generateEventLog({ - duration: 0, - start: DATE_1970, + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 2, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.activeInstance, - actionGroupId: 'default', - actionSubgroup: 'subDefault', - instanceId: '1', - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 4, - generateEventLog({ - action: EVENT_LOG_ACTIONS.executeAction, - actionGroupId: 'default', - instanceId: '1', - actionSubgroup: 'subDefault', - savedObjects: [generateAlertSO('1'), generateActionSO('1')], - consumer: 'bar', - actionId: '1', + group: 'default', + subgroup: 'subDefault', + state: { start: DATE_1970, duration: '0' }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 5, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'success', - status: 'active', - numberOfTriggeredActions: 1, - numberOfGeneratedActions: 1, - numberOfActiveAlerts: 1, - numberOfNewAlerts: 1, - task: true, - consumer: 'bar', - }) + expect(alertingEventLogger.logAction).toHaveBeenNthCalledWith( + 1, + generateActionOpts({ subgroup: 'subDefault' }) ); + expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); } ); @@ -417,6 +405,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalledTimes(1); + rulesClient.get.mockResolvedValue({ ...mockedRuleTypeSavedObject, muteAll: true, @@ -445,53 +435,29 @@ describe('Task Runner', () => { 'ruleRunMetrics for test:1: {"numSearches":3,"totalSearchDurationMs":23423,"esSearchDurationMs":33,"numberOfTriggeredActions":0,"numberOfGeneratedActions":0,"numberOfActiveAlerts":1,"numberOfRecoveredAlerts":0,"numberOfNewAlerts":1,"triggeredActionsStatus":"complete"}' ); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent).toHaveBeenCalledTimes(4); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + testAlertingEventLogCalls({ + activeAlerts: 1, + newAlerts: 1, + status: 'active', + logAlert: 2, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ - duration: 0, - start: DATE_1970, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.newInstance, - actionGroupId: 'default', - instanceId: '1', - consumer: 'bar', + group: 'default', + state: { start: DATE_1970, duration: '0' }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 3, - generateEventLog({ - duration: 0, - start: DATE_1970, + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 2, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.activeInstance, - actionGroupId: 'default', - instanceId: '1', - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 4, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'success', - status: 'active', - numberOfTriggeredActions: 0, - numberOfGeneratedActions: 0, - numberOfActiveAlerts: 1, - numberOfNewAlerts: 1, - task: true, - consumer: 'bar', + group: 'default', + state: { start: DATE_1970, duration: '0' }, }) ); + expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -536,6 +502,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalledTimes(1); + rulesClient.get.mockResolvedValue({ ...mockedRuleTypeSavedObject, muteAll, @@ -588,6 +556,8 @@ describe('Task Runner', () => { customTaskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalledTimes(1); + rulesClient.get.mockResolvedValue({ ...mockedRuleTypeSavedObject, mutedInstanceIds: ['2'], @@ -666,6 +636,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalledTimes(1); + rulesClient.get.mockResolvedValue({ ...mockedRuleTypeSavedObject, throttle: '1d', @@ -709,6 +681,8 @@ describe('Task Runner', () => { customTaskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalledTimes(1); + rulesClient.get.mockResolvedValue({ ...mockedRuleTypeSavedObject, mutedInstanceIds: ['2'], @@ -765,6 +739,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalledTimes(1); + rulesClient.get.mockResolvedValue({ ...mockedRuleTypeSavedObject, notifyWhen: 'onActionGroupChange', @@ -773,46 +749,25 @@ describe('Task Runner', () => { await taskRunner.run(); expect(actionsClient.ephemeralEnqueuedExecution).toHaveBeenCalledTimes(0); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(3); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + testAlertingEventLogCalls({ + activeAlerts: 1, + status: 'active', + logAlert: 1, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ - duration: MOCK_DURATION, - start: DATE_1969, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.activeInstance, - actionGroupId: 'default', - instanceId: '1', - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 3, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'success', - status: 'active', - numberOfTriggeredActions: 0, - numberOfGeneratedActions: 0, - numberOfActiveAlerts: 1, - task: true, - consumer: 'bar', + group: 'default', + state: { start: DATE_1969, duration: MOCK_DURATION, bar: false }, }) ); + expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); test.each(ephemeralTestParams)( - 'actionsPlugin.execute is called when notifyWhen=onActionGroupChange and alert alert state has changed %s', + 'actionsPlugin.execute is called when notifyWhen=onActionGroupChange and alert state has changed %s', async (nameExtension, customTaskRunnerFactoryInitializerParams, enqueueFunction) => { customTaskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue( true @@ -852,28 +807,34 @@ describe('Task Runner', () => { customTaskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalledTimes(1); + rulesClient.get.mockResolvedValue({ ...mockedRuleTypeSavedObject, notifyWhen: 'onActionGroupChange', }); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); - const eventLogger = customTaskRunnerFactoryInitializerParams.eventLogger; await taskRunner.run(); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 4, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'success', - status: 'active', - numberOfTriggeredActions: 1, - numberOfGeneratedActions: 1, - numberOfActiveAlerts: 1, - task: true, - consumer: 'bar', + testAlertingEventLogCalls({ + activeAlerts: 1, + triggeredActions: 1, + generatedActions: 1, + status: 'active', + logAlert: 1, + logAction: 1, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 1, + generateAlertOpts({ + action: EVENT_LOG_ACTIONS.activeInstance, + group: 'default', + state: { bar: false }, }) ); + expect(alertingEventLogger.logAction).toHaveBeenNthCalledWith(1, generateActionOpts({})); + expect(enqueueFunction).toHaveBeenCalledTimes(1); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); } @@ -927,6 +888,8 @@ describe('Task Runner', () => { customTaskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue({ ...mockedRuleTypeSavedObject, notifyWhen: 'onActionGroupChange', @@ -934,21 +897,27 @@ describe('Task Runner', () => { encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); - const eventLogger = customTaskRunnerFactoryInitializerParams.eventLogger; - - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 4, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'success', - status: 'active', - numberOfTriggeredActions: 1, - numberOfGeneratedActions: 1, - numberOfActiveAlerts: 1, - task: true, - consumer: 'bar', + testAlertingEventLogCalls({ + activeAlerts: 1, + triggeredActions: 1, + generatedActions: 1, + status: 'active', + logAlert: 1, + logAction: 1, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 1, + generateAlertOpts({ + action: EVENT_LOG_ACTIONS.activeInstance, + state: { bar: false }, + group: 'default', + subgroup: 'subgroup1', }) ); + expect(alertingEventLogger.logAction).toHaveBeenNthCalledWith( + 1, + generateActionOpts({ subgroup: 'subgroup1' }) + ); expect(enqueueFunction).toHaveBeenCalledTimes(1); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); @@ -984,6 +953,8 @@ describe('Task Runner', () => { customTaskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); await taskRunner.run(); @@ -1010,65 +981,33 @@ describe('Task Runner', () => { expect(enqueueFunction).toHaveBeenCalledTimes(1); expect(enqueueFunction).toHaveBeenCalledWith(generateEnqueueFunctionInput()); - const eventLogger = customTaskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(5); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + testAlertingEventLogCalls({ + activeAlerts: 1, + newAlerts: 1, + triggeredActions: 1, + generatedActions: 1, + status: 'active', + logAlert: 2, + logAction: 1, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ - duration: 0, - start: DATE_1970, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.newInstance, - actionGroupId: 'default', - instanceId: '1', - consumer: 'bar', + group: 'default', + state: { start: DATE_1970, duration: '0' }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 3, - generateEventLog({ - duration: 0, - start: DATE_1970, + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 2, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.activeInstance, - actionGroupId: 'default', - instanceId: '1', - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 4, - generateEventLog({ - action: EVENT_LOG_ACTIONS.executeAction, - actionGroupId: 'default', - instanceId: '1', - actionId: '1', - savedObjects: [generateAlertSO('1'), generateActionSO('1')], - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 5, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'success', - status: 'active', - numberOfTriggeredActions: 1, - numberOfGeneratedActions: 1, - numberOfActiveAlerts: 1, - numberOfNewAlerts: 1, - task: true, - consumer: 'bar', + group: 'default', + state: { start: DATE_1970, duration: '0' }, }) ); + expect(alertingEventLogger.logAction).toHaveBeenNthCalledWith(1, generateActionOpts({})); + expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); } ); @@ -1109,7 +1048,7 @@ describe('Task Runner', () => { state: { bar: false, start: DATE_1969, - duration: 80000000000, + duration: '80000000000', }, }, '2': { @@ -1117,7 +1056,7 @@ describe('Task Runner', () => { state: { bar: false, start: '1969-12-31T06:00:00.000Z', - duration: 70000000000, + duration: '70000000000', }, }, }, @@ -1126,6 +1065,8 @@ describe('Task Runner', () => { customTaskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); @@ -1153,76 +1094,40 @@ describe('Task Runner', () => { 'ruleRunMetrics for test:1: {"numSearches":3,"totalSearchDurationMs":23423,"esSearchDurationMs":33,"numberOfTriggeredActions":2,"numberOfGeneratedActions":2,"numberOfActiveAlerts":1,"numberOfRecoveredAlerts":1,"numberOfNewAlerts":0,"triggeredActionsStatus":"complete"}' ); - const eventLogger = customTaskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(6); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + testAlertingEventLogCalls({ + activeAlerts: 1, + recoveredAlerts: 1, + triggeredActions: 2, + generatedActions: 2, + status: 'active', + logAlert: 2, + logAction: 2, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ + generateAlertOpts({ action: EVENT_LOG_ACTIONS.recoveredInstance, - duration: 64800000000000, - instanceId: '2', - start: '1969-12-31T06:00:00.000Z', - end: DATE_1970, - consumer: 'bar', + id: '2', + state: { + bar: false, + start: '1969-12-31T06:00:00.000Z', + duration: '64800000000000', + end: DATE_1970, + }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 3, - generateEventLog({ + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 2, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.activeInstance, - actionGroupId: 'default', - duration: MOCK_DURATION, - start: DATE_1969, - instanceId: '1', - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 4, - generateEventLog({ - action: EVENT_LOG_ACTIONS.executeAction, - savedObjects: [generateAlertSO('1'), generateActionSO('1')], - actionGroupId: 'default', - instanceId: '1', - actionId: '1', - consumer: 'bar', - }) - ); - - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 5, - generateEventLog({ - action: EVENT_LOG_ACTIONS.executeAction, - savedObjects: [generateAlertSO('1'), generateActionSO('2')], - actionGroupId: 'recovered', - instanceId: '2', - actionId: '2', - consumer: 'bar', + group: 'default', + state: { bar: false, start: DATE_1969, duration: MOCK_DURATION }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 6, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'success', - status: 'active', - numberOfTriggeredActions: 2, - numberOfGeneratedActions: 2, - numberOfActiveAlerts: 1, - numberOfRecoveredAlerts: 1, - task: true, - consumer: 'bar', - }) + expect(alertingEventLogger.logAction).toHaveBeenNthCalledWith(1, generateActionOpts({})); + expect(alertingEventLogger.logAction).toHaveBeenNthCalledWith( + 2, + generateActionOpts({ id: '2', alertId: '2', alertGroup: 'recovered' }) ); expect(enqueueFunction).toHaveBeenCalledTimes(2); @@ -1234,7 +1139,6 @@ describe('Task Runner', () => { test.each(ephemeralTestParams)( "should skip alertInstances which weren't active on the previous execution %s", async (nameExtension, customTaskRunnerFactoryInitializerParams, enqueueFunction) => { - const alertId = 'e558aaad-fd81-46d2-96fc-3bd8fc3dc03f'; customTaskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue( true ); @@ -1270,13 +1174,12 @@ describe('Task Runner', () => { '2': { meta: {}, state: { bar: false } }, }, }, - params: { - alertId, - }, }, customTaskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); @@ -1284,24 +1187,32 @@ describe('Task Runner', () => { const logger = customTaskRunnerFactoryInitializerParams.logger; expect(logger.debug).toHaveBeenCalledWith( - `rule test:${alertId}: '${RULE_NAME}' has 1 active alerts: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"}]` + `rule test:1: '${RULE_NAME}' has 1 active alerts: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"}]` ); expect(logger.debug).nthCalledWith( 3, - `rule test:${alertId}: '${RULE_NAME}' has 1 recovered alerts: [\"2\"]` + `rule test:1: '${RULE_NAME}' has 1 recovered alerts: [\"2\"]` ); expect(logger.debug).nthCalledWith( 4, - `ruleRunStatus for test:${alertId}: {"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}` + `ruleRunStatus for test:1: {"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}` ); expect(logger.debug).nthCalledWith( 5, - `ruleRunMetrics for test:${alertId}: {"numSearches":3,"totalSearchDurationMs":23423,"esSearchDurationMs":33,"numberOfTriggeredActions":2,"numberOfGeneratedActions":2,"numberOfActiveAlerts":1,"numberOfRecoveredAlerts":1,"numberOfNewAlerts":0,"triggeredActionsStatus":"complete"}` + `ruleRunMetrics for test:1: {"numSearches":3,"totalSearchDurationMs":23423,"esSearchDurationMs":33,"numberOfTriggeredActions":2,"numberOfGeneratedActions":2,"numberOfActiveAlerts":1,"numberOfRecoveredAlerts":1,"numberOfNewAlerts":0,"triggeredActionsStatus":"complete"}` ); - const eventLogger = customTaskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(6); + testAlertingEventLogCalls({ + activeAlerts: 1, + recoveredAlerts: 1, + triggeredActions: 2, + generatedActions: 2, + status: 'active', + logAlert: 2, + logAction: 2, + }); + expect(enqueueFunction).toHaveBeenCalledTimes(2); expect((enqueueFunction as jest.Mock).mock.calls[1][0].id).toEqual('2'); expect((enqueueFunction as jest.Mock).mock.calls[0][0].id).toEqual('1'); @@ -1359,6 +1270,8 @@ describe('Task Runner', () => { customTaskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue({ ...mockedRuleTypeSavedObject, actions: [ @@ -1384,8 +1297,20 @@ describe('Task Runner', () => { const runnerResult = await taskRunner.run(); expect(runnerResult.state.alertInstances).toEqual(generateAlertInstance()); - const eventLogger = customTaskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(6); + testAlertingEventLogCalls({ + ruleContext: { + ...alertingEventLoggerInitializer, + ruleType: ruleTypeWithCustomRecovery, + }, + activeAlerts: 1, + recoveredAlerts: 1, + triggeredActions: 2, + generatedActions: 2, + status: 'active', + logAlert: 2, + logAction: 2, + }); + expect(enqueueFunction).toHaveBeenCalledTimes(2); expect(enqueueFunction).toHaveBeenCalledWith(generateEnqueueFunctionInput()); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); @@ -1419,7 +1344,7 @@ describe('Task Runner', () => { state: { bar: false, start: DATE_1969, - duration: 80000000000, + duration: '80000000000', }, }, '2': { @@ -1427,7 +1352,7 @@ describe('Task Runner', () => { state: { bar: false, start: '1969-12-31T06:00:00.000Z', - duration: 70000000000, + duration: '70000000000', }, }, }, @@ -1436,6 +1361,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); @@ -1443,55 +1370,37 @@ describe('Task Runner', () => { generateAlertInstance({ id: 1, duration: MOCK_DURATION, start: DATE_1969 }) ); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(4); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + testAlertingEventLogCalls({ + activeAlerts: 1, + recoveredAlerts: 1, + triggeredActions: 0, + generatedActions: 2, + status: 'active', + logAlert: 2, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ + generateAlertOpts({ action: EVENT_LOG_ACTIONS.recoveredInstance, - actionGroupId: 'default', - duration: 64800000000000, - instanceId: '2', - start: '1969-12-31T06:00:00.000Z', - end: DATE_1970, - consumer: 'bar', + id: '2', + group: 'default', + state: { + bar: false, + start: '1969-12-31T06:00:00.000Z', + duration: '64800000000000', + end: DATE_1970, + }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 3, - generateEventLog({ + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 2, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.activeInstance, - actionGroupId: 'default', - duration: MOCK_DURATION, - start: DATE_1969, - instanceId: '1', - consumer: 'bar', + group: 'default', + state: { bar: false, start: DATE_1969, duration: MOCK_DURATION }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 4, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'success', - status: 'active', - numberOfTriggeredActions: 0, - numberOfGeneratedActions: 2, - numberOfActiveAlerts: 1, - numberOfRecoveredAlerts: 1, - task: true, - consumer: 'bar', - }) - ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -1515,6 +1424,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); const runnerResult = await taskRunner.run(); @@ -1532,6 +1443,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); @@ -1560,6 +1473,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ ...SAVED_OBJECT, @@ -1590,6 +1505,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); rulesClient.get.mockResolvedValueOnce(mockedRuleTypeSavedObject); rulesClient.get.mockResolvedValueOnce({ @@ -1626,6 +1542,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); @@ -1633,28 +1550,13 @@ describe('Task Runner', () => { const runnerResult = await taskRunner.run(); expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0 })); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'failure', - reason: 'execute', - task: true, - status: 'error', - consumer: 'bar', - }) - ); + + testAlertingEventLogCalls({ + status: 'error', + errorReason: 'execute', + executionStatus: 'failed', + }); + expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -1669,6 +1571,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); @@ -1676,28 +1579,13 @@ describe('Task Runner', () => { expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0 })); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'failure', - task: true, - reason: 'decrypt', - status: 'error', - consumer: 'bar', - }) - ); + testAlertingEventLogCalls({ + setRuleName: false, + status: 'error', + errorReason: 'decrypt', + executionStatus: 'not-reached', + }); + expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -1712,6 +1600,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); @@ -1720,28 +1609,12 @@ describe('Task Runner', () => { expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0 })); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'failure', - task: true, - reason: 'license', - status: 'error', - consumer: 'bar', - }) - ); + testAlertingEventLogCalls({ + status: 'error', + errorReason: 'license', + executionStatus: 'not-reached', + }); + expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -1756,6 +1629,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); @@ -1764,20 +1638,13 @@ describe('Task Runner', () => { expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0 })); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'failure', - task: true, - reason: 'unknown', - status: 'error', - consumer: 'bar', - }) - ); + testAlertingEventLogCalls({ + setRuleName: false, + status: 'error', + errorReason: 'unknown', + executionStatus: 'not-reached', + }); + expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -1792,6 +1659,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); @@ -1799,20 +1667,13 @@ describe('Task Runner', () => { expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0 })); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'failure', - task: true, - reason: 'read', - status: 'error', - consumer: 'bar', - }) - ); + testAlertingEventLogCalls({ + setRuleName: false, + status: 'error', + errorReason: 'read', + executionStatus: 'not-reached', + }); + expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -1831,6 +1692,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); @@ -1868,6 +1730,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); @@ -1897,6 +1760,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); @@ -1927,6 +1791,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); @@ -1950,6 +1815,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); @@ -1975,6 +1841,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); @@ -2022,6 +1889,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue({ ...mockedRuleTypeSavedObject, notifyWhen: 'onActionGroupChange', @@ -2030,76 +1899,53 @@ describe('Task Runner', () => { encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(6); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + testAlertingEventLogCalls({ + activeAlerts: 2, + newAlerts: 2, + status: 'active', + logAlert: 4, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', + generateAlertOpts({ + action: EVENT_LOG_ACTIONS.newInstance, + group: 'default', + state: { + start: DATE_1970, + duration: '0', + }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( 2, - generateEventLog({ - duration: 0, - start: DATE_1970, + generateAlertOpts({ + id: '2', action: EVENT_LOG_ACTIONS.newInstance, - actionGroupId: 'default', - instanceId: '1', - consumer: 'bar', + group: 'default', + state: { + start: DATE_1970, + duration: '0', + }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( 3, - generateEventLog({ - duration: 0, - start: DATE_1970, - action: EVENT_LOG_ACTIONS.newInstance, - actionGroupId: 'default', - instanceId: '2', - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 4, - generateEventLog({ - duration: 0, - start: DATE_1970, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.activeInstance, - actionGroupId: 'default', - instanceId: '1', - consumer: 'bar', + group: 'default', + state: { start: DATE_1970, duration: '0' }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 5, - generateEventLog({ - duration: 0, - start: DATE_1970, + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 4, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.activeInstance, - actionGroupId: 'default', - instanceId: '2', - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 6, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'success', - status: 'active', - numberOfTriggeredActions: 0, - numberOfGeneratedActions: 0, - numberOfActiveAlerts: 2, - numberOfNewAlerts: 2, - task: true, - consumer: 'bar', + id: '2', + group: 'default', + state: { start: DATE_1970, duration: '0' }, }) ); + expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -2132,7 +1978,7 @@ describe('Task Runner', () => { state: { bar: false, start: DATE_1969, - duration: 80000000000, + duration: '80000000000', }, }, '2': { @@ -2140,7 +1986,7 @@ describe('Task Runner', () => { state: { bar: false, start: '1969-12-31T06:00:00.000Z', - duration: 70000000000, + duration: '70000000000', }, }, }, @@ -2149,6 +1995,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue({ ...mockedRuleTypeSavedObject, notifyWhen: 'onActionGroupChange', @@ -2157,51 +2005,26 @@ describe('Task Runner', () => { encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(4); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + testAlertingEventLogCalls({ + activeAlerts: 2, + status: 'active', + logAlert: 2, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ + generateAlertOpts({ action: EVENT_LOG_ACTIONS.activeInstance, - actionGroupId: 'default', - duration: MOCK_DURATION, - start: DATE_1969, - instanceId: '1', - consumer: 'bar', + group: 'default', + state: { bar: false, start: DATE_1969, duration: MOCK_DURATION }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 3, - generateEventLog({ + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 2, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.activeInstance, - actionGroupId: 'default', - duration: 64800000000000, - start: '1969-12-31T06:00:00.000Z', - instanceId: '2', - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 4, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'success', - status: 'active', - numberOfTriggeredActions: 0, - numberOfGeneratedActions: 0, - numberOfActiveAlerts: 2, - task: true, - consumer: 'bar', + id: '2', + group: 'default', + state: { bar: false, start: '1969-12-31T06:00:00.000Z', duration: '64800000000000' }, }) ); @@ -2246,6 +2069,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue({ ...mockedRuleTypeSavedObject, notifyWhen: 'onActionGroupChange', @@ -2254,48 +2079,29 @@ describe('Task Runner', () => { encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(4); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + testAlertingEventLogCalls({ + activeAlerts: 2, + status: 'active', + logAlert: 2, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ + generateAlertOpts({ action: EVENT_LOG_ACTIONS.activeInstance, - actionGroupId: 'default', - instanceId: '1', - consumer: 'bar', + group: 'default', + state: { bar: false }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 3, - generateEventLog({ + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 2, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.activeInstance, - actionGroupId: 'default', - consumer: 'bar', - instanceId: '2', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 4, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'success', - status: 'active', - consumer: 'bar', - numberOfTriggeredActions: 0, - numberOfGeneratedActions: 0, - numberOfActiveAlerts: 2, - task: true, + id: '2', + group: 'default', + state: { bar: false }, }) ); + expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -2315,7 +2121,7 @@ describe('Task Runner', () => { state: { bar: false, start: DATE_1969, - duration: 80000000000, + duration: '80000000000', }, }, '2': { @@ -2323,7 +2129,7 @@ describe('Task Runner', () => { state: { bar: false, start: '1969-12-31T06:00:00.000Z', - duration: 70000000000, + duration: '70000000000', }, }, }, @@ -2332,6 +2138,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue({ ...mockedRuleTypeSavedObject, notifyWhen: 'onActionGroupChange', @@ -2340,53 +2148,32 @@ describe('Task Runner', () => { encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(4); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + testAlertingEventLogCalls({ + recoveredAlerts: 2, + status: 'ok', + logAlert: 2, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ + generateAlertOpts({ action: EVENT_LOG_ACTIONS.recoveredInstance, - duration: MOCK_DURATION, - start: DATE_1969, - end: DATE_1970, - consumer: 'bar', - instanceId: '1', + state: { bar: false, start: DATE_1969, end: DATE_1970, duration: MOCK_DURATION }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 3, - generateEventLog({ + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 2, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.recoveredInstance, - duration: 64800000000000, - start: '1969-12-31T06:00:00.000Z', - end: DATE_1970, - consumer: 'bar', - instanceId: '2', + id: '2', + state: { + bar: false, + start: '1969-12-31T06:00:00.000Z', + end: DATE_1970, + duration: '64800000000000', + }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 4, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'success', - status: 'ok', - consumer: 'bar', - numberOfTriggeredActions: 0, - numberOfGeneratedActions: 0, - numberOfRecoveredAlerts: 2, - task: true, - }) - ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -2425,6 +2212,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue({ ...mockedRuleTypeSavedObject, notifyWhen: 'onActionGroupChange', @@ -2433,47 +2222,29 @@ describe('Task Runner', () => { encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(4); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + testAlertingEventLogCalls({ + recoveredAlerts: 2, + status: 'ok', + logAlert: 2, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ + generateAlertOpts({ action: EVENT_LOG_ACTIONS.recoveredInstance, - consumer: 'bar', - instanceId: '1', + state: { bar: false }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 3, - generateEventLog({ + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 2, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.recoveredInstance, - consumer: 'bar', - instanceId: '2', + id: '2', + state: { + bar: false, + }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 4, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'success', - status: 'ok', - consumer: 'bar', - numberOfTriggeredActions: 0, - numberOfGeneratedActions: 0, - numberOfRecoveredAlerts: 2, - task: true, - }) - ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -2493,6 +2264,8 @@ describe('Task Runner', () => { }, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); @@ -2539,17 +2312,10 @@ describe('Task Runner', () => { 'ruleRunMetrics for test:1: {"numSearches":3,"totalSearchDurationMs":23423,"esSearchDurationMs":33,"numberOfTriggeredActions":0,"numberOfGeneratedActions":0,"numberOfActiveAlerts":0,"numberOfRecoveredAlerts":0,"numberOfNewAlerts":0,"triggeredActionsStatus":"complete"}' ); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); + testAlertingEventLogCalls({ + status: 'ok', + }); + expect( taskRunnerFactoryInitializerParams.internalSavedObjectsRepository.update ).toHaveBeenCalledWith(...generateSavedObjectParams({})); @@ -2570,6 +2336,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ ...SAVED_OBJECT, @@ -2579,28 +2347,14 @@ describe('Task Runner', () => { expect(runnerResult.state.previousStartedAt?.toISOString()).toBe(state.previousStartedAt); expect(runnerResult.schedule).toStrictEqual(mockedTaskInstance.schedule); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ - errorMessage: 'Rule failed to execute because rule ran after it was disabled.', - action: EVENT_LOG_ACTIONS.execute, - consumer: 'bar', - outcome: 'failure', - task: true, - reason: 'disabled', - status: 'error', - }) - ); + testAlertingEventLogCalls({ + setRuleName: false, + status: 'error', + errorReason: 'disabled', + errorMessage: `Rule failed to execute because rule ran after it was disabled.`, + executionStatus: 'not-reached', + }); + expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -2611,6 +2365,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); @@ -2625,6 +2380,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); ruleType.executor.mockImplementation( @@ -2651,6 +2408,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); @@ -2684,6 +2443,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); @@ -2767,6 +2528,7 @@ describe('Task Runner', () => { }, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); const runnerResult = await taskRunner.run(); @@ -2789,7 +2551,7 @@ describe('Task Runner', () => { }, }, state: { - duration: 0, + duration: '0', start: '1970-01-01T00:00:00.000Z', }, }, @@ -2805,87 +2567,40 @@ describe('Task Runner', () => { 'Rule "1" skipped scheduling action "4" because the maximum number of allowed actions has been reached.' ); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(7); - - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + testAlertingEventLogCalls({ + newAlerts: 1, + activeAlerts: 1, + triggeredActions: actionsConfigMap.default.max, + generatedActions: mockActions.length, + status: 'warning', + errorReason: `maxExecutableActions`, + logAlert: 2, + logAction: 3, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( 1, - generateEventLog({ - task: true, - action: EVENT_LOG_ACTIONS.executeStart, - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 2, - generateEventLog({ - duration: 0, - start: DATE_1970, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.newInstance, - actionGroupId: 'default', - instanceId: '1', - consumer: 'bar', + group: 'default', + state: { start: DATE_1970, duration: '0' }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 3, - generateEventLog({ - duration: 0, - start: DATE_1970, + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 2, + generateAlertOpts({ action: EVENT_LOG_ACTIONS.activeInstance, - actionGroupId: 'default', - instanceId: '1', - consumer: 'bar', - }) - ); - - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 4, - generateEventLog({ - action: EVENT_LOG_ACTIONS.executeAction, - savedObjects: [generateAlertSO('1'), generateActionSO('1')], - actionGroupId: 'default', - instanceId: '1', - actionId: '1', - consumer: 'bar', - }) - ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 5, - generateEventLog({ - action: EVENT_LOG_ACTIONS.executeAction, - savedObjects: [generateAlertSO('1'), generateActionSO('2')], - actionGroupId: 'default', - instanceId: '1', - actionId: '2', - consumer: 'bar', + group: 'default', + state: { start: DATE_1970, duration: '0' }, }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 6, - generateEventLog({ - action: EVENT_LOG_ACTIONS.executeAction, - savedObjects: [generateAlertSO('1'), generateActionSO('3')], - actionGroupId: 'default', - instanceId: '1', - actionId: '3', - consumer: 'bar', - }) + expect(alertingEventLogger.logAction).toHaveBeenNthCalledWith(1, generateActionOpts({})); + expect(alertingEventLogger.logAction).toHaveBeenNthCalledWith( + 2, + generateActionOpts({ id: '2' }) ); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith( - 7, - generateEventLog({ - action: EVENT_LOG_ACTIONS.execute, - outcome: 'success', - status: 'warning', - numberOfTriggeredActions: actionsConfigMap.default.max, - numberOfGeneratedActions: mockActions.length, - numberOfActiveAlerts: 1, - numberOfNewAlerts: 1, - reason: RuleExecutionStatusWarningReasons.MAX_EXECUTABLE_ACTIONS, - task: true, - consumer: 'bar', - }) + expect(alertingEventLogger.logAction).toHaveBeenNthCalledWith( + 3, + generateActionOpts({ id: '3' }) ); }); @@ -2966,6 +2681,7 @@ describe('Task Runner', () => { }, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); const runnerResult = await taskRunner.run(); @@ -2989,7 +2705,7 @@ describe('Task Runner', () => { }, }, state: { - duration: 0, + duration: '0', start: '1970-01-01T00:00:00.000Z', }, }, @@ -3001,7 +2717,7 @@ describe('Task Runner', () => { }, }, state: { - duration: 0, + duration: '0', start: '1970-01-01T00:00:00.000Z', }, }, @@ -3017,8 +2733,16 @@ describe('Task Runner', () => { 'Rule "1" skipped scheduling action "1" because the maximum number of allowed actions for connector type .server-log has been reached.' ); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(11); + testAlertingEventLogCalls({ + newAlerts: 2, + activeAlerts: 2, + generatedActions: 10, + triggeredActions: 5, + status: 'warning', + errorReason: `maxExecutableActions`, + logAlert: 4, + logAction: 5, + }); }); test('increments monitoring metrics after execution', async () => { @@ -3028,6 +2752,8 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalled(); + rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ id: '1', @@ -3067,4 +2793,125 @@ describe('Task Runner', () => { expect(inMemoryMetrics.increment.mock.calls[4][0]).toBe(IN_MEMORY_METRICS.RULE_FAILURES); expect(inMemoryMetrics.increment.mock.calls[5][0]).toBe(IN_MEMORY_METRICS.RULE_TIMEOUTS); }); + + function testAlertingEventLogCalls({ + ruleContext = alertingEventLoggerInitializer, + activeAlerts = 0, + newAlerts = 0, + recoveredAlerts = 0, + triggeredActions = 0, + generatedActions = 0, + status, + errorReason, + errorMessage = 'GENERIC ERROR MESSAGE', + executionStatus = 'succeeded', + setRuleName = true, + logAlert = 0, + logAction = 0, + }: { + status: string; + ruleContext?: RuleContextOpts; + activeAlerts?: number; + newAlerts?: number; + recoveredAlerts?: number; + triggeredActions?: number; + generatedActions?: number; + executionStatus?: 'succeeded' | 'failed' | 'not-reached'; + setRuleName?: boolean; + logAlert?: number; + logAction?: number; + errorReason?: string; + errorMessage?: string; + }) { + expect(alertingEventLogger.initialize).toHaveBeenCalledWith(ruleContext); + expect(alertingEventLogger.start).toHaveBeenCalled(); + if (setRuleName) { + expect(alertingEventLogger.setRuleName).toHaveBeenCalledWith(mockedRuleTypeSavedObject.name); + } else { + expect(alertingEventLogger.setRuleName).not.toHaveBeenCalled(); + } + expect(alertingEventLogger.getStartAndDuration).toHaveBeenCalled(); + if (status === 'error') { + expect(alertingEventLogger.done).toHaveBeenCalledWith({ + metrics: null, + status: { + lastExecutionDate: new Date('1970-01-01T00:00:00.000Z'), + status, + error: { + message: errorMessage, + reason: errorReason, + }, + }, + }); + } else if (status === 'warning') { + expect(alertingEventLogger.done).toHaveBeenCalledWith({ + metrics: { + esSearchDurationMs: 33, + numSearches: 3, + numberOfActiveAlerts: activeAlerts, + numberOfGeneratedActions: generatedActions, + numberOfNewAlerts: newAlerts, + numberOfRecoveredAlerts: recoveredAlerts, + numberOfTriggeredActions: triggeredActions, + totalSearchDurationMs: 23423, + triggeredActionsStatus: 'partial', + }, + status: { + lastExecutionDate: new Date('1970-01-01T00:00:00.000Z'), + status, + warning: { + message: `The maximum number of actions for this rule type was reached; excess actions were not triggered.`, + reason: errorReason, + }, + }, + }); + } else { + expect(alertingEventLogger.done).toHaveBeenCalledWith({ + metrics: { + esSearchDurationMs: 33, + numSearches: 3, + numberOfActiveAlerts: activeAlerts, + numberOfGeneratedActions: generatedActions, + numberOfNewAlerts: newAlerts, + numberOfRecoveredAlerts: recoveredAlerts, + numberOfTriggeredActions: triggeredActions, + totalSearchDurationMs: 23423, + triggeredActionsStatus: 'complete', + }, + status: { + lastExecutionDate: new Date('1970-01-01T00:00:00.000Z'), + status, + }, + }); + } + + if (executionStatus === 'succeeded') { + expect(alertingEventLogger.setExecutionSucceeded).toHaveBeenCalledWith( + `rule executed: test:1: 'rule-name'` + ); + expect(alertingEventLogger.setExecutionFailed).not.toHaveBeenCalled(); + } else if (executionStatus === 'failed') { + expect(alertingEventLogger.setExecutionFailed).toHaveBeenCalledWith( + `rule execution failure: test:1: 'rule-name'`, + errorMessage + ); + expect(alertingEventLogger.setExecutionSucceeded).not.toHaveBeenCalled(); + } else if (executionStatus === 'not-reached') { + expect(alertingEventLogger.setExecutionSucceeded).not.toHaveBeenCalled(); + expect(alertingEventLogger.setExecutionFailed).not.toHaveBeenCalled(); + } + + if (logAlert > 0) { + expect(alertingEventLogger.logAlert).toHaveBeenCalledTimes(logAlert); + } else { + expect(alertingEventLogger.logAlert).not.toHaveBeenCalled(); + } + + if (logAction > 0) { + expect(alertingEventLogger.logAction).toHaveBeenCalledTimes(logAction); + } else { + expect(alertingEventLogger.logAction).not.toHaveBeenCalled(); + } + expect(alertingEventLogger.logTimeout).not.toHaveBeenCalled(); + } }); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 6c161332bb58b..6cd6b73b9539e 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -5,14 +5,14 @@ * 2.0. */ import apm from 'elastic-apm-node'; -import { cloneDeep, mapValues, omit, pickBy, set, without } from 'lodash'; +import { cloneDeep, mapValues, omit, pickBy, without } from 'lodash'; import type { Request } from '@hapi/hapi'; import { UsageCounter } from '@kbn/usage-collection-plugin/server'; import uuid from 'uuid'; import { addSpaceIdToPath } from '@kbn/spaces-plugin/server'; import { KibanaRequest, Logger } from '@kbn/core/server'; import { ConcreteTaskInstance, throwUnrecoverableError } from '@kbn/task-manager-plugin/server'; -import { IEvent, SAVED_OBJECT_REL_PRIMARY } from '@kbn/event-log-plugin/server'; +import { millisToNanos, nanosToMillis } from '@kbn/event-log-plugin/server'; import { TaskRunnerContext } from './task_runner_factory'; import { createExecutionHandler, ExecutionHandler } from './create_execution_handler'; import { Alert, createAlertFactory } from '../alert'; @@ -57,10 +57,6 @@ import { } from '../../common'; import { NormalizedRuleType, UntypedNormalizedRuleType } from '../rule_type_registry'; import { getEsErrorMessage } from '../lib/errors'; -import { - createAlertEventLogRecordObject, - Event, -} from '../lib/create_alert_event_log_record_object'; import { InMemoryMetrics, IN_MEMORY_METRICS } from '../monitoring'; import { GenerateNewAndRecoveredAlertEventsParams, @@ -74,13 +70,11 @@ import { } from './types'; import { IExecutionStatusAndMetrics } from '../lib/rule_execution_status'; import { RuleRunMetricsStore } from '../lib/rule_run_metrics_store'; +import { AlertingEventLogger } from '../lib/alerting_event_logger/alerting_event_logger'; const FALLBACK_RETRY_INTERVAL = '5m'; const CONNECTIVITY_RETRY_INTERVAL = '5m'; -// 1,000,000 nanoseconds in 1 millisecond -const Millis2Nanos = 1000 * 1000; - export const getDefaultRuleMonitoring = (): RuleMonitoring => ({ execution: { history: [], @@ -102,7 +96,6 @@ export class TaskRunner< private context: TaskRunnerContext; private logger: Logger; private taskInstance: RuleTaskInstance; - private ruleName: string | null; private ruleConsumer: string | null; private ruleType: NormalizedRuleType< Params, @@ -116,6 +109,7 @@ export class TaskRunner< private readonly executionId: string; private readonly ruleTypeRegistry: RuleTypeRegistry; private readonly inMemoryMetrics: InMemoryMetrics; + private alertingEventLogger: AlertingEventLogger; private usageCounter?: UsageCounter; private searchAbortController: AbortController; private cancelled: boolean; @@ -138,7 +132,6 @@ export class TaskRunner< this.logger = context.logger; this.usageCounter = context.usageCounter; this.ruleType = ruleType; - this.ruleName = null; this.ruleConsumer = null; this.taskInstance = taskInstanceToAlertTaskInstance(taskInstance); this.ruleTypeRegistry = context.ruleTypeRegistry; @@ -146,6 +139,7 @@ export class TaskRunner< this.cancelled = false; this.executionId = uuid.v4(); this.inMemoryMetrics = inMemoryMetrics; + this.alertingEventLogger = new AlertingEventLogger(this.context.eventLogger); } private async getDecryptedAttributes( @@ -226,7 +220,7 @@ export class TaskRunner< spaceId, ruleType: this.ruleType, kibanaBaseUrl, - eventLogger: this.context.eventLogger, + alertingEventLogger: this.alertingEventLogger, request, ruleParams, supportsEphemeralTasks: this.context.supportsEphemeralTasks, @@ -316,8 +310,7 @@ export class TaskRunner< rule: SanitizedRule, params: Params, executionHandler: ExecutionHandler, - spaceId: string, - event: Event + spaceId: string ): Promise { const { alertTypeId, @@ -353,7 +346,6 @@ export class TaskRunner< const originalAlerts = cloneDeep(alerts); const originalAlertIds = new Set(Object.keys(originalAlerts)); - const eventLogger = this.context.eventLogger; const ruleLabel = `${this.ruleType.id}:${ruleId}: '${name}'`; const scopedClusterClient = this.context.elasticsearch.client.asScoped(fakeRequest); @@ -435,22 +427,15 @@ export class TaskRunner< }) ); } catch (err) { - event.message = `rule execution failure: ${ruleLabel}`; - event.error = event.error || {}; - event.error.message = err.message; - event.event = event.event || {}; - event.event.outcome = 'failure'; + this.alertingEventLogger.setExecutionFailed( + `rule execution failure: ${ruleLabel}`, + err.message + ); throw new ErrorWithReason(RuleExecutionStatusErrorReasons.Execute, err); } - event.message = `rule executed: ${ruleLabel}`; - event.event = event.event || {}; - event.event.outcome = 'success'; - event.rule = { - ...event.rule, - name: rule.name, - }; + this.alertingEventLogger.setExecutionSucceeded(`rule executed: ${ruleLabel}`); const ruleRunMetricsStore = new RuleRunMetricsStore(); @@ -483,17 +468,11 @@ export class TaskRunner< if (this.shouldLogAndScheduleActionsForAlerts()) { generateNewAndRecoveredAlertEvents({ - eventLogger, - executionId: this.executionId, + alertingEventLogger: this.alertingEventLogger, originalAlerts, currentAlerts: alertsWithScheduledActions, recoveredAlerts, - ruleId, ruleLabel, - namespace, - ruleType, - rule, - spaceId, ruleRunMetricsStore, }); } @@ -579,8 +558,7 @@ export class TaskRunner< private async validateAndExecuteRule( fakeRequest: KibanaRequest, apiKey: RawRule['apiKey'], - rule: SanitizedRule, - event: Event + rule: SanitizedRule ) { const { params: { alertId: ruleId, spaceId }, @@ -599,10 +577,10 @@ export class TaskRunner< rule.params, fakeRequest ); - return this.executeRule(fakeRequest, rule, validatedParams, executionHandler, spaceId, event); + return this.executeRule(fakeRequest, rule, validatedParams, executionHandler, spaceId); } - private async loadRuleAttributesAndRun(event: Event): Promise> { + private async loadRuleAttributesAndRun(): Promise> { const { params: { alertId: ruleId, spaceId }, } = this.taskInstance; @@ -652,7 +630,7 @@ export class TaskRunner< throw new ErrorWithReason(RuleExecutionStatusErrorReasons.Read, err); } - this.ruleName = rule.name; + this.alertingEventLogger.setRuleName(rule.name); try { this.ruleTypeRegistry.ensureRuleTypeEnabled(rule.alertTypeId); @@ -669,7 +647,7 @@ export class TaskRunner< return { monitoring: asOk(rule.monitoring), stateWithMetrics: await promiseResult( - this.validateAndExecuteRule(fakeRequest, apiKey, rule, event) + this.validateAndExecuteRule(fakeRequest, apiKey, rule) ), schedule: asOk( // fetch the rule again to ensure we return the correct schedule as it may have @@ -711,46 +689,21 @@ export class TaskRunner< this.logger.debug(`executing rule ${this.ruleType.id}:${ruleId} at ${runDateString}`); const namespace = this.context.spaceIdToNamespace(spaceId); - const eventLogger = this.context.eventLogger; - const scheduleDelay = runDate.getTime() - this.taskInstance.scheduledAt.getTime(); - const event = createAlertEventLogRecordObject({ + this.alertingEventLogger.initialize({ ruleId, ruleType: this.ruleType as UntypedNormalizedRuleType, consumer: this.ruleConsumer!, - action: EVENT_LOG_ACTIONS.execute, - namespace, spaceId, executionId: this.executionId, - task: { - scheduled: this.taskInstance.scheduledAt.toISOString(), - scheduleDelay: Millis2Nanos * scheduleDelay, - }, - savedObjects: [ - { - id: ruleId, - type: 'alert', - typeId: this.ruleType.id, - relation: SAVED_OBJECT_REL_PRIMARY, - }, - ], - }); - - eventLogger.startTiming(event); - - const startEvent = cloneDeep({ - ...event, - event: { - ...event.event, - action: EVENT_LOG_ACTIONS.executeStart, - }, - message: `rule execution start: "${ruleId}"`, + taskScheduledAt: this.taskInstance.scheduledAt, + ...(namespace ? { namespace } : {}), }); - eventLogger.logEvent(startEvent); + this.alertingEventLogger.start(); const { stateWithMetrics, schedule, monitoring } = await errorAsRuleTaskRunResult( - this.loadRuleAttributesAndRun(event) + this.loadRuleAttributesAndRun() ); const ruleMonitoring = @@ -767,10 +720,6 @@ export class TaskRunner< (ruleRunStateWithMetrics) => executionStatusFromState(ruleRunStateWithMetrics, runDate), (err: ElasticsearchError) => executionStatusFromError(err, runDate) ); - // set the executionStatus date to same as event, if it's set - if (event.event?.start) { - executionStatus.lastExecutionDate = new Date(event.event.start); - } if (apm.currentTransaction) { if (executionStatus.status === 'ok' || executionStatus.status === 'active') { @@ -789,91 +738,27 @@ export class TaskRunner< ); } - eventLogger.stopTiming(event); - set(event, 'kibana.alerting.status', executionStatus.status); - - if (this.ruleConsumer) { - set(event, 'kibana.alert.rule.consumer', this.ruleConsumer); - } + this.alertingEventLogger.done({ status: executionStatus, metrics: executionMetrics }); const monitoringHistory: RuleMonitoringHistory = { success: true, timestamp: +new Date(), }; - // Copy duration into execution status if available - if (null != event.event?.duration) { - executionStatus.lastDuration = Math.round(event.event?.duration / Millis2Nanos); + // set start and duration based on event log + const { start, duration } = this.alertingEventLogger.getStartAndDuration(); + if (null != start) { + executionStatus.lastExecutionDate = start; + } + if (null != duration) { + executionStatus.lastDuration = nanosToMillis(duration); monitoringHistory.duration = executionStatus.lastDuration; } // if executionStatus indicates an error, fill in fields in // event from it if (executionStatus.error) { - set(event, 'event.reason', executionStatus.error?.reason || 'unknown'); - set(event, 'event.outcome', 'failure'); - set(event, 'error.message', event?.error?.message || executionStatus.error.message); - if (!event.message) { - event.message = `${this.ruleType.id}:${ruleId}: execution failed`; - } monitoringHistory.success = false; - } else { - if (executionStatus.warning) { - set(event, 'event.reason', executionStatus.warning?.reason || 'unknown'); - set(event, 'message', executionStatus.warning?.message || event?.message); - } - if (executionMetrics) { - set( - event, - 'kibana.alert.rule.execution.metrics.number_of_triggered_actions', - executionMetrics.numberOfTriggeredActions - ); - set( - event, - 'kibana.alert.rule.execution.metrics.number_of_generated_actions', - executionMetrics.numberOfGeneratedActions - ); - set( - event, - 'kibana.alert.rule.execution.metrics.number_of_active_alerts', - executionMetrics.numberOfActiveAlerts - ); - set( - event, - 'kibana.alert.rule.execution.metrics.number_of_new_alerts', - executionMetrics.numberOfNewAlerts - ); - set( - event, - 'kibana.alert.rule.execution.metrics.total_number_of_alerts', - (executionMetrics.numberOfActiveAlerts ?? 0) + - (executionMetrics.numberOfRecoveredAlerts ?? 0) - ); - set( - event, - 'kibana.alert.rule.execution.metrics.number_of_recovered_alerts', - executionMetrics.numberOfRecoveredAlerts - ); - } - } - - // Copy search stats into event log - if (executionMetrics) { - set( - event, - 'kibana.alert.rule.execution.metrics.number_of_searches', - executionMetrics.numSearches ?? 0 - ); - set( - event, - 'kibana.alert.rule.execution.metrics.es_search_duration_ms', - executionMetrics.esSearchDurationMs ?? 0 - ); - set( - event, - 'kibana.alert.rule.execution.metrics.total_search_duration_ms', - executionMetrics.totalSearchDurationMs ?? 0 - ); } ruleMonitoring.execution.history.push(monitoringHistory); @@ -882,8 +767,6 @@ export class TaskRunner< ...getExecutionDurationPercentiles(ruleMonitoring), }; - eventLogger.logEvent(event); - if (!this.cancelled) { this.inMemoryMetrics.increment(IN_MEMORY_METRICS.RULE_EXECUTIONS); if (executionStatus.error) { @@ -977,48 +860,7 @@ export class TaskRunner< ); this.searchAbortController.abort(); - const eventLogger = this.context.eventLogger; - const event: IEvent = { - event: { - action: EVENT_LOG_ACTIONS.executeTimeout, - kind: 'alert', - category: [this.ruleType.producer], - }, - message: `rule: ${this.ruleType.id}:${ruleId}: '${ - this.ruleName ?? '' - }' execution cancelled due to timeout - exceeded rule type timeout of ${ - this.ruleType.ruleTaskTimeout - }`, - kibana: { - alert: { - rule: { - ...(this.ruleConsumer ? { consumer: this.ruleConsumer } : {}), - execution: { - uuid: this.executionId, - }, - rule_type_id: this.ruleType.id, - }, - }, - saved_objects: [ - { - rel: SAVED_OBJECT_REL_PRIMARY, - type: 'alert', - id: ruleId, - type_id: this.ruleType.id, - namespace, - }, - ], - space_ids: [spaceId], - }, - rule: { - id: ruleId, - license: this.ruleType.minimumLicenseRequired, - category: this.ruleType.id, - ruleset: this.ruleType.producer, - ...(this.ruleName ? { name: this.ruleName } : {}), - }, - }; - eventLogger.logEvent(event); + this.alertingEventLogger.logTimeout(); this.inMemoryMetrics.increment(IN_MEMORY_METRICS.RULE_TIMEOUTS); @@ -1062,9 +904,9 @@ function trackAlertDurations< const state = originalAlertIds.includes(id) ? originalAlerts[id].getState() : currentAlerts[id].getState(); - const duration = state.start - ? (new Date(currentTime).valueOf() - new Date(state.start as string).valueOf()) * 1000 * 1000 // nanoseconds - : undefined; + const durationInMs = + new Date(currentTime).valueOf() - new Date(state.start as string).valueOf(); + const duration = state.start ? millisToNanos(durationInMs) : undefined; currentAlerts[id].replaceState({ ...state, ...(state.start ? { start: state.start } : {}), @@ -1075,9 +917,9 @@ function trackAlertDurations< // Inject end time into alert state of recovered alerts for (const id of recoveredAlertIds) { const state = recoveredAlerts[id].getState(); - const duration = state.start - ? (new Date(currentTime).valueOf() - new Date(state.start as string).valueOf()) * 1000 * 1000 // nanoseconds - : undefined; + const durationInMs = + new Date(currentTime).valueOf() - new Date(state.start as string).valueOf(); + const duration = state.start ? millisToNanos(durationInMs) : undefined; recoveredAlerts[id].replaceState({ ...state, ...(duration ? { duration } : {}), @@ -1091,16 +933,10 @@ function generateNewAndRecoveredAlertEvents< InstanceContext extends AlertInstanceContext >(params: GenerateNewAndRecoveredAlertEventsParams) { const { - eventLogger, - executionId, - ruleId, - namespace, + alertingEventLogger, currentAlerts, originalAlerts, recoveredAlerts, - rule, - ruleType, - spaceId, ruleRunMetricsStore, } = params; const originalAlertIds = Object.keys(originalAlerts); @@ -1123,14 +959,15 @@ function generateNewAndRecoveredAlertEvents< recoveredAlerts[id].getLastScheduledActions() ?? {}; const state = recoveredAlerts[id].getState(); const message = `${params.ruleLabel} alert '${id}' has recovered`; - logAlertEvent( + + alertingEventLogger.logAlert({ + action: EVENT_LOG_ACTIONS.recoveredInstance, id, - EVENT_LOG_ACTIONS.recoveredInstance, + group: actionGroup, + subgroup: actionSubgroup, message, state, - actionGroup, - actionSubgroup - ); + }); } for (const id of newIds) { @@ -1138,7 +975,14 @@ function generateNewAndRecoveredAlertEvents< currentAlerts[id].getScheduledActionOptions() ?? {}; const state = currentAlerts[id].getState(); const message = `${params.ruleLabel} created new alert: '${id}'`; - logAlertEvent(id, EVENT_LOG_ACTIONS.newInstance, message, state, actionGroup, actionSubgroup); + alertingEventLogger.logAlert({ + action: EVENT_LOG_ACTIONS.newInstance, + id, + group: actionGroup, + subgroup: actionSubgroup, + message, + state, + }); } for (const id of currentAlertIds) { @@ -1150,69 +994,14 @@ function generateNewAndRecoveredAlertEvents< ? `actionGroup(subgroup): '${actionGroup}(${actionSubgroup})'` : `actionGroup: '${actionGroup}'` }`; - logAlertEvent( + alertingEventLogger.logAlert({ + action: EVENT_LOG_ACTIONS.activeInstance, id, - EVENT_LOG_ACTIONS.activeInstance, + group: actionGroup, + subgroup: actionSubgroup, message, state, - actionGroup, - actionSubgroup - ); - } - - function logAlertEvent( - alertId: string, - action: string, - message: string, - state: InstanceState, - group?: string, - subgroup?: string - ) { - const event: IEvent = { - event: { - action, - kind: 'alert', - category: [ruleType.producer], - ...(state?.start ? { start: state.start as string } : {}), - ...(state?.end ? { end: state.end as string } : {}), - ...(state?.duration !== undefined ? { duration: state.duration as number } : {}), - }, - kibana: { - alert: { - rule: { - consumer: rule.consumer, - execution: { - uuid: executionId, - }, - rule_type_id: ruleType.id, - }, - }, - alerting: { - instance_id: alertId, - ...(group ? { action_group_id: group } : {}), - ...(subgroup ? { action_subgroup: subgroup } : {}), - }, - saved_objects: [ - { - rel: SAVED_OBJECT_REL_PRIMARY, - type: 'alert', - id: ruleId, - type_id: ruleType.id, - namespace, - }, - ], - space_ids: [spaceId], - }, - message, - rule: { - id: rule.id, - license: ruleType.minimumLicenseRequired, - category: ruleType.id, - ruleset: ruleType.producer, - name: rule.name, - }, - }; - eventLogger.logEvent(event); + }); } } diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts index 4986359115377..fb2d1be3a3872 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts @@ -14,7 +14,7 @@ import { AlertInstanceState, AlertInstanceContext, } from '../types'; -import { ConcreteTaskInstance, TaskStatus } from '@kbn/task-manager-plugin/server'; +import { ConcreteTaskInstance } from '@kbn/task-manager-plugin/server'; import { TaskRunnerContext } from './task_runner_factory'; import { TaskRunner } from './task_runner'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; @@ -32,11 +32,23 @@ import { actionsMock, actionsClientMock } from '@kbn/actions-plugin/server/mocks import { alertsMock, rulesClientMock } from '../mocks'; import { eventLoggerMock } from '@kbn/event-log-plugin/server/event_logger.mock'; import { IEventLogger } from '@kbn/event-log-plugin/server'; -import { Rule, RecoveredActionGroup } from '../../common'; -import { UntypedNormalizedRuleType } from '../rule_type_registry'; import { ruleTypeRegistryMock } from '../rule_type_registry.mock'; import { dataPluginMock } from '@kbn/data-plugin/server/mocks'; import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock'; +import { + AlertingEventLogger, + RuleContextOpts, +} from '../lib/alerting_event_logger/alerting_event_logger'; +import { alertingEventLoggerMock } from '../lib/alerting_event_logger/alerting_event_logger.mock'; +import { + mockTaskInstance, + ruleType, + mockedRuleTypeSavedObject, + generateAlertOpts, + DATE_1970, + generateActionOpts, +} from './fixtures'; +import { EVENT_LOG_ACTIONS } from '../plugin'; jest.mock('uuid', () => ({ v4: () => '5f6aa57d-3e22-484e-bae8-cbed868f4d28', @@ -45,48 +57,29 @@ jest.mock('../lib/wrap_scoped_cluster_client', () => ({ createWrappedScopedClusterClientFactory: jest.fn(), })); -const ruleType: jest.Mocked = { - id: 'test', - name: 'My test rule', - actionGroups: [{ id: 'default', name: 'Default' }, RecoveredActionGroup], - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - isExportable: true, - recoveryActionGroup: RecoveredActionGroup, - executor: jest.fn(), - producer: 'alerts', - cancelAlertsOnRuleTimeout: true, - ruleTaskTimeout: '5m', -}; +jest.mock('../lib/alerting_event_logger/alerting_event_logger'); let fakeTimer: sinon.SinonFakeTimers; const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); +const alertingEventLogger = alertingEventLoggerMock.create(); describe('Task Runner Cancel', () => { let mockedTaskInstance: ConcreteTaskInstance; + let alertingEventLoggerInitializer: RuleContextOpts; beforeAll(() => { fakeTimer = sinon.useFakeTimers(); - mockedTaskInstance = { - id: '', - attempts: 0, - status: TaskStatus.Running, - version: '123', - runAt: new Date(), - schedule: { interval: '10s' }, - scheduledAt: new Date(), - startedAt: new Date(), - retryAt: new Date(Date.now() + 5 * 60 * 1000), - state: {}, - taskType: 'alerting:test', - params: { - alertId: '1', - spaceId: 'default', - consumer: 'bar', - }, - ownerId: null, + mockedTaskInstance = mockTaskInstance(); + + alertingEventLoggerInitializer = { + consumer: mockedTaskInstance.params.consumer, + executionId: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + ruleId: mockedTaskInstance.params.alertId, + ruleType, + spaceId: mockedTaskInstance.params.spaceId, + taskScheduledAt: mockedTaskInstance.scheduledAt, }; }); @@ -136,53 +129,6 @@ describe('Task Runner Cancel', () => { }, }; - const mockDate = new Date('2019-02-12T21:01:22.479Z'); - - const mockedRuleSavedObject: Rule = { - id: '1', - consumer: 'bar', - createdAt: mockDate, - updatedAt: mockDate, - throttle: null, - muteAll: false, - notifyWhen: 'onActiveAlert', - enabled: true, - alertTypeId: ruleType.id, - apiKey: '', - apiKeyOwner: 'elastic', - schedule: { interval: '10s' }, - name: 'rule-name', - tags: ['rule-', '-tags'], - createdBy: 'rule-creator', - updatedBy: 'rule-updater', - mutedInstanceIds: [], - params: { - bar: true, - }, - actions: [ - { - group: 'default', - id: '1', - actionTypeId: 'action', - params: { - foo: true, - }, - }, - { - group: RecoveredActionGroup.id, - id: '2', - actionTypeId: 'action', - params: { - isResolved: true, - }, - }, - ], - executionStatus: { - status: 'unknown', - lastExecutionDate: new Date('2020-08-20T19:23:38Z'), - }, - }; - beforeEach(() => { jest.resetAllMocks(); jest @@ -208,7 +154,7 @@ describe('Task Runner Cancel', () => { taskRunnerFactoryInitializerParams.executionContext.withContext.mockImplementation((ctx, fn) => fn() ); - rulesClient.get.mockResolvedValue(mockedRuleSavedObject); + rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ id: '1', type: 'alert', @@ -221,6 +167,8 @@ describe('Task Runner Cancel', () => { }); taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); + alertingEventLogger.getStartAndDuration.mockImplementation(() => ({ start: new Date() })); + (AlertingEventLogger as jest.Mock).mockImplementation(() => alertingEventLogger); }); test('updates rule saved object execution status and writes to event log entry when task is cancelled mid-execution', async () => { @@ -230,6 +178,7 @@ describe('Task Runner Cancel', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalledTimes(1); const promise = taskRunner.run(); await Promise.resolve(); @@ -242,136 +191,7 @@ describe('Task Runner Cancel', () => { `Aborting any in-progress ES searches for rule type test with id 1` ); - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - // execute-start event, timeout event and then an execute event because rule executors are not cancelling anything yet - expect(eventLogger.logEvent).toHaveBeenCalledTimes(3); - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(1, { - event: { - action: 'execute-start', - category: ['alerts'], - kind: 'alert', - }, - kibana: { - alert: { - rule: { - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - rule_type_id: 'test', - }, - }, - saved_objects: [ - { - id: '1', - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - space_ids: ['default'], - task: { - schedule_delay: 0, - scheduled: '1970-01-01T00:00:00.000Z', - }, - }, - message: 'rule execution start: "1"', - rule: { - category: 'test', - id: '1', - license: 'basic', - ruleset: 'alerts', - }, - }); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(2, { - event: { - action: 'execute-timeout', - category: ['alerts'], - kind: 'alert', - }, - kibana: { - alert: { - rule: { - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - rule_type_id: 'test', - }, - }, - saved_objects: [ - { - id: '1', - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - space_ids: ['default'], - }, - message: `rule: test:1: '' execution cancelled due to timeout - exceeded rule type timeout of 5m`, - rule: { - category: 'test', - id: '1', - license: 'basic', - ruleset: 'alerts', - }, - }); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(3, { - event: { - action: 'execute', - category: ['alerts'], - kind: 'alert', - outcome: 'success', - }, - kibana: { - alert: { - rule: { - consumer: 'bar', - execution: { - metrics: { - number_of_searches: 3, - number_of_triggered_actions: 0, - number_of_generated_actions: 0, - number_of_active_alerts: 0, - number_of_new_alerts: 0, - number_of_recovered_alerts: 0, - total_number_of_alerts: 0, - es_search_duration_ms: 33, - total_search_duration_ms: 23423, - }, - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - rule_type_id: 'test', - }, - }, - alerting: { - status: 'ok', - }, - saved_objects: [ - { - id: '1', - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - space_ids: ['default'], - task: { - schedule_delay: 0, - scheduled: '1970-01-01T00:00:00.000Z', - }, - }, - message: `rule executed: test:1: 'rule-name'`, - rule: { - category: 'test', - id: '1', - license: 'basic', - name: 'rule-name', - ruleset: 'alerts', - }, - }); + testAlertingEventLogCalls({ status: 'ok' }); expect( taskRunnerFactoryInitializerParams.internalSavedObjectsRepository.update @@ -426,22 +246,50 @@ describe('Task Runner Cancel', () => { }, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalledTimes(1); const promise = taskRunner.run(); await Promise.resolve(); await taskRunner.cancel(); await promise; - testActionsExecute(); + testLogger(); + testAlertingEventLogCalls({ + status: 'active', + newAlerts: 1, + activeAlerts: 1, + generatedActions: 1, + triggeredActions: 1, + logAction: 1, + logAlert: 2, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 1, + generateAlertOpts({ + action: EVENT_LOG_ACTIONS.newInstance, + group: 'default', + state: { start: DATE_1970, duration: '0' }, + }) + ); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 2, + generateAlertOpts({ + action: EVENT_LOG_ACTIONS.activeInstance, + group: 'default', + state: { start: DATE_1970, duration: '0' }, + }) + ); + expect(alertingEventLogger.logAction).toHaveBeenNthCalledWith(1, generateActionOpts({})); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); test('actionsPlugin.execute is called if rule execution is cancelled but cancelAlertsOnRuleTimeout for ruleType is false', async () => { - ruleTypeRegistry.get.mockReturnValue({ + const updatedRuleType = { ...ruleType, cancelAlertsOnRuleTimeout: false, - }); + }; + ruleTypeRegistry.get.mockReturnValue(updatedRuleType); ruleType.executor.mockImplementation( async ({ services: executorServices, @@ -457,21 +305,47 @@ describe('Task Runner Cancel', () => { ); // setting cancelAlertsOnRuleTimeout for ruleType to false here const taskRunner = new TaskRunner( - { - ...ruleType, - cancelAlertsOnRuleTimeout: false, - }, + updatedRuleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalledTimes(1); const promise = taskRunner.run(); await Promise.resolve(); await taskRunner.cancel(); await promise; - testActionsExecute(); + testLogger(); + testAlertingEventLogCalls({ + ruleContext: { ...alertingEventLoggerInitializer, ruleType: updatedRuleType }, + status: 'active', + activeAlerts: 1, + generatedActions: 1, + newAlerts: 1, + triggeredActions: 1, + logAlert: 2, + logAction: 1, + }); + + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 1, + generateAlertOpts({ + action: EVENT_LOG_ACTIONS.newInstance, + group: 'default', + state: { start: DATE_1970, duration: '0' }, + }) + ); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 2, + generateAlertOpts({ + action: EVENT_LOG_ACTIONS.activeInstance, + group: 'default', + state: { start: DATE_1970, duration: '0' }, + }) + ); + expect(alertingEventLogger.logAction).toHaveBeenNthCalledWith(1, generateActionOpts({})); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -496,174 +370,15 @@ describe('Task Runner Cancel', () => { taskRunnerFactoryInitializerParams, inMemoryMetrics ); + expect(AlertingEventLogger).toHaveBeenCalledTimes(1); const promise = taskRunner.run(); await Promise.resolve(); await taskRunner.cancel(); await promise; - const logger = taskRunnerFactoryInitializerParams.logger; - expect(logger.debug).toHaveBeenCalledTimes(8); - expect(logger.debug).nthCalledWith(1, 'executing rule test:1 at 1970-01-01T00:00:00.000Z'); - expect(logger.debug).nthCalledWith( - 2, - `Cancelling rule type test with id 1 - execution exceeded rule type timeout of 5m` - ); - expect(logger.debug).nthCalledWith( - 3, - 'Aborting any in-progress ES searches for rule type test with id 1' - ); - expect(logger.debug).nthCalledWith( - 4, - `Updating rule task for test rule with id 1 - execution error due to timeout` - ); - expect(logger.debug).nthCalledWith( - 5, - `rule test:1: 'rule-name' has 1 active alerts: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"}]` - ); - expect(logger.debug).nthCalledWith( - 6, - `no scheduling of actions for rule test:1: 'rule-name': rule execution has been cancelled.` - ); - expect(logger.debug).nthCalledWith( - 7, - 'ruleRunStatus for test:1: {"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' - ); - expect(logger.debug).nthCalledWith( - 8, - 'ruleRunMetrics for test:1: {"numSearches":3,"totalSearchDurationMs":23423,"esSearchDurationMs":33,"numberOfTriggeredActions":0,"numberOfGeneratedActions":0,"numberOfActiveAlerts":0,"numberOfRecoveredAlerts":0,"numberOfNewAlerts":0,"triggeredActionsStatus":"complete"}' - ); - - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent).toHaveBeenCalledTimes(3); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(1, { - event: { - action: 'execute-start', - category: ['alerts'], - kind: 'alert', - }, - kibana: { - alert: { - rule: { - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - rule_type_id: 'test', - }, - }, - task: { - schedule_delay: 0, - scheduled: '1970-01-01T00:00:00.000Z', - }, - saved_objects: [ - { - id: '1', - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - space_ids: ['default'], - }, - message: `rule execution start: \"1\"`, - rule: { - category: 'test', - id: '1', - license: 'basic', - ruleset: 'alerts', - }, - }); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(2, { - event: { - action: 'execute-timeout', - category: ['alerts'], - kind: 'alert', - }, - kibana: { - alert: { - rule: { - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - rule_type_id: 'test', - }, - }, - saved_objects: [ - { - id: '1', - namespace: undefined, - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - space_ids: ['default'], - }, - message: `rule: test:1: '' execution cancelled due to timeout - exceeded rule type timeout of 5m`, - rule: { - category: 'test', - id: '1', - license: 'basic', - ruleset: 'alerts', - }, - }); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(3, { - event: { - action: 'execute', - category: ['alerts'], - kind: 'alert', - outcome: 'success', - }, - kibana: { - alert: { - rule: { - consumer: 'bar', - execution: { - metrics: { - number_of_searches: 3, - number_of_triggered_actions: 0, - number_of_generated_actions: 0, - number_of_active_alerts: 0, - number_of_recovered_alerts: 0, - number_of_new_alerts: 0, - total_number_of_alerts: 0, - es_search_duration_ms: 33, - total_search_duration_ms: 23423, - }, - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - rule_type_id: 'test', - }, - }, - alerting: { - status: 'active', - }, - task: { - schedule_delay: 0, - scheduled: '1970-01-01T00:00:00.000Z', - }, - saved_objects: [ - { - id: '1', - namespace: undefined, - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - space_ids: ['default'], - }, - message: "rule executed: test:1: 'rule-name'", - rule: { - category: 'test', - id: '1', - license: 'basic', - name: 'rule-name', - ruleset: 'alerts', - }, + testAlertingEventLogCalls({ + status: 'active', }); expect(mockUsageCounter.incrementCounter).toHaveBeenCalledTimes(1); @@ -673,7 +388,7 @@ describe('Task Runner Cancel', () => { }); }); - function testActionsExecute() { + function testLogger() { const logger = taskRunnerFactoryInitializerParams.logger; expect(logger.debug).toHaveBeenCalledTimes(7); expect(logger.debug).nthCalledWith(1, 'executing rule test:1 at 1970-01-01T00:00:00.000Z'); @@ -701,256 +416,69 @@ describe('Task Runner Cancel', () => { 7, 'ruleRunMetrics for test:1: {"numSearches":3,"totalSearchDurationMs":23423,"esSearchDurationMs":33,"numberOfTriggeredActions":1,"numberOfGeneratedActions":1,"numberOfActiveAlerts":1,"numberOfRecoveredAlerts":0,"numberOfNewAlerts":1,"triggeredActionsStatus":"complete"}' ); + } - const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(6); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(1, { - event: { - action: 'execute-start', - category: ['alerts'], - kind: 'alert', - }, - kibana: { - alert: { - rule: { - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - rule_type_id: 'test', - }, - }, - task: { - schedule_delay: 0, - scheduled: '1970-01-01T00:00:00.000Z', - }, - saved_objects: [ - { - id: '1', - namespace: undefined, - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - space_ids: ['default'], - }, - message: `rule execution start: "1"`, - rule: { - category: 'test', - id: '1', - license: 'basic', - ruleset: 'alerts', - }, - }); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(2, { - event: { - action: 'execute-timeout', - category: ['alerts'], - kind: 'alert', - }, - kibana: { - alert: { - rule: { - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - rule_type_id: 'test', - }, - }, - saved_objects: [ - { - id: '1', - namespace: undefined, - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - space_ids: ['default'], - }, - message: `rule: test:1: '' execution cancelled due to timeout - exceeded rule type timeout of 5m`, - rule: { - category: 'test', - id: '1', - license: 'basic', - ruleset: 'alerts', - }, - }); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(3, { - event: { - action: 'new-instance', - category: ['alerts'], - kind: 'alert', - duration: 0, - start: '1970-01-01T00:00:00.000Z', - }, - kibana: { - alert: { - rule: { - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - rule_type_id: 'test', - }, - }, - alerting: { - action_group_id: 'default', - instance_id: '1', - }, - saved_objects: [ - { - id: '1', - namespace: undefined, - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - space_ids: ['default'], - }, - message: "test:1: 'rule-name' created new alert: '1'", - rule: { - category: 'test', - id: '1', - license: 'basic', - name: 'rule-name', - namespace: undefined, - ruleset: 'alerts', - }, - }); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(4, { - event: { - action: 'active-instance', - category: ['alerts'], - duration: 0, - kind: 'alert', - start: '1970-01-01T00:00:00.000Z', - }, - kibana: { - alert: { - rule: { - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - rule_type_id: 'test', - }, - }, - alerting: { - action_group_id: 'default', - instance_id: '1', - }, - saved_objects: [ - { id: '1', namespace: undefined, rel: 'primary', type: 'alert', type_id: 'test' }, - ], - space_ids: ['default'], - }, - message: "test:1: 'rule-name' active alert: '1' in actionGroup: 'default'", - rule: { - category: 'test', - id: '1', - license: 'basic', - name: 'rule-name', - ruleset: 'alerts', - }, - }); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(5, { - event: { - action: 'execute-action', - category: ['alerts'], - kind: 'alert', - }, - kibana: { - alert: { - rule: { - consumer: 'bar', - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - rule_type_id: 'test', - }, - }, - alerting: { - instance_id: '1', - action_group_id: 'default', - }, - saved_objects: [ - { - id: '1', - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - { - id: '1', - type: 'action', - type_id: 'action', - }, - ], - space_ids: ['default'], - }, - message: - "alert: test:1: 'rule-name' instanceId: '1' scheduled actionGroup: 'default' action: action:1", - rule: { - category: 'test', - id: '1', - license: 'basic', - name: 'rule-name', - ruleset: 'alerts', - }, - }); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(6, { - event: { action: 'execute', category: ['alerts'], kind: 'alert', outcome: 'success' }, - kibana: { - alert: { - rule: { - consumer: 'bar', - execution: { - metrics: { - number_of_searches: 3, - number_of_triggered_actions: 1, - number_of_generated_actions: 1, - number_of_active_alerts: 1, - number_of_new_alerts: 1, - number_of_recovered_alerts: 0, - total_number_of_alerts: 1, - es_search_duration_ms: 33, - total_search_duration_ms: 23423, - }, - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - rule_type_id: 'test', - }, - }, - alerting: { - status: 'active', - }, - task: { - schedule_delay: 0, - scheduled: '1970-01-01T00:00:00.000Z', - }, - saved_objects: [ - { - id: '1', - namespace: undefined, - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - space_ids: ['default'], - }, - message: "rule executed: test:1: 'rule-name'", - rule: { - category: 'test', - id: '1', - license: 'basic', - name: 'rule-name', - ruleset: 'alerts', + function testAlertingEventLogCalls({ + ruleContext = alertingEventLoggerInitializer, + activeAlerts = 0, + newAlerts = 0, + recoveredAlerts = 0, + triggeredActions = 0, + generatedActions = 0, + status, + logAlert = 0, + logAction = 0, + }: { + status: string; + ruleContext?: RuleContextOpts; + activeAlerts?: number; + newAlerts?: number; + recoveredAlerts?: number; + triggeredActions?: number; + generatedActions?: number; + setRuleName?: boolean; + logAlert?: number; + logAction?: number; + }) { + expect(alertingEventLogger.initialize).toHaveBeenCalledWith(ruleContext); + expect(alertingEventLogger.start).toHaveBeenCalled(); + expect(alertingEventLogger.setRuleName).toHaveBeenCalledWith(mockedRuleTypeSavedObject.name); + expect(alertingEventLogger.getStartAndDuration).toHaveBeenCalled(); + + expect(alertingEventLogger.done).toHaveBeenCalledWith({ + metrics: { + esSearchDurationMs: 33, + numSearches: 3, + numberOfActiveAlerts: activeAlerts, + numberOfGeneratedActions: generatedActions, + numberOfNewAlerts: newAlerts, + numberOfRecoveredAlerts: recoveredAlerts, + numberOfTriggeredActions: triggeredActions, + totalSearchDurationMs: 23423, + triggeredActionsStatus: 'complete', + }, + status: { + lastExecutionDate: new Date('1970-01-01T00:00:00.000Z'), + status, }, }); + + expect(alertingEventLogger.setExecutionSucceeded).toHaveBeenCalledWith( + `rule executed: test:1: 'rule-name'` + ); + expect(alertingEventLogger.setExecutionFailed).not.toHaveBeenCalled(); + + if (logAlert > 0) { + expect(alertingEventLogger.logAlert).toHaveBeenCalledTimes(logAlert); + } else { + expect(alertingEventLogger.logAlert).not.toHaveBeenCalled(); + } + + if (logAction > 0) { + expect(alertingEventLogger.logAction).toHaveBeenCalledTimes(logAction); + } else { + expect(alertingEventLogger.logAction).not.toHaveBeenCalled(); + } + expect(alertingEventLogger.logTimeout).toHaveBeenCalled(); } }); diff --git a/x-pack/plugins/alerting/server/task_runner/types.ts b/x-pack/plugins/alerting/server/task_runner/types.ts index 1f4a31fa1d9ac..d3c6038474a38 100644 --- a/x-pack/plugins/alerting/server/task_runner/types.ts +++ b/x-pack/plugins/alerting/server/task_runner/types.ts @@ -8,8 +8,8 @@ import { Dictionary } from 'lodash'; import { KibanaRequest, Logger } from '@kbn/core/server'; import { ConcreteTaskInstance } from '@kbn/task-manager-plugin/server'; -import { IEventLogger } from '@kbn/event-log-plugin/server'; import { PluginStartContract as ActionsPluginStartContract } from '@kbn/actions-plugin/server'; +import { PublicMethodsOf } from '@kbn/utility-types'; import { ActionGroup, RuleAction, @@ -20,7 +20,6 @@ import { IntervalSchedule, RuleMonitoring, RuleTaskState, - SanitizedRule, } from '../../common'; import { Alert } from '../alert'; import { NormalizedRuleType } from '../rule_type_registry'; @@ -28,6 +27,7 @@ import { ExecutionHandler } from './create_execution_handler'; import { RawRule } from '../types'; import { ActionsConfigMap } from '../lib/get_actions_config_map'; import { RuleRunMetrics, RuleRunMetricsStore } from '../lib/rule_run_metrics_store'; +import { AlertingEventLogger } from '../lib/alerting_event_logger/alerting_event_logger'; export interface RuleTaskRunResult { state: RuleTaskState; @@ -61,29 +61,11 @@ export interface GenerateNewAndRecoveredAlertEventsParams< InstanceState extends AlertInstanceState, InstanceContext extends AlertInstanceContext > { - eventLogger: IEventLogger; - executionId: string; + alertingEventLogger: AlertingEventLogger; originalAlerts: Dictionary>; currentAlerts: Dictionary>; recoveredAlerts: Dictionary>; - ruleId: string; ruleLabel: string; - namespace: string | undefined; - ruleType: NormalizedRuleType< - RuleTypeParams, - RuleTypeParams, - RuleTypeState, - { - [x: string]: unknown; - }, - { - [x: string]: unknown; - }, - string, - string - >; - rule: SanitizedRule; - spaceId: string; ruleRunMetricsStore: RuleRunMetricsStore; } @@ -145,7 +127,7 @@ export interface CreateExecutionHandlerOptions< RecoveryActionGroupId >; logger: Logger; - eventLogger: IEventLogger; + alertingEventLogger: PublicMethodsOf; request: KibanaRequest; ruleParams: RuleTypeParams; supportsEphemeralTasks: boolean; diff --git a/x-pack/plugins/apm/common/environment_filter_values.ts b/x-pack/plugins/apm/common/environment_filter_values.ts index ddd1ffd9b8d45..df53b08fcf56f 100644 --- a/x-pack/plugins/apm/common/environment_filter_values.ts +++ b/x-pack/plugins/apm/common/environment_filter_values.ts @@ -33,13 +33,6 @@ export function getEnvironmentLabel(environment: string) { return environment; } -// #TODO Once we replace the select dropdown we can remove it -// EuiSelect > EuiSelectOption accepts text attribute -export const ENVIRONMENT_ALL_SELECT_OPTION = { - value: ENVIRONMENT_ALL_VALUE, - text: getEnvironmentLabel(ENVIRONMENT_ALL_VALUE), -}; - export const ENVIRONMENT_ALL = { value: ENVIRONMENT_ALL_VALUE, label: getEnvironmentLabel(ENVIRONMENT_ALL_VALUE), @@ -47,7 +40,7 @@ export const ENVIRONMENT_ALL = { export const ENVIRONMENT_NOT_DEFINED = { value: ENVIRONMENT_NOT_DEFINED_VALUE, - text: getEnvironmentLabel(ENVIRONMENT_NOT_DEFINED_VALUE), + label: getEnvironmentLabel(ENVIRONMENT_NOT_DEFINED_VALUE), }; export function getEnvironmentEsField(environment: string) { diff --git a/x-pack/plugins/apm/common/service_groups.ts b/x-pack/plugins/apm/common/service_groups.ts index d56acc846dc1b..1fa6e03f43719 100644 --- a/x-pack/plugins/apm/common/service_groups.ts +++ b/x-pack/plugins/apm/common/service_groups.ts @@ -7,7 +7,7 @@ export const APM_SERVICE_GROUP_SAVED_OBJECT_TYPE = 'apm-service-group'; export const SERVICE_GROUP_COLOR_DEFAULT = '#D1DAE7'; -export const MAX_NUMBER_OF_SERVICES_IN_GROUP = 500; +export const MAX_NUMBER_OF_SERVICE_GROUPS = 500; export interface ServiceGroup { groupName: string; diff --git a/x-pack/plugins/apm/dev_docs/testing.md b/x-pack/plugins/apm/dev_docs/testing.md index 6c35979add784..e1819875f58d8 100644 --- a/x-pack/plugins/apm/dev_docs/testing.md +++ b/x-pack/plugins/apm/dev_docs/testing.md @@ -96,13 +96,13 @@ TODO: We could try moving this tests to the new e2e tests located at `x-pack/plu **Start server** ``` -node scripts/functional_tests_server --config x-pack/test/functional/config.js +node scripts/functional_tests_server --config x-pack/test/functional/config.base.js ``` **Run tests** ``` -node scripts/functional_test_runner --config x-pack/test/functional/config.js --grep='APM specs' +node scripts/functional_test_runner --config x-pack/test/functional/config.base.js --grep='APM specs' ``` APM tests are located in `x-pack/test/functional/apps/apm`. diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_inventory/service_inventory.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_inventory/service_inventory.spec.ts index 0dab9c6f2440f..216a9c87d6db8 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_inventory/service_inventory.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_inventory/service_inventory.spec.ts @@ -20,7 +20,7 @@ const serviceInventoryHref = url.format({ query: timeRange, }); -const apiRequestsToIntercept = [ +const mainApiRequestsToIntercept = [ { endpoint: '/internal/apm/services?*', aliasName: 'servicesRequest', @@ -31,7 +31,14 @@ const apiRequestsToIntercept = [ }, ]; -const aliasNames = apiRequestsToIntercept.map( +const secondaryApiRequestsToIntercept = [ + { + endpoint: 'internal/apm/suggestions?*', + aliasName: 'suggestionsRequest', + }, +]; + +const mainAliasNames = mainApiRequestsToIntercept.map( ({ aliasName }) => `@${aliasName}` ); @@ -77,43 +84,51 @@ describe('When navigating to the service inventory', () => { describe.skip('Calls APIs', () => { beforeEach(() => { - apiRequestsToIntercept.map(({ endpoint, aliasName }) => { - cy.intercept('GET', endpoint).as(aliasName); - }); + [...mainApiRequestsToIntercept, ...secondaryApiRequestsToIntercept].map( + ({ endpoint, aliasName }) => { + cy.intercept('GET', endpoint).as(aliasName); + } + ); cy.loginAsReadOnlyUser(); cy.visit(serviceInventoryHref); }); it('with the correct environment when changing the environment', () => { - cy.wait(aliasNames); + cy.wait(mainAliasNames); + cy.get('[data-test-subj="environmentFilter"]').type('pro'); + + cy.expectAPIsToHaveBeenCalledWith({ + apisIntercepted: ['@suggestionsRequest'], + value: 'fieldValue=pro', + }); - cy.get('[data-test-subj="environmentFilter"]').select('production'); + cy.contains('button', 'production').click(); cy.expectAPIsToHaveBeenCalledWith({ - apisIntercepted: aliasNames, + apisIntercepted: mainAliasNames, value: 'environment=production', }); }); it('when clicking the refresh button', () => { - cy.wait(aliasNames); + cy.wait(mainAliasNames); cy.contains('Refresh').click(); - cy.wait(aliasNames); + cy.wait(mainAliasNames); }); it('when selecting a different time range and clicking the update button', () => { - cy.wait(aliasNames); + cy.wait(mainAliasNames); cy.selectAbsoluteTimeRange( moment(timeRange.rangeFrom).subtract(5, 'm').toISOString(), moment(timeRange.rangeTo).subtract(5, 'm').toISOString() ); cy.contains('Update').click(); - cy.wait(aliasNames); + cy.wait(mainAliasNames); cy.contains('Refresh').click(); - cy.wait(aliasNames); + cy.wait(mainAliasNames); }); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/service_overview.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/service_overview.spec.ts index 48fe14b44c793..caeac36b7cba8 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/service_overview.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/service_overview.spec.ts @@ -216,7 +216,22 @@ describe('Service Overview', () => { it('with the correct environment when changing the environment', () => { cy.wait(aliasNames, { requestTimeout: 10000 }); - cy.get('[data-test-subj="environmentFilter"]').select('production'); + cy.intercept('GET', 'internal/apm/suggestions?*').as( + 'suggestionsRequest' + ); + + cy.get('[data-test-subj="environmentFilter"]').type('production'); + + cy.expectAPIsToHaveBeenCalledWith({ + apisIntercepted: ['@suggestionsRequest'], + value: 'fieldValue=production', + }); + + cy.get( + '[data-test-subj="comboBoxOptionsList environmentFilter-optionsList"]' + ) + .contains('production') + .click({ force: true }); cy.expectAPIsToHaveBeenCalledWith({ apisIntercepted: aliasNames, diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/tasks/es_archiver.ts b/x-pack/plugins/apm/ftr_e2e/cypress/tasks/es_archiver.ts index 383a6fa68cf8a..2d9b530ed76a9 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/tasks/es_archiver.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/tasks/es_archiver.ts @@ -19,7 +19,7 @@ const NODE_TLS_REJECT_UNAUTHORIZED = '1'; export const esArchiverLoad = (archiveName: string) => { const archivePath = path.join(ES_ARCHIVE_DIR, archiveName); execSync( - `node ../../../../scripts/es_archiver load "${archivePath}" --config ../../../test/functional/config.js`, + `node ../../../../scripts/es_archiver load "${archivePath}" --config ../../../test/functional/config.base.js`, { env: { ...process.env, NODE_TLS_REJECT_UNAUTHORIZED }, stdio: 'inherit' } ); }; @@ -27,14 +27,14 @@ export const esArchiverLoad = (archiveName: string) => { export const esArchiverUnload = (archiveName: string) => { const archivePath = path.join(ES_ARCHIVE_DIR, archiveName); execSync( - `node ../../../../scripts/es_archiver unload "${archivePath}" --config ../../../test/functional/config.js`, + `node ../../../../scripts/es_archiver unload "${archivePath}" --config ../../../test/functional/config.base.js`, { env: { ...process.env, NODE_TLS_REJECT_UNAUTHORIZED }, stdio: 'inherit' } ); }; export const esArchiverResetKibana = () => { execSync( - `node ../../../../scripts/es_archiver empty-kibana-index --config ../../../test/functional/config.js`, + `node ../../../../scripts/es_archiver empty-kibana-index --config ../../../test/functional/config.base.js`, { env: { ...process.env, NODE_TLS_REJECT_UNAUTHORIZED }, stdio: 'inherit' } ); }; diff --git a/x-pack/plugins/apm/ftr_e2e/ftr_config.ts b/x-pack/plugins/apm/ftr_e2e/ftr_config.ts index ec2e8d05a97dd..dd2166508d134 100644 --- a/x-pack/plugins/apm/ftr_e2e/ftr_config.ts +++ b/x-pack/plugins/apm/ftr_e2e/ftr_config.ts @@ -13,7 +13,7 @@ async function config({ readConfigFile }: FtrConfigProviderContext) { require.resolve('../../../../test/common/config.js') ); const xpackFunctionalTestsConfig = await readConfigFile( - require.resolve('../../../test/functional/config.js') + require.resolve('../../../test/functional/config.base.js') ); return { diff --git a/x-pack/plugins/apm/public/components/alerting/fields.tsx b/x-pack/plugins/apm/public/components/alerting/fields.tsx index 9ea3c5aa2a6bd..07373bdba9a21 100644 --- a/x-pack/plugins/apm/public/components/alerting/fields.tsx +++ b/x-pack/plugins/apm/public/components/alerting/fields.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import moment from 'moment'; import { EuiFieldNumber } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; @@ -38,16 +38,21 @@ export function ServiceField({ })} > ); @@ -68,16 +73,21 @@ export function EnvironmentField({ })} > ); @@ -96,12 +106,15 @@ export function TransactionTypeField({ return ( ); diff --git a/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/form_row_suggestions_select.tsx b/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/form_row_suggestions_select.tsx index 5fa3a46b00901..2fb481b26be2e 100644 --- a/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/form_row_suggestions_select.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/form_row_suggestions_select.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import moment from 'moment'; import { EuiDescribedFormGroup, EuiFormRow } from '@elastic/eui'; import React from 'react'; import { i18n } from '@kbn/i18n'; @@ -13,7 +13,7 @@ import { ENVIRONMENT_ALL } from '../../../../../../../common/environment_filter_ interface Props { title: string; - field: string; + fieldName: string; description: string; fieldLabel: string; value?: string; @@ -24,7 +24,7 @@ interface Props { export function FormRowSuggestionsSelect({ title, - field, + fieldName, description, fieldLabel, value, @@ -40,9 +40,9 @@ export function FormRowSuggestionsSelect({ > diff --git a/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/service_page.tsx b/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/service_page.tsx index 1ede5cd5405c7..50872375027dc 100644 --- a/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/service_page.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/agent_configurations/agent_configuration_create_edit/service_page/service_page.tsx @@ -97,7 +97,7 @@ export function ServicePage({ newConfig, setNewConfig, onClickNext }: Props) { 'xpack.apm.agentConfig.servicePage.service.fieldLabel', { defaultMessage: 'Service name' } )} - field={SERVICE_NAME} + fieldName={SERVICE_NAME} value={newConfig.service.name} onChange={(name) => { setNewConfig((prev) => ({ diff --git a/x-pack/plugins/apm/public/components/app/settings/custom_link/create_edit_custom_link_flyout/filters_section.tsx b/x-pack/plugins/apm/public/components/app/settings/custom_link/create_edit_custom_link_flyout/filters_section.tsx index 3b1438b4dddb0..60f6003ceeeb4 100644 --- a/x-pack/plugins/apm/public/components/app/settings/custom_link/create_edit_custom_link_flyout/filters_section.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/custom_link/create_edit_custom_link_flyout/filters_section.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import moment from 'moment'; import { EuiButtonEmpty, EuiFlexGroup, @@ -119,7 +119,7 @@ export function FiltersSection({ diff --git a/x-pack/plugins/apm/public/components/app/settings/custom_link/empty_prompt.tsx b/x-pack/plugins/apm/public/components/app/settings/custom_link/empty_prompt.tsx index dd9cd760d70cf..fd7a3d2587190 100644 --- a/x-pack/plugins/apm/public/components/app/settings/custom_link/empty_prompt.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/custom_link/empty_prompt.tsx @@ -5,16 +5,19 @@ * 2.0. */ -import { EuiEmptyPrompt } from '@elastic/eui'; +import { EuiEmptyPrompt, EuiLink, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; import { CreateCustomLinkButton } from './create_custom_link_button'; +import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; export function EmptyPrompt({ onCreateCustomLinkClick, }: { onCreateCustomLinkClick: () => void; }) { + const { docLinks } = useApmPluginContext().core; return ( -

- {i18n.translate('xpack.apm.settings.customLink.emptyPromptText', { - defaultMessage: - "Let's change that! You can add custom links to the Actions context menu by the transaction details for each service. Create a helpful link to your company's support portal or open a new bug report. Learn more about it in our docs.", - })} -

+ + + {i18n.translate( + 'xpack.apm.settings.customLink.emptyPromptText.customLinkDocLinkText', + { defaultMessage: 'docs' } + )} + + ), + }} + /> + } actions={} diff --git a/x-pack/plugins/apm/public/components/app/settings/custom_link/index.test.tsx b/x-pack/plugins/apm/public/components/app/settings/custom_link/index.test.tsx index e9979746426a3..40f8f5ad1db25 100644 --- a/x-pack/plugins/apm/public/components/app/settings/custom_link/index.test.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/custom_link/index.test.tsx @@ -23,6 +23,7 @@ import { expectTextsNotInDocument, } from '../../../../utils/test_helpers'; import * as saveCustomLink from './create_edit_custom_link_flyout/save_custom_link'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; const data = { customLinks: [ @@ -73,9 +74,11 @@ describe('CustomLink', () => { it('shows when no link is available', () => { const component = render( - - - + + + + + ); expectTextsInDocument(component, ['No links found.']); @@ -360,9 +363,11 @@ describe('CustomLink', () => { }); const component = render( - - - + + + + + ); expectTextsNotInDocument(component, ['Start free 30-day trial']); @@ -375,9 +380,11 @@ describe('CustomLink', () => { const { getByTestId } = render( - - - + + + + + ); const createButton = getByTestId('createButton') as HTMLButtonElement; @@ -389,9 +396,11 @@ describe('CustomLink', () => { const { queryAllByText } = render( - - - + + + + + ); diff --git a/x-pack/plugins/apm/public/components/app/settings/schema/schema_overview.tsx b/x-pack/plugins/apm/public/components/app/settings/schema/schema_overview.tsx index 1e31eb8ccbe67..1f17b9f63c0a0 100644 --- a/x-pack/plugins/apm/public/components/app/settings/schema/schema_overview.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/schema/schema_overview.tsx @@ -11,6 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon, + EuiLink, EuiLoadingSpinner, EuiSpacer, EuiText, @@ -21,11 +22,11 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; import semverLt from 'semver/functions/lt'; import { PackagePolicy } from '@kbn/fleet-plugin/common/types'; -import { ElasticDocsLink } from '../../../shared/links/elastic_docs_link'; import rocketLaunchGraphic from './blog_rocket_720x420.png'; import { MigrationInProgressPanel } from './migration_in_progress_panel'; import { UpgradeAvailableCard } from './migrated/upgrade_available_card'; import { SuccessfulMigrationCard } from './migrated/successful_migration_card'; +import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; interface Props { onSwitch: () => void; @@ -184,6 +185,7 @@ export function SchemaOverview({ } export function SchemaOverviewHeading() { + const { docLinks } = useApmPluginContext().core; return ( <> @@ -208,16 +210,12 @@ export function SchemaOverviewHeading() { ), elasticAgentDocLink: ( - + {i18n.translate( 'xpack.apm.settings.schema.descriptionText.elasticAgentDocLinkText', { defaultMessage: 'Elastic Agent' } )} - + ), }} /> diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_agents/agent_instructions_accordion.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_agents/agent_instructions_accordion.tsx index 238ffc760d93f..a9ec9778ed3e6 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_agents/agent_instructions_accordion.tsx +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_agents/agent_instructions_accordion.tsx @@ -13,7 +13,6 @@ import { EuiText, EuiCodeBlock, EuiTabbedContent, - EuiBetaBadge, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { ComponentType } from 'react'; @@ -32,6 +31,7 @@ import type { } from '../apm_policy_form/typings'; import { getCommands } from '../../../tutorial/config_agent/commands/get_commands'; import { renderMustache } from './render_mustache'; +import { TechnicalPreviewBadge } from '../../shared/technical_preview_badge'; function AccordionButtonContent({ agentName, @@ -240,19 +240,7 @@ export function AgentInstructionsAccordion({ )}
- + ), diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/edit_apm_policy_form.stories.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/edit_apm_policy_form.stories.tsx new file mode 100644 index 0000000000000..e4e6f7062ffdb --- /dev/null +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/edit_apm_policy_form.stories.tsx @@ -0,0 +1,272 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { Meta, Story } from '@storybook/react'; +import { CoreStart } from '@kbn/core/public'; +import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; +import { EditAPMPolicyForm } from './edit_apm_policy_form'; +import { NewPackagePolicy, PackagePolicy } from './typings'; + +const coreMock = { + http: { get: async () => ({}) }, + notifications: { toasts: { add: () => {} } }, + uiSettings: { get: () => {} }, +} as unknown as CoreStart; + +const KibanaReactContext = createKibanaReactContext(coreMock); + +const stories: Meta<{}> = { + title: 'fleet/Edit APM policy', + component: EditAPMPolicyForm, + decorators: [ + (StoryComponent) => { + return ( +
+ + + +
+ ); + }, + ], +}; +export default stories; + +export const EditAPMPolicy: Story = () => { + const [newPolicy, setNewPolicy] = useState(policy); + const [isPolicyValid, setIsPolicyValid] = useState(true); + + return ( + <> +
+
+          {`Is Policy valid: ${isPolicyValid} (when false, "Save integration" button is disabled)`}
+        
+
+ { + setIsPolicyValid(value.isValid); + const updatedVars = value.updatedPolicy.inputs?.[0].vars; + setNewPolicy((state) => ({ + ...state, + inputs: [{ ...state.inputs[0], vars: updatedVars }], + })); + }} + /> +
+
+
{JSON.stringify(newPolicy, null, 4)}
+ + ); +}; + +const policy = { + version: 'WzM2OTksMl0=', + name: 'Elastic APM', + namespace: 'default', + enabled: true, + policy_id: 'policy-elastic-agent-on-cloud', + output_id: '', + package: { + name: 'apm', + version: '8.3.0', + title: 'Elastic APM', + }, + elasticsearch: { + privileges: { + cluster: ['cluster:monitor/main'], + }, + }, + inputs: [ + { + type: 'apm', + enabled: true, + config: { + 'apm-server': { + value: { + rum: { + source_mapping: { + metadata: [], + }, + }, + agent_config: [], + }, + }, + }, + streams: [], + vars: { + host: { + type: 'text', + value: '0.0.0.0:8200', + }, + url: { + type: 'text', + value: 'cloud_apm_url_test', + }, + secret_token: { + type: 'text', + value: 'asdfkjhasdf', + }, + api_key_enabled: { + type: 'bool', + value: true, + }, + enable_rum: { + type: 'bool', + value: true, + }, + anonymous_enabled: { + type: 'bool', + value: true, + }, + anonymous_allow_agent: { + type: 'text', + value: ['rum-js', 'js-base', 'iOS/swift'], + }, + anonymous_allow_service: { + type: 'text', + value: '', + }, + anonymous_rate_limit_event_limit: { + type: 'integer', + value: 300, + }, + anonymous_rate_limit_ip_limit: { + type: 'integer', + value: 1000, + }, + default_service_environment: { + type: 'text', + value: '', + }, + rum_allow_origins: { + type: 'text', + value: ['"*"'], + }, + rum_allow_headers: { + type: 'text', + value: '', + }, + rum_response_headers: { + type: 'yaml', + value: '', + }, + rum_library_pattern: { + type: 'text', + value: '"node_modules|bower_components|~"', + }, + rum_exclude_from_grouping: { + type: 'text', + value: '"^/webpack"', + }, + api_key_limit: { + type: 'integer', + value: 100, + }, + max_event_bytes: { + type: 'integer', + value: 307200, + }, + capture_personal_data: { + type: 'bool', + value: true, + }, + max_header_bytes: { + type: 'integer', + value: 1048576, + }, + idle_timeout: { + type: 'text', + value: '45s', + }, + read_timeout: { + type: 'text', + value: '3600s', + }, + shutdown_timeout: { + type: 'text', + value: '30s', + }, + write_timeout: { + type: 'text', + value: '30s', + }, + max_connections: { + type: 'integer', + value: 0, + }, + response_headers: { + type: 'yaml', + value: '', + }, + expvar_enabled: { + type: 'bool', + value: false, + }, + java_attacher_discovery_rules: { + type: 'yaml', + value: '', + }, + java_attacher_agent_version: { + type: 'text', + value: '', + }, + java_attacher_enabled: { + type: 'bool', + value: false, + }, + tls_enabled: { + type: 'bool', + value: false, + }, + tls_certificate: { + type: 'text', + value: '', + }, + tls_key: { + type: 'text', + value: '', + }, + tls_supported_protocols: { + type: 'text', + value: ['TLSv1.0', 'TLSv1.1', 'TLSv1.2'], + }, + tls_cipher_suites: { + type: 'text', + value: '', + }, + tls_curve_types: { + type: 'text', + value: '', + }, + tail_sampling_policies: { + type: 'yaml', + value: '- sample_rate: 0.1\n', + }, + tail_sampling_interval: { + type: 'text', + value: '1m', + }, + tail_sampling_enabled: { + type: 'bool', + value: false, + }, + }, + }, + ], +} as NewPackagePolicy; diff --git a/x-pack/plugins/apm/public/components/routing/apm_route_config.tsx b/x-pack/plugins/apm/public/components/routing/apm_route_config.tsx index 0ff0b18818301..ad6947c736fe2 100644 --- a/x-pack/plugins/apm/public/components/routing/apm_route_config.tsx +++ b/x-pack/plugins/apm/public/components/routing/apm_route_config.tsx @@ -21,13 +21,9 @@ import { ServiceGroupsList } from '../app/service_groups'; import { ServiceGroupsRedirect } from './service_groups_redirect'; import { offsetRt } from '../../../common/offset_rt'; -const ServiceGroupsBreadcrumnbLabel = i18n.translate( - 'xpack.apm.views.serviceGroups.breadcrumbLabel', - { defaultMessage: 'Services' } -); const ServiceGroupsTitle = i18n.translate( 'xpack.apm.views.serviceGroups.title', - { defaultMessage: 'Service groups' } + { defaultMessage: 'Services' } ); /** @@ -77,10 +73,7 @@ const apmRoutes = { // this route fails on navigation unless it's defined before home '/service-groups': { element: ( - + diff --git a/x-pack/plugins/apm/public/components/shared/backend_link.tsx b/x-pack/plugins/apm/public/components/shared/backend_link.tsx index 6dde4e269ec54..ac93228c76bcd 100644 --- a/x-pack/plugins/apm/public/components/shared/backend_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/backend_link.tsx @@ -42,7 +42,9 @@ export function BackendLink({ - {query.backendName} + + {query.backendName} + ); diff --git a/x-pack/plugins/apm/public/components/shared/environment_filter/index.tsx b/x-pack/plugins/apm/public/components/shared/environment_filter/index.tsx index 9a3d677b3f078..80217273c62d3 100644 --- a/x-pack/plugins/apm/public/components/shared/environment_filter/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/environment_filter/index.tsx @@ -5,18 +5,11 @@ * 2.0. */ -import { EuiSelect } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; import { History } from 'history'; import React from 'react'; import { useHistory, useLocation } from 'react-router-dom'; -import { - ENVIRONMENT_ALL_SELECT_OPTION, - ENVIRONMENT_NOT_DEFINED, -} from '../../../../common/environment_filter_values'; import { fromQuery, toQuery } from '../links/url_helpers'; -import { FETCH_STATUS } from '../../../hooks/use_fetcher'; -import { Environment } from '../../../../common/environment_rt'; +import { EnvironmentSelect } from '../environment_select'; import { useEnvironmentsContext } from '../../../context/environments_context/use_environments_context'; function updateEnvironmentUrl( @@ -34,77 +27,19 @@ function updateEnvironmentUrl( }); } -const SEPARATOR_OPTION = { - text: `- ${i18n.translate( - 'xpack.apm.filter.environment.selectEnvironmentLabel', - { defaultMessage: 'Select environment' } - )} -`, - disabled: true, -}; - -function getOptions(environments: string[]) { - const environmentOptions = environments - .filter((env) => env !== ENVIRONMENT_NOT_DEFINED.value) - .map((environment) => ({ - value: environment, - text: environment, - })); - - return [ - ENVIRONMENT_ALL_SELECT_OPTION, - ...(environments.includes(ENVIRONMENT_NOT_DEFINED.value) - ? [ENVIRONMENT_NOT_DEFINED] - : []), - ...(environmentOptions.length > 0 ? [SEPARATOR_OPTION] : []), - ...environmentOptions, - ]; -} - export function ApmEnvironmentFilter() { - const { status, environments, environment } = useEnvironmentsContext(); - - return ( - - ); -} - -export function EnvironmentFilter({ - environment, - environments, - status, -}: { - environment: Environment; - environments: Environment[]; - status: FETCH_STATUS; -}) { + const { environment, environments, status } = useEnvironmentsContext(); const history = useHistory(); const location = useLocation(); - // Set the min-width so we don't see as much collapsing of the select during - // the loading state. 200px is what is looks like if "production" is - // the contents. - const minWidth = 200; - - const options = getOptions(environments); - return ( - { - updateEnvironmentUrl(history, location, event.target.value); - }} - isLoading={status === FETCH_STATUS.LOADING} - style={{ minWidth }} - data-test-subj="environmentFilter" + + updateEnvironmentUrl(history, location, changeValue) + } /> ); } diff --git a/x-pack/plugins/apm/public/components/shared/environment_select/index.tsx b/x-pack/plugins/apm/public/components/shared/environment_select/index.tsx new file mode 100644 index 0000000000000..f954d6d693171 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/environment_select/index.tsx @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { isEmpty } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import React, { useMemo, useState } from 'react'; +import { debounce } from 'lodash'; +import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; +import { + getEnvironmentLabel, + ENVIRONMENT_NOT_DEFINED, + ENVIRONMENT_ALL, +} from '../../../../common/environment_filter_values'; +import { SERVICE_ENVIRONMENT } from '../../../../common/elasticsearch_fieldnames'; +import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; +import { useTimeRange } from '../../../hooks/use_time_range'; +import { useApmParams } from '../../../hooks/use_apm_params'; +import { Environment } from '../../../../common/environment_rt'; + +function getEnvironmentOptions(environments: Environment[]) { + const environmentOptions = environments + .filter((env) => env !== ENVIRONMENT_NOT_DEFINED.value) + .map((environment) => ({ + value: environment, + label: environment, + })); + + return [ + ENVIRONMENT_ALL, + ...(environments.includes(ENVIRONMENT_NOT_DEFINED.value) + ? [ENVIRONMENT_NOT_DEFINED] + : []), + ...environmentOptions, + ]; +} + +export function EnvironmentSelect({ + environment, + availableEnvironments, + status, + onChange, +}: { + environment: Environment; + availableEnvironments: Environment[]; + status: FETCH_STATUS; + onChange: (value: string) => void; +}) { + const [searchValue, setSearchValue] = useState(''); + const { + path: { serviceName }, + query: { rangeFrom, rangeTo }, + } = useApmParams('/services/{serviceName}/*'); + + const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + + const selectedOptions: Array> = [ + { + value: environment, + label: getEnvironmentLabel(environment), + }, + ]; + + const onSelect = (changedOptions: Array>) => { + if (changedOptions.length === 1 && changedOptions[0].value) { + onChange(changedOptions[0].value); + } + }; + + const { data, status: searchStatus } = useFetcher( + (callApmApi) => { + return isEmpty(searchValue) + ? Promise.resolve({ terms: [] }) + : callApmApi('GET /internal/apm/suggestions', { + params: { + query: { + fieldName: SERVICE_ENVIRONMENT, + fieldValue: searchValue, + serviceName, + start, + end, + }, + }, + }); + }, + [searchValue, start, end, serviceName] + ); + const terms = data?.terms ?? []; + + const options: Array> = [ + ...(searchValue === '' + ? getEnvironmentOptions(availableEnvironments) + : terms.map((name) => { + return { label: name, value: name }; + })), + ]; + + const onSearch = useMemo(() => debounce(setSearchValue, 300), []); + + return ( + onSelect(changedOptions)} + onSearchChange={onSearch} + isLoading={ + status === FETCH_STATUS.LOADING || searchStatus === FETCH_STATUS.LOADING + } + /> + ); +} diff --git a/x-pack/plugins/apm/public/components/shared/service_link.tsx b/x-pack/plugins/apm/public/components/shared/service_link.tsx index dc4f56ac57053..00a360f3fdebb 100644 --- a/x-pack/plugins/apm/public/components/shared/service_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/service_link.tsx @@ -42,7 +42,9 @@ export function ServiceLink({ - {serviceName} + + {serviceName} + ); diff --git a/x-pack/plugins/apm/public/components/shared/suggestions_select/index.tsx b/x-pack/plugins/apm/public/components/shared/suggestions_select/index.tsx index 8b8907af4bc21..66c8980bf00c6 100644 --- a/x-pack/plugins/apm/public/components/shared/suggestions_select/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/suggestions_select/index.tsx @@ -11,27 +11,33 @@ import React, { useCallback, useState } from 'react'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; interface SuggestionsSelectProps { - allOption?: EuiComboBoxOptionOption; + customOptions?: Array>; customOptionText?: string; defaultValue?: string; - field: string; + fieldName: string; + start: string; + end: string; onChange: (value?: string) => void; isClearable?: boolean; isInvalid?: boolean; placeholder: string; dataTestSubj?: string; + prepend?: string; } export function SuggestionsSelect({ - allOption, + customOptions, customOptionText, defaultValue, - field, + fieldName, + start, + end, onChange, placeholder, isInvalid, dataTestSubj, isClearable = true, + prepend, }: SuggestionsSelectProps) { let defaultOption: EuiComboBoxOptionOption | undefined; @@ -48,11 +54,16 @@ export function SuggestionsSelect({ (callApmApi) => { return callApmApi('GET /internal/apm/suggestions', { params: { - query: { field, string: searchValue }, + query: { + fieldName, + fieldValue: searchValue, + start, + end, + }, }, }); }, - [field, searchValue], + [fieldName, searchValue, start, end], { preservePreviousData: false } ); @@ -85,11 +96,7 @@ export function SuggestionsSelect({ const terms = data?.terms ?? []; const options: Array> = [ - ...(allOption && - (searchValue === '' || - searchValue.toLowerCase() === allOption.label.toLowerCase()) - ? [allOption] - : []), + ...(customOptions ? customOptions : []), ...terms.map((name) => { return { label: name, value: name }; }), @@ -111,6 +118,7 @@ export function SuggestionsSelect({ style={{ minWidth: '256px' }} onCreateOption={handleCreateOption} data-test-subj={dataTestSubj} + prepend={prepend} /> ); } diff --git a/x-pack/plugins/apm/public/components/shared/suggestions_select/suggestions_select.stories.tsx b/x-pack/plugins/apm/public/components/shared/suggestions_select/suggestions_select.stories.tsx index 742ea46cee539..58de938a70e34 100644 --- a/x-pack/plugins/apm/public/components/shared/suggestions_select/suggestions_select.stories.tsx +++ b/x-pack/plugins/apm/public/components/shared/suggestions_select/suggestions_select.stories.tsx @@ -60,11 +60,13 @@ export const Example: Story = ({ }) => { return ( {}} placeholder={placeholder} + start={'2022-04-13T10:29:28.541Z'} + end={'2021-04-13T10:29:28.541Z'} /> ); }; diff --git a/x-pack/plugins/apm/public/components/shared/technical_preview_badge.tsx b/x-pack/plugins/apm/public/components/shared/technical_preview_badge.tsx new file mode 100644 index 0000000000000..7068c9c6fe793 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/technical_preview_badge.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiBetaBadge } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +export function TechnicalPreviewBadge() { + return ( + + ); +} diff --git a/x-pack/plugins/apm/public/context/environments_context/environments_context.tsx b/x-pack/plugins/apm/public/context/environments_context/environments_context.tsx index 6350dd3f0a7e8..41ae58ba04825 100644 --- a/x-pack/plugins/apm/public/context/environments_context/environments_context.tsx +++ b/x-pack/plugins/apm/public/context/environments_context/environments_context.tsx @@ -8,9 +8,9 @@ import React from 'react'; import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; import { Environment } from '../../../common/environment_rt'; import { useApmParams } from '../../hooks/use_apm_params'; -import { useEnvironmentsFetcher } from '../../hooks/use_environments_fetcher'; import { FETCH_STATUS } from '../../hooks/use_fetcher'; import { useTimeRange } from '../../hooks/use_time_range'; +import { useEnvironmentsFetcher } from '../../hooks/use_environments_fetcher'; export const EnvironmentsContext = React.createContext<{ environment: Environment; @@ -48,9 +48,9 @@ export function EnvironmentsContextProvider({ return ( {children} diff --git a/x-pack/plugins/apm/public/hooks/use_environments_fetcher.tsx b/x-pack/plugins/apm/public/hooks/use_environments_fetcher.tsx index edd1b59be144e..50ad012e1aa02 100644 --- a/x-pack/plugins/apm/public/hooks/use_environments_fetcher.tsx +++ b/x-pack/plugins/apm/public/hooks/use_environments_fetcher.tsx @@ -5,23 +5,7 @@ * 2.0. */ -import { useMemo } from 'react'; import { useFetcher } from './use_fetcher'; -import { - ENVIRONMENT_ALL, - ENVIRONMENT_NOT_DEFINED, -} from '../../common/environment_filter_values'; - -function getEnvironmentOptions(environments: string[]) { - const environmentOptions = environments - .filter((env) => env !== ENVIRONMENT_NOT_DEFINED.value) - .map((environment) => ({ - value: environment, - text: environment, - })); - - return [ENVIRONMENT_ALL, ...environmentOptions]; -} const INITIAL_DATA = { environments: [] }; @@ -51,10 +35,5 @@ export function useEnvironmentsFetcher({ [start, end, serviceName] ); - const environmentOptions = useMemo( - () => getEnvironmentOptions(data.environments), - [data?.environments] - ); - - return { environments: data.environments, status, environmentOptions }; + return { environments: data.environments, status }; } diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index 627196a9a4bdc..2ee87571cb719 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -93,6 +93,14 @@ export interface ApmPluginStartDeps { const servicesTitle = i18n.translate('xpack.apm.navigation.servicesTitle', { defaultMessage: 'Services', }); + +const allServicesTitle = i18n.translate( + 'xpack.apm.navigation.allServicesTitle', + { + defaultMessage: 'All services', + } +); + const tracesTitle = i18n.translate('xpack.apm.navigation.tracesTitle', { defaultMessage: 'Traces', }); @@ -270,12 +278,16 @@ export class ApmPlugin implements Plugin { ? [ { id: 'service-groups-list', - title: 'Service groups', + title: servicesTitle, path: '/service-groups', }, ] : []), - { id: 'services', title: servicesTitle, path: '/services' }, + { + id: 'services', + title: serviceGroupsEnabled ? allServicesTitle : servicesTitle, + path: '/services', + }, { id: 'traces', title: tracesTitle, path: '/traces' }, { id: 'service-map', title: serviceMapTitle, path: '/service-map' }, { id: 'backends', title: dependenciesTitle, path: '/backends' }, diff --git a/x-pack/plugins/apm/public/services/rest/create_call_apm_api.ts b/x-pack/plugins/apm/public/services/rest/create_call_apm_api.ts index e9450ec67cc88..4d6ca03ba6a29 100644 --- a/x-pack/plugins/apm/public/services/rest/create_call_apm_api.ts +++ b/x-pack/plugins/apm/public/services/rest/create_call_apm_api.ts @@ -8,16 +8,11 @@ import { CoreSetup, CoreStart } from '@kbn/core/public'; import type { ClientRequestParamsOf, - formatRequest as formatRequestType, ReturnOf, RouteRepositoryClient, ServerRouteRepository, } from '@kbn/server-route-repository'; -// @ts-expect-error cannot find module or correspondent type declarations -// The code and types are at separated folders on @kbn/server-route-repository -// so in order to do targeted imports they must me imported separately, and -// an error is expected here -import { formatRequest } from '@kbn/server-route-repository/target_node/format_request'; +import { formatRequest } from '@kbn/server-route-repository'; import { InspectResponse } from '@kbn/observability-plugin/typings/common'; import { FetchOptions } from '../../../common/fetch_options'; import { CallApi, callApi } from './call_api'; @@ -73,10 +68,7 @@ export function createCallApmApi(core: CoreStart | CoreSetup) { params?: Partial>; }; - const { method, pathname } = formatRequest( - endpoint, - params?.path - ) as ReturnType; + const { method, pathname } = formatRequest(endpoint, params?.path); return callApi(core, { ...options, diff --git a/x-pack/plugins/apm/server/lib/helpers/get_metric_indices.ts b/x-pack/plugins/apm/server/lib/helpers/get_metric_indices.ts new file mode 100644 index 0000000000000..61d12ba730942 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/helpers/get_metric_indices.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 { SavedObjectsClientContract } from '@kbn/core/server'; +import { APMRouteHandlerResources } from '../../routes/typings'; + +export async function getMetricIndices({ + infraPlugin, + savedObjectsClient, +}: { + infraPlugin: Required; + savedObjectsClient: SavedObjectsClientContract; +}): Promise { + const infra = await infraPlugin.start(); + const metricIndices = await infra.getMetricIndices(savedObjectsClient); + + return metricIndices; +} diff --git a/x-pack/plugins/apm/server/routes/metrics/by_agent/shared/memory/index.ts b/x-pack/plugins/apm/server/routes/metrics/by_agent/shared/memory/index.ts index d0e84211ef455..714ac5cdf38d1 100644 --- a/x-pack/plugins/apm/server/routes/metrics/by_agent/shared/memory/index.ts +++ b/x-pack/plugins/apm/server/routes/metrics/by_agent/shared/memory/index.ts @@ -44,8 +44,16 @@ const chartBase: ChartBase = { }; export const percentSystemMemoryUsedScript = { - lang: 'expression', - source: `1 - doc['${METRIC_SYSTEM_FREE_MEMORY}'] / doc['${METRIC_SYSTEM_TOTAL_MEMORY}']`, + lang: 'painless', + source: ` + if(doc.containsKey('${METRIC_SYSTEM_FREE_MEMORY}') && doc.containsKey('${METRIC_SYSTEM_TOTAL_MEMORY}')){ + double freeMemoryValue = doc['${METRIC_SYSTEM_FREE_MEMORY}'].value; + double totalMemoryValue = doc['${METRIC_SYSTEM_TOTAL_MEMORY}'].value; + return 1 - freeMemoryValue / totalMemoryValue + } + + return null; + `, } as const; export const percentCgroupMemoryUsedScript = { diff --git a/x-pack/plugins/apm/server/routes/service_groups/get_service_groups.ts b/x-pack/plugins/apm/server/routes/service_groups/get_service_groups.ts index 6307d273e12f5..2e4690777d466 100644 --- a/x-pack/plugins/apm/server/routes/service_groups/get_service_groups.ts +++ b/x-pack/plugins/apm/server/routes/service_groups/get_service_groups.ts @@ -10,7 +10,7 @@ import { ServiceGroup, SavedServiceGroup, APM_SERVICE_GROUP_SAVED_OBJECT_TYPE, - MAX_NUMBER_OF_SERVICES_IN_GROUP, + MAX_NUMBER_OF_SERVICE_GROUPS, } from '../../../common/service_groups'; export async function getServiceGroups({ @@ -21,7 +21,7 @@ export async function getServiceGroups({ const result = await savedObjectsClient.find({ type: APM_SERVICE_GROUP_SAVED_OBJECT_TYPE, page: 1, - perPage: MAX_NUMBER_OF_SERVICES_IN_GROUP, + perPage: MAX_NUMBER_OF_SERVICE_GROUPS, }); return result.saved_objects.map( ({ id, attributes, updated_at: upatedAt }) => ({ diff --git a/x-pack/plugins/apm/server/routes/service_groups/lookup_services.ts b/x-pack/plugins/apm/server/routes/service_groups/lookup_services.ts index a9b0d9fe8c57b..78b066a538699 100644 --- a/x-pack/plugins/apm/server/routes/service_groups/lookup_services.ts +++ b/x-pack/plugins/apm/server/routes/service_groups/lookup_services.ts @@ -14,7 +14,7 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../common/processor_event'; import { Setup } from '../../lib/helpers/setup_request'; -import { MAX_NUMBER_OF_SERVICES_IN_GROUP } from '../../../common/service_groups'; +import { MAX_NUMBER_OF_SERVICE_GROUPS } from '../../../common/service_groups'; export async function lookupServices({ setup, @@ -49,7 +49,7 @@ export async function lookupServices({ services: { terms: { field: SERVICE_NAME, - size: MAX_NUMBER_OF_SERVICES_IN_GROUP, + size: MAX_NUMBER_OF_SERVICE_GROUPS, }, aggs: { environments: { diff --git a/x-pack/plugins/apm/server/routes/services/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/routes/services/__snapshots__/queries.test.ts.snap index e09c50708c476..8e2afb1bd1bf7 100644 --- a/x-pack/plugins/apm/server/routes/services/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/routes/services/__snapshots__/queries.test.ts.snap @@ -126,7 +126,7 @@ Array [ }, "terms": Object { "field": "service.name", - "size": 50, + "size": 500, }, }, }, @@ -186,7 +186,7 @@ Array [ }, "terms": Object { "field": "service.name", - "size": 50, + "size": 500, }, }, }, diff --git a/x-pack/plugins/apm/server/routes/services/get_services/get_services_items.ts b/x-pack/plugins/apm/server/routes/services/get_services/get_services_items.ts index a5936dd68d026..de484ed309884 100644 --- a/x-pack/plugins/apm/server/routes/services/get_services/get_services_items.ts +++ b/x-pack/plugins/apm/server/routes/services/get_services/get_services_items.ts @@ -16,7 +16,7 @@ import { ServiceGroup } from '../../../../common/service_groups'; export type ServicesItemsSetup = Setup; -const MAX_NUMBER_OF_SERVICES = 50; +const MAX_NUMBER_OF_SERVICES = 500; export async function getServicesItems({ environment, diff --git a/x-pack/plugins/apm/server/routes/suggestions/get_suggestions.ts b/x-pack/plugins/apm/server/routes/suggestions/get_suggestions.ts index 624bf2bb4c018..f65a843ed95d0 100644 --- a/x-pack/plugins/apm/server/routes/suggestions/get_suggestions.ts +++ b/x-pack/plugins/apm/server/routes/suggestions/get_suggestions.ts @@ -4,23 +4,26 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - import { ProcessorEvent } from '../../../common/processor_event'; import { getProcessorEventForTransactions } from '../../lib/helpers/transactions'; import { Setup } from '../../lib/helpers/setup_request'; export async function getSuggestions({ - field, + fieldName, + fieldValue, searchAggregatedTransactions, setup, size, - string, + start, + end, }: { - field: string; + fieldName: string; + fieldValue: string; searchAggregatedTransactions: boolean; setup: Setup; size: number; - string: string; + start: number; + end: number; }) { const { apmEventClient } = setup; @@ -34,9 +37,18 @@ export async function getSuggestions({ }, body: { case_insensitive: true, - field, + field: fieldName, size, - string, + string: fieldValue, + index_filter: { + range: { + ['@timestamp']: { + gte: start, + lte: end, + format: 'epoch_millis', + }, + }, + }, }, }); diff --git a/x-pack/plugins/apm/server/routes/suggestions/get_suggestions_with_terms_aggregation.ts b/x-pack/plugins/apm/server/routes/suggestions/get_suggestions_with_terms_aggregation.ts new file mode 100644 index 0000000000000..268bc4151efdd --- /dev/null +++ b/x-pack/plugins/apm/server/routes/suggestions/get_suggestions_with_terms_aggregation.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { rangeQuery, termQuery } from '@kbn/observability-plugin/server'; +import { ProcessorEvent } from '../../../common/processor_event'; +import { getProcessorEventForTransactions } from '../../lib/helpers/transactions'; +import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames'; +import { Setup } from '../../lib/helpers/setup_request'; + +export async function getSuggestionsWithTermsAggregation({ + fieldName, + fieldValue, + searchAggregatedTransactions, + serviceName, + setup, + size, + start, + end, +}: { + fieldName: string; + fieldValue: string; + searchAggregatedTransactions: boolean; + serviceName: string; + setup: Setup; + size: number; + start: number; + end: number; +}) { + const { apmEventClient } = setup; + + const response = await apmEventClient.search( + 'get_suggestions_with_terms_aggregation', + { + apm: { + events: [ + getProcessorEventForTransactions(searchAggregatedTransactions), + ProcessorEvent.error, + ProcessorEvent.metric, + ], + }, + body: { + timeout: '1500ms', + size: 0, + query: { + bool: { + filter: [ + ...termQuery(SERVICE_NAME, serviceName), + ...rangeQuery(start, end), + { + wildcard: { + [fieldName]: `*${fieldValue}*`, + }, + }, + ], + }, + }, + aggs: { + items: { + terms: { field: fieldName, size }, + }, + }, + }, + } + ); + + return { + terms: + response.aggregations?.items.buckets.map( + (bucket) => bucket.key as string + ) ?? [], + }; +} diff --git a/x-pack/plugins/apm/server/routes/suggestions/route.ts b/x-pack/plugins/apm/server/routes/suggestions/route.ts index b49204cd86fc6..68f94634a5ded 100644 --- a/x-pack/plugins/apm/server/routes/suggestions/route.ts +++ b/x-pack/plugins/apm/server/routes/suggestions/route.ts @@ -8,20 +8,29 @@ import * as t from 'io-ts'; import { maxSuggestions } from '@kbn/observability-plugin/common'; import { getSuggestions } from './get_suggestions'; +import { getSuggestionsWithTermsAggregation } from './get_suggestions_with_terms_aggregation'; import { getSearchAggregatedTransactions } from '../../lib/helpers/transactions'; import { setupRequest } from '../../lib/helpers/setup_request'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; +import { rangeRt } from '../default_api_types'; const suggestionsRoute = createApmServerRoute({ endpoint: 'GET /internal/apm/suggestions', - params: t.partial({ - query: t.type({ field: t.string, string: t.string }), + params: t.type({ + query: t.intersection([ + t.type({ + fieldName: t.string, + fieldValue: t.string, + }), + rangeRt, + t.partial({ serviceName: t.string }), + ]), }), options: { tags: ['access:apm'] }, handler: async (resources): Promise<{ terms: string[] }> => { const setup = await setupRequest(resources); const { context, params } = resources; - const { field, string } = params.query; + const { fieldName, fieldValue, serviceName, start, end } = params.query; const searchAggregatedTransactions = await getSearchAggregatedTransactions({ apmEventClient: setup.apmEventClient, config: setup.config, @@ -31,16 +40,32 @@ const suggestionsRoute = createApmServerRoute({ const size = await coreContext.uiSettings.client.get( maxSuggestions ); - const suggestions = await getSuggestions({ - field, - searchAggregatedTransactions, - setup, - size, - string, - }); + + const suggestions = serviceName + ? await getSuggestionsWithTermsAggregation({ + fieldName, + fieldValue, + searchAggregatedTransactions, + serviceName, + setup, + size, + start, + end, + }) + : await getSuggestions({ + fieldName, + fieldValue, + searchAggregatedTransactions, + setup, + size, + start, + end, + }); return suggestions; }, }); -export const suggestionsRouteRepository = suggestionsRoute; +export const suggestionsRouteRepository = { + ...suggestionsRoute, +}; diff --git a/x-pack/plugins/apm/server/types.ts b/x-pack/plugins/apm/server/types.ts index d375886728748..73258ef2008fa 100644 --- a/x-pack/plugins/apm/server/types.ts +++ b/x-pack/plugins/apm/server/types.ts @@ -49,6 +49,7 @@ import { FleetSetupContract as FleetPluginSetup, FleetStartContract as FleetPluginStart, } from '@kbn/fleet-plugin/server'; +import { InfraPluginStart, InfraPluginSetup } from '@kbn/infra-plugin/server'; import { APMConfig } from '.'; import { ApmIndicesConfig } from './routes/settings/apm_indices/get_apm_indices'; import { APMEventClient } from './lib/helpers/create_es_client/create_apm_event_client'; @@ -71,6 +72,7 @@ export interface APMPluginSetupDependencies { licensing: LicensingPluginSetup; observability: ObservabilityPluginSetup; ruleRegistry: RuleRegistryPluginSetupContract; + infra: InfraPluginSetup; // optional dependencies actions?: ActionsPlugin['setup']; @@ -92,6 +94,7 @@ export interface APMPluginStartDependencies { licensing: LicensingPluginStart; observability: undefined; ruleRegistry: RuleRegistryPluginStartContract; + infra: InfraPluginStart; // optional dependencies actions?: ActionsPlugin['start']; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts index 2554d2efd1bc9..a74b4c262dd79 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts @@ -163,10 +163,12 @@ export function embeddableFunctionFactory({ return state; }, - migrations: mapValues< - MigrateFunctionsObject, - MigrateFunction - >(embeddablePersistableStateService.getAllMigrations(), migrateByValueEmbeddable), + migrations: () => { + return mapValues< + MigrateFunctionsObject, + MigrateFunction + >(embeddablePersistableStateService.getAllMigrations(), migrateByValueEmbeddable); + }, }; }; } diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/render.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/render.tsx index 38d1d502704e2..cd82ca43c6264 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/render.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/render.tsx @@ -15,6 +15,7 @@ export const defaultHandlers: RendererHandlers = { getFilter: () => 'filter', getRenderMode: () => 'view', isSyncColorsEnabled: () => false, + isSyncTooltipsEnabled: () => false, isInteractive: () => true, onComplete: (fn) => undefined, onEmbeddableDestroyed: action('onEmbeddableDestroyed'), diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx index 8add1aa41b83e..dc164f862aa51 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx @@ -5,7 +5,8 @@ * 2.0. */ -import React from 'react'; +import React, { FC } from 'react'; +import useObservable from 'react-use/lib/useObservable'; import ReactDOM from 'react-dom'; import { CoreStart } from '@kbn/core/public'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; @@ -32,9 +33,28 @@ const embeddablesRegistry: { const renderEmbeddableFactory = (core: CoreStart, plugins: StartDeps) => { const I18nContext = core.i18n.Context; + const EmbeddableRenderer: FC<{ embeddable: IEmbeddable }> = ({ embeddable }) => { + const currentAppId = useObservable(core.application.currentAppId$, undefined); - const embeddableContainerContext: EmbeddableContainerContext = { - getCurrentPath: () => window.location.hash, + if (!currentAppId) { + return null; + } + + const embeddableContainerContext: EmbeddableContainerContext = { + getCurrentPath: () => { + const urlToApp = core.application.getUrlForApp(currentAppId); + const inAppPath = window.location.pathname.replace(urlToApp, ''); + + return inAppPath + window.location.search + window.location.hash; + }, + }; + + return ( + + ); }; return (embeddableObject: IEmbeddable) => { @@ -45,10 +65,7 @@ const renderEmbeddableFactory = (core: CoreStart, plugins: StartDeps) => { > - +
diff --git a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__stories__/__snapshots__/extended_template.stories.storyshot b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__stories__/__snapshots__/extended_template.stories.storyshot index 24d6e94de0ec2..beab512ea62e1 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__stories__/__snapshots__/extended_template.stories.storyshot +++ b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__stories__/__snapshots__/extended_template.stories.storyshot @@ -11,7 +11,7 @@ exports[`Storyshots arguments/AxisConfig extended 1`] = ` } >
= ({ history }) => { - const historyRef = useRef(createHashStateHistory() as History); const { updatePath } = useNavLinkService(); useEffect(() => { - return historyRef.current.listen(({ pathname }) => { - updatePath(pathname); + return history.listen(({ pathname, search }) => { + updatePath(pathname + search); }); }); - useEffect(() => { - return history.listen(({ pathname, hash }) => { - // The scoped history could have something that triggers a url change, and that change is not seen by - // our hash router. For example, a scopedHistory.replace() as done as part of the saved object resolve - // alias match flow will do the replace on the scopedHistory, and our app doesn't react appropriately - - // So, to work around this, whenever we see a url on the scoped history, we're going to wait a beat and see - // if it shows up in our hash router. If it doesn't, then we're going to force it onto our hash router - - // I don't like this at all, and to overcome this we should switch away from hash router sooner rather than later - // and just use scopedHistory as our history object - const expectedPath = hash.substr(1); - const action = history.action; - - // Observable of all the path - const hashPaths$ = new Observable((subscriber) => { - subscriber.next(historyRef.current.location.pathname); - - const unsubscribeHashListener = historyRef.current.listen(({ pathname: newPath }) => { - subscriber.next(newPath); - }); - - return unsubscribeHashListener; - }); - - const subscription = hashPaths$ - .pipe( - skipWhile((value) => value !== expectedPath), - timeout(100), - take(1) - ) - .subscribe({ - error: (e) => { - if (action === 'REPLACE') { - historyRef.current.replace(expectedPath); - } else { - historyRef.current.push(expectedPath); - } - }, - }); - - window.setTimeout(() => subscription.unsubscribe(), 150); - }); - }, [history, historyRef]); - return (
- +
); diff --git a/x-pack/plugins/canvas/public/components/custom_element_modal/__stories__/__snapshots__/custom_element_modal.stories.storyshot b/x-pack/plugins/canvas/public/components/custom_element_modal/__stories__/__snapshots__/custom_element_modal.stories.storyshot index feb04e68ca1d3..552487f1b6d59 100644 --- a/x-pack/plugins/canvas/public/components/custom_element_modal/__stories__/__snapshots__/custom_element_modal.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/custom_element_modal/__stories__/__snapshots__/custom_element_modal.stories.storyshot @@ -58,7 +58,7 @@ exports[`Storyshots components/Elements/CustomElementModal with description 1`] className="euiFlexItem euiFlexItem--flexGrow2 canvasCustomElementForm" >
{ - const { setBreadcrumbs } = usePlatformService(); const [isMounted, setIsMounted] = useState(false); const dispatch = useDispatch(); @@ -25,9 +21,5 @@ export const Home = () => { } }, [dispatch, isMounted, setIsMounted]); - useEffect(() => { - setBreadcrumbs([getBaseBreadcrumb()]); - }, [setBreadcrumbs]); - return ; }; diff --git a/x-pack/plugins/canvas/public/components/home/my_workpads/__snapshots__/workpad_table.stories.storyshot b/x-pack/plugins/canvas/public/components/home/my_workpads/__snapshots__/workpad_table.stories.storyshot index 7536cc7acf7dd..d2e52dde1ad5c 100644 --- a/x-pack/plugins/canvas/public/components/home/my_workpads/__snapshots__/workpad_table.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/home/my_workpads/__snapshots__/workpad_table.stories.storyshot @@ -382,6 +382,7 @@ exports[`Storyshots Home/Components/Workpad Table Workpad Table 1`] = ` className="euiLink euiLink--primary" data-test-subj="canvasWorkpadTableWorkpad" href="/workpad/workpad-2" + onClick={[Function]} rel="noreferrer" > @@ -558,6 +559,7 @@ exports[`Storyshots Home/Components/Workpad Table Workpad Table 1`] = ` className="euiLink euiLink--primary" data-test-subj="canvasWorkpadTableWorkpad" href="/workpad/workpad-1" + onClick={[Function]} rel="noreferrer" > @@ -734,6 +736,7 @@ exports[`Storyshots Home/Components/Workpad Table Workpad Table 1`] = ` className="euiLink euiLink--primary" data-test-subj="canvasWorkpadTableWorkpad" href="/workpad/workpad-0" + onClick={[Function]} rel="noreferrer" > diff --git a/x-pack/plugins/canvas/public/components/home_app/home_app.tsx b/x-pack/plugins/canvas/public/components/home_app/home_app.tsx index b288612450bf7..086a737d525e9 100644 --- a/x-pack/plugins/canvas/public/components/home_app/home_app.tsx +++ b/x-pack/plugins/canvas/public/components/home_app/home_app.tsx @@ -6,6 +6,7 @@ */ import React, { useEffect } from 'react'; +import { useHistory } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import { getBaseBreadcrumb } from '../../lib/breadcrumbs'; import { resetWorkpad } from '../../state/actions/workpad'; @@ -16,10 +17,11 @@ export const HomeApp = () => { const { setBreadcrumbs } = usePlatformService(); const dispatch = useDispatch(); const onLoad = () => dispatch(resetWorkpad()); + const history = useHistory(); useEffect(() => { - setBreadcrumbs([getBaseBreadcrumb()]); - }, [setBreadcrumbs]); + setBreadcrumbs([getBaseBreadcrumb(history)]); + }, [setBreadcrumbs, history]); return ; }; diff --git a/x-pack/plugins/canvas/public/components/routing/routing_link.tsx b/x-pack/plugins/canvas/public/components/routing/routing_link.tsx index bb3123de3fec8..5f82b5050a172 100644 --- a/x-pack/plugins/canvas/public/components/routing/routing_link.tsx +++ b/x-pack/plugins/canvas/public/components/routing/routing_link.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { FC } from 'react'; +import React, { FC, useCallback, MouseEvent } from 'react'; import { EuiLink, EuiLinkProps, EuiButtonIcon, EuiButtonIconProps } from '@elastic/eui'; import { useHistory } from 'react-router-dom'; @@ -15,13 +15,43 @@ interface RoutingProps { type RoutingLinkProps = Omit & RoutingProps; +const isModifiedEvent = (event: MouseEvent) => + !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); + +const isLeftClickEvent = (event: MouseEvent) => event.button === 0; + +const isTargetBlank = (event: MouseEvent) => { + const target = (event.target as HTMLElement).getAttribute('target'); + return target && target !== '_self'; +}; + export const RoutingLink: FC = ({ to, ...rest }) => { const history = useHistory(); + const onClick = useCallback( + (event: MouseEvent) => { + if (event.defaultPrevented) { + return; + } + + // Let the browser handle links that open new tabs/windows + if (isModifiedEvent(event) || !isLeftClickEvent(event) || isTargetBlank(event)) { + return; + } + + // Prevent regular link behavior, which causes a browser refresh. + event.preventDefault(); + + // Push the route to the history. + history.push(to); + }, + [history, to] + ); + // Generate the correct link href (with basename accounted for) const href = history.createHref({ pathname: to }); - const props = { ...rest, href } as EuiLinkProps; + const props = { ...rest, href, onClick } as EuiLinkProps; return ; }; @@ -31,10 +61,30 @@ type RoutingButtonIconProps = Omit & Rou export const RoutingButtonIcon: FC = ({ to, ...rest }) => { const history = useHistory(); + const onClick = useCallback( + (event: MouseEvent) => { + if (event.defaultPrevented) { + return; + } + + // Let the browser handle links that open new tabs/windows + if (isModifiedEvent(event) || !isLeftClickEvent(event) || isTargetBlank(event)) { + return; + } + + // Prevent regular link behavior, which causes a browser refresh. + event.preventDefault(); + + // Push the route to the history. + history.push(to); + }, + [history, to] + ); + // Generate the correct link href (with basename accounted for) const href = history.createHref({ pathname: to }); - const props = { ...rest, href } as EuiButtonIconProps; + const props = { ...rest, href, onClick } as EuiButtonIconProps; return ; }; diff --git a/x-pack/plugins/canvas/public/components/var_config/__stories__/__snapshots__/edit_var.stories.storyshot b/x-pack/plugins/canvas/public/components/var_config/__stories__/__snapshots__/edit_var.stories.storyshot index 2bc247480467a..430e16d622865 100644 --- a/x-pack/plugins/canvas/public/components/var_config/__stories__/__snapshots__/edit_var.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/var_config/__stories__/__snapshots__/edit_var.stories.storyshot @@ -67,7 +67,7 @@ Array [ className="euiForm" >
= ({ addElement }) => { const embeddablesService = useEmbeddablesService(); - const { pathname, search } = useLocation(); + const { pathname, search, hash } = useLocation(); const platformService = usePlatformService(); const stateTransferService = embeddablesService.getStateTransfer(); const visualizationsService = useVisualizationsService(); @@ -61,11 +61,11 @@ export const EditorMenu: FC = ({ addElement }) => { path, state: { originatingApp: CANVAS_APP, - originatingPath: `#/${pathname}${search}`, + originatingPath: `${pathname}${search}${hash}`, }, }); }, - [stateTransferService, pathname, search] + [stateTransferService, pathname, search, hash] ); const createNewEmbeddable = useCallback( diff --git a/x-pack/plugins/canvas/public/expression_types/arg_types/container_style/__stories__/__snapshots__/extended_template.stories.storyshot b/x-pack/plugins/canvas/public/expression_types/arg_types/container_style/__stories__/__snapshots__/extended_template.stories.storyshot index 720c70f50e2b1..6e647bf7350f2 100644 --- a/x-pack/plugins/canvas/public/expression_types/arg_types/container_style/__stories__/__snapshots__/extended_template.stories.storyshot +++ b/x-pack/plugins/canvas/public/expression_types/arg_types/container_style/__stories__/__snapshots__/extended_template.stories.storyshot @@ -30,7 +30,7 @@ exports[`Storyshots arguments/ContainerStyle extended 1`] = ` className="euiFlexItem euiFlexItem--flexGrow2" >
({ - text: 'Canvas', - href: '#/', -}); +const isModifiedEvent = (event: MouseEvent) => + !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); + +const isLeftClickEvent = (event: MouseEvent) => event.button === 0; + +const isTargetBlank = (event: MouseEvent) => { + const target = (event.target as HTMLElement).getAttribute('target'); + return target && target !== '_self'; +}; + +export const getBaseBreadcrumb = (history: History): ChromeBreadcrumb => { + const path = '/'; + const href = history.createHref({ pathname: path }); + + const onClick = (event: MouseEvent) => { + if (event.defaultPrevented) { + return; + } + + // Let the browser handle links that open new tabs/windows + if (isModifiedEvent(event) || !isLeftClickEvent(event) || isTargetBlank(event)) { + return; + } + + // Prevent regular link behavior, which causes a browser refresh. + event.preventDefault(); + + // Push the route to the history. + history.push(path); + }; + + return { + text: 'Canvas', + href, + onClick, + }; +}; export const getWorkpadBreadcrumb = ({ name = 'Workpad', diff --git a/x-pack/plugins/canvas/public/lib/create_handlers.ts b/x-pack/plugins/canvas/public/lib/create_handlers.ts index 66993c0d39bae..3e7d8a0242bd4 100644 --- a/x-pack/plugins/canvas/public/lib/create_handlers.ts +++ b/x-pack/plugins/canvas/public/lib/create_handlers.ts @@ -27,6 +27,7 @@ export const createBaseHandlers = (): IInterpreterRenderHandlers => ({ onDestroy() {}, getRenderMode: () => 'view', isSyncColorsEnabled: () => false, + isSyncTooltipsEnabled: () => false, isInteractive: () => true, }); diff --git a/x-pack/plugins/canvas/public/routes/index.tsx b/x-pack/plugins/canvas/public/routes/index.tsx index fd09aeae3fa9a..e7e9cd6541a3d 100644 --- a/x-pack/plugins/canvas/public/routes/index.tsx +++ b/x-pack/plugins/canvas/public/routes/index.tsx @@ -6,17 +6,48 @@ */ import React, { FC } from 'react'; -import { Router, Switch } from 'react-router-dom'; +import { Router, Switch, Route, RouteComponentProps, Redirect } from 'react-router-dom'; import { History } from 'history'; +import { parse, stringify } from 'query-string'; import { HomeRoute } from './home'; import { WorkpadRoute, ExportWorkpadRoute } from './workpad'; +const isHashPath = (hash: string) => { + return hash.indexOf('#/') === 0; +}; + +const mergeQueryStrings = (query: string, queryFromHash: string) => { + const queryObject = parse(query); + const hashObject = parse(queryFromHash); + + return stringify({ ...queryObject, ...hashObject }); +}; + export const CanvasRouter: FC<{ history: History }> = ({ history }) => ( - - {ExportWorkpadRoute()} - {WorkpadRoute()} - {HomeRoute()} - + { + // If it looks like the hash is a route then we will do a redirect + if (isHashPath(route.location.hash)) { + const [hashPath, hashQuery] = route.location.hash.split('?'); + let search = route.location.search || '?'; + + if (hashQuery !== undefined) { + search = mergeQueryStrings(search, `?${hashQuery}`); + } + + return 1 ? `?${search}` : ''}`} />; + } + + return ( + + {ExportWorkpadRoute()} + {WorkpadRoute()} + {HomeRoute()} + + ); + }} + /> ); diff --git a/x-pack/plugins/canvas/public/routes/workpad/workpad_presentation_helper.tsx b/x-pack/plugins/canvas/public/routes/workpad/workpad_presentation_helper.tsx index 084c9d8c76b00..d6c4b1c6277f5 100644 --- a/x-pack/plugins/canvas/public/routes/workpad/workpad_presentation_helper.tsx +++ b/x-pack/plugins/canvas/public/routes/workpad/workpad_presentation_helper.tsx @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import React, { FC, useEffect } from 'react'; import { useSelector } from 'react-redux'; +import { useHistory } from 'react-router-dom'; import { getBaseBreadcrumb, getWorkpadBreadcrumb } from '../../lib/breadcrumbs'; // @ts-expect-error import { setDocTitle } from '../../lib/doc_title'; @@ -27,13 +28,14 @@ export const WorkpadPresentationHelper: FC = ({ children }) => { useFullscreenPresentationHelper(); useAutoplayHelper(); useRefreshHelper(); + const history = useHistory(); useEffect(() => { platformService.setBreadcrumbs([ - getBaseBreadcrumb(), + getBaseBreadcrumb(history), getWorkpadBreadcrumb({ name: workpad.name }), ]); - }, [workpad.name, workpad.id, platformService]); + }, [workpad.name, workpad.id, platformService, history]); useEffect(() => { setDocTitle(workpad.name); @@ -44,7 +46,7 @@ export const WorkpadPresentationHelper: FC = ({ children }) => { objectNoun: getWorkpadLabel(), currentObjectId: workpad.id, otherObjectId: workpad.aliasId, - otherObjectPath: `#/workpad/${workpad.aliasId}`, + otherObjectPath: `/workpad/${workpad.aliasId}`, }) : null; diff --git a/x-pack/plugins/canvas/public/services/kibana/nav_link.ts b/x-pack/plugins/canvas/public/services/kibana/nav_link.ts index cf68b69155ad7..8470c688f5b2b 100644 --- a/x-pack/plugins/canvas/public/services/kibana/nav_link.ts +++ b/x-pack/plugins/canvas/public/services/kibana/nav_link.ts @@ -20,7 +20,7 @@ export type CanvasNavLinkServiceFactory = KibanaPluginServiceFactory< export const navLinkServiceFactory: CanvasNavLinkServiceFactory = ({ coreStart, appUpdater }) => ({ updatePath: (path: string) => { appUpdater?.next(() => ({ - defaultPath: `#${path}`, + defaultPath: `${path}`, })); getSessionStorage().set(`${SESSIONSTORAGE_LASTPATH}:${coreStart.http.basePath.get()}`, path); diff --git a/x-pack/plugins/canvas/server/saved_objects/custom_element.ts b/x-pack/plugins/canvas/server/saved_objects/custom_element.ts index db87909fbd44f..c9f9ea8453e5f 100644 --- a/x-pack/plugins/canvas/server/saved_objects/custom_element.ts +++ b/x-pack/plugins/canvas/server/saved_objects/custom_element.ts @@ -32,7 +32,7 @@ export const customElementType = (deps: CanvasSavedObjectTypeMigrationsDeps): Sa '@created': { type: 'date' }, }, }, - migrations: customElementMigrationsFactory(deps), + migrations: () => customElementMigrationsFactory(deps), management: { icon: 'canvasApp', defaultSearchField: 'name', diff --git a/x-pack/plugins/canvas/server/saved_objects/workpad.ts b/x-pack/plugins/canvas/server/saved_objects/workpad.ts index b23ea62f88954..6c392aaaa62b1 100644 --- a/x-pack/plugins/canvas/server/saved_objects/workpad.ts +++ b/x-pack/plugins/canvas/server/saved_objects/workpad.ts @@ -32,7 +32,7 @@ export const workpadTypeFactory = ( '@created': { type: 'date' }, }, }, - migrations: workpadMigrationsFactory(deps), + migrations: () => workpadMigrationsFactory(deps), management: { importableAndExportable: true, icon: 'canvasApp', diff --git a/x-pack/plugins/canvas/server/saved_objects/workpad_template.ts b/x-pack/plugins/canvas/server/saved_objects/workpad_template.ts index ec852113e1ca4..224af9bed6759 100644 --- a/x-pack/plugins/canvas/server/saved_objects/workpad_template.ts +++ b/x-pack/plugins/canvas/server/saved_objects/workpad_template.ts @@ -50,7 +50,7 @@ export const workpadTemplateType = ( }, }, }, - migrations: templateWorkpadMigrationsFactory(deps), + migrations: () => templateWorkpadMigrationsFactory(deps), management: { importableAndExportable: false, icon: 'canvasApp', diff --git a/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__snapshots__/settings.test.tsx.snap b/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__snapshots__/settings.test.tsx.snap index 557b8698f7e12..c12891f376e6f 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__snapshots__/settings.test.tsx.snap +++ b/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__snapshots__/settings.test.tsx.snap @@ -43,54 +43,52 @@ exports[` can navigate Autoplay Settings 1`] = `
-
- - + -
+ + +
@@ -143,54 +141,52 @@ exports[` can navigate Autoplay Settings 2`] = `
-
- - + -
+ + +
can navigate Autoplay Settings 2`] = `
-
+
-
- - - Cycle Slides -
-
- + + + Cycle Slides + +
+
+ +
-
- -
+ Set a custom interval + +
+
-
- -
-
-
- Use shorthand notation, like 30s, 10m, or 1h +
+
+ Use shorthand notation, like 30s, 10m, or 1h +
+
+
-
- -
+ +
- -
+
+
@@ -398,54 +392,52 @@ exports[` can navigate Toolbar Settings, closes when activated 1`] =
-
- - + -
+ + +
@@ -498,54 +490,52 @@ exports[` can navigate Toolbar Settings, closes when activated 2`] =
-
- - + -
+ + +
can navigate Toolbar Settings, closes when activated 2`] =
-
+
-
- - - Hide Toolbar -
-
+ - Hide the toolbar when the mouse is not within the Canvas? -
+ Hide Toolbar + +
+
+ Hide the toolbar when the mouse is not within the Canvas?
@@ -643,4 +631,4 @@ exports[` can navigate Toolbar Settings, closes when activated 2`] =
`; -exports[` can navigate Toolbar Settings, closes when activated 3`] = `"

You are in a dialog. To close this dialog, hit escape.

Settings
Hide Toolbar
Hide the toolbar when the mouse is not within the Canvas?
"`; +exports[` can navigate Toolbar Settings, closes when activated 3`] = `"

You are in a dialog. To close this dialog, hit escape.

Settings
Hide Toolbar
Hide the toolbar when the mouse is not within the Canvas?
"`; diff --git a/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__stories__/__snapshots__/autoplay_settings.stories.storyshot b/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__stories__/__snapshots__/autoplay_settings.stories.storyshot index e803600fe8d51..df3d090de3132 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__stories__/__snapshots__/autoplay_settings.stories.storyshot +++ b/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__stories__/__snapshots__/autoplay_settings.stories.storyshot @@ -82,7 +82,7 @@ exports[`Storyshots shareables/Footer/Settings/AutoplaySettings component: off, className="euiFlexItem" >
>; diff --git a/x-pack/plugins/cases/common/api/metrics/case.ts b/x-pack/plugins/cases/common/api/metrics/case.ts index c42887462ae7d..2c3a65764769f 100644 --- a/x-pack/plugins/cases/common/api/metrics/case.ts +++ b/x-pack/plugins/cases/common/api/metrics/case.ts @@ -7,7 +7,10 @@ import * as rt from 'io-ts'; -export type CaseMetricsResponse = rt.TypeOf; +export type SingleCaseMetricsRequest = rt.TypeOf; +export type SingleCaseMetricsResponse = rt.TypeOf; +export type CasesMetricsRequest = rt.TypeOf; +export type CasesMetricsResponse = rt.TypeOf; export type AlertHostsMetrics = rt.TypeOf; export type AlertUsersMetrics = rt.TypeOf; export type StatusInfo = rt.TypeOf; @@ -69,7 +72,43 @@ const AlertUsersMetricsRt = rt.type({ ), }); -export const CaseMetricsResponseRt = rt.partial( +export const SingleCaseMetricsRequestRt = rt.type({ + /** + * The ID of the case. + */ + caseId: rt.string, + /** + * The metrics to retrieve. + */ + features: rt.array(rt.string), +}); + +export const CasesMetricsRequestRt = rt.intersection([ + rt.type({ + /** + * The metrics to retrieve. + */ + features: rt.array(rt.string), + }), + rt.partial({ + /** + * A KQL date. If used all cases created after (gte) the from date will be returned + */ + from: rt.string, + /** + * A KQL date. If used all cases created before (lte) the to date will be returned. + */ + to: rt.string, + /** + * The owner(s) to filter by. The user making the request must have privileges to retrieve cases of that + * ownership or they will be ignored. If no owner is included, then all ownership types will be included in the response + * that the user has access to. + */ + owner: rt.union([rt.array(rt.string), rt.string]), + }), +]); + +export const SingleCaseMetricsResponseRt = rt.partial( rt.type({ alerts: rt.partial( rt.type({ @@ -142,3 +181,12 @@ export const CaseMetricsResponseRt = rt.partial( }), }).props ); + +export const CasesMetricsResponseRt = rt.partial( + rt.type({ + /** + * The average resolve time of all cases in seconds + */ + mttr: rt.number, + }).props +); diff --git a/x-pack/plugins/cases/common/api/runtime_types.ts b/x-pack/plugins/cases/common/api/runtime_types.ts index c807d4b31b751..0a31479b29da8 100644 --- a/x-pack/plugins/cases/common/api/runtime_types.ts +++ b/x-pack/plugins/cases/common/api/runtime_types.ts @@ -9,42 +9,18 @@ import { either, fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/pipeable'; import * as rt from 'io-ts'; -import { isObject } from 'lodash/fp'; +import { formatErrors } from '@kbn/securitysolution-io-ts-utils'; type ErrorFactory = (message: string) => Error; - -/** - * @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts - * Bug fix for the TODO is in the format_errors package - */ -export const formatErrors = (errors: rt.Errors): string[] => { - const err = errors.map((error) => { - if (error.message != null) { - return error.message; - } else { - const keyContext = error.context - .filter( - (entry) => entry.key != null && !Number.isInteger(+entry.key) && entry.key.trim() !== '' - ) - .map((entry) => entry.key) - .join(','); - - const nameContext = error.context.find((entry) => { - // TODO: Put in fix for optional chaining https://github.com/cypress-io/cypress/issues/9298 - if (entry.type && entry.type.name) { - return entry.type.name.length > 0; - } - return false; - }); - const suppliedValue = - keyContext !== '' ? keyContext : nameContext != null ? nameContext.type.name : ''; - const value = isObject(error.value) ? JSON.stringify(error.value) : error.value; - return `Invalid value "${value}" supplied to "${suppliedValue}"`; - } - }); - - return [...new Set(err)]; -}; +export type GenericIntersectionC = + // eslint-disable-next-line @typescript-eslint/no-explicit-any + | rt.IntersectionC<[any, any]> + // eslint-disable-next-line @typescript-eslint/no-explicit-any + | rt.IntersectionC<[any, any, any]> + // eslint-disable-next-line @typescript-eslint/no-explicit-any + | rt.IntersectionC<[any, any, any, any]> + // eslint-disable-next-line @typescript-eslint/no-explicit-any + | rt.IntersectionC<[any, any, any, any, any]>; export const createPlainError = (message: string) => new Error(message); @@ -57,6 +33,40 @@ export const decodeOrThrow = (inputValue: I) => pipe(runtimeType.decode(inputValue), fold(throwErrors(createError), identity)); +const getProps = ( + codec: + | rt.HasProps + // eslint-disable-next-line @typescript-eslint/no-explicit-any + | rt.RecordC + | GenericIntersectionC +): rt.Props | null => { + if (codec == null) { + return null; + } + switch (codec._tag) { + case 'DictionaryType': + if (codec.codomain.props != null) { + return codec.codomain.props; + } + const dTypes: rt.HasProps[] = codec.codomain.types; + return dTypes.reduce((props, type) => Object.assign(props, getProps(type)), {}); + case 'RefinementType': + case 'ReadonlyType': + return getProps(codec.type); + case 'InterfaceType': + case 'StrictType': + case 'PartialType': + return codec.props; + case 'IntersectionType': + const iTypes = codec.types as rt.HasProps[]; + return iTypes.reduce((props, type) => { + return Object.assign(props, getProps(type) as rt.Props); + }, {} as rt.Props) as rt.Props; + default: + return null; + } +}; + const getExcessProps = (props: rt.Props, r: Record): string[] => { const ex: string[] = []; for (const k of Object.keys(r)) { @@ -67,15 +77,21 @@ const getExcessProps = (props: rt.Props, r: Record): string[] = return ex; }; -export function excess | rt.PartialType>( - codec: C -): C { +export function excess< + C extends rt.InterfaceType | GenericIntersectionC | rt.PartialType +>(codec: C): C { + const codecProps = getProps(codec); + const r = new rt.InterfaceType( codec.name, codec.is, (i, c) => either.chain(rt.UnknownRecord.validate(i, c), (s: Record) => { - const ex = getExcessProps(codec.props, s); + if (codecProps == null) { + return rt.failure(i, c, 'unknown codec'); + } + + const ex = getExcessProps(codecProps, s); return ex.length > 0 ? rt.failure( i, @@ -87,7 +103,7 @@ export function excess | rt.PartialType = { [K in keyof T]: DeepRequired } & Required; export interface CasesContextFeatures { alerts: { sync?: boolean; enabled?: boolean }; - metrics: CaseMetricsFeature[]; + metrics: SingleCaseMetricsFeature[]; } export type CasesFeaturesAllRequired = DeepRequired; @@ -43,6 +46,9 @@ export type StatusAllType = typeof StatusAll; export type CaseStatusWithAllStatus = CaseStatuses | StatusAllType; +export const SeverityAll = 'all' as const; +export type CaseSeverityWithAll = CaseSeverity | typeof SeverityAll; + /** * The type for the `refreshRef` prop (a `React.Ref`) defined by the `CaseViewComponentProps`. * @@ -61,31 +67,10 @@ export type Comment = SnakeToCamelCase; export type AlertComment = SnakeToCamelCase; export type CaseUserActions = SnakeToCamelCase; export type CaseExternalService = SnakeToCamelCase; - -interface BasicCase { - id: string; - owner: string; - closedAt: string | null; - closedBy: ElasticUser | null; - comments: Comment[]; - createdAt: string; - createdBy: ElasticUser; - status: CaseStatuses; - title: string; - totalAlerts: number; - totalComment: number; - updatedAt: string | null; - updatedBy: ElasticUser | null; - version: string; -} - -export interface Case extends BasicCase { - connector: CaseConnector; - description: string; - externalService: CaseExternalService | null; - settings: CaseAttributes['settings']; - tags: string[]; -} +export type Case = Omit, 'comments'> & { comments: Comment[] }; +export type Cases = Omit, 'cases'> & { cases: Case[] }; +export type CasesStatus = SnakeToCamelCase; +export type CasesMetrics = SnakeToCamelCase; export interface ResolvedCase { case: Case; @@ -103,27 +88,15 @@ export interface QueryParams { export interface FilterOptions { search: string; + severity: CaseSeverityWithAll; status: CaseStatusWithAllStatus; tags: string[]; reporters: User[]; owner: string[]; } -export interface CasesStatus { - countClosedCases: number | null; - countOpenCases: number | null; - countInProgressCases: number | null; -} - -export interface AllCases extends CasesStatus { - cases: Case[]; - page: number; - perPage: number; - total: number; -} - -export type CaseMetrics = CaseMetricsResponse; -export type CaseMetricsFeature = +export type SingleCaseMetrics = SingleCaseMetricsResponse; +export type SingleCaseMetricsFeature = | 'alerts.count' | 'alerts.users' | 'alerts.hosts' @@ -176,7 +149,7 @@ export interface FieldMappings { export type UpdateKey = keyof Pick< CasePatchRequest, - 'connector' | 'description' | 'status' | 'tags' | 'title' | 'settings' + 'connector' | 'description' | 'status' | 'tags' | 'title' | 'settings' | 'severity' >; export interface UpdateByKey { diff --git a/x-pack/plugins/cases/docs/openapi/README.md b/x-pack/plugins/cases/docs/openapi/README.md new file mode 100644 index 0000000000000..1ff3e24c2e91f --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/README.md @@ -0,0 +1,29 @@ +# OpenAPI (Experimental) + +The current self-contained spec file is [as JSON](https://raw.githubusercontent.com/elastic/kibana/master/x-pack/plugins/cases/common/openapi/bundled.json) or [as YAML](https://raw.githubusercontent.com/elastic/kibana/master/x-pack/plugins/cases/common/openapi/bundled.yaml) and can be used for online tools like those found at https://openapi.tools/. +This spec is experimental and may be incomplete or change later. + +A guide about the openApi specification can be found at [https://swagger.io/docs/specification/about/](https://swagger.io/docs/specification/about/). + +## The `openapi` folder + +* `entrypoint.yaml` is the overview file which pulls together all the paths and components. +* [Paths](paths/README.md): this defines each endpoint. A path can have one operation per http method. +* [Components](components/README.md): Reusable components + +## Tools + +It is possible to validate the docs before bundling them with the following +command in the `x-pack/plugins/cases/docs/openapi/` folder: + + ``` + npx swagger-cli validate entrypoint.yaml + ``` + +Then you can generate the `bundled` files by running the following commands: + + ``` + npx @redocly/openapi-cli bundle --ext yaml --output bundled.yaml entrypoint.yaml + npx @redocly/openapi-cli bundle --ext json --output bundled.json entrypoint.yaml + ``` + diff --git a/x-pack/plugins/cases/docs/openapi/bundled.json b/x-pack/plugins/cases/docs/openapi/bundled.json new file mode 100644 index 0000000000000..31feae3331b04 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/bundled.json @@ -0,0 +1,2122 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Cases", + "description": "OpenAPI schema for Cases endpoints", + "version": "0.1", + "contact": { + "name": "Cases Team" + }, + "license": { + "name": "Elastic License 2.0", + "url": "https://www.elastic.co/licensing/elastic-license" + } + }, + "tags": [ + { + "name": "cases", + "description": "Case APIs enable you to open and track issues." + }, + { + "name": "kibana", + "description": "Kibana APIs enable you to interact with Kibana features." + } + ], + "servers": [ + { + "url": "http://localhost:5601", + "description": "local" + } + ], + "paths": { + "/api/cases": { + "post": { + "description": "Creates a case. You must have all privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're creating.\n", + "tags": [ + "cases", + "kibana" + ], + "parameters": [ + { + "$ref": "#/components/parameters/kbn_xsrf" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "connector": { + "description": "An object that contains the connector configuration.", + "type": "object", + "properties": { + "fields": { + "description": "An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value.", + "nullable": true, + "type": "object", + "properties": { + "caseId": { + "description": "The case identifier for Swimlane connectors.", + "type": "string" + }, + "category": { + "description": "The category of the incident for ServiceNow ITSM and ServiceNow SecOps connectors.", + "type": "string" + }, + "destIp": { + "description": "A comma-separated list of destination IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "impact": { + "description": "The effect an incident had on business for ServiceNow ITSM connectors.", + "type": "string" + }, + "issueType": { + "description": "The type of issue for Jira connectors.", + "type": "string" + }, + "issueTypes": { + "description": "The type of incident for IBM Resilient connectors.", + "type": "array", + "items": { + "type": "number" + } + }, + "malwareHash": { + "description": "A comma-separated list of malware hashes for ServiceNow SecOps connectors.", + "type": "string" + }, + "malwareUrl": { + "description": "A comma-separated list of malware URLs for ServiceNow SecOps connectors.", + "type": "string" + }, + "parent": { + "description": "The key of the parent issue, when the issue type is sub-task for Jira connectors.", + "type": "string" + }, + "priority": { + "description": "The priority of the issue for Jira and ServiceNow SecOps connectors.", + "type": "string" + }, + "severity": { + "description": "The severity of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "severityCode": { + "description": "The severity code of the incident for IBM Resilient connectors.", + "type": "number" + }, + "sourceIp": { + "description": "A comma-separated list of source IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "subcategory": { + "description": "The subcategory of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "urgency": { + "description": "The extent to which the incident resolution can be delayed for ServiceNow ITSM connectors.", + "type": "string" + } + }, + "required": [ + "fields", + "id", + "name", + "type" + ] + }, + "id": { + "description": "The identifier for the connector. To create a case without a connector, use `none`.", + "type": "string" + }, + "name": { + "description": "The name of the connector. To create a case without a connector, use `none`.", + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/connector_types" + } + } + }, + "description": { + "description": "The description for the case.", + "type": "string" + }, + "owner": { + "$ref": "#/components/schemas/owners" + }, + "settings": { + "description": "An object that contains the case settings.", + "type": "object", + "properties": { + "syncAlerts": { + "description": "Turns alert syncing on or off.", + "type": "boolean" + } + } + }, + "tags": { + "description": "The words and phrases that help categorize cases. It can be an empty array.", + "type": "array", + "items": { + "type": "string" + } + }, + "title": { + "description": "A title for the case.", + "type": "string" + } + }, + "required": [ + "connector", + "description", + "owner", + "settings", + "tags", + "title" + ] + }, + "examples": { + "createCaseRequest": { + "$ref": "#/components/examples/create_case_request" + } + } + } + } + }, + "responses": { + "200": { + "description": "Indicates a successful call.", + "content": { + "application/json; charset=utf-8": { + "schema": { + "type": "object", + "properties": { + "closed_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "example": null + }, + "closed_by": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "nullable": true, + "example": null + }, + "comments": { + "type": "array", + "items": { + "type": "string" + }, + "example": [] + }, + "connector": { + "type": "object", + "properties": { + "fields": { + "description": "An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value.", + "nullable": true, + "type": "object", + "properties": { + "caseId": { + "description": "The case identifier for Swimlane connectors.", + "type": "string" + }, + "category": { + "description": "The category of the incident for ServiceNow ITSM and ServiceNow SecOps connectors.", + "type": "string" + }, + "destIp": { + "description": "A comma-separated list of destination IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "impact": { + "description": "The effect an incident had on business for ServiceNow ITSM connectors.", + "type": "string" + }, + "issueType": { + "description": "The type of issue for Jira connectors.", + "type": "string" + }, + "issueTypes": { + "description": "The type of incident for IBM Resilient connectors.", + "type": "array", + "items": { + "type": "number" + } + }, + "malwareHash": { + "description": "A comma-separated list of malware hashes for ServiceNow SecOps connectors.", + "type": "string" + }, + "malwareUrl": { + "description": "A comma-separated list of malware URLs for ServiceNow SecOps connectors.", + "type": "string" + }, + "parent": { + "description": "The key of the parent issue, when the issue type is sub-task for Jira connectors.", + "type": "string" + }, + "priority": { + "description": "The priority of the issue for Jira and ServiceNow SecOps connectors.", + "type": "string" + }, + "severity": { + "description": "The severity of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "severityCode": { + "description": "The severity code of the incident for IBM Resilient connectors.", + "type": "number" + }, + "sourceIp": { + "description": "A comma-separated list of source IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "subcategory": { + "description": "The subcategory of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "urgency": { + "description": "The extent to which the incident resolution can be delayed for ServiceNow ITSM connectors.", + "type": "string" + } + }, + "required": [ + "fields", + "id", + "name", + "type" + ] + }, + "id": { + "description": "The identifier for the connector. To create a case without a connector, use `none`.", + "type": "string" + }, + "name": { + "description": "The name of the connector. To create a case without a connector, use `none`.", + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/connector_types" + } + } + }, + "created_at": { + "type": "string", + "format": "date-time", + "example": "2022-05-13T09:16:17.416Z" + }, + "created_by": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": "ahunley@imf.usa.gov" + }, + "full_name": { + "type": "string", + "example": "Alan Hunley" + }, + "username": { + "type": "string", + "example": "ahunley" + } + } + }, + "description": { + "type": "string", + "example": "James Bond clicked on a highly suspicious email banner advertising cheap holidays for underpaid civil servants. Operation bubblegum is active. Repeat - operation bubblegum is now active" + }, + "external_service": { + "type": "object", + "properties": { + "connector_id": { + "type": "string" + }, + "connector_name": { + "type": "string" + }, + "external_id": { + "type": "string" + }, + "external_title": { + "type": "string" + }, + "external_url": { + "type": "string" + }, + "pushed_at": { + "type": "string", + "format": "date-time" + }, + "pushed_by": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "nullable": true, + "example": null + } + } + }, + "id": { + "type": "string", + "example": "66b9aa00-94fa-11ea-9f74-e7e108796192" + }, + "owner": { + "$ref": "#/components/schemas/owners" + }, + "settings": { + "type": "object", + "properties": { + "syncAlerts": { + "type": "boolean", + "example": true + } + } + }, + "status": { + "$ref": "#/components/schemas/status" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "phishing", + "social engineering", + "bubblegum" + ] + }, + "title": { + "type": "string", + "example": "This case will self-destruct in 5 seconds" + }, + "totalAlerts": { + "type": "integer", + "example": 0 + }, + "totalComment": { + "type": "integer", + "example": 0 + }, + "updated_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "example": null + }, + "updated_by": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "nullable": true, + "example": null + }, + "version": { + "type": "string", + "example": "WzUzMiwxXQ==" + } + } + }, + "examples": { + "createCaseResponse": { + "$ref": "#/components/examples/create_case_response" + } + } + } + } + } + }, + "servers": [ + { + "url": "https://localhost:5601" + } + ] + }, + "delete": { + "description": "Deletes one or more cases. You must have all privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the cases you're deleting.\n", + "tags": [ + "cases", + "kibana" + ], + "parameters": [ + { + "$ref": "#/components/parameters/kbn_xsrf" + }, + { + "name": "ids", + "description": "The cases that you want to removed. To retrieve case IDs, use the find cases API. All non-ASCII characters must be URL encoded.", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "example": "d4e7abb0-b462-11ec-9a8d-698504725a43" + } + ], + "responses": { + "204": { + "description": "Indicates a successful call." + } + }, + "servers": [ + { + "url": "https://localhost:5601" + } + ] + }, + "patch": { + "description": "Updates one or more cases. You must have all privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're updating.\n", + "tags": [ + "cases", + "kibana" + ], + "parameters": [ + { + "$ref": "#/components/parameters/kbn_xsrf" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "cases": { + "type": "array", + "items": { + "type": "object", + "properties": { + "connector": { + "description": "An object that contains the connector configuration.", + "type": "object", + "properties": { + "fields": { + "description": "An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value.", + "nullable": true, + "type": "object", + "properties": { + "caseId": { + "description": "The case identifier for Swimlane connectors.", + "type": "string" + }, + "category": { + "description": "The category of the incident for ServiceNow ITSM and ServiceNow SecOps connectors.", + "type": "string" + }, + "destIp": { + "description": "A comma-separated list of destination IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "impact": { + "description": "The effect an incident had on business for ServiceNow ITSM connectors.", + "type": "string" + }, + "issueType": { + "description": "The type of issue for Jira connectors.", + "type": "string" + }, + "issueTypes": { + "description": "The type of incident for IBM Resilient connectors.", + "type": "array", + "items": { + "type": "number" + } + }, + "malwareHash": { + "description": "A comma-separated list of malware hashes for ServiceNow SecOps connectors.", + "type": "string" + }, + "malwareUrl": { + "description": "A comma-separated list of malware URLs for ServiceNow SecOps connectors.", + "type": "string" + }, + "parent": { + "description": "The key of the parent issue, when the issue type is sub-task for Jira connectors.", + "type": "string" + }, + "priority": { + "description": "The priority of the issue for Jira and ServiceNow SecOps connectors.", + "type": "string" + }, + "severity": { + "description": "The severity of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "severityCode": { + "description": "The severity code of the incident for IBM Resilient connectors.", + "type": "number" + }, + "sourceIp": { + "description": "A comma-separated list of source IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "subcategory": { + "description": "The subcategory of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "urgency": { + "description": "The extent to which the incident resolution can be delayed for ServiceNow ITSM connectors.", + "type": "string" + } + }, + "required": [ + "fields", + "id", + "name", + "type" + ] + }, + "id": { + "description": "The identifier for the connector. To create a case without a connector, use `none`.", + "type": "string" + }, + "name": { + "description": "The name of the connector. To create a case without a connector, use `none`.", + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/connector_types" + } + } + }, + "description": { + "description": "The description for the case.", + "type": "string" + }, + "id": { + "description": "The identifier for the case.", + "type": "string" + }, + "settings": { + "description": "An object that contains the case settings.", + "type": "object", + "properties": { + "syncAlerts": { + "description": "Turns alert syncing on or off.", + "type": "boolean" + } + } + }, + "status": { + "$ref": "#/components/schemas/status" + }, + "tags": { + "description": "The words and phrases that help categorize cases.", + "type": "array", + "items": { + "type": "string" + } + }, + "title": { + "description": "A title for the case.", + "type": "string" + }, + "version": { + "description": "The current version of the case.", + "type": "string" + } + }, + "required": [ + "id", + "version" + ] + } + } + } + }, + "examples": { + "updateCaseRequest": { + "$ref": "#/components/examples/update_case_request" + } + } + } + } + }, + "responses": { + "200": { + "description": "Indicates a successful call.", + "content": { + "application/json; charset=utf-8": { + "schema": { + "type": "object", + "properties": { + "closed_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "example": null + }, + "closed_by": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "nullable": true, + "example": null + }, + "comments": { + "type": "array", + "items": { + "type": "string" + }, + "example": [] + }, + "connector": { + "type": "object", + "properties": { + "fields": { + "description": "An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value.", + "nullable": true, + "type": "object", + "properties": { + "caseId": { + "description": "The case identifier for Swimlane connectors.", + "type": "string" + }, + "category": { + "description": "The category of the incident for ServiceNow ITSM and ServiceNow SecOps connectors.", + "type": "string" + }, + "destIp": { + "description": "A comma-separated list of destination IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "impact": { + "description": "The effect an incident had on business for ServiceNow ITSM connectors.", + "type": "string" + }, + "issueType": { + "description": "The type of issue for Jira connectors.", + "type": "string" + }, + "issueTypes": { + "description": "The type of incident for IBM Resilient connectors.", + "type": "array", + "items": { + "type": "number" + } + }, + "malwareHash": { + "description": "A comma-separated list of malware hashes for ServiceNow SecOps connectors.", + "type": "string" + }, + "malwareUrl": { + "description": "A comma-separated list of malware URLs for ServiceNow SecOps connectors.", + "type": "string" + }, + "parent": { + "description": "The key of the parent issue, when the issue type is sub-task for Jira connectors.", + "type": "string" + }, + "priority": { + "description": "The priority of the issue for Jira and ServiceNow SecOps connectors.", + "type": "string" + }, + "severity": { + "description": "The severity of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "severityCode": { + "description": "The severity code of the incident for IBM Resilient connectors.", + "type": "number" + }, + "sourceIp": { + "description": "A comma-separated list of source IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "subcategory": { + "description": "The subcategory of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "urgency": { + "description": "The extent to which the incident resolution can be delayed for ServiceNow ITSM connectors.", + "type": "string" + } + }, + "required": [ + "fields", + "id", + "name", + "type" + ] + }, + "id": { + "description": "The identifier for the connector. To create a case without a connector, use `none`.", + "type": "string" + }, + "name": { + "description": "The name of the connector. To create a case without a connector, use `none`.", + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/connector_types" + } + } + }, + "created_at": { + "type": "string", + "format": "date-time", + "example": "2022-05-13T09:16:17.416Z" + }, + "created_by": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": "ahunley@imf.usa.gov" + }, + "full_name": { + "type": "string", + "example": "Alan Hunley" + }, + "username": { + "type": "string", + "example": "ahunley" + } + } + }, + "description": { + "type": "string", + "example": "James Bond clicked on a highly suspicious email banner advertising cheap holidays for underpaid civil servants. Operation bubblegum is active. Repeat - operation bubblegum is now active" + }, + "external_service": { + "type": "object", + "properties": { + "connector_id": { + "type": "string" + }, + "connector_name": { + "type": "string" + }, + "external_id": { + "type": "string" + }, + "external_title": { + "type": "string" + }, + "external_url": { + "type": "string" + }, + "pushed_at": { + "type": "string", + "format": "date-time" + }, + "pushed_by": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "nullable": true, + "example": null + } + } + }, + "id": { + "type": "string", + "example": "66b9aa00-94fa-11ea-9f74-e7e108796192" + }, + "owner": { + "$ref": "#/components/schemas/owners" + }, + "settings": { + "type": "object", + "properties": { + "syncAlerts": { + "type": "boolean", + "example": true + } + } + }, + "status": { + "$ref": "#/components/schemas/status" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "phishing", + "social engineering", + "bubblegum" + ] + }, + "title": { + "type": "string", + "example": "This case will self-destruct in 5 seconds" + }, + "totalAlerts": { + "type": "integer", + "example": 0 + }, + "totalComment": { + "type": "integer", + "example": 0 + }, + "updated_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "example": null + }, + "updated_by": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "nullable": true, + "example": null + }, + "version": { + "type": "string", + "example": "WzUzMiwxXQ==" + } + } + }, + "examples": { + "updateCaseResponse": { + "$ref": "#/components/examples/update_case_response" + } + } + } + } + } + }, + "servers": [ + { + "url": "https://localhost:5601" + } + ] + }, + "servers": [ + { + "url": "https://localhost:5601" + } + ] + }, + "/s/{spaceId}/api/cases": { + "post": { + "description": "Creates a case. You must have all privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're creating.\n", + "tags": [ + "cases", + "kibana" + ], + "parameters": [ + { + "$ref": "#/components/parameters/kbn_xsrf" + }, + { + "$ref": "#/components/parameters/space_id" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "connector": { + "description": "An object that contains the connector configuration.", + "type": "object", + "properties": { + "fields": { + "description": "An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value.", + "nullable": true, + "type": "object", + "properties": { + "caseId": { + "description": "The case identifier for Swimlane connectors.", + "type": "string" + }, + "category": { + "description": "The category of the incident for ServiceNow ITSM and ServiceNow SecOps connectors.", + "type": "string" + }, + "destIp": { + "description": "A comma-separated list of destination IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "impact": { + "description": "The effect an incident had on business for ServiceNow ITSM connectors.", + "type": "string" + }, + "issueType": { + "description": "The type of issue for Jira connectors.", + "type": "string" + }, + "issueTypes": { + "description": "The type of incident for IBM Resilient connectors.", + "type": "array", + "items": { + "type": "number" + } + }, + "malwareHash": { + "description": "A comma-separated list of malware hashes for ServiceNow SecOps connectors.", + "type": "string" + }, + "malwareUrl": { + "description": "A comma-separated list of malware URLs for ServiceNow SecOps connectors.", + "type": "string" + }, + "parent": { + "description": "The key of the parent issue, when the issue type is sub-task for Jira connectors.", + "type": "string" + }, + "priority": { + "description": "The priority of the issue for Jira and ServiceNow SecOps connectors.", + "type": "string" + }, + "severity": { + "description": "The severity of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "severityCode": { + "description": "The severity code of the incident for IBM Resilient connectors.", + "type": "number" + }, + "sourceIp": { + "description": "A comma-separated list of source IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "subcategory": { + "description": "The subcategory of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "urgency": { + "description": "The extent to which the incident resolution can be delayed for ServiceNow ITSM connectors.", + "type": "string" + } + }, + "required": [ + "fields", + "id", + "name", + "type" + ] + }, + "id": { + "description": "The identifier for the connector. To create a case without a connector, use `none`.", + "type": "string" + }, + "name": { + "description": "The name of the connector. To create a case without a connector, use `none`.", + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/connector_types" + } + } + }, + "description": { + "description": "The description for the case.", + "type": "string" + }, + "owner": { + "$ref": "#/components/schemas/owners" + }, + "settings": { + "description": "An object that contains the case settings.", + "type": "object", + "properties": { + "syncAlerts": { + "description": "Turns alert syncing on or off.", + "type": "boolean" + } + } + }, + "tags": { + "description": "The words and phrases that help categorize cases. It can be an empty array.", + "type": "array", + "items": { + "type": "string" + } + }, + "title": { + "description": "A title for the case.", + "type": "string" + } + }, + "required": [ + "connector", + "description", + "owner", + "settings", + "tags", + "title" + ] + }, + "examples": { + "createCaseRequest": { + "$ref": "#/components/examples/create_case_request" + } + } + } + } + }, + "responses": { + "200": { + "description": "Indicates a successful call.", + "content": { + "application/json; charset=utf-8": { + "schema": { + "type": "object", + "properties": { + "closed_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "example": null + }, + "closed_by": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "nullable": true, + "example": null + }, + "comments": { + "type": "array", + "items": { + "type": "string" + }, + "example": [] + }, + "connector": { + "type": "object", + "properties": { + "fields": { + "description": "An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value.", + "nullable": true, + "type": "object", + "properties": { + "caseId": { + "description": "The case identifier for Swimlane connectors.", + "type": "string" + }, + "category": { + "description": "The category of the incident for ServiceNow ITSM and ServiceNow SecOps connectors.", + "type": "string" + }, + "destIp": { + "description": "A comma-separated list of destination IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "impact": { + "description": "The effect an incident had on business for ServiceNow ITSM connectors.", + "type": "string" + }, + "issueType": { + "description": "The type of issue for Jira connectors.", + "type": "string" + }, + "issueTypes": { + "description": "The type of incident for IBM Resilient connectors.", + "type": "array", + "items": { + "type": "number" + } + }, + "malwareHash": { + "description": "A comma-separated list of malware hashes for ServiceNow SecOps connectors.", + "type": "string" + }, + "malwareUrl": { + "description": "A comma-separated list of malware URLs for ServiceNow SecOps connectors.", + "type": "string" + }, + "parent": { + "description": "The key of the parent issue, when the issue type is sub-task for Jira connectors.", + "type": "string" + }, + "priority": { + "description": "The priority of the issue for Jira and ServiceNow SecOps connectors.", + "type": "string" + }, + "severity": { + "description": "The severity of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "severityCode": { + "description": "The severity code of the incident for IBM Resilient connectors.", + "type": "number" + }, + "sourceIp": { + "description": "A comma-separated list of source IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "subcategory": { + "description": "The subcategory of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "urgency": { + "description": "The extent to which the incident resolution can be delayed for ServiceNow ITSM connectors.", + "type": "string" + } + }, + "required": [ + "fields", + "id", + "name", + "type" + ] + }, + "id": { + "description": "The identifier for the connector. To create a case without a connector, use `none`.", + "type": "string" + }, + "name": { + "description": "The name of the connector. To create a case without a connector, use `none`.", + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/connector_types" + } + } + }, + "created_at": { + "type": "string", + "format": "date-time", + "example": "2022-05-13T09:16:17.416Z" + }, + "created_by": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": "ahunley@imf.usa.gov" + }, + "full_name": { + "type": "string", + "example": "Alan Hunley" + }, + "username": { + "type": "string", + "example": "ahunley" + } + } + }, + "description": { + "type": "string", + "example": "James Bond clicked on a highly suspicious email banner advertising cheap holidays for underpaid civil servants. Operation bubblegum is active. Repeat - operation bubblegum is now active" + }, + "external_service": { + "type": "object", + "properties": { + "connector_id": { + "type": "string" + }, + "connector_name": { + "type": "string" + }, + "external_id": { + "type": "string" + }, + "external_title": { + "type": "string" + }, + "external_url": { + "type": "string" + }, + "pushed_at": { + "type": "string", + "format": "date-time" + }, + "pushed_by": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "nullable": true, + "example": null + } + } + }, + "id": { + "type": "string", + "example": "66b9aa00-94fa-11ea-9f74-e7e108796192" + }, + "owner": { + "$ref": "#/components/schemas/owners" + }, + "settings": { + "type": "object", + "properties": { + "syncAlerts": { + "type": "boolean", + "example": true + } + } + }, + "status": { + "$ref": "#/components/schemas/status" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "phishing", + "social engineering", + "bubblegum" + ] + }, + "title": { + "type": "string", + "example": "This case will self-destruct in 5 seconds" + }, + "totalAlerts": { + "type": "integer", + "example": 0 + }, + "totalComment": { + "type": "integer", + "example": 0 + }, + "updated_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "example": null + }, + "updated_by": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "nullable": true, + "example": null + }, + "version": { + "type": "string", + "example": "WzUzMiwxXQ==" + } + } + }, + "examples": { + "createCaseResponse": { + "$ref": "#/components/examples/create_case_response" + } + } + } + } + } + }, + "servers": [ + { + "url": "https://localhost:5601" + } + ] + }, + "delete": { + "description": "Deletes one or more cases. You must have all privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the cases you're deleting.\n", + "tags": [ + "cases", + "kibana" + ], + "parameters": [ + { + "$ref": "#/components/parameters/kbn_xsrf" + }, + { + "$ref": "#/components/parameters/space_id" + }, + { + "name": "ids", + "description": "The cases that you want to removed. All non-ASCII characters must be URL encoded.", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "example": "d4e7abb0-b462-11ec-9a8d-698504725a43" + } + ], + "responses": { + "204": { + "description": "Indicates a successful call." + } + }, + "servers": [ + { + "url": "https://localhost:5601" + } + ] + }, + "patch": { + "description": "Updates one or more cases. You must have all privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're updating.\n", + "tags": [ + "cases", + "kibana" + ], + "parameters": [ + { + "$ref": "#/components/parameters/kbn_xsrf" + }, + { + "$ref": "#/components/parameters/space_id" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "cases": { + "type": "array", + "items": { + "type": "object", + "properties": { + "connector": { + "description": "An object that contains the connector configuration.", + "type": "object", + "properties": { + "fields": { + "description": "An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value.", + "nullable": true, + "type": "object", + "properties": { + "caseId": { + "description": "The case identifier for Swimlane connectors.", + "type": "string" + }, + "category": { + "description": "The category of the incident for ServiceNow ITSM and ServiceNow SecOps connectors.", + "type": "string" + }, + "destIp": { + "description": "A comma-separated list of destination IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "impact": { + "description": "The effect an incident had on business for ServiceNow ITSM connectors.", + "type": "string" + }, + "issueType": { + "description": "The type of issue for Jira connectors.", + "type": "string" + }, + "issueTypes": { + "description": "The type of incident for IBM Resilient connectors.", + "type": "array", + "items": { + "type": "number" + } + }, + "malwareHash": { + "description": "A comma-separated list of malware hashes for ServiceNow SecOps connectors.", + "type": "string" + }, + "malwareUrl": { + "description": "A comma-separated list of malware URLs for ServiceNow SecOps connectors.", + "type": "string" + }, + "parent": { + "description": "The key of the parent issue, when the issue type is sub-task for Jira connectors.", + "type": "string" + }, + "priority": { + "description": "The priority of the issue for Jira and ServiceNow SecOps connectors.", + "type": "string" + }, + "severity": { + "description": "The severity of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "severityCode": { + "description": "The severity code of the incident for IBM Resilient connectors.", + "type": "number" + }, + "sourceIp": { + "description": "A comma-separated list of source IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "subcategory": { + "description": "The subcategory of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "urgency": { + "description": "The extent to which the incident resolution can be delayed for ServiceNow ITSM connectors.", + "type": "string" + } + }, + "required": [ + "fields", + "id", + "name", + "type" + ] + }, + "id": { + "description": "The identifier for the connector. To create a case without a connector, use `none`.", + "type": "string" + }, + "name": { + "description": "The name of the connector. To create a case without a connector, use `none`.", + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/connector_types" + } + } + }, + "description": { + "description": "The description for the case.", + "type": "string" + }, + "id": { + "description": "The identifier for the case.", + "type": "string" + }, + "settings": { + "description": "An object that contains the case settings.", + "type": "object", + "properties": { + "syncAlerts": { + "description": "Turns alert syncing on or off.", + "type": "boolean" + } + } + }, + "status": { + "$ref": "#/components/schemas/status" + }, + "tags": { + "description": "The words and phrases that help categorize cases.", + "type": "array", + "items": { + "type": "string" + } + }, + "title": { + "description": "A title for the case.", + "type": "string" + }, + "version": { + "description": "The current version of the case.", + "type": "string" + } + }, + "required": [ + "id", + "version" + ] + } + } + } + }, + "examples": { + "updateCaseRequest": { + "$ref": "#/components/examples/update_case_request" + } + } + } + } + }, + "responses": { + "200": { + "description": "Indicates a successful call.", + "content": { + "application/json; charset=utf-8": { + "schema": { + "type": "object", + "properties": { + "closed_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "example": null + }, + "closed_by": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "nullable": true, + "example": null + }, + "comments": { + "type": "array", + "items": { + "type": "string" + }, + "example": [] + }, + "connector": { + "type": "object", + "properties": { + "fields": { + "description": "An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value.", + "nullable": true, + "type": "object", + "properties": { + "caseId": { + "description": "The case identifier for Swimlane connectors.", + "type": "string" + }, + "category": { + "description": "The category of the incident for ServiceNow ITSM and ServiceNow SecOps connectors.", + "type": "string" + }, + "destIp": { + "description": "A comma-separated list of destination IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "impact": { + "description": "The effect an incident had on business for ServiceNow ITSM connectors.", + "type": "string" + }, + "issueType": { + "description": "The type of issue for Jira connectors.", + "type": "string" + }, + "issueTypes": { + "description": "The type of incident for IBM Resilient connectors.", + "type": "array", + "items": { + "type": "number" + } + }, + "malwareHash": { + "description": "A comma-separated list of malware hashes for ServiceNow SecOps connectors.", + "type": "string" + }, + "malwareUrl": { + "description": "A comma-separated list of malware URLs for ServiceNow SecOps connectors.", + "type": "string" + }, + "parent": { + "description": "The key of the parent issue, when the issue type is sub-task for Jira connectors.", + "type": "string" + }, + "priority": { + "description": "The priority of the issue for Jira and ServiceNow SecOps connectors.", + "type": "string" + }, + "severity": { + "description": "The severity of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "severityCode": { + "description": "The severity code of the incident for IBM Resilient connectors.", + "type": "number" + }, + "sourceIp": { + "description": "A comma-separated list of source IPs for ServiceNow SecOps connectors.", + "type": "string" + }, + "subcategory": { + "description": "The subcategory of the incident for ServiceNow ITSM connectors.", + "type": "string" + }, + "urgency": { + "description": "The extent to which the incident resolution can be delayed for ServiceNow ITSM connectors.", + "type": "string" + } + }, + "required": [ + "fields", + "id", + "name", + "type" + ] + }, + "id": { + "description": "The identifier for the connector. To create a case without a connector, use `none`.", + "type": "string" + }, + "name": { + "description": "The name of the connector. To create a case without a connector, use `none`.", + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/connector_types" + } + } + }, + "created_at": { + "type": "string", + "format": "date-time", + "example": "2022-05-13T09:16:17.416Z" + }, + "created_by": { + "type": "object", + "properties": { + "email": { + "type": "string", + "example": "ahunley@imf.usa.gov" + }, + "full_name": { + "type": "string", + "example": "Alan Hunley" + }, + "username": { + "type": "string", + "example": "ahunley" + } + } + }, + "description": { + "type": "string", + "example": "James Bond clicked on a highly suspicious email banner advertising cheap holidays for underpaid civil servants. Operation bubblegum is active. Repeat - operation bubblegum is now active" + }, + "external_service": { + "type": "object", + "properties": { + "connector_id": { + "type": "string" + }, + "connector_name": { + "type": "string" + }, + "external_id": { + "type": "string" + }, + "external_title": { + "type": "string" + }, + "external_url": { + "type": "string" + }, + "pushed_at": { + "type": "string", + "format": "date-time" + }, + "pushed_by": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "nullable": true, + "example": null + } + } + }, + "id": { + "type": "string", + "example": "66b9aa00-94fa-11ea-9f74-e7e108796192" + }, + "owner": { + "$ref": "#/components/schemas/owners" + }, + "settings": { + "type": "object", + "properties": { + "syncAlerts": { + "type": "boolean", + "example": true + } + } + }, + "status": { + "$ref": "#/components/schemas/status" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "phishing", + "social engineering", + "bubblegum" + ] + }, + "title": { + "type": "string", + "example": "This case will self-destruct in 5 seconds" + }, + "totalAlerts": { + "type": "integer", + "example": 0 + }, + "totalComment": { + "type": "integer", + "example": 0 + }, + "updated_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "example": null + }, + "updated_by": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "nullable": true, + "example": null + }, + "version": { + "type": "string", + "example": "WzUzMiwxXQ==" + } + } + }, + "examples": { + "updateCaseResponse": { + "$ref": "#/components/examples/update_case_response" + } + } + } + } + } + }, + "servers": [ + { + "url": "https://localhost:5601" + } + ] + }, + "servers": [ + { + "url": "https://localhost:5601" + } + ] + } + }, + "components": { + "securitySchemes": { + "basicAuth": { + "type": "http", + "scheme": "basic" + }, + "apiKeyAuth": { + "type": "apiKey", + "in": "header", + "name": "ApiKey" + } + }, + "parameters": { + "kbn_xsrf": { + "schema": { + "type": "string" + }, + "in": "header", + "name": "kbn-xsrf", + "required": true + }, + "space_id": { + "in": "path", + "name": "spaceId", + "description": "An identifier for the space.", + "required": true, + "schema": { + "type": "string", + "example": "default" + } + } + }, + "schemas": { + "connector_types": { + "type": "string", + "description": "The type of connector.", + "enum": [ + ".jira", + ".none", + ".resilient", + ".servicenow", + ".servicenow-sir", + ".swimlane" + ] + }, + "owners": { + "type": "string", + "description": "Owner apps", + "enum": [ + "cases", + "observability", + "securitySolution" + ] + }, + "status": { + "type": "string", + "description": "The status of the case.", + "enum": [ + "closed", + "in-progress", + "open" + ] + } + }, + "examples": { + "create_case_request": { + "summary": "Create a security case that uses a Jira connector.", + "value": { + "description": "James Bond clicked on a highly suspicious email banner advertising cheap holidays for underpaid civil servants.", + "title": "This case will self-destruct in 5 seconds", + "tags": [ + "phishing", + "social engineering" + ], + "connector": { + "id": "131d4448-abe0-4789-939d-8ef60680b498", + "name": "My connector", + "type": ".jira", + "fields": { + "issueType": "10006", + "priority": "High", + "parent": null + } + }, + "settings": { + "syncAlerts": true + }, + "owner": "securitySolution" + } + }, + "create_case_response": { + "summary": "The create case API returns a JSON object that includes the user who created the case and the case identifier, version, and creation time.", + "value": { + "id": "66b9aa00-94fa-11ea-9f74-e7e108796192", + "version": "WzUzMiwxXQ==", + "comments": [], + "totalComment": 0, + "totalAlerts": 0, + "title": "This case will self-destruct in 5 seconds", + "tags": [ + "phishing", + "social engineering", + "bubblegum" + ], + "settings": { + "syncAlerts": true + }, + "owner": "securitySolution", + "description": "James Bond clicked on a highly suspicious email banner advertising cheap holidays for underpaid civil servants. Operation bubblegum is active. Repeat - operation bubblegum is now active", + "closed_at": null, + "closed_by": null, + "created_at": "2022-05-13T09:16:17.416Z", + "created_by": { + "email": "ahunley@imf.usa.gov", + "full_name": "Alan Hunley", + "username": "ahunley" + }, + "status": "open", + "updated_at": null, + "updated_by": null, + "connector": { + "id": "131d4448-abe0-4789-939d-8ef60680b498", + "name": "My connector", + "type": ".jira", + "fields": { + "issueType": "10006", + "parent": null, + "priority": "High" + } + }, + "external_service": null + } + }, + "update_case_request": { + "summary": "Update the case description, tags, and connector.", + "value": { + "cases": [ + { + "id": "a18b38a0-71b0-11ea-a0b2-c51ea50a58e2", + "version": "WzIzLDFd", + "connector": { + "id": "131d4448-abe0-4789-939d-8ef60680b498", + "name": "My connector", + "type": ".jira", + "fields": { + "issueType": "10006", + "priority": null, + "parent": null + } + }, + "description": "James Bond clicked on a highly suspicious email banner advertising cheap holidays for underpaid civil servants. Operation bubblegum is active. Repeat - operation bubblegum is now active!", + "tags": [ + "phishing", + "social engineering", + "bubblegum" + ], + "settings": { + "syncAlerts": true + } + } + ] + } + }, + "update_case_response": { + "summary": "This is an example response when the case description, tags, and connector were updated.", + "value": [ + { + "id": "66b9aa00-94fa-11ea-9f74-e7e108796192", + "version": "WzU0OCwxXQ==", + "comments": [], + "totalComment": 0, + "totalAlerts": 0, + "title": "This case will self-destruct in 5 seconds", + "tags": [ + "phishing", + "social engineering", + "bubblegum" + ], + "settings": { + "syncAlerts": true + }, + "owner": "securitySolution", + "description": "James Bond clicked on a highly suspicious email banner advertising cheap holidays for underpaid civil servants. Operation bubblegum is active. Repeat - operation bubblegum is now active!", + "closed_at": null, + "closed_by": null, + "created_at": "2022-05-13T09:16:17.416Z", + "created_by": { + "email": "ahunley@imf.usa.gov", + "full_name": "Alan Hunley", + "username": "ahunley" + }, + "status": "open", + "updated_at": "2022-05-13T09:48:33.043Z", + "updated_by": { + "email": "classified@hms.oo.gov.uk", + "full_name": "Classified", + "username": "M" + }, + "connector": { + "id": "131d4448-abe0-4789-939d-8ef60680b498", + "name": "My connector", + "type": ".jira", + "fields": { + "issueType": "10006", + "parent": null, + "priority": null + } + }, + "external_service": { + "external_title": "IS-4", + "pushed_by": { + "full_name": "Classified", + "email": "classified@hms.oo.gov.uk", + "username": "M" + }, + "external_url": "https://hms.atlassian.net/browse/IS-4", + "pushed_at": "2022-05-13T09:20:40.672Z", + "connector_id": "05da469f-1fde-4058-99a3-91e4807e2de8", + "external_id": "10003", + "connector_name": "Jira" + } + } + ] + } + } + }, + "security": [ + { + "basicAuth": [] + }, + { + "apiKeyAuth": [] + } + ] +} \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/bundled.yaml b/x-pack/plugins/cases/docs/openapi/bundled.yaml new file mode 100644 index 0000000000000..afad92f489a74 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/bundled.yaml @@ -0,0 +1,1811 @@ +openapi: 3.0.1 +info: + title: Cases + description: OpenAPI schema for Cases endpoints + version: '0.1' + contact: + name: Cases Team + license: + name: Elastic License 2.0 + url: https://www.elastic.co/licensing/elastic-license +tags: + - name: cases + description: Case APIs enable you to open and track issues. + - name: kibana + description: Kibana APIs enable you to interact with Kibana features. +servers: + - url: http://localhost:5601 + description: local +paths: + /api/cases: + post: + description: > + Creates a case. You must have all privileges for the **Cases** feature + in the **Management**, **Observability**, or **Security** section of the + Kibana feature privileges, depending on the owner of the case you're + creating. + tags: + - cases + - kibana + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + requestBody: + content: + application/json: + schema: + type: object + properties: + connector: + description: An object that contains the connector configuration. + type: object + properties: + fields: + description: >- + An object containing the connector fields. To create a + case without a connector, specify null. If you want to + omit any individual field, specify null as its value. + nullable: true + type: object + properties: + caseId: + description: The case identifier for Swimlane connectors. + type: string + category: + description: >- + The category of the incident for ServiceNow ITSM and + ServiceNow SecOps connectors. + type: string + destIp: + description: >- + A comma-separated list of destination IPs for + ServiceNow SecOps connectors. + type: string + impact: + description: >- + The effect an incident had on business for + ServiceNow ITSM connectors. + type: string + issueType: + description: The type of issue for Jira connectors. + type: string + issueTypes: + description: The type of incident for IBM Resilient connectors. + type: array + items: + type: number + malwareHash: + description: >- + A comma-separated list of malware hashes for + ServiceNow SecOps connectors. + type: string + malwareUrl: + description: >- + A comma-separated list of malware URLs for + ServiceNow SecOps connectors. + type: string + parent: + description: >- + The key of the parent issue, when the issue type is + sub-task for Jira connectors. + type: string + priority: + description: >- + The priority of the issue for Jira and ServiceNow + SecOps connectors. + type: string + severity: + description: >- + The severity of the incident for ServiceNow ITSM + connectors. + type: string + severityCode: + description: >- + The severity code of the incident for IBM Resilient + connectors. + type: number + sourceIp: + description: >- + A comma-separated list of source IPs for ServiceNow + SecOps connectors. + type: string + subcategory: + description: >- + The subcategory of the incident for ServiceNow ITSM + connectors. + type: string + urgency: + description: >- + The extent to which the incident resolution can be + delayed for ServiceNow ITSM connectors. + type: string + required: + - fields + - id + - name + - type + id: + description: >- + The identifier for the connector. To create a case + without a connector, use `none`. + type: string + name: + description: >- + The name of the connector. To create a case without a + connector, use `none`. + type: string + type: + $ref: '#/components/schemas/connector_types' + description: + description: The description for the case. + type: string + owner: + $ref: '#/components/schemas/owners' + settings: + description: An object that contains the case settings. + type: object + properties: + syncAlerts: + description: Turns alert syncing on or off. + type: boolean + tags: + description: >- + The words and phrases that help categorize cases. It can be + an empty array. + type: array + items: + type: string + title: + description: A title for the case. + type: string + required: + - connector + - description + - owner + - settings + - tags + - title + examples: + createCaseRequest: + $ref: '#/components/examples/create_case_request' + responses: + '200': + description: Indicates a successful call. + content: + application/json; charset=utf-8: + schema: + type: object + properties: + closed_at: + type: string + format: date-time + nullable: true + example: null + closed_by: + type: object + properties: + email: + type: string + full_name: + type: string + username: + type: string + nullable: true + example: null + comments: + type: array + items: + type: string + example: [] + connector: + type: object + properties: + fields: + description: >- + An object containing the connector fields. To create a + case without a connector, specify null. If you want to + omit any individual field, specify null as its value. + nullable: true + type: object + properties: + caseId: + description: The case identifier for Swimlane connectors. + type: string + category: + description: >- + The category of the incident for ServiceNow ITSM + and ServiceNow SecOps connectors. + type: string + destIp: + description: >- + A comma-separated list of destination IPs for + ServiceNow SecOps connectors. + type: string + impact: + description: >- + The effect an incident had on business for + ServiceNow ITSM connectors. + type: string + issueType: + description: The type of issue for Jira connectors. + type: string + issueTypes: + description: The type of incident for IBM Resilient connectors. + type: array + items: + type: number + malwareHash: + description: >- + A comma-separated list of malware hashes for + ServiceNow SecOps connectors. + type: string + malwareUrl: + description: >- + A comma-separated list of malware URLs for + ServiceNow SecOps connectors. + type: string + parent: + description: >- + The key of the parent issue, when the issue type + is sub-task for Jira connectors. + type: string + priority: + description: >- + The priority of the issue for Jira and ServiceNow + SecOps connectors. + type: string + severity: + description: >- + The severity of the incident for ServiceNow ITSM + connectors. + type: string + severityCode: + description: >- + The severity code of the incident for IBM + Resilient connectors. + type: number + sourceIp: + description: >- + A comma-separated list of source IPs for + ServiceNow SecOps connectors. + type: string + subcategory: + description: >- + The subcategory of the incident for ServiceNow + ITSM connectors. + type: string + urgency: + description: >- + The extent to which the incident resolution can be + delayed for ServiceNow ITSM connectors. + type: string + required: + - fields + - id + - name + - type + id: + description: >- + The identifier for the connector. To create a case + without a connector, use `none`. + type: string + name: + description: >- + The name of the connector. To create a case without a + connector, use `none`. + type: string + type: + $ref: '#/components/schemas/connector_types' + created_at: + type: string + format: date-time + example: '2022-05-13T09:16:17.416Z' + created_by: + type: object + properties: + email: + type: string + example: ahunley@imf.usa.gov + full_name: + type: string + example: Alan Hunley + username: + type: string + example: ahunley + description: + type: string + example: >- + James Bond clicked on a highly suspicious email banner + advertising cheap holidays for underpaid civil servants. + Operation bubblegum is active. Repeat - operation + bubblegum is now active + external_service: + type: object + properties: + connector_id: + type: string + connector_name: + type: string + external_id: + type: string + external_title: + type: string + external_url: + type: string + pushed_at: + type: string + format: date-time + pushed_by: + type: object + properties: + email: + type: string + full_name: + type: string + username: + type: string + nullable: true + example: null + id: + type: string + example: 66b9aa00-94fa-11ea-9f74-e7e108796192 + owner: + $ref: '#/components/schemas/owners' + settings: + type: object + properties: + syncAlerts: + type: boolean + example: true + status: + $ref: '#/components/schemas/status' + tags: + type: array + items: + type: string + example: + - phishing + - social engineering + - bubblegum + title: + type: string + example: This case will self-destruct in 5 seconds + totalAlerts: + type: integer + example: 0 + totalComment: + type: integer + example: 0 + updated_at: + type: string + format: date-time + nullable: true + example: null + updated_by: + type: object + properties: + email: + type: string + full_name: + type: string + username: + type: string + nullable: true + example: null + version: + type: string + example: WzUzMiwxXQ== + examples: + createCaseResponse: + $ref: '#/components/examples/create_case_response' + servers: + - url: https://localhost:5601 + delete: + description: > + Deletes one or more cases. You must have all privileges for the + **Cases** feature in the **Management**, **Observability**, or + **Security** section of the Kibana feature privileges, depending on the + owner of the cases you're deleting. + tags: + - cases + - kibana + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - name: ids + description: >- + The cases that you want to removed. To retrieve case IDs, use the + find cases API. All non-ASCII characters must be URL encoded. + in: query + required: true + schema: + type: string + example: d4e7abb0-b462-11ec-9a8d-698504725a43 + responses: + '204': + description: Indicates a successful call. + servers: + - url: https://localhost:5601 + patch: + description: > + Updates one or more cases. You must have all privileges for the + **Cases** feature in the **Management**, **Observability**, or + **Security** section of the Kibana feature privileges, depending on the + owner of the case you're updating. + tags: + - cases + - kibana + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + requestBody: + content: + application/json: + schema: + type: object + properties: + cases: + type: array + items: + type: object + properties: + connector: + description: An object that contains the connector configuration. + type: object + properties: + fields: + description: >- + An object containing the connector fields. To + create a case without a connector, specify null. + If you want to omit any individual field, specify + null as its value. + nullable: true + type: object + properties: + caseId: + description: The case identifier for Swimlane connectors. + type: string + category: + description: >- + The category of the incident for ServiceNow + ITSM and ServiceNow SecOps connectors. + type: string + destIp: + description: >- + A comma-separated list of destination IPs for + ServiceNow SecOps connectors. + type: string + impact: + description: >- + The effect an incident had on business for + ServiceNow ITSM connectors. + type: string + issueType: + description: The type of issue for Jira connectors. + type: string + issueTypes: + description: >- + The type of incident for IBM Resilient + connectors. + type: array + items: + type: number + malwareHash: + description: >- + A comma-separated list of malware hashes for + ServiceNow SecOps connectors. + type: string + malwareUrl: + description: >- + A comma-separated list of malware URLs for + ServiceNow SecOps connectors. + type: string + parent: + description: >- + The key of the parent issue, when the issue + type is sub-task for Jira connectors. + type: string + priority: + description: >- + The priority of the issue for Jira and + ServiceNow SecOps connectors. + type: string + severity: + description: >- + The severity of the incident for ServiceNow + ITSM connectors. + type: string + severityCode: + description: >- + The severity code of the incident for IBM + Resilient connectors. + type: number + sourceIp: + description: >- + A comma-separated list of source IPs for + ServiceNow SecOps connectors. + type: string + subcategory: + description: >- + The subcategory of the incident for ServiceNow + ITSM connectors. + type: string + urgency: + description: >- + The extent to which the incident resolution + can be delayed for ServiceNow ITSM connectors. + type: string + required: + - fields + - id + - name + - type + id: + description: >- + The identifier for the connector. To create a case + without a connector, use `none`. + type: string + name: + description: >- + The name of the connector. To create a case + without a connector, use `none`. + type: string + type: + $ref: '#/components/schemas/connector_types' + description: + description: The description for the case. + type: string + id: + description: The identifier for the case. + type: string + settings: + description: An object that contains the case settings. + type: object + properties: + syncAlerts: + description: Turns alert syncing on or off. + type: boolean + status: + $ref: '#/components/schemas/status' + tags: + description: The words and phrases that help categorize cases. + type: array + items: + type: string + title: + description: A title for the case. + type: string + version: + description: The current version of the case. + type: string + required: + - id + - version + examples: + updateCaseRequest: + $ref: '#/components/examples/update_case_request' + responses: + '200': + description: Indicates a successful call. + content: + application/json; charset=utf-8: + schema: + type: object + properties: + closed_at: + type: string + format: date-time + nullable: true + example: null + closed_by: + type: object + properties: + email: + type: string + full_name: + type: string + username: + type: string + nullable: true + example: null + comments: + type: array + items: + type: string + example: [] + connector: + type: object + properties: + fields: + description: >- + An object containing the connector fields. To create a + case without a connector, specify null. If you want to + omit any individual field, specify null as its value. + nullable: true + type: object + properties: + caseId: + description: The case identifier for Swimlane connectors. + type: string + category: + description: >- + The category of the incident for ServiceNow ITSM + and ServiceNow SecOps connectors. + type: string + destIp: + description: >- + A comma-separated list of destination IPs for + ServiceNow SecOps connectors. + type: string + impact: + description: >- + The effect an incident had on business for + ServiceNow ITSM connectors. + type: string + issueType: + description: The type of issue for Jira connectors. + type: string + issueTypes: + description: The type of incident for IBM Resilient connectors. + type: array + items: + type: number + malwareHash: + description: >- + A comma-separated list of malware hashes for + ServiceNow SecOps connectors. + type: string + malwareUrl: + description: >- + A comma-separated list of malware URLs for + ServiceNow SecOps connectors. + type: string + parent: + description: >- + The key of the parent issue, when the issue type + is sub-task for Jira connectors. + type: string + priority: + description: >- + The priority of the issue for Jira and ServiceNow + SecOps connectors. + type: string + severity: + description: >- + The severity of the incident for ServiceNow ITSM + connectors. + type: string + severityCode: + description: >- + The severity code of the incident for IBM + Resilient connectors. + type: number + sourceIp: + description: >- + A comma-separated list of source IPs for + ServiceNow SecOps connectors. + type: string + subcategory: + description: >- + The subcategory of the incident for ServiceNow + ITSM connectors. + type: string + urgency: + description: >- + The extent to which the incident resolution can be + delayed for ServiceNow ITSM connectors. + type: string + required: + - fields + - id + - name + - type + id: + description: >- + The identifier for the connector. To create a case + without a connector, use `none`. + type: string + name: + description: >- + The name of the connector. To create a case without a + connector, use `none`. + type: string + type: + $ref: '#/components/schemas/connector_types' + created_at: + type: string + format: date-time + example: '2022-05-13T09:16:17.416Z' + created_by: + type: object + properties: + email: + type: string + example: ahunley@imf.usa.gov + full_name: + type: string + example: Alan Hunley + username: + type: string + example: ahunley + description: + type: string + example: >- + James Bond clicked on a highly suspicious email banner + advertising cheap holidays for underpaid civil servants. + Operation bubblegum is active. Repeat - operation + bubblegum is now active + external_service: + type: object + properties: + connector_id: + type: string + connector_name: + type: string + external_id: + type: string + external_title: + type: string + external_url: + type: string + pushed_at: + type: string + format: date-time + pushed_by: + type: object + properties: + email: + type: string + full_name: + type: string + username: + type: string + nullable: true + example: null + id: + type: string + example: 66b9aa00-94fa-11ea-9f74-e7e108796192 + owner: + $ref: '#/components/schemas/owners' + settings: + type: object + properties: + syncAlerts: + type: boolean + example: true + status: + $ref: '#/components/schemas/status' + tags: + type: array + items: + type: string + example: + - phishing + - social engineering + - bubblegum + title: + type: string + example: This case will self-destruct in 5 seconds + totalAlerts: + type: integer + example: 0 + totalComment: + type: integer + example: 0 + updated_at: + type: string + format: date-time + nullable: true + example: null + updated_by: + type: object + properties: + email: + type: string + full_name: + type: string + username: + type: string + nullable: true + example: null + version: + type: string + example: WzUzMiwxXQ== + examples: + updateCaseResponse: + $ref: '#/components/examples/update_case_response' + servers: + - url: https://localhost:5601 + servers: + - url: https://localhost:5601 + /s/{spaceId}/api/cases: + post: + description: > + Creates a case. You must have all privileges for the **Cases** feature + in the **Management**, **Observability**, or **Security** section of the + Kibana feature privileges, depending on the owner of the case you're + creating. + tags: + - cases + - kibana + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/space_id' + requestBody: + content: + application/json: + schema: + type: object + properties: + connector: + description: An object that contains the connector configuration. + type: object + properties: + fields: + description: >- + An object containing the connector fields. To create a + case without a connector, specify null. If you want to + omit any individual field, specify null as its value. + nullable: true + type: object + properties: + caseId: + description: The case identifier for Swimlane connectors. + type: string + category: + description: >- + The category of the incident for ServiceNow ITSM and + ServiceNow SecOps connectors. + type: string + destIp: + description: >- + A comma-separated list of destination IPs for + ServiceNow SecOps connectors. + type: string + impact: + description: >- + The effect an incident had on business for + ServiceNow ITSM connectors. + type: string + issueType: + description: The type of issue for Jira connectors. + type: string + issueTypes: + description: The type of incident for IBM Resilient connectors. + type: array + items: + type: number + malwareHash: + description: >- + A comma-separated list of malware hashes for + ServiceNow SecOps connectors. + type: string + malwareUrl: + description: >- + A comma-separated list of malware URLs for + ServiceNow SecOps connectors. + type: string + parent: + description: >- + The key of the parent issue, when the issue type is + sub-task for Jira connectors. + type: string + priority: + description: >- + The priority of the issue for Jira and ServiceNow + SecOps connectors. + type: string + severity: + description: >- + The severity of the incident for ServiceNow ITSM + connectors. + type: string + severityCode: + description: >- + The severity code of the incident for IBM Resilient + connectors. + type: number + sourceIp: + description: >- + A comma-separated list of source IPs for ServiceNow + SecOps connectors. + type: string + subcategory: + description: >- + The subcategory of the incident for ServiceNow ITSM + connectors. + type: string + urgency: + description: >- + The extent to which the incident resolution can be + delayed for ServiceNow ITSM connectors. + type: string + required: + - fields + - id + - name + - type + id: + description: >- + The identifier for the connector. To create a case + without a connector, use `none`. + type: string + name: + description: >- + The name of the connector. To create a case without a + connector, use `none`. + type: string + type: + $ref: '#/components/schemas/connector_types' + description: + description: The description for the case. + type: string + owner: + $ref: '#/components/schemas/owners' + settings: + description: An object that contains the case settings. + type: object + properties: + syncAlerts: + description: Turns alert syncing on or off. + type: boolean + tags: + description: >- + The words and phrases that help categorize cases. It can be + an empty array. + type: array + items: + type: string + title: + description: A title for the case. + type: string + required: + - connector + - description + - owner + - settings + - tags + - title + examples: + createCaseRequest: + $ref: '#/components/examples/create_case_request' + responses: + '200': + description: Indicates a successful call. + content: + application/json; charset=utf-8: + schema: + type: object + properties: + closed_at: + type: string + format: date-time + nullable: true + example: null + closed_by: + type: object + properties: + email: + type: string + full_name: + type: string + username: + type: string + nullable: true + example: null + comments: + type: array + items: + type: string + example: [] + connector: + type: object + properties: + fields: + description: >- + An object containing the connector fields. To create a + case without a connector, specify null. If you want to + omit any individual field, specify null as its value. + nullable: true + type: object + properties: + caseId: + description: The case identifier for Swimlane connectors. + type: string + category: + description: >- + The category of the incident for ServiceNow ITSM + and ServiceNow SecOps connectors. + type: string + destIp: + description: >- + A comma-separated list of destination IPs for + ServiceNow SecOps connectors. + type: string + impact: + description: >- + The effect an incident had on business for + ServiceNow ITSM connectors. + type: string + issueType: + description: The type of issue for Jira connectors. + type: string + issueTypes: + description: The type of incident for IBM Resilient connectors. + type: array + items: + type: number + malwareHash: + description: >- + A comma-separated list of malware hashes for + ServiceNow SecOps connectors. + type: string + malwareUrl: + description: >- + A comma-separated list of malware URLs for + ServiceNow SecOps connectors. + type: string + parent: + description: >- + The key of the parent issue, when the issue type + is sub-task for Jira connectors. + type: string + priority: + description: >- + The priority of the issue for Jira and ServiceNow + SecOps connectors. + type: string + severity: + description: >- + The severity of the incident for ServiceNow ITSM + connectors. + type: string + severityCode: + description: >- + The severity code of the incident for IBM + Resilient connectors. + type: number + sourceIp: + description: >- + A comma-separated list of source IPs for + ServiceNow SecOps connectors. + type: string + subcategory: + description: >- + The subcategory of the incident for ServiceNow + ITSM connectors. + type: string + urgency: + description: >- + The extent to which the incident resolution can be + delayed for ServiceNow ITSM connectors. + type: string + required: + - fields + - id + - name + - type + id: + description: >- + The identifier for the connector. To create a case + without a connector, use `none`. + type: string + name: + description: >- + The name of the connector. To create a case without a + connector, use `none`. + type: string + type: + $ref: '#/components/schemas/connector_types' + created_at: + type: string + format: date-time + example: '2022-05-13T09:16:17.416Z' + created_by: + type: object + properties: + email: + type: string + example: ahunley@imf.usa.gov + full_name: + type: string + example: Alan Hunley + username: + type: string + example: ahunley + description: + type: string + example: >- + James Bond clicked on a highly suspicious email banner + advertising cheap holidays for underpaid civil servants. + Operation bubblegum is active. Repeat - operation + bubblegum is now active + external_service: + type: object + properties: + connector_id: + type: string + connector_name: + type: string + external_id: + type: string + external_title: + type: string + external_url: + type: string + pushed_at: + type: string + format: date-time + pushed_by: + type: object + properties: + email: + type: string + full_name: + type: string + username: + type: string + nullable: true + example: null + id: + type: string + example: 66b9aa00-94fa-11ea-9f74-e7e108796192 + owner: + $ref: '#/components/schemas/owners' + settings: + type: object + properties: + syncAlerts: + type: boolean + example: true + status: + $ref: '#/components/schemas/status' + tags: + type: array + items: + type: string + example: + - phishing + - social engineering + - bubblegum + title: + type: string + example: This case will self-destruct in 5 seconds + totalAlerts: + type: integer + example: 0 + totalComment: + type: integer + example: 0 + updated_at: + type: string + format: date-time + nullable: true + example: null + updated_by: + type: object + properties: + email: + type: string + full_name: + type: string + username: + type: string + nullable: true + example: null + version: + type: string + example: WzUzMiwxXQ== + examples: + createCaseResponse: + $ref: '#/components/examples/create_case_response' + servers: + - url: https://localhost:5601 + delete: + description: > + Deletes one or more cases. You must have all privileges for the + **Cases** feature in the **Management**, **Observability**, or + **Security** section of the Kibana feature privileges, depending on the + owner of the cases you're deleting. + tags: + - cases + - kibana + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/space_id' + - name: ids + description: >- + The cases that you want to removed. All non-ASCII characters must be + URL encoded. + in: query + required: true + schema: + type: string + example: d4e7abb0-b462-11ec-9a8d-698504725a43 + responses: + '204': + description: Indicates a successful call. + servers: + - url: https://localhost:5601 + patch: + description: > + Updates one or more cases. You must have all privileges for the + **Cases** feature in the **Management**, **Observability**, or + **Security** section of the Kibana feature privileges, depending on the + owner of the case you're updating. + tags: + - cases + - kibana + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/space_id' + requestBody: + content: + application/json: + schema: + type: object + properties: + cases: + type: array + items: + type: object + properties: + connector: + description: An object that contains the connector configuration. + type: object + properties: + fields: + description: >- + An object containing the connector fields. To + create a case without a connector, specify null. + If you want to omit any individual field, specify + null as its value. + nullable: true + type: object + properties: + caseId: + description: The case identifier for Swimlane connectors. + type: string + category: + description: >- + The category of the incident for ServiceNow + ITSM and ServiceNow SecOps connectors. + type: string + destIp: + description: >- + A comma-separated list of destination IPs for + ServiceNow SecOps connectors. + type: string + impact: + description: >- + The effect an incident had on business for + ServiceNow ITSM connectors. + type: string + issueType: + description: The type of issue for Jira connectors. + type: string + issueTypes: + description: >- + The type of incident for IBM Resilient + connectors. + type: array + items: + type: number + malwareHash: + description: >- + A comma-separated list of malware hashes for + ServiceNow SecOps connectors. + type: string + malwareUrl: + description: >- + A comma-separated list of malware URLs for + ServiceNow SecOps connectors. + type: string + parent: + description: >- + The key of the parent issue, when the issue + type is sub-task for Jira connectors. + type: string + priority: + description: >- + The priority of the issue for Jira and + ServiceNow SecOps connectors. + type: string + severity: + description: >- + The severity of the incident for ServiceNow + ITSM connectors. + type: string + severityCode: + description: >- + The severity code of the incident for IBM + Resilient connectors. + type: number + sourceIp: + description: >- + A comma-separated list of source IPs for + ServiceNow SecOps connectors. + type: string + subcategory: + description: >- + The subcategory of the incident for ServiceNow + ITSM connectors. + type: string + urgency: + description: >- + The extent to which the incident resolution + can be delayed for ServiceNow ITSM connectors. + type: string + required: + - fields + - id + - name + - type + id: + description: >- + The identifier for the connector. To create a case + without a connector, use `none`. + type: string + name: + description: >- + The name of the connector. To create a case + without a connector, use `none`. + type: string + type: + $ref: '#/components/schemas/connector_types' + description: + description: The description for the case. + type: string + id: + description: The identifier for the case. + type: string + settings: + description: An object that contains the case settings. + type: object + properties: + syncAlerts: + description: Turns alert syncing on or off. + type: boolean + status: + $ref: '#/components/schemas/status' + tags: + description: The words and phrases that help categorize cases. + type: array + items: + type: string + title: + description: A title for the case. + type: string + version: + description: The current version of the case. + type: string + required: + - id + - version + examples: + updateCaseRequest: + $ref: '#/components/examples/update_case_request' + responses: + '200': + description: Indicates a successful call. + content: + application/json; charset=utf-8: + schema: + type: object + properties: + closed_at: + type: string + format: date-time + nullable: true + example: null + closed_by: + type: object + properties: + email: + type: string + full_name: + type: string + username: + type: string + nullable: true + example: null + comments: + type: array + items: + type: string + example: [] + connector: + type: object + properties: + fields: + description: >- + An object containing the connector fields. To create a + case without a connector, specify null. If you want to + omit any individual field, specify null as its value. + nullable: true + type: object + properties: + caseId: + description: The case identifier for Swimlane connectors. + type: string + category: + description: >- + The category of the incident for ServiceNow ITSM + and ServiceNow SecOps connectors. + type: string + destIp: + description: >- + A comma-separated list of destination IPs for + ServiceNow SecOps connectors. + type: string + impact: + description: >- + The effect an incident had on business for + ServiceNow ITSM connectors. + type: string + issueType: + description: The type of issue for Jira connectors. + type: string + issueTypes: + description: The type of incident for IBM Resilient connectors. + type: array + items: + type: number + malwareHash: + description: >- + A comma-separated list of malware hashes for + ServiceNow SecOps connectors. + type: string + malwareUrl: + description: >- + A comma-separated list of malware URLs for + ServiceNow SecOps connectors. + type: string + parent: + description: >- + The key of the parent issue, when the issue type + is sub-task for Jira connectors. + type: string + priority: + description: >- + The priority of the issue for Jira and ServiceNow + SecOps connectors. + type: string + severity: + description: >- + The severity of the incident for ServiceNow ITSM + connectors. + type: string + severityCode: + description: >- + The severity code of the incident for IBM + Resilient connectors. + type: number + sourceIp: + description: >- + A comma-separated list of source IPs for + ServiceNow SecOps connectors. + type: string + subcategory: + description: >- + The subcategory of the incident for ServiceNow + ITSM connectors. + type: string + urgency: + description: >- + The extent to which the incident resolution can be + delayed for ServiceNow ITSM connectors. + type: string + required: + - fields + - id + - name + - type + id: + description: >- + The identifier for the connector. To create a case + without a connector, use `none`. + type: string + name: + description: >- + The name of the connector. To create a case without a + connector, use `none`. + type: string + type: + $ref: '#/components/schemas/connector_types' + created_at: + type: string + format: date-time + example: '2022-05-13T09:16:17.416Z' + created_by: + type: object + properties: + email: + type: string + example: ahunley@imf.usa.gov + full_name: + type: string + example: Alan Hunley + username: + type: string + example: ahunley + description: + type: string + example: >- + James Bond clicked on a highly suspicious email banner + advertising cheap holidays for underpaid civil servants. + Operation bubblegum is active. Repeat - operation + bubblegum is now active + external_service: + type: object + properties: + connector_id: + type: string + connector_name: + type: string + external_id: + type: string + external_title: + type: string + external_url: + type: string + pushed_at: + type: string + format: date-time + pushed_by: + type: object + properties: + email: + type: string + full_name: + type: string + username: + type: string + nullable: true + example: null + id: + type: string + example: 66b9aa00-94fa-11ea-9f74-e7e108796192 + owner: + $ref: '#/components/schemas/owners' + settings: + type: object + properties: + syncAlerts: + type: boolean + example: true + status: + $ref: '#/components/schemas/status' + tags: + type: array + items: + type: string + example: + - phishing + - social engineering + - bubblegum + title: + type: string + example: This case will self-destruct in 5 seconds + totalAlerts: + type: integer + example: 0 + totalComment: + type: integer + example: 0 + updated_at: + type: string + format: date-time + nullable: true + example: null + updated_by: + type: object + properties: + email: + type: string + full_name: + type: string + username: + type: string + nullable: true + example: null + version: + type: string + example: WzUzMiwxXQ== + examples: + updateCaseResponse: + $ref: '#/components/examples/update_case_response' + servers: + - url: https://localhost:5601 + servers: + - url: https://localhost:5601 +components: + securitySchemes: + basicAuth: + type: http + scheme: basic + apiKeyAuth: + type: apiKey + in: header + name: ApiKey + parameters: + kbn_xsrf: + schema: + type: string + in: header + name: kbn-xsrf + required: true + space_id: + in: path + name: spaceId + description: An identifier for the space. + required: true + schema: + type: string + example: default + schemas: + connector_types: + type: string + description: The type of connector. + enum: + - .jira + - .none + - .resilient + - .servicenow + - .servicenow-sir + - .swimlane + owners: + type: string + description: Owner apps + enum: + - cases + - observability + - securitySolution + status: + type: string + description: The status of the case. + enum: + - closed + - in-progress + - open + examples: + create_case_request: + summary: Create a security case that uses a Jira connector. + value: + description: >- + James Bond clicked on a highly suspicious email banner advertising + cheap holidays for underpaid civil servants. + title: This case will self-destruct in 5 seconds + tags: + - phishing + - social engineering + connector: + id: 131d4448-abe0-4789-939d-8ef60680b498 + name: My connector + type: .jira + fields: + issueType: '10006' + priority: High + parent: null + settings: + syncAlerts: true + owner: securitySolution + create_case_response: + summary: >- + The create case API returns a JSON object that includes the user who + created the case and the case identifier, version, and creation time. + value: + id: 66b9aa00-94fa-11ea-9f74-e7e108796192 + version: WzUzMiwxXQ== + comments: [] + totalComment: 0 + totalAlerts: 0 + title: This case will self-destruct in 5 seconds + tags: + - phishing + - social engineering + - bubblegum + settings: + syncAlerts: true + owner: securitySolution + description: >- + James Bond clicked on a highly suspicious email banner advertising + cheap holidays for underpaid civil servants. Operation bubblegum is + active. Repeat - operation bubblegum is now active + closed_at: null + closed_by: null + created_at: '2022-05-13T09:16:17.416Z' + created_by: + email: ahunley@imf.usa.gov + full_name: Alan Hunley + username: ahunley + status: open + updated_at: null + updated_by: null + connector: + id: 131d4448-abe0-4789-939d-8ef60680b498 + name: My connector + type: .jira + fields: + issueType: '10006' + parent: null + priority: High + external_service: null + update_case_request: + summary: Update the case description, tags, and connector. + value: + cases: + - id: a18b38a0-71b0-11ea-a0b2-c51ea50a58e2 + version: WzIzLDFd + connector: + id: 131d4448-abe0-4789-939d-8ef60680b498 + name: My connector + type: .jira + fields: + issueType: '10006' + priority: null + parent: null + description: >- + James Bond clicked on a highly suspicious email banner advertising + cheap holidays for underpaid civil servants. Operation bubblegum + is active. Repeat - operation bubblegum is now active! + tags: + - phishing + - social engineering + - bubblegum + settings: + syncAlerts: true + update_case_response: + summary: >- + This is an example response when the case description, tags, and + connector were updated. + value: + - id: 66b9aa00-94fa-11ea-9f74-e7e108796192 + version: WzU0OCwxXQ== + comments: [] + totalComment: 0 + totalAlerts: 0 + title: This case will self-destruct in 5 seconds + tags: + - phishing + - social engineering + - bubblegum + settings: + syncAlerts: true + owner: securitySolution + description: >- + James Bond clicked on a highly suspicious email banner advertising + cheap holidays for underpaid civil servants. Operation bubblegum is + active. Repeat - operation bubblegum is now active! + closed_at: null + closed_by: null + created_at: '2022-05-13T09:16:17.416Z' + created_by: + email: ahunley@imf.usa.gov + full_name: Alan Hunley + username: ahunley + status: open + updated_at: '2022-05-13T09:48:33.043Z' + updated_by: + email: classified@hms.oo.gov.uk + full_name: Classified + username: M + connector: + id: 131d4448-abe0-4789-939d-8ef60680b498 + name: My connector + type: .jira + fields: + issueType: '10006' + parent: null + priority: null + external_service: + external_title: IS-4 + pushed_by: + full_name: Classified + email: classified@hms.oo.gov.uk + username: M + external_url: https://hms.atlassian.net/browse/IS-4 + pushed_at: '2022-05-13T09:20:40.672Z' + connector_id: 05da469f-1fde-4058-99a3-91e4807e2de8 + external_id: '10003' + connector_name: Jira +security: + - basicAuth: [] + - apiKeyAuth: [] diff --git a/x-pack/plugins/cases/docs/openapi/components/README.md b/x-pack/plugins/cases/docs/openapi/components/README.md new file mode 100644 index 0000000000000..0841562a33150 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/README.md @@ -0,0 +1,7 @@ +Reusable components +=========== + + - `examples` - reusable [Example objects](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.2.md#exampleObject) + - `headers` - reusable [Header objects](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#headerObject) + - `parameters` - reusable [Parameter objects](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#parameterObject) + - `schemas` - reusable [Schema objects](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.2.md#schemaObject) diff --git a/x-pack/plugins/cases/docs/openapi/components/examples/create_case_request.yaml b/x-pack/plugins/cases/docs/openapi/components/examples/create_case_request.yaml new file mode 100644 index 0000000000000..0659ed18a8569 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/examples/create_case_request.yaml @@ -0,0 +1,21 @@ +summary: Create a security case that uses a Jira connector. +value: + { + "description": "James Bond clicked on a highly suspicious email banner advertising cheap holidays for underpaid civil servants.", + "title": "This case will self-destruct in 5 seconds", + "tags": [ "phishing","social engineering"], + "connector": { + "id": "131d4448-abe0-4789-939d-8ef60680b498", + "name": "My connector", + "type": ".jira", + "fields": { + "issueType": "10006", + "priority": "High", + "parent": null + } + }, + "settings": { + "syncAlerts": true + }, + "owner": "securitySolution" + } diff --git a/x-pack/plugins/cases/docs/openapi/components/examples/create_case_response.yaml b/x-pack/plugins/cases/docs/openapi/components/examples/create_case_response.yaml new file mode 100644 index 0000000000000..f9f2ce3d61beb --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/examples/create_case_response.yaml @@ -0,0 +1,42 @@ +summary: The create case API returns a JSON object that includes the user who created the case and the case identifier, version, and creation time. +value: + { + "id": "66b9aa00-94fa-11ea-9f74-e7e108796192", + "version": "WzUzMiwxXQ==", + "comments": [], + "totalComment": 0, + "totalAlerts": 0, + "title": "This case will self-destruct in 5 seconds", + "tags": [ + "phishing", + "social engineering", + "bubblegum" + ], + "settings": { + "syncAlerts": true + }, + "owner": "securitySolution", + "description": "James Bond clicked on a highly suspicious email banner advertising cheap holidays for underpaid civil servants. Operation bubblegum is active. Repeat - operation bubblegum is now active", + "closed_at": null, + "closed_by": null, + "created_at": "2022-05-13T09:16:17.416Z", + "created_by": { + "email": "ahunley@imf.usa.gov", + "full_name": "Alan Hunley", + "username": "ahunley" + }, + "status": "open", + "updated_at": null, + "updated_by": null, + "connector": { + "id": "131d4448-abe0-4789-939d-8ef60680b498", + "name": "My connector", + "type": ".jira", + "fields": { + "issueType": "10006", + "parent": null, + "priority": "High" + } + }, + "external_service": null + } diff --git a/x-pack/plugins/cases/docs/openapi/components/examples/update_case_request.yaml b/x-pack/plugins/cases/docs/openapi/components/examples/update_case_request.yaml new file mode 100644 index 0000000000000..7ecb306cf0735 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/examples/update_case_request.yaml @@ -0,0 +1,29 @@ +summary: Update the case description, tags, and connector. +value: + { + "cases": [ + { + "id": "a18b38a0-71b0-11ea-a0b2-c51ea50a58e2", + "version": "WzIzLDFd", + "connector": { + "id": "131d4448-abe0-4789-939d-8ef60680b498", + "name": "My connector", + "type": ".jira", + "fields": { + "issueType": "10006", + "priority": null, + "parent": null + } + }, + "description": "James Bond clicked on a highly suspicious email banner advertising cheap holidays for underpaid civil servants. Operation bubblegum is active. Repeat - operation bubblegum is now active!", + "tags": [ + "phishing", + "social engineering", + "bubblegum" + ], + "settings": { + "syncAlerts": true + } + } + ] +} \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/examples/update_case_response.yaml b/x-pack/plugins/cases/docs/openapi/components/examples/update_case_response.yaml new file mode 100644 index 0000000000000..a73191868c8ee --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/examples/update_case_response.yaml @@ -0,0 +1,60 @@ +summary: This is an example response when the case description, tags, and connector were updated. +value: + [ + { + "id": "66b9aa00-94fa-11ea-9f74-e7e108796192", + "version": "WzU0OCwxXQ==", + "comments": [], + "totalComment": 0, + "totalAlerts": 0, + "title": "This case will self-destruct in 5 seconds", + "tags": [ + "phishing", + "social engineering", + "bubblegum" + ], + "settings": { + "syncAlerts": true + }, + "owner": "securitySolution", + "description": "James Bond clicked on a highly suspicious email banner advertising cheap holidays for underpaid civil servants. Operation bubblegum is active. Repeat - operation bubblegum is now active!", + "closed_at": null, + "closed_by": null, + "created_at": "2022-05-13T09:16:17.416Z", + "created_by": { + "email": "ahunley@imf.usa.gov", + "full_name": "Alan Hunley", + "username": "ahunley" + }, + "status": "open", + "updated_at": "2022-05-13T09:48:33.043Z", + "updated_by": { + "email": "classified@hms.oo.gov.uk", + "full_name": "Classified", + "username": "M" + }, + "connector": { + "id": "131d4448-abe0-4789-939d-8ef60680b498", + "name": "My connector", + "type": ".jira", + "fields": { + "issueType": "10006", + "parent": null, + "priority": null, + } + }, + "external_service": { + "external_title": "IS-4", + "pushed_by": { + "full_name": "Classified", + "email": "classified@hms.oo.gov.uk", + "username": "M" + }, + "external_url": "https://hms.atlassian.net/browse/IS-4", + "pushed_at": "2022-05-13T09:20:40.672Z", + "connector_id": "05da469f-1fde-4058-99a3-91e4807e2de8", + "external_id": "10003", + "connector_name": "Jira" + } + } + ] \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/headers/kbn_xsrf.yaml b/x-pack/plugins/cases/docs/openapi/components/headers/kbn_xsrf.yaml new file mode 100644 index 0000000000000..3d8dfae634e68 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/headers/kbn_xsrf.yaml @@ -0,0 +1,5 @@ +schema: + type: string +in: header +name: kbn-xsrf +required: true diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/space_id.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/space_id.yaml new file mode 100644 index 0000000000000..0ff325b08a082 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/space_id.yaml @@ -0,0 +1,7 @@ +in: path +name: spaceId +description: An identifier for the space. +required: true +schema: + type: string + example: default diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_properties.yaml new file mode 100644 index 0000000000000..780496f1591b4 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_properties.yaml @@ -0,0 +1,117 @@ +closed_at: + type: string + format: date-time + nullable: true + example: null +closed_by: + type: object + properties: + email: + type: string + full_name: + type: string + username: + type: string + nullable: true + example: null +comments: + type: array + items: + type: string + example: [] +connector: + type: object + properties: + $ref: 'connector_properties.yaml' +created_at: + type: string + format: date-time + example: "2022-05-13T09:16:17.416Z" +created_by: + type: object + properties: + email: + type: string + example: "ahunley@imf.usa.gov" + full_name: + type: string + example: "Alan Hunley" + username: + type: string + example: "ahunley" +description: + type: string + example: "James Bond clicked on a highly suspicious email banner advertising cheap holidays for underpaid civil servants. Operation bubblegum is active. Repeat - operation bubblegum is now active" +external_service: + type: object + properties: + connector_id: + type: string + connector_name: + type: string + external_id: + type: string + external_title: + type: string + external_url: + type: string + pushed_at: + type: string + format: date-time + pushed_by: + type: object + properties: + email: + type: string + full_name: + type: string + username: + type: string + nullable: true + example: null +id: + type: string + example: "66b9aa00-94fa-11ea-9f74-e7e108796192" +owner: + $ref: 'owners.yaml' +settings: + type: object + properties: + syncAlerts: + type: boolean + example: true +status: + $ref: 'status.yaml' +tags: + type: array + items: + type: string + example: ["phishing","social engineering","bubblegum"] +title: + type: string + example: "This case will self-destruct in 5 seconds" +totalAlerts: + type: integer + example: 0 +totalComment: + type: integer + example: 0 +updated_at: + type: string + format: date-time + nullable: true + example: null +updated_by: + type: object + properties: + email: + type: string + full_name: + type: string + username: + type: string + nullable: true + example: null +version: + type: string + example: "WzUzMiwxXQ==" diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/closure_types.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/closure_types.yaml new file mode 100644 index 0000000000000..f09063d0db18f --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/closure_types.yaml @@ -0,0 +1,5 @@ +type: string +description: Indicates whether a case is automatically closed when it is pushed to external systems (`close-by-pushing`) or not automatically closed (`close-by-user`). +enum: + - close-by-pushing + - close-by-user \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/comment_types.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/comment_types.yaml new file mode 100644 index 0000000000000..a6a86ae163b20 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/comment_types.yaml @@ -0,0 +1,5 @@ +type: string +description: The type of comment. +enum: + - alert + - user \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties.yaml new file mode 100644 index 0000000000000..c2bc2ab7c887a --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties.yaml @@ -0,0 +1,65 @@ +fields: + description: An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value. + nullable: true + type: object + properties: + caseId: + description: The case identifier for Swimlane connectors. + type: string + category: + description: The category of the incident for ServiceNow ITSM and ServiceNow SecOps connectors. + type: string + destIp: + description: A comma-separated list of destination IPs for ServiceNow SecOps connectors. + type: string + impact: + description: The effect an incident had on business for ServiceNow ITSM connectors. + type: string + issueType: + description: The type of issue for Jira connectors. + type: string + issueTypes: + description: The type of incident for IBM Resilient connectors. + type: array + items: + type: number + malwareHash: + description: A comma-separated list of malware hashes for ServiceNow SecOps connectors. + type: string + malwareUrl: + description: A comma-separated list of malware URLs for ServiceNow SecOps connectors. + type: string + parent: + description: The key of the parent issue, when the issue type is sub-task for Jira connectors. + type: string + priority: + description: The priority of the issue for Jira and ServiceNow SecOps connectors. + type: string + severity: + description: The severity of the incident for ServiceNow ITSM connectors. + type: string + severityCode: + description: The severity code of the incident for IBM Resilient connectors. + type: number + sourceIp: + description: A comma-separated list of source IPs for ServiceNow SecOps connectors. + type: string + subcategory: + description: The subcategory of the incident for ServiceNow ITSM connectors. + type: string + urgency: + description: The extent to which the incident resolution can be delayed for ServiceNow ITSM connectors. + type: string + required: + - fields + - id + - name + - type +id: + description: The identifier for the connector. To create a case without a connector, use `none`. + type: string +name: + description: The name of the connector. To create a case without a connector, use `none`. + type: string +type: + $ref: 'connector_types.yaml' \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_types.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_types.yaml new file mode 100644 index 0000000000000..24c1ec5880828 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_types.yaml @@ -0,0 +1,9 @@ +type: string +description: The type of connector. +enum: + - .jira + - .none + - .resilient + - .servicenow + - .servicenow-sir + - .swimlane \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/owners.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/owners.yaml new file mode 100644 index 0000000000000..f39324a36e702 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/owners.yaml @@ -0,0 +1,6 @@ +type: string +description: Owner apps +enum: + - cases + - observability + - securitySolution \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/status.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/status.yaml new file mode 100644 index 0000000000000..1fe2e342dd776 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/status.yaml @@ -0,0 +1,6 @@ +type: string +description: The status of the case. +enum: + - closed + - in-progress + - open \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/entrypoint.yaml b/x-pack/plugins/cases/docs/openapi/entrypoint.yaml new file mode 100644 index 0000000000000..14155c156b0cc --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/entrypoint.yaml @@ -0,0 +1,92 @@ +openapi: 3.0.1 +info: + title: Cases + description: OpenAPI schema for Cases endpoints + version: '0.1' + contact: + name: Cases Team + license: + name: Elastic License 2.0 + url: https://www.elastic.co/licensing/elastic-license +tags: + - name: cases + description: Case APIs enable you to open and track issues. + - name: kibana + description: Kibana APIs enable you to interact with Kibana features. +servers: + - url: 'http://localhost:5601' + description: local +paths: + /api/cases: + $ref: paths/api@cases.yaml +# /api/cases/_find: +# $ref: paths/api@cases@_find.yaml +# '/api/cases/alerts/{alertId}': +# $ref: 'paths/api@cases@alerts@{alertid}.yaml' +# '/api/cases/configure': +# $ref: paths/api@cases@configure.yaml +# '/api/cases/configure/{configurationId}': +# $ref: paths/api@cases@configure@{configurationid}.yaml +# '/api/cases/configure/connectors/_find': +# $ref: paths/api@cases@configure@connectors@_find.yaml +# '/api/cases/reporters': +# $ref: 'paths/api@cases@reporters.yaml' +# '/api/cases/status': +# $ref: 'paths/api@cases@status.yaml' +# '/api/cases/tags': +# $ref: 'paths/api@cases@tags.yaml' +# '/api/cases/{caseId}': +# $ref: 'paths/api@cases@{caseid}.yaml' +# '/api/cases/{caseId}/alerts': +# $ref: 'paths/api@cases@{caseid}@alerts.yaml' +# '/api/cases/{caseId}/comments': +# $ref: 'paths/api@cases@{caseid}@comments.yaml' +# '/api/cases/{caseId}/comments/{commentId}': +# $ref: 'paths/api@cases@{caseid}@comments@{commentid}.yaml' +# '/api/cases/{caseId}/connector/{connectorId}/_push': +# $ref: 'paths/api@cases@{caseid}@connector@{connectorid}@_push.yaml' +# '/api/cases/{caseId}/user_actions': +# $ref: 'paths/api@cases@{caseid}@user_actions.yaml' + + '/s/{spaceId}/api/cases': + $ref: 'paths/s@{spaceid}@api@cases.yaml' + # '/s/{spaceId}/api/cases/_find': + # $ref: 'paths/s@{spaceid}@api@cases@_find.yaml' + # '/s/{spaceId}/api/cases/alerts/{alertId}': + # $ref: 'paths/s@{spaceid}@api@cases@alerts@{alertid}.yaml' + # '/s/{spaceId}/api/cases/configure': + # $ref: paths/s@{spaceid}@api@cases@configure.yaml + # '/s/{spaceId}/api/cases/configure/{configurationId}': + # $ref: paths/s@{spaceid}@api@cases@configure@{configurationid}.yaml + # '/s/{spaceId}/api/cases/configure/connectors/_find': + # $ref: paths/s@{spaceid}@api@cases@configure@connectors@_find.yaml + # '/s/{spaceId}/api/cases/reporters': + # $ref: 'paths/s@{spaceid}@api@cases@reporters.yaml' + # '/s/{spaceId}/api/cases/status': + # $ref: 'paths/s@{spaceid}@api@cases@status.yaml' + # '/s/{spaceId}/api/cases/tags': + # $ref: 'paths/s@{spaceid}@api@cases@tags.yaml' + # '/s/{spaceId}/api/cases/{caseId}': + # $ref: 'paths/s@{spaceid}@api@cases@{caseid}.yaml' + # '/s/{spaceId}/api/cases/{caseId}/alerts': + # $ref: 'paths/s@{spaceid}@api@cases@{caseid}@alerts.yaml' + # '/s/{spaceId}/api/cases/{caseId}/comments': + # $ref: 'paths/s@{spaceid}@api@cases@{caseid}@comments.yaml' + # '/s/{spaceId}/api/cases/{caseId}/comments/{commentId}': + # $ref: 'paths/s@{spaceid}@api@cases@{caseid}@comments@{commentid}.yaml' + # '/s/{spaceId}/api/cases/{caseId}/connector/{connectorId}/_push': + # $ref: 'paths/s@{spaceid}@api@cases@{caseid}@connector@{connectorid}@_push.yaml' + # '/s/{spaceId}/api/cases/{caseId}/user_actions': + # $ref: 'paths/s@{spaceid}@api@cases@{caseid}@user_actions.yaml' +components: + securitySchemes: + basicAuth: + type: http + scheme: basic + apiKeyAuth: + type: apiKey + in: header + name: ApiKey +security: + - basicAuth: [] + - apiKeyAuth: [] diff --git a/x-pack/plugins/cases/docs/openapi/paths/README.md b/x-pack/plugins/cases/docs/openapi/paths/README.md new file mode 100644 index 0000000000000..b7818c8474fc8 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/paths/README.md @@ -0,0 +1,10 @@ +Paths +===== + +Each path definition for which there is a specification exists within this folder. + +These files currently use the following conventions: + +* path separator token (e.g. `@`) is included in the file name +* path parameter (e.g. `{example}`) is included in the file name +* there is one file per path; each file can contain multiple operations diff --git a/x-pack/plugins/cases/docs/openapi/paths/api@cases.yaml b/x-pack/plugins/cases/docs/openapi/paths/api@cases.yaml new file mode 100644 index 0000000000000..c37bb3ecef645 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/paths/api@cases.yaml @@ -0,0 +1,161 @@ +post: + description: > + Creates a case. + You must have all privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're creating. + tags: + - cases + - kibana + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + requestBody: + content: + application/json: + schema: + type: object + properties: + connector: + description: An object that contains the connector configuration. + type: object + properties: + $ref: '../components/schemas/connector_properties.yaml' + description: + description: The description for the case. + type: string + owner: + $ref: '../components/schemas/owners.yaml' + settings: + description: An object that contains the case settings. + type: object + properties: + syncAlerts: + description: Turns alert syncing on or off. + type: boolean + tags: + description: The words and phrases that help categorize cases. It can be an empty array. + type: array + items: + type: string + title: + description: A title for the case. + type: string + required: + - connector + - description + - owner + - settings + - tags + - title + examples: + createCaseRequest: + $ref: '../components/examples/create_case_request.yaml' + responses: + '200': + description: Indicates a successful call. + content: + application/json; charset=utf-8: + schema: + type: object + properties: + $ref: '../components/schemas/case_response_properties.yaml' + examples: + createCaseResponse: + $ref: '../components/examples/create_case_response.yaml' + servers: + - url: https://localhost:5601 + +delete: + description: > + Deletes one or more cases. + You must have all privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the cases you're deleting. + tags: + - cases + - kibana + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + - name: ids + description: The cases that you want to removed. To retrieve case IDs, use the find cases API. All non-ASCII characters must be URL encoded. + in: query + required: true + schema: + type: string + example: 'd4e7abb0-b462-11ec-9a8d-698504725a43' + responses: + '204': + description: Indicates a successful call. + servers: + - url: https://localhost:5601 + +patch: + description: > + Updates one or more cases. + You must have all privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're updating. + tags: + - cases + - kibana + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + requestBody: + content: + application/json: + schema: + type: object + properties: + cases: + type: array + items: + type: object + properties: + connector: + description: An object that contains the connector configuration. + type: object + properties: + $ref: '../components/schemas/connector_properties.yaml' + description: + description: The description for the case. + type: string + id: + description: The identifier for the case. + type: string + settings: + description: An object that contains the case settings. + type: object + properties: + syncAlerts: + description: Turns alert syncing on or off. + type: boolean + status: + $ref: '../components/schemas/status.yaml' + tags: + description: The words and phrases that help categorize cases. + type: array + items: + type: string + title: + description: A title for the case. + type: string + version: + description: The current version of the case. + type: string + required: + - id + - version + examples: + updateCaseRequest: + $ref: '../components/examples/update_case_request.yaml' + responses: + '200': + description: Indicates a successful call. + content: + application/json; charset=utf-8: + schema: + type: object + properties: + $ref: '../components/schemas/case_response_properties.yaml' + examples: + updateCaseResponse: + $ref: '../components/examples/update_case_response.yaml' + servers: + - url: https://localhost:5601 + +servers: + - url: https://localhost:5601 \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases.yaml b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases.yaml new file mode 100644 index 0000000000000..c03ea64a53538 --- /dev/null +++ b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases.yaml @@ -0,0 +1,164 @@ +post: + description: > + Creates a case. + You must have all privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're creating. + tags: + - cases + - kibana + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + - $ref: '../components/parameters/space_id.yaml' + requestBody: + content: + application/json: + schema: + type: object + properties: + connector: + description: An object that contains the connector configuration. + type: object + properties: + $ref: '../components/schemas/connector_properties.yaml' + description: + description: The description for the case. + type: string + owner: + $ref: '../components/schemas/owners.yaml' + settings: + description: An object that contains the case settings. + type: object + properties: + syncAlerts: + description: Turns alert syncing on or off. + type: boolean + tags: + description: The words and phrases that help categorize cases. It can be an empty array. + type: array + items: + type: string + title: + description: A title for the case. + type: string + required: + - connector + - description + - owner + - settings + - tags + - title + examples: + createCaseRequest: + $ref: '../components/examples/create_case_request.yaml' + responses: + '200': + description: Indicates a successful call. + content: + application/json; charset=utf-8: + schema: + type: object + properties: + $ref: '../components/schemas/case_response_properties.yaml' + examples: + createCaseResponse: + $ref: '../components/examples/create_case_response.yaml' + servers: + - url: https://localhost:5601 + +delete: + description: > + Deletes one or more cases. + You must have all privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the cases you're deleting. + tags: + - cases + - kibana + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + - $ref: '../components/parameters/space_id.yaml' + - name: ids + description: The cases that you want to removed. All non-ASCII characters must be URL encoded. + in: query + required: true + schema: + type: string + example: 'd4e7abb0-b462-11ec-9a8d-698504725a43' + responses: + '204': + description: Indicates a successful call. + servers: + - url: https://localhost:5601 + +patch: + description: > + Updates one or more cases. + You must have all privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're updating. + tags: + - cases + - kibana + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + - $ref: '../components/parameters/space_id.yaml' + requestBody: + content: + application/json: + schema: + type: object + properties: + cases: + type: array + items: + type: object + properties: + connector: + description: An object that contains the connector configuration. + type: object + properties: + $ref: '../components/schemas/connector_properties.yaml' + description: + description: The description for the case. + type: string + id: + description: The identifier for the case. + type: string + settings: + description: An object that contains the case settings. + type: object + properties: + syncAlerts: + description: Turns alert syncing on or off. + type: boolean + status: + $ref: '../components/schemas/status.yaml' + tags: + description: The words and phrases that help categorize cases. + type: array + items: + type: string + title: + description: A title for the case. + type: string + version: + description: The current version of the case. + type: string + required: + - id + - version + examples: + updateCaseRequest: + $ref: '../components/examples/update_case_request.yaml' + responses: + '200': + description: Indicates a successful call. + content: + application/json; charset=utf-8: + schema: + type: object + properties: + $ref: '../components/schemas/case_response_properties.yaml' + examples: + updateCaseResponse: + $ref: '../components/examples/update_case_response.yaml' + servers: + - url: https://localhost:5601 + +servers: + - url: https://localhost:5601 \ No newline at end of file diff --git a/x-pack/plugins/cases/public/api/__mocks__/index.ts b/x-pack/plugins/cases/public/api/__mocks__/index.ts new file mode 100644 index 0000000000000..a1df651240224 --- /dev/null +++ b/x-pack/plugins/cases/public/api/__mocks__/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CasesFindRequest } from '../../../common/api'; +import { HTTPService } from '..'; +import { casesStatus } from '../../containers/mock'; +import { CasesStatus } from '../../containers/types'; + +export const getCasesStatus = async ({ + http, + signal, + query, +}: HTTPService & { query: CasesFindRequest }): Promise => Promise.resolve(casesStatus); diff --git a/x-pack/plugins/cases/public/api/decoders.ts b/x-pack/plugins/cases/public/api/decoders.ts new file mode 100644 index 0000000000000..6b9cfdb21742c --- /dev/null +++ b/x-pack/plugins/cases/public/api/decoders.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { fold } from 'fp-ts/lib/Either'; +import { identity } from 'fp-ts/lib/function'; +import { pipe } from 'fp-ts/lib/pipeable'; + +import { createToasterPlainError } from '../containers/utils'; +import { throwErrors } from '../../common'; +import { + CasesFindResponse, + CasesFindResponseRt, + CasesStatusResponse, + CasesStatusResponseRt, + CasesMetricsResponse, + CasesMetricsResponseRt, +} from '../../common/api'; + +export const decodeCasesFindResponse = (respCases?: CasesFindResponse) => + pipe(CasesFindResponseRt.decode(respCases), fold(throwErrors(createToasterPlainError), identity)); + +export const decodeCasesStatusResponse = (respCase?: CasesStatusResponse) => + pipe( + CasesStatusResponseRt.decode(respCase), + fold(throwErrors(createToasterPlainError), identity) + ); + +export const decodeCasesMetricsResponse = (metrics?: CasesMetricsResponse) => + pipe( + CasesMetricsResponseRt.decode(metrics), + fold(throwErrors(createToasterPlainError), identity) + ); diff --git a/x-pack/plugins/cases/public/api/index.test.ts b/x-pack/plugins/cases/public/api/index.test.ts new file mode 100644 index 0000000000000..321a1db206846 --- /dev/null +++ b/x-pack/plugins/cases/public/api/index.test.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { httpServiceMock } from '@kbn/core/public/mocks'; +import { getCases, getCasesMetrics } from '.'; +import { allCases, allCasesSnake } from '../containers/mock'; + +describe('api', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('getCases', () => { + const http = httpServiceMock.createStartContract({ basePath: '' }); + http.get.mockResolvedValue(allCasesSnake); + + it('should return the correct response', async () => { + expect(await getCases({ http, query: { from: 'now-1d' } })).toEqual(allCases); + }); + + it('should have been called with the correct path', async () => { + await getCases({ http, query: { perPage: 10 } }); + expect(http.get).toHaveBeenCalledWith('/api/cases/_find', { + query: { perPage: 10 }, + }); + }); + }); + + describe('getCasesMetrics', () => { + const http = httpServiceMock.createStartContract({ basePath: '' }); + http.get.mockResolvedValue({ mttr: 0 }); + + it('should return the correct response', async () => { + expect( + await getCasesMetrics({ http, query: { features: ['mttr'], from: 'now-1d' } }) + ).toEqual({ mttr: 0 }); + }); + + it('should have been called with the correct path', async () => { + await getCasesMetrics({ http, query: { features: ['mttr'], to: 'now-1d' } }); + expect(http.get).toHaveBeenCalledWith('/api/cases/metrics', { + query: { features: ['mttr'], to: 'now-1d' }, + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/api/index.ts b/x-pack/plugins/cases/public/api/index.ts new file mode 100644 index 0000000000000..209c0ad619d4b --- /dev/null +++ b/x-pack/plugins/cases/public/api/index.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { HttpStart } from '@kbn/core/public'; +import { Cases, CasesStatus, CasesMetrics } from '../../common/ui'; +import { CASE_FIND_URL, CASE_METRICS_URL, CASE_STATUS_URL } from '../../common/constants'; +import { + CasesFindRequest, + CasesFindResponse, + CasesMetricsRequest, + CasesMetricsResponse, + CasesStatusRequest, + CasesStatusResponse, +} from '../../common/api'; +import { convertAllCasesToCamel, convertToCamelCase } from './utils'; +import { + decodeCasesFindResponse, + decodeCasesMetricsResponse, + decodeCasesStatusResponse, +} from './decoders'; + +export interface HTTPService { + http: HttpStart; + signal?: AbortSignal; +} + +export const getCases = async ({ + http, + signal, + query, +}: HTTPService & { query: CasesFindRequest }): Promise => { + const res = await http.get(CASE_FIND_URL, { query, signal }); + return convertAllCasesToCamel(decodeCasesFindResponse(res)); +}; + +export const getCasesStatus = async ({ + http, + signal, + query, +}: HTTPService & { query: CasesStatusRequest }): Promise => { + const response = await http.get(CASE_STATUS_URL, { + signal, + query, + }); + + return convertToCamelCase(decodeCasesStatusResponse(response)); +}; + +export const getCasesMetrics = async ({ + http, + signal, + query, +}: HTTPService & { query: CasesMetricsRequest }): Promise => { + const res = await http.get(CASE_METRICS_URL, { signal, query }); + return convertToCamelCase(decodeCasesMetricsResponse(res)); +}; diff --git a/x-pack/plugins/cases/public/api/utils.test.ts b/x-pack/plugins/cases/public/api/utils.test.ts new file mode 100644 index 0000000000000..f46ada35ffca1 --- /dev/null +++ b/x-pack/plugins/cases/public/api/utils.test.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { allCases, allCasesSnake } from '../containers/mock'; +import { convertAllCasesToCamel, convertArrayToCamelCase, convertToCamelCase } from './utils'; + +describe('utils', () => { + describe('convertArrayToCamelCase', () => { + it('converts an array of items to camel case correctly', () => { + const items = [ + { foo_bar: [{ bar_foo: '1' }], test_bar: '2', obj_pros: { is_valid: true } }, + { bar_test: [{ baz_foo: '1' }], test_bar: '2', obj_pros: { is_valid: true } }, + ]; + expect(convertArrayToCamelCase(items)).toEqual([ + { fooBar: [{ barFoo: '1' }], testBar: '2', objPros: { isValid: true } }, + { barTest: [{ bazFoo: '1' }], testBar: '2', objPros: { isValid: true } }, + ]); + }); + }); + + describe('convertToCamelCase', () => { + it('converts an object to camel case correctly', () => { + const obj = { bar_test: [{ baz_foo: '1' }], test_bar: '2', obj_pros: { is_valid: true } }; + + expect(convertToCamelCase(obj)).toEqual({ + barTest: [{ bazFoo: '1' }], + testBar: '2', + objPros: { isValid: true }, + }); + }); + }); + + describe('convertAllCasesToCamel', () => { + it('converts the find response to camel case', () => { + expect(convertAllCasesToCamel(allCasesSnake)).toEqual(allCases); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/api/utils.ts b/x-pack/plugins/cases/public/api/utils.ts new file mode 100644 index 0000000000000..19cb800de921b --- /dev/null +++ b/x-pack/plugins/cases/public/api/utils.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 { isArray, set, camelCase, isObject } from 'lodash'; +import { CasesFindResponse, CaseResponse } from '../../common/api'; +import { Cases, Case } from '../containers/types'; + +export const convertArrayToCamelCase = (arrayOfSnakes: unknown[]): unknown[] => + arrayOfSnakes.reduce((acc: unknown[], value) => { + if (isArray(value)) { + return [...acc, convertArrayToCamelCase(value)]; + } else if (isObject(value)) { + return [...acc, convertToCamelCase(value)]; + } else { + return [...acc, value]; + } + }, []); + +export const convertToCamelCase = (obj: T): U => + Object.entries(obj).reduce((acc, [key, value]) => { + if (isArray(value)) { + set(acc, camelCase(key), convertArrayToCamelCase(value)); + } else if (isObject(value)) { + set(acc, camelCase(key), convertToCamelCase(value)); + } else { + set(acc, camelCase(key), value); + } + return acc; + }, {} as U); + +export const convertAllCasesToCamel = (snakeCases: CasesFindResponse): Cases => ({ + cases: snakeCases.cases.map((theCase) => convertToCamelCase(theCase)), + countOpenCases: snakeCases.count_open_cases, + countInProgressCases: snakeCases.count_in_progress_cases, + countClosedCases: snakeCases.count_closed_cases, + page: snakeCases.page, + perPage: snakeCases.per_page, + total: snakeCases.total, +}); diff --git a/x-pack/plugins/cases/public/client/api/index.test.ts b/x-pack/plugins/cases/public/client/api/index.test.ts index 8bf4ca682fe3b..dacea3350bd4a 100644 --- a/x-pack/plugins/cases/public/client/api/index.test.ts +++ b/x-pack/plugins/cases/public/client/api/index.test.ts @@ -7,7 +7,7 @@ import { httpServiceMock } from '@kbn/core/public/mocks'; import { createClientAPI } from '.'; -import { allCases, casesStatus } from '../../containers/mock'; +import { allCases, allCasesSnake } from '../../containers/mock'; describe('createClientAPI', () => { beforeEach(() => { @@ -48,7 +48,7 @@ describe('createClientAPI', () => { describe('find', () => { const http = httpServiceMock.createStartContract({ basePath: '' }); const api = createClientAPI({ http }); - http.get.mockResolvedValue(allCases); + http.get.mockResolvedValue(allCasesSnake); it('should return the correct response', async () => { expect(await api.cases.find({ from: 'now-1d' })).toEqual(allCases); @@ -62,19 +62,21 @@ describe('createClientAPI', () => { }); }); - describe('getAllCasesMetrics', () => { + describe('getCasesMetrics', () => { const http = httpServiceMock.createStartContract({ basePath: '' }); const api = createClientAPI({ http }); - http.get.mockResolvedValue(casesStatus); + http.get.mockResolvedValue({ mttr: 0 }); it('should return the correct response', async () => { - expect(await api.cases.getAllCasesMetrics({ from: 'now-1d' })).toEqual(casesStatus); + expect(await api.cases.getCasesMetrics({ features: ['mttr'], from: 'now-1d' })).toEqual({ + mttr: 0, + }); }); it('should have been called with the correct path', async () => { - await api.cases.getAllCasesMetrics({ from: 'now-1d' }); - expect(http.get).toHaveBeenCalledWith('/api/cases/status', { - query: { from: 'now-1d' }, + await api.cases.getCasesMetrics({ features: ['mttr'], from: 'now-1d' }); + expect(http.get).toHaveBeenCalledWith('/api/cases/metrics', { + query: { features: ['mttr'], from: 'now-1d' }, }); }); }); diff --git a/x-pack/plugins/cases/public/client/api/index.ts b/x-pack/plugins/cases/public/client/api/index.ts index 8f55f7a4f0739..e9caee514a9cd 100644 --- a/x-pack/plugins/cases/public/client/api/index.ts +++ b/x-pack/plugins/cases/public/client/api/index.ts @@ -11,11 +11,11 @@ import { CasesByAlertIDRequest, CasesFindRequest, getCasesFromAlertsUrl, - CasesResponse, CasesStatusRequest, - CasesStatusResponse, + CasesMetricsRequest, } from '../../../common/api'; -import { CASE_FIND_URL, CASE_STATUS_URL } from '../../../common/constants'; +import { Cases, CasesStatus, CasesMetrics } from '../../../common/ui'; +import { getCases, getCasesMetrics, getCasesStatus } from '../../api'; import { CasesUiStart } from '../../types'; export const createClientAPI = ({ http }: { http: HttpStart }): CasesUiStart['api'] => { @@ -26,10 +26,12 @@ export const createClientAPI = ({ http }: { http: HttpStart }): CasesUiStart['ap ): Promise => http.get(getCasesFromAlertsUrl(alertId), { query }), cases: { - find: (query: CasesFindRequest): Promise => - http.get(CASE_FIND_URL, { query }), - getAllCasesMetrics: (query: CasesStatusRequest): Promise => - http.get(CASE_STATUS_URL, { query }), + find: (query: CasesFindRequest, signal?: AbortSignal): Promise => + getCases({ http, query, signal }), + getCasesStatus: (query: CasesStatusRequest, signal?: AbortSignal): Promise => + getCasesStatus({ http, query, signal }), + getCasesMetrics: (query: CasesMetricsRequest, signal?: AbortSignal): Promise => + getCasesMetrics({ http, signal, query }), }, }; }; diff --git a/x-pack/plugins/cases/public/common/lib/kibana/hooks.ts b/x-pack/plugins/cases/public/common/lib/kibana/hooks.ts index 02a099f9c6a1f..a53a1e9ea452f 100644 --- a/x-pack/plugins/cases/public/common/lib/kibana/hooks.ts +++ b/x-pack/plugins/cases/public/common/lib/kibana/hooks.ts @@ -12,12 +12,12 @@ import { i18n } from '@kbn/i18n'; import { AuthenticatedUser } from '@kbn/security-plugin/common/model'; import { NavigateToAppOptions } from '@kbn/core/public'; +import { convertToCamelCase } from '../../../api/utils'; import { FEATURE_ID, DEFAULT_DATE_FORMAT, DEFAULT_DATE_FORMAT_TZ, } from '../../../../common/constants'; -import { convertToCamelCase } from '../../../containers/utils'; import { StartServices } from '../../../types'; import { useUiSetting, useKibana } from './kibana_react'; diff --git a/x-pack/plugins/cases/public/common/navigation/__mocks__/hooks.ts b/x-pack/plugins/cases/public/common/navigation/__mocks__/hooks.ts index 93f202994918d..38f57a9ef45bd 100644 --- a/x-pack/plugins/cases/public/common/navigation/__mocks__/hooks.ts +++ b/x-pack/plugins/cases/public/common/navigation/__mocks__/hooks.ts @@ -26,3 +26,8 @@ export const useConfigureCasesNavigation = jest.fn().mockReturnValue({ getConfigureCasesUrl: jest.fn().mockReturnValue('/app/security/cases/configure'), navigateToConfigureCases: jest.fn(), }); + +export const useUrlParams = jest.fn().mockReturnValue({ + urlParams: {}, + toUrlParams: jest.fn(), +}); diff --git a/x-pack/plugins/cases/public/common/navigation/hooks.ts b/x-pack/plugins/cases/public/common/navigation/hooks.ts index c5488b4060795..1197ef0c5cf11 100644 --- a/x-pack/plugins/cases/public/common/navigation/hooks.ts +++ b/x-pack/plugins/cases/public/common/navigation/hooks.ts @@ -5,9 +5,10 @@ * 2.0. */ -import { useCallback } from 'react'; -import { useParams } from 'react-router-dom'; +import { useCallback, useEffect, useState } from 'react'; +import { useLocation, useParams } from 'react-router-dom'; +import { parse, stringify } from 'query-string'; import { APP_ID } from '../../../common/constants'; import { useNavigation } from '../lib/kibana'; import { useCasesContext } from '../../components/cases_context/use_cases_context'; @@ -16,11 +17,28 @@ import { CASES_CONFIGURE_PATH, CASES_CREATE_PATH, CaseViewPathParams, + CaseViewPathSearchParams, generateCaseViewPath, } from './paths'; export const useCaseViewParams = () => useParams(); +export function useUrlParams() { + const { search } = useLocation(); + const [urlParams, setUrlParams] = useState(() => parse(search)); + const toUrlParams = useCallback( + (params: CaseViewPathSearchParams = urlParams) => stringify(params), + [urlParams] + ); + useEffect(() => { + setUrlParams(parse(search)); + }, [search]); + return { + urlParams, + toUrlParams, + }; +} + type GetCasesUrl = (absolute?: boolean) => string; type NavigateToCases = () => void; type UseCasesNavigation = [GetCasesUrl, NavigateToCases]; diff --git a/x-pack/plugins/cases/public/common/navigation/paths.ts b/x-pack/plugins/cases/public/common/navigation/paths.ts index a8660b5cf63ab..857f832f7aed3 100644 --- a/x-pack/plugins/cases/public/common/navigation/paths.ts +++ b/x-pack/plugins/cases/public/common/navigation/paths.ts @@ -6,17 +6,26 @@ */ import { generatePath } from 'react-router-dom'; +import { CASE_VIEW_PAGE_TABS } from '../../components/case_view/types'; export const DEFAULT_BASE_PATH = '/cases'; -export interface CaseViewPathParams { + +export interface CaseViewPathSearchParams { + tabId?: CASE_VIEW_PAGE_TABS; +} + +export type CaseViewPathParams = { detailName: string; commentId?: string; -} +} & CaseViewPathSearchParams; export const CASES_CREATE_PATH = '/create' as const; export const CASES_CONFIGURE_PATH = '/configure' as const; export const CASE_VIEW_PATH = '/:detailName' as const; export const CASE_VIEW_COMMENT_PATH = `${CASE_VIEW_PATH}/:commentId` as const; +export const CASE_VIEW_ALERT_TABLE_PATH = + `${CASE_VIEW_PATH}/?tabId=${CASE_VIEW_PAGE_TABS.ALERTS}` as const; +export const CASE_VIEW_TAB_PATH = `${CASE_VIEW_PATH}/?tabId=:tabId` as const; const normalizePath = (path: string): string => path.replaceAll('//', '/'); @@ -30,12 +39,19 @@ export const getCaseViewWithCommentPath = (casesBasePath: string) => normalizePath(`${casesBasePath}${CASE_VIEW_COMMENT_PATH}`); export const generateCaseViewPath = (params: CaseViewPathParams): string => { - const { commentId } = params; + const { commentId, tabId } = params; // Cast for generatePath argument type constraint const pathParams = params as unknown as { [paramName: string]: string }; + // paths with commentId have their own specific path. + // Effectively overwrites the tabId if (commentId) { return normalizePath(generatePath(CASE_VIEW_COMMENT_PATH, pathParams)); } + + if (tabId !== undefined) { + return normalizePath(generatePath(CASE_VIEW_TAB_PATH, pathParams)); + } + return normalizePath(generatePath(CASE_VIEW_PATH, pathParams)); }; diff --git a/x-pack/plugins/cases/public/common/translations.ts b/x-pack/plugins/cases/public/common/translations.ts index 10005b2c87bce..dbc57e163d3ff 100644 --- a/x-pack/plugins/cases/public/common/translations.ts +++ b/x-pack/plugins/cases/public/common/translations.ts @@ -198,6 +198,10 @@ export const MARKED_CASE_AS = i18n.translate('xpack.cases.caseView.markedCaseAs' defaultMessage: 'marked case as', }); +export const SET_SEVERITY_TO = i18n.translate('xpack.cases.caseView.setSeverityTo', { + defaultMessage: 'set severity to', +}); + export const OPEN_CASES = i18n.translate('xpack.cases.caseTable.openCases', { defaultMessage: 'Open cases', }); 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 ef6d789e97a36..22e12d5ee11b5 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 @@ -17,7 +17,7 @@ import { TestProviders } from '../../common/mock'; import { casesStatus, useGetCasesMockState, mockCase, connectorsMock } from '../../containers/mock'; import { StatusAll } from '../../../common/ui/types'; -import { CaseStatuses } from '../../../common/api'; +import { CaseSeverity, CaseStatuses } from '../../../common/api'; import { SECURITY_SOLUTION_OWNER } from '../../../common/constants'; import { getEmptyTagValue } from '../empty_value'; import { useDeleteCases } from '../../containers/use_delete_cases'; @@ -204,6 +204,11 @@ describe('AllCasesListGeneric', () => { .childAt(0) .prop('value') ).toBe(useGetCasesMockState.data.cases[0].createdAt); + + expect( + wrapper.find(`[data-test-subj="case-table-column-severity"]`).first().text().toLowerCase() + ).toBe(useGetCasesMockState.data.cases[0].severity); + expect(wrapper.find(`[data-test-subj="case-table-case-count"]`).first().text()).toEqual( 'Showing 10 cases' ); @@ -223,6 +228,7 @@ describe('AllCasesListGeneric', () => { createdAt: null, createdBy: null, status: null, + severity: null, tags: null, title: null, totalComment: null, @@ -560,6 +566,8 @@ describe('AllCasesListGeneric', () => { username: 'lknope', }, description: 'Security banana Issue', + severity: CaseSeverity.LOW, + duration: null, externalService: { connectorId: '123', connectorName: 'connector name', diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx index 5eac485e24c7b..96b220283b452 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx @@ -218,6 +218,7 @@ export const AllCasesList = React.memo( tags: filterOptions.tags, status: filterOptions.status, owner: filterOptions.owner, + severity: filterOptions.severity, }} setFilterRefetch={setFilterRefetch} hiddenStatuses={hiddenStatuses} diff --git a/x-pack/plugins/cases/public/components/all_cases/columns.tsx b/x-pack/plugins/cases/public/components/all_cases/columns.tsx index 543e6ef6f4871..43096d3de061c 100644 --- a/x-pack/plugins/cases/public/components/all_cases/columns.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/columns.tsx @@ -18,12 +18,13 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon, + EuiHealth, } from '@elastic/eui'; import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services'; import styled from 'styled-components'; import { Case, DeleteCase } from '../../../common/ui/types'; -import { CaseStatuses, ActionConnector } from '../../../common/api'; +import { CaseStatuses, ActionConnector, CaseSeverity } from '../../../common/api'; import { OWNER_INFO } from '../../../common/constants'; import { getEmptyTagValue } from '../empty_value'; import { FormattedRelativePreferenceDate } from '../formatted_date'; @@ -40,6 +41,7 @@ import { TruncatedText } from '../truncated_text'; import { getConnectorIcon } from '../utils'; import type { CasesOwners } from '../../client/helpers/can_use_cases'; import { useCasesFeatures } from '../cases_context/use_cases_features'; +import { severities } from '../severity/config'; export type CasesColumns = | EuiTableActionsColumnType @@ -300,30 +302,6 @@ export const useCasesColumns = ({ return getEmptyTagValue(); }, }, - ...(isSelectorView - ? [ - { - align: RIGHT_ALIGNMENT, - render: (theCase: Case) => { - if (theCase.id != null) { - return ( - { - assignCaseAction(theCase); - }} - size="s" - fill={true} - > - {i18n.SELECT} - - ); - } - return getEmptyTagValue(); - }, - }, - ] - : []), ...(!isSelectorView ? [ { @@ -351,6 +329,45 @@ export const useCasesColumns = ({ }, ] : []), + { + name: i18n.SEVERITY, + render: (theCase: Case) => { + if (theCase.severity != null) { + const severityData = severities[theCase.severity ?? CaseSeverity.LOW]; + return ( + + {severityData.label} + + ); + } + return getEmptyTagValue(); + }, + }, + + ...(isSelectorView + ? [ + { + align: RIGHT_ALIGNMENT, + render: (theCase: Case) => { + if (theCase.id != null) { + return ( + { + assignCaseAction(theCase); + }} + size="s" + fill={true} + > + {i18n.SELECT} + + ); + } + return getEmptyTagValue(); + }, + }, + ] + : []), ...(userCanCrud && !isSelectorView ? [ { diff --git a/x-pack/plugins/cases/public/components/all_cases/severity_filter.test.tsx b/x-pack/plugins/cases/public/components/all_cases/severity_filter.test.tsx new file mode 100644 index 0000000000000..7366bb3fceebb --- /dev/null +++ b/x-pack/plugins/cases/public/components/all_cases/severity_filter.test.tsx @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CaseSeverity } from '../../../common/api'; +import React from 'react'; +import { AppMockRenderer, createAppMockRenderer } from '../../common/mock'; +import userEvent from '@testing-library/user-event'; +import { waitFor } from '@testing-library/dom'; +import { SeverityFilter } from './severity_filter'; + +describe('Severity form field', () => { + const onSeverityChange = jest.fn(); + let appMockRender: AppMockRenderer; + const props = { + isLoading: false, + selectedSeverity: CaseSeverity.LOW, + isDisabled: false, + onSeverityChange, + }; + beforeEach(() => { + appMockRender = createAppMockRenderer(); + }); + it('renders', () => { + const result = appMockRender.render(); + expect(result.getByTestId('case-severity-filter')).not.toHaveAttribute('disabled'); + }); + + // default to LOW in this test configuration + it('defaults to the correct value', () => { + const result = appMockRender.render(); + // two items. one for the popover one for the selected field + expect(result.getAllByTestId('case-severity-filter-low').length).toBe(2); + }); + + it('selects the correct value when changed', async () => { + const result = appMockRender.render(); + userEvent.click(result.getByTestId('case-severity-filter')); + userEvent.click(result.getByTestId('case-severity-filter-high')); + await waitFor(() => { + expect(onSeverityChange).toHaveBeenCalledWith('high'); + }); + }); + + it('selects the correct value when changed (all)', async () => { + const result = appMockRender.render(); + userEvent.click(result.getByTestId('case-severity-filter')); + userEvent.click(result.getByTestId('case-severity-filter-all')); + await waitFor(() => { + expect(onSeverityChange).toHaveBeenCalledWith('all'); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/components/all_cases/severity_filter.tsx b/x-pack/plugins/cases/public/components/all_cases/severity_filter.tsx new file mode 100644 index 0000000000000..a9f4a6565c318 --- /dev/null +++ b/x-pack/plugins/cases/public/components/all_cases/severity_filter.tsx @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiFlexGroup, + EuiFlexItem, + EuiHealth, + EuiSuperSelect, + EuiSuperSelectOption, + EuiText, +} from '@elastic/eui'; +import React from 'react'; +import { CaseSeverityWithAll, SeverityAll } from '../../containers/types'; +import { severitiesWithAll } from '../severity/config'; + +interface Props { + selectedSeverity: CaseSeverityWithAll; + onSeverityChange: (status: CaseSeverityWithAll) => void; + isLoading: boolean; + isDisabled: boolean; +} + +export const SeverityFilter: React.FC = ({ + selectedSeverity, + onSeverityChange, + isLoading, + isDisabled, +}) => { + const caseSeverities = Object.keys(severitiesWithAll) as CaseSeverityWithAll[]; + const options: Array> = caseSeverities.map( + (severity) => { + const severityData = severitiesWithAll[severity]; + return { + value: severity, + inputDisplay: ( + + + {severity === SeverityAll ? ( + {severityData.label} + ) : ( + {severityData.label} + )} + + + ), + }; + } + ); + + return ( + + ); +}; +SeverityFilter.displayName = 'SeverityFilter'; diff --git a/x-pack/plugins/cases/public/components/all_cases/table.tsx b/x-pack/plugins/cases/public/components/all_cases/table.tsx index 8190acce9e784..ce3442e734b43 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table.tsx @@ -19,13 +19,13 @@ import styled from 'styled-components'; import { CasesTableUtilityBar } from './utility_bar'; import { LinkButton } from '../links'; -import { AllCases, Case, FilterOptions } from '../../../common/ui/types'; +import { Cases, Case, FilterOptions } from '../../../common/ui/types'; import * as i18n from './translations'; import { useCreateCaseNavigation } from '../../common/navigation'; interface CasesTableProps { columns: EuiBasicTableProps['columns']; - data: AllCases; + data: Cases; filterOptions: FilterOptions; goToCreateCase?: () => void; handleIsLoading: (a: boolean) => void; diff --git a/x-pack/plugins/cases/public/components/all_cases/table_filters.test.tsx b/x-pack/plugins/cases/public/components/all_cases/table_filters.test.tsx index 5e83c33717abd..ff1c00b56d031 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table_filters.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table_filters.test.tsx @@ -10,11 +10,12 @@ import { mount } from 'enzyme'; import { CaseStatuses } from '../../../common/api'; import { OBSERVABILITY_OWNER, SECURITY_SOLUTION_OWNER } from '../../../common/constants'; -import { TestProviders } from '../../common/mock'; +import { AppMockRenderer, createAppMockRenderer, TestProviders } from '../../common/mock'; import { useGetTags } from '../../containers/use_get_tags'; import { useGetReporters } from '../../containers/use_get_reporters'; import { DEFAULT_FILTER_OPTIONS } from '../../containers/use_get_cases'; import { CasesTableFilters } from './table_filters'; +import userEvent from '@testing-library/user-event'; jest.mock('../../containers/use_get_reporters'); jest.mock('../../containers/use_get_tags'); @@ -35,7 +36,9 @@ const props = { }; describe('CasesTableFilters ', () => { + let appMockRender: AppMockRenderer; beforeEach(() => { + appMockRender = createAppMockRenderer(); jest.clearAllMocks(); (useGetTags as jest.Mock).mockReturnValue({ tags: ['coke', 'pepsi'], fetchTags }); (useGetReporters as jest.Mock).mockReturnValue({ @@ -57,6 +60,19 @@ describe('CasesTableFilters ', () => { expect(wrapper.find(`[data-test-subj="case-status-filter"]`).first().exists()).toBeTruthy(); }); + it('should render the case severity filter dropdown', () => { + const result = appMockRender.render(); + expect(result.getByTestId('case-severity-filter')).toBeTruthy(); + }); + + it('should call onFilterChange when the severity filter changes', () => { + const result = appMockRender.render(); + userEvent.click(result.getByTestId('case-severity-filter')); + userEvent.click(result.getByTestId('case-severity-filter-high')); + + expect(onFilterChanged).toBeCalledWith({ severity: 'high' }); + }); + it('should call onFilterChange when selected tags change', () => { const wrapper = mount( diff --git a/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx b/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx index faee469d1c4bc..0a34e756e37a6 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx @@ -10,7 +10,12 @@ import { isEqual } from 'lodash/fp'; import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiFilterGroup, EuiButton } from '@elastic/eui'; -import { StatusAll, CaseStatusWithAllStatus } from '../../../common/ui/types'; +import { + StatusAll, + CaseStatusWithAllStatus, + SeverityAll, + CaseSeverityWithAll, +} from '../../../common/ui/types'; import { CaseStatuses } from '../../../common/api'; import { FilterOptions } from '../../containers/types'; import { useGetTags } from '../../containers/use_get_tags'; @@ -18,6 +23,7 @@ import { useGetReporters } from '../../containers/use_get_reporters'; import { FilterPopover } from '../filter_popover'; import { StatusFilter } from './status_filter'; import * as i18n from './translations'; +import { SeverityFilter } from './severity_filter'; interface CasesTableFiltersProps { countClosedCases: number | null; @@ -39,6 +45,12 @@ const StatusFilterWrapper = styled(EuiFlexItem)` } `; +const SeverityFilterWrapper = styled(EuiFlexItem)` + && { + flex-basis: 180px; + } +`; + /** * Collection of filters for filtering data within the CasesTable. Contains search bar, * and tag selection @@ -48,6 +60,7 @@ const StatusFilterWrapper = styled(EuiFlexItem)` const defaultInitial = { search: '', + severity: SeverityAll, reporters: [], status: StatusAll, tags: [], @@ -151,6 +164,13 @@ const CasesTableFiltersComponent = ({ [onFilterChanged] ); + const onSeverityChanged = useCallback( + (severity: CaseSeverityWithAll) => { + onFilterChanged({ severity }); + }, + [onFilterChanged] + ); + const stats = useMemo( () => ({ [StatusAll]: null, @@ -181,6 +201,14 @@ const CasesTableFiltersComponent = ({ onSearch={handleOnSearch} /> + + + void; diff --git a/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx b/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx index 639d0617ddb74..fd0f7eebe0095 100644 --- a/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx @@ -5,29 +5,29 @@ * 2.0. */ -import React from 'react'; +import { act, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { mount } from 'enzyme'; -import { waitFor } from '@testing-library/react'; - +import React from 'react'; +import { ConnectorTypes } from '../../../common/api'; +import { AppMockRenderer, createAppMockRenderer, TestProviders } from '../../common/mock'; import '../../common/mock/match_media'; -import { CaseViewPage } from './case_view_page'; -import { CaseViewPageProps } from './types'; +import { useCaseViewNavigation, useUrlParams } from '../../common/navigation/hooks'; +import { useConnectors } from '../../containers/configure/use_connectors'; import { basicCaseClosed, basicCaseMetrics, caseUserActions, - getAlertUserAction, connectorsMock, + getAlertUserAction, } from '../../containers/mock'; -import { TestProviders } from '../../common/mock'; -import { useUpdateCase } from '../../containers/use_update_case'; +import { useGetCaseMetrics } from '../../containers/use_get_case_metrics'; import { useGetCaseUserActions } from '../../containers/use_get_case_user_actions'; - -import { useConnectors } from '../../containers/configure/use_connectors'; import { usePostPushToService } from '../../containers/use_post_push_to_service'; -import { useGetCaseMetrics } from '../../containers/use_get_case_metrics'; -import { ConnectorTypes } from '../../../common/api'; -import { caseViewProps, caseData } from './index.test'; +import { useUpdateCase } from '../../containers/use_update_case'; +import { CaseViewPage } from './case_view_page'; +import { caseData, caseViewProps } from './index.test'; +import { CaseViewPageProps, CASE_VIEW_PAGE_TABS } from './types'; jest.mock('../../containers/use_update_case'); jest.mock('../../containers/use_get_case_metrics'); @@ -37,7 +37,10 @@ jest.mock('../../containers/configure/use_connectors'); jest.mock('../../containers/use_post_push_to_service'); jest.mock('../user_actions/timestamp'); jest.mock('../../common/navigation/hooks'); +jest.mock('../../common/hooks'); +const useUrlParamsMock = useUrlParams as jest.Mock; +const useCaseViewNavigationMock = useCaseViewNavigation as jest.Mock; const useUpdateCaseMock = useUpdateCase as jest.Mock; const useGetCaseMetricsMock = useGetCaseMetrics as jest.Mock; const useGetCaseUserActionsMock = useGetCaseUserActions as jest.Mock; @@ -575,4 +578,108 @@ describe('CaseViewPage', () => { }); }); }); + + describe('Tabs', () => { + let appMockRender: AppMockRenderer; + beforeEach(() => { + appMockRender = createAppMockRenderer(); + }); + + // unskip when alerts tab is activated + it.skip('renders tabs correctly', async () => { + const result = appMockRender.render(); + await act(async () => { + expect(result.getByTestId('case-view-tab-title-alerts')).toBeTruthy(); + expect(result.getByTestId('case-view-tab-title-activity')).toBeTruthy(); + }); + }); + + it('renders the activity tab by default', async () => { + const result = appMockRender.render(); + await act(async () => { + expect(result.getByTestId('case-view-tab-content-activity')).toBeTruthy(); + }); + }); + + it('renders the alerts tab when the query parameter tabId has alerts', async () => { + useUrlParamsMock.mockReturnValue({ + urlParams: { + tabId: CASE_VIEW_PAGE_TABS.ALERTS, + }, + }); + const result = appMockRender.render(); + await act(async () => { + expect(result.getByTestId('case-view-tab-content-alerts')).toBeTruthy(); + }); + }); + + it('renders the activity tab when the query parameter tabId has activity', async () => { + useUrlParamsMock.mockReturnValue({ + urlParams: { + tabId: CASE_VIEW_PAGE_TABS.ACTIVITY, + }, + }); + const result = appMockRender.render(); + await act(async () => { + expect(result.getByTestId('case-view-tab-content-activity')).toBeTruthy(); + }); + }); + + it('renders the activity tab when the query parameter tabId has an unknown value', async () => { + useUrlParamsMock.mockReturnValue({ + urlParams: { + tabId: 'what-is-love', + }, + }); + const result = appMockRender.render(); + await act(async () => { + expect(result.getByTestId('case-view-tab-content-activity')).toBeTruthy(); + expect(result.queryByTestId('case-view-tab-content-alerts')).toBeFalsy(); + }); + }); + + it('navigates to the activity tab when the activity tab is clicked', async () => { + const navigateToCaseViewMock = useCaseViewNavigationMock().navigateToCaseView; + const result = appMockRender.render(); + userEvent.click(result.getByTestId('case-view-tab-title-activity')); + await act(async () => { + expect(navigateToCaseViewMock).toHaveBeenCalledWith({ + detailName: caseData.id, + tabId: CASE_VIEW_PAGE_TABS.ACTIVITY, + }); + }); + }); + + // unskip when alerts tab is activated + it.skip('navigates to the alerts tab when the alerts tab is clicked', async () => { + const navigateToCaseViewMock = useCaseViewNavigationMock().navigateToCaseView; + const result = appMockRender.render(); + userEvent.click(result.getByTestId('case-view-tab-title-alerts')); + await act(async () => { + expect(navigateToCaseViewMock).toHaveBeenCalledWith({ + detailName: caseData.id, + tabId: CASE_VIEW_PAGE_TABS.ALERTS, + }); + }); + }); + + // unskip when alerts tab is activated + it.skip('should display the alerts tab when the feature is enabled', async () => { + appMockRender = createAppMockRenderer({ features: { alerts: { enabled: true } } }); + const result = appMockRender.render(); + await act(async () => { + expect(result.queryByTestId('case-view-tab-title-activity')).toBeTruthy(); + expect(result.queryByTestId('case-view-tab-title-alerts')).toBeTruthy(); + }); + }); + + it('should not display the alerts tab when the feature is disabled', async () => { + appMockRender = createAppMockRenderer({ features: { alerts: { enabled: false } } }); + const result = appMockRender.render(); + await act(async () => { + expect(result.queryByTestId('case-view-tab-title-activity')).toBeTruthy(); + expect(result.queryByTestId('case-view-tab-title-alerts')).toBeFalsy(); + }); + }); + }); }); diff --git a/x-pack/plugins/cases/public/components/case_view/case_view_page.tsx b/x-pack/plugins/cases/public/components/case_view/case_view_page.tsx index d95eecde876fa..b6f22e9c5fb4d 100644 --- a/x-pack/plugins/cases/public/components/case_view/case_view_page.tsx +++ b/x-pack/plugins/cases/public/components/case_view/case_view_page.tsx @@ -5,24 +5,38 @@ * 2.0. */ -import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; - +import { + EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, + EuiLoadingLogo, + EuiSpacer, + EuiTab, + EuiTabs, +} from '@elastic/eui'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Case, UpdateKey } from '../../../common/ui'; -import { EditableTitle } from '../header_page/editable_title'; -import { ContentWrapper, WhitePageWrapper } from '../wrappers'; -import { CaseActionBar } from '../case_action_bar'; +import { useCaseViewNavigation, useUrlParams } from '../../common/navigation'; +import { useGetCaseMetrics } from '../../containers/use_get_case_metrics'; import { useGetCaseUserActions } from '../../containers/use_get_case_user_actions'; -import { useTimelineContext } from '../timeline_context/use_timeline_context'; import { useCasesContext } from '../cases_context/use_cases_context'; +import { useCasesFeatures } from '../cases_context/use_cases_features'; +import { CaseActionBar } from '../case_action_bar'; import { HeaderPage } from '../header_page'; +import { EditableTitle } from '../header_page/editable_title'; +import { useTimelineContext } from '../timeline_context/use_timeline_context'; import { useCasesTitleBreadcrumbs } from '../use_breadcrumbs'; -import { useGetCaseMetrics } from '../../containers/use_get_case_metrics'; +import { WhitePageWrapperNoBorder } from '../wrappers'; +import { CaseViewActivity } from './components/case_view_activity'; import { CaseViewMetrics } from './metrics'; -import type { CaseViewPageProps } from './types'; -import { useCasesFeatures } from '../cases_context/use_cases_features'; +import { ACTIVITY_TAB, ALERTS_TAB } from './translations'; +import { CaseViewPageProps, CASE_VIEW_PAGE_TABS } from './types'; import { useOnUpdateField } from './use_on_update_field'; -import { CaseViewActivity } from './components/case_view_activity'; + +// This hardcoded constant is left here intentionally +// as a way to hide a wip functionality +// that will be merge in the 8.3 release. +const ENABLE_ALERTS_TAB = false; export const CaseViewPage = React.memo( ({ @@ -37,10 +51,19 @@ export const CaseViewPage = React.memo( showAlertDetails, useFetchAlertData, }) => { - const { userCanCrud } = useCasesContext(); + const { userCanCrud, features } = useCasesContext(); const { metricsFeatures } = useCasesFeatures(); useCasesTitleBreadcrumbs(caseData.title); + const { navigateToCaseView } = useCaseViewNavigation(); + const { urlParams } = useUrlParams(); + const activeTabId = useMemo(() => { + if (urlParams.tabId && Object.values(CASE_VIEW_PAGE_TABS).includes(urlParams.tabId)) { + return urlParams.tabId; + } + return CASE_VIEW_PAGE_TABS.ACTIVITY; + }, [urlParams.tabId]); + const [initLoadingData, setInitLoadingData] = useState(true); const init = useRef(true); const timelineUi = useTimelineContext()?.ui; @@ -146,9 +169,76 @@ export const CaseViewPage = React.memo( } }, [onComponentInitialized]); + const tabs = useMemo( + () => [ + { + id: CASE_VIEW_PAGE_TABS.ACTIVITY, + name: ACTIVITY_TAB, + content: ( + + ), + }, + ...(features.alerts.enabled && ENABLE_ALERTS_TAB + ? [ + { + id: CASE_VIEW_PAGE_TABS.ALERTS, + name: ALERTS_TAB, + content: ( + } + title={

{'Alerts table placeholder'}

} + /> + ), + }, + ] + : []), + ], + [ + actionsNavigation, + caseData, + caseId, + features.alerts.enabled, + fetchCaseMetrics, + getCaseUserActions, + initLoadingData, + ruleDetailsNavigation, + showAlertDetails, + updateCase, + useFetchAlertData, + ] + ); + const selectedTabContent = useMemo(() => { + return tabs.find((obj) => obj.id === activeTabId)?.content; + }, [activeTabId, tabs]); + + const renderTabs = useCallback(() => { + return tabs.map((tab, index) => ( + navigateToCaseView({ detailName: caseId, tabId: tab.id })} + isSelected={tab.id === activeTabId} + > + {tab.name} + + )); + }, [activeTabId, caseId, navigateToCaseView, tabs]); + return ( <> ( /> - - - - - {!initLoadingData && metricsFeatures.length > 0 ? ( + + {!initLoadingData && metricsFeatures.length > 0 ? ( + <> + + - ) : null} - - - - - - - - + + + + + ) : null} + {renderTabs()} + + + {selectedTabContent} + + {timelineUi?.renderTimelineDetailsPanel ? timelineUi.renderTimelineDetailsPanel() : null} ); diff --git a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.tsx b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.tsx index b9e4beb5d7e26..452601d142848 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.tsx @@ -7,6 +7,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiLoadingContent } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; +import { CaseSeverity } from '../../../../common/api'; import { useConnectors } from '../../../containers/configure/use_connectors'; import { useCaseViewNavigation } from '../../../common/navigation'; import { UpdateKey, UseFetchAlertData } from '../../../../common/ui/types'; @@ -23,6 +24,7 @@ import * as i18n from '../translations'; import { getNoneConnector, normalizeActionConnector } from '../../configure_cases/utils'; import { getConnectorById } from '../../utils'; import { UseGetCaseUserActions } from '../../../containers/use_get_case_user_actions'; +import { SeveritySidebarSelector } from '../../severity/sidebar_selector'; export const CaseViewActivity = ({ initLoadingData, @@ -108,6 +110,12 @@ export const CaseViewActivity = ({ (newTags) => onUpdateField({ key: 'tags', value: newTags }), [onUpdateField] ); + + const onUpdateSeverity = useCallback( + (newSeverity: CaseSeverity) => onUpdateField({ key: 'severity', value: newSeverity }), + [onUpdateField] + ); + const { loading: isLoadingConnectors, connectors } = useConnectors(); const [connectorName, isValidConnector] = useMemo(() => { @@ -180,6 +188,12 @@ export const CaseViewActivity = ({ )} + { return ( {i18n.DOES_NOT_EXIST_TITLE}} titleSize="xs" body={

{i18n.DOES_NOT_EXIST_DESCRIPTION(caseId)}

} diff --git a/x-pack/plugins/cases/public/components/case_view/metrics/index.test.tsx b/x-pack/plugins/cases/public/components/case_view/metrics/index.test.tsx index f816d39e8c0e0..cecf49ee5d1a3 100644 --- a/x-pack/plugins/cases/public/components/case_view/metrics/index.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/metrics/index.test.tsx @@ -13,7 +13,7 @@ import { basicCaseStatusFeatures, } from '../../../containers/mock'; import { CaseViewMetrics } from '.'; -import { CaseMetrics, CaseMetricsFeature } from '../../../../common/ui'; +import { SingleCaseMetrics, SingleCaseMetricsFeature } from '../../../../common/ui'; import { TestProviders } from '../../../common/mock'; const renderCaseMetrics = ({ @@ -21,8 +21,8 @@ const renderCaseMetrics = ({ features = [...basicCaseNumericValueFeatures, ...basicCaseStatusFeatures], isLoading = false, }: { - metrics?: CaseMetrics; - features?: CaseMetricsFeature[]; + metrics?: SingleCaseMetrics; + features?: SingleCaseMetricsFeature[]; isLoading?: boolean; } = {}) => { return render( @@ -33,7 +33,7 @@ const renderCaseMetrics = ({ }; interface FeatureTest { - feature: CaseMetricsFeature; + feature: SingleCaseMetricsFeature; items: Array<{ title: string; value: string | number; diff --git a/x-pack/plugins/cases/public/components/case_view/metrics/status.tsx b/x-pack/plugins/cases/public/components/case_view/metrics/status.tsx index df19d1776d86a..d86c5534e1e40 100644 --- a/x-pack/plugins/cases/public/components/case_view/metrics/status.tsx +++ b/x-pack/plugins/cases/public/components/case_view/metrics/status.tsx @@ -9,7 +9,7 @@ import React, { useMemo } from 'react'; import prettyMilliseconds from 'pretty-ms'; import { EuiFlexGrid, EuiFlexGroup, EuiFlexItem, EuiIconTip, EuiSpacer } from '@elastic/eui'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; -import { CaseMetrics, CaseMetricsFeature } from '../../../../common/ui'; +import { SingleCaseMetrics, SingleCaseMetricsFeature } from '../../../../common/ui'; import { CASE_CREATED, CASE_IN_PROGRESS_DURATION, @@ -90,10 +90,10 @@ export const CaseStatusMetrics: React.FC { - return useMemo(() => { + metrics: SingleCaseMetrics | null, + features: SingleCaseMetricsFeature[] +): SingleCaseMetrics['lifespan'] | undefined => { + return useMemo(() => { const lifespan = metrics?.lifespan ?? { closeDate: '', creationDate: '', diff --git a/x-pack/plugins/cases/public/components/case_view/metrics/totals.tsx b/x-pack/plugins/cases/public/components/case_view/metrics/totals.tsx index 39b3f8613120a..77a0852901b9c 100644 --- a/x-pack/plugins/cases/public/components/case_view/metrics/totals.tsx +++ b/x-pack/plugins/cases/public/components/case_view/metrics/totals.tsx @@ -8,7 +8,7 @@ import React, { useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; -import { CaseMetrics, CaseMetricsFeature } from '../../../../common/ui'; +import { SingleCaseMetrics, SingleCaseMetricsFeature } from '../../../../common/ui'; import { ASSOCIATED_HOSTS_METRIC, ASSOCIATED_USERS_METRIC, @@ -50,8 +50,8 @@ interface MetricItem { type MetricItems = MetricItem[]; const useGetTitleValueMetricItems = ( - metrics: CaseMetrics | null, - features: CaseMetricsFeature[] + metrics: SingleCaseMetrics | null, + features: SingleCaseMetricsFeature[] ): MetricItems => { const { alerts, actions, connectors } = metrics ?? {}; const totalConnectors = connectors?.total ?? 0; @@ -61,7 +61,7 @@ const useGetTitleValueMetricItems = ( const totalIsolatedHosts = calculateTotalIsolatedHosts(actions); const metricItems = useMemo(() => { - const items: Array<[CaseMetricsFeature, Omit]> = [ + const items: Array<[SingleCaseMetricsFeature, Omit]> = [ ['alerts.count', { title: TOTAL_ALERTS_METRIC, value: alertsCount }], ['alerts.users', { title: ASSOCIATED_USERS_METRIC, value: totalAlertUsers }], ['alerts.hosts', { title: ASSOCIATED_HOSTS_METRIC, value: totalAlertHosts }], @@ -88,7 +88,7 @@ const useGetTitleValueMetricItems = ( return metricItems; }; -const calculateTotalIsolatedHosts = (actions: CaseMetrics['actions']) => { +const calculateTotalIsolatedHosts = (actions: SingleCaseMetrics['actions']) => { if (!actions?.isolateHost) { return 0; } diff --git a/x-pack/plugins/cases/public/components/case_view/metrics/types.ts b/x-pack/plugins/cases/public/components/case_view/metrics/types.ts index 5a00aed38dd8a..20138a482cb36 100644 --- a/x-pack/plugins/cases/public/components/case_view/metrics/types.ts +++ b/x-pack/plugins/cases/public/components/case_view/metrics/types.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { CaseMetrics, CaseMetricsFeature } from '../../../../common/ui'; +import { SingleCaseMetrics, SingleCaseMetricsFeature } from '../../../../common/ui'; export interface CaseViewMetricsProps { - metrics: CaseMetrics | null; - features: CaseMetricsFeature[]; + metrics: SingleCaseMetrics | null; + features: SingleCaseMetricsFeature[]; isLoading: boolean; } diff --git a/x-pack/plugins/cases/public/components/case_view/translations.ts b/x-pack/plugins/cases/public/components/case_view/translations.ts index 761cecb1121ca..94c19165e515b 100644 --- a/x-pack/plugins/cases/public/components/case_view/translations.ts +++ b/x-pack/plugins/cases/public/components/case_view/translations.ts @@ -155,3 +155,11 @@ export const DOES_NOT_EXIST_DESCRIPTION = (caseId: string) => export const DOES_NOT_EXIST_BUTTON = i18n.translate('xpack.cases.caseView.doesNotExist.button', { defaultMessage: 'Back to Cases', }); + +export const ACTIVITY_TAB = i18n.translate('xpack.cases.caseView.tabs.activity', { + defaultMessage: 'Activity', +}); + +export const ALERTS_TAB = i18n.translate('xpack.cases.caseView.tabs.alerts', { + defaultMessage: 'Alerts', +}); diff --git a/x-pack/plugins/cases/public/components/case_view/types.ts b/x-pack/plugins/cases/public/components/case_view/types.ts index 3d436a7db3186..2b806e5f804cd 100644 --- a/x-pack/plugins/cases/public/components/case_view/types.ts +++ b/x-pack/plugins/cases/public/components/case_view/types.ts @@ -41,3 +41,8 @@ export interface OnUpdateFields { onSuccess?: () => void; onError?: () => void; } + +export enum CASE_VIEW_PAGE_TABS { + ALERTS = 'alerts', + ACTIVITY = 'activity', +} diff --git a/x-pack/plugins/cases/public/components/case_view/use_on_update_field.ts b/x-pack/plugins/cases/public/components/case_view/use_on_update_field.ts index dba7719d24fbf..9820de137760a 100644 --- a/x-pack/plugins/cases/public/components/case_view/use_on_update_field.ts +++ b/x-pack/plugins/cases/public/components/case_view/use_on_update_field.ts @@ -72,6 +72,11 @@ export const useOnUpdateField = ({ callUpdate('settings', settingsUpdate); } break; + case 'severity': + const severityUpdate = getTypedPayload(value); + if (caseData.severity !== value) { + callUpdate('severity', severityUpdate); + } default: return null; } diff --git a/x-pack/plugins/cases/public/components/cases_context/use_cases_features.tsx b/x-pack/plugins/cases/public/components/cases_context/use_cases_features.tsx index 6241e81e419b9..b0316b5ff9665 100644 --- a/x-pack/plugins/cases/public/components/cases_context/use_cases_features.tsx +++ b/x-pack/plugins/cases/public/components/cases_context/use_cases_features.tsx @@ -6,13 +6,13 @@ */ import { useMemo } from 'react'; -import { CaseMetricsFeature } from '../../containers/types'; +import { SingleCaseMetricsFeature } from '../../containers/types'; import { useCasesContext } from './use_cases_context'; export interface UseCasesFeatures { isAlertsEnabled: boolean; isSyncAlertsEnabled: boolean; - metricsFeatures: CaseMetricsFeature[]; + metricsFeatures: SingleCaseMetricsFeature[]; } export const useCasesFeatures = (): UseCasesFeatures => { diff --git a/x-pack/plugins/cases/public/components/create/form.tsx b/x-pack/plugins/cases/public/components/create/form.tsx index ac2729564b387..50a3c69f2073e 100644 --- a/x-pack/plugins/cases/public/components/create/form.tsx +++ b/x-pack/plugins/cases/public/components/create/form.tsx @@ -35,6 +35,7 @@ import { CreateCaseOwnerSelector } from './owner_selector'; import { useCasesContext } from '../cases_context/use_cases_context'; import { useAvailableCasesOwners } from '../app/use_available_owners'; import { CaseAttachments } from '../../types'; +import { Severity } from './severity'; interface ContainerProps { big?: boolean; @@ -88,6 +89,9 @@ export const CreateCaseFormFields: React.FC = React.m + + + {canShowCaseSolutionSelection && ( = React.m + ), }), diff --git a/x-pack/plugins/cases/public/components/create/form_context.test.tsx b/x-pack/plugins/cases/public/components/create/form_context.test.tsx index 634f518ae5ebd..bfa4f391458da 100644 --- a/x-pack/plugins/cases/public/components/create/form_context.test.tsx +++ b/x-pack/plugins/cases/public/components/create/form_context.test.tsx @@ -10,7 +10,7 @@ import { mount, ReactWrapper } from 'enzyme'; import { act, RenderResult, waitFor, within } from '@testing-library/react'; import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { CommentType, ConnectorTypes } from '../../../common/api'; +import { CaseSeverity, CommentType, ConnectorTypes } from '../../../common/api'; import { useKibana } from '../../common/lib/kibana'; import { AppMockRenderer, createAppMockRenderer, TestProviders } from '../../common/mock'; import { usePostCase } from '../../containers/use_post_case'; @@ -182,6 +182,7 @@ describe('Create case', () => { ); expect(renderResult.getByTestId('caseTitle')).toBeTruthy(); + expect(renderResult.getByTestId('caseSeverity')).toBeTruthy(); expect(renderResult.getByTestId('caseDescription')).toBeTruthy(); expect(renderResult.getByTestId('caseTags')).toBeTruthy(); expect(renderResult.getByTestId('caseConnectors')).toBeTruthy(); @@ -208,6 +209,34 @@ describe('Create case', () => { }); }); + it('should post a case on submit click with the selected severity', async () => { + useConnectorsMock.mockReturnValue({ + ...sampleConnectorData, + connectors: connectorsMock, + }); + + const renderResult = mockedContext.render( + + + + + ); + + await fillFormReactTestingLib(renderResult); + + userEvent.click(renderResult.getByTestId('case-severity-selection')); + expect(renderResult.getByTestId('case-severity-selection-high')).toBeTruthy(); + userEvent.click(renderResult.getByTestId('case-severity-selection-high')); + + userEvent.click(renderResult.getByTestId('create-case-submit')); + await waitFor(() => { + expect(postCase).toBeCalledWith({ + ...sampleData, + severity: CaseSeverity.HIGH, + }); + }); + }); + it('does not submits the title when the length is longer than 64 characters', async () => { const longTitle = 'This is a title that should not be saved as it is longer than 64 characters.'; @@ -285,6 +314,18 @@ describe('Create case', () => { ); }); + it('should select LOW as the default severity', async () => { + const renderResult = mockedContext.render( + + + + + ); + expect(renderResult.getByTestId('caseSeverity')).toBeTruthy(); + // there should be 2 low elements. one for the options popover and one for the displayed one. + expect(renderResult.getAllByTestId('case-severity-selection-low').length).toBe(2); + }); + it('should select the default connector set in the configuration', async () => { useCaseConfigureMock.mockImplementation(() => ({ ...useCaseConfigureResponse, diff --git a/x-pack/plugins/cases/public/components/create/form_context.tsx b/x-pack/plugins/cases/public/components/create/form_context.tsx index 4385053a8c8c0..a65e9f5960e9d 100644 --- a/x-pack/plugins/cases/public/components/create/form_context.tsx +++ b/x-pack/plugins/cases/public/components/create/form_context.tsx @@ -14,7 +14,7 @@ import { usePostPushToService } from '../../containers/use_post_push_to_service' import { useConnectors } from '../../containers/configure/use_connectors'; import { Case } from '../../containers/types'; -import { NONE_CONNECTOR_ID } from '../../../common/api'; +import { CaseSeverity, NONE_CONNECTOR_ID } from '../../../common/api'; import { UseCreateAttachments, useCreateAttachments, @@ -28,6 +28,7 @@ const initialCaseValue: FormProps = { description: '', tags: [], title: '', + severity: CaseSeverity.LOW, connectorId: NONE_CONNECTOR_ID, fields: null, syncAlerts: true, diff --git a/x-pack/plugins/cases/public/components/create/mock.ts b/x-pack/plugins/cases/public/components/create/mock.ts index 8ab515c79f67e..38d57bf24781e 100644 --- a/x-pack/plugins/cases/public/components/create/mock.ts +++ b/x-pack/plugins/cases/public/components/create/mock.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { CasePostRequest, ConnectorTypes } from '../../../common/api'; +import { CasePostRequest, CaseSeverity, ConnectorTypes } from '../../../common/api'; import { SECURITY_SOLUTION_OWNER } from '../../../common/constants'; import { choices } from '../connectors/mock'; @@ -13,6 +13,7 @@ export const sampleTags = ['coke', 'pepsi']; export const sampleData: CasePostRequest = { description: 'what a great description', tags: sampleTags, + severity: CaseSeverity.LOW, title: 'what a cool title', connector: { fields: null, diff --git a/x-pack/plugins/cases/public/components/create/schema.tsx b/x-pack/plugins/cases/public/components/create/schema.tsx index b7c363b263998..d72b1cc523f0d 100644 --- a/x-pack/plugins/cases/public/components/create/schema.tsx +++ b/x-pack/plugins/cases/public/components/create/schema.tsx @@ -17,6 +17,7 @@ import { import * as i18n from './translations'; import { OptionalFieldLabel } from './optional_field_label'; +import { SEVERITY_TITLE } from '../severity/translations'; const { emptyField, maxLengthField } = fieldValidators; export const schemaTags = { @@ -83,6 +84,9 @@ export const schema: FormSchema = { ], }, tags: schemaTags, + severity: { + label: SEVERITY_TITLE, + }, connectorId: { type: FIELD_TYPES.SUPER_SELECT, label: i18n.CONNECTORS, diff --git a/x-pack/plugins/cases/public/components/create/severity.test.tsx b/x-pack/plugins/cases/public/components/create/severity.test.tsx new file mode 100644 index 0000000000000..d2434a37a4392 --- /dev/null +++ b/x-pack/plugins/cases/public/components/create/severity.test.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CaseSeverity } from '../../../common/api'; +import React from 'react'; +import { AppMockRenderer, createAppMockRenderer } from '../../common/mock'; +import { Form, FormHook, useForm } from '../../common/shared_imports'; +import { Severity } from './severity'; +import { FormProps, schema } from './schema'; +import userEvent from '@testing-library/user-event'; +import { waitFor } from '@testing-library/dom'; + +let globalForm: FormHook; +const MockHookWrapperComponent: React.FC = ({ children }) => { + const { form } = useForm({ + defaultValue: { severity: CaseSeverity.LOW }, + schema: { + severity: schema.severity, + }, + }); + + globalForm = form; + + return
{children}
; +}; +describe('Severity form field', () => { + let appMockRender: AppMockRenderer; + beforeEach(() => { + appMockRender = createAppMockRenderer(); + }); + it('renders', () => { + const result = appMockRender.render( + + + + ); + expect(result.getByTestId('caseSeverity')).toBeTruthy(); + expect(result.getByTestId('case-severity-selection')).not.toHaveAttribute('disabled'); + }); + + // default to LOW in this test configuration + it('defaults to the correct value', () => { + const result = appMockRender.render( + + + + ); + expect(result.getByTestId('caseSeverity')).toBeTruthy(); + // two items. one for the popover one for the selected field + expect(result.getAllByTestId('case-severity-selection-low').length).toBe(2); + }); + + it('selects the correct value when changed', async () => { + const result = appMockRender.render( + + + + ); + expect(result.getByTestId('caseSeverity')).toBeTruthy(); + userEvent.click(result.getByTestId('case-severity-selection')); + userEvent.click(result.getByTestId('case-severity-selection-high')); + await waitFor(() => { + expect(globalForm.getFormData()).toEqual({ severity: 'high' }); + }); + }); + + it('disables when loading data', () => { + const result = appMockRender.render( + + + + ); + expect(result.getByTestId('case-severity-selection')).toHaveAttribute('disabled'); + }); +}); diff --git a/x-pack/plugins/cases/public/components/create/severity.tsx b/x-pack/plugins/cases/public/components/create/severity.tsx new file mode 100644 index 0000000000000..730eab5d77ac6 --- /dev/null +++ b/x-pack/plugins/cases/public/components/create/severity.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFormRow } from '@elastic/eui'; +import React, { memo } from 'react'; +import { CaseSeverity } from '../../../common/api'; +import { UseField, useFormContext, useFormData } from '../../common/shared_imports'; +import { SeveritySelector } from '../severity/selector'; +import { SEVERITY_TITLE } from '../severity/translations'; + +interface Props { + isLoading: boolean; +} + +const SeverityFieldFormComponent = ({ isLoading }: { isLoading: boolean }) => { + const { setFieldValue } = useFormContext(); + const [{ severity }] = useFormData({ watch: ['severity'] }); + const onSeverityChange = (newSeverity: CaseSeverity) => { + setFieldValue('severity', newSeverity); + }; + return ( + + + + ); +}; +SeverityFieldFormComponent.displayName = 'SeverityFieldForm'; + +const SeverityComponent: React.FC = ({ isLoading }) => ( + +); + +SeverityComponent.displayName = 'SeverityComponent'; + +export const Severity = memo(SeverityComponent); diff --git a/x-pack/plugins/cases/public/components/no_privileges/index.tsx b/x-pack/plugins/cases/public/components/no_privileges/index.tsx index 58d2aa36df583..2e5a4ef48839f 100644 --- a/x-pack/plugins/cases/public/components/no_privileges/index.tsx +++ b/x-pack/plugins/cases/public/components/no_privileges/index.tsx @@ -21,7 +21,7 @@ export const NoPrivilegesPage = React.memo(({ pageName }: NoPrivilegesPageProps) return ( {i18n.NO_PRIVILEGES_TITLE}} titleSize="xs" body={

{i18n.NO_PRIVILEGES_MSG(pageName)}

} diff --git a/x-pack/plugins/cases/public/components/severity/config.ts b/x-pack/plugins/cases/public/components/severity/config.ts new file mode 100644 index 0000000000000..e22f7bda54665 --- /dev/null +++ b/x-pack/plugins/cases/public/components/severity/config.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 { euiLightVars } from '@kbn/ui-theme'; +import { CaseSeverity } from '../../../common/api'; +import { SeverityAll } from '../../containers/types'; +import { ALL_SEVERITIES, CRITICAL, HIGH, LOW, MEDIUM } from './translations'; + +export const severities = { + [CaseSeverity.LOW]: { + color: euiLightVars.euiColorVis0, + label: LOW, + }, + [CaseSeverity.MEDIUM]: { + color: euiLightVars.euiColorVis5, + label: MEDIUM, + }, + [CaseSeverity.HIGH]: { + color: euiLightVars.euiColorVis7, + label: HIGH, + }, + [CaseSeverity.CRITICAL]: { + color: euiLightVars.euiColorVis9, + label: CRITICAL, + }, +}; + +export const severitiesWithAll = { + [SeverityAll]: { + color: 'transparent', + label: ALL_SEVERITIES, + }, + ...severities, +}; diff --git a/x-pack/plugins/cases/public/components/severity/selector.test.tsx b/x-pack/plugins/cases/public/components/severity/selector.test.tsx new file mode 100644 index 0000000000000..126dc64e7af1b --- /dev/null +++ b/x-pack/plugins/cases/public/components/severity/selector.test.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CaseSeverity } from '../../../common/api'; +import { render } from '@testing-library/react'; +import React from 'react'; +import { SeveritySelector } from './selector'; +import userEvent from '@testing-library/user-event'; + +describe('Severity field selector', () => { + const onSeverityChange = jest.fn(); + it('renders a list of severity fields', () => { + const result = render( + + ); + + expect(result.getByTestId('case-severity-selection')).toBeTruthy(); + expect(result.getAllByTestId('case-severity-selection-medium').length).toBeTruthy(); + }); + + it('renders a list of severity options when clicked', () => { + const result = render( + + ); + userEvent.click(result.getByTestId('case-severity-selection')); + expect(result.getByTestId('case-severity-selection-low')).toBeTruthy(); + expect(result.getAllByTestId('case-severity-selection-medium').length).toBeTruthy(); + expect(result.getByTestId('case-severity-selection-high')).toBeTruthy(); + expect(result.getByTestId('case-severity-selection-critical')).toBeTruthy(); + }); + + it('calls onSeverityChange with the newly selected severity when clicked', () => { + const result = render( + + ); + userEvent.click(result.getByTestId('case-severity-selection')); + expect(result.getByTestId('case-severity-selection-low')).toBeTruthy(); + userEvent.click(result.getByTestId('case-severity-selection-low')); + expect(onSeverityChange).toHaveBeenLastCalledWith('low'); + }); +}); diff --git a/x-pack/plugins/cases/public/components/severity/selector.tsx b/x-pack/plugins/cases/public/components/severity/selector.tsx new file mode 100644 index 0000000000000..0d1ff4b319f2b --- /dev/null +++ b/x-pack/plugins/cases/public/components/severity/selector.tsx @@ -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 { + EuiFlexGroup, + EuiFlexItem, + EuiHealth, + EuiSuperSelect, + EuiSuperSelectOption, +} from '@elastic/eui'; +import React from 'react'; +import { CaseSeverity } from '../../../common/api'; +import { severities } from './config'; + +interface Props { + selectedSeverity: CaseSeverity; + onSeverityChange: (status: CaseSeverity) => void; + isLoading: boolean; + isDisabled: boolean; +} + +export const SeveritySelector: React.FC = ({ + selectedSeverity, + onSeverityChange, + isLoading, + isDisabled, +}) => { + const caseSeverities = Object.keys(severities) as CaseSeverity[]; + const options: Array> = caseSeverities.map((severity) => { + const severityData = severities[severity]; + return { + value: severity, + inputDisplay: ( + + + {severityData.label} + + + ), + }; + }); + + return ( + + ); +}; +SeveritySelector.displayName = 'SeveritySelector'; diff --git a/x-pack/plugins/cases/public/components/severity/sidebar_selector.tsx b/x-pack/plugins/cases/public/components/severity/sidebar_selector.tsx new file mode 100644 index 0000000000000..ff591e342793f --- /dev/null +++ b/x-pack/plugins/cases/public/components/severity/sidebar_selector.tsx @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFlexItem, EuiHorizontalRule, EuiSpacer, EuiText } from '@elastic/eui'; +import React from 'react'; +import { CaseSeverity } from '../../../common/api'; +import { SeveritySelector } from './selector'; +import { SEVERITY_TITLE } from './translations'; + +interface Props { + selectedSeverity: CaseSeverity; + onSeverityChange: (status: CaseSeverity) => void; + isLoading: boolean; + isDisabled: boolean; +} + +export const SeveritySidebarSelector: React.FC = ({ + selectedSeverity, + onSeverityChange, + isLoading, + isDisabled, +}) => { + return ( + + +

{SEVERITY_TITLE}

+
+ + + +
+ ); +}; +SeveritySidebarSelector.displayName = 'SeveritySidebarSelector'; diff --git a/x-pack/plugins/cases/public/components/severity/translations.ts b/x-pack/plugins/cases/public/components/severity/translations.ts new file mode 100644 index 0000000000000..b70dbebe41d19 --- /dev/null +++ b/x-pack/plugins/cases/public/components/severity/translations.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const LOW = i18n.translate('xpack.cases.severity.low', { + defaultMessage: 'Low', +}); + +export const MEDIUM = i18n.translate('xpack.cases.severity.medium', { + defaultMessage: 'Medium', +}); + +export const HIGH = i18n.translate('xpack.cases.severity.high', { + defaultMessage: 'High', +}); + +export const CRITICAL = i18n.translate('xpack.cases.severity.critical', { + defaultMessage: 'Critical', +}); + +export const SEVERITY_TITLE = i18n.translate('xpack.cases.severity.title', { + defaultMessage: 'Severity', +}); + +export const ALL_SEVERITIES = i18n.translate('xpack.cases.severity.all', { + defaultMessage: 'All severities', +}); diff --git a/x-pack/plugins/cases/public/components/status/translations.ts b/x-pack/plugins/cases/public/components/status/translations.ts index b3eadfd681ba5..4fe75bbcfac7a 100644 --- a/x-pack/plugins/cases/public/components/status/translations.ts +++ b/x-pack/plugins/cases/public/components/status/translations.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; export * from '../../common/translations'; export const ALL = i18n.translate('xpack.cases.status.all', { - defaultMessage: 'All', + defaultMessage: 'All status', }); export const OPEN = i18n.translate('xpack.cases.status.open', { diff --git a/x-pack/plugins/cases/public/components/user_actions/builder.tsx b/x-pack/plugins/cases/public/components/user_actions/builder.tsx index 5e1c11fbdd2df..36298bbae601b 100644 --- a/x-pack/plugins/cases/public/components/user_actions/builder.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/builder.tsx @@ -10,6 +10,7 @@ import { createConnectorUserActionBuilder } from './connector'; import { createDescriptionUserActionBuilder } from './description'; import { createPushedUserActionBuilder } from './pushed'; import { createSettingsUserActionBuilder } from './settings'; +import { createSeverityUserActionBuilder } from './severity'; import { createStatusUserActionBuilder } from './status'; import { createTagsUserActionBuilder } from './tags'; import { createTitleUserActionBuilder } from './title'; @@ -20,6 +21,7 @@ export const builderMap: UserActionBuilderMap = { tags: createTagsUserActionBuilder, title: createTitleUserActionBuilder, status: createStatusUserActionBuilder, + severity: createSeverityUserActionBuilder, pushed: createPushedUserActionBuilder, comment: createCommentUserActionBuilder, description: createDescriptionUserActionBuilder, diff --git a/x-pack/plugins/cases/public/components/user_actions/severity.test.tsx b/x-pack/plugins/cases/public/components/user_actions/severity.test.tsx new file mode 100644 index 0000000000000..d92a5cb5a153d --- /dev/null +++ b/x-pack/plugins/cases/public/components/user_actions/severity.test.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiCommentList } from '@elastic/eui'; +import { Actions, CaseSeverity } from '../../../common/api'; +import React from 'react'; +import { AppMockRenderer, createAppMockRenderer } from '../../common/mock'; +import { getUserAction } from '../../containers/mock'; +import { getMockBuilderArgs } from './mock'; +import { createSeverityUserActionBuilder } from './severity'; + +jest.mock('../../common/lib/kibana'); +jest.mock('../../common/navigation/hooks'); + +const builderArgs = getMockBuilderArgs(); +describe('createSeverityUserActionBuilder', () => { + let appMockRenderer: AppMockRenderer; + beforeEach(() => { + appMockRenderer = createAppMockRenderer(); + }); + it('renders correctly', () => { + const userAction = getUserAction('severity', Actions.update, { + payload: { severity: CaseSeverity.LOW }, + }); + const builder = createSeverityUserActionBuilder({ + ...builderArgs, + userAction, + }); + const createdUserAction = builder.build(); + + const result = appMockRenderer.render(); + expect(result.getByTestId('severity-update-user-action-severity-title')).toBeTruthy(); + expect(result.getByTestId('severity-update-user-action-severity-title-low')).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/cases/public/components/user_actions/severity.tsx b/x-pack/plugins/cases/public/components/user_actions/severity.tsx new file mode 100644 index 0000000000000..3e2cf8605b080 --- /dev/null +++ b/x-pack/plugins/cases/public/components/user_actions/severity.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 { EuiFlexGroup, EuiFlexItem, EuiHealth } from '@elastic/eui'; +import React from 'react'; +import { SeverityUserAction } from '../../../common/api/cases/user_actions/severity'; +import { SET_SEVERITY_TO } from '../create/translations'; +import { createCommonUpdateUserActionBuilder } from './common'; +import { UserActionBuilder, UserActionResponse } from './types'; +import { severities } from '../severity/config'; + +const getLabelTitle = (userAction: UserActionResponse) => { + const severity = userAction.payload.severity; + const severityData = severities[severity]; + if (severityData === undefined) { + return null; + } + return ( + + {SET_SEVERITY_TO} + + {severityData.label} + + + ); +}; + +export const createSeverityUserActionBuilder: UserActionBuilder = ({ + userAction, + handleOutlineComment, +}) => ({ + build: () => { + const severityUserAction = userAction as UserActionResponse; + const label = getLabelTitle(severityUserAction); + const commonBuilder = createCommonUpdateUserActionBuilder({ + userAction, + handleOutlineComment, + label, + icon: 'dot', + }); + + return commonBuilder.build(); + }, +}); diff --git a/x-pack/plugins/cases/public/components/wrappers/index.tsx b/x-pack/plugins/cases/public/components/wrappers/index.tsx index d412ef34451b2..54c575ab95316 100644 --- a/x-pack/plugins/cases/public/components/wrappers/index.tsx +++ b/x-pack/plugins/cases/public/components/wrappers/index.tsx @@ -13,6 +13,10 @@ export const WhitePageWrapper = styled.div` flex: 1 1 auto; `; +export const WhitePageWrapperNoBorder = styled.div` + background-color: ${({ theme }) => theme.eui.euiColorEmptyShade}; + flex: 1 1 auto; +`; export const SectionWrapper = styled.div` box-sizing: content-box; margin: 0 auto; diff --git a/x-pack/plugins/cases/public/containers/__mocks__/api.ts b/x-pack/plugins/cases/public/containers/__mocks__/api.ts index 1f5c1652edfff..c330fb7eb9cf0 100644 --- a/x-pack/plugins/cases/public/containers/__mocks__/api.ts +++ b/x-pack/plugins/cases/public/containers/__mocks__/api.ts @@ -7,7 +7,7 @@ import { ActionLicense, - AllCases, + Cases, BulkUpdateStatus, Case, CasesStatus, @@ -29,14 +29,14 @@ import { respReporters, tags, } from '../mock'; -import { ResolvedCase } from '../../../common/ui/types'; +import { ResolvedCase, SeverityAll } from '../../../common/ui/types'; import { CasePatchRequest, CasePostRequest, CommentRequest, User, CaseStatuses, - CaseMetricsResponse, + SingleCaseMetricsResponse, } from '../../../common/api'; export const getCase = async ( @@ -51,10 +51,10 @@ export const resolveCase = async ( signal: AbortSignal ): Promise => Promise.resolve(basicResolvedCase); -export const getCaseMetrics = async ( +export const getSingleCaseMetrics = async ( caseId: string, signal: AbortSignal -): Promise => Promise.resolve(basicCaseMetrics); +): Promise => Promise.resolve(basicCaseMetrics); export const getCasesStatus = async (signal: AbortSignal): Promise => Promise.resolve(casesStatus); @@ -71,6 +71,7 @@ export const getCaseUserActions = async ( export const getCases = async ({ filterOptions = { + severity: SeverityAll, search: '', reporters: [], status: CaseStatuses.open, @@ -84,7 +85,7 @@ export const getCases = async ({ sortOrder: 'desc', }, signal, -}: FetchCasesProps): Promise => Promise.resolve(allCases); +}: FetchCasesProps): Promise => Promise.resolve(allCases); export const postCase = async (newCase: CasePostRequest, signal: AbortSignal): Promise => Promise.resolve(basicCasePost); diff --git a/x-pack/plugins/cases/public/containers/api.test.tsx b/x-pack/plugins/cases/public/containers/api.test.tsx index 6b8bd6f65bc6d..e37955b2768c0 100644 --- a/x-pack/plugins/cases/public/containers/api.test.tsx +++ b/x-pack/plugins/cases/public/containers/api.test.tsx @@ -5,9 +5,10 @@ * 2.0. */ +import { httpServiceMock } from '@kbn/core/public/mocks'; import { KibanaServices } from '../common/lib/kibana'; -import { ConnectorTypes, CommentType, CaseStatuses } from '../../common/api'; +import { ConnectorTypes, CommentType, CaseStatuses, CaseSeverity } from '../../common/api'; import { CASES_URL, INTERNAL_BULK_CREATE_ATTACHMENTS_URL, @@ -20,7 +21,6 @@ import { getActionLicense, getCase, getCases, - getCasesStatus, getCaseUserActions, getReporters, getTags, @@ -54,6 +54,7 @@ import { } from './mock'; import { DEFAULT_FILTER_OPTIONS, DEFAULT_QUERY_PARAMS } from './use_get_cases'; +import { getCasesStatus } from '../api'; const abortCtrl = new AbortController(); const mockKibanaServices = KibanaServices.get as jest.Mock; @@ -205,6 +206,47 @@ describe('Case Configuration API', () => { }); }); + test('should apply the severity field correctly (with severity value)', async () => { + await getCases({ + filterOptions: { + ...DEFAULT_FILTER_OPTIONS, + severity: CaseSeverity.HIGH, + }, + queryParams: DEFAULT_QUERY_PARAMS, + signal: abortCtrl.signal, + }); + expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/_find`, { + method: 'GET', + query: { + ...DEFAULT_QUERY_PARAMS, + reporters: [], + tags: [], + severity: CaseSeverity.HIGH, + }, + signal: abortCtrl.signal, + }); + }); + + test('should not send the severity field with "all" severity value', async () => { + await getCases({ + filterOptions: { + ...DEFAULT_FILTER_OPTIONS, + severity: 'all', + }, + queryParams: DEFAULT_QUERY_PARAMS, + signal: abortCtrl.signal, + }); + expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/_find`, { + method: 'GET', + query: { + ...DEFAULT_QUERY_PARAMS, + reporters: [], + tags: [], + }, + signal: abortCtrl.signal, + }); + }); + test('should handle tags with weird chars', async () => { const weirdTags: string[] = ['(', '"double"']; @@ -246,21 +288,33 @@ describe('Case Configuration API', () => { }); describe('getCasesStatus', () => { + const http = httpServiceMock.createStartContract({ basePath: '' }); + http.get.mockResolvedValue(casesStatusSnake); + beforeEach(() => { fetchMock.mockClear(); - fetchMock.mockResolvedValue(casesStatusSnake); }); + test('should be called with correct check url, method, signal', async () => { - await getCasesStatus(abortCtrl.signal, [SECURITY_SOLUTION_OWNER]); - expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/status`, { - method: 'GET', + await getCasesStatus({ + http, + signal: abortCtrl.signal, + query: { owner: [SECURITY_SOLUTION_OWNER] }, + }); + + expect(http.get).toHaveBeenCalledWith(`${CASES_URL}/status`, { signal: abortCtrl.signal, query: { owner: [SECURITY_SOLUTION_OWNER] }, }); }); test('should return correct response', async () => { - const resp = await getCasesStatus(abortCtrl.signal, [SECURITY_SOLUTION_OWNER]); + const resp = await getCasesStatus({ + http, + signal: abortCtrl.signal, + query: { owner: SECURITY_SOLUTION_OWNER }, + }); + expect(resp).toEqual(casesStatus); }); }); diff --git a/x-pack/plugins/cases/public/containers/api.ts b/x-pack/plugins/cases/public/containers/api.ts index d4593dd1f2813..b0f00ad202c5f 100644 --- a/x-pack/plugins/cases/public/containers/api.ts +++ b/x-pack/plugins/cases/public/containers/api.ts @@ -5,18 +5,21 @@ * 2.0. */ -import { omit } from 'lodash'; - -import { StatusAll, ResolvedCase } from '../../common/ui/types'; +import { + Cases, + FetchCasesProps, + ResolvedCase, + SeverityAll, + SortFieldCase, + StatusAll, +} from '../../common/ui/types'; import { BulkCreateCommentRequest, CasePatchRequest, CasePostRequest, CaseResponse, CaseResolveResponse, - CasesFindResponse, CasesResponse, - CasesStatusResponse, CaseUserActionsResponse, CommentRequest, CommentType, @@ -26,12 +29,12 @@ import { getCasePushUrl, getCaseUserActionUrl, User, - CaseMetricsResponse, getCaseCommentDeleteUrl, + SingleCaseMetricsResponse, + CasesFindResponse, } from '../../common/api'; import { CASE_REPORTERS_URL, - CASE_STATUS_URL, CASE_TAGS_URL, CASES_URL, INTERNAL_BULK_CREATE_ATTACHMENTS_URL, @@ -40,31 +43,25 @@ import { getAllConnectorTypesUrl } from '../../common/utils/connectors_api'; import { KibanaServices } from '../common/lib/kibana'; +import { convertAllCasesToCamel, convertToCamelCase, convertArrayToCamelCase } from '../api/utils'; + import { ActionLicense, - AllCases, BulkUpdateStatus, Case, - CaseMetrics, - CaseMetricsFeature, - CasesStatus, - FetchCasesProps, - SortFieldCase, + SingleCaseMetrics, + SingleCaseMetricsFeature, CaseUserActions, } from './types'; import { - convertToCamelCase, - convertAllCasesToCamel, - convertArrayToCamelCase, decodeCaseResponse, decodeCasesResponse, - decodeCasesFindResponse, - decodeCasesStatusResponse, decodeCaseUserActionsResponse, decodeCaseResolveResponse, - decodeCaseMetricsResponse, + decodeSingleCaseMetricsResponse, } from './utils'; +import { decodeCasesFindResponse } from '../api/decoders'; export const getCase = async ( caseId: string, @@ -99,18 +96,6 @@ export const resolveCase = async ( return convertToCamelCase(decodeCaseResolveResponse(response)); }; -export const getCasesStatus = async ( - signal: AbortSignal, - owner: string[] -): Promise => { - const response = await KibanaServices.get().http.fetch(CASE_STATUS_URL, { - method: 'GET', - signal, - query: { ...(owner.length > 0 ? { owner } : {}) }, - }); - return convertToCamelCase(decodeCasesStatusResponse(response)); -}; - export const getTags = async (signal: AbortSignal, owner: string[]): Promise => { const response = await KibanaServices.get().http.fetch(CASE_TAGS_URL, { method: 'GET', @@ -129,12 +114,12 @@ export const getReporters = async (signal: AbortSignal, owner: string[]): Promis return response ?? []; }; -export const getCaseMetrics = async ( +export const getSingleCaseMetrics = async ( caseId: string, - features: CaseMetricsFeature[], + features: SingleCaseMetricsFeature[], signal: AbortSignal -): Promise => { - const response = await KibanaServices.get().http.fetch( +): Promise => { + const response = await KibanaServices.get().http.fetch( getCaseDetailsMetricsUrl(caseId), { method: 'GET', @@ -142,7 +127,9 @@ export const getCaseMetrics = async ( query: { features: JSON.stringify(features) }, } ); - return convertToCamelCase(decodeCaseMetricsResponse(response)); + return convertToCamelCase( + decodeSingleCaseMetricsResponse(response) + ); }; export const getCaseUserActions = async ( @@ -162,6 +149,7 @@ export const getCaseUserActions = async ( export const getCases = async ({ filterOptions = { search: '', + severity: SeverityAll, reporters: [], status: StatusAll, tags: [], @@ -174,20 +162,23 @@ export const getCases = async ({ sortOrder: 'desc', }, signal, -}: FetchCasesProps): Promise => { +}: FetchCasesProps): Promise => { const query = { + ...(filterOptions.status !== StatusAll ? { status: filterOptions.status } : {}), + ...(filterOptions.severity !== SeverityAll ? { severity: filterOptions.severity } : {}), reporters: filterOptions.reporters.map((r) => r.username ?? '').filter((r) => r !== ''), tags: filterOptions.tags, - status: filterOptions.status, ...(filterOptions.search.length > 0 ? { search: filterOptions.search } : {}), ...(filterOptions.owner.length > 0 ? { owner: filterOptions.owner } : {}), ...queryParams, }; + const response = await KibanaServices.get().http.fetch(`${CASES_URL}/_find`, { method: 'GET', - query: query.status === StatusAll ? omit(query, ['status']) : query, + query, signal, }); + return convertAllCasesToCamel(decodeCasesFindResponse(response)); }; diff --git a/x-pack/plugins/cases/public/containers/configure/api.ts b/x-pack/plugins/cases/public/containers/configure/api.ts index 32202afc34881..53e045882cb7c 100644 --- a/x-pack/plugins/cases/public/containers/configure/api.ts +++ b/x-pack/plugins/cases/public/containers/configure/api.ts @@ -18,14 +18,9 @@ import { } from '../../../common/api'; import { CASE_CONFIGURE_CONNECTORS_URL, CASE_CONFIGURE_URL } from '../../../common/constants'; import { KibanaServices } from '../../common/lib/kibana'; - +import { convertToCamelCase, convertArrayToCamelCase } from '../../api/utils'; import { ApiProps } from '../types'; -import { - convertArrayToCamelCase, - convertToCamelCase, - decodeCaseConfigurationsResponse, - decodeCaseConfigureResponse, -} from '../utils'; +import { decodeCaseConfigurationsResponse, decodeCaseConfigureResponse } from '../utils'; import { CaseConfigure } from './types'; export const fetchConnectors = async ({ signal }: ApiProps): Promise => { diff --git a/x-pack/plugins/cases/public/containers/mock.ts b/x-pack/plugins/cases/public/containers/mock.ts index 69931629a77cb..ed9e9ebd1ff8f 100644 --- a/x-pack/plugins/cases/public/containers/mock.ts +++ b/x-pack/plugins/cases/public/containers/mock.ts @@ -5,12 +5,12 @@ * 2.0. */ -import { ActionLicense, AllCases, Case, CasesStatus, CaseUserActions, Comment } from './types'; +import { ActionLicense, Cases, Case, CasesStatus, CaseUserActions, Comment } from './types'; import type { ResolvedCase, - CaseMetrics, - CaseMetricsFeature, + SingleCaseMetrics, + SingleCaseMetricsFeature, AlertComment, } from '../../common/ui/types'; import { @@ -31,6 +31,7 @@ import { UserActionTypes, UserActionWithResponse, CommentUserAction, + CaseSeverity, } from '../../common/api'; import { SECURITY_SOLUTION_OWNER } from '../../common/constants'; import { UseGetCasesState, DEFAULT_FILTER_OPTIONS, DEFAULT_QUERY_PARAMS } from './use_get_cases'; @@ -154,6 +155,8 @@ export const basicCase: Case = { fields: null, }, description: 'Security banana Issue', + severity: CaseSeverity.LOW, + duration: null, externalService: null, status: CaseStatuses.open, tags, @@ -188,7 +191,7 @@ export const basicResolvedCase: ResolvedCase = { aliasTargetId: `${basicCase.id}_2`, }; -export const basicCaseNumericValueFeatures: CaseMetricsFeature[] = [ +export const basicCaseNumericValueFeatures: SingleCaseMetricsFeature[] = [ 'alerts.count', 'alerts.users', 'alerts.hosts', @@ -196,9 +199,9 @@ export const basicCaseNumericValueFeatures: CaseMetricsFeature[] = [ 'connectors', ]; -export const basicCaseStatusFeatures: CaseMetricsFeature[] = ['lifespan']; +export const basicCaseStatusFeatures: SingleCaseMetricsFeature[] = ['lifespan']; -export const basicCaseMetrics: CaseMetrics = { +export const basicCaseMetrics: SingleCaseMetrics = { alerts: { count: 12, hosts: { @@ -245,6 +248,8 @@ export const mockCase: Case = { type: ConnectorTypes.none, fields: null, }, + duration: null, + severity: CaseSeverity.LOW, description: 'Security banana Issue', externalService: null, status: CaseStatuses.open, @@ -328,7 +333,7 @@ export const cases: Case[] = [ caseWithAlertsSyncOff, ]; -export const allCases: AllCases = { +export const allCases: Cases = { cases, page: 1, perPage: 5, @@ -383,6 +388,7 @@ export const basicCaseSnake: CaseResponse = { connector: { id: 'none', name: 'My Connector', type: ConnectorTypes.none, fields: null }, created_at: basicCreatedAt, created_by: elasticUserSnake, + duration: null, external_service: null, updated_at: basicUpdatedAt, updated_by: elasticUserSnake, @@ -509,6 +515,7 @@ export const getUserAction = ( description: 'a desc', connector: { ...getJiraConnector() }, status: CaseStatuses.open, + severity: CaseSeverity.LOW, title: 'a title', tags: ['a tag'], settings: { syncAlerts: true }, diff --git a/x-pack/plugins/cases/public/containers/use_get_case_metrics.test.tsx b/x-pack/plugins/cases/public/containers/use_get_case_metrics.test.tsx index 73c69ec388977..4c7d446f4f27f 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case_metrics.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case_metrics.test.tsx @@ -6,7 +6,7 @@ */ import { renderHook, act } from '@testing-library/react-hooks'; -import { CaseMetricsFeature } from '../../common/ui'; +import { SingleCaseMetricsFeature } from '../../common/ui'; import { useGetCaseMetrics, UseGetCaseMetrics } from './use_get_case_metrics'; import { basicCase, basicCaseMetrics } from './mock'; import * as api from './api'; @@ -16,7 +16,7 @@ jest.mock('../common/lib/kibana'); describe('useGetCaseMetrics', () => { const abortCtrl = new AbortController(); - const features: CaseMetricsFeature[] = ['alerts.count']; + const features: SingleCaseMetricsFeature[] = ['alerts.count']; beforeEach(() => { jest.clearAllMocks(); @@ -38,8 +38,8 @@ describe('useGetCaseMetrics', () => { }); }); - it('calls getCaseMetrics with correct arguments', async () => { - const spyOnGetCaseMetrics = jest.spyOn(api, 'getCaseMetrics'); + it('calls getSingleCaseMetrics with correct arguments', async () => { + const spyOnGetCaseMetrics = jest.spyOn(api, 'getSingleCaseMetrics'); await act(async () => { const { waitForNextUpdate } = renderHook(() => useGetCaseMetrics(basicCase.id, features) @@ -50,8 +50,8 @@ describe('useGetCaseMetrics', () => { }); }); - it('does not call getCaseMetrics if empty feature parameter passed', async () => { - const spyOnGetCaseMetrics = jest.spyOn(api, 'getCaseMetrics'); + it('does not call getSingleCaseMetrics if empty feature parameter passed', async () => { + const spyOnGetCaseMetrics = jest.spyOn(api, 'getSingleCaseMetrics'); await act(async () => { const { waitForNextUpdate } = renderHook(() => useGetCaseMetrics(basicCase.id, []) @@ -78,7 +78,7 @@ describe('useGetCaseMetrics', () => { }); it('refetch case metrics', async () => { - const spyOnGetCaseMetrics = jest.spyOn(api, 'getCaseMetrics'); + const spyOnGetCaseMetrics = jest.spyOn(api, 'getSingleCaseMetrics'); await act(async () => { const { result, waitForNextUpdate } = renderHook(() => useGetCaseMetrics(basicCase.id, features) @@ -116,8 +116,8 @@ describe('useGetCaseMetrics', () => { }); }); - it('returns an error when getCaseMetrics throws', async () => { - const spyOnGetCaseMetrics = jest.spyOn(api, 'getCaseMetrics'); + it('returns an error when getSingleCaseMetrics throws', async () => { + const spyOnGetCaseMetrics = jest.spyOn(api, 'getSingleCaseMetrics'); spyOnGetCaseMetrics.mockImplementation(() => { throw new Error('Something went wrong'); }); diff --git a/x-pack/plugins/cases/public/containers/use_get_case_metrics.tsx b/x-pack/plugins/cases/public/containers/use_get_case_metrics.tsx index 411b43e050abf..774ecfd2371b6 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case_metrics.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case_metrics.tsx @@ -7,20 +7,20 @@ import { useEffect, useReducer, useCallback, useRef } from 'react'; -import { CaseMetrics, CaseMetricsFeature } from './types'; +import { SingleCaseMetrics, SingleCaseMetricsFeature } from './types'; import * as i18n from './translations'; import { useToasts } from '../common/lib/kibana'; -import { getCaseMetrics } from './api'; +import { getSingleCaseMetrics } from './api'; interface CaseMeticsState { - metrics: CaseMetrics | null; + metrics: SingleCaseMetrics | null; isLoading: boolean; isError: boolean; } type Action = | { type: 'FETCH_INIT'; payload: { silent: boolean } } - | { type: 'FETCH_SUCCESS'; payload: CaseMetrics } + | { type: 'FETCH_SUCCESS'; payload: SingleCaseMetrics } | { type: 'FETCH_FAILURE' }; const dataFetchReducer = (state: CaseMeticsState, action: Action): CaseMeticsState => { @@ -59,7 +59,7 @@ export interface UseGetCaseMetrics extends CaseMeticsState { export const useGetCaseMetrics = ( caseId: string, - features: CaseMetricsFeature[] + features: SingleCaseMetricsFeature[] ): UseGetCaseMetrics => { const [state, dispatch] = useReducer(dataFetchReducer, { metrics: null, @@ -81,7 +81,7 @@ export const useGetCaseMetrics = ( abortCtrlRef.current = new AbortController(); dispatch({ type: 'FETCH_INIT', payload: { silent } }); - const response: CaseMetrics = await getCaseMetrics( + const response: SingleCaseMetrics = await getSingleCaseMetrics( caseId, features, abortCtrlRef.current.signal diff --git a/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx b/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx index dee4d424c84de..b689746a7af00 100644 --- a/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { renderHook, act } from '@testing-library/react-hooks'; -import { CaseStatuses } from '../../common/api'; +import { CaseSeverity, CaseStatuses } from '../../common/api'; import { SECURITY_SOLUTION_OWNER } from '../../common/constants'; import { DEFAULT_FILTER_OPTIONS, @@ -219,6 +219,7 @@ describe('useGetCases', () => { const spyOnGetCases = jest.spyOn(api, 'getCases'); const newFilters = { search: 'new', + severity: CaseSeverity.LOW, tags: ['new'], status: CaseStatuses.closed, owner: [SECURITY_SOLUTION_OWNER], diff --git a/x-pack/plugins/cases/public/containers/use_get_cases.tsx b/x-pack/plugins/cases/public/containers/use_get_cases.tsx index 283cdfdf39aa4..f708d98282252 100644 --- a/x-pack/plugins/cases/public/containers/use_get_cases.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_cases.tsx @@ -8,20 +8,21 @@ import { useCallback, useEffect, useReducer, useRef } from 'react'; import { DEFAULT_TABLE_ACTIVE_PAGE, DEFAULT_TABLE_LIMIT } from './constants'; import { - AllCases, + Cases, Case, FilterOptions, QueryParams, SortFieldCase, StatusAll, UpdateByKey, + SeverityAll, } from './types'; import { useToasts } from '../common/lib/kibana'; import * as i18n from './translations'; import { getCases, patchCase } from './api'; export interface UseGetCasesState { - data: AllCases; + data: Cases; filterOptions: FilterOptions; isError: boolean; loading: string[]; @@ -39,7 +40,7 @@ export type Action = | { type: 'FETCH_INIT'; payload: string } | { type: 'FETCH_CASES_SUCCESS'; - payload: AllCases; + payload: Cases; } | { type: 'FETCH_FAILURE'; payload: string } | { type: 'FETCH_UPDATE_CASE_SUCCESS' } @@ -101,6 +102,7 @@ const dataFetchReducer = (state: UseGetCasesState, action: Action): UseGetCasesS export const DEFAULT_FILTER_OPTIONS: FilterOptions = { search: '', + severity: SeverityAll, reporters: [], status: StatusAll, tags: [], @@ -114,11 +116,11 @@ export const DEFAULT_QUERY_PARAMS: QueryParams = { sortOrder: 'desc', }; -export const initialData: AllCases = { +export const initialData: Cases = { cases: [], - countClosedCases: null, - countInProgressCases: null, - countOpenCases: null, + countClosedCases: 0, + countInProgressCases: 0, + countOpenCases: 0, page: 0, perPage: 0, total: 0, diff --git a/x-pack/plugins/cases/public/containers/use_get_cases_status.test.tsx b/x-pack/plugins/cases/public/containers/use_get_cases_status.test.tsx index 2e3e42255145d..3978e944db949 100644 --- a/x-pack/plugins/cases/public/containers/use_get_cases_status.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_cases_status.test.tsx @@ -9,11 +9,11 @@ import React from 'react'; import { renderHook, act } from '@testing-library/react-hooks'; import { useGetCasesStatus, UseGetCasesStatus } from './use_get_cases_status'; import { casesStatus } from './mock'; -import * as api from './api'; +import * as api from '../api'; import { TestProviders } from '../common/mock'; import { SECURITY_SOLUTION_OWNER } from '../../common/constants'; -jest.mock('./api'); +jest.mock('../api'); jest.mock('../common/lib/kibana'); describe('useGetCasesStatus', () => { @@ -30,9 +30,9 @@ describe('useGetCasesStatus', () => { await act(async () => { expect(result.current).toEqual({ - countClosedCases: null, - countOpenCases: null, - countInProgressCases: null, + countClosedCases: 0, + countOpenCases: 0, + countInProgressCases: 0, isLoading: true, isError: false, fetchCasesStatus: result.current.fetchCasesStatus, @@ -49,12 +49,17 @@ describe('useGetCasesStatus', () => { wrapper: ({ children }) => {children}, } ); + await waitForNextUpdate(); - expect(spyOnGetCasesStatus).toBeCalledWith(abortCtrl.signal, [SECURITY_SOLUTION_OWNER]); + expect(spyOnGetCasesStatus).toBeCalledWith({ + http: expect.anything(), + signal: abortCtrl.signal, + query: { owner: [SECURITY_SOLUTION_OWNER] }, + }); }); }); - it('fetch reporters', async () => { + it('fetch statuses', async () => { await act(async () => { const { result, waitForNextUpdate } = renderHook( () => useGetCasesStatus(), @@ -62,6 +67,7 @@ describe('useGetCasesStatus', () => { wrapper: ({ children }) => {children}, } ); + await waitForNextUpdate(); expect(result.current).toEqual({ countClosedCases: casesStatus.countClosedCases, diff --git a/x-pack/plugins/cases/public/containers/use_get_cases_status.tsx b/x-pack/plugins/cases/public/containers/use_get_cases_status.tsx index 5e21c339856fb..6530236a2fee6 100644 --- a/x-pack/plugins/cases/public/containers/use_get_cases_status.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_cases_status.tsx @@ -8,10 +8,10 @@ import { useCallback, useEffect, useState, useRef } from 'react'; import { useCasesContext } from '../components/cases_context/use_cases_context'; -import { getCasesStatus } from './api'; import * as i18n from './translations'; import { CasesStatus } from './types'; -import { useToasts } from '../common/lib/kibana'; +import { useHttp, useToasts } from '../common/lib/kibana'; +import { getCasesStatus } from '../api'; interface CasesStatusState extends CasesStatus { isLoading: boolean; @@ -19,9 +19,9 @@ interface CasesStatusState extends CasesStatus { } const initialData: CasesStatusState = { - countClosedCases: null, - countInProgressCases: null, - countOpenCases: null, + countClosedCases: 0, + countInProgressCases: 0, + countOpenCases: 0, isLoading: true, isError: false, }; @@ -31,6 +31,7 @@ export interface UseGetCasesStatus extends CasesStatusState { } export const useGetCasesStatus = (): UseGetCasesStatus => { + const http = useHttp(); const { owner } = useCasesContext(); const [casesStatusState, setCasesStatusState] = useState(initialData); const toasts = useToasts(); @@ -47,7 +48,11 @@ export const useGetCasesStatus = (): UseGetCasesStatus => { isLoading: true, }); - const response = await getCasesStatus(abortCtrlRef.current.signal, owner); + const response = await getCasesStatus({ + http, + signal: abortCtrlRef.current.signal, + query: { owner }, + }); if (!isCancelledRef.current) { setCasesStatusState({ diff --git a/x-pack/plugins/cases/public/containers/utils.ts b/x-pack/plugins/cases/public/containers/utils.ts index cd1682c0cd988..a9a0eff53c07c 100644 --- a/x-pack/plugins/cases/public/containers/utils.ts +++ b/x-pack/plugins/cases/public/containers/utils.ts @@ -5,22 +5,17 @@ * 2.0. */ -import { set } from '@elastic/safer-lodash-set'; -import { camelCase, isArray, isObject, transform, snakeCase } from 'lodash'; +import { isObject, transform, snakeCase } from 'lodash'; import { fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/pipeable'; import { ToastInputFields } from '@kbn/core/public'; import { - CasesFindResponse, - CasesFindResponseRt, CaseResponse, CaseResponseRt, CasesResponse, CasesResponseRt, - CasesStatusResponseRt, - CasesStatusResponse, throwErrors, CasesConfigurationsResponse, CaseConfigurationsResponseRt, @@ -32,10 +27,10 @@ import { CasePatchRequest, CaseResolveResponse, CaseResolveResponseRt, - CaseMetricsResponse, - CaseMetricsResponseRt, + SingleCaseMetricsResponse, + SingleCaseMetricsResponseRt, } from '../../common/api'; -import { AllCases, Case, UpdateByKey } from './types'; +import { Case, UpdateByKey } from './types'; import * as i18n from './translations'; export const getTypedPayload = (a: unknown): T => a as T; @@ -46,45 +41,6 @@ export const covertToSnakeCase = (obj: Record) => acc[camelKey] = isObject(value) ? covertToSnakeCase(value as Record) : value; }); -export const convertArrayToCamelCase = (arrayOfSnakes: unknown[]): unknown[] => - arrayOfSnakes.reduce((acc: unknown[], value) => { - if (isArray(value)) { - return [...acc, convertArrayToCamelCase(value)]; - } else if (isObject(value)) { - return [...acc, convertToCamelCase(value)]; - } else { - return [...acc, value]; - } - }, []); - -export const convertToCamelCase = (obj: T): U => - Object.entries(obj).reduce((acc, [key, value]) => { - if (isArray(value)) { - set(acc, camelCase(key), convertArrayToCamelCase(value)); - } else if (isObject(value)) { - set(acc, camelCase(key), convertToCamelCase(value)); - } else { - set(acc, camelCase(key), value); - } - return acc; - }, {} as U); - -export const convertAllCasesToCamel = (snakeCases: CasesFindResponse): AllCases => ({ - cases: snakeCases.cases.map((theCase) => convertToCamelCase(theCase)), - countOpenCases: snakeCases.count_open_cases, - countInProgressCases: snakeCases.count_in_progress_cases, - countClosedCases: snakeCases.count_closed_cases, - page: snakeCases.page, - perPage: snakeCases.per_page, - total: snakeCases.total, -}); - -export const decodeCasesStatusResponse = (respCase?: CasesStatusResponse) => - pipe( - CasesStatusResponseRt.decode(respCase), - fold(throwErrors(createToasterPlainError), identity) - ); - export const createToasterPlainError = (message: string) => new ToasterError([message]); export const decodeCaseResponse = (respCase?: CaseResponse) => @@ -96,18 +52,15 @@ export const decodeCaseResolveResponse = (respCase?: CaseResolveResponse) => fold(throwErrors(createToasterPlainError), identity) ); -export const decodeCaseMetricsResponse = (respCase?: CaseMetricsResponse) => +export const decodeSingleCaseMetricsResponse = (respCase?: SingleCaseMetricsResponse) => pipe( - CaseMetricsResponseRt.decode(respCase), + SingleCaseMetricsResponseRt.decode(respCase), fold(throwErrors(createToasterPlainError), identity) ); export const decodeCasesResponse = (respCase?: CasesResponse) => pipe(CasesResponseRt.decode(respCase), fold(throwErrors(createToasterPlainError), identity)); -export const decodeCasesFindResponse = (respCases?: CasesFindResponse) => - pipe(CasesFindResponseRt.decode(respCases), fold(throwErrors(createToasterPlainError), identity)); - export const decodeCaseConfigurationsResponse = (respCase?: CasesConfigurationsResponse) => { return pipe( CaseConfigurationsResponseRt.decode(respCase), diff --git a/x-pack/plugins/cases/public/mocks.ts b/x-pack/plugins/cases/public/mocks.ts index f8c0eaaaef7de..5ec7a10c88acc 100644 --- a/x-pack/plugins/cases/public/mocks.ts +++ b/x-pack/plugins/cases/public/mocks.ts @@ -10,7 +10,7 @@ import { CasesUiStart } from './types'; const apiMock: jest.Mocked = { getRelatedCases: jest.fn(), - cases: { find: jest.fn(), getAllCasesMetrics: jest.fn() }, + cases: { find: jest.fn(), getCasesMetrics: jest.fn(), getCasesStatus: jest.fn() }, }; const uiMock: jest.Mocked = { diff --git a/x-pack/plugins/cases/public/plugin.ts b/x-pack/plugins/cases/public/plugin.ts index 3b124f920e889..559d670eeab68 100644 --- a/x-pack/plugins/cases/public/plugin.ts +++ b/x-pack/plugins/cases/public/plugin.ts @@ -47,7 +47,7 @@ export class CasesUiPlugin id: APP_ID, title: APP_TITLE, description: APP_DESC, - icon: 'watchesApp', + icon: 'casesApp', path: APP_PATH, showOnHomePage: false, category: 'admin', diff --git a/x-pack/plugins/cases/public/types.ts b/x-pack/plugins/cases/public/types.ts index 65cc1de25d345..bff5e5fd14700 100644 --- a/x-pack/plugins/cases/public/types.ts +++ b/x-pack/plugins/cases/public/types.ts @@ -21,9 +21,8 @@ import { CasesByAlertId, CasesByAlertIDRequest, CasesFindRequest, - CasesResponse, + CasesMetricsRequest, CasesStatusRequest, - CasesStatusResponse, CommentRequestAlertType, CommentRequestUserType, } from '../common/api'; @@ -36,6 +35,7 @@ import type { GetCasesProps } from './client/ui/get_cases'; import { GetAllCasesSelectorModalProps } from './client/ui/get_all_cases_selector_modal'; import { GetCreateCaseFlyoutProps } from './client/ui/get_create_case_flyout'; import { GetRecentCasesProps } from './client/ui/get_recent_cases'; +import { Cases, CasesStatus, CasesMetrics } from '../common/ui'; export interface CasesPluginSetup { security: SecurityPluginSetup; @@ -76,8 +76,9 @@ export interface CasesUiStart { api: { getRelatedCases: (alertId: string, query: CasesByAlertIDRequest) => Promise; cases: { - find: (query: CasesFindRequest) => Promise; - getAllCasesMetrics: (query: CasesStatusRequest) => Promise; + find: (query: CasesFindRequest, signal?: AbortSignal) => Promise; + getCasesStatus: (query: CasesStatusRequest, signal?: AbortSignal) => Promise; + getCasesMetrics: (query: CasesMetricsRequest, signal?: AbortSignal) => Promise; }; }; ui: { diff --git a/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap b/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap index 0ec6dffee02ea..bbeb9ce05445b 100644 --- a/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap +++ b/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap @@ -1344,6 +1344,90 @@ Object { } `; +exports[`audit_logger log function event structure creates the correct audit event for operation: "getCasesMetrics" with an error and entity 1`] = ` +Object { + "error": Object { + "code": "Error", + "message": "an error", + }, + "event": Object { + "action": "cases_get_metrics", + "category": Array [ + "database", + ], + "outcome": "failure", + "type": Array [ + "access", + ], + }, + "kibana": Object { + "saved_object": Object { + "id": "1", + "type": "cases", + }, + }, + "message": "Failed attempt to access cases [id=1] as owner \\"awesome\\"", +} +`; + +exports[`audit_logger log function event structure creates the correct audit event for operation: "getCasesMetrics" with an error but no entity 1`] = ` +Object { + "error": Object { + "code": "Error", + "message": "an error", + }, + "event": Object { + "action": "cases_get_metrics", + "category": Array [ + "database", + ], + "outcome": "failure", + "type": Array [ + "access", + ], + }, + "message": "Failed attempt to access a cases as any owners", +} +`; + +exports[`audit_logger log function event structure creates the correct audit event for operation: "getCasesMetrics" without an error but with an entity 1`] = ` +Object { + "event": Object { + "action": "cases_get_metrics", + "category": Array [ + "database", + ], + "outcome": "success", + "type": Array [ + "access", + ], + }, + "kibana": Object { + "saved_object": Object { + "id": "5", + "type": "cases", + }, + }, + "message": "User has accessed cases [id=5] as owner \\"super\\"", +} +`; + +exports[`audit_logger log function event structure creates the correct audit event for operation: "getCasesMetrics" without an error or entity 1`] = ` +Object { + "event": Object { + "action": "cases_get_metrics", + "category": Array [ + "database", + ], + "outcome": "success", + "type": Array [ + "access", + ], + }, + "message": "User has accessed a cases as any owners", +} +`; + exports[`audit_logger log function event structure creates the correct audit event for operation: "getComment" with an error and entity 1`] = ` Object { "error": Object { diff --git a/x-pack/plugins/cases/server/authorization/index.ts b/x-pack/plugins/cases/server/authorization/index.ts index cd3ceebf02f92..122eb90f44dc1 100644 --- a/x-pack/plugins/cases/server/authorization/index.ts +++ b/x-pack/plugins/cases/server/authorization/index.ts @@ -126,6 +126,14 @@ const CaseOperations = { docType: 'case', savedObjectType: CASE_SAVED_OBJECT, }, + [ReadOperations.GetCasesMetrics]: { + ecsType: EVENT_TYPES.access, + name: ACCESS_CASE_OPERATION, + action: 'cases_get_metrics', + verbs: accessVerbs, + docType: 'cases', + savedObjectType: CASE_SAVED_OBJECT, + }, [WriteOperations.CreateCase]: { ecsType: EVENT_TYPES.creation, name: WriteOperations.CreateCase as const, diff --git a/x-pack/plugins/cases/server/authorization/types.ts b/x-pack/plugins/cases/server/authorization/types.ts index 8c672ffb9d245..81c3d0746aa33 100644 --- a/x-pack/plugins/cases/server/authorization/types.ts +++ b/x-pack/plugins/cases/server/authorization/types.ts @@ -43,6 +43,7 @@ export enum ReadOperations { GetAlertsAttachedToCase = 'getAlertsAttachedToCase', GetAttachmentMetrics = 'getAttachmentMetrics', GetCaseMetrics = 'getCaseMetrics', + GetCasesMetrics = 'getCasesMetrics', GetUserActionMetrics = 'getUserActionMetrics', } diff --git a/x-pack/plugins/cases/server/client/cases/create.ts b/x-pack/plugins/cases/server/client/cases/create.ts index ab9f6a4305800..714c8199d11a5 100644 --- a/x-pack/plugins/cases/server/client/cases/create.ts +++ b/x-pack/plugins/cases/server/client/cases/create.ts @@ -14,12 +14,13 @@ import { SavedObjectsUtils } from '@kbn/core/server'; import { throwErrors, - excess, CaseResponseRt, CaseResponse, CasePostRequest, ActionTypes, CasePostRequestRt, + excess, + CaseSeverity, } from '../../../common/api'; import { MAX_TITLE_LENGTH } from '../../../common/constants'; import { isInvalidTag } from '../../../common/utils/validators'; @@ -85,7 +86,7 @@ export const create = async ( unsecuredSavedObjectsClient, caseId: newCase.id, user, - payload: query, + payload: { ...query, severity: query.severity ?? CaseSeverity.LOW }, owner: newCase.attributes.owner, }); diff --git a/x-pack/plugins/cases/server/client/cases/find.ts b/x-pack/plugins/cases/server/client/cases/find.ts index b5d3cee05ced6..0c22229692842 100644 --- a/x-pack/plugins/cases/server/client/cases/find.ts +++ b/x-pack/plugins/cases/server/client/cases/find.ts @@ -53,6 +53,7 @@ export const find = async ( reporters: queryParams.reporters, sortByField: queryParams.sortField, status: queryParams.status, + severity: queryParams.severity, owner: queryParams.owner, from: queryParams.from, to: queryParams.to, diff --git a/x-pack/plugins/cases/server/client/cases/mock.ts b/x-pack/plugins/cases/server/client/cases/mock.ts index 69a5f2d3a587b..4c0698b209bef 100644 --- a/x-pack/plugins/cases/server/client/cases/mock.ts +++ b/x-pack/plugins/cases/server/client/cases/mock.ts @@ -240,6 +240,7 @@ export const userActions: CaseUserActionsResponse = [ }, settings: { syncAlerts: true }, status: 'open', + severity: 'low', owner: SECURITY_SOLUTION_OWNER, }, action_id: 'fd830c60-6646-11eb-a291-51bf6b175a53', diff --git a/x-pack/plugins/cases/server/client/cases/update.ts b/x-pack/plugins/cases/server/client/cases/update.ts index ae53cb03c28a7..6569dcd3f52b2 100644 --- a/x-pack/plugins/cases/server/client/cases/update.ts +++ b/x-pack/plugins/cases/server/client/cases/update.ts @@ -50,6 +50,7 @@ import { import { UpdateAlertRequest } from '../alerts/types'; import { CasesClientArgs } from '..'; import { Operations, OwnerEntity } from '../../authorization'; +import { getClosedInfoForUpdate, getDurationForUpdate } from './utils'; /** * Throws an error if any of the requests attempt to update the owner of a case. @@ -311,37 +312,29 @@ export const update = async ( throwIfUpdateOwner(updateCases); throwIfTitleIsInvalid(updateCases); - // eslint-disable-next-line @typescript-eslint/naming-convention - const { username, full_name, email } = user; const updatedDt = new Date().toISOString(); const updatedCases = await caseService.patchCases({ cases: updateCases.map(({ updateReq, originalCase }) => { // intentionally removing owner from the case so that we don't accidentally allow it to be updated const { id: caseId, version, owner, ...updateCaseAttributes } = updateReq; - let closedInfo = {}; - if (updateCaseAttributes.status && updateCaseAttributes.status === CaseStatuses.closed) { - closedInfo = { - closed_at: updatedDt, - closed_by: { email, full_name, username }, - }; - } else if ( - updateCaseAttributes.status && - (updateCaseAttributes.status === CaseStatuses.open || - updateCaseAttributes.status === CaseStatuses['in-progress']) - ) { - closedInfo = { - closed_at: null, - closed_by: null, - }; - } + return { caseId, originalCase, updatedAttributes: { ...updateCaseAttributes, - ...closedInfo, + ...getClosedInfoForUpdate({ + user, + closedDate: updatedDt, + status: updateCaseAttributes.status, + }), + ...getDurationForUpdate({ + status: updateCaseAttributes.status, + closedAt: updatedDt, + createdAt: originalCase.attributes.created_at, + }), updated_at: updatedDt, - updated_by: { email, full_name, username }, + updated_by: user, }, version, }; diff --git a/x-pack/plugins/cases/server/client/cases/utils.test.ts b/x-pack/plugins/cases/server/client/cases/utils.test.ts index 3dca8014957da..4832ffe5b2eaf 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.test.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.test.ts @@ -24,13 +24,15 @@ import { import { createIncident, + getClosedInfoForUpdate, + getDurationForUpdate, getLatestPushInfo, prepareFieldsForTransformation, transformComments, transformers, transformFields, } from './utils'; -import { Actions } from '../../../common/api'; +import { Actions, CaseStatuses } from '../../../common/api'; import { flattenCaseSavedObject } from '../../common/utils'; import { SECURITY_SOLUTION_OWNER } from '../../../common/constants'; import { casesConnectors } from '../../connectors'; @@ -836,5 +838,119 @@ describe('utils', () => { }); }); }); + + describe('getClosedInfoForUpdate', () => { + const date = '2021-02-03T17:41:26.108Z'; + const user = { full_name: 'Elastic', username: 'elastic', email: 'elastic@elastic.co' }; + + it('returns the correct closed info when the case closes', async () => { + expect( + getClosedInfoForUpdate({ status: CaseStatuses.closed, closedDate: date, user }) + ).toEqual({ + closed_at: date, + closed_by: user, + }); + }); + + it.each([[CaseStatuses.open], [CaseStatuses['in-progress']]])( + 'returns the correct closed info when the case %s', + async (status) => { + expect(getClosedInfoForUpdate({ status, closedDate: date, user })).toEqual({ + closed_at: null, + closed_by: null, + }); + } + ); + + it('returns undefined if the status is not provided', async () => { + expect(getClosedInfoForUpdate({ closedDate: date, user })).toBe(undefined); + }); + }); + + describe('getDurationForUpdate', () => { + const createdAt = '2021-11-23T19:00:00Z'; + const closedAt = '2021-11-23T19:02:00Z'; + + it('returns the correct duration when the case closes', () => { + expect(getDurationForUpdate({ status: CaseStatuses.closed, closedAt, createdAt })).toEqual({ + duration: 120, + }); + }); + + it.each([[CaseStatuses.open], [CaseStatuses['in-progress']]])( + 'returns the correct duration when the case %s', + (status) => { + expect(getDurationForUpdate({ status, closedAt, createdAt })).toEqual({ + duration: null, + }); + } + ); + + it('returns undefined if the status is not provided', async () => { + expect(getDurationForUpdate({ closedAt, createdAt })).toBe(undefined); + }); + + it.each([['invalid'], [null]])( + 'returns undefined if the createdAt date is %s', + (createdAtInvalid) => { + expect( + getDurationForUpdate({ + status: CaseStatuses.closed, + closedAt, + // @ts-expect-error + createdAt: createdAtInvalid, + }) + ).toBe(undefined); + } + ); + + it.each([['invalid'], [null]])( + 'returns undefined if the closedAt date is %s', + (closedAtInvalid) => { + expect( + getDurationForUpdate({ + status: CaseStatuses.closed, + // @ts-expect-error + closedAt: closedAtInvalid, + createdAt, + }) + ).toBe(undefined); + } + ); + + it('returns undefined if created_at > closed_at', async () => { + expect( + getDurationForUpdate({ + status: CaseStatuses.closed, + closedAt: '2021-11-23T19:00:00Z', + createdAt: '2021-11-23T19:05:00Z', + }) + ).toBe(undefined); + }); + + it('rounds the seconds correctly', () => { + expect( + getDurationForUpdate({ + status: CaseStatuses.closed, + createdAt: '2022-04-11T15:56:00.087Z', + closedAt: '2022-04-11T15:58:56.187Z', + }) + ).toEqual({ + duration: 176, + }); + }); + + it('rounds the zero correctly', () => { + expect( + getDurationForUpdate({ + status: CaseStatuses.closed, + createdAt: '2022-04-11T15:56:00.087Z', + closedAt: '2022-04-11T15:56:00.187Z', + }) + ).toEqual({ + duration: 0, + }); + }); + }); }); }); diff --git a/x-pack/plugins/cases/server/client/cases/utils.ts b/x-pack/plugins/cases/server/client/cases/utils.ts index 42a9a54466906..01c1a9ab897b5 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.ts @@ -23,6 +23,9 @@ import { CommentRequestAlertType, CommentRequestActionsType, ActionTypes, + CaseStatuses, + User, + CaseAttributes, } from '../../../common/api'; import { CasesClientGetAlertsResponse } from '../alerts/types'; import { @@ -405,3 +408,62 @@ export const getCommentContextFromAttributes = ( }; } }; + +export const getClosedInfoForUpdate = ({ + user, + status, + closedDate, +}: { + closedDate: string; + user: User; + status?: CaseStatuses; +}): Pick | undefined => { + if (status && status === CaseStatuses.closed) { + return { + closed_at: closedDate, + closed_by: user, + }; + } + + if (status && (status === CaseStatuses.open || status === CaseStatuses['in-progress'])) { + return { + closed_at: null, + closed_by: null, + }; + } +}; + +export const getDurationForUpdate = ({ + status, + closedAt, + createdAt, +}: { + closedAt: string; + createdAt: CaseAttributes['created_at']; + status?: CaseStatuses; +}): Pick | undefined => { + if (status && status === CaseStatuses.closed) { + try { + if (createdAt != null && closedAt != null) { + const createdAtMillis = new Date(createdAt).getTime(); + const closedAtMillis = new Date(closedAt).getTime(); + + if ( + !isNaN(createdAtMillis) && + !isNaN(closedAtMillis) && + closedAtMillis >= createdAtMillis + ) { + return { duration: Math.floor((closedAtMillis - createdAtMillis) / 1000) }; + } + } + } catch (err) { + // Silence date errors + } + } + + if (status && (status === CaseStatuses.open || status === CaseStatuses['in-progress'])) { + return { + duration: null, + }; + } +}; diff --git a/x-pack/plugins/cases/server/client/metrics/actions/actions.ts b/x-pack/plugins/cases/server/client/metrics/actions/actions.ts index c700c3998e503..4eecc37339c2d 100644 --- a/x-pack/plugins/cases/server/client/metrics/actions/actions.ts +++ b/x-pack/plugins/cases/server/client/metrics/actions/actions.ts @@ -6,30 +6,32 @@ */ import { merge } from 'lodash'; -import { CaseMetricsResponse } from '../../../../common/api'; +import { SingleCaseMetricsResponse } from '../../../../common/api'; import { Operations } from '../../../authorization'; import { createCaseError } from '../../../common/error'; -import { AggregationHandler } from '../aggregation_handler'; -import { AggregationBuilder, BaseHandlerCommonOptions } from '../types'; +import { SingleCaseAggregationHandler } from '../single_case_aggregation_handler'; +import { AggregationBuilder, SingleCaseBaseHandlerCommonOptions } from '../types'; import { IsolateHostActions } from './aggregations/isolate_host'; -export class Actions extends AggregationHandler { - constructor(options: BaseHandlerCommonOptions) { +export class Actions extends SingleCaseAggregationHandler { + constructor(options: SingleCaseBaseHandlerCommonOptions) { super( options, - new Map([['actions.isolateHost', new IsolateHostActions()]]) + new Map>([ + ['actions.isolateHost', new IsolateHostActions()], + ]) ); } - public async compute(): Promise { + public async compute(): Promise { const { unsecuredSavedObjectsClient, authorization, attachmentService, logger } = this.options.clientArgs; - const { caseId, casesClient } = this.options; + const { casesClient } = this.options; try { // This will perform an authorization check to ensure the user has access to the parent case const theCase = await casesClient.cases.get({ - id: caseId, + id: this.caseId, includeComments: false, }); @@ -48,13 +50,13 @@ export class Actions extends AggregationHandler { aggregations, }); - return this.aggregationBuilders.reduce( + return this.aggregationBuilders.reduce( (acc, aggregator) => merge(acc, aggregator.formatResponse(response)), {} ); } catch (error) { throw createCaseError({ - message: `Failed to compute actions attached case id: ${caseId}: ${error}`, + message: `Failed to compute actions attached case id: ${this.caseId}: ${error}`, error, logger, }); diff --git a/x-pack/plugins/cases/server/client/metrics/actions/aggregations/isolate_host.ts b/x-pack/plugins/cases/server/client/metrics/actions/aggregations/isolate_host.ts index f0cf670a105db..479de16bc262f 100644 --- a/x-pack/plugins/cases/server/client/metrics/actions/aggregations/isolate_host.ts +++ b/x-pack/plugins/cases/server/client/metrics/actions/aggregations/isolate_host.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { IsolateHostActionType } from '../../../../../common/api'; +import { IsolateHostActionType, SingleCaseMetricsResponse } from '../../../../../common/api'; import { CASE_COMMENT_SAVED_OBJECT } from '../../../../../common/constants'; import { AggregationBuilder, AggregationResponse } from '../../types'; @@ -16,7 +16,7 @@ interface ActionsAggregation { } type ActionsAggregationResponse = ActionsAggregation | undefined; -export class IsolateHostActions implements AggregationBuilder { +export class IsolateHostActions implements AggregationBuilder { // uniqueValuesLimit should not be lower than the number of actions.type values (currently 2) or some information could be lost constructor(private readonly uniqueValuesLimit: number = 10) {} diff --git a/x-pack/plugins/cases/server/client/metrics/aggregation_handler.ts b/x-pack/plugins/cases/server/client/metrics/aggregation_handler.ts index 382faa354db59..e70c7add20f5e 100644 --- a/x-pack/plugins/cases/server/client/metrics/aggregation_handler.ts +++ b/x-pack/plugins/cases/server/client/metrics/aggregation_handler.ts @@ -5,15 +5,16 @@ * 2.0. */ +import { merge } from 'lodash'; import { BaseHandler } from './base_handler'; -import { AggregationBuilder, BaseHandlerCommonOptions } from './types'; +import { AggregationBuilder, AggregationResponse, BaseHandlerCommonOptions } from './types'; -export abstract class AggregationHandler extends BaseHandler { - protected aggregationBuilders: AggregationBuilder[] = []; +export abstract class AggregationHandler extends BaseHandler { + protected aggregationBuilders: Array> = []; constructor( options: BaseHandlerCommonOptions, - private readonly aggregations: Map + protected readonly aggregations: Map> ) { super(options); } @@ -28,4 +29,11 @@ export abstract class AggregationHandler extends BaseHandler { this.aggregationBuilders.push(aggregation); } } + + public formatResponse(aggregationsResponse?: AggregationResponse): F { + return this.aggregationBuilders.reduce( + (acc, feature) => merge(acc, feature.formatResponse(aggregationsResponse)), + {} as F + ); + } } diff --git a/x-pack/plugins/cases/server/client/metrics/alerts/aggregations/hosts.ts b/x-pack/plugins/cases/server/client/metrics/alerts/aggregations/hosts.ts index dc2a1162bd9de..a9052e2e2a9ce 100644 --- a/x-pack/plugins/cases/server/client/metrics/alerts/aggregations/hosts.ts +++ b/x-pack/plugins/cases/server/client/metrics/alerts/aggregations/hosts.ts @@ -8,6 +8,7 @@ import { get } from 'lodash'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { SingleCaseMetricsResponse } from '../../../../../common/api'; import { AggregationBuilder, AggregationResponse } from '../../types'; type HostsAggregate = HostsAggregateResponse | undefined; @@ -30,7 +31,7 @@ interface FieldAggregateBucket { const hostName = 'host.name'; const hostId = 'host.id'; -export class AlertHosts implements AggregationBuilder { +export class AlertHosts implements AggregationBuilder { constructor(private readonly uniqueValuesLimit: number = 10) {} build() { diff --git a/x-pack/plugins/cases/server/client/metrics/alerts/aggregations/users.ts b/x-pack/plugins/cases/server/client/metrics/alerts/aggregations/users.ts index 46db6c665327a..8d068e354693b 100644 --- a/x-pack/plugins/cases/server/client/metrics/alerts/aggregations/users.ts +++ b/x-pack/plugins/cases/server/client/metrics/alerts/aggregations/users.ts @@ -5,9 +5,10 @@ * 2.0. */ +import { SingleCaseMetricsResponse } from '../../../../../common/api'; import { AggregationBuilder, AggregationResponse } from '../../types'; -export class AlertUsers implements AggregationBuilder { +export class AlertUsers implements AggregationBuilder { constructor(private readonly uniqueValuesLimit: number = 10) {} build() { diff --git a/x-pack/plugins/cases/server/client/metrics/alerts/count.test.ts b/x-pack/plugins/cases/server/client/metrics/alerts/count.test.ts new file mode 100644 index 0000000000000..58776f7bdb77e --- /dev/null +++ b/x-pack/plugins/cases/server/client/metrics/alerts/count.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CaseResponse } from '../../../../common/api'; +import { createCasesClientMock } from '../../mocks'; +import { CasesClientArgs } from '../../types'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { createAttachmentServiceMock } from '../../../services/mocks'; + +import { AlertsCount } from './count'; + +const clientMock = createCasesClientMock(); +const attachmentService = createAttachmentServiceMock(); + +const logger = loggingSystemMock.createLogger(); +const getAuthorizationFilter = jest.fn().mockResolvedValue({}); + +const clientArgs = { + logger, + attachmentService, + authorization: { getAuthorizationFilter }, +} as unknown as CasesClientArgs; + +const constructorOptions = { caseId: 'test-id', casesClient: clientMock, clientArgs }; + +describe('AlertsCount', () => { + beforeAll(() => { + getAuthorizationFilter.mockResolvedValue({}); + clientMock.cases.get.mockResolvedValue({ id: 'test-id' } as unknown as CaseResponse); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('returns empty values when attachment services returns undefined', async () => { + attachmentService.countAlertsAttachedToCase.mockResolvedValue(undefined); + const handler = new AlertsCount(constructorOptions); + expect(await handler.compute()).toEqual({ alerts: { count: 0 } }); + }); + + it('returns values when the attachment service returns a value', async () => { + attachmentService.countAlertsAttachedToCase.mockResolvedValue(5); + const handler = new AlertsCount(constructorOptions); + + expect(await handler.compute()).toEqual({ alerts: { count: 5 } }); + }); +}); diff --git a/x-pack/plugins/cases/server/client/metrics/alerts/count.ts b/x-pack/plugins/cases/server/client/metrics/alerts/count.ts index 10fb1b97b4511..2afbd41863a11 100644 --- a/x-pack/plugins/cases/server/client/metrics/alerts/count.ts +++ b/x-pack/plugins/cases/server/client/metrics/alerts/count.ts @@ -5,27 +5,27 @@ * 2.0. */ -import { CaseMetricsResponse } from '../../../../common/api'; +import { SingleCaseMetricsResponse } from '../../../../common/api'; import { Operations } from '../../../authorization'; import { createCaseError } from '../../../common/error'; -import { BaseHandler } from '../base_handler'; -import { BaseHandlerCommonOptions } from '../types'; +import { SingleCaseBaseHandler } from '../single_case_base_handler'; +import { SingleCaseBaseHandlerCommonOptions } from '../types'; -export class AlertsCount extends BaseHandler { - constructor(options: BaseHandlerCommonOptions) { +export class AlertsCount extends SingleCaseBaseHandler { + constructor(options: SingleCaseBaseHandlerCommonOptions) { super(options, ['alerts.count']); } - public async compute(): Promise { + public async compute(): Promise { const { unsecuredSavedObjectsClient, authorization, attachmentService, logger } = this.options.clientArgs; - const { caseId, casesClient } = this.options; + const { casesClient } = this.options; try { // This will perform an authorization check to ensure the user has access to the parent case const theCase = await casesClient.cases.get({ - id: caseId, + id: this.caseId, includeComments: false, }); @@ -46,7 +46,7 @@ export class AlertsCount extends BaseHandler { }; } catch (error) { throw createCaseError({ - message: `Failed to count alerts attached case id: ${caseId}: ${error}`, + message: `Failed to count alerts attached case id: ${this.caseId}: ${error}`, error, logger, }); diff --git a/x-pack/plugins/cases/server/client/metrics/alerts/details.test.ts b/x-pack/plugins/cases/server/client/metrics/alerts/details.test.ts index 6f8a7b284d1eb..a8f5cda3501c4 100644 --- a/x-pack/plugins/cases/server/client/metrics/alerts/details.test.ts +++ b/x-pack/plugins/cases/server/client/metrics/alerts/details.test.ts @@ -11,13 +11,13 @@ import { loggingSystemMock } from '@kbn/core/server/mocks'; import { AlertDetails } from './details'; import { mockAlertsService } from '../test_utils/alerts'; -import { BaseHandlerCommonOptions } from '../types'; +import { SingleCaseBaseHandlerCommonOptions } from '../types'; describe('AlertDetails', () => { let client: CasesClientMock; let mockServices: ReturnType['mockServices']; let clientArgs: ReturnType['clientArgs']; - let constructorOptions: BaseHandlerCommonOptions; + let constructorOptions: SingleCaseBaseHandlerCommonOptions; beforeEach(() => { client = createMockClient(); diff --git a/x-pack/plugins/cases/server/client/metrics/alerts/details.ts b/x-pack/plugins/cases/server/client/metrics/alerts/details.ts index eec21d23c4639..87cb0fc3be2ac 100644 --- a/x-pack/plugins/cases/server/client/metrics/alerts/details.ts +++ b/x-pack/plugins/cases/server/client/metrics/alerts/details.ts @@ -5,33 +5,31 @@ * 2.0. */ -import { merge } from 'lodash'; - -import { CaseMetricsResponse } from '../../../../common/api'; +import { SingleCaseMetricsResponse } from '../../../../common/api'; import { createCaseError } from '../../../common/error'; -import { AggregationHandler } from '../aggregation_handler'; -import { AggregationBuilder, AggregationResponse, BaseHandlerCommonOptions } from '../types'; +import { SingleCaseAggregationHandler } from '../single_case_aggregation_handler'; +import { AggregationBuilder, SingleCaseBaseHandlerCommonOptions } from '../types'; import { AlertHosts, AlertUsers } from './aggregations'; -export class AlertDetails extends AggregationHandler { - constructor(options: BaseHandlerCommonOptions) { +export class AlertDetails extends SingleCaseAggregationHandler { + constructor(options: SingleCaseBaseHandlerCommonOptions) { super( options, - new Map([ + new Map>([ ['alerts.hosts', new AlertHosts()], ['alerts.users', new AlertUsers()], ]) ); } - public async compute(): Promise { + public async compute(): Promise { const { alertsService, logger } = this.options.clientArgs; - const { caseId, casesClient } = this.options; + const { casesClient } = this.options; try { const alerts = await casesClient.attachments.getAllAlertsAttachToCase({ - caseId, + caseId: this.caseId, }); if (alerts.length <= 0 || this.aggregationBuilders.length <= 0) { @@ -43,20 +41,13 @@ export class AlertDetails extends AggregationHandler { alerts, }); - return this.formatResponse(aggregationsResponse); + return this.formatResponse(aggregationsResponse); } catch (error) { throw createCaseError({ - message: `Failed to retrieve alerts details attached case id: ${caseId}: ${error}`, + message: `Failed to retrieve alerts details attached case id: ${this.caseId}: ${error}`, error, logger, }); } } - - private formatResponse(aggregationsResponse?: AggregationResponse): CaseMetricsResponse { - return this.aggregationBuilders.reduce( - (acc, feature) => merge(acc, feature.formatResponse(aggregationsResponse)), - {} - ); - } } diff --git a/x-pack/plugins/cases/server/client/metrics/all_cases/aggregations/avg_duration.test.ts b/x-pack/plugins/cases/server/client/metrics/all_cases/aggregations/avg_duration.test.ts new file mode 100644 index 0000000000000..1e63332fd419b --- /dev/null +++ b/x-pack/plugins/cases/server/client/metrics/all_cases/aggregations/avg_duration.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AverageDuration } from './avg_duration'; + +describe('AverageDuration', () => { + it('returns the correct aggregation', async () => { + const agg = new AverageDuration(); + + expect(agg.build()).toEqual({ + mttr: { + avg: { + field: 'cases.attributes.duration', + }, + }, + }); + }); + + it('formats the response correctly', async () => { + const agg = new AverageDuration(); + const res = agg.formatResponse({ mttr: { value: 5 } }); + expect(res).toEqual({ mttr: 5 }); + }); + + it('formats the response correctly if the res is undefined', async () => { + const agg = new AverageDuration(); + // @ts-expect-error + const res = agg.formatResponse(); + expect(res).toEqual({ mttr: 0 }); + }); + + it('formats the response correctly if the mttr is not defined', async () => { + const agg = new AverageDuration(); + const res = agg.formatResponse({}); + expect(res).toEqual({ mttr: 0 }); + }); + + it('formats the response correctly if the value is not defined', async () => { + const agg = new AverageDuration(); + const res = agg.formatResponse({ mttr: {} }); + expect(res).toEqual({ mttr: 0 }); + }); + + it('gets the name correctly', async () => { + const agg = new AverageDuration(); + expect(agg.getName()).toBe('mttr'); + }); +}); diff --git a/x-pack/plugins/cases/server/client/metrics/all_cases/aggregations/avg_duration.ts b/x-pack/plugins/cases/server/client/metrics/all_cases/aggregations/avg_duration.ts new file mode 100644 index 0000000000000..afa0638a2cf0a --- /dev/null +++ b/x-pack/plugins/cases/server/client/metrics/all_cases/aggregations/avg_duration.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 { CASE_SAVED_OBJECT } from '../../../../../common/constants'; +import { CasesMetricsResponse } from '../../../../../common/api'; +import { AggregationBuilder, AggregationResponse } from '../../types'; + +export class AverageDuration implements AggregationBuilder { + build() { + return { + mttr: { + avg: { + field: `${CASE_SAVED_OBJECT}.attributes.duration`, + }, + }, + }; + } + + formatResponse(aggregations: AggregationResponse) { + const aggs = aggregations as MTTRAggregate; + + const mttr = aggs?.mttr?.value ?? 0; + + return { mttr }; + } + + getName() { + return 'mttr'; + } +} + +type MTTRAggregate = MTTRAggregateResponse | undefined; + +interface MTTRAggregateResponse { + mttr?: { + value: number; + }; +} diff --git a/x-pack/plugins/cases/server/client/metrics/all_cases/mttr.test.ts b/x-pack/plugins/cases/server/client/metrics/all_cases/mttr.test.ts new file mode 100644 index 0000000000000..e133082e69756 --- /dev/null +++ b/x-pack/plugins/cases/server/client/metrics/all_cases/mttr.test.ts @@ -0,0 +1,160 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CaseResponse } from '../../../../common/api'; +import { createCasesClientMock } from '../../mocks'; +import { CasesClientArgs } from '../../types'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { createCaseServiceMock } from '../../../services/mocks'; + +import { MTTR } from './mttr'; + +const clientMock = createCasesClientMock(); +const caseService = createCaseServiceMock(); + +const logger = loggingSystemMock.createLogger(); +const getAuthorizationFilter = jest.fn().mockResolvedValue({}); + +const clientArgs = { + logger, + caseService, + authorization: { getAuthorizationFilter }, +} as unknown as CasesClientArgs; + +const constructorOptions = { casesClient: clientMock, clientArgs }; + +describe('MTTR', () => { + beforeAll(() => { + getAuthorizationFilter.mockResolvedValue({}); + clientMock.cases.get.mockResolvedValue({ id: '' } as unknown as CaseResponse); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('returns empty values when no features set up', async () => { + caseService.executeAggregations.mockResolvedValue(undefined); + const handler = new MTTR(constructorOptions); + expect(await handler.compute()).toEqual({}); + }); + + it('returns zero values when aggregation returns undefined', async () => { + caseService.executeAggregations.mockResolvedValue(undefined); + const handler = new MTTR(constructorOptions); + handler.setupFeature('mttr'); + + expect(await handler.compute()).toEqual({ mttr: 0 }); + }); + + it('returns zero values when aggregation returns empty object', async () => { + caseService.executeAggregations.mockResolvedValue({}); + const handler = new MTTR(constructorOptions); + handler.setupFeature('mttr'); + + expect(await handler.compute()).toEqual({ mttr: 0 }); + }); + + it('returns zero values when aggregation returns empty mttr object', async () => { + caseService.executeAggregations.mockResolvedValue({ mttr: {} }); + const handler = new MTTR(constructorOptions); + handler.setupFeature('mttr'); + + expect(await handler.compute()).toEqual({ mttr: 0 }); + }); + + it('returns values when there is a mttr value', async () => { + caseService.executeAggregations.mockResolvedValue({ mttr: { value: 5 } }); + const handler = new MTTR(constructorOptions); + handler.setupFeature('mttr'); + + expect(await handler.compute()).toEqual({ mttr: 5 }); + }); + + it('passes the query options correctly', async () => { + caseService.executeAggregations.mockResolvedValue({ mttr: { value: 5 } }); + const handler = new MTTR({ + ...constructorOptions, + from: '2022-04-28T15:18:00.000Z', + to: '2022-04-28T15:22:00.000Z', + owner: 'cases', + }); + + handler.setupFeature('mttr'); + await handler.compute(); + + expect(caseService.executeAggregations.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "aggregationBuilders": Array [ + AverageDuration {}, + ], + "options": Object { + "filter": Object { + "arguments": Array [ + Object { + "arguments": Array [ + Object { + "arguments": Array [ + Object { + "type": "literal", + "value": "cases.attributes.created_at", + }, + "gte", + Object { + "type": "literal", + "value": "2022-04-28T15:18:00.000Z", + }, + ], + "function": "range", + "type": "function", + }, + Object { + "arguments": Array [ + Object { + "type": "literal", + "value": "cases.attributes.created_at", + }, + "lte", + Object { + "type": "literal", + "value": "2022-04-28T15:22:00.000Z", + }, + ], + "function": "range", + "type": "function", + }, + ], + "function": "and", + "type": "function", + }, + Object { + "arguments": Array [ + Object { + "type": "literal", + "value": "cases.attributes.owner", + }, + Object { + "type": "literal", + "value": "cases", + }, + Object { + "type": "literal", + "value": false, + }, + ], + "function": "is", + "type": "function", + }, + ], + "function": "and", + "type": "function", + }, + }, + } + `); + }); +}); diff --git a/x-pack/plugins/cases/server/client/metrics/all_cases/mttr.ts b/x-pack/plugins/cases/server/client/metrics/all_cases/mttr.ts new file mode 100644 index 0000000000000..69cacb7e2318e --- /dev/null +++ b/x-pack/plugins/cases/server/client/metrics/all_cases/mttr.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 { CasesMetricsResponse } from '../../../../common/api'; +import { Operations } from '../../../authorization'; +import { createCaseError } from '../../../common/error'; +import { constructQueryOptions } from '../../utils'; +import { AllCasesAggregationHandler } from '../all_cases_aggregation_handler'; +import { AggregationBuilder, AllCasesBaseHandlerCommonOptions } from '../types'; +import { AverageDuration } from './aggregations/avg_duration'; + +export class MTTR extends AllCasesAggregationHandler { + constructor(options: AllCasesBaseHandlerCommonOptions) { + super( + options, + new Map>([['mttr', new AverageDuration()]]) + ); + } + + public async compute(): Promise { + const { authorization, caseService, logger } = this.options.clientArgs; + + try { + const { filter: authorizationFilter } = await authorization.getAuthorizationFilter( + Operations.getCasesMetrics + ); + + const caseQueryOptions = constructQueryOptions({ + from: this.from, + to: this.to, + owner: this.owner, + authorizationFilter, + }); + + const aggregationsResponse = await caseService.executeAggregations({ + aggregationBuilders: this.aggregationBuilders, + options: { filter: caseQueryOptions.filter }, + }); + + return this.formatResponse(aggregationsResponse); + } catch (error) { + throw createCaseError({ + message: `Failed to calculate average mttr: ${error}`, + error, + logger, + }); + } + } +} diff --git a/x-pack/plugins/cases/server/client/metrics/all_cases_aggregation_handler.ts b/x-pack/plugins/cases/server/client/metrics/all_cases_aggregation_handler.ts new file mode 100644 index 0000000000000..3a5a259c28296 --- /dev/null +++ b/x-pack/plugins/cases/server/client/metrics/all_cases_aggregation_handler.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 { CasesMetricsResponse } from '../../../common/api'; +import { AggregationHandler } from './aggregation_handler'; +import { AggregationBuilder, AllCasesBaseHandlerCommonOptions } from './types'; + +export abstract class AllCasesAggregationHandler extends AggregationHandler { + protected readonly from?: string; + protected readonly to?: string; + protected readonly owner?: string | string[]; + + constructor( + options: AllCasesBaseHandlerCommonOptions, + aggregations: Map> + ) { + const { owner, from, to, ...restOptions } = options; + super(restOptions, aggregations); + + this.from = from; + this.to = to; + this.owner = owner; + } +} diff --git a/x-pack/plugins/cases/server/client/metrics/all_cases_base_handler.ts b/x-pack/plugins/cases/server/client/metrics/all_cases_base_handler.ts new file mode 100644 index 0000000000000..de9f1f089c8c8 --- /dev/null +++ b/x-pack/plugins/cases/server/client/metrics/all_cases_base_handler.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 { CasesMetricsResponse } from '../../../common/api'; +import { BaseHandler } from './base_handler'; +import { AllCasesBaseHandlerCommonOptions } from './types'; + +export abstract class AllCasesBaseHandler extends BaseHandler { + protected readonly owner?: string | string[]; + + constructor(options: AllCasesBaseHandlerCommonOptions, features?: string[]) { + const { owner, ...restOptions } = options; + super(restOptions, features); + + this.owner = owner; + } +} diff --git a/x-pack/plugins/cases/server/client/metrics/base_handler.ts b/x-pack/plugins/cases/server/client/metrics/base_handler.ts index bf76be05f58b3..6525de35bc00c 100644 --- a/x-pack/plugins/cases/server/client/metrics/base_handler.ts +++ b/x-pack/plugins/cases/server/client/metrics/base_handler.ts @@ -5,10 +5,9 @@ * 2.0. */ -import { CaseMetricsResponse } from '../../../common/api'; import { BaseHandlerCommonOptions, MetricsHandler } from './types'; -export abstract class BaseHandler implements MetricsHandler { +export abstract class BaseHandler implements MetricsHandler { constructor( protected readonly options: BaseHandlerCommonOptions, private readonly features?: string[] @@ -18,5 +17,5 @@ export abstract class BaseHandler implements MetricsHandler { return new Set(this.features); } - abstract compute(): Promise; + abstract compute(): Promise; } diff --git a/x-pack/plugins/cases/server/client/metrics/client.ts b/x-pack/plugins/cases/server/client/metrics/client.ts index 8fbb30486bc41..e2e0dfb5c9415 100644 --- a/x-pack/plugins/cases/server/client/metrics/client.ts +++ b/x-pack/plugins/cases/server/client/metrics/client.ts @@ -5,19 +5,27 @@ * 2.0. */ -import { CaseMetricsResponse, CasesStatusRequest, CasesStatusResponse } from '../../../common/api'; +import { + SingleCaseMetricsResponse, + CasesMetricsRequest, + CasesStatusRequest, + CasesStatusResponse, + SingleCaseMetricsRequest, + CasesMetricsResponse, +} from '../../../common/api'; import { CasesClient } from '../client'; import { CasesClientArgs } from '../types'; -import { getStatusTotalsByType } from './get_cases_metrics'; - -import { getCaseMetrics, CaseMetricsParams } from './get_case_metrics'; +import { getStatusTotalsByType } from './get_status_totals'; +import { getCaseMetrics } from './get_case_metrics'; +import { getCasesMetrics } from './get_cases_metrics'; /** * API for interacting with the metrics. */ export interface MetricsSubClient { - getCaseMetrics(params: CaseMetricsParams): Promise; + getCaseMetrics(params: SingleCaseMetricsRequest): Promise; + getCasesMetrics(params: CasesMetricsRequest): Promise; /** * Retrieves the total number of open, closed, and in-progress cases. */ @@ -34,7 +42,10 @@ export const createMetricsSubClient = ( casesClient: CasesClient ): MetricsSubClient => { const casesSubClient: MetricsSubClient = { - getCaseMetrics: (params: CaseMetricsParams) => getCaseMetrics(params, casesClient, clientArgs), + getCaseMetrics: (params: SingleCaseMetricsRequest) => + getCaseMetrics(params, casesClient, clientArgs), + getCasesMetrics: (params: CasesMetricsRequest) => + getCasesMetrics(params, casesClient, clientArgs), getStatusTotalsByType: (params: CasesStatusRequest) => getStatusTotalsByType(params, clientArgs), }; diff --git a/x-pack/plugins/cases/server/client/metrics/connectors.ts b/x-pack/plugins/cases/server/client/metrics/connectors.ts index 3dd29b8b6dda7..1701cef3b8cf9 100644 --- a/x-pack/plugins/cases/server/client/metrics/connectors.ts +++ b/x-pack/plugins/cases/server/client/metrics/connectors.ts @@ -5,30 +5,28 @@ * 2.0. */ -import { CaseMetricsResponse } from '../../../common/api'; +import { SingleCaseMetricsResponse } from '../../../common/api'; import { Operations } from '../../authorization'; import { createCaseError } from '../../common/error'; -import { BaseHandler } from './base_handler'; -import { BaseHandlerCommonOptions } from './types'; +import { SingleCaseBaseHandler } from './single_case_base_handler'; +import { SingleCaseBaseHandlerCommonOptions } from './types'; -export class Connectors extends BaseHandler { - constructor(options: BaseHandlerCommonOptions) { +export class Connectors extends SingleCaseBaseHandler { + constructor(options: SingleCaseBaseHandlerCommonOptions) { super(options, ['connectors']); } - public async compute(): Promise { + public async compute(): Promise { const { unsecuredSavedObjectsClient, authorization, userActionService, logger } = this.options.clientArgs; - const { caseId } = this.options; - const { filter: authorizationFilter } = await authorization.getAuthorizationFilter( Operations.getUserActionMetrics ); const uniqueConnectors = await userActionService.getUniqueConnectors({ unsecuredSavedObjectsClient, - caseId, + caseId: this.caseId, filter: authorizationFilter, }); @@ -38,7 +36,7 @@ export class Connectors extends BaseHandler { }; } catch (error) { throw createCaseError({ - message: `Failed to retrieve total connectors metrics for case id: ${caseId}: ${error}`, + message: `Failed to retrieve total connectors metrics for case id: ${this.caseId}: ${error}`, error, logger, }); diff --git a/x-pack/plugins/cases/server/client/metrics/get_case_metrics.test.ts b/x-pack/plugins/cases/server/client/metrics/get_case_metrics.test.ts index 03b03eafa7d97..51353d9558c1b 100644 --- a/x-pack/plugins/cases/server/client/metrics/get_case_metrics.test.ts +++ b/x-pack/plugins/cases/server/client/metrics/get_case_metrics.test.ts @@ -5,22 +5,23 @@ * 2.0. */ +import { loggingSystemMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; +import { SavedObject } from '@kbn/core/server'; + import { getCaseMetrics } from './get_case_metrics'; import { CaseAttributes, CaseResponse, CaseStatuses } from '../../../common/api'; import { CasesClientMock, createCasesClientMock } from '../mocks'; import { CasesClientArgs } from '../types'; import { createAuthorizationMock } from '../../authorization/mock'; -import { loggingSystemMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; import { createAttachmentServiceMock, createCaseServiceMock, createUserActionServiceMock, } from '../../services/mocks'; -import { SavedObject } from '@kbn/core/server'; import { mockAlertsService } from './test_utils/alerts'; import { createStatusChangeSavedObject } from './test_utils/lifespan'; -describe('getMetrics', () => { +describe('getCaseMetrics', () => { const inProgressStatusChangeTimestamp = new Date('2021-11-23T20:00:43Z'); const currentTime = new Date('2021-11-23T20:01:43Z'); diff --git a/x-pack/plugins/cases/server/client/metrics/get_case_metrics.ts b/x-pack/plugins/cases/server/client/metrics/get_case_metrics.ts index d2ce8c03edeb7..e3132b4a590f7 100644 --- a/x-pack/plugins/cases/server/client/metrics/get_case_metrics.ts +++ b/x-pack/plugins/cases/server/client/metrics/get_case_metrics.ts @@ -5,36 +5,23 @@ * 2.0. */ import { merge } from 'lodash'; -import Boom from '@hapi/boom'; -import { CaseMetricsResponseRt, CaseMetricsResponse } from '../../../common/api'; +import { + SingleCaseMetricsRequest, + SingleCaseMetricsResponse, + SingleCaseMetricsResponseRt, +} from '../../../common/api'; import { Operations } from '../../authorization'; import { createCaseError } from '../../common/error'; import { CasesClient } from '../client'; import { CasesClientArgs } from '../types'; -import { AlertsCount } from './alerts/count'; -import { AlertDetails } from './alerts/details'; -import { Actions } from './actions'; -import { Connectors } from './connectors'; -import { Lifespan } from './lifespan'; -import { MetricsHandler } from './types'; - -export interface CaseMetricsParams { - /** - * The ID of the case. - */ - caseId: string; - /** - * The metrics to retrieve. - */ - features: string[]; -} +import { buildHandlers } from './utils'; export const getCaseMetrics = async ( - params: CaseMetricsParams, + params: SingleCaseMetricsRequest, casesClient: CasesClient, clientArgs: CasesClientArgs -): Promise => { +): Promise => { const { logger } = clientArgs; try { @@ -49,9 +36,9 @@ export const getCaseMetrics = async ( const mergedResults = computedMetrics.reduce((acc, metric) => { return merge(acc, metric); - }, {}); + }, {}) as SingleCaseMetricsResponse; - return CaseMetricsResponseRt.encode(mergedResults); + return SingleCaseMetricsResponseRt.encode(mergedResults); } catch (error) { throw createCaseError({ logger, @@ -61,50 +48,10 @@ export const getCaseMetrics = async ( } }; -const buildHandlers = ( - params: CaseMetricsParams, - casesClient: CasesClient, +const checkAuthorization = async ( + params: SingleCaseMetricsRequest, clientArgs: CasesClientArgs -): Set => { - const handlers: MetricsHandler[] = [AlertsCount, AlertDetails, Actions, Connectors, Lifespan].map( - (ClassName) => new ClassName({ caseId: params.caseId, casesClient, clientArgs }) - ); - - const uniqueFeatures = new Set(params.features); - const handlerFeatures = new Set(); - const handlersToExecute = new Set(); - for (const handler of handlers) { - for (const handlerFeature of handler.getFeatures()) { - if (uniqueFeatures.has(handlerFeature)) { - handler.setupFeature?.(handlerFeature); - handlersToExecute.add(handler); - } - - handlerFeatures.add(handlerFeature); - } - } - - checkAndThrowIfInvalidFeatures(params, handlerFeatures); - - return handlersToExecute; -}; - -const checkAndThrowIfInvalidFeatures = ( - params: CaseMetricsParams, - handlerFeatures: Set ) => { - const invalidFeatures = params.features.filter((feature) => !handlerFeatures.has(feature)); - if (invalidFeatures.length > 0) { - const invalidFeaturesAsString = invalidFeatures.join(', '); - const validFeaturesAsString = [...handlerFeatures.keys()].sort().join(', '); - - throw Boom.badRequest( - `invalid features: [${invalidFeaturesAsString}], please only provide valid features: [${validFeaturesAsString}]` - ); - } -}; - -const checkAuthorization = async (params: CaseMetricsParams, clientArgs: CasesClientArgs) => { const { caseService, authorization } = clientArgs; const caseInfo = await caseService.getCase({ diff --git a/x-pack/plugins/cases/server/client/metrics/get_cases_metrics.test.ts b/x-pack/plugins/cases/server/client/metrics/get_cases_metrics.test.ts new file mode 100644 index 0000000000000..3e94f58a2ba05 --- /dev/null +++ b/x-pack/plugins/cases/server/client/metrics/get_cases_metrics.test.ts @@ -0,0 +1,120 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CasesClientMock } from '../mocks'; +import { getCasesMetrics } from './get_cases_metrics'; +import { createMockClientArgs, createMockClient } from './test_utils/client'; + +describe('getCasesMetrics', () => { + let client: CasesClientMock; + let mockServices: ReturnType['mockServices']; + let clientArgs: ReturnType['clientArgs']; + + beforeEach(() => { + client = createMockClient(); + ({ mockServices, clientArgs } = createMockClientArgs()); + + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + describe('MTTR', () => { + beforeEach(() => { + mockServices.caseService.executeAggregations.mockResolvedValue({ mttr: { value: 5 } }); + }); + + it('returns the mttr metric', async () => { + const metrics = await getCasesMetrics({ features: ['mttr'] }, client, clientArgs); + expect(metrics).toEqual({ mttr: 5 }); + }); + + it('calls the executeAggregations correctly', async () => { + await getCasesMetrics( + { + features: ['mttr'], + from: '2022-04-28T15:18:00.000Z', + to: '2022-04-28T15:22:00.000Z', + owner: 'cases', + }, + client, + clientArgs + ); + expect(mockServices.caseService.executeAggregations.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "aggregationBuilders": Array [ + AverageDuration {}, + ], + "options": Object { + "filter": Object { + "arguments": Array [ + Object { + "arguments": Array [ + Object { + "arguments": Array [ + Object { + "type": "literal", + "value": "cases.attributes.created_at", + }, + "gte", + Object { + "type": "literal", + "value": "2022-04-28T15:18:00.000Z", + }, + ], + "function": "range", + "type": "function", + }, + Object { + "arguments": Array [ + Object { + "type": "literal", + "value": "cases.attributes.created_at", + }, + "lte", + Object { + "type": "literal", + "value": "2022-04-28T15:22:00.000Z", + }, + ], + "function": "range", + "type": "function", + }, + ], + "function": "and", + "type": "function", + }, + Object { + "arguments": Array [ + Object { + "type": "literal", + "value": "cases.attributes.owner", + }, + Object { + "type": "literal", + "value": "cases", + }, + Object { + "type": "literal", + "value": false, + }, + ], + "function": "is", + "type": "function", + }, + ], + "function": "and", + "type": "function", + }, + }, + } + `); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/client/metrics/get_cases_metrics.ts b/x-pack/plugins/cases/server/client/metrics/get_cases_metrics.ts index e02f882820fa7..c7cb0673db42e 100644 --- a/x-pack/plugins/cases/server/client/metrics/get_cases_metrics.ts +++ b/x-pack/plugins/cases/server/client/metrics/get_cases_metrics.ts @@ -5,57 +5,55 @@ * 2.0. */ +import { merge } from 'lodash'; import Boom from '@hapi/boom'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; import { - CasesStatusRequest, - CasesStatusResponse, - excess, - CasesStatusRequestRt, + CasesMetricsRequest, + CasesMetricsRequestRt, + CasesMetricsResponse, + CasesMetricsResponseRt, throwErrors, - CasesStatusResponseRt, } from '../../../common/api'; -import { CasesClientArgs } from '../types'; -import { Operations } from '../../authorization'; -import { constructQueryOptions } from '../utils'; import { createCaseError } from '../../common/error'; +import { CasesClient } from '../client'; +import { CasesClientArgs } from '../types'; +import { buildHandlers } from './utils'; -export async function getStatusTotalsByType( - params: CasesStatusRequest, +export const getCasesMetrics = async ( + params: CasesMetricsRequest, + casesClient: CasesClient, clientArgs: CasesClientArgs -): Promise { - const { caseService, logger, authorization } = clientArgs; +): Promise => { + const { logger } = clientArgs; + + const queryParams = pipe( + CasesMetricsRequestRt.decode(params), + fold(throwErrors(Boom.badRequest), identity) + ); try { - const queryParams = pipe( - excess(CasesStatusRequestRt).decode(params), - fold(throwErrors(Boom.badRequest), identity) - ); + const handlers = buildHandlers(queryParams, casesClient, clientArgs); - const { filter: authorizationFilter } = await authorization.getAuthorizationFilter( - Operations.getCaseStatuses + const computedMetrics = await Promise.all( + Array.from(handlers).map(async (handler) => { + return handler.compute(); + }) ); - const options = constructQueryOptions({ - owner: queryParams.owner, - from: queryParams.from, - to: queryParams.to, - authorizationFilter, - }); + const mergedResults = computedMetrics.reduce((acc, metric) => { + return merge(acc, metric); + }, {}) as CasesMetricsResponse; - const statusStats = await caseService.getCaseStatusStats({ - searchOptions: options, - }); - - return CasesStatusResponseRt.encode({ - count_open_cases: statusStats.open, - count_in_progress_cases: statusStats['in-progress'], - count_closed_cases: statusStats.closed, - }); + return CasesMetricsResponseRt.encode(mergedResults); } catch (error) { - throw createCaseError({ message: `Failed to get status stats: ${error}`, error, logger }); + throw createCaseError({ + logger, + message: `Failed to retrieve metrics within client for cases: ${error}`, + error, + }); } -} +}; diff --git a/x-pack/plugins/cases/server/client/metrics/get_status_totals.test.ts b/x-pack/plugins/cases/server/client/metrics/get_status_totals.test.ts new file mode 100644 index 0000000000000..775a6904783bf --- /dev/null +++ b/x-pack/plugins/cases/server/client/metrics/get_status_totals.test.ts @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getStatusTotalsByType } from './get_status_totals'; +import { createMockClientArgs } from './test_utils/client'; + +describe('getStatusTotalsByType', () => { + let mockServices: ReturnType['mockServices']; + let clientArgs: ReturnType['clientArgs']; + + beforeEach(() => { + ({ mockServices, clientArgs } = createMockClientArgs()); + + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + describe('MTTR', () => { + beforeEach(() => { + mockServices.caseService.getCaseStatusStats.mockResolvedValue({ + open: 1, + 'in-progress': 2, + closed: 1, + }); + }); + + it('returns the status correctly', async () => { + const metrics = await getStatusTotalsByType({}, clientArgs); + expect(metrics).toEqual({ + count_closed_cases: 1, + count_in_progress_cases: 2, + count_open_cases: 1, + }); + }); + + it('calls the executeAggregations correctly', async () => { + await getStatusTotalsByType( + { + from: '2022-04-28T15:18:00.000Z', + to: '2022-04-28T15:22:00.000Z', + owner: 'cases', + }, + clientArgs + ); + + expect(mockServices.caseService.getCaseStatusStats.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "searchOptions": Object { + "filter": Object { + "arguments": Array [ + Object { + "arguments": Array [ + Object { + "arguments": Array [ + Object { + "type": "literal", + "value": "cases.attributes.created_at", + }, + "gte", + Object { + "type": "literal", + "value": "2022-04-28T15:18:00.000Z", + }, + ], + "function": "range", + "type": "function", + }, + Object { + "arguments": Array [ + Object { + "type": "literal", + "value": "cases.attributes.created_at", + }, + "lte", + Object { + "type": "literal", + "value": "2022-04-28T15:22:00.000Z", + }, + ], + "function": "range", + "type": "function", + }, + ], + "function": "and", + "type": "function", + }, + Object { + "arguments": Array [ + Object { + "type": "literal", + "value": "cases.attributes.owner", + }, + Object { + "type": "literal", + "value": "cases", + }, + Object { + "type": "literal", + "value": false, + }, + ], + "function": "is", + "type": "function", + }, + ], + "function": "and", + "type": "function", + }, + "sortField": "created_at", + }, + } + `); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/client/metrics/get_status_totals.ts b/x-pack/plugins/cases/server/client/metrics/get_status_totals.ts new file mode 100644 index 0000000000000..e02f882820fa7 --- /dev/null +++ b/x-pack/plugins/cases/server/client/metrics/get_status_totals.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 Boom from '@hapi/boom'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { fold } from 'fp-ts/lib/Either'; +import { identity } from 'fp-ts/lib/function'; + +import { + CasesStatusRequest, + CasesStatusResponse, + excess, + CasesStatusRequestRt, + throwErrors, + CasesStatusResponseRt, +} from '../../../common/api'; +import { CasesClientArgs } from '../types'; +import { Operations } from '../../authorization'; +import { constructQueryOptions } from '../utils'; +import { createCaseError } from '../../common/error'; + +export async function getStatusTotalsByType( + params: CasesStatusRequest, + clientArgs: CasesClientArgs +): Promise { + const { caseService, logger, authorization } = clientArgs; + + try { + const queryParams = pipe( + excess(CasesStatusRequestRt).decode(params), + fold(throwErrors(Boom.badRequest), identity) + ); + + const { filter: authorizationFilter } = await authorization.getAuthorizationFilter( + Operations.getCaseStatuses + ); + + const options = constructQueryOptions({ + owner: queryParams.owner, + from: queryParams.from, + to: queryParams.to, + authorizationFilter, + }); + + const statusStats = await caseService.getCaseStatusStats({ + searchOptions: options, + }); + + return CasesStatusResponseRt.encode({ + count_open_cases: statusStats.open, + count_in_progress_cases: statusStats['in-progress'], + count_closed_cases: statusStats.closed, + }); + } catch (error) { + throw createCaseError({ message: `Failed to get status stats: ${error}`, error, logger }); + } +} diff --git a/x-pack/plugins/cases/server/client/metrics/lifespan.ts b/x-pack/plugins/cases/server/client/metrics/lifespan.ts index 6198886036471..d5acf266dd9a0 100644 --- a/x-pack/plugins/cases/server/client/metrics/lifespan.ts +++ b/x-pack/plugins/cases/server/client/metrics/lifespan.ts @@ -7,9 +7,9 @@ import { SavedObject } from '@kbn/core/server'; import { - CaseMetricsResponse, CaseStatuses, CaseUserActionResponse, + SingleCaseMetricsResponse, StatusInfo, StatusUserAction, StatusUserActionRt, @@ -17,22 +17,22 @@ import { } from '../../../common/api'; import { Operations } from '../../authorization'; import { createCaseError } from '../../common/error'; -import { BaseHandler } from './base_handler'; -import { BaseHandlerCommonOptions } from './types'; +import { SingleCaseBaseHandler } from './single_case_base_handler'; +import { SingleCaseBaseHandlerCommonOptions } from './types'; -export class Lifespan extends BaseHandler { - constructor(options: BaseHandlerCommonOptions) { +export class Lifespan extends SingleCaseBaseHandler { + constructor(options: SingleCaseBaseHandlerCommonOptions) { super(options, ['lifespan']); } - public async compute(): Promise { + public async compute(): Promise { const { unsecuredSavedObjectsClient, authorization, userActionService, logger } = this.options.clientArgs; - const { caseId, casesClient } = this.options; + const { casesClient } = this.options; try { - const caseInfo = await casesClient.cases.get({ id: caseId }); + const caseInfo = await casesClient.cases.get({ id: this.caseId }); const caseOpenTimestamp = new Date(caseInfo.created_at); if (!isDateValid(caseOpenTimestamp)) { @@ -47,7 +47,7 @@ export class Lifespan extends BaseHandler { const statusUserActions = await userActionService.findStatusChanges({ unsecuredSavedObjectsClient, - caseId, + caseId: this.caseId, filter: authorizationFilter, }); @@ -62,7 +62,7 @@ export class Lifespan extends BaseHandler { }; } catch (error) { throw createCaseError({ - message: `Failed to retrieve lifespan metrics for case id: ${caseId}: ${error}`, + message: `Failed to retrieve lifespan metrics for case id: ${this.caseId}: ${error}`, error, logger, }); diff --git a/x-pack/plugins/cases/server/client/metrics/single_case_aggregation_handler.ts b/x-pack/plugins/cases/server/client/metrics/single_case_aggregation_handler.ts new file mode 100644 index 0000000000000..509a2f0125ec6 --- /dev/null +++ b/x-pack/plugins/cases/server/client/metrics/single_case_aggregation_handler.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 { SingleCaseMetricsResponse } from '../../../common/api'; +import { AggregationHandler } from './aggregation_handler'; +import { AggregationBuilder, SingleCaseBaseHandlerCommonOptions } from './types'; + +export abstract class SingleCaseAggregationHandler extends AggregationHandler { + protected readonly caseId: string; + + constructor( + options: SingleCaseBaseHandlerCommonOptions, + aggregations: Map> + ) { + const { caseId, ...restOptions } = options; + super(restOptions, aggregations); + + this.caseId = caseId; + } +} diff --git a/x-pack/plugins/cases/server/client/metrics/single_case_base_handler.ts b/x-pack/plugins/cases/server/client/metrics/single_case_base_handler.ts new file mode 100644 index 0000000000000..d11af800186b0 --- /dev/null +++ b/x-pack/plugins/cases/server/client/metrics/single_case_base_handler.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 { SingleCaseMetricsResponse } from '../../../common/api'; +import { BaseHandler } from './base_handler'; +import { SingleCaseBaseHandlerCommonOptions } from './types'; + +export abstract class SingleCaseBaseHandler extends BaseHandler { + protected readonly caseId: string; + + constructor(options: SingleCaseBaseHandlerCommonOptions, features?: string[]) { + const { caseId, ...restOptions } = options; + super(restOptions, features); + + this.caseId = caseId; + } +} diff --git a/x-pack/plugins/cases/server/client/metrics/test_utils/alerts.ts b/x-pack/plugins/cases/server/client/metrics/test_utils/alerts.ts index 6412f7eb27959..73d22fb575f27 100644 --- a/x-pack/plugins/cases/server/client/metrics/test_utils/alerts.ts +++ b/x-pack/plugins/cases/server/client/metrics/test_utils/alerts.ts @@ -12,7 +12,11 @@ import { AlertHosts, AlertUsers } from '../alerts/aggregations'; export function mockAlertsService() { const alertsService = createAlertServiceMock(); alertsService.executeAggregations.mockImplementation( - async ({ aggregationBuilders }: { aggregationBuilders: AggregationBuilder[] }) => { + async ({ + aggregationBuilders, + }: { + aggregationBuilders: Array>; + }) => { let result = {}; for (const builder of aggregationBuilders) { switch (builder.constructor) { diff --git a/x-pack/plugins/cases/server/client/metrics/test_utils/client.ts b/x-pack/plugins/cases/server/client/metrics/test_utils/client.ts new file mode 100644 index 0000000000000..b132503d41458 --- /dev/null +++ b/x-pack/plugins/cases/server/client/metrics/test_utils/client.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import { createAuthorizationMock } from '../../../authorization/mock'; +import { createCaseServiceMock } from '../../../services/mocks'; +import { createCasesClientMock } from '../../mocks'; +import { CasesClientArgs } from '../../types'; + +export function createMockClient() { + const client = createCasesClientMock(); + + return client; +} + +export function createMockClientArgs() { + const authorization = createAuthorizationMock(); + authorization.getAuthorizationFilter.mockImplementation(async () => { + return { filter: undefined, ensureSavedObjectsAreAuthorized: () => {} }; + }); + + const soClient = savedObjectsClientMock.create(); + + const caseService = createCaseServiceMock(); + const logger = loggingSystemMock.createLogger(); + + const clientArgs = { + authorization, + unsecuredSavedObjectsClient: soClient, + caseService, + logger, + }; + + return { mockServices: clientArgs, clientArgs: clientArgs as unknown as CasesClientArgs }; +} diff --git a/x-pack/plugins/cases/server/client/metrics/types.ts b/x-pack/plugins/cases/server/client/metrics/types.ts index 6773ab59b0b02..35bdbc0933fbc 100644 --- a/x-pack/plugins/cases/server/client/metrics/types.ts +++ b/x-pack/plugins/cases/server/client/metrics/types.ts @@ -6,26 +6,34 @@ */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { CaseMetricsResponse } from '../../../common/api'; import { CasesClient } from '../client'; import { CasesClientArgs } from '../types'; -export interface MetricsHandler { +export interface MetricsHandler { getFeatures(): Set; - compute(): Promise; + compute(): Promise; setupFeature?(feature: string): void; } -export interface AggregationBuilder { +export interface AggregationBuilder { build(): Record; - formatResponse(aggregations: AggregationResponse): CaseMetricsResponse; + formatResponse(aggregations: AggregationResponse): R; getName(): string; } export type AggregationResponse = Record | undefined; export interface BaseHandlerCommonOptions { - caseId: string; casesClient: CasesClient; clientArgs: CasesClientArgs; } + +export interface SingleCaseBaseHandlerCommonOptions extends BaseHandlerCommonOptions { + caseId: string; +} + +export interface AllCasesBaseHandlerCommonOptions extends BaseHandlerCommonOptions { + from?: string; + to?: string; + owner?: string | string[]; +} diff --git a/x-pack/plugins/cases/server/client/metrics/utils.test.ts b/x-pack/plugins/cases/server/client/metrics/utils.test.ts new file mode 100644 index 0000000000000..d376ed56dc232 --- /dev/null +++ b/x-pack/plugins/cases/server/client/metrics/utils.test.ts @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createMockClient, createMockClientArgs } from './test_utils/client'; +import { buildHandlers } from './utils'; + +describe('utils', () => { + describe('buildHandlers', () => { + const casesClient = createMockClient(); + const clientArgs = createMockClientArgs(); + const SINGLE_CASE_FEATURES = [ + 'alerts.count', + 'alerts.users', + 'alerts.hosts', + 'actions.isolateHost', + 'connectors', + 'lifespan', + ]; + + const CASES_FEATURES = ['mttr']; + + it('returns the correct single case handlers', async () => { + const handlers = buildHandlers( + { + caseId: 'test-case-id', + features: SINGLE_CASE_FEATURES, + }, + casesClient, + clientArgs.clientArgs + ); + + handlers.forEach((handler) => { + // @ts-expect-error + expect(handler.caseId).toBe('test-case-id'); + expect( + Array.from(handler.getFeatures().values()).every((feature) => + SINGLE_CASE_FEATURES.includes(feature) + ) + ).toBe(true); + }); + }); + + it('returns the correct cases handlers', async () => { + const handlers = buildHandlers( + { + features: CASES_FEATURES, + from: '2022-04-28T15:18:00.000Z', + to: '2022-04-28T15:22:00.000Z', + owner: 'cases', + }, + casesClient, + clientArgs.clientArgs + ); + + handlers.forEach((handler) => { + // @ts-expect-error + expect(handler.from).toBe('2022-04-28T15:18:00.000Z'); + // @ts-expect-error + expect(handler.to).toBe('2022-04-28T15:22:00.000Z'); + // @ts-expect-error + expect(handler.owner).toBe('cases'); + + expect( + Array.from(handler.getFeatures().values()).every((feature) => + CASES_FEATURES.includes(feature) + ) + ).toBe(true); + }); + }); + + it.each([ + [ + { caseId: 'test-case-id' }, + 'invalid features: [not-exists], please only provide valid features: [actions.isolateHost, alerts.count, alerts.hosts, alerts.users, connectors, lifespan]', + ], + [ + { caseId: null }, + 'invalid features: [not-exists], please only provide valid features: [mttr]', + ], + ])('throws if the feature is not supported: %s', async (opts, msg) => { + expect(() => + buildHandlers( + { + ...opts, + features: ['not-exists'], + }, + casesClient, + clientArgs.clientArgs + ) + ).toThrow(msg); + }); + + it('filters the handlers correctly', async () => { + const handlers = buildHandlers( + { + caseId: 'test-case-id', + features: ['alerts.count'], + }, + casesClient, + clientArgs.clientArgs + ); + + const handler = Array.from(handlers)[0]; + // @ts-expect-error + expect(handler.caseId).toBe('test-case-id'); + expect(Array.from(handler.getFeatures().values())).toEqual(['alerts.count']); + }); + + it('set up the feature correctly', async () => { + const handlers = buildHandlers( + { + caseId: 'test-case-id', + features: ['alerts.hosts'], + }, + casesClient, + clientArgs.clientArgs + ); + + const handler = Array.from(handlers)[0]; + // @ts-expect-error + const aggregationBuilder = handler.aggregationBuilders[0]; + expect(aggregationBuilder.getName()).toBe('hosts'); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/client/metrics/utils.ts b/x-pack/plugins/cases/server/client/metrics/utils.ts new file mode 100644 index 0000000000000..9d6634d888d71 --- /dev/null +++ b/x-pack/plugins/cases/server/client/metrics/utils.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import Boom from '@hapi/boom'; +import { CasesMetricsRequest, SingleCaseMetricsRequest } from '../../../common/api'; +import { CasesClient } from '../client'; +import { CasesClientArgs } from '../types'; +import { AlertsCount } from './alerts/count'; +import { AlertDetails } from './alerts/details'; +import { Actions } from './actions'; +import { Connectors } from './connectors'; +import { Lifespan } from './lifespan'; +import { MetricsHandler } from './types'; +import { MTTR } from './all_cases/mttr'; + +const isSingleCaseMetrics = ( + params: SingleCaseMetricsRequest | CasesMetricsRequest +): params is SingleCaseMetricsRequest => (params as SingleCaseMetricsRequest).caseId != null; + +export const buildHandlers = ( + params: SingleCaseMetricsRequest | CasesMetricsRequest, + casesClient: CasesClient, + clientArgs: CasesClientArgs +): Set> => { + let handlers: Array> = []; + + if (isSingleCaseMetrics(params)) { + handlers = [AlertsCount, AlertDetails, Actions, Connectors, Lifespan].map( + (ClassName) => new ClassName({ caseId: params.caseId, casesClient, clientArgs }) + ); + } else { + handlers = [MTTR].map( + (ClassName) => + new ClassName({ + owner: params.owner, + from: params.from, + to: params.to, + casesClient, + clientArgs, + }) + ); + } + + const uniqueFeatures = new Set(params.features); + const handlerFeatures = new Set(); + const handlersToExecute = new Set>(); + + for (const handler of handlers) { + for (const handlerFeature of handler.getFeatures()) { + if (uniqueFeatures.has(handlerFeature)) { + handler.setupFeature?.(handlerFeature); + handlersToExecute.add(handler); + } + + handlerFeatures.add(handlerFeature); + } + } + + checkAndThrowIfInvalidFeatures(params, handlerFeatures); + + return handlersToExecute; +}; + +const checkAndThrowIfInvalidFeatures = ( + params: SingleCaseMetricsRequest | CasesMetricsRequest, + handlerFeatures: Set +) => { + const invalidFeatures = params.features.filter((feature) => !handlerFeatures.has(feature)); + if (invalidFeatures.length > 0) { + const invalidFeaturesAsString = invalidFeatures.join(', '); + const validFeaturesAsString = [...handlerFeatures.keys()].sort().join(', '); + + throw Boom.badRequest( + `invalid features: [${invalidFeaturesAsString}], please only provide valid features: [${validFeaturesAsString}]` + ); + } +}; diff --git a/x-pack/plugins/cases/server/client/mocks.ts b/x-pack/plugins/cases/server/client/mocks.ts index 6ad4663f1e5ea..a5842cf9137ba 100644 --- a/x-pack/plugins/cases/server/client/mocks.ts +++ b/x-pack/plugins/cases/server/client/mocks.ts @@ -37,6 +37,7 @@ type MetricsSubClientMock = jest.Mocked; const createMetricsSubClientMock = (): MetricsSubClientMock => { return { getCaseMetrics: jest.fn(), + getCasesMetrics: jest.fn(), getStatusTotalsByType: jest.fn(), }; }; diff --git a/x-pack/plugins/cases/server/client/utils.test.ts b/x-pack/plugins/cases/server/client/utils.test.ts index 0210ce9eaf3d4..88140658c2b2b 100644 --- a/x-pack/plugins/cases/server/client/utils.test.ts +++ b/x-pack/plugins/cases/server/client/utils.test.ts @@ -5,9 +5,6 @@ * 2.0. */ -import { CaseConnector, ConnectorTypes } from '../../common/api'; -import { newCase } from '../routes/api/__mocks__/request_responses'; -import { transformNewCase } from '../common/utils'; import { buildRangeFilter, sortToSnake } from './utils'; import { toElasticsearchQuery } from '@kbn/es-query'; @@ -38,73 +35,6 @@ describe('utils', () => { }); }); - describe('transformNewCase', () => { - beforeAll(() => { - jest.useFakeTimers('modern'); - jest.setSystemTime(new Date('2020-04-09T09:43:51.778Z')); - }); - - afterAll(() => { - jest.useRealTimers(); - }); - - const connector: CaseConnector = { - id: '123', - name: 'My connector', - type: ConnectorTypes.jira, - fields: { issueType: 'Task', priority: 'High', parent: null }, - }; - it('transform correctly', () => { - const myCase = { - newCase: { ...newCase, connector }, - user: { - email: 'elastic@elastic.co', - full_name: 'Elastic', - username: 'elastic', - }, - }; - - const res = transformNewCase(myCase); - - expect(res).toMatchInlineSnapshot(` - Object { - "closed_at": null, - "closed_by": null, - "connector": Object { - "fields": Object { - "issueType": "Task", - "parent": null, - "priority": "High", - }, - "id": "123", - "name": "My connector", - "type": ".jira", - }, - "created_at": "2020-04-09T09:43:51.778Z", - "created_by": Object { - "email": "elastic@elastic.co", - "full_name": "Elastic", - "username": "elastic", - }, - "description": "A description", - "external_service": null, - "owner": "securitySolution", - "settings": Object { - "syncAlerts": true, - }, - "status": "open", - "tags": Array [ - "new", - "case", - ], - "title": "My new case", - "updated_at": null, - "updated_by": null, - } - `); - }); - }); - describe('buildRangeFilter', () => { it('returns undefined if both the from and or are undefined', () => { const node = buildRangeFilter({}); @@ -117,18 +47,6 @@ describe('utils', () => { expect(node).toBeFalsy(); }); - it('returns undefined if the from is malformed', () => { - expect(() => buildRangeFilter({ from: '<' })).toThrowError( - 'Invalid "from" and/or "to" query parameters' - ); - }); - - it('returns undefined if the to is malformed', () => { - expect(() => buildRangeFilter({ to: '<' })).toThrowError( - 'Invalid "from" and/or "to" query parameters' - ); - }); - it('creates a range filter with only the from correctly', () => { const node = buildRangeFilter({ from: 'now-1M' }); expect(toElasticsearchQuery(node!)).toMatchInlineSnapshot(` @@ -216,6 +134,7 @@ describe('utils', () => { field: 'test', savedObjectType: 'test-type', }); + expect(toElasticsearchQuery(node!)).toMatchInlineSnapshot(` Object { "bool": Object { @@ -253,5 +172,51 @@ describe('utils', () => { } `); }); + + it('escapes the query correctly', () => { + const node = buildRangeFilter({ + from: '2022-04-27T12:55:47.576Z', + to: '2022-04-27T12:56:47.576Z', + field: ' 1 ? nodeBuilder.and(filters) : filters[0]; }; +export const addSeverityFilter = ({ + severity, + appendFilter, + type = CASE_SAVED_OBJECT, +}: { + severity: CaseSeverity; + appendFilter?: KueryNode; + type?: string; +}): KueryNode => { + const filters: KueryNode[] = []; + filters.push(nodeBuilder.is(`${type}.attributes.severity`, severity)); + + if (appendFilter) { + filters.push(appendFilter); + } + + return filters.length > 1 ? nodeBuilder.and(filters) : filters[0]; +}; + interface FilterField { filters?: string | string[]; field: string; @@ -199,8 +219,14 @@ export const buildRangeFilter = ({ } try { - const fromKQL = from != null ? `${savedObjectType}.attributes.${field} >= ${from}` : undefined; - const toKQL = to != null ? `${savedObjectType}.attributes.${field} <= ${to}` : undefined; + const fromKQL = + from != null + ? `${escapeKuery(savedObjectType)}.attributes.${escapeKuery(field)} >= ${escapeKuery(from)}` + : undefined; + const toKQL = + to != null + ? `${escapeKuery(savedObjectType)}.attributes.${escapeKuery(field)} <= ${escapeKuery(to)}` + : undefined; const rangeKQLQuery = `${fromKQL != null ? fromKQL : ''} ${ fromKQL != null && toKQL != null ? 'and' : '' @@ -216,6 +242,7 @@ export const constructQueryOptions = ({ tags, reporters, status, + severity, sortByField, owner, authorizationFilter, @@ -225,6 +252,7 @@ export const constructQueryOptions = ({ tags?: string | string[]; reporters?: string | string[]; status?: CaseStatuses; + severity?: CaseSeverity; sortByField?: string; owner?: string | string[]; authorizationFilter?: KueryNode; @@ -244,10 +272,12 @@ export const constructQueryOptions = ({ const ownerFilter = buildFilter({ filters: owner ?? [], field: OWNER_FIELD, operator: 'or' }); const statusFilter = status != null ? addStatusFilter({ status }) : undefined; + const severityFilter = severity != null ? addSeverityFilter({ severity }) : undefined; const rangeFilter = buildRangeFilter({ from, to }); const filters: KueryNode[] = [ statusFilter, + severityFilter, tagsFilter, reportersFilter, rangeFilter, diff --git a/x-pack/plugins/cases/server/common/utils.test.ts b/x-pack/plugins/cases/server/common/utils.test.ts index 47af4b11a0a96..918a48863cac0 100644 --- a/x-pack/plugins/cases/server/common/utils.test.ts +++ b/x-pack/plugins/cases/server/common/utils.test.ts @@ -9,11 +9,14 @@ import { SavedObject, SavedObjectsFindResponse } from '@kbn/core/server'; import { makeLensEmbeddableFactory } from '@kbn/lens-plugin/server/embeddable/make_lens_embeddable_factory'; import { SECURITY_SOLUTION_OWNER } from '../../common/constants'; import { + CaseConnector, CaseResponse, + CaseSeverity, CommentAttributes, CommentRequest, CommentRequestUserType, CommentType, + ConnectorTypes, } from '../../common/api'; import { mockCaseComments, mockCases } from '../routes/api/__fixtures__/mock_saved_objects'; import { @@ -29,7 +32,9 @@ import { extractLensReferencesFromCommentString, getOrUpdateLensReferences, asArray, + transformNewCase, } from './utils'; +import { newCase } from '../routes/api/__mocks__/request_responses'; interface CommentReference { ids: string[]; @@ -67,6 +72,128 @@ function createCommentFindResponse( } describe('common utils', () => { + describe('transformNewCase', () => { + beforeAll(() => { + jest.useFakeTimers('modern'); + jest.setSystemTime(new Date('2020-04-09T09:43:51.778Z')); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + const connector: CaseConnector = { + id: '123', + name: 'My connector', + type: ConnectorTypes.jira, + fields: { issueType: 'Task', priority: 'High', parent: null }, + }; + + it('transform correctly', () => { + const myCase = { + newCase: { ...newCase, connector }, + user: { + email: 'elastic@elastic.co', + full_name: 'Elastic', + username: 'elastic', + }, + }; + + const res = transformNewCase(myCase); + + expect(res).toMatchInlineSnapshot(` + Object { + "closed_at": null, + "closed_by": null, + "connector": Object { + "fields": Object { + "issueType": "Task", + "parent": null, + "priority": "High", + }, + "id": "123", + "name": "My connector", + "type": ".jira", + }, + "created_at": "2020-04-09T09:43:51.778Z", + "created_by": Object { + "email": "elastic@elastic.co", + "full_name": "Elastic", + "username": "elastic", + }, + "description": "A description", + "duration": null, + "external_service": null, + "owner": "securitySolution", + "settings": Object { + "syncAlerts": true, + }, + "severity": "low", + "status": "open", + "tags": Array [ + "new", + "case", + ], + "title": "My new case", + "updated_at": null, + "updated_by": null, + } + `); + }); + + it('transform correctly with severity provided', () => { + const myCase = { + newCase: { ...newCase, connector, severity: CaseSeverity.MEDIUM }, + user: { + email: 'elastic@elastic.co', + full_name: 'Elastic', + username: 'elastic', + }, + }; + + const res = transformNewCase(myCase); + + expect(res).toMatchInlineSnapshot(` + Object { + "closed_at": null, + "closed_by": null, + "connector": Object { + "fields": Object { + "issueType": "Task", + "parent": null, + "priority": "High", + }, + "id": "123", + "name": "My connector", + "type": ".jira", + }, + "created_at": "2020-04-09T09:43:51.778Z", + "created_by": Object { + "email": "elastic@elastic.co", + "full_name": "Elastic", + "username": "elastic", + }, + "description": "A description", + "duration": null, + "external_service": null, + "owner": "securitySolution", + "settings": Object { + "syncAlerts": true, + }, + "severity": "medium", + "status": "open", + "tags": Array [ + "new", + "case", + ], + "title": "My new case", + "updated_at": null, + "updated_by": null, + } + `); + }); + }); + describe('transformCases', () => { it('transforms correctly', () => { const casesMap = new Map( @@ -103,12 +230,14 @@ describe('common utils', () => { "username": "elastic", }, "description": "This is a brand new case of a bad meanie defacing data", + "duration": null, "external_service": null, "id": "mock-id-1", "owner": "securitySolution", "settings": Object { "syncAlerts": true, }, + "severity": "low", "status": "open", "tags": Array [ "defacement", @@ -141,12 +270,14 @@ describe('common utils', () => { "username": "elastic", }, "description": "Oh no, a bad meanie destroying data!", + "duration": null, "external_service": null, "id": "mock-id-2", "owner": "securitySolution", "settings": Object { "syncAlerts": true, }, + "severity": "low", "status": "open", "tags": Array [ "Data Destruction", @@ -183,12 +314,14 @@ describe('common utils', () => { "username": "elastic", }, "description": "Oh no, a bad meanie going LOLBins all over the place!", + "duration": null, "external_service": null, "id": "mock-id-3", "owner": "securitySolution", "settings": Object { "syncAlerts": true, }, + "severity": "low", "status": "open", "tags": Array [ "LOLBins", @@ -229,12 +362,14 @@ describe('common utils', () => { "username": "elastic", }, "description": "Oh no, a bad meanie going LOLBins all over the place!", + "duration": null, "external_service": null, "id": "mock-id-4", "owner": "securitySolution", "settings": Object { "syncAlerts": true, }, + "severity": "low", "status": "closed", "tags": Array [ "LOLBins", @@ -292,12 +427,14 @@ describe('common utils', () => { "username": "elastic", }, "description": "Oh no, a bad meanie going LOLBins all over the place!", + "duration": null, "external_service": null, "id": "mock-id-3", "owner": "securitySolution", "settings": Object { "syncAlerts": true, }, + "severity": "low", "status": "open", "tags": Array [ "LOLBins", @@ -346,12 +483,14 @@ describe('common utils', () => { "username": "elastic", }, "description": "Oh no, a bad meanie going LOLBins all over the place!", + "duration": null, "external_service": null, "id": "mock-id-3", "owner": "securitySolution", "settings": Object { "syncAlerts": true, }, + "severity": "low", "status": "open", "tags": Array [ "LOLBins", @@ -423,12 +562,14 @@ describe('common utils', () => { "username": "elastic", }, "description": "Oh no, a bad meanie going LOLBins all over the place!", + "duration": null, "external_service": null, "id": "mock-id-3", "owner": "securitySolution", "settings": Object { "syncAlerts": true, }, + "severity": "low", "status": "open", "tags": Array [ "LOLBins", @@ -475,12 +616,14 @@ describe('common utils', () => { "username": "elastic", }, "description": "This is a brand new case of a bad meanie defacing data", + "duration": null, "external_service": null, "id": "mock-id-1", "owner": "securitySolution", "settings": Object { "syncAlerts": true, }, + "severity": "low", "status": "open", "tags": Array [ "defacement", diff --git a/x-pack/plugins/cases/server/common/utils.ts b/x-pack/plugins/cases/server/common/utils.ts index 9385e83c948c9..bc8dbf8a6e842 100644 --- a/x-pack/plugins/cases/server/common/utils.ts +++ b/x-pack/plugins/cases/server/common/utils.ts @@ -19,6 +19,7 @@ import { CaseAttributes, CasePostRequest, CaseResponse, + CaseSeverity, CasesFindResponse, CaseStatuses, CommentAttributes, @@ -55,6 +56,8 @@ export const transformNewCase = ({ newCase: CasePostRequest; }): CaseAttributes => ({ ...newCase, + duration: null, + severity: newCase.severity ?? CaseSeverity.LOW, closed_at: null, closed_by: null, created_at: new Date().toISOString(), diff --git a/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts b/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts index cf5de90f4f61f..77e1a64012c6d 100644 --- a/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts +++ b/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts @@ -8,6 +8,7 @@ import { SavedObject } from '@kbn/core/server'; import { CaseAttributes, + CaseSeverity, CaseStatuses, CommentAttributes, CommentType, @@ -34,6 +35,8 @@ export const mockCases: Array> = [ email: 'testemail@elastic.co', username: 'elastic', }, + severity: CaseSeverity.LOW, + duration: null, description: 'This is a brand new case of a bad meanie defacing data', external_service: null, title: 'Super Bad Security Issue', @@ -72,6 +75,8 @@ export const mockCases: Array> = [ email: 'testemail@elastic.co', username: 'elastic', }, + severity: CaseSeverity.LOW, + duration: null, description: 'Oh no, a bad meanie destroying data!', external_service: null, title: 'Damaging Data Destruction Detected', @@ -110,6 +115,8 @@ export const mockCases: Array> = [ email: 'testemail@elastic.co', username: 'elastic', }, + severity: CaseSeverity.LOW, + duration: null, description: 'Oh no, a bad meanie going LOLBins all over the place!', external_service: null, title: 'Another bad one', @@ -152,6 +159,8 @@ export const mockCases: Array> = [ email: 'testemail@elastic.co', username: 'elastic', }, + severity: CaseSeverity.LOW, + duration: null, description: 'Oh no, a bad meanie going LOLBins all over the place!', external_service: null, status: CaseStatuses.closed, diff --git a/x-pack/plugins/cases/server/routes/api/get_external_routes.ts b/x-pack/plugins/cases/server/routes/api/get_external_routes.ts index 7908e4eb84359..7b7a18cc7c83c 100644 --- a/x-pack/plugins/cases/server/routes/api/get_external_routes.ts +++ b/x-pack/plugins/cases/server/routes/api/get_external_routes.ts @@ -30,6 +30,7 @@ import { patchCaseConfigureRoute } from './configure/patch_configure'; import { postCaseConfigureRoute } from './configure/post_configure'; import { getAllAlertsAttachedToCaseRoute } from './comments/get_alerts'; import { getCaseMetricRoute } from './metrics/get_case_metrics'; +import { getCasesMetricRoute } from './metrics/get_cases_metrics'; export const getExternalRoutes = () => [ @@ -58,4 +59,5 @@ export const getExternalRoutes = () => postCaseConfigureRoute, getAllAlertsAttachedToCaseRoute, getCaseMetricRoute, + getCasesMetricRoute, ] as CaseRoute[]; diff --git a/x-pack/plugins/cases/server/routes/api/metrics/get_case_metrics.ts b/x-pack/plugins/cases/server/routes/api/metrics/get_case_metrics.ts index 13bfa2093f623..394c0099b4991 100644 --- a/x-pack/plugins/cases/server/routes/api/metrics/get_case_metrics.ts +++ b/x-pack/plugins/cases/server/routes/api/metrics/get_case_metrics.ts @@ -19,17 +19,22 @@ export const getCaseMetricRoute = createCasesRoute({ case_id: schema.string({ minLength: 1 }), }), query: schema.object({ - features: schema.arrayOf(schema.string({ minLength: 1 })), + features: schema.oneOf([ + schema.arrayOf(schema.string({ minLength: 1 })), + schema.string({ minLength: 1 }), + ]), }), }, handler: async ({ context, request, response }) => { try { const caseContext = await context.cases; const client = await caseContext.getCasesClient(); + const { features } = request.query; + return response.ok({ body: await client.metrics.getCaseMetrics({ caseId: request.params.case_id, - features: request.query.features, + features: Array.isArray(features) ? features : [features], }), }); } catch (error) { diff --git a/x-pack/plugins/cases/server/routes/api/metrics/get_cases_metrics.ts b/x-pack/plugins/cases/server/routes/api/metrics/get_cases_metrics.ts new file mode 100644 index 0000000000000..44d351571edbb --- /dev/null +++ b/x-pack/plugins/cases/server/routes/api/metrics/get_cases_metrics.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 { schema } from '@kbn/config-schema'; + +import { CASE_METRICS_URL } from '../../../../common/constants'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; + +export const getCasesMetricRoute = createCasesRoute({ + method: 'get', + path: CASE_METRICS_URL, + params: { + query: schema.object({ + features: schema.oneOf([ + schema.arrayOf(schema.string({ minLength: 1 })), + schema.string({ minLength: 1 }), + ]), + owner: schema.maybe(schema.oneOf([schema.arrayOf(schema.string()), schema.string()])), + from: schema.maybe(schema.string()), + to: schema.maybe(schema.string()), + }), + }, + handler: async ({ context, request, response }) => { + try { + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); + const { features } = request.query; + + return response.ok({ + body: await client.metrics.getCasesMetrics({ + ...request.query, + features: Array.isArray(features) ? features : [features], + }), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to get cases metrics in route: ${error}`, + error, + }); + } + }, +}); diff --git a/x-pack/plugins/cases/server/saved_object_types/cases.ts b/x-pack/plugins/cases/server/saved_object_types/cases.ts index 441bd818189ae..9b2ea975c4dcd 100644 --- a/x-pack/plugins/cases/server/saved_object_types/cases.ts +++ b/x-pack/plugins/cases/server/saved_object_types/cases.ts @@ -59,6 +59,9 @@ export const createCaseSavedObjectType = ( }, }, }, + duration: { + type: 'unsigned_long', + }, description: { type: 'text', }, @@ -149,13 +152,16 @@ export const createCaseSavedObjectType = ( }, }, }, + severity: { + type: 'keyword', + }, }, }, migrations: caseMigrations, management: { importableAndExportable: true, defaultSearchField: 'title', - icon: 'folderExclamation', + icon: 'casesApp', getTitle: (savedObject: SavedObject) => savedObject.attributes.title, onExport: async ( context: SavedObjectsExportTransformContext, diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts index bdc6b6ca18e64..b4d3421643a41 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts @@ -9,13 +9,14 @@ import { SavedObjectSanitizedDoc } from '@kbn/core/server'; import { CaseAttributes, CaseFullExternalService, + CaseSeverity, ConnectorTypes, NONE_CONNECTOR_ID, } from '../../../common/api'; import { CASE_SAVED_OBJECT } from '../../../common/constants'; import { getNoneCaseConnector } from '../../common/utils'; import { createExternalService, ESCaseConnectorWithId } from '../../services/test_utils'; -import { caseConnectorIdMigration, removeCaseType } from './cases'; +import { addDuration, addSeverity, caseConnectorIdMigration, removeCaseType } from './cases'; // eslint-disable-next-line @typescript-eslint/naming-convention const create_7_14_0_case = ({ @@ -371,4 +372,170 @@ describe('case migrations', () => { }); }); }); + + describe('addDuration', () => { + it('adds the duration correctly', () => { + const doc = { + id: '123', + attributes: { + created_at: '2021-11-23T19:00:00Z', + closed_at: '2021-11-23T19:02:00Z', + }, + type: 'abc', + references: [], + } as unknown as SavedObjectSanitizedDoc; + + expect(addDuration(doc)).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + duration: 120, + }, + }); + }); + + it.each([['invalid'], [null]])( + 'returns null if the createdAt date is %s', + (createdAtInvalid) => { + const doc = { + id: '123', + attributes: { + created_at: createdAtInvalid, + closed_at: '2021-11-23T19:02:00Z', + }, + type: 'abc', + references: [], + } as unknown as SavedObjectSanitizedDoc; + + expect(addDuration(doc)).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + duration: null, + }, + }); + } + ); + + it.each([['invalid'], [null]])('returns null if the closedAt date is %s', (closedAtInvalid) => { + const doc = { + id: '123', + attributes: { + created_at: '2021-11-23T19:02:00Z', + closed_at: closedAtInvalid, + }, + type: 'abc', + references: [], + } as unknown as SavedObjectSanitizedDoc; + + expect(addDuration(doc)).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + duration: null, + }, + }); + }); + + it('returns null if created_at > closed_at', () => { + const doc = { + id: '123', + attributes: { + created_at: '2021-11-23T19:05:00Z', + closed_at: '2021-11-23T19:00:00Z', + }, + type: 'abc', + references: [], + } as unknown as SavedObjectSanitizedDoc; + + expect(addDuration(doc)).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + duration: null, + }, + }); + }); + + it('rounds the seconds correctly', () => { + const doc = { + id: '123', + attributes: { + created_at: '2022-04-11T15:56:00.087Z', + closed_at: '2022-04-11T15:58:56.187Z', + }, + type: 'abc', + references: [], + } as unknown as SavedObjectSanitizedDoc; + + expect(addDuration(doc)).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + duration: 176, + }, + }); + }); + + it('rounds to zero correctly', () => { + const doc = { + id: '123', + attributes: { + created_at: '2022-04-11T15:56:00.087Z', + closed_at: '2022-04-11T15:56:00.187Z', + }, + type: 'abc', + references: [], + } as unknown as SavedObjectSanitizedDoc; + + expect(addDuration(doc)).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + duration: 0, + }, + }); + }); + }); + + describe('add severity', () => { + it('adds the severity correctly when none is present', () => { + const doc = { + id: '123', + attributes: { + created_at: '2021-11-23T19:00:00Z', + closed_at: '2021-11-23T19:02:00Z', + }, + type: 'abc', + references: [], + } as unknown as SavedObjectSanitizedDoc; + expect(addSeverity(doc)).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + severity: CaseSeverity.LOW, + }, + }); + }); + + it('keeps the existing value if the field already exists', () => { + const doc = { + id: '123', + attributes: { + severity: CaseSeverity.CRITICAL, + created_at: '2021-11-23T19:00:00Z', + closed_at: '2021-11-23T19:02:00Z', + }, + type: 'abc', + references: [], + } as unknown as SavedObjectSanitizedDoc; + expect(addSeverity(doc)).toEqual({ + ...doc, + attributes: { + ...doc.attributes, + severity: CaseSeverity.CRITICAL, + }, + }); + }); + }); }); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts index eaa93a585015f..c4961f742abc7 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts @@ -11,7 +11,7 @@ import { cloneDeep, unset } from 'lodash'; import { SavedObjectUnsanitizedDoc, SavedObjectSanitizedDoc } from '@kbn/core/server'; import { addOwnerToSO, SanitizedCaseOwner } from '.'; import { ESConnectorFields } from '../../services'; -import { ConnectorTypes } from '../../../common/api'; +import { CaseAttributes, CaseSeverity, ConnectorTypes } from '../../../common/api'; import { CONNECTOR_ID_REFERENCE_NAME, PUSH_CONNECTOR_ID_REFERENCE_NAME, @@ -21,6 +21,7 @@ import { transformPushConnectorIdToReference, } from './user_actions/connector_id'; import { CASE_TYPE_INDIVIDUAL } from './constants'; +import { pipeMigrations } from './utils'; interface UnsanitizedCaseConnector { connector_id: string; @@ -86,6 +87,41 @@ export const removeCaseType = ( return { ...docCopy, references: doc.references ?? [] }; }; +export const addDuration = ( + doc: SavedObjectUnsanitizedDoc> +): SavedObjectSanitizedDoc => { + let duration = null; + + try { + const createdAt = doc.attributes.created_at; + const closedAt = doc.attributes.closed_at; + + if (createdAt != null && closedAt != null) { + const createdAtMillis = new Date(createdAt).getTime(); + const closedAtMillis = new Date(closedAt).getTime(); + + if (!isNaN(createdAtMillis) && !isNaN(closedAtMillis) && closedAtMillis >= createdAtMillis) { + duration = Math.floor((closedAtMillis - createdAtMillis) / 1000); + } + } + } catch (err) { + // Silence date errors + } + + /** + * Duration is the time from the creation of the case to the close of the case in seconds + * If an error occurs or the case has not been closed then the duration is set to null + */ + return { ...doc, attributes: { ...doc.attributes, duration }, references: doc.references ?? [] }; +}; + +export const addSeverity = ( + doc: SavedObjectUnsanitizedDoc +): SavedObjectSanitizedDoc => { + const severity = doc.attributes.severity ?? CaseSeverity.LOW; + return { ...doc, attributes: { ...doc.attributes, severity }, references: doc.references ?? [] }; +}; + export const caseMigrations = { '7.10.0': ( doc: SavedObjectUnsanitizedDoc @@ -147,4 +183,5 @@ export const caseMigrations = { }, '7.15.0': caseConnectorIdMigration, '8.1.0': removeCaseType, + '8.3.0': pipeMigrations(addDuration, addSeverity), }; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts index 65c1d42271845..8996f89155949 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { LogMeta, SavedObjectMigrationContext } from '@kbn/core/server'; +import { LogMeta, SavedObjectMigrationContext, SavedObjectUnsanitizedDoc } from '@kbn/core/server'; interface MigrationLogMeta extends LogMeta { migrations: { @@ -39,3 +39,10 @@ export function logError({ } ); } + +type CaseMigration = (doc: SavedObjectUnsanitizedDoc) => SavedObjectUnsanitizedDoc; + +export function pipeMigrations(...migrations: Array>): CaseMigration { + return (doc: SavedObjectUnsanitizedDoc) => + migrations.reduce((migratedDoc, nextMigration) => nextMigration(migratedDoc), doc); +} diff --git a/x-pack/plugins/cases/server/services/alerts/index.ts b/x-pack/plugins/cases/server/services/alerts/index.ts index 69c44b30fec28..b219c50964d39 100644 --- a/x-pack/plugins/cases/server/services/alerts/index.ts +++ b/x-pack/plugins/cases/server/services/alerts/index.ts @@ -30,7 +30,7 @@ export class AlertService { aggregationBuilders, alerts, }: { - aggregationBuilders: AggregationBuilder[]; + aggregationBuilders: Array>; alerts: AlertIdIndex[]; }): Promise { try { diff --git a/x-pack/plugins/cases/server/services/cases/index.test.ts b/x-pack/plugins/cases/server/services/cases/index.test.ts index 7e576b83404db..826a8d06e97f2 100644 --- a/x-pack/plugins/cases/server/services/cases/index.test.ts +++ b/x-pack/plugins/cases/server/services/cases/index.test.ts @@ -20,6 +20,7 @@ import { SavedObject, SavedObjectReference, SavedObjectsCreateOptions, + SavedObjectsFindResponse, SavedObjectsFindResult, SavedObjectsUpdateOptions, SavedObjectsUpdateResponse, @@ -160,10 +161,12 @@ describe('CasesService', () => { "username": "elastic", }, "description": "This is a brand new case of a bad meanie defacing data", + "duration": null, "owner": "securitySolution", "settings": Object { "syncAlerts": true, }, + "severity": "low", "status": "open", "tags": Array [ "defacement", @@ -500,6 +503,7 @@ describe('CasesService', () => { "username": "elastic", }, "description": "This is a brand new case of a bad meanie defacing data", + "duration": null, "external_service": Object { "connector_name": ".jira", "external_id": "100", @@ -516,6 +520,7 @@ describe('CasesService', () => { "settings": Object { "syncAlerts": true, }, + "severity": "low", "status": "open", "tags": Array [ "defacement", @@ -1132,4 +1137,71 @@ describe('CasesService', () => { }); }); }); + + describe('executeAggregations', () => { + const aggregationBuilders = [ + { + build: () => ({ + myAggregation: { avg: { field: 'avg-field' } }, + }), + getName: () => 'avg-test-builder', + formatResponse: () => {}, + }, + { + build: () => ({ + myAggregation: { min: { field: 'min-field' } }, + }), + getName: () => 'min-test-builder', + formatResponse: () => {}, + }, + ]; + + it('returns an aggregation correctly', async () => { + unsecuredSavedObjectsClient.find.mockResolvedValue({ + saved_objects: [], + total: 0, + page: 1, + per_page: 1, + aggregations: { myAggregation: { value: 0 } }, + } as SavedObjectsFindResponse); + + const res = await service.executeAggregations({ aggregationBuilders }); + expect(res).toEqual({ myAggregation: { value: 0 } }); + }); + + it('calls find correctly', async () => { + unsecuredSavedObjectsClient.find.mockResolvedValue({ + saved_objects: [], + total: 0, + page: 1, + per_page: 1, + aggregations: { myAggregation: { value: 0 } }, + } as SavedObjectsFindResponse); + + await service.executeAggregations({ aggregationBuilders, options: { perPage: 20 } }); + expect(unsecuredSavedObjectsClient.find.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "aggs": Object { + "myAggregation": Object { + "min": Object { + "field": "min-field", + }, + }, + }, + "perPage": 20, + "sortField": "created_at", + "type": "cases", + } + `); + }); + + it('throws an error correctly', async () => { + expect.assertions(1); + unsecuredSavedObjectsClient.find.mockRejectedValue(new Error('Aggregation error')); + + await expect(service.executeAggregations({ aggregationBuilders })).rejects.toThrow( + 'Failed to execute aggregations [avg-test-builder,min-test-builder]: Error: Aggregation error' + ); + }); + }); }); diff --git a/x-pack/plugins/cases/server/services/cases/index.ts b/x-pack/plugins/cases/server/services/cases/index.ts index 26557d4ea7748..f75e52e63dca9 100644 --- a/x-pack/plugins/cases/server/services/cases/index.ts +++ b/x-pack/plugins/cases/server/services/cases/index.ts @@ -16,6 +16,7 @@ import { SavedObjectsBulkUpdateResponse, SavedObjectsUpdateResponse, SavedObjectsResolveResponse, + SavedObjectsFindOptions, } from '@kbn/core/server'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; @@ -52,6 +53,8 @@ import { } from './transform'; import { ESCaseAttributes } from './types'; import { AttachmentService } from '../attachments'; +import { AggregationBuilder, AggregationResponse } from '../../client/metrics/types'; +import { createCaseError } from '../../common/error'; interface GetCaseIdsByAlertIdArgs { alertId: string; @@ -626,4 +629,38 @@ export class CasesService { throw error; } } + + public async executeAggregations({ + aggregationBuilders, + options, + }: { + aggregationBuilders: Array>; + options?: Omit; + }): Promise { + try { + const builtAggs = aggregationBuilders.reduce((acc, agg) => { + return { ...acc, ...agg.build() }; + }, {}); + + const res = await this.unsecuredSavedObjectsClient.find< + ESCaseAttributes, + AggregationResponse + >({ + sortField: defaultSortField, + ...options, + aggs: builtAggs, + type: CASE_SAVED_OBJECT, + }); + + return res.aggregations; + } catch (error) { + const aggregationNames = aggregationBuilders.map((agg) => agg.getName()); + + throw createCaseError({ + message: `Failed to execute aggregations [${aggregationNames.join(',')}]: ${error}`, + error, + logger: this.log, + }); + } + } } diff --git a/x-pack/plugins/cases/server/services/mocks.ts b/x-pack/plugins/cases/server/services/mocks.ts index a9f3d427bba65..acd19506277c1 100644 --- a/x-pack/plugins/cases/server/services/mocks.ts +++ b/x-pack/plugins/cases/server/services/mocks.ts @@ -39,6 +39,7 @@ export const createCaseServiceMock = (): CaseServiceMock => { patchCases: jest.fn(), findCasesGroupedByID: jest.fn(), getCaseStatusStats: jest.fn(), + executeAggregations: jest.fn(), }; // the cast here is required because jest.Mocked tries to include private members and would throw an error diff --git a/x-pack/plugins/cases/server/services/test_utils.ts b/x-pack/plugins/cases/server/services/test_utils.ts index 16c6445af774f..ff86783ae8e9c 100644 --- a/x-pack/plugins/cases/server/services/test_utils.ts +++ b/x-pack/plugins/cases/server/services/test_utils.ts @@ -10,15 +10,18 @@ import { ACTION_SAVED_OBJECT_TYPE } from '@kbn/actions-plugin/server'; import { ESConnectorFields } from '.'; import { CONNECTOR_ID_REFERENCE_NAME, PUSH_CONNECTOR_ID_REFERENCE_NAME } from '../common/constants'; import { + CaseAttributes, CaseConnector, CaseExternalServiceBasic, CaseFullExternalService, + CaseSeverity, CaseStatuses, ConnectorTypes, NONE_CONNECTOR_ID, } from '../../common/api'; import { CASE_SAVED_OBJECT, SECURITY_SOLUTION_OWNER } from '../../common/constants'; import { ESCaseAttributes, ExternalServicesWithoutConnectorId } from './cases/types'; +import { getNoneCaseConnector } from '../common/utils'; /** * This is only a utility interface to help with constructing test cases. After the migration, the ES format will no longer @@ -96,7 +99,7 @@ export const createExternalService = ( ...overrides, }); -export const basicCaseFields = { +export const basicCaseFields: CaseAttributes = { closed_at: null, closed_by: null, created_at: '2019-11-25T21:54:48.952Z', @@ -105,6 +108,8 @@ export const basicCaseFields = { email: 'testemail@elastic.co', username: 'elastic', }, + severity: CaseSeverity.LOW, + duration: null, description: 'This is a brand new case of a bad meanie defacing data', title: 'Super Bad Security Issue', status: CaseStatuses.open, @@ -115,6 +120,8 @@ export const basicCaseFields = { email: 'testemail@elastic.co', username: 'elastic', }, + connector: getNoneCaseConnector(), + external_service: null, settings: { syncAlerts: true, }, diff --git a/x-pack/plugins/cases/server/services/user_actions/builder_factory.test.ts b/x-pack/plugins/cases/server/services/user_actions/builder_factory.test.ts index 2e2a9e905bb7e..ab349d690edef 100644 --- a/x-pack/plugins/cases/server/services/user_actions/builder_factory.test.ts +++ b/x-pack/plugins/cases/server/services/user_actions/builder_factory.test.ts @@ -9,6 +9,7 @@ import { SECURITY_SOLUTION_OWNER } from '../../../common'; import { Actions, ActionTypes, + CaseSeverity, CaseStatuses, CommentType, ConnectorTypes, @@ -340,6 +341,40 @@ describe('UserActionBuilder', () => { `); }); + it('builds a severity user action correctly', () => { + const builder = builderFactory.getBuilder(ActionTypes.severity)!; + const userAction = builder.build({ + payload: { severity: CaseSeverity.LOW }, + ...commonArgs, + }); + + expect(userAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "update", + "created_at": "2022-01-09T22:00:00.000Z", + "created_by": Object { + "email": "elastic@elastic.co", + "full_name": "Elastic User", + "username": "elastic", + }, + "owner": "securitySolution", + "payload": Object { + "severity": "low", + }, + "type": "severity", + }, + "references": Array [ + Object { + "id": "123", + "name": "associated-cases", + "type": "cases", + }, + ], + } + `); + }); + it('builds a settings user action correctly', () => { const builder = builderFactory.getBuilder(ActionTypes.settings)!; const userAction = builder.build({ @@ -413,6 +448,7 @@ describe('UserActionBuilder', () => { "settings": Object { "syncAlerts": true, }, + "severity": "low", "status": "open", "tags": Array [ "sir", diff --git a/x-pack/plugins/cases/server/services/user_actions/builder_factory.ts b/x-pack/plugins/cases/server/services/user_actions/builder_factory.ts index 5d5f33c2ae4f5..510b6d12b1fa1 100644 --- a/x-pack/plugins/cases/server/services/user_actions/builder_factory.ts +++ b/x-pack/plugins/cases/server/services/user_actions/builder_factory.ts @@ -17,6 +17,7 @@ import { TagsUserActionBuilder } from './builders/tags'; import { SettingsUserActionBuilder } from './builders/settings'; import { DeleteCaseUserActionBuilder } from './builders/delete_case'; import { UserActionBuilder } from './abstract_builder'; +import { SeverityUserActionBuilder } from './builders/severity'; const builderMap = { title: TitleUserActionBuilder, @@ -27,6 +28,7 @@ const builderMap = { pushed: PushedUserActionBuilder, tags: TagsUserActionBuilder, status: StatusUserActionBuilder, + severity: SeverityUserActionBuilder, settings: SettingsUserActionBuilder, delete_case: DeleteCaseUserActionBuilder, }; diff --git a/x-pack/plugins/cases/server/services/user_actions/builders/severity.ts b/x-pack/plugins/cases/server/services/user_actions/builders/severity.ts new file mode 100644 index 0000000000000..4abd5856972b4 --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/builders/severity.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 { Actions, ActionTypes } from '../../../../common/api'; +import { UserActionBuilder } from '../abstract_builder'; +import { UserActionParameters, BuilderReturnValue } from '../types'; + +export class SeverityUserActionBuilder extends UserActionBuilder { + build(args: UserActionParameters<'severity'>): BuilderReturnValue { + return this.buildCommonUserAction({ + ...args, + action: Actions.update, + valueKey: 'severity', + value: args.payload.severity, + type: ActionTypes.severity, + }); + } +} diff --git a/x-pack/plugins/cases/server/services/user_actions/index.test.ts b/x-pack/plugins/cases/server/services/user_actions/index.test.ts index eb1b57622d24d..44e91bcae09d3 100644 --- a/x-pack/plugins/cases/server/services/user_actions/index.test.ts +++ b/x-pack/plugins/cases/server/services/user_actions/index.test.ts @@ -13,6 +13,7 @@ import { ACTION_SAVED_OBJECT_TYPE } from '@kbn/actions-plugin/server'; import { Actions, ActionTypes, + CaseSeverity, CaseStatuses, CaseUserActionAttributes, ConnectorUserAction, @@ -107,6 +108,7 @@ const createCaseUserAction = (): SavedObject => { description: 'a desc', settings: { syncAlerts: false }, status: CaseStatuses.open, + severity: CaseSeverity.LOW, tags: [], owner: SECURITY_SOLUTION_OWNER, }, @@ -447,6 +449,7 @@ describe('CaseUserActionService', () => { payload: casePayload, type: ActionTypes.create_case, }); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( 'cases-user-actions', { @@ -477,6 +480,7 @@ describe('CaseUserActionService', () => { owner: 'securitySolution', settings: { syncAlerts: true }, status: 'open', + severity: 'low', tags: ['sir'], title: 'Case SIR', }, @@ -517,6 +521,33 @@ describe('CaseUserActionService', () => { }); }); + describe('severity', () => { + it('creates an update severity user action', async () => { + await service.createUserAction({ + ...commonArgs, + payload: { severity: CaseSeverity.MEDIUM }, + type: ActionTypes.severity, + }); + + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( + 'cases-user-actions', + { + action: Actions.update, + created_at: '2022-01-09T22:00:00.000Z', + created_by: { + email: 'elastic@elastic.co', + full_name: 'Elastic User', + username: 'elastic', + }, + type: 'severity', + owner: 'securitySolution', + payload: { severity: 'medium' }, + }, + { references: [{ id: '123', name: 'associated-cases', type: 'cases' }] } + ); + }); + }); + describe('push', () => { it('creates a push user action', async () => { await service.createUserAction({ @@ -801,6 +832,30 @@ describe('CaseUserActionService', () => { references: [{ id: '2', name: 'associated-cases', type: 'cases' }], type: 'cases-user-actions', }, + { + attributes: { + action: 'update', + created_at: '2022-01-09T22:00:00.000Z', + created_by: { + email: 'elastic@elastic.co', + full_name: 'Elastic User', + username: 'elastic', + }, + owner: 'securitySolution', + payload: { + severity: 'critical', + }, + type: 'severity', + }, + references: [ + { + id: '2', + name: 'associated-cases', + type: 'cases', + }, + ], + type: 'cases-user-actions', + }, ]); }); }); diff --git a/x-pack/plugins/cases/server/services/user_actions/mocks.ts b/x-pack/plugins/cases/server/services/user_actions/mocks.ts index c745c040ac2ce..bc35f98bf926e 100644 --- a/x-pack/plugins/cases/server/services/user_actions/mocks.ts +++ b/x-pack/plugins/cases/server/services/user_actions/mocks.ts @@ -7,7 +7,7 @@ import { CASE_SAVED_OBJECT } from '../../../common/constants'; import { SECURITY_SOLUTION_OWNER } from '../../../common'; -import { CaseStatuses, CommentType, ConnectorTypes } from '../../../common/api'; +import { CaseSeverity, CaseStatuses, CommentType, ConnectorTypes } from '../../../common/api'; import { createCaseSavedObjectResponse } from '../test_utils'; import { transformSavedObjectToExternalModel } from '../cases/transform'; @@ -30,6 +30,7 @@ export const casePayload = { }, }, settings: { syncAlerts: true }, + severity: CaseSeverity.LOW, owner: SECURITY_SOLUTION_OWNER, }; @@ -69,6 +70,7 @@ export const updatedCases = [ description: 'updated desc', tags: ['one', 'two'], settings: { syncAlerts: false }, + severity: CaseSeverity.CRITICAL, }, references: [], }, diff --git a/x-pack/plugins/cases/server/services/user_actions/types.ts b/x-pack/plugins/cases/server/services/user_actions/types.ts index f681a9186181c..a60dee552a6be 100644 --- a/x-pack/plugins/cases/server/services/user_actions/types.ts +++ b/x-pack/plugins/cases/server/services/user_actions/types.ts @@ -9,6 +9,7 @@ import { SavedObjectReference } from '@kbn/core/server'; import { CasePostRequest, CaseSettings, + CaseSeverity, CaseStatuses, CommentUserAction, ConnectorUserAction, @@ -28,6 +29,9 @@ export interface BuilderParameters { status: { parameters: { payload: { status: CaseStatuses } }; }; + severity: { + parameters: { payload: { severity: CaseSeverity } }; + }; tags: { parameters: { payload: { tags: string[] } }; }; diff --git a/x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.test.ts b/x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.test.ts new file mode 100644 index 0000000000000..a6dc1f59b00e3 --- /dev/null +++ b/x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.test.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { firstValueFrom } from 'rxjs'; +import { registerCloudDeploymentIdAnalyticsContext } from './register_cloud_deployment_id_analytics_context'; + +describe('registerCloudDeploymentIdAnalyticsContext', () => { + let analytics: { registerContextProvider: jest.Mock }; + beforeEach(() => { + analytics = { + registerContextProvider: jest.fn(), + }; + }); + + test('it does not register the context provider if cloudId not provided', () => { + registerCloudDeploymentIdAnalyticsContext(analytics); + expect(analytics.registerContextProvider).not.toHaveBeenCalled(); + }); + + test('it registers the context provider and emits the cloudId', async () => { + registerCloudDeploymentIdAnalyticsContext(analytics, 'cloud_id'); + expect(analytics.registerContextProvider).toHaveBeenCalledTimes(1); + const [{ context$ }] = analytics.registerContextProvider.mock.calls[0]; + await expect(firstValueFrom(context$)).resolves.toEqual({ cloudId: 'cloud_id' }); + }); +}); diff --git a/x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.ts b/x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.ts new file mode 100644 index 0000000000000..e8bdc6b37b50c --- /dev/null +++ b/x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AnalyticsClient } from '@kbn/analytics-client'; +import { of } from 'rxjs'; + +export function registerCloudDeploymentIdAnalyticsContext( + analytics: Pick, + cloudId?: string +) { + if (!cloudId) { + return; + } + analytics.registerContextProvider({ + name: 'Cloud Deployment ID', + context$: of({ cloudId }), + schema: { + cloudId: { + type: 'keyword', + _meta: { description: 'The Cloud Deployment ID' }, + }, + }, + }); +} diff --git a/x-pack/plugins/cloud/public/plugin.test.ts b/x-pack/plugins/cloud/public/plugin.test.ts index 5e0294178a5da..36be9e590f216 100644 --- a/x-pack/plugins/cloud/public/plugin.test.ts +++ b/x-pack/plugins/cloud/public/plugin.test.ts @@ -9,9 +9,9 @@ import { nextTick } from '@kbn/test-jest-helpers'; import { coreMock } from '@kbn/core/public/mocks'; import { homePluginMock } from '@kbn/home-plugin/public/mocks'; import { securityMock } from '@kbn/security-plugin/public/mocks'; -import { CloudPlugin, CloudConfigType, loadUserId } from './plugin'; -import { firstValueFrom, Observable, Subject } from 'rxjs'; -import { KibanaExecutionContext } from '@kbn/core/public'; +import { CloudPlugin, CloudConfigType } from './plugin'; +import { firstValueFrom } from 'rxjs'; +import { Sha256 } from '@kbn/core/public/utils'; describe('Cloud Plugin', () => { describe('#setup', () => { @@ -20,17 +20,7 @@ describe('Cloud Plugin', () => { jest.clearAllMocks(); }); - const setupPlugin = async ({ - config = {}, - securityEnabled = true, - currentUserProps = {}, - currentContext$ = undefined, - }: { - config?: Partial; - securityEnabled?: boolean; - currentUserProps?: Record; - currentContext$?: Observable; - }) => { + const setupPlugin = async ({ config = {} }: { config?: Partial }) => { const initContext = coreMock.createPluginInitializerContext({ id: 'cloudId', base_url: 'https://cloud.elastic.co', @@ -49,21 +39,9 @@ describe('Cloud Plugin', () => { const plugin = new CloudPlugin(initContext); const coreSetup = coreMock.createSetup(); - const coreStart = coreMock.createStart(); - if (currentContext$) { - coreStart.executionContext.context$ = currentContext$; - } - - coreSetup.getStartServices.mockResolvedValue([coreStart, {}, undefined]); - - const securitySetup = securityMock.createSetup(); - - securitySetup.authc.getCurrentUser.mockResolvedValue( - securityMock.createMockAuthenticatedUser(currentUserProps) - ); + const setup = plugin.setup(coreSetup, {}); - const setup = plugin.setup(coreSetup, securityEnabled ? { security: securitySetup } : {}); // Wait for FullStory dynamic import to resolve await new Promise((r) => setImmediate(r)); @@ -73,9 +51,6 @@ describe('Cloud Plugin', () => { test('register the shipper FullStory with correct args when enabled and org_id are set', async () => { const { coreSetup } = await setupPlugin({ config: { full_story: { enabled: true, org_id: 'foo' } }, - currentUserProps: { - username: '1234', - }, }); expect(coreSetup.analytics.registerShipper).toHaveBeenCalled(); @@ -86,12 +61,71 @@ describe('Cloud Plugin', () => { }); }); - test('register the context provider for the cloud user with hashed user ID when security is available', async () => { + it('does not call initializeFullStory when enabled=false', async () => { const { coreSetup } = await setupPlugin({ - config: { full_story: { enabled: true, org_id: 'foo' } }, - currentUserProps: { - username: '1234', + config: { full_story: { enabled: false, org_id: 'foo' } }, + }); + expect(coreSetup.analytics.registerShipper).not.toHaveBeenCalled(); + }); + + it('does not call initializeFullStory when org_id is undefined', async () => { + const { coreSetup } = await setupPlugin({ config: { full_story: { enabled: true } } }); + expect(coreSetup.analytics.registerShipper).not.toHaveBeenCalled(); + }); + }); + + describe('setupTelemetryContext', () => { + const username = '1234'; + const expectedHashedPlainUsername = new Sha256().update(username, 'utf8').digest('hex'); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + const setupPlugin = async ({ + config = {}, + securityEnabled = true, + currentUserProps = {}, + }: { + config?: Partial; + securityEnabled?: boolean; + currentUserProps?: Record | Error; + }) => { + const initContext = coreMock.createPluginInitializerContext({ + base_url: 'https://cloud.elastic.co', + deployment_url: '/abc123', + profile_url: '/profile/alice', + organization_url: '/org/myOrg', + full_story: { + enabled: false, }, + chat: { + enabled: false, + }, + ...config, + }); + + const plugin = new CloudPlugin(initContext); + + const coreSetup = coreMock.createSetup(); + const securitySetup = securityMock.createSetup(); + if (currentUserProps instanceof Error) { + securitySetup.authc.getCurrentUser.mockRejectedValue(currentUserProps); + } else { + securitySetup.authc.getCurrentUser.mockResolvedValue( + securityMock.createMockAuthenticatedUser(currentUserProps) + ); + } + + const setup = plugin.setup(coreSetup, securityEnabled ? { security: securitySetup } : {}); + + return { initContext, plugin, setup, coreSetup }; + }; + + test('register the context provider for the cloud user with hashed user ID when security is available', async () => { + const { coreSetup } = await setupPlugin({ + config: { id: 'cloudId' }, + currentUserProps: { username }, }); expect(coreSetup.analytics.registerContextProvider).toHaveBeenCalled(); @@ -105,12 +139,10 @@ describe('Cloud Plugin', () => { }); }); - it('user hash includes org id', async () => { + it('user hash includes cloud id', async () => { const { coreSetup: coreSetup1 } = await setupPlugin({ - config: { full_story: { enabled: true, org_id: 'foo' }, id: 'esOrg1' }, - currentUserProps: { - username: '1234', - }, + config: { id: 'esOrg1' }, + currentUserProps: { username }, }); const [{ context$: context1$ }] = @@ -119,12 +151,11 @@ describe('Cloud Plugin', () => { )!; const hashId1 = await firstValueFrom(context1$); + expect(hashId1).not.toEqual(expectedHashedPlainUsername); const { coreSetup: coreSetup2 } = await setupPlugin({ config: { full_story: { enabled: true, org_id: 'foo' }, id: 'esOrg2' }, - currentUserProps: { - username: '1234', - }, + currentUserProps: { username }, }); const [{ context$: context2$ }] = @@ -133,150 +164,60 @@ describe('Cloud Plugin', () => { )!; const hashId2 = await firstValueFrom(context2$); + expect(hashId2).not.toEqual(expectedHashedPlainUsername); expect(hashId1).not.toEqual(hashId2); }); - it('emits the execution context provider everytime an app changes', async () => { - const currentContext$ = new Subject(); + test('user hash does not include cloudId when authenticated via Cloud SAML', async () => { const { coreSetup } = await setupPlugin({ - config: { full_story: { enabled: true, org_id: 'foo' } }, + config: { id: 'cloudDeploymentId' }, currentUserProps: { - username: '1234', + username, + authentication_realm: { type: 'saml', name: 'cloud-saml-kibana' }, }, - currentContext$, }); + expect(coreSetup.analytics.registerContextProvider).toHaveBeenCalled(); + const [{ context$ }] = coreSetup.analytics.registerContextProvider.mock.calls.find( - ([{ name }]) => name === 'execution_context' + ([{ name }]) => name === 'cloud_user_id' )!; - let latestContext; - context$.subscribe((context) => { - latestContext = context; - }); - - // takes the app name - expect(latestContext).toBeUndefined(); - currentContext$.next({ - name: 'App1', - description: '123', - }); - - await new Promise((r) => setImmediate(r)); - - expect(latestContext).toEqual({ - pageName: 'App1', - applicationId: 'App1', - }); - - // context clear - currentContext$.next({}); - expect(latestContext).toEqual({ - pageName: '', - applicationId: 'unknown', - }); - - // different app - currentContext$.next({ - name: 'App2', - page: 'page2', - id: '123', - }); - expect(latestContext).toEqual({ - pageName: 'App2:page2', - applicationId: 'App2', - page: 'page2', - entityId: '123', - }); - - // Back to first app - currentContext$.next({ - name: 'App1', - page: 'page3', - id: '123', - }); - - expect(latestContext).toEqual({ - pageName: 'App1:page3', - applicationId: 'App1', - page: 'page3', - entityId: '123', + await expect(firstValueFrom(context$)).resolves.toEqual({ + userId: expectedHashedPlainUsername, }); }); - it('does not register the cloud user id context provider when security is not available', async () => { + test('user hash does not include cloudId when not provided', async () => { const { coreSetup } = await setupPlugin({ - config: { full_story: { enabled: true, org_id: 'foo' } }, - securityEnabled: false, + config: {}, + currentUserProps: { username }, }); - expect( - coreSetup.analytics.registerContextProvider.mock.calls.find( - ([{ name }]) => name === 'cloud_user_id' - ) - ).toBeUndefined(); - }); - - describe('with memory', () => { - beforeAll(() => { - // @ts-expect-error 2339 - window.performance.memory = { - get jsHeapSizeLimit() { - return 3; - }, - get totalJSHeapSize() { - return 2; - }, - get usedJSHeapSize() { - return 1; - }, - }; - }); - - afterAll(() => { - // @ts-expect-error 2339 - delete window.performance.memory; - }); + expect(coreSetup.analytics.registerContextProvider).toHaveBeenCalled(); - it('reports an event when security is available', async () => { - const { initContext, coreSetup } = await setupPlugin({ - config: { full_story: { enabled: true, org_id: 'foo' } }, - currentUserProps: { - username: '1234', - }, - }); + const [{ context$ }] = coreSetup.analytics.registerContextProvider.mock.calls.find( + ([{ name }]) => name === 'cloud_user_id' + )!; - expect(coreSetup.analytics.reportEvent).toHaveBeenCalledWith('Loaded Kibana', { - kibana_version: initContext.env.packageInfo.version, - memory_js_heap_size_limit: 3, - memory_js_heap_size_total: 2, - memory_js_heap_size_used: 1, - }); + await expect(firstValueFrom(context$)).resolves.toEqual({ + userId: expectedHashedPlainUsername, }); }); - it('reports an event when security is not available', async () => { - const { initContext, coreSetup } = await setupPlugin({ - config: { full_story: { enabled: true, org_id: 'foo' } }, - securityEnabled: false, + test('user hash is undefined when failed to fetch a user', async () => { + const { coreSetup } = await setupPlugin({ + currentUserProps: new Error('failed to fetch a user'), }); - expect(coreSetup.analytics.reportEvent).toHaveBeenCalledWith('Loaded Kibana', { - kibana_version: initContext.env.packageInfo.version, - }); - }); + expect(coreSetup.analytics.registerContextProvider).toHaveBeenCalled(); - it('does not call initializeFullStory when enabled=false', async () => { - const { coreSetup } = await setupPlugin({ - config: { full_story: { enabled: false, org_id: 'foo' } }, - }); - expect(coreSetup.analytics.registerShipper).not.toHaveBeenCalled(); - }); + const [{ context$ }] = coreSetup.analytics.registerContextProvider.mock.calls.find( + ([{ name }]) => name === 'cloud_user_id' + )!; - it('does not call initializeFullStory when org_id is undefined', async () => { - const { coreSetup } = await setupPlugin({ config: { full_story: { enabled: true } } }); - expect(coreSetup.analytics.registerShipper).not.toHaveBeenCalled(); + await expect(firstValueFrom(context$)).resolves.toEqual({ userId: undefined }); }); }); @@ -652,56 +593,4 @@ describe('Cloud Plugin', () => { expect(securityStart.navControlService.addUserMenuLinks).not.toHaveBeenCalled(); }); }); - - describe('loadFullStoryUserId', () => { - let consoleMock: jest.SpyInstance; - - beforeEach(() => { - consoleMock = jest.spyOn(console, 'debug').mockImplementation(() => {}); - }); - afterEach(() => { - consoleMock.mockRestore(); - }); - - it('returns principal ID when username specified', async () => { - expect( - await loadUserId({ - getCurrentUser: jest.fn().mockResolvedValue({ - username: '1234', - }), - }) - ).toEqual('1234'); - expect(consoleMock).not.toHaveBeenCalled(); - }); - - it('returns undefined if getCurrentUser throws', async () => { - expect( - await loadUserId({ - getCurrentUser: jest.fn().mockRejectedValue(new Error(`Oh no!`)), - }) - ).toBeUndefined(); - }); - - it('returns undefined if getCurrentUser returns undefined', async () => { - expect( - await loadUserId({ - getCurrentUser: jest.fn().mockResolvedValue(undefined), - }) - ).toBeUndefined(); - }); - - it('returns undefined and logs if username undefined', async () => { - expect( - await loadUserId({ - getCurrentUser: jest.fn().mockResolvedValue({ - username: undefined, - metadata: { foo: 'bar' }, - }), - }) - ).toBeUndefined(); - expect(consoleMock).toHaveBeenLastCalledWith( - `[cloud.analytics] username not specified. User metadata: {"foo":"bar"}` - ); - }); - }); }); diff --git a/x-pack/plugins/cloud/public/plugin.tsx b/x-pack/plugins/cloud/public/plugin.tsx index bffc1d9496422..1bccf219225dc 100644 --- a/x-pack/plugins/cloud/public/plugin.tsx +++ b/x-pack/plugins/cloud/public/plugin.tsx @@ -13,21 +13,16 @@ import type { PluginInitializerContext, HttpStart, IBasePath, - ExecutionContextStart, AnalyticsServiceSetup, } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import useObservable from 'react-use/lib/useObservable'; -import { BehaviorSubject, from, of, Subscription } from 'rxjs'; -import { exhaustMap, filter, map } from 'rxjs/operators'; -import { compact } from 'lodash'; +import { BehaviorSubject, catchError, from, map, of } from 'rxjs'; -import type { - AuthenticatedUser, - SecurityPluginSetup, - SecurityPluginStart, -} from '@kbn/security-plugin/public'; +import type { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/public'; import { HomePublicPluginSetup } from '@kbn/home-plugin/public'; +import { Sha256 } from '@kbn/core/public/utils'; +import { registerCloudDeploymentIdAnalyticsContext } from '../common/register_cloud_deployment_id_analytics_context'; import { getIsCloudEnabled } from '../common/is_cloud_enabled'; import { ELASTIC_SUPPORT_LINK, @@ -49,6 +44,7 @@ export interface CloudConfigType { full_story: { enabled: boolean; org_id?: string; + eventTypesAllowlist?: string[]; }; /** Configuration to enable live chat in Cloud-enabled instances of Kibana. */ chat: { @@ -90,11 +86,6 @@ interface SetupFullStoryDeps { analytics: AnalyticsServiceSetup; basePath: IBasePath; } -interface SetupTelemetryContextDeps extends CloudSetupDependencies { - analytics: AnalyticsServiceSetup; - executionContextPromise: Promise; - cloudId?: string; -} interface SetupChatDeps extends Pick { http: CoreSetup['http']; @@ -103,7 +94,6 @@ interface SetupChatDeps extends Pick { export class CloudPlugin implements Plugin { private readonly config: CloudConfigType; private isCloudEnabled: boolean; - private appSubscription?: Subscription; private chatConfig$ = new BehaviorSubject({ enabled: false }); constructor(private readonly initializerContext: PluginInitializerContext) { @@ -112,19 +102,7 @@ export class CloudPlugin implements Plugin { } public setup(core: CoreSetup, { home, security }: CloudSetupDependencies) { - const executionContextPromise = core.getStartServices().then(([coreStart]) => { - return coreStart.executionContext; - }); - - this.setupTelemetryContext({ - analytics: core.analytics, - security, - executionContextPromise, - cloudId: this.config.id, - }).catch((e) => { - // eslint-disable-next-line no-console - console.debug(`Error setting up TelemetryContext: ${e.toString()}`); - }); + this.setupTelemetryContext(core.analytics, security, this.config.id); this.setupFullStory({ analytics: core.analytics, basePath: core.http.basePath }).catch((e) => // eslint-disable-next-line no-console @@ -212,9 +190,7 @@ export class CloudPlugin implements Plugin { }; } - public stop() { - this.appSubscription?.unsubscribe(); - } + public stop() {} /** * Determines if the current user should see links back to Cloud. @@ -249,7 +225,7 @@ export class CloudPlugin implements Plugin { * @private */ private async setupFullStory({ analytics, basePath }: SetupFullStoryDeps) { - const { enabled, org_id: fullStoryOrgId } = this.config.full_story; + const { enabled, org_id: fullStoryOrgId, eventTypesAllowlist } = this.config.full_story; if (!enabled || !fullStoryOrgId) { return; // do not load any FullStory code in the browser if not enabled } @@ -257,6 +233,7 @@ export class CloudPlugin implements Plugin { // Keep this import async so that we do not load any FullStory code into the browser when it is disabled. const { FullStoryShipper } = await import('@kbn/analytics-shippers-fullstory'); analytics.registerShipper(FullStoryShipper, { + eventTypesAllowlist, fullStoryOrgId, // Load an Elastic-internally audited script. Ideally, it should be hosted on a CDN. scriptUrl: basePath.prepend( @@ -270,48 +247,36 @@ export class CloudPlugin implements Plugin { * Set up the Analytics context providers. * @param analytics Core's Analytics service. The Setup contract. * @param security The security plugin. - * @param executionContextPromise Core's executionContext's start contract. - * @param esOrgId The Cloud Org ID. + * @param cloudId The Cloud Org ID. * @private */ - private async setupTelemetryContext({ - analytics, - security, - executionContextPromise, - cloudId, - }: SetupTelemetryContextDeps) { - // Some context providers can be moved to other places for better domain isolation. - // Let's use https://github.com/elastic/kibana/issues/125690 for that purpose. - analytics.registerContextProvider({ - name: 'kibana_version', - context$: of({ version: this.initializerContext.env.packageInfo.version }), - schema: { version: { type: 'keyword', _meta: { description: 'The version of Kibana' } } }, - }); + private setupTelemetryContext( + analytics: AnalyticsServiceSetup, + security?: Pick, + cloudId?: string + ) { + registerCloudDeploymentIdAnalyticsContext(analytics, cloudId); - analytics.registerContextProvider({ - name: 'cloud_org_id', - context$: of({ cloudId }), - schema: { - cloudId: { - type: 'keyword', - _meta: { description: 'The Cloud ID', optional: true }, - }, - }, - }); - - // This needs to be called synchronously to be sure that we populate the user ID soon enough to make sessions merging - // across domains work if (security) { analytics.registerContextProvider({ name: 'cloud_user_id', - context$: from(loadUserId({ getCurrentUser: security.authc.getCurrentUser })).pipe( - filter((userId): userId is string => Boolean(userId)), - exhaustMap(async (userId) => { - const { sha256 } = await import('js-sha256'); - // Join the cloud org id and the user to create a truly unique user id. - // The hashing here is to keep it at clear as possible in our source code that we do not send literal user IDs - return { userId: sha256(cloudId ? `${cloudId}:${userId}` : `${userId}`) }; - }) + context$: from(security.authc.getCurrentUser()).pipe( + map((user) => { + if ( + getIsCloudEnabled(cloudId) && + user.authentication_realm?.type === 'saml' && + user.authentication_realm?.name === 'cloud-saml-kibana' + ) { + // If authenticated via Cloud SAML, use the SAML username as the user ID + return user.username; + } + + return cloudId ? `${cloudId}:${user.username}` : user.username; + }), + // Join the cloud org id and the user to create a truly unique user id. + // The hashing here is to keep it at clear as possible in our source code that we do not send literal user IDs + map((userId) => ({ userId: sha256(userId) })), + catchError(() => of({ userId: undefined })) ), schema: { userId: { @@ -321,81 +286,6 @@ export class CloudPlugin implements Plugin { }, }); } - - const executionContext = await executionContextPromise; - analytics.registerContextProvider({ - name: 'execution_context', - context$: executionContext.context$.pipe( - // Update the current context every time it changes - map(({ name, page, id }) => ({ - pageName: `${compact([name, page]).join(':')}`, - applicationId: name ?? 'unknown', - page, - entityId: id, - })) - ), - schema: { - pageName: { - type: 'keyword', - _meta: { description: 'The name of the current page' }, - }, - page: { - type: 'keyword', - _meta: { description: 'The current page', optional: true }, - }, - applicationId: { - type: 'keyword', - _meta: { description: 'The id of the current application' }, - }, - entityId: { - type: 'keyword', - _meta: { - description: - 'The id of the current entity (dashboard, visualization, canvas, lens, etc)', - optional: true, - }, - }, - }, - }); - - analytics.registerEventType({ - eventType: 'Loaded Kibana', - schema: { - kibana_version: { - type: 'keyword', - _meta: { description: 'The version of Kibana', optional: true }, - }, - memory_js_heap_size_limit: { - type: 'long', - _meta: { description: 'The maximum size of the heap', optional: true }, - }, - memory_js_heap_size_total: { - type: 'long', - _meta: { description: 'The total size of the heap', optional: true }, - }, - memory_js_heap_size_used: { - type: 'long', - _meta: { description: 'The used size of the heap', optional: true }, - }, - }, - }); - - // Get performance information from the browser (non standard property - // @ts-expect-error 2339 - const memory = window.performance.memory; - let memoryInfo = {}; - if (memory) { - memoryInfo = { - memory_js_heap_size_limit: memory.jsHeapSizeLimit, - memory_js_heap_size_total: memory.totalJSHeapSize, - memory_js_heap_size_used: memory.usedJSHeapSize, - }; - } - - analytics.reportEvent('Loaded Kibana', { - kibana_version: this.initializerContext.env.packageInfo.version, - ...memoryInfo, - }); } private async setupChat({ http, security }: SetupChatDeps) { @@ -436,32 +326,6 @@ export class CloudPlugin implements Plugin { } } -/** @internal exported for testing */ -export const loadUserId = async ({ - getCurrentUser, -}: { - getCurrentUser: () => Promise; -}) => { - try { - const currentUser = await getCurrentUser().catch(() => undefined); - if (!currentUser) { - return undefined; - } - - // Log very defensively here so we can debug this easily if it breaks - if (!currentUser.username) { - // eslint-disable-next-line no-console - console.debug( - `[cloud.analytics] username not specified. User metadata: ${JSON.stringify( - currentUser.metadata - )}` - ); - } - - return currentUser.username; - } catch (e) { - // eslint-disable-next-line no-console - console.error(`[cloud.analytics] Error loading the current user: ${e.toString()}`, e); - return undefined; - } -}; +function sha256(str: string) { + return new Sha256().update(str, 'utf8').digest('hex'); +} diff --git a/x-pack/plugins/cloud/server/config.ts b/x-pack/plugins/cloud/server/config.ts index bee83e23475b5..aebbc65e50f18 100644 --- a/x-pack/plugins/cloud/server/config.ts +++ b/x-pack/plugins/cloud/server/config.ts @@ -26,6 +26,9 @@ const fullStoryConfigSchema = schema.object({ schema.string({ minLength: 1 }), schema.maybe(schema.string()) ), + eventTypesAllowlist: schema.arrayOf(schema.string(), { + defaultValue: ['Loaded Kibana'], + }), }); const chatConfigSchema = schema.object({ diff --git a/x-pack/plugins/cloud/server/env.ts b/x-pack/plugins/cloud/server/env.ts new file mode 100644 index 0000000000000..435e62bf47698 --- /dev/null +++ b/x-pack/plugins/cloud/server/env.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. + */ + +// Best effort to get instance size from process.env +export function readInstanceSizeMb(): undefined | number { + const capacityString = process.env.CLOUD_KIBANA_CAPACITY; + if (capacityString) { + const instanceSizeMb = parseInt(capacityString, 10); + return isNaN(instanceSizeMb) ? undefined : instanceSizeMb; + } +} diff --git a/x-pack/plugins/cloud/server/plugin.ts b/x-pack/plugins/cloud/server/plugin.ts index fbf3c925cff87..2cbb41531ecf5 100644 --- a/x-pack/plugins/cloud/server/plugin.ts +++ b/x-pack/plugins/cloud/server/plugin.ts @@ -8,12 +8,14 @@ import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; import { CoreSetup, Logger, Plugin, PluginInitializerContext } from '@kbn/core/server'; import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; +import { registerCloudDeploymentIdAnalyticsContext } from '../common/register_cloud_deployment_id_analytics_context'; import { CloudConfigType } from './config'; import { registerCloudUsageCollector } from './collectors'; import { getIsCloudEnabled } from '../common/is_cloud_enabled'; import { parseDeploymentIdFromDeploymentUrl } from './utils'; import { registerFullstoryRoute } from './routes/fullstory'; import { registerChatRoute } from './routes/chat'; +import { readInstanceSizeMb } from './env'; interface PluginsSetup { usageCollection?: UsageCollectionSetup; @@ -24,6 +26,7 @@ export interface CloudSetup { cloudId?: string; deploymentId?: string; isCloudEnabled: boolean; + instanceSizeMb?: number; apm: { url?: string; secretToken?: string; @@ -33,7 +36,7 @@ export interface CloudSetup { export class CloudPlugin implements Plugin { private readonly logger: Logger; private readonly config: CloudConfigType; - private isDev: boolean; + private readonly isDev: boolean; constructor(private readonly context: PluginInitializerContext) { this.logger = this.context.logger.get(); @@ -41,9 +44,10 @@ export class CloudPlugin implements Plugin { this.isDev = this.context.env.mode.dev; } - public setup(core: CoreSetup, { usageCollection, security }: PluginsSetup) { + public setup(core: CoreSetup, { usageCollection, security }: PluginsSetup): CloudSetup { this.logger.debug('Setting up Cloud plugin'); const isCloudEnabled = getIsCloudEnabled(this.config.id); + registerCloudDeploymentIdAnalyticsContext(core.analytics, this.config.id); registerCloudUsageCollector(usageCollection, { isCloudEnabled }); if (this.config.full_story.enabled) { @@ -64,6 +68,7 @@ export class CloudPlugin implements Plugin { return { cloudId: this.config.id, + instanceSizeMb: readInstanceSizeMb(), deploymentId: parseDeploymentIdFromDeploymentUrl(this.config.deployment_url), isCloudEnabled, apm: { diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts index 363adc568ba9b..30e9651b6e739 100644 --- a/x-pack/plugins/cloud_security_posture/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/common/constants.ts @@ -11,19 +11,16 @@ export const BENCHMARKS_ROUTE_PATH = '/internal/cloud_security_posture/benchmark export const UPDATE_RULES_CONFIG_ROUTE_PATH = '/internal/cloud_security_posture/update_rules_config'; -export const CSP_FINDINGS_INDEX_NAME = 'findings'; -export const CIS_KUBERNETES_PACKAGE_NAME = 'cis_kubernetes_benchmark'; -export const FINDINGS_DATA_STREAM_NAME = - // Currently 'cis_kubernetes_benchmark.findings', To be refactored to 'cloud_security_posture.findings' - CIS_KUBERNETES_PACKAGE_NAME + '.' + CSP_FINDINGS_INDEX_NAME; +export const CLOUD_SECURITY_POSTURE_PACKAGE_NAME = 'cloud_security_posture'; + +export const CSP_LATEST_FINDINGS_DATA_VIEW = 'logs-cloud_security_posture.findings_latest-*'; +export const FINDINGS_INDEX_PATTERN = 'logs-cloud_security_posture.findings-default*'; + export const LATEST_FINDINGS_INDEX_NAME = 'cloud_security_posture.findings_latest'; -export const BENCHMARK_SCORE_INDEX_NAME = 'cloud_security_posture.scores'; +export const LATEST_FINDINGS_INDEX_DEFAULT_NS = 'logs-' + LATEST_FINDINGS_INDEX_NAME + '-default'; -export const AGENT_LOGS_INDEX_PATTERN = '.logs-cis_kubernetes_benchmark.metadata*'; -export const CSP_KUBEBEAT_INDEX_PATTERN = 'logs-cis_kubernetes_benchmark.findings-*'; -export const FINDINGS_INDEX_PATTERN = 'logs-' + FINDINGS_DATA_STREAM_NAME + '-default*'; -export const LATEST_FINDINGS_INDEX_PATTERN = 'logs-' + LATEST_FINDINGS_INDEX_NAME + '-default'; -export const BENCHMARK_SCORE_INDEX_PATTERN = 'logs-' + BENCHMARK_SCORE_INDEX_NAME + '-default'; +export const BENCHMARK_SCORE_INDEX_NAME = 'cloud_security_posture.scores'; +export const BENCHMARK_SCORE_INDEX_DEFAULT_NS = 'logs-' + BENCHMARK_SCORE_INDEX_NAME + '-default'; export const RULE_PASSED = `passed`; export const RULE_FAILED = `failed`; @@ -31,8 +28,10 @@ export const RULE_FAILED = `failed`; // A mapping of in-development features to their status. These features should be hidden from users but can be easily // activated via a simple code change in a single location. export const INTERNAL_FEATURE_FLAGS = { - showBenchmarks: false, + showBenchmarks: true, showManageRulesMock: false, showRisksMock: false, - showFindingsGroupBy: false, + showFindingsGroupBy: true, } as const; + +export const cspRuleAssetSavedObjectType = 'csp_rule'; diff --git a/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule.ts b/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule.ts index a2c7f06e0a676..cdefc461cd952 100644 --- a/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule.ts +++ b/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule.ts @@ -6,9 +6,7 @@ */ import { schema as rt, TypeOf } from '@kbn/config-schema'; -export const cspRuleAssetSavedObjectType = 'csp_rule'; - -// TODO: needs to be shared with kubebeat +// TODO: needs to be shared with cloudbeat export const cspRuleSchema = rt.object({ id: rt.string(), name: rt.string(), diff --git a/x-pack/plugins/cloud_security_posture/common/types.ts b/x-pack/plugins/cloud_security_posture/common/types.ts index 0aee5ce2f350d..110e8c071deff 100644 --- a/x-pack/plugins/cloud_security_posture/common/types.ts +++ b/x-pack/plugins/cloud_security_posture/common/types.ts @@ -21,7 +21,7 @@ export interface Stats extends FindingsEvaluation { postureScore: Score; } -export interface ResourceType extends FindingsEvaluation { +export interface GroupedFindingsEvaluation extends FindingsEvaluation { name: string; } @@ -36,17 +36,23 @@ export interface Cluster { lastUpdate: number; // unix epoch time }; stats: Stats; - resourcesTypes: ResourceType[]; + groupedFindingsEvaluation: GroupedFindingsEvaluation[]; trend: PostureTrend[]; } export interface ComplianceDashboardData { stats: Stats; - resourcesTypes: ResourceType[]; + groupedFindingsEvaluation: GroupedFindingsEvaluation[]; clusters: Cluster[]; trend: PostureTrend[]; } +export interface CspRulesStatus { + all: number; + enabled: number; + disabled: number; +} + export interface Benchmark { package_policy: Pick< PackagePolicy, @@ -61,4 +67,5 @@ export interface Benchmark { | 'created_by' >; agent_policy: Pick; + rules: CspRulesStatus; } diff --git a/x-pack/plugins/cloud_security_posture/public/common/api/use_cis_kubernetes_integration.tsx b/x-pack/plugins/cloud_security_posture/public/common/api/use_cis_kubernetes_integration.tsx index 0a9f2c2134a6e..5660080cb0a43 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/api/use_cis_kubernetes_integration.tsx +++ b/x-pack/plugins/cloud_security_posture/public/common/api/use_cis_kubernetes_integration.tsx @@ -11,7 +11,7 @@ import { type GetInfoResponse, type DefaultPackagesInstallationError, } from '@kbn/fleet-plugin/common'; -import { CIS_KUBERNETES_PACKAGE_NAME } from '../../../common/constants'; +import { CLOUD_SECURITY_POSTURE_PACKAGE_NAME } from '../../../common/constants'; import { useKibana } from '../hooks/use_kibana'; /** @@ -21,7 +21,7 @@ export const useCisKubernetesIntegration = () => { const { http } = useKibana().services; return useQuery(['integrations'], () => - http.get(epmRouteService.getInfoPath(CIS_KUBERNETES_PACKAGE_NAME), { + http.get(epmRouteService.getInfoPath(CLOUD_SECURITY_POSTURE_PACKAGE_NAME), { query: { experimental: true }, }) ); diff --git a/x-pack/plugins/cloud_security_posture/public/common/api/use_kubebeat_data_view.ts b/x-pack/plugins/cloud_security_posture/public/common/api/use_kubebeat_data_view.ts deleted file mode 100644 index 20d22b93cb9d2..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/common/api/use_kubebeat_data_view.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useQuery } from 'react-query'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { CSP_KUBEBEAT_INDEX_PATTERN } from '../../../common/constants'; -import { CspClientPluginStartDeps } from '../../types'; - -/** - * TODO: use perfected kibana data views - */ -export const useKubebeatDataView = () => { - const { - data: { dataViews }, - } = useKibana().services; - - // TODO: check if index exists - // if not, no point in creating a data view - // const check = () => http?.get(`/kubebeat`); - - // TODO: use `dataViews.get(ID)` - const findDataView = async () => (await dataViews.find(CSP_KUBEBEAT_INDEX_PATTERN))?.[0]; - - return useQuery(['kubebeat_dataview'], findDataView); -}; diff --git a/x-pack/plugins/cloud_security_posture/public/common/api/use_latest_findings_data_view.ts b/x-pack/plugins/cloud_security_posture/public/common/api/use_latest_findings_data_view.ts new file mode 100644 index 0000000000000..21708c3be1f5e --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/common/api/use_latest_findings_data_view.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 { useQuery } from 'react-query'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { CSP_LATEST_FINDINGS_DATA_VIEW } from '../../../common/constants'; +import { CspClientPluginStartDeps } from '../../types'; + +/** + * TODO: use perfected kibana data views + */ +export const useLatestFindingsDataView = () => { + const { + data: { dataViews }, + } = useKibana().services; + + // TODO: use `dataViews.get(ID)` + const findDataView = async () => (await dataViews.find(CSP_LATEST_FINDINGS_DATA_VIEW))?.[0]; + + return useQuery(['latest_findings_dataview'], findDataView); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_findings.ts b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_findings.ts index b5ce12095f99a..e4afcd3306576 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_findings.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_findings.ts @@ -7,7 +7,7 @@ import { useHistory } from 'react-router-dom'; import { Query } from '@kbn/es-query'; -import { allNavigationItems } from '../navigation/constants'; +import { findingsNavigation } from '../navigation/constants'; import { encodeQuery } from '../navigation/query_utils'; import { FindingsBaseURLQuery } from '../../pages/findings/types'; @@ -18,7 +18,7 @@ const getFindingsQuery = (queryValue: Query['query']): Pick((a, [key, value]) => { - a.push(`${key} : "${value}"`); + a.push(`${key}: "${value}"`); return a; }, []) .join(' and '); @@ -37,7 +37,7 @@ export const useNavigateFindings = () => { return (query?: Query['query']) => { history.push({ - pathname: allNavigationItems.findings.path, + pathname: findingsNavigation.findings_default.path, ...(query && { search: encodeQuery(getFindingsQuery(query)) }), }); }; diff --git a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_url_query.ts b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_url_query.ts index 69055de1a78af..2eea943f757cf 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_url_query.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_url_query.ts @@ -6,6 +6,7 @@ */ import { useEffect, useCallback, useMemo } from 'react'; import { useHistory, useLocation } from 'react-router-dom'; +import type { RisonObject } from 'rison-node'; import { decodeQuery, encodeQuery } from '../navigation/query_utils'; /** @@ -14,7 +15,7 @@ import { decodeQuery, encodeQuery } from '../navigation/query_utils'; * @note shallow-merges default, current and next query */ export const useUrlQuery = (getDefaultQuery: () => T) => { - const { push } = useHistory(); + const { push, replace } = useHistory(); const { search, key } = useLocation(); const urlQuery = useMemo( @@ -35,8 +36,8 @@ export const useUrlQuery = (getDefaultQuery: () => T) => { // TODO: condition should be if decoding failed if (search) return; - setUrlQuery(getDefaultQuery()); - }, [getDefaultQuery, search, setUrlQuery]); + replace({ search: encodeQuery(getDefaultQuery() as RisonObject) }); + }, [getDefaultQuery, search, replace]); return { key, diff --git a/x-pack/plugins/cloud_security_posture/public/common/navigation/constants.ts b/x-pack/plugins/cloud_security_posture/public/common/navigation/constants.ts index 1132b7a348b5d..b9089b4b5a58d 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/navigation/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/navigation/constants.ts @@ -24,3 +24,9 @@ export const allNavigationItems: Record = { disabled: !INTERNAL_FEATURE_FLAGS.showBenchmarks, }, }; + +export const findingsNavigation = { + findings_default: { name: TEXT.FINDINGS, path: '/findings/default' }, + findings_by_resource: { name: TEXT.FINDINGS, path: '/findings/resource' }, + resource_findings: { name: TEXT.FINDINGS, path: '/findings/resource/:resourceId' }, +}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/csp_evaluation_badge.tsx b/x-pack/plugins/cloud_security_posture/public/components/csp_evaluation_badge.tsx index af1cf6c5c43c1..ab193ab6dc8be 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/csp_evaluation_badge.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/csp_evaluation_badge.tsx @@ -22,9 +22,9 @@ const getColor = (type: Props['type']): EuiBadgeProps['color'] => { export const CspEvaluationBadge = ({ type }: Props) => ( {type === 'failed' ? ( - + ) : ( - + )} ); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx index 990140f3cf949..48e9d3f67e8e5 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx @@ -8,9 +8,6 @@ import React from 'react'; import { render, screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import type { UseQueryResult } from 'react-query/types/react/types'; -import { createStubDataView } from '@kbn/data-views-plugin/public/data_views/data_view.stub'; -import { CSP_KUBEBEAT_INDEX_PATTERN } from '../../../common/constants'; -import { useKubebeatDataView } from '../../common/api/use_kubebeat_data_view'; import { createCspBenchmarkIntegrationFixture } from '../../test/fixtures/csp_benchmark_integration'; import { createReactQueryResponse } from '../../test/fixtures/react_query'; import { TestProvider } from '../../test/test_provider'; @@ -24,7 +21,6 @@ import { useCspBenchmarkIntegrations } from './use_csp_benchmark_integrations'; import { useCisKubernetesIntegration } from '../../common/api/use_cis_kubernetes_integration'; jest.mock('./use_csp_benchmark_integrations'); -jest.mock('../../common/api/use_kubebeat_data_view'); jest.mock('../../common/api/use_cis_kubernetes_integration'); describe('', () => { @@ -35,17 +31,6 @@ describe('', () => { (useCisKubernetesIntegration as jest.Mock).mockImplementation(() => ({ data: { item: { status: 'installed' } }, })); - // Required for the page template to render the benchmarks page - (useKubebeatDataView as jest.Mock).mockImplementation(() => - createReactQueryResponse({ - status: 'success', - data: createStubDataView({ - spec: { - id: CSP_KUBEBEAT_INDEX_PATTERN, - }, - }), - }) - ); }); const renderBenchmarks = ( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/use_csp_benchmark_integrations.ts b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/use_csp_benchmark_integrations.ts index 53e65d05ce08d..3bef982deb3a9 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/use_csp_benchmark_integrations.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/use_csp_benchmark_integrations.ts @@ -8,7 +8,7 @@ import { useQuery } from 'react-query'; import type { ListResult } from '@kbn/fleet-plugin/common'; import { BENCHMARKS_ROUTE_PATH } from '../../../common/constants'; -import { BenchmarksQuerySchema } from '../../../common/schemas/benchmark'; +import type { BenchmarksQuerySchema } from '../../../common/schemas/benchmark'; import { useKibana } from '../../common/hooks/use_kibana'; import type { Benchmark } from '../../../common/types'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/risks_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/risks_table.tsx index 086fea34a2c50..b6b309cadbb02 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/risks_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/risks_table.tsx @@ -14,7 +14,7 @@ import { EuiLink, EuiText, } from '@elastic/eui'; -import { ComplianceDashboardData, ResourceType } from '../../../../common/types'; +import { ComplianceDashboardData, GroupedFindingsEvaluation } from '../../../../common/types'; import { CompactFormattedNumber } from '../../../components/compact_formatted_number'; import * as TEXT from '../translations'; import { INTERNAL_FEATURE_FLAGS } from '../../../../common/constants'; @@ -59,17 +59,17 @@ const mockData = [ ]; export interface RisksTableProps { - data: ComplianceDashboardData['resourcesTypes']; + data: ComplianceDashboardData['groupedFindingsEvaluation']; maxItems: number; - onCellClick: (resourceTypeName: string) => void; + onCellClick: (name: string) => void; onViewAllClick: () => void; } export const getTopRisks = ( - resourcesTypes: ComplianceDashboardData['resourcesTypes'], + groupedFindingsEvaluation: ComplianceDashboardData['groupedFindingsEvaluation'], maxItems: number ) => { - const filtered = resourcesTypes.filter((x) => x.totalFailed > 0); + const filtered = groupedFindingsEvaluation.filter((x) => x.totalFailed > 0); const sorted = filtered.slice().sort((first, second) => second.totalFailed - first.totalFailed); return sorted.slice(0, maxItems); @@ -85,15 +85,18 @@ export const RisksTable = ({ () => [ { field: 'name', - name: TEXT.RESOURCE_TYPE, - render: (resourceTypeName: ResourceType['name']) => ( - onCellClick(resourceTypeName)}>{resourceTypeName} + name: TEXT.CIS_SECTION, + render: (name: GroupedFindingsEvaluation['name']) => ( + onCellClick(name)}>{name} ), }, { field: 'totalFailed', name: TEXT.FINDINGS, - render: (totalFailed: ResourceType['totalFailed'], resource: ResourceType) => ( + render: ( + totalFailed: GroupedFindingsEvaluation['totalFailed'], + resource: GroupedFindingsEvaluation + ) => ( <> @@ -114,7 +117,7 @@ export const RisksTable = ({ return ( - + rowHeader="name" items={INTERNAL_FEATURE_FLAGS.showRisksMock ? getTopRisks(mockData, maxItems) : items} columns={columns} diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx index a3b32ef3fe2c6..daa154f62aad7 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx @@ -42,19 +42,19 @@ export const BenchmarksSection = ({ const [layerValue] = element; const evaluation = layerValue[0].groupByRollup as Evaluation; - navToFindings({ cluster_id: clusterId, 'result.evaluation': evaluation }); + navToFindings({ 'cluster_id.keyword': clusterId, 'result.evaluation.keyword': evaluation }); }; - const handleCellClick = (clusterId: string, resourceTypeName: string) => { + const handleCellClick = (clusterId: string, ruleSection: string) => { navToFindings({ - cluster_id: clusterId, - 'resource.type': resourceTypeName, - 'result.evaluation': RULE_FAILED, + 'cluster_id.keyword': clusterId, + 'rule.section.keyword': ruleSection, + 'result.evaluation.keyword': RULE_FAILED, }); }; const handleViewAllClick = (clusterId: string) => { - navToFindings({ cluster_id: clusterId, 'result.evaluation': RULE_FAILED }); + navToFindings({ 'cluster_id.keyword': clusterId, 'result.evaluation.keyword': RULE_FAILED }); }; return ( @@ -110,7 +110,7 @@ export const BenchmarksSection = ({ handleCellClick(cluster.meta.clusterId, resourceTypeName) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx index c123459d9e963..b7782e5778bd3 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx @@ -32,15 +32,18 @@ export const SummarySection = ({ complianceData }: { complianceData: ComplianceD const [layerValue] = element; const evaluation = layerValue[0].groupByRollup as Evaluation; - navToFindings({ 'result.evaluation': evaluation }); + navToFindings({ 'result.evaluation.keyword': evaluation }); }; - const handleCellClick = (resourceTypeName: string) => { - navToFindings({ 'resource.type': resourceTypeName, 'result.evaluation': RULE_FAILED }); + const handleCellClick = (ruleSection: string) => { + navToFindings({ + 'rule.section.keyword': ruleSection, + 'result.evaluation.keyword': RULE_FAILED, + }); }; const handleViewAllClick = () => { - navToFindings({ 'result.evaluation': RULE_FAILED }); + navToFindings({ 'result.evaluation.keyword': RULE_FAILED }); }; return ( @@ -58,7 +61,7 @@ export const SummarySection = ({ complianceData }: { complianceData: ComplianceD { @@ -40,8 +40,8 @@ const Wrapper = ({ ); -describe('', () => { - it("renders the success state component when 'kubebeat' DataView exists and request status is 'success'", async () => { +describe.skip('', () => { + it("renders the success state component when 'latest findings' DataView exists and request status is 'success'", async () => { const data = dataPluginMock.createStartContract(); const unifiedSearch = unifiedSearchPluginMock.createStartContract(); const source = await data.search.searchSource.create(); @@ -51,11 +51,11 @@ describe('', () => { })); (source.fetch$ as jest.Mock).mockReturnValue(of({ rawResponse: { hits: { hits: [] } } })); - (useKubebeatDataView as jest.Mock).mockReturnValue({ + (useLatestFindingsDataView as jest.Mock).mockReturnValue({ status: 'success', data: createStubDataView({ spec: { - id: CSP_KUBEBEAT_INDEX_PATTERN, + id: CSP_LATEST_FINDINGS_DATA_VIEW, }, }), } as UseQueryResult); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx index 4eae5610da15a..4fa5c33903477 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx @@ -5,19 +5,47 @@ * 2.0. */ import React from 'react'; -import { useKubebeatDataView } from '../../common/api/use_kubebeat_data_view'; -import { allNavigationItems } from '../../common/navigation/constants'; -import { useCspBreadcrumbs } from '../../common/navigation/use_csp_breadcrumbs'; -import { FindingsContainer } from './findings_container'; +import { Redirect, Switch, Route, useLocation } from 'react-router-dom'; +import { useLatestFindingsDataView } from '../../common/api/use_latest_findings_data_view'; +import { allNavigationItems, findingsNavigation } from '../../common/navigation/constants'; import { CspPageTemplate } from '../../components/csp_page_template'; +import { FindingsByResourceContainer } from './latest_findings_by_resource/findings_by_resource_container'; +import { LatestFindingsContainer } from './latest_findings/latest_findings_container'; export const Findings = () => { - const dataViewQuery = useKubebeatDataView(); - useCspBreadcrumbs([allNavigationItems.findings]); + const location = useLocation(); + const dataViewQuery = useLatestFindingsDataView(); + + if (!dataViewQuery.data) return ; return ( - {dataViewQuery.data && } + + ( + + )} + /> + } + /> + } + /> + } + /> + ); }; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_container.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_container.test.tsx deleted file mode 100644 index ea2cd9ce7c0cf..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_container.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 { render } from '@testing-library/react'; -import { FindingsContainer, getDefaultQuery } from './findings_container'; -import { createStubDataView } from '@kbn/data-views-plugin/common/mocks'; -import { CSP_KUBEBEAT_INDEX_PATTERN } from '../../../common/constants'; -import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; -import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; -import { TestProvider } from '../../test/test_provider'; -import { getFindingsQuery } from './use_findings'; -import { encodeQuery } from '../../common/navigation/query_utils'; -import { useLocation } from 'react-router-dom'; -import { RisonObject } from 'rison-node'; -import { buildEsQuery } from '@kbn/es-query'; -import { getFindingsCountAggQuery } from './use_findings_count'; - -jest.mock('../../common/api/use_kubebeat_data_view'); -jest.mock('../../common/api/use_cis_kubernetes_integration'); - -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useHistory: () => ({ push: jest.fn() }), - useLocation: jest.fn(), -})); - -beforeEach(() => { - jest.restoreAllMocks(); -}); - -describe('', () => { - it('data#search.search fn called with URL query', () => { - const query = getDefaultQuery(); - const dataMock = dataPluginMock.createStartContract(); - const dataView = createStubDataView({ - spec: { - id: CSP_KUBEBEAT_INDEX_PATTERN, - }, - }); - - (useLocation as jest.Mock).mockReturnValue({ - search: encodeQuery(query as unknown as RisonObject), - }); - - render( - - - - ); - - const baseQuery = { - index: dataView.title, - query: buildEsQuery(dataView, query.query, query.filters), - }; - - expect(dataMock.search.search).toHaveBeenNthCalledWith(1, { - params: getFindingsCountAggQuery(baseQuery), - }); - - expect(dataMock.search.search).toHaveBeenNthCalledWith(2, { - params: getFindingsQuery({ - ...baseQuery, - sort: query.sort, - size: query.size, - from: query.from, - }), - }); - }); -}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_container.tsx deleted file mode 100644 index 213592d50e069..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_container.tsx +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { EuiComboBoxOptionOption, EuiSpacer, EuiTitle, useEuiTheme } from '@elastic/eui'; -import { css } from '@emotion/react'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { i18n } from '@kbn/i18n'; -import type { DataView } from '@kbn/data-plugin/common'; -import { SortDirection } from '@kbn/data-plugin/common'; -import { buildEsQuery } from '@kbn/es-query'; -import { FindingsTable } from './findings_table'; -import { FindingsSearchBar } from './findings_search_bar'; -import * as TEST_SUBJECTS from './test_subjects'; -import { useUrlQuery } from '../../common/hooks/use_url_query'; -import { useFindings } from './use_findings'; -import type { FindingsGroupByNoneQuery } from './use_findings'; -import type { FindingsBaseURLQuery } from './types'; -import { useFindingsByResource } from './use_findings_by_resource'; -import { FindingsGroupBySelector } from './findings_group_by_selector'; -import { INTERNAL_FEATURE_FLAGS } from '../../../common/constants'; -import { useFindingsCounter } from './use_findings_count'; -import { FindingsDistributionBar } from './findings_distribution_bar'; -import { FindingsByResourceTable } from './findings_by_resource_table'; - -// TODO: define this as a schema with default values -export const getDefaultQuery = (): FindingsBaseURLQuery & FindingsGroupByNoneQuery => ({ - query: { language: 'kuery', query: '' }, - filters: [], - sort: [{ ['@timestamp']: SortDirection.desc }], - from: 0, - size: 10, - groupBy: 'none', -}); - -const getGroupByOptions = (): Array> => [ - { - value: 'none', - label: i18n.translate('xpack.csp.findings.groupBySelector.groupByNoneLabel', { - defaultMessage: 'None', - }), - }, - { - value: 'resource', - label: i18n.translate('xpack.csp.findings.groupBySelector.groupByResourceIdLabel', { - defaultMessage: 'Resource', - }), - }, -]; - -export const FindingsContainer = ({ dataView }: { dataView: DataView }) => { - const { euiTheme } = useEuiTheme(); - const groupByOptions = useMemo(getGroupByOptions, []); - const { urlQuery, setUrlQuery } = useUrlQuery(getDefaultQuery); - - const baseEsQuery = useMemo( - () => ({ - index: dataView.title, - // TODO: this will throw for malformed query - // page will display an error boundary with the JS error - // will be accounted for before releasing the feature - query: buildEsQuery(dataView, urlQuery.query, urlQuery.filters), - }), - [dataView, urlQuery] - ); - - const findingsGroupByResource = useFindingsByResource({ - ...baseEsQuery, - enabled: urlQuery.groupBy === 'resource', - }); - - const findingsCount = useFindingsCounter({ - ...baseEsQuery, - enabled: urlQuery.groupBy === 'none', - }); - - const findingsGroupByNone = useFindings({ - ...baseEsQuery, - enabled: urlQuery.groupBy === 'none', - size: urlQuery.size, - from: urlQuery.from, - sort: urlQuery.sort, - }); - - return ( -
- -
- - - {INTERNAL_FEATURE_FLAGS.showFindingsGroupBy && ( - setUrlQuery({ groupBy: type[0]?.value })} - options={groupByOptions} - /> - )} - - {urlQuery.groupBy === 'none' && ( - <> - - - - - )} - {urlQuery.groupBy === 'resource' && ( - <> - - - )} -
-
- ); -}; - -const PageTitle = () => ( - -

- -

-
-); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout.tsx deleted file mode 100644 index 65493bd493342..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout.tsx +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { - EuiCodeBlock, - EuiFlexItem, - EuiSpacer, - EuiDescriptionList, - EuiTextColor, - EuiFlyout, - EuiFlyoutHeader, - EuiTitle, - EuiFlyoutBody, - EuiBadge, - EuiTabs, - EuiTab, - EuiFlexGrid, - EuiCard, - EuiFlexGroup, - EuiIcon, - type PropsOf, -} from '@elastic/eui'; -import { assertNever } from '@kbn/std'; -import type { CspFinding } from './types'; -import { CspEvaluationBadge } from '../../components/csp_evaluation_badge'; -import * as TEXT from './translations'; -import cisLogoIcon from '../../assets/icons/cis_logo.svg'; -import k8sLogoIcon from '../../assets/icons/k8s_logo.svg'; - -const tabs = ['remediation', 'resource', 'general'] as const; - -const CodeBlock: React.FC> = (props) => ( - -); - -type FindingsTab = typeof tabs[number]; - -type EuiListItemsProps = NonNullable['listItems']>[number]; - -interface Card { - title: string; - listItems: Array<[EuiListItemsProps['title'], EuiListItemsProps['description']]>; -} - -interface FindingFlyoutProps { - onClose(): void; - findings: CspFinding; -} - -export const FindingsRuleFlyout = ({ onClose, findings }: FindingFlyoutProps) => { - const [tab, setTab] = useState('remediation'); - return ( - - - - - - - - - - {findings.rule.name} - - - - - - - {tabs.map((v) => ( - setTab(v)} - style={{ textTransform: 'capitalize' }} - > - {v} - - ))} - - - - - - - ); -}; - -const Cards = ({ data }: { data: Card[] }) => ( - - {data.map((card) => ( - - - ({ title: v[0], description: v[1] }))} - style={{ flexFlow: 'column' }} - descriptionProps={{ - style: { width: '100%' }, - }} - /> - - - ))} - -); - -const FindingsTab = ({ tab, findings }: { findings: CspFinding; tab: FindingsTab }) => { - switch (tab) { - case 'remediation': - return ; - case 'resource': - return ; - case 'general': - return ; - default: - assertNever(tab); - } -}; - -const getResourceCards = ({ resource, host }: CspFinding): Card[] => [ - { - title: TEXT.RESOURCE, - listItems: [ - [TEXT.FILENAME, {resource.filename}], - [TEXT.MODE, resource.mode], - [TEXT.PATH, {resource.path}], - [TEXT.TYPE, resource.type], - [TEXT.UID, resource.uid], - ], - }, - { - title: TEXT.HOST, - listItems: [ - [TEXT.ARCHITECTURE, host.architecture], - [TEXT.CONTAINERIZED, host.containerized ? 'true' : 'false'], - [TEXT.HOSTNAME, host.hostname], - [TEXT.ID, {host.id}], - [TEXT.IP, {host.ip.join(', ')}], - [TEXT.MAC, {host.mac.join(', ')}], - [TEXT.NAME, host.name], - ], - }, - { - title: TEXT.OS, - listItems: [ - [TEXT.CODENAME, host.os.codename], - [TEXT.FAMILY, host.os.family], - [TEXT.KERNEL, host.os.kernel], - [TEXT.NAME, host.os.name], - [TEXT.PLATFORM, host.os.platform], - [TEXT.TYPE, host.os.type], - [TEXT.VERSION, host.os.version], - ], - }, -]; - -const getGeneralCards = ({ rule }: CspFinding): Card[] => [ - { - title: TEXT.RULE, - listItems: [ - [TEXT.SEVERITY, ''], - [TEXT.INDEX, ''], - [TEXT.RULE_EVALUATED_AT, ''], - [ - TEXT.FRAMEWORK_SOURCES, - - - - - - - - , - ], - [TEXT.SECTION, ''], - [TEXT.PROFILE_APPLICABILITY, ''], - [TEXT.AUDIT, ''], - [TEXT.BENCHMARK, rule.benchmark.name], - [TEXT.NAME, rule.name], - [TEXT.DESCRIPTION, rule.description], - [ - TEXT.TAGS, - rule.tags.map((t) => ( - - {t} - - )), - ], - ], - }, -]; - -const getRemediationCards = ({ result, ...rest }: CspFinding): Card[] => [ - { - title: TEXT.RESULT, - listItems: [ - [TEXT.EXPECTED, ''], - [TEXT.EVIDENCE, {JSON.stringify(result.evidence, null, 2)}], - [TEXT.TIMESTAMP, {rest['@timestamp']}], - ], - }, - { - title: TEXT.REMEDIATION, - listItems: [ - ['', {rest.rule.remediation}], - [TEXT.IMPACT, rest.rule.impact], - [TEXT.DEFAULT_VALUE, ''], - [TEXT.RATIONALE, ''], - ], - }, -]; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/__snapshots__/resource_tab.test.ts.snap b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/__snapshots__/resource_tab.test.ts.snap new file mode 100644 index 0000000000000..ffa5d91e2d677 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/__snapshots__/resource_tab.test.ts.snap @@ -0,0 +1,100 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`prepareDescriptionList create description lists accordingly 1`] = ` +Array [ + Object { + "description": + string + , + "title": + + a + + , + }, + Object { + "description": + 123 + , + "title": + + b + + , + }, + Object { + "description": "undefined", + "title": + + c + + , + }, + Object { + "description": + null + , + "title": + + d + + , + }, + Object { + "description": + true + , + "title": + + e + + , + }, + Object { + "description": + false + , + "title": + + f + + , + }, + Object { + "description": + [ + { + "a": "another string", + "b": 123 + } +] + , + "title": + + g + + , + }, +] +`; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/findings_flyout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/findings_flyout.tsx new file mode 100644 index 0000000000000..be05e2b8418d6 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/findings_flyout.tsx @@ -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 React, { useState } from 'react'; +import { + EuiCodeBlock, + EuiFlexItem, + EuiSpacer, + EuiDescriptionList, + EuiTextColor, + EuiFlyout, + EuiFlyoutHeader, + EuiTitle, + EuiFlyoutBody, + EuiTabs, + EuiTab, + EuiFlexGrid, + EuiCard, + EuiFlexGroup, + EuiIcon, + type PropsOf, + EuiMarkdownFormat, +} from '@elastic/eui'; +import { assertNever } from '@kbn/std'; +import moment from 'moment'; +import type { CspFinding } from '../types'; +import { CspEvaluationBadge } from '../../../components/csp_evaluation_badge'; +import * as TEXT from '../translations'; +import cisLogoIcon from '../../../assets/icons/cis_logo.svg'; +import k8sLogoIcon from '../../../assets/icons/k8s_logo.svg'; +import { ResourceTab } from './resource_tab'; +import { JsonTab } from './json_tab'; + +const tabs = [ + { title: TEXT.REMEDIATION, id: 'remediation' }, + { title: TEXT.RESOURCE, id: 'resource' }, + { title: TEXT.GENERAL, id: 'general' }, + { title: TEXT.JSON, id: 'json' }, +] as const; + +const CodeBlock: React.FC> = (props) => ( + +); + +const Markdown: React.FC> = (props) => ( + +); + +type FindingsTab = typeof tabs[number]; + +type EuiListItemsProps = NonNullable['listItems']>[number]; + +interface Card { + title: string; + listItems: Array<[EuiListItemsProps['title'], EuiListItemsProps['description']]>; +} + +interface FindingFlyoutProps { + onClose(): void; + findings: CspFinding; +} + +const Cards = ({ data }: { data: Card[] }) => ( + + {data.map((card) => ( + + + ({ title: v[0], description: v[1] }))} + style={{ flexFlow: 'column' }} + descriptionProps={{ + style: { width: '100%' }, + }} + /> + + + ))} + +); + +const FindingsTab = ({ tab, findings }: { findings: CspFinding; tab: FindingsTab }) => { + switch (tab.id) { + case 'remediation': + return ; + case 'resource': + return ; + case 'general': + return ; + case 'json': + return ; + default: + assertNever(tab); + } +}; + +export const FindingsRuleFlyout = ({ onClose, findings }: FindingFlyoutProps) => { + const [tab, setTab] = useState(tabs[0]); + + return ( + + + + + + + + + + {findings.rule.name} + + + + + + + {tabs.map((v) => ( + setTab(v)}> + {v.title} + + ))} + + + + + + + ); +}; + +const getGeneralCards = ({ rule, ...rest }: CspFinding): Card[] => [ + { + title: TEXT.RULE, + listItems: [ + [TEXT.RULE_EVALUATED_AT, moment(rest['@timestamp']).format('MMMM D, YYYY @ HH:mm:ss.SSS')], + [ + TEXT.FRAMEWORK_SOURCES, + + + + + + + + , + ], + [TEXT.CIS_SECTION, rule.section], + [TEXT.PROFILE_APPLICABILITY, {rule.profile_applicability}], + [TEXT.BENCHMARK, rule.benchmark.name], + [TEXT.NAME, rule.name], + [TEXT.DESCRIPTION, {rule.description}], + [TEXT.AUDIT, {rule.audit}], + [TEXT.REFERENCES, {rule.references}], + ], + }, +]; + +const getRemediationCards = ({ result, rule, ...rest }: CspFinding): Card[] => [ + { + title: TEXT.RESULT_DETAILS, + listItems: [ + result.expected + ? [TEXT.EXPECTED, {JSON.stringify(result.expected, null, 2)}] + : ['', ''], + [TEXT.EVIDENCE, {JSON.stringify(result.evidence, null, 2)}], + [ + TEXT.RULE_EVALUATED_AT, + {moment(rest['@timestamp']).format('MMMM D, YYYY @ HH:mm:ss.SSS')}, + ], + ], + }, + { + title: TEXT.REMEDIATION, + listItems: [ + ['', {rule.remediation}], + [TEXT.IMPACT, {rule.impact}], + [TEXT.DEFAULT_VALUE, {rule.default_value}], + [TEXT.RATIONALE, {rule.rationale}], + ], + }, +]; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/json_tab.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/json_tab.tsx new file mode 100644 index 0000000000000..ed5845abf19bb --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/json_tab.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; +import { XJsonLang } from '@kbn/monaco'; +import { CspFinding } from '../types'; + +const offsetHeight = 120; + +export const JsonTab = ({ data }: { data: CspFinding }) => ( +
+ +
+); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.test.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.test.ts new file mode 100644 index 0000000000000..94886e8fb254d --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.test.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { prepareDescriptionList } from './resource_tab'; + +const mockData = { + a: 'string', + b: 123, + c: undefined, + d: null, + e: true, + f: false, + g: [{ a: 'another string', b: 123 }], +}; + +describe('prepareDescriptionList', () => { + it('create description lists accordingly', () => { + const descriptionList = prepareDescriptionList(mockData); + expect(descriptionList).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.tsx new file mode 100644 index 0000000000000..7919b836a3a73 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.tsx @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiAccordion, + EuiCode, + EuiCodeBlock, + EuiDescriptionList, + EuiPanel, + EuiSpacer, + EuiText, + useEuiTheme, +} from '@elastic/eui'; +import React, { useMemo } from 'react'; +import { getFlattenedObject } from '@kbn/std'; +import { CspFinding } from '../types'; +import * as TEXT from '../translations'; + +const getDescriptionDisplay = (value: unknown) => { + if (value === undefined) return 'undefined'; + if (typeof value === 'boolean' || value === null) { + return {JSON.stringify(value)}; + } + + if (typeof value === 'object') { + return ( + + {JSON.stringify(value, null, 2)} + + ); + } + + return {value as string}; +}; + +export const prepareDescriptionList = (data: Record) => + Object.entries(getFlattenedObject(data)) + .sort((a, b) => a[0].localeCompare(b[0])) + .map(([key, value]) => ({ + title: ( + + {key} + + ), + description: getDescriptionDisplay(value), + })); + +export const ResourceTab = ({ data }: { data: CspFinding }) => { + const { euiTheme } = useEuiTheme(); + + const accordions = useMemo( + () => [ + { + title: TEXT.RESOURCE, + id: 'resourceAccordion', + listItems: prepareDescriptionList(data.resource), + }, + { + title: TEXT.HOST, + id: 'hostAccordion', + listItems: prepareDescriptionList(data.host), + }, + ], + [data.host, data.resource] + ); + + return ( + <> + {accordions.map((accordion) => ( + + + + {accordion.title} +
+ } + arrowDisplay="right" + initialIsOpen + > + + + + + + ))} + + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_group_by_selector.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_group_by_selector.tsx deleted file mode 100644 index 84adf919a5fe1..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_group_by_selector.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 React from 'react'; -import { EuiComboBox, EuiFormLabel, type EuiComboBoxOptionOption } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import type { FindingsGroupByKind } from './types'; - -interface Props { - type: FindingsGroupByKind; - options: Array>; - onChange(selectedOptions: Array>): void; -} - -export const FindingsGroupBySelector = ({ type, options, onChange }: Props) => ( - } - singleSelection={{ asPlainText: true }} - options={options} - selectedOptions={options.filter((o) => o.value === type)} - onChange={onChange} - /> -); - -const GroupByLabel = () => ( - - - -); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_table.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_table.test.tsx deleted file mode 100644 index 28d14b42ae099..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_table.test.tsx +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import * as TEST_SUBJECTS from './test_subjects'; -import { FindingsTable } from './findings_table'; -import type { PropsOf } from '@elastic/eui'; -import Chance from 'chance'; -import type { CspFinding } from './types'; - -const chance = new Chance(); - -const getFakeFindings = (name: string): CspFinding & { id: string } => ({ - id: chance.word(), - result: { - evaluation: chance.weighted(['passed', 'failed'], [0.5, 0.5]), - evidence: { - filemode: chance.word(), - }, - }, - rule: { - name, - description: chance.paragraph(), - impact: chance.word(), - remediation: chance.word(), - benchmark: { - name: 'CIS Kubernetes', - version: '1.6.0', - }, - tags: [], - }, - agent: { - id: chance.string(), - name: chance.string(), - type: chance.string(), - version: chance.string(), - }, - resource: { - filename: chance.string(), - type: chance.string(), - path: chance.string(), - uid: chance.string(), - mode: chance.string(), - }, - cycle_id: chance.string(), - host: {} as any, - ecs: {} as any, - '@timestamp': new Date().toISOString(), -}); - -type TableProps = PropsOf; - -describe('', () => { - it('renders the zero state when status success and data has a length of zero ', async () => { - const props: TableProps = { - loading: false, - data: { page: [], total: 0 }, - error: null, - sort: [], - from: 1, - size: 10, - setQuery: jest.fn, - }; - - render(); - - expect(screen.getByTestId(TEST_SUBJECTS.FINDINGS_TABLE_ZERO_STATE)).toBeInTheDocument(); - }); - - it('renders the table with provided items', () => { - const names = chance.unique(chance.sentence, 10); - const data = names.map(getFakeFindings); - - const props: TableProps = { - loading: false, - data: { page: data, total: 10 }, - error: null, - sort: [], - from: 0, - size: 10, - setQuery: jest.fn, - }; - - render(); - - data.forEach((item) => { - expect(screen.getByText(item.rule.name)).toBeInTheDocument(); - }); - }); -}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_table.tsx deleted file mode 100644 index 189cf42994382..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_table.tsx +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import React, { useCallback, useMemo, useState } from 'react'; -import { - type Criteria, - EuiToolTip, - EuiLink, - EuiTableFieldDataColumnType, - EuiEmptyPrompt, - EuiBasicTable, - PropsOf, - EuiBasicTableProps, -} from '@elastic/eui'; -import moment from 'moment'; -import { SortDirection } from '@kbn/data-plugin/common'; -import { extractErrorMessage } from '../../../common/utils/helpers'; -import * as TEST_SUBJECTS from './test_subjects'; -import * as TEXT from './translations'; -import type { CspFinding } from './types'; -import { CspEvaluationBadge } from '../../components/csp_evaluation_badge'; -import type { FindingsGroupByNoneQuery, CspFindingsResult } from './use_findings'; -import { FindingsRuleFlyout } from './findings_flyout'; - -interface BaseFindingsTableProps extends FindingsGroupByNoneQuery { - setQuery(query: Partial): void; -} - -type FindingsTableProps = CspFindingsResult & BaseFindingsTableProps; - -const FindingsTableComponent = ({ - setQuery, - from, - size, - sort = [], - error, - data, - loading, -}: FindingsTableProps) => { - const [selectedFinding, setSelectedFinding] = useState(); - - const pagination = useMemo( - () => - getEuiPaginationFromEsSearchSource({ - from, - size, - total: data?.total, - }), - [from, size, data] - ); - - const sorting = useMemo(() => getEuiSortFromEsSearchSource(sort), [sort]); - - const getCellProps = useCallback( - (item: CspFinding, column: EuiTableFieldDataColumnType) => ({ - onClick: column.field === 'rule.name' ? () => setSelectedFinding(item) : undefined, - }), - [] - ); - - const onTableChange = useCallback( - (params: Criteria) => { - setQuery(getEsSearchQueryFromEuiTableParams(params)); - }, - [setQuery] - ); - - // Show "zero state" - if (!loading && !data?.page.length) - // TODO: use our own logo - return ( - {TEXT.NO_FINDINGS}} - data-test-subj={TEST_SUBJECTS.FINDINGS_TABLE_ZERO_STATE} - /> - ); - - return ( - <> - - {selectedFinding && ( - setSelectedFinding(undefined)} - /> - )} - - ); -}; - -const getEuiPaginationFromEsSearchSource = ({ - from: pageIndex, - size: pageSize, - total, -}: Pick & { - total: number | undefined; -}): EuiBasicTableProps['pagination'] => ({ - pageSize, - pageIndex: Math.ceil(pageIndex / pageSize), - totalItemCount: total || 0, - pageSizeOptions: [10, 25, 100], - showPerPageOptions: true, -}); - -const getEuiSortFromEsSearchSource = ( - sort: FindingsGroupByNoneQuery['sort'] -): EuiBasicTableProps['sorting'] => { - if (!sort.length) return; - - const entry = Object.entries(sort[0])?.[0]; - if (!entry) return; - - const [field, direction] = entry; - return { sort: { field: field as keyof CspFinding, direction: direction as SortDirection } }; -}; - -const getEsSearchQueryFromEuiTableParams = ({ - page, - sort, -}: Criteria): Partial => ({ - ...(!!page && { from: page.index * page.size, size: page.size }), - sort: sort ? [{ [sort.field]: SortDirection[sort.direction] }] : undefined, -}); - -const timestampRenderer = (timestamp: string) => ( - - {moment.duration(moment().diff(timestamp)).humanize()} - -); - -const resourceFilenameRenderer = (filename: string) => ( - - {filename} - -); - -const ruleNameRenderer = (name: string) => ( - - {name} - -); - -const resultEvaluationRenderer = (type: PropsOf['type']) => ( - -); - -const columns: Array> = [ - { - field: 'resource.filename', - name: TEXT.RESOURCE, - truncateText: true, - width: '15%', - sortable: true, - render: resourceFilenameRenderer, - }, - { - field: 'rule.name', - name: TEXT.RULE_NAME, - truncateText: true, - render: ruleNameRenderer, - sortable: true, - }, - { - field: 'result.evaluation', - name: TEXT.EVALUATION, - width: '100px', - render: resultEvaluationRenderer, - sortable: true, - }, - { - field: '@timestamp', - width: '100px', - name: TEXT.TIMESTAMP, - truncateText: true, - render: timestampRenderer, - sortable: true, - }, -]; - -export const FindingsTable = React.memo(FindingsTableComponent); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.test.tsx new file mode 100644 index 0000000000000..ae980c1e492bb --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.test.tsx @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { render } from '@testing-library/react'; +import { LatestFindingsContainer, getDefaultQuery } from './latest_findings_container'; +import { createStubDataView } from '@kbn/data-views-plugin/common/mocks'; +import { CSP_LATEST_FINDINGS_DATA_VIEW } from '../../../../common/constants'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { TestProvider } from '../../../test/test_provider'; +import { getFindingsQuery } from './use_latest_findings'; +import { encodeQuery } from '../../../common/navigation/query_utils'; +import { useLocation } from 'react-router-dom'; +import { RisonObject } from 'rison-node'; +import { buildEsQuery } from '@kbn/es-query'; +import { getFindingsCountAggQuery } from '../use_findings_count'; + +jest.mock('../../../common/api/use_latest_findings_data_view'); +jest.mock('../../../common/api/use_cis_kubernetes_integration'); + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({ push: jest.fn() }), + useLocation: jest.fn(), +})); + +beforeEach(() => { + jest.restoreAllMocks(); +}); + +describe('', () => { + it('data#search.search fn called with URL query', () => { + const query = getDefaultQuery(); + const dataMock = dataPluginMock.createStartContract(); + const dataView = createStubDataView({ + spec: { + id: CSP_LATEST_FINDINGS_DATA_VIEW, + }, + }); + + (useLocation as jest.Mock).mockReturnValue({ + search: encodeQuery(query as unknown as RisonObject), + }); + + render( + + + + ); + + const baseQuery = { + index: dataView.title, + query: buildEsQuery(dataView, query.query, query.filters), + }; + + expect(dataMock.search.search).toHaveBeenNthCalledWith(1, { + params: getFindingsCountAggQuery(baseQuery), + }); + + expect(dataMock.search.search).toHaveBeenNthCalledWith(2, { + params: getFindingsQuery({ + ...baseQuery, + sort: query.sort, + size: query.size, + from: query.from, + }), + }); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx new file mode 100644 index 0000000000000..78a1fd758b6ee --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useMemo } from 'react'; +import { EuiSpacer } from '@elastic/eui'; +import type { DataView } from '@kbn/data-plugin/common'; +import { SortDirection } from '@kbn/data-plugin/common'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { FindingsTable } from './latest_findings_table'; +import { FindingsSearchBar } from '../layout/findings_search_bar'; +import * as TEST_SUBJECTS from '../test_subjects'; +import { useUrlQuery } from '../../../common/hooks/use_url_query'; +import { useLatestFindings } from './use_latest_findings'; +import type { FindingsGroupByNoneQuery } from './use_latest_findings'; +import type { FindingsBaseURLQuery } from '../types'; +import { useFindingsCounter } from '../use_findings_count'; +import { FindingsDistributionBar } from '../layout/findings_distribution_bar'; +import { getBaseQuery } from '../utils'; +import { PageWrapper, PageTitle, PageTitleText } from '../layout/findings_layout'; +import { FindingsGroupBySelector } from '../layout/findings_group_by_selector'; +import { useCspBreadcrumbs } from '../../../common/navigation/use_csp_breadcrumbs'; +import { findingsNavigation } from '../../../common/navigation/constants'; + +export const getDefaultQuery = (): FindingsBaseURLQuery & FindingsGroupByNoneQuery => ({ + query: { language: 'kuery', query: '' }, + filters: [], + sort: [{ ['@timestamp']: SortDirection.desc }], + from: 0, + size: 10, +}); + +export const LatestFindingsContainer = ({ dataView }: { dataView: DataView }) => { + useCspBreadcrumbs([findingsNavigation.findings_default]); + const { urlQuery, setUrlQuery } = useUrlQuery(getDefaultQuery); + const baseEsQuery = useMemo( + () => getBaseQuery({ dataView, filters: urlQuery.filters, query: urlQuery.query }), + [dataView, urlQuery.filters, urlQuery.query] + ); + + const findingsCount = useFindingsCounter(baseEsQuery); + const findingsGroupByNone = useLatestFindings({ + ...baseEsQuery, + size: urlQuery.size, + from: urlQuery.from, + sort: urlQuery.sort, + }); + + return ( +
+ + + + + } + /> + + + + + + +
+ ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx new file mode 100644 index 0000000000000..d01af2fa96e94 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx @@ -0,0 +1,112 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import * as TEST_SUBJECTS from '../test_subjects'; +import { FindingsTable } from './latest_findings_table'; +import type { PropsOf } from '@elastic/eui'; +import Chance from 'chance'; +import type { CspFinding } from '../types'; +import { TestProvider } from '../../../test/test_provider'; + +const chance = new Chance(); + +const getFakeFindings = (name: string): CspFinding & { id: string } => ({ + id: chance.word(), + result: { + expected: { + source: {}, + }, + evaluation: chance.weighted(['passed', 'failed'], [0.5, 0.5]), + evidence: { + filemode: chance.word(), + }, + }, + rule: { + name, + description: chance.paragraph(), + impact: chance.word(), + remediation: chance.word(), + benchmark: { + name: 'CIS Kubernetes', + version: '1.6.0', + }, + section: chance.sentence(), + audit: chance.paragraph(), + references: chance.paragraph(), + profile_applicability: chance.sentence(), + rationale: chance.paragraph(), + default_value: chance.sentence(), + tags: [], + }, + agent: { + id: chance.string(), + name: chance.string(), + type: chance.string(), + version: chance.string(), + }, + resource: { + filename: chance.string(), + type: chance.string(), + path: chance.string(), + uid: chance.string(), + mode: chance.string(), + }, + cycle_id: chance.string(), + host: {} as any, + ecs: {} as any, + '@timestamp': new Date().toISOString(), +}); + +type TableProps = PropsOf; + +describe('', () => { + it('renders the zero state when status success and data has a length of zero ', async () => { + const props: TableProps = { + loading: false, + data: { page: [], total: 0 }, + error: null, + sort: [], + from: 1, + size: 10, + setQuery: jest.fn, + }; + + render( + + + + ); + + expect(screen.getByTestId(TEST_SUBJECTS.FINDINGS_TABLE_ZERO_STATE)).toBeInTheDocument(); + }); + + it('renders the table with provided items', () => { + const names = chance.unique(chance.sentence, 10); + const data = names.map(getFakeFindings); + + const props: TableProps = { + loading: false, + data: { page: data, total: 10 }, + error: null, + sort: [], + from: 0, + size: 10, + setQuery: jest.fn, + }; + + render( + + + + ); + + data.forEach((item) => { + expect(screen.getByText(item.rule.name)).toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx new file mode 100644 index 0000000000000..6a2bd1c129b50 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.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 React, { useCallback, useMemo, useState } from 'react'; +import { + type Criteria, + EuiEmptyPrompt, + EuiBasicTable, + EuiBasicTableProps, + EuiBasicTableColumn, +} from '@elastic/eui'; +import { SortDirection } from '@kbn/data-plugin/common'; +import { EuiTableActionsColumnType } from '@elastic/eui/src/components/basic_table/table_types'; +import { extractErrorMessage } from '../../../../common/utils/helpers'; +import * as TEST_SUBJECTS from '../test_subjects'; +import * as TEXT from '../translations'; +import type { CspFinding } from '../types'; +import type { FindingsGroupByNoneQuery, CspFindingsResult } from './use_latest_findings'; +import { FindingsRuleFlyout } from '../findings_flyout/findings_flyout'; +import { getExpandColumn, getFindingsColumns } from '../layout/findings_layout'; + +interface BaseFindingsTableProps extends FindingsGroupByNoneQuery { + setQuery(query: Partial): void; +} + +type FindingsTableProps = CspFindingsResult & BaseFindingsTableProps; + +const FindingsTableComponent = ({ + setQuery, + from, + size, + sort = [], + error, + data, + loading, +}: FindingsTableProps) => { + const [selectedFinding, setSelectedFinding] = useState(); + + const columns: [ + EuiTableActionsColumnType, + ...Array> + ] = useMemo( + () => [getExpandColumn({ onClick: setSelectedFinding }), ...getFindingsColumns()], + [] + ); + + const pagination = useMemo( + () => + getEuiPaginationFromEsSearchSource({ + from, + size, + total: data?.total, + }), + [from, size, data] + ); + + const sorting = useMemo(() => getEuiSortFromEsSearchSource(sort), [sort]); + + const onTableChange = useCallback( + (params: Criteria) => { + setQuery(getEsSearchQueryFromEuiTableParams(params)); + }, + [setQuery] + ); + + // Show "zero state" + if (!loading && !data?.page.length) + // TODO: use our own logo + return ( + {TEXT.NO_FINDINGS}} + data-test-subj={TEST_SUBJECTS.FINDINGS_TABLE_ZERO_STATE} + /> + ); + + return ( + <> + + {selectedFinding && ( + setSelectedFinding(undefined)} + /> + )} + + ); +}; + +const getEuiPaginationFromEsSearchSource = ({ + from: pageIndex, + size: pageSize, + total, +}: Pick & { + total: number | undefined; +}): EuiBasicTableProps['pagination'] => ({ + pageSize, + pageIndex: Math.ceil(pageIndex / pageSize), + totalItemCount: total || 0, + pageSizeOptions: [10, 25, 100], + showPerPageOptions: true, +}); + +const getEuiSortFromEsSearchSource = ( + sort: FindingsGroupByNoneQuery['sort'] +): EuiBasicTableProps['sorting'] => { + if (!sort.length) return; + + const entry = Object.entries(sort[0])?.[0]; + if (!entry) return; + + const [field, direction] = entry; + return { sort: { field: field as keyof CspFinding, direction: direction as SortDirection } }; +}; + +const getEsSearchQueryFromEuiTableParams = ({ + page, + sort, +}: Criteria): Partial => ({ + ...(!!page && { from: page.index * page.size, size: page.size }), + sort: sort ? [{ [sort.field]: SortDirection[sort.direction] }] : undefined, +}); + +export const FindingsTable = React.memo(FindingsTableComponent); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/use_latest_findings.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/use_latest_findings.ts new file mode 100644 index 0000000000000..608f400953c86 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/use_latest_findings.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 { useQuery } from 'react-query'; +import { number } from 'io-ts'; +import { lastValueFrom } from 'rxjs'; +import type { EsQuerySortValue, IEsSearchResponse } from '@kbn/data-plugin/common'; +import type { CoreStart } from '@kbn/core/public'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { extractErrorMessage } from '../../../../common/utils/helpers'; +import * as TEXT from '../translations'; +import type { CspFinding, FindingsQueryResult } from '../types'; +import { useKibana } from '../../../common/hooks/use_kibana'; +import type { FindingsBaseEsQuery } from '../types'; + +interface UseFindingsOptions extends FindingsBaseEsQuery, FindingsGroupByNoneQuery {} + +export interface FindingsGroupByNoneQuery { + from: NonNullable; + size: NonNullable; + sort: EsQuerySortValue[]; +} + +interface CspFindingsData { + page: CspFinding[]; + total: number; +} + +export type CspFindingsResult = FindingsQueryResult; + +const FIELDS_WITHOUT_KEYWORD_MAPPING = new Set(['@timestamp']); + +// NOTE: .keyword comes from the mapping we defined for the Findings index +const getSortKey = (key: string): string => + FIELDS_WITHOUT_KEYWORD_MAPPING.has(key) ? key : `${key}.keyword`; + +/** + * @description utility to transform a column header key to its field mapping for sorting + * @example Adds '.keyword' to every property we sort on except values of `FIELDS_WITHOUT_KEYWORD_MAPPING` + * @todo find alternative + * @note we choose the keyword 'keyword' in the field mapping + */ +const mapEsQuerySortKey = (sort: readonly EsQuerySortValue[]): EsQuerySortValue[] => + sort.slice().reduce((acc, cur) => { + const entry = Object.entries(cur)[0]; + if (!entry) return acc; + + const [k, v] = entry; + acc.push({ [getSortKey(k)]: v }); + + return acc; + }, []); + +export const showErrorToast = ( + toasts: CoreStart['notifications']['toasts'], + error: unknown +): void => { + if (error instanceof Error) toasts.addError(error, { title: TEXT.SEARCH_FAILED }); + else toasts.addDanger(extractErrorMessage(error, TEXT.SEARCH_FAILED)); +}; + +export const getFindingsQuery = ({ index, query, size, from, sort }: UseFindingsOptions) => ({ + index, + query, + size, + from, + sort: mapEsQuerySortKey(sort), +}); + +export const useLatestFindings = ({ index, query, sort, from, size }: UseFindingsOptions) => { + const { + data, + notifications: { toasts }, + } = useKibana().services; + + return useQuery( + ['csp_findings', { index, query, sort, from, size }], + () => + lastValueFrom>( + data.search.search({ + params: getFindingsQuery({ index, query, sort, from, size }), + }) + ), + { + select: ({ rawResponse: { hits } }) => ({ + page: hits.hits.map((hit) => hit._source!), + total: number.is(hits.total) ? hits.total : 0, + }), + onError: (err) => showErrorToast(toasts, err), + } + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_container.tsx new file mode 100644 index 0000000000000..3dfbd477d4236 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_container.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import type { DataView } from '@kbn/data-plugin/common'; +import { Route, Switch } from 'react-router-dom'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { FindingsSearchBar } from '../layout/findings_search_bar'; +import * as TEST_SUBJECTS from '../test_subjects'; +import { useUrlQuery } from '../../../common/hooks/use_url_query'; +import type { FindingsBaseURLQuery } from '../types'; +import { useFindingsByResource } from './use_findings_by_resource'; +import { FindingsByResourceTable } from './findings_by_resource_table'; +import { getBaseQuery } from '../utils'; +import { PageTitle, PageTitleText, PageWrapper } from '../layout/findings_layout'; +import { FindingsGroupBySelector } from '../layout/findings_group_by_selector'; +import { findingsNavigation } from '../../../common/navigation/constants'; +import { useCspBreadcrumbs } from '../../../common/navigation/use_csp_breadcrumbs'; +import { ResourceFindings } from './resource_findings/resource_findings_container'; + +const getDefaultQuery = (): FindingsBaseURLQuery => ({ + query: { language: 'kuery', query: '' }, + filters: [], +}); + +export const FindingsByResourceContainer = ({ dataView }: { dataView: DataView }) => ( + + } + /> + } + /> + +); + +const LatestFindingsByResource = ({ dataView }: { dataView: DataView }) => { + useCspBreadcrumbs([findingsNavigation.findings_by_resource]); + const { urlQuery, setUrlQuery } = useUrlQuery(getDefaultQuery); + const findingsGroupByResource = useFindingsByResource( + getBaseQuery({ dataView, filters: urlQuery.filters, query: urlQuery.query }) + ); + + return ( +
+ + + + + } + /> + + + + +
+ ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_by_resource_table.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.test.tsx similarity index 85% rename from x-pack/plugins/cloud_security_posture/public/pages/findings/findings_by_resource_table.test.tsx rename to x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.test.tsx index 3018de31aeb58..f51be5f7a43e1 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_by_resource_table.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.test.tsx @@ -6,12 +6,13 @@ */ import React from 'react'; import { render, screen, within } from '@testing-library/react'; -import * as TEST_SUBJECTS from './test_subjects'; +import * as TEST_SUBJECTS from '../test_subjects'; import { FindingsByResourceTable, formatNumber, getResourceId } from './findings_by_resource_table'; -import * as TEXT from './translations'; +import * as TEXT from '../translations'; import type { PropsOf } from '@elastic/eui'; import Chance from 'chance'; import numeral from '@elastic/numeral'; +import { TestProvider } from '../../../test/test_provider'; const chance = new Chance(); @@ -35,7 +36,11 @@ describe('', () => { error: null, }; - render(); + render( + + + + ); expect(screen.getByText(TEXT.NO_FINDINGS)).toBeInTheDocument(); }); @@ -49,7 +54,11 @@ describe('', () => { error: null, }; - render(); + render( + + + + ); data.forEach((item, i) => { const row = screen.getByTestId( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_by_resource_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.tsx similarity index 87% rename from x-pack/plugins/cloud_security_posture/public/pages/findings/findings_by_resource_table.tsx rename to x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.tsx index d2acee177686a..ef7b3da67fbb4 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_by_resource_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.tsx @@ -12,14 +12,15 @@ import { EuiTextColor, EuiFlexGroup, EuiFlexItem, - EuiLink, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import numeral from '@elastic/numeral'; -import { extractErrorMessage } from '../../../common/utils/helpers'; -import * as TEST_SUBJECTS from './test_subjects'; -import * as TEXT from './translations'; +import { Link, generatePath } from 'react-router-dom'; +import { extractErrorMessage } from '../../../../common/utils/helpers'; +import * as TEST_SUBJECTS from '../test_subjects'; +import * as TEXT from '../translations'; import type { CspFindingsByResourceResult } from './use_findings_by_resource'; +import { findingsNavigation } from '../../../common/navigation/constants'; export const formatNumber = (value: number) => value < 1000 ? value : numeral(value).format('0.0a'); @@ -62,7 +63,11 @@ const columns: Array> = [ defaultMessage="Resource ID" /> ), - render: (resourceId: CspFindingsByResource['resource_id']) => {resourceId}, + render: (resourceId: CspFindingsByResource['resource_id']) => ( + + {resourceId} + + ), }, { field: 'cis_section', diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx new file mode 100644 index 0000000000000..a48926b3653aa --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { EuiSpacer, EuiButtonEmpty } from '@elastic/eui'; +import type { DataView } from '@kbn/data-plugin/common'; +import { Link, useParams } from 'react-router-dom'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useEuiTheme } from '@elastic/eui'; +import { generatePath } from 'react-router-dom'; +import * as TEST_SUBJECTS from '../../test_subjects'; +import { PageWrapper, PageTitle, PageTitleText } from '../../layout/findings_layout'; +import { useCspBreadcrumbs } from '../../../../common/navigation/use_csp_breadcrumbs'; +import { findingsNavigation } from '../../../../common/navigation/constants'; +import { useResourceFindings } from './use_resource_findings'; +import { useUrlQuery } from '../../../../common/hooks/use_url_query'; +import type { FindingsBaseURLQuery } from '../../types'; +import { getBaseQuery } from '../../utils'; +import { ResourceFindingsTable } from './resource_findings_table'; +import { FindingsSearchBar } from '../../layout/findings_search_bar'; + +const getDefaultQuery = (): FindingsBaseURLQuery => ({ + query: { language: 'kuery', query: '' }, + filters: [], +}); + +const BackToResourcesButton = () => { + return ( + + + + + + ); +}; + +export const ResourceFindings = ({ dataView }: { dataView: DataView }) => { + useCspBreadcrumbs([findingsNavigation.findings_default]); + const { euiTheme } = useEuiTheme(); + const params = useParams<{ resourceId: string }>(); + const { urlQuery, setUrlQuery } = useUrlQuery(getDefaultQuery); + + const resourceFindings = useResourceFindings({ + ...getBaseQuery({ dataView, filters: urlQuery.filters, query: urlQuery.query }), + resourceId: params.resourceId, + }); + + return ( +
+ + + + + + +
+ } + /> + + + + +
+ ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx new file mode 100644 index 0000000000000..ec04d05109cdd --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { EuiEmptyPrompt, EuiBasicTable } from '@elastic/eui'; +import { extractErrorMessage } from '../../../../../common/utils/helpers'; +import * as TEXT from '../../translations'; +import type { ResourceFindingsResult } from './use_resource_findings'; +import { getFindingsColumns } from '../../layout/findings_layout'; + +type FindingsGroupByResourceProps = ResourceFindingsResult; + +const columns = getFindingsColumns(); + +const ResourceFindingsTableComponent = ({ error, data, loading }: FindingsGroupByResourceProps) => { + if (!loading && !data?.page.length) + return {TEXT.NO_FINDINGS}} />; + + return ( + + ); +}; + +export const ResourceFindingsTable = React.memo(ResourceFindingsTableComponent); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts new file mode 100644 index 0000000000000..7123b80ef0228 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.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 { useQuery } from 'react-query'; +import { lastValueFrom } from 'rxjs'; +import { IEsSearchResponse } from '@kbn/data-plugin/common'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { useKibana } from '../../../../common/hooks/use_kibana'; +import { showErrorToast } from '../../latest_findings/use_latest_findings'; +import type { CspFinding, FindingsBaseEsQuery, FindingsQueryResult } from '../../types'; + +interface UseResourceFindingsOptions extends FindingsBaseEsQuery { + resourceId: string; +} + +export type ResourceFindingsResult = FindingsQueryResult< + ReturnType['data'] | undefined, + unknown +>; + +export const getResourceFindingsQuery = ({ + index, + query, + resourceId, +}: UseResourceFindingsOptions): estypes.SearchRequest => ({ + index, + body: { + query: { + ...query, + bool: { + ...query?.bool, + filter: [...(query?.bool?.filter || []), { term: { 'resource_id.keyword': resourceId } }], + }, + }, + }, +}); + +export const useResourceFindings = ({ index, query, resourceId }: UseResourceFindingsOptions) => { + const { + data, + notifications: { toasts }, + } = useKibana().services; + + return useQuery( + ['csp_resource_findings', { index, query, resourceId }], + () => + lastValueFrom>( + data.search.search({ + params: getResourceFindingsQuery({ index, query, resourceId }), + }) + ), + { + select: ({ rawResponse: { hits } }) => ({ + page: hits.hits.map((hit) => hit._source!), + total: hits.total as number, + }), + onError: (err) => showErrorToast(toasts, err), + } + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/use_findings_by_resource.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/use_findings_by_resource.ts similarity index 81% rename from x-pack/plugins/cloud_security_posture/public/pages/findings/use_findings_by_resource.ts rename to x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/use_findings_by_resource.ts index b5596d55a6664..6fec85531b196 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/use_findings_by_resource.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/use_findings_by_resource.ts @@ -8,11 +8,9 @@ import { useQuery } from 'react-query'; import { lastValueFrom } from 'rxjs'; import { IKibanaSearchRequest, IKibanaSearchResponse } from '@kbn/data-plugin/common'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { useKibana } from '../../common/hooks/use_kibana'; -import { showErrorToast } from './use_findings'; -import type { FindingsBaseEsQuery, FindingsQueryResult, FindingsQueryStatus } from './types'; - -interface UseFindingsByResourceOptions extends FindingsBaseEsQuery, FindingsQueryStatus {} +import { useKibana } from '../../../common/hooks/use_kibana'; +import { showErrorToast } from '../latest_findings/use_latest_findings'; +import type { FindingsBaseEsQuery, FindingsQueryResult } from '../types'; type FindingsAggRequest = IKibanaSearchRequest; type FindingsAggResponse = IKibanaSearchResponse< @@ -43,7 +41,7 @@ interface FindingsAggBucket { export const getFindingsByResourceAggQuery = ({ index, query, -}: Omit): estypes.SearchRequest => ({ +}: FindingsBaseEsQuery): estypes.SearchRequest => ({ index, size: 0, body: { @@ -55,7 +53,7 @@ export const getFindingsByResourceAggQuery = ({ sources: [ { resource_id: { terms: { field: 'resource_id.keyword' } } }, { cluster_id: { terms: { field: 'cluster_id.keyword' } } }, - { cis_section: { terms: { field: 'rule.section' } } }, + { cis_section: { terms: { field: 'rule.section.keyword' } } }, ], }, aggs: { @@ -68,7 +66,7 @@ export const getFindingsByResourceAggQuery = ({ }, }); -export const useFindingsByResource = ({ enabled, index, query }: UseFindingsByResourceOptions) => { +export const useFindingsByResource = ({ index, query }: FindingsBaseEsQuery) => { const { data, notifications: { toasts }, @@ -83,7 +81,6 @@ export const useFindingsByResource = ({ enabled, index, query }: UseFindingsByRe }) ), { - enabled, select: ({ rawResponse }) => ({ page: rawResponse.aggregations?.groupBy.buckets.map(createFindingsByResource) || [], }), diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_distribution_bar.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_distribution_bar.tsx similarity index 100% rename from x-pack/plugins/cloud_security_posture/public/pages/findings/findings_distribution_bar.tsx rename to x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_distribution_bar.tsx diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_group_by_selector.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_group_by_selector.tsx new file mode 100644 index 0000000000000..efbda89eb3e4d --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_group_by_selector.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, { useMemo } from 'react'; +import { EuiComboBox, EuiFormLabel, EuiSpacer, type EuiComboBoxOptionOption } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useHistory } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; +import { INTERNAL_FEATURE_FLAGS } from '../../../../common/constants'; +import type { FindingsGroupByKind } from '../types'; +import { findingsNavigation } from '../../../common/navigation/constants'; + +const getGroupByOptions = (): Array> => [ + { + 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; +} + +const getFindingsGroupPath = (opts: Array>) => { + 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 }: Props) => { + const groupByOptions = useMemo(getGroupByOptions, []); + const history = useHistory(); + + if (!INTERNAL_FEATURE_FLAGS.showFindingsGroupBy) return null; + + const onChange = (options: Array>) => + history.push({ pathname: getFindingsGroupPath(options) }); + + return ( +
+ } + singleSelection={{ asPlainText: true }} + options={groupByOptions} + selectedOptions={groupByOptions.filter((o) => o.value === type)} + onChange={onChange} + /> + +
+ ); +}; + +const GroupByLabel = () => ( + + + +); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_layout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_layout.tsx new file mode 100644 index 0000000000000..ee1a00abc4901 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_layout.tsx @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { + EuiBasicTableColumn, + EuiSpacer, + EuiTableActionsColumnType, + EuiTitle, + EuiToolTip, + PropsOf, + useEuiTheme, +} from '@elastic/eui'; +import { css } from '@emotion/react'; +import moment from 'moment'; +import { CspEvaluationBadge } from '../../../components/csp_evaluation_badge'; +import * as TEXT from '../translations'; +import { CspFinding } from '../types'; + +export const PageWrapper: React.FC = ({ children }) => { + const { euiTheme } = useEuiTheme(); + return ( +
+ {children} +
+ ); +}; + +export const PageTitle: React.FC = ({ children }) => ( + +
+ {children} + +
+
+); + +export const PageTitleText = ({ title }: { title: React.ReactNode }) =>

{title}

; + +export const getExpandColumn = ({ + onClick, +}: { + onClick(item: T): void; +}): EuiTableActionsColumnType => ({ + width: '40px', + actions: [ + { + name: 'Expand', + description: 'Expand', + type: 'icon', + icon: 'expand', + onClick, + }, + ], +}); + +export const getFindingsColumns = (): Array> => [ + { + field: 'resource_id', + name: TEXT.RESOURCE_ID, + truncateText: true, + width: '15%', + sortable: true, + render: (filename: string) => ( + + {filename} + + ), + }, + { + field: 'result.evaluation', + name: TEXT.RESULT, + width: '100px', + sortable: true, + render: (type: PropsOf['type']) => ( + + ), + }, + { + field: 'rule.name', + name: TEXT.RULE, + sortable: true, + }, + { + field: 'cluster_id', + name: TEXT.CLUSTER_ID, + truncateText: true, + sortable: true, + }, + { + field: 'rule.section', + name: TEXT.CIS_SECTION, + sortable: true, + truncateText: true, + }, + { + field: '@timestamp', + name: TEXT.LAST_CHECKED, + truncateText: true, + sortable: true, + render: (timestamp: number) => ( + + {moment(timestamp).fromNow()} + + ), + }, +]; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_search_bar.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_search_bar.tsx similarity index 83% rename from x-pack/plugins/cloud_security_posture/public/pages/findings/findings_search_bar.tsx rename to x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_search_bar.tsx index 89673dabbf91f..ba8309ea57261 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_search_bar.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_search_bar.tsx @@ -9,18 +9,17 @@ import { css } from '@emotion/react'; import { EuiThemeComputed, useEuiTheme } from '@elastic/eui'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import type { DataView } from '@kbn/data-plugin/common'; -import * as TEST_SUBJECTS from './test_subjects'; -import type { CspFindingsResult } from './use_findings'; -import type { FindingsBaseURLQuery } from './types'; -import type { CspClientPluginStartDeps } from '../../types'; -import { PLUGIN_NAME } from '../../../common'; -import { FINDINGS_SEARCH_PLACEHOLDER } from './translations'; +import * as TEST_SUBJECTS from '../test_subjects'; +import type { FindingsBaseURLQuery } from '../types'; +import type { CspClientPluginStartDeps } from '../../../types'; +import { PLUGIN_NAME } from '../../../../common'; +import { FINDINGS_SEARCH_PLACEHOLDER } from '../translations'; type SearchBarQueryProps = Pick; interface FindingsSearchBarProps extends SearchBarQueryProps { setQuery(v: Partial): void; - loading: CspFindingsResult['loading']; + loading: boolean; } export const FindingsSearchBar = ({ diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/translations.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/translations.ts index 5ffa474e7a63d..53d4b8a86e5c0 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/translations.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/translations.ts @@ -42,6 +42,49 @@ export const RESOURCE = i18n.translate('xpack.csp.findings.resourceLabel', { defaultMessage: 'Resource', }); +export const GENERAL = i18n.translate('xpack.csp.findings.findingsFlyout.generalTabLabel', { + defaultMessage: 'General', +}); + +export const JSON = i18n.translate('xpack.csp.findings.findingsFlyout.jsonTabLabel', { + defaultMessage: 'JSON', +}); + +export const RESOURCE_ID = i18n.translate( + 'xpack.csp.findings.findingsTable.findingsTableColumn.resourceIdColumnLabel', + { + defaultMessage: 'Resource ID', + } +); + +export const RESULT = i18n.translate( + 'xpack.csp.findings.findingsTable.findingsTableColumn.resultColumnLabel', + { + defaultMessage: 'Result', + } +); + +export const CLUSTER_ID = i18n.translate( + 'xpack.csp.findings.findingsTable.findingsTableColumn.clusterIdColumnLabel', + { + defaultMessage: 'Cluster ID', + } +); + +export const CIS_SECTION = i18n.translate( + 'xpack.csp.findings.findingsTable.findingsTableColumn.ruleSectionColumnLabel', + { + defaultMessage: 'CIS Section', + } +); + +export const LAST_CHECKED = i18n.translate( + 'xpack.csp.findings.findingsTable.findingsTableColumn.lastCheckedColumnLabel', + { + defaultMessage: 'Last Checked', + } +); + export const FILENAME = i18n.translate('xpack.csp.findings.filenameLabel', { defaultMessage: 'Filename', }); @@ -106,7 +149,14 @@ export const AUDIT = i18n.translate('xpack.csp.findings.auditLabel', { defaultMessage: 'Audit', }); -export const RESULT = i18n.translate('xpack.csp.findings.resultLabel', { +export const REFERENCES = i18n.translate( + 'xpack.csp.findings.findingsFlyout.generalTab.referencesLabel', + { + defaultMessage: 'References', + } +); + +export const RESULT_DETAILS = i18n.translate('xpack.csp.findings.resultLabel', { defaultMessage: 'Result Details', }); @@ -144,7 +194,7 @@ export const ID = i18n.translate('xpack.csp.findings.idLabel', { }); export const HOST = i18n.translate('xpack.csp.findings.hostLabel', { - defaultMessage: 'HOST', + defaultMessage: 'Host', }); export const ARCHITECTURE = i18n.translate('xpack.csp.findings.architectureLabel', { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts index 79d8f3507c896..9fed484a88128 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts @@ -7,10 +7,9 @@ import type { BoolQuery, Filter, Query } from '@kbn/es-query'; import { UseQueryResult } from 'react-query'; -export type FindingsGroupByKind = 'none' | 'resource'; +export type FindingsGroupByKind = 'default' | 'resource'; export interface FindingsBaseURLQuery { - groupBy: FindingsGroupByKind; query: Query; filters: Filter[]; } @@ -22,10 +21,6 @@ export interface FindingsBaseEsQuery { }; } -export interface FindingsQueryStatus { - enabled: boolean; -} - export interface FindingsQueryResult { loading: UseQueryResult['isLoading']; error: TError; @@ -48,8 +43,14 @@ export interface CspFinding { interface CspRule { benchmark: { name: string; version: string }; + section: string; + audit: string; + references: string; + profile_applicability: string; description: string; impact: string; + default_value: string; + rationale: string; name: string; remediation: string; tags: string[]; @@ -57,9 +58,8 @@ interface CspRule { interface CspFindingResult { evaluation: 'passed' | 'failed'; - evidence: { - filemode: string; - }; + expected?: Record; + evidence: Record; } interface CspFindingResource { @@ -69,6 +69,7 @@ interface CspFindingResource { mode: string; path: string; type: string; + [other_keys: string]: unknown; } interface CspFindingHost { @@ -88,6 +89,7 @@ interface CspFindingHost { family: string; name: string; }; + [other_keys: string]: unknown; } interface CspFindingAgent { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/use_findings.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/use_findings.ts deleted file mode 100644 index 1f7e8dae483bf..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/use_findings.ts +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { useQuery } from 'react-query'; -import { number } from 'io-ts'; -import { lastValueFrom } from 'rxjs'; -import type { EsQuerySortValue, IEsSearchResponse } from '@kbn/data-plugin/common'; -import type { CoreStart } from '@kbn/core/public'; -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { extractErrorMessage } from '../../../common/utils/helpers'; -import * as TEXT from './translations'; -import type { CspFinding, FindingsQueryResult } from './types'; -import { useKibana } from '../../common/hooks/use_kibana'; -import type { FindingsBaseEsQuery, FindingsQueryStatus } from './types'; - -interface UseFindingsOptions - extends FindingsBaseEsQuery, - FindingsGroupByNoneQuery, - FindingsQueryStatus {} - -export interface FindingsGroupByNoneQuery { - from: NonNullable; - size: NonNullable; - sort: EsQuerySortValue[]; -} - -interface CspFindingsData { - page: CspFinding[]; - total: number; -} - -export type CspFindingsResult = FindingsQueryResult; - -const FIELDS_WITHOUT_KEYWORD_MAPPING = new Set(['@timestamp']); - -// NOTE: .keyword comes from the mapping we defined for the Findings index -const getSortKey = (key: string): string => - FIELDS_WITHOUT_KEYWORD_MAPPING.has(key) ? key : `${key}.keyword`; - -/** - * @description utility to transform a column header key to its field mapping for sorting - * @example Adds '.keyword' to every property we sort on except values of `FIELDS_WITHOUT_KEYWORD_MAPPING` - * @todo find alternative - * @note we choose the keyword 'keyword' in the field mapping - */ -const mapEsQuerySortKey = (sort: readonly EsQuerySortValue[]): EsQuerySortValue[] => - sort.slice().reduce((acc, cur) => { - const entry = Object.entries(cur)[0]; - if (!entry) return acc; - - const [k, v] = entry; - acc.push({ [getSortKey(k)]: v }); - - return acc; - }, []); - -export const showErrorToast = ( - toasts: CoreStart['notifications']['toasts'], - error: unknown -): void => { - if (error instanceof Error) toasts.addError(error, { title: TEXT.SEARCH_FAILED }); - else toasts.addDanger(extractErrorMessage(error, TEXT.SEARCH_FAILED)); -}; - -export const getFindingsQuery = ({ - index, - query, - size, - from, - sort, -}: Omit) => ({ - index, - query, - size, - from, - sort: mapEsQuerySortKey(sort), -}); - -export const useFindings = ({ enabled, index, query, sort, from, size }: UseFindingsOptions) => { - const { - data, - notifications: { toasts }, - } = useKibana().services; - - return useQuery( - ['csp_findings', { index, query, sort, from, size }], - () => - lastValueFrom>( - data.search.search({ - params: getFindingsQuery({ index, query, sort, from, size }), - }) - ), - { - enabled, - select: ({ rawResponse: { hits } }) => ({ - page: hits.hits.map((hit) => hit._source!), - total: number.is(hits.total) ? hits.total : 0, - }), - onError: (err) => showErrorToast(toasts, err), - } - ); -}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/use_findings_count.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/use_findings_count.ts index 6ed56ea1d4397..f48e630b489d4 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/use_findings_count.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/use_findings_count.ts @@ -9,10 +9,8 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { lastValueFrom } from 'rxjs'; import type { IKibanaSearchRequest, IKibanaSearchResponse } from '@kbn/data-plugin/public'; import { useKibana } from '../../common/hooks/use_kibana'; -import { showErrorToast } from './use_findings'; -import type { FindingsBaseEsQuery, FindingsQueryStatus } from './types'; - -interface UseFindingsCountOptions extends FindingsBaseEsQuery, FindingsQueryStatus {} +import { showErrorToast } from './latest_findings/use_latest_findings'; +import type { FindingsBaseEsQuery } from './types'; type FindingsAggRequest = IKibanaSearchRequest; type FindingsAggResponse = IKibanaSearchResponse>; @@ -35,7 +33,7 @@ export const getFindingsCountAggQuery = ({ index, query }: FindingsBaseEsQuery) }, }); -export const useFindingsCounter = ({ enabled, index, query }: UseFindingsCountOptions) => { +export const useFindingsCounter = ({ index, query }: FindingsBaseEsQuery) => { const { data, notifications: { toasts }, @@ -50,7 +48,6 @@ export const useFindingsCounter = ({ enabled, index, query }: UseFindingsCountOp }) ), { - enabled, onError: (err) => showErrorToast(toasts, err), select: (response) => Object.fromEntries( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts new file mode 100644 index 0000000000000..d3281a1a0dbc8 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { buildEsQuery } from '@kbn/es-query'; +import type { DataView } from '@kbn/data-plugin/common'; +import type { FindingsBaseEsQuery, FindingsBaseURLQuery } from './types'; + +export const getBaseQuery = ({ + dataView, + query, + filters, +}: FindingsBaseURLQuery & { dataView: DataView }): FindingsBaseEsQuery => ({ + index: dataView.title, + // TODO: this will throw for malformed query + // page will display an error boundary with the JS error + // will be accounted for before releasing the feature + query: buildEsQuery(dataView, query, filters), +}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx index 7782be9c4917e..0787bc37c45e7 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx @@ -13,6 +13,7 @@ import { useFindCspRules, useBulkUpdateCspRules, type RuleSavedObject } from './ import * as TEST_SUBJECTS from './test_subjects'; import { Chance } from 'chance'; import { TestProvider } from '../../test/test_provider'; +import { useParams } from 'react-router-dom'; const chance = new Chance(); @@ -21,6 +22,11 @@ jest.mock('./use_csp_rules', () => ({ useBulkUpdateCspRules: jest.fn(), })); +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: jest.fn(), +})); + const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false }, @@ -32,21 +38,54 @@ const getWrapper = ({ children }) => {children}; -const getRuleMock = ({ id = chance.guid(), enabled }: { id?: string; enabled: boolean }) => +const getRuleMock = ({ + packagePolicyId = chance.guid(), + policyId = chance.guid(), + savedObjectId = chance.guid(), + id = chance.guid(), + enabled, +}: { + packagePolicyId?: string; + policyId?: string; + savedObjectId?: string; + id?: string; + enabled: boolean; +}) => ({ - id, + id: savedObjectId, updatedAt: chance.date().toISOString(), attributes: { id, name: chance.sentence(), + package_policy_id: packagePolicyId, + policy_id: policyId, enabled, }, } as RuleSavedObject); +const getSavedObjectRuleEnable = ( + rule: RuleSavedObject, + enabled: RuleSavedObject['attributes']['enabled'] +) => ({ + ...rule, + attributes: { + ...rule.attributes, + enabled, + }, +}); + +const params = { + policyId: chance.guid(), + packagePolicyId: chance.guid(), +}; + describe('', () => { beforeEach(() => { queryClient.clear(); jest.clearAllMocks(); + + (useParams as jest.Mock).mockReturnValue(params); + (useBulkUpdateCspRules as jest.Mock).mockReturnValue({ status: 'idle', mutate: jest.fn(), @@ -102,6 +141,13 @@ describe('', () => { const switchId1 = TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule1.id); const switchId2 = TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule2.id); + expect(screen.getByTestId(switchId1).getAttribute('aria-checked')).toEqual( + rule1.attributes.enabled.toString() + ); + expect(screen.getByTestId(switchId2).getAttribute('aria-checked')).toEqual( + rule2.attributes.enabled.toString() + ); + fireEvent.click(screen.getByTestId(switchId1)); fireEvent.click(screen.getByTestId(switchId2)); @@ -154,6 +200,7 @@ describe('', () => { it('updates rules with local changes done by non-bulk toggles', () => { const Wrapper = getWrapper(); + const rule1 = getRuleMock({ enabled: false }); const rule2 = getRuleMock({ enabled: true }); const rule3 = getRuleMock({ enabled: true }); @@ -184,10 +231,13 @@ describe('', () => { fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_SAVE_BUTTON)); expect(mutate).toHaveBeenCalledTimes(1); - expect(mutate).toHaveBeenCalledWith([ - { ...rule1.attributes, enabled: !rule1.attributes.enabled }, - { ...rule2.attributes, enabled: !rule2.attributes.enabled }, - ]); + expect(mutate).toHaveBeenCalledWith({ + packagePolicyId: params.packagePolicyId, + savedObjectRules: [ + getSavedObjectRuleEnable(rule1, !rule1.attributes.enabled), + getSavedObjectRuleEnable(rule2, !rule2.attributes.enabled), + ], + }); }); it('updates rules with local changes done by bulk toggles', () => { @@ -217,9 +267,10 @@ describe('', () => { fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_SAVE_BUTTON)); expect(mutate).toHaveBeenCalledTimes(1); - expect(mutate).toHaveBeenCalledWith([ - { ...rule1.attributes, enabled: !rule1.attributes.enabled }, - ]); + expect(mutate).toHaveBeenCalledWith({ + packagePolicyId: params.packagePolicyId, + savedObjectRules: [getSavedObjectRuleEnable(rule1, !rule1.attributes.enabled)], + }); }); it('only changes selected rules in bulk operations', () => { @@ -254,11 +305,14 @@ describe('', () => { fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_SAVE_BUTTON)); expect(mutate).toHaveBeenCalledTimes(1); - expect(mutate).toHaveBeenCalledWith([ - { ...rule1.attributes, enabled: !rule1.attributes.enabled }, - { ...rule4.attributes, enabled: !rule4.attributes.enabled }, - { ...rule5.attributes, enabled: !rule5.attributes.enabled }, - ]); + expect(mutate).toHaveBeenCalledWith({ + packagePolicyId: params.packagePolicyId, + savedObjectRules: [ + getSavedObjectRuleEnable(rule1, !rule1.attributes.enabled), + getSavedObjectRuleEnable(rule4, !rule4.attributes.enabled), + getSavedObjectRuleEnable(rule5, !rule5.attributes.enabled), + ], + }); }); it('updates rules with changes of both bulk/non-bulk toggles', () => { @@ -295,11 +349,14 @@ describe('', () => { fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_SAVE_BUTTON)); expect(mutate).toHaveBeenCalledTimes(1); - expect(mutate).toHaveBeenCalledWith([ - { ...rule1.attributes, enabled: !rule1.attributes.enabled }, - { ...rule4.attributes, enabled: !rule4.attributes.enabled }, - { ...rule5.attributes, enabled: !rule5.attributes.enabled }, - ]); + expect(mutate).toHaveBeenCalledWith({ + packagePolicyId: params.packagePolicyId, + savedObjectRules: [ + getSavedObjectRuleEnable(rule1, !rule1.attributes.enabled), + getSavedObjectRuleEnable(rule4, !rule4.attributes.enabled), + getSavedObjectRuleEnable(rule5, !rule5.attributes.enabled), + ], + }); }); it('selects and updates all rules', async () => { @@ -329,12 +386,12 @@ describe('', () => { fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_SAVE_BUTTON)); expect(mutate).toHaveBeenCalledTimes(1); - expect(mutate).toHaveBeenCalledWith( - rules.map((rule) => ({ - ...rule.attributes, - enabled: !enabled, - })) - ); + expect(mutate).toHaveBeenCalledWith({ + packagePolicyId: params.packagePolicyId, + savedObjectRules: rules.map((rule) => + getSavedObjectRuleEnable(rule, !rule.attributes.enabled) + ), + }); }); it('updates the rules from within the flyout', () => { @@ -370,6 +427,9 @@ describe('', () => { const { mutate } = useBulkUpdateCspRules(); expect(mutate).toHaveBeenCalledTimes(1); - expect(mutate).toHaveBeenCalledWith([{ ...rule.attributes, enabled: !enabled }]); + expect(mutate).toHaveBeenCalledWith({ + packagePolicyId: params.packagePolicyId, + savedObjectRules: [getSavedObjectRuleEnable(rule, !rule.attributes.enabled)], + }); }); }); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.tsx index cf77fba3c513f..0db7392e0b933 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.tsx @@ -16,6 +16,7 @@ import { import { useParams } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n-react'; import { pagePathGetters } from '@kbn/fleet-plugin/public'; +import { cspRuleAssetSavedObjectType } from '../../../common/constants'; import { extractErrorMessage, isNonNullable } from '../../../common/utils/helpers'; import { RulesTable } from './rules_table'; import { RulesBottomBar } from './rules_bottom_bar'; @@ -81,7 +82,7 @@ const getRulesPageData = ( error: error ? extractErrorMessage(error) : undefined, all_rules: rules, rules_map: new Map(rules.map((rule) => [rule.id, rule])), - rules_page: page.map((rule) => changedRules.get(rule.attributes.id) || rule), + rules_page: page.map((rule) => changedRules.get(rule.id) || rule), total: data?.total || 0, lastModified: getLastModified(rules) || null, }; @@ -107,9 +108,15 @@ export const RulesContainer = () => { const [selectedRuleId, setSelectedRuleId] = useState(null); const [isAllSelected, setIsAllSelected] = useState(false); const [visibleSelectedRulesIds, setVisibleSelectedRulesIds] = useState([]); - const [rulesQuery, setRulesQuery] = useState({ page: 0, perPage: 5, search: '' }); + const [rulesQuery, setRulesQuery] = useState({ + filter: `${cspRuleAssetSavedObjectType}.attributes.policy_id: "${params.policyId}" and ${cspRuleAssetSavedObjectType}.attributes.package_policy_id: "${params.packagePolicyId}"`, + search: '', + page: 0, + perPage: 5, + }); const { data, status, error, refetch } = useFindCspRules({ + filter: rulesQuery.filter, search: getSimpleQueryString(rulesQuery.search), page: 1, perPage: MAX_ITEMS_PER_PAGE, @@ -152,7 +159,11 @@ export const RulesContainer = () => { const toggleRule = (rule: RuleSavedObject) => toggleRules([rule], !rule.attributes.enabled); - const bulkUpdateRules = () => bulkUpdate([...changedRules].map(([, rule]) => rule.attributes)); + const bulkUpdateRules = () => + bulkUpdate({ + savedObjectRules: [...changedRules].map(([, savedObjectRule]) => savedObjectRule), + packagePolicyId: params.packagePolicyId, + }); const discardChanges = useCallback(() => setChangedRules(new Map()), []); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_flyout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_flyout.tsx index 54657660c7aa4..f21ca53b76649 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_flyout.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_flyout.tsx @@ -146,7 +146,7 @@ const RuleOverviewTab = ({ rule, toggleRule }: { rule: RuleSavedObject; toggleRu label={TEXT.ACTIVATED} checked={rule.attributes.enabled} onChange={toggleRule} - data-test-subj={TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule.attributes.id)} + data-test-subj={TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule.id)} /> diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table.tsx index c577daebb58a6..704b87697094c 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table.tsx @@ -148,7 +148,7 @@ const getColumns = ({ label={enabled ? TEXT.DISABLE : TEXT.ENABLE} checked={enabled} onChange={() => toggleRule(rule)} - data-test-subj={TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule.attributes.id)} + data-test-subj={TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule.id)} /> ), diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_rules.ts b/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_rules.ts index 87f06fe70df76..8c4012f6c8b45 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_rules.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_rules.ts @@ -7,7 +7,11 @@ import { useQuery, useMutation, useQueryClient } from 'react-query'; import { FunctionKeys } from 'utility-types'; import type { SavedObjectsFindOptions, SimpleSavedObject } from '@kbn/core/public'; -import { cspRuleAssetSavedObjectType, type CspRuleSchema } from '../../../common/schemas/csp_rule'; +import { + UPDATE_RULES_CONFIG_ROUTE_PATH, + cspRuleAssetSavedObjectType, +} from '../../../common/constants'; +import type { CspRuleSchema } from '../../../common/schemas/csp_rule'; import { useKibana } from '../../common/hooks/use_kibana'; import { UPDATE_FAILED } from './translations'; @@ -16,11 +20,14 @@ export type RuleSavedObject = Omit< FunctionKeys >; -export type RulesQuery = Required>; +export type RulesQuery = Required< + Pick +>; export type RulesQueryResult = ReturnType; -export const useFindCspRules = ({ search, page, perPage }: RulesQuery) => { +export const useFindCspRules = ({ search, page, perPage, filter }: RulesQuery) => { const { savedObjects } = useKibana().services; + return useQuery( [cspRuleAssetSavedObjectType, { search, page, perPage }], () => @@ -32,24 +39,37 @@ export const useFindCspRules = ({ search, page, perPage }: RulesQuery) => { // NOTE: 'name.raw' is a field mapping we defined on 'name' sortField: 'name.raw', perPage, + filter, }), { refetchOnWindowFocus: false } ); }; export const useBulkUpdateCspRules = () => { - const { savedObjects, notifications } = useKibana().services; + const { savedObjects, notifications, http } = useKibana().services; const queryClient = useQueryClient(); return useMutation( - (rules: CspRuleSchema[]) => - savedObjects.client.bulkUpdate( - rules.map((rule) => ({ + async ({ + savedObjectRules, + packagePolicyId, + }: { + savedObjectRules: RuleSavedObject[]; + packagePolicyId: CspRuleSchema['package_policy_id']; + }) => { + await savedObjects.client.bulkUpdate( + savedObjectRules.map((savedObjectRule) => ({ type: cspRuleAssetSavedObjectType, - id: rule.id, - attributes: rule, + id: savedObjectRule.id, + attributes: savedObjectRule.attributes, })) - ), + ); + await http.post(UPDATE_RULES_CONFIG_ROUTE_PATH, { + body: JSON.stringify({ + package_policy_id: packagePolicyId, + }), + }); + }, { onError: (err) => { if (err instanceof Error) notifications.toasts.addError(err, { title: UPDATE_FAILED }); diff --git a/x-pack/plugins/cloud_security_posture/public/test/fixtures/csp_benchmark_integration.ts b/x-pack/plugins/cloud_security_posture/public/test/fixtures/csp_benchmark_integration.ts index 2bbce9840d800..2f05049c4fe15 100644 --- a/x-pack/plugins/cloud_security_posture/public/test/fixtures/csp_benchmark_integration.ts +++ b/x-pack/plugins/cloud_security_posture/public/test/fixtures/csp_benchmark_integration.ts @@ -36,7 +36,13 @@ export const createCspBenchmarkIntegrationFixture = ({ name: chance.sentence(), agents: chance.integer({ min: 0 }), }, + rules = { + all: chance.integer(), + enabled: chance.integer(), + disabled: chance.integer(), + }, }: CreateCspBenchmarkIntegrationFixtureInput = {}): Benchmark => ({ package_policy, agent_policy, + rules, }); diff --git a/x-pack/plugins/cloud_security_posture/server/create_indices/create_transforms_indices.ts b/x-pack/plugins/cloud_security_posture/server/create_indices/create_transforms_indices.ts index c30bf09a60e0f..4ee09119c15e4 100644 --- a/x-pack/plugins/cloud_security_posture/server/create_indices/create_transforms_indices.ts +++ b/x-pack/plugins/cloud_security_posture/server/create_indices/create_transforms_indices.ts @@ -10,9 +10,9 @@ import type { ElasticsearchClient, Logger } from '@kbn/core/server'; import { benchmarkScoreMapping } from './benchmark_score_mapping'; import { latestFindingsMapping } from './latest_findings_mapping'; import { - LATEST_FINDINGS_INDEX_PATTERN, + LATEST_FINDINGS_INDEX_DEFAULT_NS, LATEST_FINDINGS_INDEX_NAME, - BENCHMARK_SCORE_INDEX_PATTERN, + BENCHMARK_SCORE_INDEX_DEFAULT_NS, BENCHMARK_SCORE_INDEX_NAME, } from '../../common/constants'; @@ -25,14 +25,14 @@ export const initializeCspTransformsIndices = async ( createIndexIfNotExists( esClient, LATEST_FINDINGS_INDEX_NAME, - LATEST_FINDINGS_INDEX_PATTERN, + LATEST_FINDINGS_INDEX_DEFAULT_NS, latestFindingsMapping, logger ), createIndexIfNotExists( esClient, BENCHMARK_SCORE_INDEX_NAME, - BENCHMARK_SCORE_INDEX_PATTERN, + BENCHMARK_SCORE_INDEX_DEFAULT_NS, benchmarkScoreMapping, logger ), @@ -41,7 +41,7 @@ export const initializeCspTransformsIndices = async ( export const createIndexIfNotExists = async ( esClient: ElasticsearchClient, - indexName: string, + indexTemplateName: string, indexPattern: string, mappings: MappingTypeMapping, logger: Logger @@ -53,7 +53,7 @@ export const createIndexIfNotExists = async ( if (!isLatestIndexExists) { await esClient.indices.putIndexTemplate({ - name: indexName, + name: indexTemplateName, index_patterns: indexPattern, template: { mappings }, priority: 500, @@ -65,7 +65,7 @@ export const createIndexIfNotExists = async ( } } catch (err) { const error = transformError(err); - logger.error(`Failed to create ${LATEST_FINDINGS_INDEX_PATTERN}`); + logger.error(`Failed to create the index template: ${indexTemplateName}`); logger.error(error.message); } }; diff --git a/x-pack/plugins/cloud_security_posture/server/create_indices/latest_findings_mapping.ts b/x-pack/plugins/cloud_security_posture/server/create_indices/latest_findings_mapping.ts index 4392cc5a47d30..80c35083e09a4 100644 --- a/x-pack/plugins/cloud_security_posture/server/create_indices/latest_findings_mapping.ts +++ b/x-pack/plugins/cloud_security_posture/server/create_indices/latest_findings_mapping.ts @@ -103,6 +103,24 @@ export const latestFindingsMapping: MappingTypeMapping = { }, }, }, + section: { + type: 'text', + fields: { + keyword: { + ignore_above: 1024, + type: 'keyword', + }, + }, + }, + }, + }, + cluster_id: { + type: 'text', + fields: { + keyword: { + ignore_above: 1024, + type: 'keyword', + }, }, }, }, diff --git a/x-pack/plugins/cloud_security_posture/server/create_transforms/benchmark_score_transform.ts b/x-pack/plugins/cloud_security_posture/server/create_transforms/benchmark_score_transform.ts index 8837fea0fa183..19528d42f09af 100644 --- a/x-pack/plugins/cloud_security_posture/server/create_transforms/benchmark_score_transform.ts +++ b/x-pack/plugins/cloud_security_posture/server/create_transforms/benchmark_score_transform.ts @@ -7,18 +7,18 @@ import type { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; import { - LATEST_FINDINGS_INDEX_PATTERN, - BENCHMARK_SCORE_INDEX_PATTERN, + LATEST_FINDINGS_INDEX_DEFAULT_NS, + BENCHMARK_SCORE_INDEX_DEFAULT_NS, } from '../../common/constants'; export const benchmarkScoreTransform: TransformPutTransformRequest = { transform_id: 'cloud_security_posture.score-default-0.0.1', description: 'Calculate latest findings score', source: { - index: LATEST_FINDINGS_INDEX_PATTERN, + index: LATEST_FINDINGS_INDEX_DEFAULT_NS, }, dest: { - index: BENCHMARK_SCORE_INDEX_PATTERN, + index: BENCHMARK_SCORE_INDEX_DEFAULT_NS, }, frequency: '30m', sync: { diff --git a/x-pack/plugins/cloud_security_posture/server/create_transforms/latest_findings_transform.ts b/x-pack/plugins/cloud_security_posture/server/create_transforms/latest_findings_transform.ts index fc042149f3193..fb8027a7675d2 100644 --- a/x-pack/plugins/cloud_security_posture/server/create_transforms/latest_findings_transform.ts +++ b/x-pack/plugins/cloud_security_posture/server/create_transforms/latest_findings_transform.ts @@ -5,7 +5,7 @@ * 2.0. */ import type { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; -import { FINDINGS_INDEX_PATTERN, LATEST_FINDINGS_INDEX_PATTERN } from '../../common/constants'; +import { FINDINGS_INDEX_PATTERN, LATEST_FINDINGS_INDEX_DEFAULT_NS } from '../../common/constants'; export const latestFindingsTransform: TransformPutTransformRequest = { transform_id: 'cloud_security_posture.findings_latest-default-0.0.1', @@ -14,7 +14,7 @@ export const latestFindingsTransform: TransformPutTransformRequest = { index: FINDINGS_INDEX_PATTERN, }, dest: { - index: LATEST_FINDINGS_INDEX_PATTERN, + index: LATEST_FINDINGS_INDEX_DEFAULT_NS, }, frequency: '5m', sync: { diff --git a/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.test.ts b/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.test.ts index 8872c359fc770..82d831c362fbb 100644 --- a/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.test.ts @@ -8,7 +8,7 @@ import { loggingSystemMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; import { SavedObjectsClientContract, SavedObjectsFindResponse } from '@kbn/core/server'; import { createPackagePolicyMock } from '@kbn/fleet-plugin/common/mocks'; -import { CIS_KUBERNETES_PACKAGE_NAME } from '../../common/constants'; +import { CLOUD_SECURITY_POSTURE_PACKAGE_NAME } from '../../common/constants'; import { onPackagePolicyPostCreateCallback } from './fleet_integration'; describe('create CSP rules with post package create callback', () => { @@ -40,7 +40,7 @@ describe('create CSP rules with post package create callback', () => { }); it('should create stateful rules based on rule template', async () => { const mockPackagePolicy = createPackagePolicyMock(); - mockPackagePolicy.package!.name = CIS_KUBERNETES_PACKAGE_NAME; + mockPackagePolicy.package!.name = CLOUD_SECURITY_POSTURE_PACKAGE_NAME; mockSoClient.find.mockResolvedValueOnce({ saved_objects: [ { @@ -66,6 +66,24 @@ describe('create CSP rules with post package create callback', () => { ]); }); + it('validate that all rules templates are fetched', async () => { + const mockPackagePolicy = createPackagePolicyMock(); + mockPackagePolicy.package!.name = CLOUD_SECURITY_POSTURE_PACKAGE_NAME; + mockSoClient.find.mockResolvedValueOnce({ + saved_objects: [ + { + type: 'csp-rule-template', + id: 'csp_rule_template-41308bcdaaf665761478bb6f0d745a5c', + attributes: { ...ruleAttributes }, + }, + ], + pit_id: undefined, + } as unknown as SavedObjectsFindResponse); + await onPackagePolicyPostCreateCallback(logger, mockPackagePolicy, mockSoClient); + + expect(mockSoClient.find.mock.calls[0][0]).toMatchObject({ perPage: 10000 }); + }); + it('should not create rules when the package policy is not csp package', async () => { const mockPackagePolicy = createPackagePolicyMock(); mockPackagePolicy.package!.name = 'not_csp_package'; diff --git a/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.ts b/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.ts index 4693a3e55337b..9404985ce077f 100644 --- a/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.ts +++ b/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.ts @@ -17,8 +17,11 @@ import { cloudSecurityPostureRuleTemplateSavedObjectType, CloudSecurityPostureRuleTemplateSchema, } from '../../common/schemas/csp_rule_template'; -import { CIS_KUBERNETES_PACKAGE_NAME } from '../../common/constants'; -import { CspRuleSchema, cspRuleAssetSavedObjectType } from '../../common/schemas/csp_rule'; +import { + CLOUD_SECURITY_POSTURE_PACKAGE_NAME, + cspRuleAssetSavedObjectType, +} from '../../common/constants'; +import { CspRuleSchema } from '../../common/schemas/csp_rule'; type ArrayElement = ArrayType extends ReadonlyArray< infer ElementType @@ -29,7 +32,7 @@ type ArrayElement = ArrayType extends Read const isCspPackagePolicy = ( packagePolicy: T ): boolean => { - return packagePolicy.package?.name === CIS_KUBERNETES_PACKAGE_NAME; + return packagePolicy.package?.name === CLOUD_SECURITY_POSTURE_PACKAGE_NAME; }; /** @@ -46,7 +49,10 @@ export const onPackagePolicyPostCreateCallback = async ( } // Create csp-rules from the generic asset const existingRuleTemplates: SavedObjectsFindResponse = - await savedObjectsClient.find({ type: cloudSecurityPostureRuleTemplateSavedObjectType }); + await savedObjectsClient.find({ + type: cloudSecurityPostureRuleTemplateSavedObjectType, + perPage: 10000, + }); if (existingRuleTemplates.total === 0) { return; diff --git a/x-pack/plugins/cloud_security_posture/server/plugin.test.ts b/x-pack/plugins/cloud_security_posture/server/plugin.test.ts index 79ff710fd5c2c..43dc550a677a9 100644 --- a/x-pack/plugins/cloud_security_posture/server/plugin.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/plugin.test.ts @@ -24,7 +24,7 @@ import { FleetStartContract, PostPackagePolicyPostCreateCallback, } from '@kbn/fleet-plugin/server'; -import { CIS_KUBERNETES_PACKAGE_NAME } from '../common/constants'; +import { CLOUD_SECURITY_POSTURE_PACKAGE_NAME } from '../common/constants'; import Chance from 'chance'; import type { AwaitedProperties } from '@kbn/utility-types'; import type { DeeplyMockedKeys } from '@kbn/utility-types/jest'; @@ -131,7 +131,7 @@ describe('Cloud Security Posture Plugin', () => { ); const packageMock = createPackagePolicyMock(); - packageMock.package!.name = CIS_KUBERNETES_PACKAGE_NAME; + packageMock.package!.name = CLOUD_SECURITY_POSTURE_PACKAGE_NAME; const packagePolicyPostCreateCallbacks: PostPackagePolicyPostCreateCallback[] = []; fleetMock.registerExternalCallback.mockImplementation((...args) => { diff --git a/x-pack/plugins/cloud_security_posture/server/plugin.ts b/x-pack/plugins/cloud_security_posture/server/plugin.ts index 5647a9adcd0cc..d9fdea9488036 100755 --- a/x-pack/plugins/cloud_security_posture/server/plugin.ts +++ b/x-pack/plugins/cloud_security_posture/server/plugin.ts @@ -33,7 +33,7 @@ import { onPackagePolicyPostCreateCallback, onPackagePolicyDeleteCallback, } from './fleet_integration/fleet_integration'; -import { CIS_KUBERNETES_PACKAGE_NAME } from '../common/constants'; +import { CLOUD_SECURITY_POSTURE_PACKAGE_NAME } from '../common/constants'; export interface CspAppContext { logger: Logger; @@ -84,7 +84,7 @@ export class CspPlugin plugins.fleet.fleetSetupCompleted().then(async () => { const packageInfo = await plugins.fleet.packageService.asInternalUser.getInstallation( - CIS_KUBERNETES_PACKAGE_NAME + CLOUD_SECURITY_POSTURE_PACKAGE_NAME ); // If package is installed we want to make sure all needed assets are installed @@ -100,7 +100,7 @@ export class CspPlugin context: RequestHandlerContext, _: KibanaRequest ): Promise => { - if (packagePolicy.package?.name === CIS_KUBERNETES_PACKAGE_NAME) { + if (packagePolicy.package?.name === CLOUD_SECURITY_POSTURE_PACKAGE_NAME) { await this.initialize(core); const soClient = (await context.core).savedObjects.client; await onPackagePolicyPostCreateCallback(this.logger, packagePolicy, soClient); @@ -114,7 +114,7 @@ export class CspPlugin 'postPackagePolicyDelete', async (deletedPackagePolicies: DeepReadonly) => { for (const deletedPackagePolicy of deletedPackagePolicies) { - if (deletedPackagePolicy.package?.name === CIS_KUBERNETES_PACKAGE_NAME) { + if (deletedPackagePolicy.package?.name === CLOUD_SECURITY_POSTURE_PACKAGE_NAME) { await onPackagePolicyDeleteCallback( this.logger, deletedPackagePolicy, diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts index 4ed7bdb000344..1bcf5196d1d3d 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts @@ -23,12 +23,13 @@ import { import { defineGetBenchmarksRoute, PACKAGE_POLICY_SAVED_OBJECT_TYPE, - getPackagePolicies, + getCspPackagePolicies, getAgentPolicies, createBenchmarkEntry, + addPackagePolicyCspRules, } from './benchmarks'; -import { SavedObjectsClientContract } from '@kbn/core/server'; +import { SavedObjectsClientContract, SavedObjectsFindResponse } from '@kbn/core/server'; import { createMockAgentPolicyService, createPackagePolicyServiceMock, @@ -221,7 +222,7 @@ describe('benchmarks API', () => { it('should format request by package name', async () => { const mockPackagePolicyService = createPackagePolicyServiceMock(); - await getPackagePolicies(mockSoClient, mockPackagePolicyService, 'myPackage', { + await getCspPackagePolicies(mockSoClient, mockPackagePolicyService, 'myPackage', { page: 1, per_page: 100, sort_order: 'desc', @@ -239,7 +240,7 @@ describe('benchmarks API', () => { it('should build sort request by `sort_field` and default `sort_order`', async () => { const mockAgentPolicyService = createPackagePolicyServiceMock(); - await getPackagePolicies(mockSoClient, mockAgentPolicyService, 'myPackage', { + await getCspPackagePolicies(mockSoClient, mockAgentPolicyService, 'myPackage', { page: 1, per_page: 100, sort_field: 'package_policy.name', @@ -260,7 +261,7 @@ describe('benchmarks API', () => { it('should build sort request by `sort_field` and asc `sort_order`', async () => { const mockAgentPolicyService = createPackagePolicyServiceMock(); - await getPackagePolicies(mockSoClient, mockAgentPolicyService, 'myPackage', { + await getCspPackagePolicies(mockSoClient, mockAgentPolicyService, 'myPackage', { page: 1, per_page: 100, sort_field: 'package_policy.name', @@ -282,7 +283,7 @@ describe('benchmarks API', () => { it('should format request by benchmark_name', async () => { const mockAgentPolicyService = createPackagePolicyServiceMock(); - await getPackagePolicies(mockSoClient, mockAgentPolicyService, 'myPackage', { + await getCspPackagePolicies(mockSoClient, mockAgentPolicyService, 'myPackage', { page: 1, per_page: 100, sort_order: 'desc', @@ -322,6 +323,32 @@ describe('benchmarks API', () => { }); }); + describe('test addPackagePolicyCspRules', () => { + it('should filter enabled rules', async () => { + const packagePolicy = createPackagePolicyMock(); + mockSoClient.find.mockResolvedValueOnce({ + aggregations: { enabled_status: { doc_count: 2 } }, + page: 1, + per_page: 10000, + total: 3, + saved_objects: [ + { + type: 'csp_rule', + id: '0af387d0-c933-11ec-b6c8-4f8afc058cc3', + }, + ], + } as unknown as SavedObjectsFindResponse); + + const cspRulesStatus = await addPackagePolicyCspRules(mockSoClient, packagePolicy); + + expect(cspRulesStatus).toEqual({ + all: 3, + enabled: 2, + disabled: 1, + }); + }); + }); + describe('test createBenchmarkEntry', () => { it('should build benchmark entry agent policy and package policy', async () => { const packagePolicy = createPackagePolicyMock(); @@ -329,9 +356,18 @@ describe('benchmarks API', () => { // @ts-expect-error agentPolicy.agents = 3; - const enrichAgentPolicy = await createBenchmarkEntry(agentPolicy, packagePolicy); + const cspRulesStatus = { + all: 100, + enabled: 52, + disabled: 48, + }; + const enrichAgentPolicy = await createBenchmarkEntry( + agentPolicy, + packagePolicy, + cspRulesStatus + ); - expect(enrichAgentPolicy).toMatchObject({ + expect(enrichAgentPolicy).toEqual({ package_policy: { id: 'c6d16e42-c32d-4dce-8a88-113cfe276ad1', name: 'endpoint-1', @@ -348,6 +384,11 @@ describe('benchmarks API', () => { }, }, agent_policy: { id: 'some-uuid1', name: 'Test Policy', agents: 3 }, + rules: { + all: 100, + disabled: 48, + enabled: 52, + }, }); }); }); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts index 2112701f42d4a..d19a86d5b5c27 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts @@ -6,7 +6,7 @@ */ import { uniq, map } from 'lodash'; -import type { SavedObjectsClientContract } from '@kbn/core/server'; +import type { SavedObjectsClientContract, SavedObjectsFindResponse } from '@kbn/core/server'; import { transformError } from '@kbn/securitysolution-es-utils'; import type { PackagePolicyServiceInterface, @@ -19,14 +19,19 @@ import type { AgentPolicy, ListResult, } from '@kbn/fleet-plugin/common'; -import { BENCHMARKS_ROUTE_PATH, CIS_KUBERNETES_PACKAGE_NAME } from '../../../common/constants'; +import { CspRuleSchema } from '../../../common/schemas/csp_rule'; +import { + BENCHMARKS_ROUTE_PATH, + CLOUD_SECURITY_POSTURE_PACKAGE_NAME, + cspRuleAssetSavedObjectType, +} from '../../../common/constants'; import { BENCHMARK_PACKAGE_POLICY_PREFIX, benchmarksInputSchema, BenchmarksQuerySchema, } from '../../../common/schemas/benchmark'; import { CspAppContext } from '../../plugin'; -import type { Benchmark } from '../../../common/types'; +import type { Benchmark, CspRulesStatus } from '../../../common/types'; import { isNonNullable } from '../../../common/utils/helpers'; import { CspRouter } from '../../types'; @@ -41,7 +46,7 @@ const getPackageNameQuery = (packageName: string, benchmarkFilter?: string): str return kquery; }; -export const getPackagePolicies = ( +export const getCspPackagePolicies = ( soClient: SavedObjectsClientContract, packagePolicyService: PackagePolicyServiceInterface, packageName: string, @@ -91,10 +96,49 @@ const addRunningAgentToAgentPolicy = async ( ) ); }; +export interface RulesStatusAggregation { + enabled_status: { + doc_count: number; + }; +} +export const getCspRulesStatus = ( + soClient: SavedObjectsClientContract, + packagePolicy: PackagePolicy +): Promise> => { + const cspRules = soClient.find({ + type: cspRuleAssetSavedObjectType, + filter: `${cspRuleAssetSavedObjectType}.attributes.package_policy_id: ${packagePolicy.id} AND ${cspRuleAssetSavedObjectType}.attributes.policy_id: ${packagePolicy.policy_id}`, + aggs: { + enabled_status: { + filter: { + term: { + [`${cspRuleAssetSavedObjectType}.attributes.enabled`]: true, + }, + }, + }, + }, + perPage: 0, + }); + return cspRules; +}; + +export const addPackagePolicyCspRules = async ( + soClient: SavedObjectsClientContract, + packagePolicy: PackagePolicy +): Promise => { + const rules = await getCspRulesStatus(soClient, packagePolicy); + const packagePolicyRules = { + all: rules.total, + enabled: rules.aggregations?.enabled_status.doc_count || 0, + disabled: rules.total - (rules.aggregations?.enabled_status.doc_count || 0), + }; + return packagePolicyRules; +}; export const createBenchmarkEntry = ( agentPolicy: GetAgentPoliciesResponseItem, - packagePolicy: PackagePolicy + packagePolicy: PackagePolicy, + cspRulesStatus: CspRulesStatus ): Benchmark => ({ package_policy: { id: packagePolicy.id, @@ -118,26 +162,33 @@ export const createBenchmarkEntry = ( name: agentPolicy.name, agents: agentPolicy.agents, }, + rules: cspRulesStatus, }); const createBenchmarks = ( + soClient: SavedObjectsClientContract, agentPolicies: GetAgentPoliciesResponseItem[], - packagePolicies: PackagePolicy[] -): Benchmark[] => - packagePolicies.flatMap((packagePolicy) => { - return agentPolicies - .map((agentPolicy) => { - const agentPkgPolicies = agentPolicy.package_policies as string[]; - const isExistsOnAgent = agentPkgPolicies.find( - (pkgPolicy) => pkgPolicy === packagePolicy.id - ); - if (isExistsOnAgent) { - return createBenchmarkEntry(agentPolicy, packagePolicy); - } - return; - }) - .filter(isNonNullable); - }); + cspPackagePolicies: PackagePolicy[] +): Promise => { + const cspPackagePoliciesMap = new Map( + cspPackagePolicies.map((packagePolicy) => [packagePolicy.id, packagePolicy]) + ); + return Promise.all( + agentPolicies.flatMap((agentPolicy) => { + const cspPackagesOnAgent = agentPolicy.package_policies + .map((pckPolicyId) => { + if (typeof pckPolicyId === 'string') return cspPackagePoliciesMap.get(pckPolicyId); + }) + .filter(isNonNullable); + const benchmarks = cspPackagesOnAgent.map(async (cspPackage) => { + const cspRulesStatus = await addPackagePolicyCspRules(soClient, cspPackage); + const benchmark = createBenchmarkEntry(agentPolicy, cspPackage, cspRulesStatus); + return benchmark; + }); + return benchmarks; + }) + ); +}; export const defineGetBenchmarksRoute = (router: CspRouter, cspContext: CspAppContext): void => router.get( @@ -162,24 +213,29 @@ export const defineGetBenchmarksRoute = (router: CspRouter, cspContext: CspAppCo throw new Error(`Failed to get Fleet services`); } - const packagePolicies = await getPackagePolicies( + const cspPackagePolicies = await getCspPackagePolicies( soClient, packagePolicyService, - CIS_KUBERNETES_PACKAGE_NAME, + CLOUD_SECURITY_POSTURE_PACKAGE_NAME, query ); const agentPolicies = await getAgentPolicies( soClient, - packagePolicies.items, + cspPackagePolicies.items, agentPolicyService ); + const enrichAgentPolicies = await addRunningAgentToAgentPolicy(agentService, agentPolicies); - const benchmarks = createBenchmarks(enrichAgentPolicies, packagePolicies.items); + const benchmarks = await createBenchmarks( + soClient, + enrichAgentPolicies, + cspPackagePolicies.items + ); return response.ok({ body: { - ...packagePolicies, + ...cspPackagePolicies, items: benchmarks, }, }); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts index d1c0d7c0eb014..1a95737df0ad9 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts @@ -8,9 +8,9 @@ import { transformError } from '@kbn/securitysolution-es-utils'; import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import type { ComplianceDashboardData } from '../../../common/types'; -import { LATEST_FINDINGS_INDEX_PATTERN, STATS_ROUTE_PATH } from '../../../common/constants'; +import { LATEST_FINDINGS_INDEX_DEFAULT_NS, STATS_ROUTE_PATH } from '../../../common/constants'; import { CspAppContext } from '../../plugin'; -import { getResourcesTypes } from './get_resources_types'; +import { getGroupedFindingsEvaluation } from './get_grouped_findings_evaluation'; import { ClusterWithoutTrend, getClusters } from './get_clusters'; import { getStats } from './get_stats'; import { CspRouter } from '../../types'; @@ -47,7 +47,7 @@ export const defineGetComplianceDashboardRoute = ( const esClient = (await context.core).elasticsearch.client.asCurrentUser; const { id: pitId } = await esClient.openPointInTime({ - index: LATEST_FINDINGS_INDEX_PATTERN, + index: LATEST_FINDINGS_INDEX_DEFAULT_NS, keep_alive: '30s', }); @@ -55,12 +55,14 @@ export const defineGetComplianceDashboardRoute = ( match_all: {}, }; - const [stats, resourcesTypes, clustersWithoutTrends, trends] = await Promise.all([ - getStats(esClient, query, pitId), - getResourcesTypes(esClient, query, pitId), - getClusters(esClient, query, pitId), - getTrends(esClient), - ]); + const [stats, groupedFindingsEvaluation, clustersWithoutTrends, trends] = await Promise.all( + [ + getStats(esClient, query, pitId), + getGroupedFindingsEvaluation(esClient, query, pitId), + getClusters(esClient, query, pitId), + getTrends(esClient), + ] + ); // Try closing the PIT, if it fails we can safely ignore the error since it closes itself after the keep alive // ends. Not waiting on the promise returned from the `closePointInTime` call to avoid delaying the request @@ -73,7 +75,7 @@ export const defineGetComplianceDashboardRoute = ( const body: ComplianceDashboardData = { stats, - resourcesTypes, + groupedFindingsEvaluation, clusters, trend, }; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.test.ts index 1630d4fe3537f..a24e60dd7be8f 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.test.ts @@ -66,7 +66,7 @@ describe('getClustersFromAggs', () => { totalPassed: 6, postureScore: 50.0, }, - resourcesTypes: [ + groupedFindingsEvaluation: [ { name: 'foo_type', totalFindings: 6, diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts index 4a4afda9a12b2..239511f71fea3 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts @@ -12,14 +12,17 @@ import type { SearchRequest, } from '@elastic/elasticsearch/lib/api/types'; import { Cluster } from '../../../common/types'; -import { getResourceTypeFromAggs, resourceTypeAggQuery } from './get_resources_types'; -import type { ResourceTypeQueryResult } from './get_resources_types'; +import { + getFailedFindingsFromAggs, + failedFindingsAggQuery, +} from './get_grouped_findings_evaluation'; +import type { FailedFindingsQueryResult } from './get_grouped_findings_evaluation'; import { findingsEvaluationAggsQuery, getStatsFromFindingsEvaluationsAggs } from './get_stats'; import { KeyDocCount } from './compliance_dashboard'; type UnixEpochTime = number; -export interface ClusterBucket extends ResourceTypeQueryResult, KeyDocCount { +export interface ClusterBucket extends FailedFindingsQueryResult, KeyDocCount { failed_findings: { doc_count: number; }; @@ -59,7 +62,7 @@ export const getClustersQuery = (query: QueryDslQueryContainer, pitId: string): }, }, }, - ...resourceTypeAggQuery, + ...failedFindingsAggQuery, ...findingsEvaluationAggsQuery, }, }, @@ -92,12 +95,12 @@ export const getClustersFromAggs = (clusters: ClusterBucket[]): ClusterWithoutTr const resourcesTypesAggs = cluster.aggs_by_resource_type.buckets; if (!Array.isArray(resourcesTypesAggs)) throw new Error('missing aggs by resource type per cluster'); - const resourcesTypes = getResourceTypeFromAggs(resourcesTypesAggs); + const groupedFindingsEvaluation = getFailedFindingsFromAggs(resourcesTypesAggs); return { meta, stats, - resourcesTypes, + groupedFindingsEvaluation, }; }); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_grouped_findings_evaluation.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_grouped_findings_evaluation.test.ts new file mode 100644 index 0000000000000..46c73c4250157 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_grouped_findings_evaluation.test.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 { getFailedFindingsFromAggs, FailedFindingsBucket } from './get_grouped_findings_evaluation'; + +const resourceTypeBuckets: FailedFindingsBucket[] = [ + { + key: 'foo_type', + doc_count: 41, + failed_findings: { + doc_count: 30, + }, + passed_findings: { + doc_count: 11, + }, + }, + { + key: 'boo_type', + doc_count: 11, + failed_findings: { + doc_count: 5, + }, + passed_findings: { + doc_count: 6, + }, + }, +]; + +describe('getFailedFindingsFromAggs', () => { + it('should return value matching ComplianceDashboardData["resourcesTypes"]', async () => { + const resourceTypes = getFailedFindingsFromAggs(resourceTypeBuckets); + expect(resourceTypes).toEqual([ + { + name: 'foo_type', + totalFindings: 41, + totalFailed: 30, + totalPassed: 11, + }, + { + name: 'boo_type', + totalFindings: 11, + totalFailed: 5, + totalPassed: 6, + }, + ]); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_grouped_findings_evaluation.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_grouped_findings_evaluation.ts new file mode 100644 index 0000000000000..66e5e8e271a26 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_grouped_findings_evaluation.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient } from '@kbn/core/server'; +import type { + AggregationsMultiBucketAggregateBase as Aggregation, + QueryDslQueryContainer, + SearchRequest, +} from '@elastic/elasticsearch/lib/api/types'; +import type { ComplianceDashboardData } from '../../../common/types'; +import { KeyDocCount } from './compliance_dashboard'; + +export interface FailedFindingsQueryResult { + aggs_by_resource_type: Aggregation; +} + +export interface FailedFindingsBucket extends KeyDocCount { + failed_findings: { + doc_count: number; + }; + passed_findings: { + doc_count: number; + }; +} + +export const failedFindingsAggQuery = { + aggs_by_resource_type: { + terms: { + field: 'rule.section.keyword', + }, + aggs: { + failed_findings: { + filter: { term: { 'result.evaluation.keyword': 'failed' } }, + }, + passed_findings: { + filter: { term: { 'result.evaluation.keyword': 'passed' } }, + }, + }, + }, +}; + +export const getRisksEsQuery = (query: QueryDslQueryContainer, pitId: string): SearchRequest => ({ + size: 0, + query, + aggs: failedFindingsAggQuery, + pit: { + id: pitId, + }, +}); + +export const getFailedFindingsFromAggs = ( + queryResult: FailedFindingsBucket[] +): ComplianceDashboardData['groupedFindingsEvaluation'] => + queryResult.map((bucket) => ({ + name: bucket.key, + totalFindings: bucket.doc_count, + totalFailed: bucket.failed_findings.doc_count || 0, + totalPassed: bucket.passed_findings.doc_count || 0, + })); + +export const getGroupedFindingsEvaluation = async ( + esClient: ElasticsearchClient, + query: QueryDslQueryContainer, + pitId: string +): Promise => { + const resourceTypesQueryResult = await esClient.search( + getRisksEsQuery(query, pitId) + ); + + const ruleSections = resourceTypesQueryResult.aggregations?.aggs_by_resource_type.buckets; + if (!Array.isArray(ruleSections)) { + return []; + } + + return getFailedFindingsFromAggs(ruleSections); +}; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.test.ts deleted file mode 100644 index 411290738f33e..0000000000000 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { getResourceTypeFromAggs, ResourceTypeBucket } from './get_resources_types'; - -const resourceTypeBuckets: ResourceTypeBucket[] = [ - { - key: 'foo_type', - doc_count: 41, - failed_findings: { - doc_count: 30, - }, - passed_findings: { - doc_count: 11, - }, - }, - { - key: 'boo_type', - doc_count: 11, - failed_findings: { - doc_count: 5, - }, - passed_findings: { - doc_count: 6, - }, - }, -]; - -describe('getResourceTypeFromAggs', () => { - it('should return value matching ComplianceDashboardData["resourcesTypes"]', async () => { - const resourceTypes = getResourceTypeFromAggs(resourceTypeBuckets); - expect(resourceTypes).toEqual([ - { - name: 'foo_type', - totalFindings: 41, - totalFailed: 30, - totalPassed: 11, - }, - { - name: 'boo_type', - totalFindings: 11, - totalFailed: 5, - totalPassed: 6, - }, - ]); - }); -}); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.ts deleted file mode 100644 index ecb5ee755fb64..0000000000000 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ElasticsearchClient } from '@kbn/core/server'; -import type { - AggregationsMultiBucketAggregateBase as Aggregation, - QueryDslQueryContainer, - SearchRequest, -} from '@elastic/elasticsearch/lib/api/types'; -import type { ComplianceDashboardData } from '../../../common/types'; -import { KeyDocCount } from './compliance_dashboard'; - -export interface ResourceTypeQueryResult { - aggs_by_resource_type: Aggregation; -} - -export interface ResourceTypeBucket extends KeyDocCount { - failed_findings: { - doc_count: number; - }; - passed_findings: { - doc_count: number; - }; -} - -export const resourceTypeAggQuery = { - aggs_by_resource_type: { - terms: { - field: 'type.keyword', - }, - aggs: { - failed_findings: { - filter: { term: { 'result.evaluation.keyword': 'failed' } }, - }, - passed_findings: { - filter: { term: { 'result.evaluation.keyword': 'passed' } }, - }, - }, - }, -}; - -export const getRisksEsQuery = (query: QueryDslQueryContainer, pitId: string): SearchRequest => ({ - size: 0, - query, - aggs: resourceTypeAggQuery, - pit: { - id: pitId, - }, -}); - -export const getResourceTypeFromAggs = ( - queryResult: ResourceTypeBucket[] -): ComplianceDashboardData['resourcesTypes'] => - queryResult.map((bucket) => ({ - name: bucket.key, - totalFindings: bucket.doc_count, - totalFailed: bucket.failed_findings.doc_count || 0, - totalPassed: bucket.passed_findings.doc_count || 0, - })); - -export const getResourcesTypes = async ( - esClient: ElasticsearchClient, - query: QueryDslQueryContainer, - pitId: string -): Promise => { - const resourceTypesQueryResult = await esClient.search( - getRisksEsQuery(query, pitId) - ); - - const resourceTypes = resourceTypesQueryResult.aggregations?.aggs_by_resource_type.buckets; - if (!Array.isArray(resourceTypes)) throw new Error('missing resources types buckets'); - - return getResourceTypeFromAggs(resourceTypes); -}; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_trends.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_trends.ts index eba14cb8215c2..08d8cd3553fe2 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_trends.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_trends.ts @@ -6,7 +6,7 @@ */ import { ElasticsearchClient } from '@kbn/core/server'; -import { BENCHMARK_SCORE_INDEX_PATTERN } from '../../../common/constants'; +import { BENCHMARK_SCORE_INDEX_DEFAULT_NS } from '../../../common/constants'; import { Stats } from '../../../common/types'; import { calculatePostureScore } from './get_stats'; @@ -26,7 +26,7 @@ export interface ScoreTrendDoc { } export const getTrendsQuery = () => ({ - index: BENCHMARK_SCORE_INDEX_PATTERN, + index: BENCHMARK_SCORE_INDEX_DEFAULT_NS, size: 5, sort: '@timestamp:desc', }); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.test.ts index 270466d2e3adf..27dcd3cee6703 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.test.ts @@ -26,7 +26,9 @@ import { CspAppContext } from '../../plugin'; import { createPackagePolicyMock } from '@kbn/fleet-plugin/common/mocks'; import { createPackagePolicyServiceMock } from '@kbn/fleet-plugin/server/mocks'; -import { cspRuleAssetSavedObjectType, CspRuleSchema } from '../../../common/schemas/csp_rule'; +import { cspRuleAssetSavedObjectType } from '../../../common/constants'; +import { CspRuleSchema } from '../../../common/schemas/csp_rule'; + import { ElasticsearchClient, KibanaRequest, diff --git a/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts index 7b9e63f8bd69e..21587394d51e8 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts @@ -20,9 +20,12 @@ import { PackagePolicy, PackagePolicyConfigRecord } from '@kbn/fleet-plugin/comm import { PackagePolicyServiceInterface } from '@kbn/fleet-plugin/server'; import { CspAppContext } from '../../plugin'; import { CspRulesConfigSchema } from '../../../common/schemas/csp_configuration'; -import { CspRuleSchema, cspRuleAssetSavedObjectType } from '../../../common/schemas/csp_rule'; -import { UPDATE_RULES_CONFIG_ROUTE_PATH } from '../../../common/constants'; -import { CIS_KUBERNETES_PACKAGE_NAME } from '../../../common/constants'; +import { CspRuleSchema } from '../../../common/schemas/csp_rule'; +import { + CLOUD_SECURITY_POSTURE_PACKAGE_NAME, + UPDATE_RULES_CONFIG_ROUTE_PATH, + cspRuleAssetSavedObjectType, +} from '../../../common/constants'; import { CspRouter } from '../../types'; export const getPackagePolicy = async ( @@ -36,26 +39,26 @@ export const getPackagePolicy = async ( if (!packagePolicies || !packagePolicies[0].version) { throw new Error(`package policy Id '${packagePolicyId}' is not exist`); } - if (packagePolicies[0].package?.name !== CIS_KUBERNETES_PACKAGE_NAME) { - // TODO: improve this validator to support any future CSP package - throw new Error(`Package Policy Id '${packagePolicyId}' is not CSP package`); + if (packagePolicies[0].package?.name !== CLOUD_SECURITY_POSTURE_PACKAGE_NAME) { + throw new Error( + `Package Policy Id '${packagePolicyId}' is not of type cloud security posture package` + ); } return packagePolicies![0]; }; -export const getCspRules = async ( +export const getCspRules = ( soClient: SavedObjectsClientContract, packagePolicy: PackagePolicy -) => { - const cspRules = await soClient.find({ +): Promise> => { + return soClient.find({ type: cspRuleAssetSavedObjectType, filter: `${cspRuleAssetSavedObjectType}.attributes.package_policy_id: ${packagePolicy.id} AND ${cspRuleAssetSavedObjectType}.attributes.policy_id: ${packagePolicy.policy_id}`, searchFields: ['name'], // TODO: research how to get all rules perPage: 10000, }); - return cspRules; }; export const createRulesConfig = ( @@ -105,7 +108,7 @@ export const defineUpdateRulesConfigRoute = (router: CspRouter, cspContext: CspA router.post( { path: UPDATE_RULES_CONFIG_ROUTE_PATH, - validate: { query: configurationUpdateInputSchema }, + validate: { body: configurationUpdateInputSchema }, }, async (context, request, response) => { if (!(await context.fleet).authz.fleet.all) { @@ -117,7 +120,7 @@ export const defineUpdateRulesConfigRoute = (router: CspRouter, cspContext: CspA const esClient = coreContext.elasticsearch.client.asCurrentUser; const soClient = coreContext.savedObjects.client; const packagePolicyService = cspContext.service.packagePolicyService; - const packagePolicyId = request.query.package_policy_id; + const packagePolicyId = request.body.package_policy_id; if (!packagePolicyService) { throw new Error(`Failed to get Fleet services`); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/findings/findings.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/findings/findings.test.ts deleted file mode 100644 index 35d359929af99..0000000000000 --- a/x-pack/plugins/cloud_security_posture/server/routes/findings/findings.test.ts +++ /dev/null @@ -1,505 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { - elasticsearchClientMock, - ElasticsearchClientMock, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '@kbn/core/server/elasticsearch/client/mocks'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { KibanaRequest } from '@kbn/core/server/http/router/request'; -import { httpServerMock, httpServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; -import { CspAppService } from '../../lib/csp_app_services'; -import { CspAppContext } from '../../plugin'; -import { - defineFindingsIndexRoute, - findingsInputSchema, - DEFAULT_FINDINGS_PER_PAGE, -} from './findings'; - -export const getMockCspContext = (mockEsClient: ElasticsearchClientMock): KibanaRequest => { - return { - core: { - elasticsearch: { - client: { asCurrentUser: mockEsClient }, - }, - }, - fleet: { authz: { fleet: { all: true } } }, - } as unknown as KibanaRequest; -}; - -describe('findings API', () => { - let logger: ReturnType; - - beforeEach(() => { - logger = loggingSystemMock.createLogger(); - }); - - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('validate the API route path', async () => { - const router = httpServiceMock.createRouter(); - const cspAppContextService = new CspAppService(); - - const cspContext: CspAppContext = { - logger, - service: cspAppContextService, - }; - defineFindingsIndexRoute(router, cspContext); - - const [config, _] = router.get.mock.calls[0]; - - expect(config.path).toEqual('/internal/cloud_security_posture/findings'); - }); - - it('should accept to a user with fleet.all privilege', async () => { - const router = httpServiceMock.createRouter(); - const cspAppContextService = new CspAppService(); - - const cspContext: CspAppContext = { - logger, - service: cspAppContextService, - }; - defineFindingsIndexRoute(router, cspContext); - const [_, handler] = router.get.mock.calls[0]; - - const mockContext = { - fleet: { authz: { fleet: { all: true } } }, - } as unknown as KibanaRequest; - - const mockResponse = httpServerMock.createResponseFactory(); - const mockRequest = httpServerMock.createKibanaRequest(); - const [context, req, res] = [mockContext, mockRequest, mockResponse]; - - await handler(context, req, res); - - expect(res.forbidden).toHaveBeenCalledTimes(0); - }); - - it('should reject to a user without fleet.all privilege', async () => { - const router = httpServiceMock.createRouter(); - const cspAppContextService = new CspAppService(); - - const cspContext: CspAppContext = { - logger, - service: cspAppContextService, - }; - defineFindingsIndexRoute(router, cspContext); - const [_, handler] = router.get.mock.calls[0]; - - const mockContext = { - fleet: { authz: { fleet: { all: false } } }, - } as unknown as KibanaRequest; - - const mockResponse = httpServerMock.createResponseFactory(); - const mockRequest = httpServerMock.createKibanaRequest(); - const [context, req, res] = [mockContext, mockRequest, mockResponse]; - - await handler(context, req, res); - - expect(res.forbidden).toHaveBeenCalledTimes(1); - }); - - describe('test input schema', () => { - it('expect to find default values', async () => { - const validatedQuery = findingsInputSchema.validate({}); - - expect(validatedQuery).toMatchObject({ - page: 1, - per_page: DEFAULT_FINDINGS_PER_PAGE, - sort_order: expect.stringMatching('desc'), - }); - }); - - it('should throw when page field is not a positive integer', async () => { - expect(() => { - findingsInputSchema.validate({ page: -2 }); - }).toThrow(); - }); - - it('should throw when per_page field is not a positive integer', async () => { - expect(() => { - findingsInputSchema.validate({ per_page: -2 }); - }).toThrow(); - }); - - it('should throw when latest_run is not a boolean', async () => { - expect(() => { - findingsInputSchema.validate({ latest_cycle: 'some string' }); // expects to get boolean - }).toThrow(); - }); - - it('should not throw when latest_run is a boolean', async () => { - expect(() => { - findingsInputSchema.validate({ latest_cycle: true }); - }).not.toThrow(); - }); - - it('should throw when sort_field is not string', async () => { - expect(() => { - findingsInputSchema.validate({ sort_field: true }); - }).toThrow(); - }); - - it('should not throw when sort_field is a string', async () => { - expect(() => { - findingsInputSchema.validate({ sort_field: 'field1' }); - }).not.toThrow(); - }); - - it('should throw when sort_order is not `asc` or `desc`', async () => { - expect(() => { - findingsInputSchema.validate({ sort_order: 'Other Direction' }); - }).toThrow(); - }); - - it('should not throw when `asc` is input for sort_order field', async () => { - expect(() => { - findingsInputSchema.validate({ sort_order: 'asc' }); - }).not.toThrow(); - }); - - it('should not throw when `desc` is input for sort_order field', async () => { - expect(() => { - findingsInputSchema.validate({ sort_order: 'desc' }); - }).not.toThrow(); - }); - - it('should throw when fields is not string', async () => { - expect(() => { - findingsInputSchema.validate({ fields: ['field1', 'field2'] }); - }).toThrow(); - }); - - it('should not throw when fields is a string', async () => { - expect(() => { - findingsInputSchema.validate({ sort_field: 'field1, field2' }); - }).not.toThrow(); - }); - }); - - describe('test query building', () => { - it('takes cycle_id and validate the filter was built right', async () => { - const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; - const router = httpServiceMock.createRouter(); - const cspAppContextService = new CspAppService(); - - const cspContext: CspAppContext = { - logger, - service: cspAppContextService, - }; - defineFindingsIndexRoute(router, cspContext); - - const [_, handler] = router.get.mock.calls[0]; - const mockContext = getMockCspContext(mockEsClient); - const mockResponse = httpServerMock.createResponseFactory(); - const mockRequest = httpServerMock.createKibanaRequest({ - query: { latest_cycle: true }, - }); - - mockEsClient.search.mockResolvedValueOnce( - // @ts-expect-error @elastic/elasticsearch Aggregate only allows unknown values - { - aggregations: { - group: { - buckets: [ - { - group_docs: { - hits: { - hits: [{ fields: { 'cycle_id.keyword': ['randomId1'] } }], - }, - }, - }, - ], - }, - }, - } - ); - - const [context, req, res] = [mockContext, mockRequest, mockResponse]; - - await handler(context, req, res); - - expect(mockEsClient.search).toHaveBeenCalledTimes(2); - - const handlerArgs = mockEsClient.search.mock.calls[1][0]; - - expect(handlerArgs).toMatchObject({ - query: { - bool: { - filter: [{ terms: { 'cycle_id.keyword': ['randomId1'] } }], - }, - }, - }); - }); - - it('validate that default sort is timestamp desc', async () => { - const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; - const router = httpServiceMock.createRouter(); - const cspAppContextService = new CspAppService(); - - const cspContext: CspAppContext = { - logger, - service: cspAppContextService, - }; - defineFindingsIndexRoute(router, cspContext); - - const [_, handler] = router.get.mock.calls[0]; - const mockContext = getMockCspContext(mockEsClient); - const mockResponse = httpServerMock.createResponseFactory(); - const mockRequest = httpServerMock.createKibanaRequest({ - query: { - sort_order: 'desc', - }, - }); - - const [context, req, res] = [mockContext, mockRequest, mockResponse]; - - await handler(context, req, res); - - const handlerArgs = mockEsClient.search.mock.calls[0][0]; - - expect(handlerArgs).toMatchObject({ - sort: [{ '@timestamp': { order: 'desc' } }], - }); - }); - - it('should build sort request by `sort_field` and `sort_order` - asc', async () => { - const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; - const router = httpServiceMock.createRouter(); - const cspAppContextService = new CspAppService(); - - const cspContext: CspAppContext = { - logger, - service: cspAppContextService, - }; - defineFindingsIndexRoute(router, cspContext); - - const [_, handler] = router.get.mock.calls[0]; - const mockContext = getMockCspContext(mockEsClient); - const mockResponse = httpServerMock.createResponseFactory(); - const mockRequest = httpServerMock.createKibanaRequest({ - query: { - sort_field: 'agent.id', - sort_order: 'asc', - }, - }); - - const [context, req, res] = [mockContext, mockRequest, mockResponse]; - - await handler(context, req, res); - - const handlerArgs = mockEsClient.search.mock.calls[0][0]; - - expect(handlerArgs).toMatchObject({ - sort: [{ 'agent.id': 'asc' }], - }); - }); - - it('should build sort request by `sort_field` and `sort_order` - desc', async () => { - const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; - const router = httpServiceMock.createRouter(); - const cspAppContextService = new CspAppService(); - - const cspContext: CspAppContext = { - logger, - service: cspAppContextService, - }; - defineFindingsIndexRoute(router, cspContext); - - const [_, handler] = router.get.mock.calls[0]; - const mockContext = getMockCspContext(mockEsClient); - const mockResponse = httpServerMock.createResponseFactory(); - const mockRequest = httpServerMock.createKibanaRequest({ - query: { - sort_field: 'agent.id', - sort_order: 'desc', - }, - }); - - const [context, req, res] = [mockContext, mockRequest, mockResponse]; - - await handler(context, req, res); - - const handlerArgs = mockEsClient.search.mock.calls[0][0]; - - expect(handlerArgs).toMatchObject({ - sort: [{ 'agent.id': 'desc' }], - }); - }); - - it('takes `page` number and `per_page` validate that the requested selected page was called', async () => { - const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; - const router = httpServiceMock.createRouter(); - const cspAppContextService = new CspAppService(); - - const cspContext: CspAppContext = { - logger, - service: cspAppContextService, - }; - defineFindingsIndexRoute(router, cspContext); - - const [_, handler] = router.get.mock.calls[0]; - const mockContext = getMockCspContext(mockEsClient); - const mockResponse = httpServerMock.createResponseFactory(); - const mockRequest = httpServerMock.createKibanaRequest({ - query: { - per_page: 10, - page: 3, - }, - }); - - const [context, req, res] = [mockContext, mockRequest, mockResponse]; - await handler(context, req, res); - - expect(mockEsClient.search).toHaveBeenCalledTimes(1); - const handlerArgs = mockEsClient.search.mock.calls[0][0]; - - expect(handlerArgs).toMatchObject({ - from: 20, - size: 10, - }); - }); - - it('should format request by fields filter', async () => { - const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; - const router = httpServiceMock.createRouter(); - const cspAppContextService = new CspAppService(); - - const cspContext: CspAppContext = { - logger, - service: cspAppContextService, - }; - defineFindingsIndexRoute(router, cspContext); - - const [_, handler] = router.get.mock.calls[0]; - - const mockContext = getMockCspContext(mockEsClient); - const mockResponse = httpServerMock.createResponseFactory(); - const mockRequest = httpServerMock.createKibanaRequest({ - query: { - fields: 'field1,field2,field3', - }, - }); - - const [context, req, res] = [mockContext, mockRequest, mockResponse]; - - await handler(context, req, res); - - const handlerArgs = mockEsClient.search.mock.calls[0][0]; - - expect(handlerArgs).toMatchObject({ - _source: ['field1', 'field2', 'field3'], - }); - }); - - it('takes dslQuery and validate the conversion to esQuery filter', async () => { - const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; - const router = httpServiceMock.createRouter(); - const cspAppContextService = new CspAppService(); - const cspContext: CspAppContext = { - logger, - service: cspAppContextService, - }; - - defineFindingsIndexRoute(router, cspContext); - - const [_, handler] = router.get.mock.calls[0]; - const mockContext = getMockCspContext(mockEsClient); - const mockResponse = httpServerMock.createResponseFactory(); - const mockRequest = httpServerMock.createKibanaRequest({ - query: { - kquery: 'result.evaluation.keyword:failed', - }, - }); - - const [context, req, res] = [mockContext, mockRequest, mockResponse]; - - await handler(context, req, res); - const handlerArgs = mockEsClient.search.mock.calls[0][0]; - - expect(handlerArgs).toMatchObject({ - query: { - bool: { - filter: [ - { - bool: { - minimum_should_match: 1, - should: [{ match: { 'result.evaluation.keyword': 'failed' } }], - }, - }, - ], - }, - }, - }); - }); - - it('takes dslQuery and latest_cycle filter validate the conversion to esQuery filter', async () => { - const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; - const router = httpServiceMock.createRouter(); - const cspAppContextService = new CspAppService(); - const cspContext: CspAppContext = { - logger, - service: cspAppContextService, - }; - - defineFindingsIndexRoute(router, cspContext); - const [_, handler] = router.get.mock.calls[0]; - - const mockContext = getMockCspContext(mockEsClient); - const mockResponse = httpServerMock.createResponseFactory(); - const mockRequest = httpServerMock.createKibanaRequest({ - query: { - kquery: 'result.evaluation.keyword:failed', - latest_cycle: true, - }, - }); - - mockEsClient.search.mockResolvedValueOnce( - // @ts-expect-error @elastic/elasticsearch Aggregate only allows unknown values - { - aggregations: { - group: { - buckets: [ - { - group_docs: { - hits: { - hits: [{ fields: { 'cycle_id.keyword': ['randomId1'] } }], - }, - }, - }, - ], - }, - }, - } - ); - - const [context, req, res] = [mockContext, mockRequest, mockResponse]; - - await handler(context, req, res); - - const handlerArgs = mockEsClient.search.mock.calls[1][0]; - // console.log(handlerArgs.query.bool); - expect(handlerArgs).toMatchObject({ - query: { - bool: { - filter: [ - { - bool: { - should: [{ match: { 'result.evaluation.keyword': 'failed' } }], - minimum_should_match: 1, - }, - }, - { terms: { 'cycle_id.keyword': ['randomId1'] } }, - ], - }, - }, - }); - }); - }); -}); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/findings/findings.ts b/x-pack/plugins/cloud_security_posture/server/routes/findings/findings.ts deleted file mode 100644 index 4c9851be902b3..0000000000000 --- a/x-pack/plugins/cloud_security_posture/server/routes/findings/findings.ts +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { Logger } from '@kbn/core/server'; -import { SearchRequest, QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; -import { QueryDslBoolQuery } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { schema as rt, TypeOf } from '@kbn/config-schema'; -import type { SortOrder } from '@elastic/elasticsearch/lib/api/types'; -import { transformError } from '@kbn/securitysolution-es-utils'; -import { getLatestCycleIds } from './get_latest_cycle_ids'; - -import { CSP_KUBEBEAT_INDEX_PATTERN, FINDINGS_ROUTE_PATH } from '../../../common/constants'; -import { CspAppContext } from '../../plugin'; -import { CspRouter } from '../../types'; - -type FindingsQuerySchema = TypeOf; - -export const DEFAULT_FINDINGS_PER_PAGE = 20; - -export interface FindingsOptions { - size: number; - from?: number; - page?: number; - sortField?: string; - sortOrder?: SortOrder; - fields?: string[]; -} - -const getPointerForFirstDoc = (page: number, perPage: number): number => - page <= 1 ? 0 : page * perPage - perPage; - -const getSort = (sortField: string | undefined, sortOrder: string) => - sortField - ? { sort: [{ [sortField]: sortOrder }] } - : { sort: [{ '@timestamp': { order: sortOrder } }] }; - -const getSearchFields = (fields: string | undefined) => - fields ? { _source: fields.split(',') } : {}; - -const getFindingsEsQuery = ( - query: QueryDslQueryContainer, - options: FindingsOptions -): SearchRequest => { - return { - index: CSP_KUBEBEAT_INDEX_PATTERN, - query, - ...options, - }; -}; - -const buildLatestCycleFilter = ( - filter: QueryDslQueryContainer[], - latestCycleIds?: string[] -): QueryDslQueryContainer[] => { - if (!!latestCycleIds) { - filter.push({ - terms: { 'cycle_id.keyword': latestCycleIds }, - }); - } - return filter; -}; - -const convertKqueryToElasticsearchQuery = ( - kquery: string | undefined, - logger: Logger -): QueryDslQueryContainer[] => { - let dslFilterQuery: QueryDslBoolQuery['filter']; - try { - dslFilterQuery = kquery ? toElasticsearchQuery(fromKueryExpression(kquery)) : []; - if (!Array.isArray(dslFilterQuery)) { - dslFilterQuery = [dslFilterQuery]; - } - } catch (err) { - logger.warn(`Invalid kuery syntax for the filter (${kquery}) error: ${err.message}`); - throw err; - } - return dslFilterQuery; -}; - -const buildQueryRequest = ( - kquery: string | undefined, - latestCycleIds: string[] | undefined, - logger: Logger -): QueryDslQueryContainer => { - const kqueryFilter = convertKqueryToElasticsearchQuery(kquery, logger); - const filter = buildLatestCycleFilter(kqueryFilter, latestCycleIds); - const query = { - bool: { - filter, - }, - }; - return query; -}; - -const buildOptionsRequest = (queryParams: FindingsQuerySchema): FindingsOptions => ({ - size: queryParams.per_page, - from: getPointerForFirstDoc(queryParams.page, queryParams.per_page), - ...getSort(queryParams.sort_field, queryParams.sort_order), - ...getSearchFields(queryParams.fields), -}); - -export const defineFindingsIndexRoute = (router: CspRouter, cspContext: CspAppContext): void => - router.get( - { - path: FINDINGS_ROUTE_PATH, - validate: { query: findingsInputSchema }, - }, - async (context, request, response) => { - if (!(await context.fleet).authz.fleet.all) { - return response.forbidden(); - } - - try { - const esClient = (await context.core).elasticsearch.client.asCurrentUser; - const options = buildOptionsRequest(request.query); - - const latestCycleIds = - request.query.latest_cycle === true - ? await getLatestCycleIds(esClient, cspContext.logger) - : undefined; - - if (request.query.latest_cycle === true && latestCycleIds === undefined) { - return response.ok({ body: [] }); - } - - const query = buildQueryRequest(request.query.kquery, latestCycleIds, cspContext.logger); - const esQuery = getFindingsEsQuery(query, options); - - const findings = await esClient.search(esQuery); - const hits = findings.hits.hits; - - return response.ok({ body: hits }); - } catch (err) { - const error = transformError(err); - cspContext.logger.error(`Failed to fetch Findings ${error.message}`); - return response.customError({ - body: { message: error.message }, - statusCode: error.statusCode, - }); - } - } - ); - -export const findingsInputSchema = rt.object({ - /** - * The page of objects to return - */ - page: rt.number({ defaultValue: 1, min: 1 }), - /** - * The number of objects to include in each page - */ - per_page: rt.number({ defaultValue: DEFAULT_FINDINGS_PER_PAGE, min: 0 }), - /** - * Boolean flag to indicate for receiving only the latest findings - */ - latest_cycle: rt.maybe(rt.boolean()), - /** - * The field to use for sorting the found objects. - */ - sort_field: rt.maybe(rt.string()), - /** - * The order to sort by - */ - sort_order: rt.oneOf([rt.literal('asc'), rt.literal('desc')], { defaultValue: 'desc' }), - /** - * The fields in the entity to return in the response - */ - fields: rt.maybe(rt.string()), - /** - * kql query - */ - kquery: rt.maybe(rt.string()), -}); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/findings/get_latest_cycle.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/findings/get_latest_cycle.test.ts deleted file mode 100644 index 38a67e95df4a7..0000000000000 --- a/x-pack/plugins/cloud_security_posture/server/routes/findings/get_latest_cycle.test.ts +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - elasticsearchClientMock, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '@kbn/core/server/elasticsearch/client/mocks'; -import { loggingSystemMock } from '@kbn/core/server/mocks'; -import { getLatestCycleIds } from './get_latest_cycle_ids'; - -const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; - -describe('get latest cycle ids', () => { - let logger: ReturnType; - - beforeEach(() => { - logger = loggingSystemMock.createLogger(); - jest.resetAllMocks(); - }); - - it('expect to throw when find empty bucket', async () => { - mockEsClient.search.mockResolvedValueOnce( - // @ts-expect-error @elastic/elasticsearch Aggregate only allows unknown values - { - aggregations: { - group: { - buckets: [{}], - }, - }, - } - ); - expect(getLatestCycleIds(mockEsClient, logger)).rejects.toThrow(); - }); - - it('expect to find 1 cycle id', async () => { - mockEsClient.search.mockResolvedValueOnce( - // @ts-expect-error @elastic/elasticsearch Aggregate only allows unknown values - { - aggregations: { - group: { - buckets: [ - { - group_docs: { - hits: { - hits: [{ fields: { 'cycle_id.keyword': ['randomId1'] } }], - }, - }, - }, - ], - }, - }, - } - ); - const response = await getLatestCycleIds(mockEsClient, logger); - expect(response).toEqual(expect.arrayContaining(['randomId1'])); - }); - - it('expect to find multiple cycle ids', async () => { - mockEsClient.search.mockResolvedValueOnce( - // @ts-expect-error @elastic/elasticsearch Aggregate only allows unknown values - { - aggregations: { - group: { - buckets: [ - { - group_docs: { - hits: { - hits: [{ fields: { 'cycle_id.keyword': ['randomId1'] } }], - }, - }, - }, - { - group_docs: { - hits: { - hits: [{ fields: { 'cycle_id.keyword': ['randomId2'] } }], - }, - }, - }, - { - group_docs: { - hits: { - hits: [{ fields: { 'cycle_id.keyword': ['randomId3'] } }], - }, - }, - }, - ], - }, - }, - } - ); - const response = await getLatestCycleIds(mockEsClient, logger); - expect(response).toEqual(expect.arrayContaining(['randomId1', 'randomId2', 'randomId3'])); - }); -}); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/findings/get_latest_cycle_ids.ts b/x-pack/plugins/cloud_security_posture/server/routes/findings/get_latest_cycle_ids.ts deleted file mode 100644 index 781a60727f596..0000000000000 --- a/x-pack/plugins/cloud_security_posture/server/routes/findings/get_latest_cycle_ids.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import type { Logger } from '@kbn/core/server'; -import { AggregationsFiltersAggregate, SearchRequest } from '@elastic/elasticsearch/lib/api/types'; -import type { ElasticsearchClient } from '@kbn/core/server'; -import { AGENT_LOGS_INDEX_PATTERN } from '../../../common/constants'; - -const getAgentLogsEsQuery = (): SearchRequest => ({ - index: AGENT_LOGS_INDEX_PATTERN, - size: 0, - query: { - bool: { - filter: [{ term: { 'status.keyword': 'end' } }], - }, - }, - aggs: { - group: { - terms: { field: 'agent.id.keyword' }, - aggs: { - group_docs: { - top_hits: { - size: 1, - sort: [{ '@timestamp': { order: 'desc' } }], - }, - }, - }, - }, - }, - fields: ['cycle_id.keyword', 'agent.id.keyword'], - _source: false, -}); - -const getCycleId = (v: any): string => v.group_docs.hits.hits?.[0]?.fields['cycle_id.keyword'][0]; - -export const getLatestCycleIds = async ( - esClient: ElasticsearchClient, - logger: Logger -): Promise => { - try { - const agentLogs = await esClient.search(getAgentLogsEsQuery()); - const aggregations = agentLogs.aggregations; - if (!aggregations) { - return; - } - const buckets = (aggregations.group as Record).buckets; - if (!Array.isArray(buckets)) { - return; - } - - return buckets.map(getCycleId); - } catch (err) { - logger.error('Failed to fetch cycle_ids'); - throw new Error('Failed to fetch cycle_ids'); - } -}; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/index.ts b/x-pack/plugins/cloud_security_posture/server/routes/index.ts index a0981e2a956cd..f5c500f24a0dc 100755 --- a/x-pack/plugins/cloud_security_posture/server/routes/index.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/index.ts @@ -7,14 +7,12 @@ import { defineGetComplianceDashboardRoute } from './compliance_dashboard/compliance_dashboard'; import { defineGetBenchmarksRoute } from './benchmarks/benchmarks'; -import { defineFindingsIndexRoute as defineGetFindingsIndexRoute } from './findings/findings'; import { defineUpdateRulesConfigRoute } from './configuration/update_rules_configuration'; import { CspAppContext } from '../plugin'; import { CspRouter } from '../types'; export function defineRoutes(router: CspRouter, cspContext: CspAppContext) { defineGetComplianceDashboardRoute(router, cspContext); - defineGetFindingsIndexRoute(router, cspContext); defineGetBenchmarksRoute(router, cspContext); defineUpdateRulesConfigRoute(router, cspContext); } diff --git a/x-pack/plugins/cloud_security_posture/server/saved_objects/csp_rule_type.ts b/x-pack/plugins/cloud_security_posture/server/saved_objects/csp_rule_type.ts index a6309f321328f..3afa68fdea228 100644 --- a/x-pack/plugins/cloud_security_posture/server/saved_objects/csp_rule_type.ts +++ b/x-pack/plugins/cloud_security_posture/server/saved_objects/csp_rule_type.ts @@ -7,11 +7,8 @@ import { i18n } from '@kbn/i18n'; import type { SavedObjectsType, SavedObjectsValidationMap } from '@kbn/core/server'; -import { - type CspRuleSchema, - cspRuleSchema, - cspRuleAssetSavedObjectType, -} from '../../common/schemas/csp_rule'; +import { cspRuleAssetSavedObjectType } from '../../common/constants'; +import { type CspRuleSchema, cspRuleSchema } from '../../common/schemas/csp_rule'; const validationMap: SavedObjectsValidationMap = { '1.0.0': cspRuleSchema, @@ -38,6 +35,14 @@ export const ruleAssetSavedObjectMappings: SavedObjectsType['mapp description: { type: 'text', }, + enabled: { + type: 'boolean', + fields: { + keyword: { + type: 'keyword', // sort + }, + }, + }, }, }; diff --git a/x-pack/plugins/data_enhanced/.storybook/main.js b/x-pack/plugins/data_enhanced/.storybook/main.js deleted file mode 100644 index 86b48c32f103e..0000000000000 --- a/x-pack/plugins/data_enhanced/.storybook/main.js +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -module.exports = require('@kbn/storybook').defaultConfig; diff --git a/x-pack/plugins/data_enhanced/README.md b/x-pack/plugins/data_enhanced/README.md deleted file mode 100644 index fba484261fea2..0000000000000 --- a/x-pack/plugins/data_enhanced/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# data_enhanced - -The `data_enhanced` plugin is the x-pack counterpart to the `src/plguins/data` plugin. - -It exists to provide services, or parts of services, which -enhance existing functionality from `src/plugins/data`. - -Currently, the `data_enhanced` plugin doesn't return any APIs which you can -consume directly, however it is possible that you are indirectly relying on the -enhanced functionality that it provides via the `data` plugin from `src/`. - -Here is the functionality it adds: - -## Search Sessions - -Search sessions are handy when you want to enable a user to run something asynchronously (for example, a dashboard over a long period of time), and then quickly restore the results at a later time. The Search Service transparently fetches results from the .async-search index, instead of running each request again. diff --git a/x-pack/plugins/data_enhanced/common/index.ts b/x-pack/plugins/data_enhanced/common/index.ts deleted file mode 100644 index 1fec1c76430eb..0000000000000 --- a/x-pack/plugins/data_enhanced/common/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ diff --git a/x-pack/plugins/data_enhanced/common/search/index.ts b/x-pack/plugins/data_enhanced/common/search/index.ts deleted file mode 100644 index 1fec1c76430eb..0000000000000 --- a/x-pack/plugins/data_enhanced/common/search/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ diff --git a/x-pack/plugins/data_enhanced/common/search/test_data/search_phase_execution_exception.json b/x-pack/plugins/data_enhanced/common/search/test_data/search_phase_execution_exception.json deleted file mode 100644 index b79a396445e3d..0000000000000 --- a/x-pack/plugins/data_enhanced/common/search/test_data/search_phase_execution_exception.json +++ /dev/null @@ -1,229 +0,0 @@ -{ - "error": { - "root_cause": [ - { - "type": "script_exception", - "reason": "compile error", - "script_stack": [ - "invalid", - "^---- HERE" - ], - "script": "invalid", - "lang": "painless", - "position": { - "offset": 0, - "start": 0, - "end": 7 - } - }, - { - "type": "script_exception", - "reason": "compile error", - "script_stack": [ - "invalid", - "^---- HERE" - ], - "script": "invalid", - "lang": "painless", - "position": { - "offset": 0, - "start": 0, - "end": 7 - } - }, - { - "type": "parse_exception", - "reason": "failed to parse date field [2021-01-19T12:2755.047Z] with format [strict_date_optional_time]: [failed to parse date field [2021-01-19T12:2755.047Z] with format [strict_date_optional_time]]" - }, - { - "type": "script_exception", - "reason": "compile error", - "script_stack": [ - "invalid", - "^---- HERE" - ], - "script": "invalid", - "lang": "painless", - "position": { - "offset": 0, - "start": 0, - "end": 7 - } - }, - { - "type": "script_exception", - "reason": "compile error", - "script_stack": [ - "invalid", - "^---- HERE" - ], - "script": "invalid", - "lang": "painless", - "position": { - "offset": 0, - "start": 0, - "end": 7 - } - }, - { - "type": "script_exception", - "reason": "compile error", - "script_stack": [ - "invalid", - "^---- HERE" - ], - "script": "invalid", - "lang": "painless", - "position": { - "offset": 0, - "start": 0, - "end": 7 - } - } - ], - "type": "search_phase_execution_exception", - "reason": "all shards failed", - "phase": "query", - "grouped": true, - "failed_shards": [ - { - "shard": 0, - "index": ".apm-agent-configuration", - "node": "DEfMVCg5R12TRG4CYIxUgQ", - "reason": { - "type": "script_exception", - "reason": "compile error", - "script_stack": [ - "invalid", - "^---- HERE" - ], - "script": "invalid", - "lang": "painless", - "position": { - "offset": 0, - "start": 0, - "end": 7 - }, - "caused_by": { - "type": "illegal_argument_exception", - "reason": "cannot resolve symbol [invalid]" - } - } - }, - { - "shard": 0, - "index": ".apm-custom-link", - "node": "DEfMVCg5R12TRG4CYIxUgQ", - "reason": { - "type": "script_exception", - "reason": "compile error", - "script_stack": [ - "invalid", - "^---- HERE" - ], - "script": "invalid", - "lang": "painless", - "position": { - "offset": 0, - "start": 0, - "end": 7 - }, - "caused_by": { - "type": "illegal_argument_exception", - "reason": "cannot resolve symbol [invalid]" - } - } - }, - { - "shard": 0, - "index": ".kibana-event-log-8.0.0-000001", - "node": "DEfMVCg5R12TRG4CYIxUgQ", - "reason": { - "type": "parse_exception", - "reason": "failed to parse date field [2021-01-19T12:2755.047Z] with format [strict_date_optional_time]: [failed to parse date field [2021-01-19T12:2755.047Z] with format [strict_date_optional_time]]", - "caused_by": { - "type": "illegal_argument_exception", - "reason": "failed to parse date field [2021-01-19T12:2755.047Z] with format [strict_date_optional_time]", - "caused_by": { - "type": "date_time_parse_exception", - "reason": "Text '2021-01-19T12:2755.047Z' could not be parsed, unparsed text found at index 16" - } - } - } - }, - { - "shard": 0, - "index": ".kibana_1", - "node": "DEfMVCg5R12TRG4CYIxUgQ", - "reason": { - "type": "script_exception", - "reason": "compile error", - "script_stack": [ - "invalid", - "^---- HERE" - ], - "script": "invalid", - "lang": "painless", - "position": { - "offset": 0, - "start": 0, - "end": 7 - }, - "caused_by": { - "type": "illegal_argument_exception", - "reason": "cannot resolve symbol [invalid]" - } - } - }, - { - "shard": 0, - "index": ".kibana_task_manager_1", - "node": "DEfMVCg5R12TRG4CYIxUgQ", - "reason": { - "type": "script_exception", - "reason": "compile error", - "script_stack": [ - "invalid", - "^---- HERE" - ], - "script": "invalid", - "lang": "painless", - "position": { - "offset": 0, - "start": 0, - "end": 7 - }, - "caused_by": { - "type": "illegal_argument_exception", - "reason": "cannot resolve symbol [invalid]" - } - } - }, - { - "shard": 0, - "index": ".security-7", - "node": "DEfMVCg5R12TRG4CYIxUgQ", - "reason": { - "type": "script_exception", - "reason": "compile error", - "script_stack": [ - "invalid", - "^---- HERE" - ], - "script": "invalid", - "lang": "painless", - "position": { - "offset": 0, - "start": 0, - "end": 7 - }, - "caused_by": { - "type": "illegal_argument_exception", - "reason": "cannot resolve symbol [invalid]" - } - } - } - ] - }, - "status": 400 -} \ No newline at end of file diff --git a/x-pack/plugins/data_enhanced/config.ts b/x-pack/plugins/data_enhanced/config.ts deleted file mode 100644 index eb3ee0da41839..0000000000000 --- a/x-pack/plugins/data_enhanced/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 { schema, TypeOf } from '@kbn/config-schema'; -import { searchSessionsConfigSchema } from '@kbn/data-plugin/config'; - -export const configSchema = schema.object({ - search: schema.object({ - sessions: searchSessionsConfigSchema, - }), -}); - -export type ConfigSchema = TypeOf; diff --git a/x-pack/plugins/data_enhanced/jest.config.js b/x-pack/plugins/data_enhanced/jest.config.js deleted file mode 100644 index e48de352c2075..0000000000000 --- a/x-pack/plugins/data_enhanced/jest.config.js +++ /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. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../../..', - roots: ['/x-pack/plugins/data_enhanced'], - coverageDirectory: '/target/kibana-coverage/jest/x-pack/plugins/data_enhanced', - coverageReporters: ['text', 'html'], - collectCoverageFrom: [ - '/x-pack/plugins/data_enhanced/{common,public,server}/**/*.{ts,tsx}', - ], -}; diff --git a/x-pack/plugins/data_enhanced/kibana.json b/x-pack/plugins/data_enhanced/kibana.json deleted file mode 100644 index d89e76013ebd4..0000000000000 --- a/x-pack/plugins/data_enhanced/kibana.json +++ /dev/null @@ -1,29 +0,0 @@ - -{ - "id": "dataEnhanced", - "version": "8.0.0", - "kibanaVersion": "kibana", - "owner": { - "name": "App Services", - "githubTeam": "kibana-app-services" - }, - "configPath": ["xpack", "data_enhanced"], - "requiredPlugins": [ - "bfetch", - "data", - "features", - "management", - "share", - "taskManager", - "screenshotMode" - ], - "optionalPlugins": ["kibanaUtils", "usageCollection", "security"], - "server": true, - "ui": true, - "requiredBundles": ["kibanaUtils", "kibanaReact"], - "owner": { - "name": "App Services", - "githubTeam": "kibana-app-services" - }, - "description": "Enhanced data plugin. (See src/plugins/data.) Enhances the main data plugin with a search session management UI. Includes a reusable search session indicator component to use in other applications. Exposes routes for managing search sessions. Includes a service that monitors, updates, and cleans up search session saved objects." -} diff --git a/x-pack/plugins/data_enhanced/public/index.ts b/x-pack/plugins/data_enhanced/public/index.ts deleted file mode 100644 index 7af1aff1b3106..0000000000000 --- a/x-pack/plugins/data_enhanced/public/index.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 { PluginInitializerContext } from '@kbn/core/public'; -import { DataEnhancedPlugin, DataEnhancedSetup, DataEnhancedStart } from './plugin'; -import { ConfigSchema } from '../config'; - -export const plugin = (initializerContext: PluginInitializerContext) => - new DataEnhancedPlugin(initializerContext); - -export type { DataEnhancedSetup, DataEnhancedStart }; - -export { ENHANCED_ES_SEARCH_STRATEGY, EQL_SEARCH_STRATEGY } from '@kbn/data-plugin/common'; diff --git a/x-pack/plugins/data_enhanced/public/plugin.ts b/x-pack/plugins/data_enhanced/public/plugin.ts deleted file mode 100644 index f1bb9d7b4b4b6..0000000000000 --- a/x-pack/plugins/data_enhanced/public/plugin.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import moment from 'moment'; -import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public'; -import { - DataPublicPluginSetup, - DataPublicPluginStart, - SearchUsageCollector, -} from '@kbn/data-plugin/public'; -import { BfetchPublicSetup } from '@kbn/bfetch-plugin/public'; -import { ManagementSetup } from '@kbn/management-plugin/public'; -import { SharePluginStart } from '@kbn/share-plugin/public'; - -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; -import { Storage } from '@kbn/kibana-utils-plugin/public'; -import { ScreenshotModePluginStart } from '@kbn/screenshot-mode-plugin/public'; -import { registerSearchSessionsMgmt } from './search/sessions_mgmt'; -import { createConnectedSearchSessionIndicator } from './search'; -import { ConfigSchema } from '../config'; - -export interface DataEnhancedSetupDependencies { - bfetch: BfetchPublicSetup; - data: DataPublicPluginSetup; - management: ManagementSetup; -} -export interface DataEnhancedStartDependencies { - data: DataPublicPluginStart; - share: SharePluginStart; - screenshotMode: ScreenshotModePluginStart; -} - -export type DataEnhancedSetup = ReturnType; -export type DataEnhancedStart = ReturnType; - -export class DataEnhancedPlugin - implements Plugin -{ - private config!: ConfigSchema; - private readonly storage = new Storage(window.localStorage); - private usageCollector?: SearchUsageCollector; - - constructor(private initializerContext: PluginInitializerContext) {} - - public setup( - core: CoreSetup, - { bfetch, data, management }: DataEnhancedSetupDependencies - ) { - this.config = this.initializerContext.config.get(); - if (this.config.search.sessions.enabled) { - const sessionsConfig = this.config.search.sessions; - registerSearchSessionsMgmt( - core, - sessionsConfig, - this.initializerContext.env.packageInfo.version, - { data, management } - ); - } - - this.usageCollector = data.search.usageCollector; - } - - public start(core: CoreStart, plugins: DataEnhancedStartDependencies) { - if (this.config.search.sessions.enabled) { - core.chrome.setBreadcrumbsAppendExtension({ - content: toMountPoint( - React.createElement( - createConnectedSearchSessionIndicator({ - sessionService: plugins.data.search.session, - application: core.application, - basePath: core.http.basePath, - storage: this.storage, - disableSaveAfterSessionCompletesTimeout: moment - .duration(this.config.search.sessions.notTouchedTimeout) - .asMilliseconds(), - usageCollector: this.usageCollector, - tourDisabled: plugins.screenshotMode.isScreenshotMode(), - }) - ), - { theme$: core.theme.theme$ } - ), - }); - } - } - - public stop() {} -} diff --git a/x-pack/plugins/data_enhanced/public/search/index.ts b/x-pack/plugins/data_enhanced/public/search/index.ts deleted file mode 100644 index 84e70f41d6746..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor 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 './ui'; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/__mocks__/index.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/__mocks__/index.tsx deleted file mode 100644 index 7c089c8db9634..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/__mocks__/index.tsx +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { ReactNode } from 'react'; -import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; - -export function LocaleWrapper({ children }: { children?: ReactNode }) { - return {children}; -} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/index.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/index.tsx deleted file mode 100644 index d2be863170a4b..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/index.tsx +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { CoreSetup } from '@kbn/core/public'; -import type { ManagementAppMountParams } from '@kbn/management-plugin/public'; -import type { - AppDependencies, - IManagementSectionsPluginsSetup, - IManagementSectionsPluginsStart, - SessionsConfigSchema, -} from '..'; -import { APP } from '..'; -import { SearchSessionsMgmtAPI } from '../lib/api'; -import { AsyncSearchIntroDocumentation } from '../lib/documentation'; -import { renderApp } from './render'; - -export class SearchSessionsMgmtApp { - constructor( - private coreSetup: CoreSetup, - private config: SessionsConfigSchema, - private kibanaVersion: string, - private params: ManagementAppMountParams, - private pluginsSetup: IManagementSectionsPluginsSetup - ) {} - - public async mountManagementSection() { - const { coreSetup, params, pluginsSetup } = this; - const [coreStart, pluginsStart] = await coreSetup.getStartServices(); - - const { - chrome: { docTitle }, - http, - docLinks, - i18n, - notifications, - uiSettings, - application, - } = coreStart; - const { data, share } = pluginsStart; - - const pluginName = APP.getI18nName(); - docTitle.change(pluginName); - params.setBreadcrumbs([{ text: pluginName }]); - - const { sessionsClient } = data.search; - const api = new SearchSessionsMgmtAPI(sessionsClient, this.config, { - notifications, - locators: share.url.locators, - application, - usageCollector: pluginsSetup.data.search.usageCollector, - }); - - const documentation = new AsyncSearchIntroDocumentation(docLinks); - - const dependencies: AppDependencies = { - plugins: pluginsSetup, - config: this.config, - documentation, - core: coreStart, - api, - http, - i18n, - uiSettings, - share, - kibanaVersion: this.kibanaVersion, - }; - - const { element } = params; - const unmountAppCb = renderApp(element, dependencies); - - return () => { - docTitle.reset(); - unmountAppCb(); - }; - } -} - -export { renderApp }; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/index.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/index.tsx deleted file mode 100644 index cecd50125e90e..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/index.tsx +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { PopoverActionsMenu } from './popover_actions'; -export * from './types'; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/types.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/types.ts deleted file mode 100644 index c808f8469cccf..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export type OnActionComplete = () => void; -export type OnActionDismiss = () => void; - -export enum ACTION { - INSPECT = 'inspect', - EXTEND = 'extend', - DELETE = 'delete', - RENAME = 'rename', -} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/index.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/index.tsx deleted file mode 100644 index 2970c75e651d6..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/index.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiLinkProps, EuiText, EuiTextProps } from '@elastic/eui'; -import React from 'react'; -import extendSessionIcon from '../icons/extend_session.svg'; - -export type { OnActionComplete } from './actions'; -export { PopoverActionsMenu } from './actions'; - -export const TableText = ({ children, ...props }: EuiTextProps) => { - return ( - - {children} - - ); -}; - -export interface IClickActionDescriptor { - label: React.ReactNode; - iconType: 'trash' | 'cancel' | typeof extendSessionIcon; - onClick: () => Promise | void; -} - -export interface IHrefActionDescriptor { - label: string; - props: EuiLinkProps; -} - -export interface StatusDef { - textColor?: EuiTextProps['color']; - icon?: React.ReactElement; - label: React.ReactElement; - toolTipContent: string; -} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.tsx deleted file mode 100644 index e065a20003dd9..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.tsx +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiButtonEmpty, EuiPageHeader, EuiSpacer } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import type { CoreStart, HttpStart } from '@kbn/core/public'; -import React from 'react'; -import type { SessionsConfigSchema } from '..'; -import { IManagementSectionsPluginsSetup } from '..'; -import type { SearchSessionsMgmtAPI } from '../lib/api'; -import type { AsyncSearchIntroDocumentation } from '../lib/documentation'; -import { SearchSessionsMgmtTable } from './table'; - -interface Props { - documentation: AsyncSearchIntroDocumentation; - core: CoreStart; - api: SearchSessionsMgmtAPI; - http: HttpStart; - timezone: string; - config: SessionsConfigSchema; - plugins: IManagementSectionsPluginsSetup; - kibanaVersion: string; -} - -export function SearchSessionsMgmtMain({ documentation, ...tableProps }: Props) { - return ( - <> - - } - description={ - - } - bottomBorder - rightSideItems={[ - - - , - ]} - /> - - - - - ); -} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/status.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/status.tsx deleted file mode 100644 index 577e89f2c761b..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/status.tsx +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLoadingSpinner, EuiToolTip } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React, { ReactElement } from 'react'; -import { SearchSessionStatus } from '@kbn/data-plugin/common'; -import { dateString } from '../lib/date_string'; -import { UISession } from '../types'; -import { StatusDef as StatusAttributes, TableText } from '.'; - -// Shared helper function -export const getStatusText = (statusType: string): string => { - switch (statusType) { - case SearchSessionStatus.IN_PROGRESS: - return i18n.translate('xpack.data.mgmt.searchSessions.status.label.inProgress', { - defaultMessage: 'In progress', - }); - case SearchSessionStatus.EXPIRED: - return i18n.translate('xpack.data.mgmt.searchSessions.status.label.expired', { - defaultMessage: 'Expired', - }); - case SearchSessionStatus.CANCELLED: - return i18n.translate('xpack.data.mgmt.searchSessions.status.label.cancelled', { - defaultMessage: 'Cancelled', - }); - case SearchSessionStatus.COMPLETE: - return i18n.translate('xpack.data.mgmt.searchSessions.status.label.complete', { - defaultMessage: 'Complete', - }); - case SearchSessionStatus.ERROR: - return i18n.translate('xpack.data.mgmt.searchSessions.status.label.error', { - defaultMessage: 'Error', - }); - default: - // eslint-disable-next-line no-console - console.error(`Unknown status ${statusType}`); - return statusType; - } -}; - -interface StatusIndicatorProps { - now?: string; - session: UISession; - timezone: string; -} - -// Get the fields needed to show each status type -// can throw errors around date conversions -const getStatusAttributes = ({ - now, - session, - timezone, -}: StatusIndicatorProps): StatusAttributes | null => { - let expireDate: string; - if (session.expires) { - expireDate = dateString(session.expires!, timezone); - } else { - expireDate = i18n.translate('xpack.data.mgmt.searchSessions.status.expireDateUnknown', { - defaultMessage: 'unknown', - }); - } - - switch (session.status) { - case SearchSessionStatus.IN_PROGRESS: - try { - return { - textColor: 'default', - icon: , - label: {getStatusText(session.status)}, - toolTipContent: i18n.translate( - 'xpack.data.mgmt.searchSessions.status.message.createdOn', - { - defaultMessage: 'Expires on {expireDate}', - values: { expireDate }, - } - ), - }; - } catch (err) { - // eslint-disable-next-line no-console - console.error(err); - throw new Error(`Could not instantiate a createdDate object from: ${session.created}`); - } - - case SearchSessionStatus.EXPIRED: - try { - const toolTipContent = i18n.translate( - 'xpack.data.mgmt.searchSessions.status.message.expiredOn', - { - defaultMessage: 'Expired on {expireDate}', - values: { expireDate }, - } - ); - - return { - icon: , - label: {getStatusText(session.status)}, - toolTipContent, - }; - } catch (err) { - // eslint-disable-next-line no-console - console.error(err); - throw new Error(`Could not instantiate an expiration Date object from: ${session.expires}`); - } - - case SearchSessionStatus.CANCELLED: - return { - icon: , - label: {getStatusText(session.status)}, - toolTipContent: i18n.translate('xpack.data.mgmt.searchSessions.status.message.cancelled', { - defaultMessage: 'Cancelled by user', - }), - }; - - case SearchSessionStatus.ERROR: - return { - textColor: 'danger', - icon: , - label: {getStatusText(session.status)}, - toolTipContent: i18n.translate('xpack.data.mgmt.searchSessions.status.message.error', { - defaultMessage: 'Error: {error}', - values: { error: (session as any).error || 'unknown' }, - }), - }; - - case SearchSessionStatus.COMPLETE: - try { - const toolTipContent = i18n.translate('xpack.data.mgmt.searchSessions.status.expiresOn', { - defaultMessage: 'Expires on {expireDate}', - values: { expireDate }, - }); - - return { - textColor: 'success', - icon: , - label: {getStatusText(session.status)}, - toolTipContent, - }; - } catch (err) { - // eslint-disable-next-line no-console - console.error(err); - throw new Error( - `Could not instantiate an expiration Date object for completed session from: ${session.expires}` - ); - } - - // Error was thrown - return null; - - default: - throw new Error(`Unknown status: ${session.status}`); - } -}; - -export const StatusIndicator = (props: StatusIndicatorProps) => { - try { - const statusDef = getStatusAttributes(props); - const { session } = props; - - if (statusDef) { - const { toolTipContent } = statusDef; - let icon: ReactElement | undefined = statusDef.icon; - let label: ReactElement = statusDef.label; - - if (icon && toolTipContent) { - icon = {icon}; - } - if (toolTipContent) { - label = ( - - - {statusDef.label} - - - ); - } - - return ( - - {icon} - - - {label} - - - - ); - } - } catch (err) { - // eslint-disable-next-line no-console - console.error(err); - } - - // Exception has been caught - return {props.session.status}; -}; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/index.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/index.ts deleted file mode 100644 index 361261ff65c87..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor 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 { SearchSessionsMgmtTable } from './table'; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts deleted file mode 100644 index 35eb3cb4e36a4..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import type { CoreStart, HttpStart, I18nStart, IUiSettingsClient } from '@kbn/core/public'; -import { CoreSetup } from '@kbn/core/public'; -import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; -import type { ManagementSetup } from '@kbn/management-plugin/public'; -import type { SharePluginStart } from '@kbn/share-plugin/public'; -import { SEARCH_SESSIONS_MANAGEMENT_ID } from '@kbn/data-plugin/public'; -import type { ConfigSchema } from '../../../config'; -import type { DataEnhancedStartDependencies } from '../../plugin'; -import type { SearchSessionsMgmtAPI } from './lib/api'; -import type { AsyncSearchIntroDocumentation } from './lib/documentation'; - -export interface IManagementSectionsPluginsSetup { - data: DataPublicPluginSetup; - management: ManagementSetup; -} - -export interface IManagementSectionsPluginsStart { - data: DataPublicPluginStart; - share: SharePluginStart; -} - -export interface AppDependencies { - plugins: IManagementSectionsPluginsSetup; - share: SharePluginStart; - uiSettings: IUiSettingsClient; - documentation: AsyncSearchIntroDocumentation; - core: CoreStart; // for RedirectAppLinks - api: SearchSessionsMgmtAPI; - http: HttpStart; - i18n: I18nStart; - config: SessionsConfigSchema; - kibanaVersion: string; -} - -export const APP = { - id: SEARCH_SESSIONS_MANAGEMENT_ID, - getI18nName: (): string => - i18n.translate('xpack.data.mgmt.searchSessions.appTitle', { - defaultMessage: 'Search Sessions', - }), -}; - -export type SessionsConfigSchema = ConfigSchema['search']['sessions']; - -export function registerSearchSessionsMgmt( - coreSetup: CoreSetup, - config: SessionsConfigSchema, - kibanaVersion: string, - services: IManagementSectionsPluginsSetup -) { - services.management.sections.section.kibana.registerApp({ - id: APP.id, - title: APP.getI18nName(), - order: 1.75, - mount: async (params) => { - const { SearchSessionsMgmtApp: MgmtApp } = await import('./application'); - const mgmtApp = new MgmtApp(coreSetup, config, kibanaVersion, params, services); - return mgmtApp.mountManagementSection(); - }, - }); -} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts deleted file mode 100644 index bb58efd01d957..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { ApplicationStart, NotificationsStart, SavedObject } from '@kbn/core/public'; -import moment from 'moment'; -import { from, race, timer } from 'rxjs'; -import { mapTo, tap } from 'rxjs/operators'; -import type { SharePluginStart } from '@kbn/share-plugin/public'; -import { SerializableRecord } from '@kbn/utility-types'; -import { ISessionsClient, SearchUsageCollector } from '@kbn/data-plugin/public'; -import { SearchSessionStatus } from '@kbn/data-plugin/common'; -import { ACTION } from '../components/actions'; -import { - PersistedSearchSessionSavedObjectAttributes, - UISearchSessionState, - UISession, -} from '../types'; -import { SessionsConfigSchema } from '..'; - -type LocatorsStart = SharePluginStart['url']['locators']; - -function getActions(status: UISearchSessionState) { - const actions: ACTION[] = []; - actions.push(ACTION.INSPECT); - actions.push(ACTION.RENAME); - if (status === SearchSessionStatus.IN_PROGRESS || status === SearchSessionStatus.COMPLETE) { - actions.push(ACTION.EXTEND); - actions.push(ACTION.DELETE); - } - - if (status === SearchSessionStatus.EXPIRED) { - actions.push(ACTION.DELETE); - } - - return actions; -} - -/** - * Status we display on mgtm UI might be different from the one inside the saved object - * @param status - */ -function getUIStatus(session: PersistedSearchSessionSavedObjectAttributes): UISearchSessionState { - const isSessionExpired = () => { - const curTime = moment(); - return curTime.diff(moment(session.expires), 'ms') > 0; - }; - - switch (session.status) { - case SearchSessionStatus.COMPLETE: - case SearchSessionStatus.IN_PROGRESS: - return isSessionExpired() ? SearchSessionStatus.EXPIRED : session.status; - } - - return session.status; -} - -function getUrlFromState(locators: LocatorsStart, locatorId: string, state: SerializableRecord) { - try { - const locator = locators.get(locatorId); - return locator?.getRedirectUrl(state); - } catch (err) { - // eslint-disable-next-line no-console - console.error('Could not create URL from restoreState'); - // eslint-disable-next-line no-console - console.error(err); - } -} - -// Helper: factory for a function to map server objects to UI objects -const mapToUISession = - (locators: LocatorsStart, config: SessionsConfigSchema) => - async ( - savedObject: SavedObject - ): Promise => { - const { - name, - appId, - created, - expires, - locatorId, - initialState, - restoreState, - idMapping, - version, - } = savedObject.attributes; - - const status = getUIStatus(savedObject.attributes); - const actions = getActions(status); - - // TODO: initialState should be saved without the searchSessionID - if (initialState) delete initialState.searchSessionId; - // derive the URL and add it in - const reloadUrl = await getUrlFromState(locators, locatorId, initialState); - const restoreUrl = await getUrlFromState(locators, locatorId, restoreState); - - return { - id: savedObject.id, - name, - appId, - created, - expires, - status, - actions, - restoreUrl: restoreUrl!, - reloadUrl: reloadUrl!, - initialState, - restoreState, - numSearches: Object.keys(idMapping).length, - version, - }; - }; - -interface SearchSessionManagementDeps { - locators: LocatorsStart; - notifications: NotificationsStart; - application: ApplicationStart; - usageCollector?: SearchUsageCollector; -} - -export class SearchSessionsMgmtAPI { - constructor( - private sessionsClient: ISessionsClient, - private config: SessionsConfigSchema, - private deps: SearchSessionManagementDeps - ) {} - - public async fetchTableData(): Promise { - const mgmtConfig = this.config.management; - - const refreshTimeout = moment.duration(mgmtConfig.refreshTimeout); - - const fetch$ = from( - this.sessionsClient.find({ - page: 1, - perPage: mgmtConfig.maxSessions, - sortField: 'created', - sortOrder: 'asc', - searchFields: ['persisted'], - search: 'true', - }) - ); - const timeout$ = timer(refreshTimeout.asMilliseconds()).pipe( - tap(() => { - this.deps.notifications.toasts.addDanger( - i18n.translate('xpack.data.mgmt.searchSessions.api.fetchTimeout', { - defaultMessage: 'Fetching the Search Session info timed out after {timeout} seconds', - values: { timeout: refreshTimeout.asSeconds() }, - }) - ); - }), - mapTo(null) - ); - - // fetch the search sessions before timeout triggers - try { - const result = await race(fetch$, timeout$).toPromise(); - if (result && result.saved_objects) { - const savedObjects = result.saved_objects as Array< - SavedObject - >; - return await Promise.all(savedObjects.map(mapToUISession(this.deps.locators, this.config))); - } - } catch (err) { - // eslint-disable-next-line no-console - console.error(err); - this.deps.notifications.toasts.addError(err, { - title: i18n.translate('xpack.data.mgmt.searchSessions.api.fetchError', { - defaultMessage: 'Failed to refresh the page!', - }), - }); - } - - return []; - } - - public reloadSearchSession(reloadUrl: string) { - this.deps.usageCollector?.trackSessionReloaded(); - this.deps.application.navigateToUrl(reloadUrl); - } - - public getExtendByDuration() { - return this.config.defaultExpiration; - } - - // Cancel and expire - public async sendCancel(id: string): Promise { - this.deps.usageCollector?.trackSessionDeleted(); - try { - await this.sessionsClient.delete(id); - - this.deps.notifications.toasts.addSuccess({ - title: i18n.translate('xpack.data.mgmt.searchSessions.api.deleted', { - defaultMessage: 'The search session was deleted.', - }), - }); - } catch (err) { - this.deps.notifications.toasts.addError(err, { - title: i18n.translate('xpack.data.mgmt.searchSessions.api.deletedError', { - defaultMessage: 'Failed to delete the search session!', - }), - }); - } - } - - // Extend - public async sendExtend(id: string, expires: string): Promise { - this.deps.usageCollector?.trackSessionExtended(); - try { - await this.sessionsClient.extend(id, expires); - - this.deps.notifications.toasts.addSuccess({ - title: i18n.translate('xpack.data.mgmt.searchSessions.api.extended', { - defaultMessage: 'The search session was extended.', - }), - }); - } catch (err) { - this.deps.notifications.toasts.addError(err, { - title: i18n.translate('xpack.data.mgmt.searchSessions.api.extendError', { - defaultMessage: 'Failed to extend the search session!', - }), - }); - } - } - - // Change the user-facing name of a search session - public async sendRename(id: string, newName: string): Promise { - try { - await this.sessionsClient.rename(id, newName); - - this.deps.notifications.toasts.addSuccess({ - title: i18n.translate('xpack.data.mgmt.searchSessions.api.rename', { - defaultMessage: 'The search session was renamed', - }), - }); - } catch (err) { - this.deps.notifications.toasts.addError(err, { - title: i18n.translate('xpack.data.mgmt.searchSessions.api.renameError', { - defaultMessage: 'Failed to rename the search session', - }), - }); - } - } -} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/documentation.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/documentation.ts deleted file mode 100644 index 03279199912c0..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/documentation.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 { DocLinksStart } from '@kbn/core/public'; - -export class AsyncSearchIntroDocumentation { - private docUrl: string = ''; - - constructor(docs: DocLinksStart) { - const { links } = docs; - this.docUrl = links.search.sessions; - } - - public getElasticsearchDocLink() { - return `${this.docUrl}`; - } -} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_expiration_status.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_expiration_status.ts deleted file mode 100644 index a220b414ba659..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_expiration_status.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import moment from 'moment'; -import { SessionsConfigSchema } from '..'; - -export const getExpirationStatus = (config: SessionsConfigSchema, expires: string | null) => { - const tNow = moment.utc().valueOf(); - const tFuture = moment.utc(expires).valueOf(); - - // NOTE this could end up negative. If server time is off from the browser's clock - // and the session was early expired when the browser refreshed the listing - const durationToExpire = moment.duration(tFuture - tNow); - const expiresInDays = Math.floor(durationToExpire.asDays()); - const sufficientDays = Math.ceil(moment.duration(config.management.expiresSoonWarning).asDays()); - - let toolTipContent = i18n.translate('xpack.data.mgmt.searchSessions.status.expiresSoonInDays', { - defaultMessage: 'Expires in {numDays} days', - values: { numDays: expiresInDays }, - }); - let statusContent = i18n.translate( - 'xpack.data.mgmt.searchSessions.status.expiresSoonInDaysTooltip', - { defaultMessage: '{numDays} days', values: { numDays: expiresInDays } } - ); - - if (expiresInDays === 0) { - // switch to show expires in hours - const expiresInHours = Math.floor(durationToExpire.asHours()); - - toolTipContent = i18n.translate('xpack.data.mgmt.searchSessions.status.expiresSoonInHours', { - defaultMessage: 'This session expires in {numHours} hours', - values: { numHours: expiresInHours }, - }); - statusContent = i18n.translate( - 'xpack.data.mgmt.searchSessions.status.expiresSoonInHoursTooltip', - { defaultMessage: '{numHours} hours', values: { numHours: expiresInHours } } - ); - } - - if (durationToExpire.valueOf() > 0 && expiresInDays <= sufficientDays) { - return { toolTipContent, statusContent }; - } -}; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/types.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/types.ts deleted file mode 100644 index c66290a968240..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/types.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { SearchSessionSavedObjectAttributes, SearchSessionStatus } from '@kbn/data-plugin/common'; -import { ACTION } from './components/actions'; - -export const DATE_STRING_FORMAT = 'D MMM, YYYY, HH:mm:ss'; - -/** - * Some properties are optional for a non-persisted Search Session. - * This interface makes them mandatory, because management only shows persisted search sessions. - */ -export type PersistedSearchSessionSavedObjectAttributes = SearchSessionSavedObjectAttributes & - Required< - Pick< - SearchSessionSavedObjectAttributes, - 'name' | 'appId' | 'locatorId' | 'initialState' | 'restoreState' - > - >; - -export type UISearchSessionState = SearchSessionStatus; - -export interface UISession { - id: string; - name: string; - appId: string; - created: string; - expires: string | null; - status: UISearchSessionState; - numSearches: number; - actions?: ACTION[]; - reloadUrl: string; - restoreUrl: string; - initialState: Record; - restoreState: Record; - version: string; -} diff --git a/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/index.ts b/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/index.ts deleted file mode 100644 index fec61f8115486..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export type { SearchSessionIndicatorDeps } from './connected_search_session_indicator'; -export { createConnectedSearchSessionIndicator } from './connected_search_session_indicator'; diff --git a/x-pack/plugins/data_enhanced/public/search/ui/index.ts b/x-pack/plugins/data_enhanced/public/search/ui/index.ts deleted file mode 100644 index e8f83db6ed98c..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/ui/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor 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 './connected_search_session_indicator'; diff --git a/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/components/index.ts b/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/components/index.ts deleted file mode 100644 index 9093e1a2535e2..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/components/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor 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 './search_session_name'; diff --git a/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/components/search_session_name/index.ts b/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/components/search_session_name/index.ts deleted file mode 100644 index 9093e1a2535e2..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/components/search_session_name/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor 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 './search_session_name'; diff --git a/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/index.tsx b/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/index.tsx deleted file mode 100644 index fe86ad2fb5cea..0000000000000 --- a/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiDelayRender, EuiLoadingSpinner } from '@elastic/eui'; -import React from 'react'; -import type { - SearchSessionIndicatorProps, - SearchSessionIndicatorRef, -} from './search_session_indicator'; -export type { SearchSessionIndicatorProps, SearchSessionIndicatorRef }; - -const Fallback = () => ( - - - -); - -const LazySearchSessionIndicator = React.lazy(() => import('./search_session_indicator')); -export const SearchSessionIndicator = React.forwardRef< - SearchSessionIndicatorRef, - SearchSessionIndicatorProps ->((props: SearchSessionIndicatorProps, ref) => ( - }> - - -)); diff --git a/x-pack/plugins/data_enhanced/server/collectors/index.ts b/x-pack/plugins/data_enhanced/server/collectors/index.ts deleted file mode 100644 index 4a82c76e96dee..0000000000000 --- a/x-pack/plugins/data_enhanced/server/collectors/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor 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 { registerUsageCollector } from './register'; diff --git a/x-pack/plugins/data_enhanced/server/index.ts b/x-pack/plugins/data_enhanced/server/index.ts deleted file mode 100644 index a56f7216c4706..0000000000000 --- a/x-pack/plugins/data_enhanced/server/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 { PluginConfigDescriptor, PluginInitializerContext } from '@kbn/core/server'; -import { EnhancedDataServerPlugin } from './plugin'; -import { configSchema, ConfigSchema } from '../config'; - -export const config: PluginConfigDescriptor = { - exposeToBrowser: { - search: true, - }, - schema: configSchema, -}; - -export function plugin(initializerContext: PluginInitializerContext) { - return new EnhancedDataServerPlugin(initializerContext); -} - -export { ENHANCED_ES_SEARCH_STRATEGY, EQL_SEARCH_STRATEGY } from '@kbn/data-plugin/common'; - -export { EnhancedDataServerPlugin as Plugin }; diff --git a/x-pack/plugins/data_enhanced/server/plugin.ts b/x-pack/plugins/data_enhanced/server/plugin.ts deleted file mode 100644 index 007764ec95f0c..0000000000000 --- a/x-pack/plugins/data_enhanced/server/plugin.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { CoreSetup, CoreStart, Logger, Plugin, PluginInitializerContext } from '@kbn/core/server'; -import { registerSessionRoutes } from './routes'; -import { searchSessionSavedObjectType } from './saved_objects'; -import type { - DataEnhancedRequestHandlerContext, - DataEnhancedSetupDependencies as SetupDependencies, - DataEnhancedStartDependencies as StartDependencies, -} from './type'; -import { ConfigSchema } from '../config'; -import { registerUsageCollector } from './collectors'; -import { SearchSessionService } from './search'; - -export class EnhancedDataServerPlugin - implements Plugin -{ - private readonly logger: Logger; - private sessionService!: SearchSessionService; - private config: ConfigSchema; - - constructor(private initializerContext: PluginInitializerContext) { - this.logger = initializerContext.logger.get('data_enhanced'); - this.config = this.initializerContext.config.get(); - } - - public setup(core: CoreSetup, deps: SetupDependencies) { - core.savedObjects.registerType(searchSessionSavedObjectType); - - this.sessionService = new SearchSessionService( - this.logger, - this.config, - this.initializerContext.env.packageInfo.version, - deps.security - ); - - deps.data.__enhance({ - search: { - sessionService: this.sessionService, - }, - }); - - const router = core.http.createRouter(); - registerSessionRoutes(router, this.logger); - - this.sessionService.setup(core, { - taskManager: deps.taskManager, - }); - - if (deps.usageCollection) { - registerUsageCollector(deps.usageCollection, core.savedObjects.getKibanaIndex(), this.logger); - } - } - - public start(core: CoreStart, { taskManager }: StartDependencies) { - this.sessionService.start(core, { - taskManager, - }); - } - - public stop() { - this.sessionService.stop(); - } -} - -export { EnhancedDataServerPlugin as Plugin }; diff --git a/x-pack/plugins/data_enhanced/server/routes/index.ts b/x-pack/plugins/data_enhanced/server/routes/index.ts deleted file mode 100644 index 0430d283667d0..0000000000000 --- a/x-pack/plugins/data_enhanced/server/routes/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor 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 './session'; diff --git a/x-pack/plugins/data_enhanced/server/saved_objects/index.ts b/x-pack/plugins/data_enhanced/server/saved_objects/index.ts deleted file mode 100644 index 3736eb7e0394b..0000000000000 --- a/x-pack/plugins/data_enhanced/server/saved_objects/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor 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 './search_session'; diff --git a/x-pack/plugins/data_enhanced/server/search/index.ts b/x-pack/plugins/data_enhanced/server/search/index.ts deleted file mode 100644 index 0430d283667d0..0000000000000 --- a/x-pack/plugins/data_enhanced/server/search/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor 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 './session'; diff --git a/x-pack/plugins/data_enhanced/server/search/session/index.ts b/x-pack/plugins/data_enhanced/server/search/session/index.ts deleted file mode 100644 index 1e6841211bb66..0000000000000 --- a/x-pack/plugins/data_enhanced/server/search/session/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor 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 './session_service'; diff --git a/x-pack/plugins/data_enhanced/server/search/session/session_service.ts b/x-pack/plugins/data_enhanced/server/search/session/session_service.ts deleted file mode 100644 index a22e559ecd142..0000000000000 --- a/x-pack/plugins/data_enhanced/server/search/session/session_service.ts +++ /dev/null @@ -1,548 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { notFound } from '@hapi/boom'; -import { debounce } from 'lodash'; -import { nodeBuilder, fromKueryExpression } from '@kbn/es-query'; -import { - CoreSetup, - CoreStart, - KibanaRequest, - SavedObjectsClientContract, - Logger, - SavedObject, - SavedObjectsFindOptions, - SavedObjectsErrorHelpers, -} from '@kbn/core/server'; -import { - IKibanaSearchRequest, - ISearchOptions, - ENHANCED_ES_SEARCH_STRATEGY, - SEARCH_SESSION_TYPE, -} from '@kbn/data-plugin/common'; -import { ISearchSessionService, NoSearchIdInSessionError } from '@kbn/data-plugin/server'; -import { AuthenticatedUser, SecurityPluginSetup } from '@kbn/security-plugin/server'; -import { - TaskManagerSetupContract, - TaskManagerStartContract, -} from '@kbn/task-manager-plugin/server'; -import { - SearchSessionRequestInfo, - SearchSessionSavedObjectAttributes, - SearchSessionStatus, -} from '@kbn/data-plugin/common'; -import { createRequestHash } from './utils'; -import { ConfigSchema } from '../../../config'; -import { - registerSearchSessionsTask, - scheduleSearchSessionsTask, - unscheduleSearchSessionsTask, -} from './setup_task'; -import { SearchSessionsConfig, SearchStatus } from './types'; -import { DataEnhancedStartDependencies } from '../../type'; -import { - checkPersistedSessionsProgress, - SEARCH_SESSIONS_TASK_ID, - SEARCH_SESSIONS_TASK_TYPE, -} from './check_persisted_sessions'; -import { - SEARCH_SESSIONS_CLEANUP_TASK_TYPE, - checkNonPersistedSessions, - SEARCH_SESSIONS_CLEANUP_TASK_ID, -} from './check_non_persisted_sessions'; -import { - SEARCH_SESSIONS_EXPIRE_TASK_TYPE, - SEARCH_SESSIONS_EXPIRE_TASK_ID, - checkPersistedCompletedSessionExpiration, -} from './expire_persisted_sessions'; - -export interface SearchSessionDependencies { - savedObjectsClient: SavedObjectsClientContract; -} -interface SetupDependencies { - taskManager: TaskManagerSetupContract; -} - -interface StartDependencies { - taskManager: TaskManagerStartContract; -} - -const DEBOUNCE_UPDATE_OR_CREATE_WAIT = 1000; -const DEBOUNCE_UPDATE_OR_CREATE_MAX_WAIT = 5000; - -interface UpdateOrCreateQueueEntry { - deps: SearchSessionDependencies; - user: AuthenticatedUser | null; - sessionId: string; - attributes: Partial; - resolve: () => void; - reject: (reason?: unknown) => void; -} - -function sleep(ms: number) { - return new Promise((r) => setTimeout(r, ms)); -} -export class SearchSessionService - implements ISearchSessionService -{ - private sessionConfig: SearchSessionsConfig; - private readonly updateOrCreateBatchQueue: UpdateOrCreateQueueEntry[] = []; - - constructor( - private readonly logger: Logger, - private readonly config: ConfigSchema, - private readonly version: string, - private readonly security?: SecurityPluginSetup - ) { - this.sessionConfig = this.config.search.sessions; - } - - public setup(core: CoreSetup, deps: SetupDependencies) { - const taskDeps = { - config: this.config, - taskManager: deps.taskManager, - logger: this.logger, - }; - - registerSearchSessionsTask( - core, - taskDeps, - SEARCH_SESSIONS_TASK_TYPE, - 'persisted session progress', - checkPersistedSessionsProgress - ); - - registerSearchSessionsTask( - core, - taskDeps, - SEARCH_SESSIONS_CLEANUP_TASK_TYPE, - 'non persisted session cleanup', - checkNonPersistedSessions - ); - - registerSearchSessionsTask( - core, - taskDeps, - SEARCH_SESSIONS_EXPIRE_TASK_TYPE, - 'complete session expiration', - checkPersistedCompletedSessionExpiration - ); - } - - public async start(core: CoreStart, deps: StartDependencies) { - return this.setupMonitoring(core, deps); - } - - public stop() {} - - private setupMonitoring = async (core: CoreStart, deps: StartDependencies) => { - const taskDeps = { - config: this.config, - taskManager: deps.taskManager, - logger: this.logger, - }; - - if (this.sessionConfig.enabled) { - scheduleSearchSessionsTask( - taskDeps, - SEARCH_SESSIONS_TASK_ID, - SEARCH_SESSIONS_TASK_TYPE, - this.sessionConfig.trackingInterval - ); - - scheduleSearchSessionsTask( - taskDeps, - SEARCH_SESSIONS_CLEANUP_TASK_ID, - SEARCH_SESSIONS_CLEANUP_TASK_TYPE, - this.sessionConfig.cleanupInterval - ); - - scheduleSearchSessionsTask( - taskDeps, - SEARCH_SESSIONS_EXPIRE_TASK_ID, - SEARCH_SESSIONS_EXPIRE_TASK_TYPE, - this.sessionConfig.expireInterval - ); - } else { - unscheduleSearchSessionsTask(taskDeps, SEARCH_SESSIONS_TASK_ID); - unscheduleSearchSessionsTask(taskDeps, SEARCH_SESSIONS_CLEANUP_TASK_ID); - unscheduleSearchSessionsTask(taskDeps, SEARCH_SESSIONS_EXPIRE_TASK_ID); - } - }; - - private processUpdateOrCreateBatchQueue = debounce( - () => { - const queue = [...this.updateOrCreateBatchQueue]; - if (queue.length === 0) return; - this.updateOrCreateBatchQueue.length = 0; - const batchedSessionAttributes = queue.reduce((res, next) => { - if (!res[next.sessionId]) { - res[next.sessionId] = next.attributes; - } else { - res[next.sessionId] = { - ...res[next.sessionId], - ...next.attributes, - idMapping: { - ...res[next.sessionId].idMapping, - ...next.attributes.idMapping, - }, - }; - } - return res; - }, {} as { [sessionId: string]: Partial }); - - Object.keys(batchedSessionAttributes).forEach((sessionId) => { - const thisSession = queue.filter((s) => s.sessionId === sessionId); - this.updateOrCreate( - thisSession[0].deps, - thisSession[0].user, - sessionId, - batchedSessionAttributes[sessionId] - ) - .then(() => { - thisSession.forEach((s) => s.resolve()); - }) - .catch((e) => { - thisSession.forEach((s) => s.reject(e)); - }); - }); - }, - DEBOUNCE_UPDATE_OR_CREATE_WAIT, - { maxWait: DEBOUNCE_UPDATE_OR_CREATE_MAX_WAIT } - ); - private scheduleUpdateOrCreate = ( - deps: SearchSessionDependencies, - user: AuthenticatedUser | null, - sessionId: string, - attributes: Partial - ): Promise => { - return new Promise((resolve, reject) => { - this.updateOrCreateBatchQueue.push({ deps, user, sessionId, attributes, resolve, reject }); - // TODO: this would be better if we'd debounce per sessionId - this.processUpdateOrCreateBatchQueue(); - }); - }; - - private updateOrCreate = async ( - deps: SearchSessionDependencies, - user: AuthenticatedUser | null, - sessionId: string, - attributes: Partial, - retry: number = 1 - ): Promise | undefined> => { - const retryOnConflict = async (e: any) => { - this.logger.debug(`Conflict error | ${sessionId}`); - // Randomize sleep to spread updates out in case of conflicts - await sleep(100 + Math.random() * 50); - return await this.updateOrCreate(deps, user, sessionId, attributes, retry + 1); - }; - - this.logger.debug(`updateOrCreate | ${sessionId} | ${retry}`); - try { - return (await this.update( - deps, - user, - sessionId, - attributes - )) as SavedObject; - } catch (e) { - if (SavedObjectsErrorHelpers.isNotFoundError(e)) { - try { - this.logger.debug(`Object not found | ${sessionId}`); - return await this.create(deps, user, sessionId, attributes); - } catch (createError) { - if ( - SavedObjectsErrorHelpers.isConflictError(createError) && - retry < this.sessionConfig.maxUpdateRetries - ) { - return await retryOnConflict(createError); - } else { - this.logger.error(createError); - } - } - } else if ( - SavedObjectsErrorHelpers.isConflictError(e) && - retry < this.sessionConfig.maxUpdateRetries - ) { - return await retryOnConflict(e); - } else { - this.logger.error(e); - } - } - - return undefined; - }; - - public save = async ( - deps: SearchSessionDependencies, - user: AuthenticatedUser | null, - sessionId: string, - { - name, - appId, - locatorId, - initialState = {}, - restoreState = {}, - }: Partial - ) => { - if (!this.sessionConfig.enabled) throw new Error('Search sessions are disabled'); - if (!name) throw new Error('Name is required'); - if (!appId) throw new Error('AppId is required'); - if (!locatorId) throw new Error('locatorId is required'); - - return this.updateOrCreate(deps, user, sessionId, { - name, - appId, - locatorId, - initialState, - restoreState, - persisted: true, - }); - }; - - private create = ( - { savedObjectsClient }: SearchSessionDependencies, - user: AuthenticatedUser | null, - sessionId: string, - attributes: Partial - ) => { - this.logger.debug(`create | ${sessionId}`); - - const realmType = user?.authentication_realm.type; - const realmName = user?.authentication_realm.name; - const username = user?.username; - - return savedObjectsClient.create( - SEARCH_SESSION_TYPE, - { - sessionId, - status: SearchSessionStatus.IN_PROGRESS, - expires: new Date( - Date.now() + this.sessionConfig.defaultExpiration.asMilliseconds() - ).toISOString(), - created: new Date().toISOString(), - touched: new Date().toISOString(), - idMapping: {}, - persisted: false, - version: this.version, - realmType, - realmName, - username, - ...attributes, - }, - { id: sessionId } - ); - }; - - public get = async ( - { savedObjectsClient }: SearchSessionDependencies, - user: AuthenticatedUser | null, - sessionId: string - ) => { - this.logger.debug(`get | ${sessionId}`); - const session = await savedObjectsClient.get( - SEARCH_SESSION_TYPE, - sessionId - ); - this.throwOnUserConflict(user, session); - return session; - }; - - public find = ( - { savedObjectsClient }: SearchSessionDependencies, - user: AuthenticatedUser | null, - options: Omit - ) => { - const userFilters = - user === null - ? [] - : [ - nodeBuilder.is( - `${SEARCH_SESSION_TYPE}.attributes.realmType`, - `${user.authentication_realm.type}` - ), - nodeBuilder.is( - `${SEARCH_SESSION_TYPE}.attributes.realmName`, - `${user.authentication_realm.name}` - ), - nodeBuilder.is(`${SEARCH_SESSION_TYPE}.attributes.username`, `${user.username}`), - ]; - const filterKueryNode = - typeof options.filter === 'string' ? fromKueryExpression(options.filter) : options.filter; - const filter = nodeBuilder.and(userFilters.concat(filterKueryNode ?? [])); - return savedObjectsClient.find({ - ...options, - filter, - type: SEARCH_SESSION_TYPE, - }); - }; - - public update = async ( - deps: SearchSessionDependencies, - user: AuthenticatedUser | null, - sessionId: string, - attributes: Partial - ) => { - this.logger.debug(`update | ${sessionId}`); - if (!this.sessionConfig.enabled) throw new Error('Search sessions are disabled'); - await this.get(deps, user, sessionId); // Verify correct user - return deps.savedObjectsClient.update( - SEARCH_SESSION_TYPE, - sessionId, - { - ...attributes, - touched: new Date().toISOString(), - } - ); - }; - - public async extend( - deps: SearchSessionDependencies, - user: AuthenticatedUser | null, - sessionId: string, - expires: Date - ) { - this.logger.debug(`extend | ${sessionId}`); - return this.update(deps, user, sessionId, { expires: expires.toISOString() }); - } - - public cancel = async ( - deps: SearchSessionDependencies, - user: AuthenticatedUser | null, - sessionId: string - ) => { - this.logger.debug(`delete | ${sessionId}`); - return this.update(deps, user, sessionId, { - status: SearchSessionStatus.CANCELLED, - }); - }; - - public delete = async ( - deps: SearchSessionDependencies, - user: AuthenticatedUser | null, - sessionId: string - ) => { - if (!this.sessionConfig.enabled) throw new Error('Search sessions are disabled'); - this.logger.debug(`delete | ${sessionId}`); - await this.get(deps, user, sessionId); // Verify correct user - return deps.savedObjectsClient.delete(SEARCH_SESSION_TYPE, sessionId); - }; - - /** - * Tracks the given search request/search ID in the saved session. - * @internal - */ - public trackId = async ( - deps: SearchSessionDependencies, - user: AuthenticatedUser | null, - searchRequest: IKibanaSearchRequest, - searchId: string, - { sessionId, strategy = ENHANCED_ES_SEARCH_STRATEGY }: ISearchOptions - ) => { - if (!this.sessionConfig.enabled || !sessionId || !searchId) return; - this.logger.debug(`trackId | ${sessionId} | ${searchId}`); - - let idMapping: Record = {}; - - if (searchRequest.params) { - const requestHash = createRequestHash(searchRequest.params); - const searchInfo = { - id: searchId, - strategy, - status: SearchStatus.IN_PROGRESS, - }; - idMapping = { [requestHash]: searchInfo }; - } - - await this.scheduleUpdateOrCreate(deps, user, sessionId, { idMapping }); - }; - - public async getSearchIdMapping( - deps: SearchSessionDependencies, - user: AuthenticatedUser | null, - sessionId: string - ) { - const searchSession = await this.get(deps, user, sessionId); - const searchIdMapping = new Map(); - Object.values(searchSession.attributes.idMapping).forEach((requestInfo) => { - searchIdMapping.set(requestInfo.id, requestInfo.strategy); - }); - return searchIdMapping; - } - - /** - * Look up an existing search ID that matches the given request in the given session so that the - * request can continue rather than restart. - * @internal - */ - public getId = async ( - deps: SearchSessionDependencies, - user: AuthenticatedUser | null, - searchRequest: IKibanaSearchRequest, - { sessionId, isStored, isRestore }: ISearchOptions - ) => { - if (!this.sessionConfig.enabled) { - throw new Error('Search sessions are disabled'); - } else if (!sessionId) { - throw new Error('Session ID is required'); - } else if (!isStored) { - throw new Error('Cannot get search ID from a session that is not stored'); - } else if (!isRestore) { - throw new Error('Get search ID is only supported when restoring a session'); - } - - const session = await this.get(deps, user, sessionId); - const requestHash = createRequestHash(searchRequest.params); - if (!session.attributes.idMapping.hasOwnProperty(requestHash)) { - this.logger.error(`getId | ${sessionId} | ${requestHash} not found`); - throw new NoSearchIdInSessionError(); - } - this.logger.debug(`getId | ${sessionId} | ${requestHash}`); - - return session.attributes.idMapping[requestHash].id; - }; - - public asScopedProvider = ({ savedObjects }: CoreStart) => { - return (request: KibanaRequest) => { - const user = this.security?.authc.getCurrentUser(request) ?? null; - const savedObjectsClient = savedObjects.getScopedClient(request, { - includedHiddenTypes: [SEARCH_SESSION_TYPE], - }); - const deps = { savedObjectsClient }; - return { - getId: this.getId.bind(this, deps, user), - trackId: this.trackId.bind(this, deps, user), - getSearchIdMapping: this.getSearchIdMapping.bind(this, deps, user), - save: this.save.bind(this, deps, user), - get: this.get.bind(this, deps, user), - find: this.find.bind(this, deps, user), - update: this.update.bind(this, deps, user), - extend: this.extend.bind(this, deps, user), - cancel: this.cancel.bind(this, deps, user), - delete: this.delete.bind(this, deps, user), - getConfig: () => this.config.search.sessions, - }; - }; - }; - - private throwOnUserConflict = ( - user: AuthenticatedUser | null, - session?: SavedObject - ) => { - if (user === null || !session) return; - if ( - user.authentication_realm.type !== session.attributes.realmType || - user.authentication_realm.name !== session.attributes.realmName || - user.username !== session.attributes.username - ) { - this.logger.debug( - `User ${user.username} has no access to search session ${session.attributes.sessionId}` - ); - throw notFound(); - } - }; -} diff --git a/x-pack/plugins/data_enhanced/server/search/session/types.ts b/x-pack/plugins/data_enhanced/server/search/session/types.ts deleted file mode 100644 index 37e1cb2486154..0000000000000 --- a/x-pack/plugins/data_enhanced/server/search/session/types.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - ElasticsearchClient, - Logger, - SavedObjectsClientContract, - SavedObjectsFindResponse, -} from '@kbn/core/server'; -import { Observable } from 'rxjs'; -import { KueryNode, SearchSessionSavedObjectAttributes } from '@kbn/data-plugin/common'; -import { - TaskManagerSetupContract, - TaskManagerStartContract, -} from '@kbn/task-manager-plugin/server'; -import { ConfigSchema } from '../../../config'; - -export enum SearchStatus { - IN_PROGRESS = 'in_progress', - ERROR = 'error', - COMPLETE = 'complete', -} - -export type SearchSessionsConfig = ConfigSchema['search']['sessions']; - -export interface CheckSearchSessionsDeps { - savedObjectsClient: SavedObjectsClientContract; - client: ElasticsearchClient; - logger: Logger; -} - -export interface SearchSessionTaskSetupDeps { - taskManager: TaskManagerSetupContract; - logger: Logger; - config: ConfigSchema; -} - -export interface SearchSessionTaskStartDeps { - taskManager: TaskManagerStartContract; - logger: Logger; - config: ConfigSchema; -} - -export type SearchSessionTaskFn = ( - deps: CheckSearchSessionsDeps, - config: SearchSessionsConfig -) => Observable; - -export type SearchSessionsResponse = SavedObjectsFindResponse< - SearchSessionSavedObjectAttributes, - unknown ->; - -export type CheckSearchSessionsFn = ( - deps: CheckSearchSessionsDeps, - config: SearchSessionsConfig, - filter: KueryNode, - page: number -) => Observable; diff --git a/x-pack/plugins/data_enhanced/server/search/session/utils.test.ts b/x-pack/plugins/data_enhanced/server/search/session/utils.test.ts deleted file mode 100644 index 9e142bf25a56a..0000000000000 --- a/x-pack/plugins/data_enhanced/server/search/session/utils.test.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 { createRequestHash } from './utils'; - -describe('data/search/session utils', () => { - describe('createRequestHash', () => { - it('ignores `preference`', () => { - const request = { - foo: 'bar', - }; - - const withPreference = { - ...request, - preference: 1234, - }; - - expect(createRequestHash(request)).toEqual(createRequestHash(withPreference)); - }); - }); -}); diff --git a/x-pack/plugins/data_enhanced/server/search/session/utils.ts b/x-pack/plugins/data_enhanced/server/search/session/utils.ts deleted file mode 100644 index f7f3c3c17505b..0000000000000 --- a/x-pack/plugins/data_enhanced/server/search/session/utils.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. - */ - -import { createHash } from 'crypto'; -import stringify from 'json-stable-stringify'; -import { SavedObjectsFindResult } from '@kbn/core/server'; -import moment from 'moment'; -import { SearchSessionSavedObjectAttributes } from '@kbn/data-plugin/common'; - -/** - * Generate the hash for this request so that, in the future, this hash can be used to look up - * existing search IDs for this request. Ignores the `preference` parameter since it generally won't - * match from one request to another identical request. - */ -export function createRequestHash(keys: Record) { - const { preference, ...params } = keys; - return createHash(`sha256`).update(stringify(params)).digest('hex'); -} - -export function isSearchSessionExpired( - session: SavedObjectsFindResult -) { - return moment(session.attributes.expires).isBefore(moment()); -} diff --git a/x-pack/plugins/data_enhanced/server/type.ts b/x-pack/plugins/data_enhanced/server/type.ts deleted file mode 100644 index df465cdbb381d..0000000000000 --- a/x-pack/plugins/data_enhanced/server/type.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { IRouter } from '@kbn/core/server'; -import type { DataRequestHandlerContext } from '@kbn/data-plugin/server'; -import { - TaskManagerSetupContract, - TaskManagerStartContract, -} from '@kbn/task-manager-plugin/server'; -import { - PluginSetup as DataPluginSetup, - PluginStart as DataPluginStart, -} from '@kbn/data-plugin/server'; -import { SecurityPluginSetup } from '@kbn/security-plugin/server'; -import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; - -/** - * @internal - */ -export type DataEnhancedRequestHandlerContext = DataRequestHandlerContext; - -/** - * @internal - */ -export type DataEnhancedPluginRouter = IRouter; - -export interface DataEnhancedSetupDependencies { - data: DataPluginSetup; - usageCollection?: UsageCollectionSetup; - taskManager: TaskManagerSetupContract; - security?: SecurityPluginSetup; -} - -export interface DataEnhancedStartDependencies { - data: DataPluginStart; - taskManager: TaskManagerStartContract; -} diff --git a/x-pack/plugins/data_enhanced/tsconfig.json b/x-pack/plugins/data_enhanced/tsconfig.json deleted file mode 100644 index 5627951c3d9eb..0000000000000 --- a/x-pack/plugins/data_enhanced/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./target/types", - "emitDeclarationOnly": true, - "declaration": true, - "declarationMap": true, - }, - "include": [ - "common/**/*", - "public/**/*", - "server/**/*", - "config.ts", - "../../../typings/**/*", - "common/search/test_data/*.json" - ], - "references": [ - { "path": "../../../src/core/tsconfig.json" }, - { "path": "../../../src/plugins/bfetch/tsconfig.json" }, - { "path": "../../../src/plugins/data/tsconfig.json" }, - { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, - { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, - { "path": "../../../src/plugins/usage_collection/tsconfig.json" }, - { "path": "../../../src/plugins/management/tsconfig.json" }, - { "path": "../../../src/plugins/screenshot_mode/tsconfig.json"}, - { "path": "../security/tsconfig.json" }, - { "path": "../task_manager/tsconfig.json" }, - - { "path": "../features/tsconfig.json" }, - ] -} diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/link_card/link_card.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/link_card/link_card.tsx index ae5c4f0ab9ad9..4d5b5a8154e72 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/link_card/link_card.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/link_card/link_card.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { FC, ReactElement } from 'react'; +import React, { FC } from 'react'; import { EuiIcon, @@ -18,8 +18,8 @@ import { EuiLink, } from '@elastic/eui'; -interface Props { - icon: IconType | ReactElement; +export interface LinkCardProps { + icon: IconType; iconAreaLabel?: string; title: any; description: any; @@ -31,7 +31,7 @@ interface Props { // Component for rendering a card which links to the Create Job page, displaying an // icon, card title, description and link. -export const LinkCard: FC = ({ +export const LinkCard: FC = ({ icon, iconAreaLabel, title, @@ -39,7 +39,7 @@ export const LinkCard: FC = ({ onClick, href, isDisabled, - 'data-test-subj': dateTestSubj, + 'data-test-subj': dataTestSubj, }) => { const linkHrefAndOnClickProps = { ...(href ? { href } : {}), @@ -58,7 +58,7 @@ export const LinkCard: FC = ({ background: 'transparent', outline: 'none', }} - data-test-subj={dateTestSubj} + data-test-subj={dataTestSubj} color="subdued" {...linkHrefAndOnClickProps} > diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/results_links/index.ts b/x-pack/plugins/data_visualizer/public/application/common/components/results_links/index.ts index 24c36f97d7633..4c59725037577 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/results_links/index.ts +++ b/x-pack/plugins/data_visualizer/public/application/common/components/results_links/index.ts @@ -5,5 +5,5 @@ * 2.0. */ -export type { ResultLink } from './results_links'; +export type { ResultLink, GetAdditionalLinks, GetAdditionalLinksParams } from './results_links'; export { ResultsLinks } from './results_links'; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/results_links/results_links.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/results_links/results_links.tsx index 57395b506af28..64b0097401ef6 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/results_links/results_links.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/results_links/results_links.tsx @@ -12,10 +12,23 @@ import { EuiFlexGroup, EuiFlexItem, EuiCard, EuiIcon } from '@elastic/eui'; import { TimeRange, RefreshInterval } from '@kbn/data-plugin/public'; import { FindFileStructureResponse } from '@kbn/file-upload-plugin/common'; import type { FileUploadPluginStart } from '@kbn/file-upload-plugin/public'; +import { flatten } from 'lodash'; +import { LinkCardProps } from '../link_card/link_card'; import { useDataVisualizerKibana } from '../../../kibana_context'; +import { isDefined } from '../../util/is_defined'; type LinkType = 'file' | 'index'; +export interface GetAdditionalLinksParams { + dataViewId: string; + dataViewTitle?: string; + globalState?: any; +} + +export type GetAdditionalLinks = Array< + (params: GetAdditionalLinksParams) => Promise +>; + export interface ResultLink { id: string; type: LinkType; @@ -24,7 +37,7 @@ export interface ResultLink { description: string; getUrl(params?: any): Promise; canDisplay(params?: any): Promise; - dataTestSubj?: string; + 'data-test-subj'?: string; } interface Props { @@ -34,7 +47,7 @@ interface Props { timeFieldName?: string; createDataView: boolean; showFilebeatFlyout(): void; - additionalLinks: ResultLink[]; + getAdditionalLinks?: GetAdditionalLinks; } interface GlobalState { @@ -51,7 +64,7 @@ export const ResultsLinks: FC = ({ timeFieldName, createDataView, showFilebeatFlyout, - additionalLinks, + getAdditionalLinks, }) => { const { services: { @@ -70,7 +83,7 @@ export const ResultsLinks: FC = ({ const [discoverLink, setDiscoverLink] = useState(''); const [indexManagementLink, setIndexManagementLink] = useState(''); const [dataViewsManagementLink, setDataViewsManagementLink] = useState(''); - const [generatedLinks, setGeneratedLinks] = useState>({}); + const [asyncHrefCards, setAsyncHrefCards] = useState(); useEffect(() => { let unmounted = false; @@ -93,22 +106,30 @@ export const ResultsLinks: FC = ({ getDiscoverUrl(); - Promise.all( - additionalLinks.map(async ({ canDisplay, getUrl }) => { - if ((await canDisplay({ indexPatternId: dataViewId })) === false) { - return null; - } - return getUrl({ globalState, indexPatternId: dataViewId }); - }) - ).then((urls) => { - const linksById = urls.reduce((acc, url, i) => { - if (url !== null) { - acc[additionalLinks[i].id] = url; - } - return acc; - }, {} as Record); - setGeneratedLinks(linksById); - }); + if (Array.isArray(getAdditionalLinks)) { + Promise.all( + getAdditionalLinks.map(async (asyncCardGetter) => { + const results = await asyncCardGetter({ + dataViewId, + }); + if (Array.isArray(results)) { + return await Promise.all( + results.map(async (c) => ({ + ...c, + canDisplay: await c.canDisplay(), + href: await c.getUrl(), + })) + ); + } + }) + ).then((cards) => { + setAsyncHrefCards( + flatten(cards) + .filter(isDefined) + .filter((d) => d.canDisplay === true) + ); + }); + } if (!unmounted) { setIndexManagementLink( @@ -244,16 +265,15 @@ export const ResultsLinks: FC = ({ onClick={showFilebeatFlyout} /> - {additionalLinks - .filter(({ id }) => generatedLinks[id] !== undefined) - .map((link) => ( + {Array.isArray(asyncHrefCards) && + asyncHrefCards.map((link) => ( } data-test-subj="fileDataVisLink" title={link.title} description={link.description} - href={generatedLinks[link.id]} + href={link.href} /> ))} diff --git a/x-pack/plugins/data_visualizer/public/application/common/util/is_defined.ts b/x-pack/plugins/data_visualizer/public/application/common/util/is_defined.ts new file mode 100644 index 0000000000000..ead91eafc2d4e --- /dev/null +++ b/x-pack/plugins/data_visualizer/public/application/common/util/is_defined.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export function isDefined(argument: T | undefined | null): argument is T { + return argument !== undefined && argument !== null; +} diff --git a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_data_visualizer_view.js b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_data_visualizer_view.js index 0d3408a77e7c8..9495c599e73d4 100644 --- a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_data_visualizer_view.js +++ b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_data_visualizer_view.js @@ -369,7 +369,7 @@ export class FileDataVisualizerView extends Component { hideBottomBar={this.hideBottomBar} savedObjectsClient={this.savedObjectsClient} fileUpload={this.props.fileUpload} - resultsLinks={this.props.resultsLinks} + getAdditionalLinks={this.props.getAdditionalLinks} capabilities={this.props.capabilities} /> diff --git a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/import_view.js b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/import_view.js index c5021f930c942..006bd9e3356f7 100644 --- a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/import_view.js +++ b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/import_view.js @@ -585,7 +585,7 @@ export class ImportView extends Component { timeFieldName={timeFieldName} createDataView={createDataView} showFilebeatFlyout={this.showFilebeatFlyout} - additionalLinks={this.props.resultsLinks ?? []} + getAdditionalLinks={this.props.getAdditionalLinks ?? []} /> {isFilebeatFlyoutVisible && ( diff --git a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/file_data_visualizer.tsx b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/file_data_visualizer.tsx index 60a67ef8e33d5..0238659b1f348 100644 --- a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/file_data_visualizer.tsx +++ b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/file_data_visualizer.tsx @@ -11,14 +11,14 @@ import { getCoreStart, getPluginsStart } from '../../kibana_services'; // @ts-ignore import { FileDataVisualizerView } from './components/file_data_visualizer_view'; -import { ResultLink } from '../common/components/results_links'; +import { GetAdditionalLinks } from '../common/components/results_links'; interface Props { - additionalLinks?: ResultLink[]; + getAdditionalLinks?: GetAdditionalLinks; } export type FileDataVisualizerSpec = typeof FileDataVisualizer; -export const FileDataVisualizer: FC = ({ additionalLinks }) => { +export const FileDataVisualizer: FC = ({ getAdditionalLinks }) => { const coreStart = getCoreStart(); const { data, maps, embeddable, discover, share, security, fileUpload, cloud } = getPluginsStart(); @@ -45,7 +45,7 @@ export const FileDataVisualizer: FC = ({ additionalLinks }) => { savedObjectsClient={coreStart.savedObjects.client} http={coreStart.http} fileUpload={fileUpload} - resultsLinks={additionalLinks} + getAdditionalLinks={getAdditionalLinks} capabilities={coreStart.application.capabilities} /> diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/actions_panel/actions_panel.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/actions_panel/actions_panel.tsx index c3bb0eef960ba..5d055ed993575 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/actions_panel/actions_panel.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/actions_panel/actions_panel.tsx @@ -11,28 +11,31 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { EuiSpacer, EuiTitle } from '@elastic/eui'; import { DataView } from '@kbn/data-views-plugin/public'; +import { flatten } from 'lodash'; +import { LinkCardProps } from '../../../common/components/link_card/link_card'; import { useDataVisualizerKibana } from '../../../kibana_context'; import { useUrlState } from '../../../common/util/url_state'; import { LinkCard } from '../../../common/components/link_card'; -import { ResultLink } from '../../../common/components/results_links'; +import { GetAdditionalLinks } from '../../../common/components/results_links'; +import { isDefined } from '../../../common/util/is_defined'; interface Props { dataView: DataView; searchString?: string | { [key: string]: any }; searchQueryLanguage?: string; - additionalLinks: ResultLink[]; + getAdditionalLinks?: GetAdditionalLinks; } export const ActionsPanel: FC = ({ dataView, searchString, searchQueryLanguage, - additionalLinks, + getAdditionalLinks, }) => { const [globalState] = useUrlState('_g'); const [discoverLink, setDiscoverLink] = useState(''); - const [generatedLinks, setGeneratedLinks] = useState>({}); + const [asyncHrefCards, setAsyncHrefCards] = useState(); const { services: { @@ -46,6 +49,7 @@ export const ActionsPanel: FC = ({ let unmounted = false; const indexPatternId = dataView.id; + const indexPatternTitle = dataView.title; const getDiscoverUrl = async (): Promise => { const isDiscoverAvailable = capabilities.discover?.show ?? false; if (!isDiscoverAvailable) return; @@ -68,24 +72,33 @@ export const ActionsPanel: FC = ({ setDiscoverLink(discoverUrl); }; - Promise.all( - additionalLinks.map(async ({ canDisplay, getUrl }) => { - if ((await canDisplay({ indexPatternId })) === false) { - return null; - } - return getUrl({ globalState, indexPatternId }); - }) - ).then((urls) => { - const linksById = urls.reduce((acc, url, i) => { - if (url !== null) { - acc[additionalLinks[i].id] = url; - } - return acc; - }, {} as Record); - setGeneratedLinks(linksById); - }); - + if (Array.isArray(getAdditionalLinks) && indexPatternId !== undefined) { + Promise.all( + getAdditionalLinks.map(async (asyncCardGetter) => { + const results = await asyncCardGetter({ + dataViewId: indexPatternId, + dataViewTitle: indexPatternTitle, + }); + if (Array.isArray(results)) { + return await Promise.all( + results.map(async (c) => ({ + ...c, + canDisplay: await c.canDisplay(), + href: await c.getUrl(), + })) + ); + } + }) + ).then((cards) => { + setAsyncHrefCards( + flatten(cards) + .filter(isDefined) + .filter((d) => d.canDisplay === true) + ); + }); + } getDiscoverUrl(); + return () => { unmounted = true; }; @@ -96,8 +109,8 @@ export const ActionsPanel: FC = ({ globalState, capabilities, discover, - additionalLinks, data.query, + getAdditionalLinks, ]); // Note we use display:none for the DataRecognizer section as it needs to be @@ -105,20 +118,6 @@ export const ActionsPanel: FC = ({ // controls whether the recognizer section is ultimately displayed. return (
- {additionalLinks - .filter(({ id }) => generatedLinks[id] !== undefined) - .map((link) => ( - <> - - - - ))} {discoverLink && ( <> @@ -147,8 +146,23 @@ export const ActionsPanel: FC = ({ } data-test-subj="dataVisualizerViewInDiscoverCard" /> + )} + + {Array.isArray(asyncHrefCards) && + asyncHrefCards.map((link) => ( + <> + + + + ))}
); }; diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx index 9ce1fb4f552e5..d892b5f159434 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx @@ -48,7 +48,7 @@ import { DatePickerWrapper } from '../../../common/components/date_picker_wrappe import { HelpMenu } from '../../../common/components/help_menu'; import { createMergedEsQuery } from '../../utils/saved_search_utils'; import { DataVisualizerDataViewManagement } from '../data_view_management'; -import { ResultLink } from '../../../common/components/results_links'; +import { GetAdditionalLinks } from '../../../common/components/results_links'; import { useDataVisualizerGridData } from '../../hooks/use_data_visualizer_grid_data'; import { DataVisualizerGridInput } from '../../embeddables/grid_embeddable/grid_embeddable'; import './_index.scss'; @@ -110,7 +110,7 @@ export interface IndexDataVisualizerViewProps { currentDataView: DataView; currentSavedSearch: SavedSearchSavedObject | null; currentSessionId?: string; - additionalLinks?: ResultLink[]; + getAdditionalLinks?: GetAdditionalLinks; } const restorableDefaults = getDefaultDataVisualizerListState(); @@ -129,7 +129,7 @@ export const IndexDataVisualizerView: FC = (dataVi dataVisualizerProps.currentSavedSearch ); - const { currentDataView, additionalLinks, currentSessionId } = dataVisualizerProps; + const { currentDataView, currentSessionId, getAdditionalLinks } = dataVisualizerProps; useEffect(() => { if (dataVisualizerProps?.currentSavedSearch !== undefined) { @@ -487,7 +487,7 @@ export const IndexDataVisualizerView: FC = (dataVi dataView={currentDataView} searchQueryLanguage={searchQueryLanguage} searchString={searchString} - additionalLinks={additionalLinks ?? []} + getAdditionalLinks={getAdditionalLinks} /> diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.scss b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.scss index 6f274921d5ebf..6b0624fae2757 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.scss +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.scss @@ -3,6 +3,10 @@ padding: $euiSizeS; } +.dvSearchPanel__container { + align-items: baseline; +} + @include euiBreakpoint('xs', 's', 'm', 'l') { .dvSearchPanel__container { flex-direction: column; @@ -13,8 +17,4 @@ .dvSearchPanel__controls { padding: 0; } - // prevent margin -16 which scrunches the filter bar - .globalFilterGroup__wrapper-isVisible { - margin: 0 !important; - } } diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.tsx index e7ac50c906660..7d218d98afa39 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.tsx @@ -120,7 +120,6 @@ export const SearchPanel: FC = ({ return ( ; - additionalLinks: ResultLink[]; + getAdditionalLinks?: GetAdditionalLinks; } +export type IndexDataVisualizerSpec = typeof IndexDataVisualizer; export const getLocatorParams = (params: { dataViewId?: string; @@ -73,7 +72,7 @@ export const getLocatorParams = (params: { export const DataVisualizerUrlStateContextProvider: FC< DataVisualizerUrlStateContextProviderProps -> = ({ IndexDataVisualizerComponent, additionalLinks }) => { +> = ({ IndexDataVisualizerComponent, getAdditionalLinks }) => { const { services } = useDataVisualizerKibana(); const { data: { dataViews, search }, @@ -247,8 +246,8 @@ export const DataVisualizerUrlStateContextProvider: FC< ) : (
@@ -257,7 +256,9 @@ export const DataVisualizerUrlStateContextProvider: FC< ); }; -export const IndexDataVisualizer: FC<{ additionalLinks: ResultLink[] }> = ({ additionalLinks }) => { +export const IndexDataVisualizer: FC<{ + getAdditionalLinks?: GetAdditionalLinks; +}> = ({ getAdditionalLinks }) => { const coreStart = getCoreStart(); const { data, @@ -294,7 +295,7 @@ export const IndexDataVisualizer: FC<{ additionalLinks: ResultLink[] }> = ({ add diff --git a/x-pack/plugins/data_visualizer/public/index.ts b/x-pack/plugins/data_visualizer/public/index.ts index 4a579f4e3abcc..7c4208106cb75 100644 --- a/x-pack/plugins/data_visualizer/public/index.ts +++ b/x-pack/plugins/data_visualizer/public/index.ts @@ -18,4 +18,8 @@ export type { IndexDataVisualizerSpec, IndexDataVisualizerViewProps, } from './application'; -export type { ResultLink } from './application/common/components/results_links'; +export type { + GetAdditionalLinksParams, + ResultLink, + GetAdditionalLinks, +} from './application/common/components/results_links'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.tsx index 3807234fd5c11..ea5abb74d5ac0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.tsx @@ -92,7 +92,7 @@ export const AuditLogsModal: React.FC = () => { }, { type: 'field', - field: 'outcome', + field: 'event.outcome', header: i18n.translate( 'xpack.enterpriseSearch.appSearch.engines.auditLogsModal.headers.outcome', { @@ -101,7 +101,8 @@ export const AuditLogsModal: React.FC = () => { ), }, { - type: 'message', + type: 'field', + field: 'message', width: '50%', }, ]} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/index.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/index.ts index 24fa58e60e853..297bbaf36c454 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/index.ts @@ -6,3 +6,4 @@ */ export { searchIndices } from './search_indices.mock'; +export { searchEngines } from './search_engines.mock'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_engines.mock.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_engines.mock.ts new file mode 100644 index 0000000000000..775f7dde83d87 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_engines.mock.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 { Engine } from '../../app_search/components/engine/types'; + +// TODO populate them +export const searchEngines = [ + { name: 'My First Search Engine' }, + { name: 'Another Search Engine' }, + { name: 'Dharma Initiative Research' }, + { name: 'Flight 815 Customer Feedback' }, + { name: 'The Swan Station Manuals' }, + { name: 'The Hydra Station Manuals' }, +] as Engine[]; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.test.tsx new file mode 100644 index 0000000000000..ebc8fe1995f5f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.test.tsx @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import '../../../__mocks__/shallow_useeffect.mock'; +import { setMockValues, setMockActions } from '../../../__mocks__/kea_logic'; +import { searchIndices, searchEngines } from '../../__mocks__'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiBasicTable } from '@elastic/eui'; + +import { AddContentEmptyPrompt } from '../../../shared/add_content_empty_prompt'; +import { ElasticsearchResources } from '../../../shared/elasticsearch_resources'; +import { GettingStartedSteps } from '../../../shared/getting_started_steps'; + +import { SearchIndices } from './search_indices'; + +const mockActions = { + initPage: jest.fn(), +}; + +describe('SearchIndices', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('Empty state', () => { + it('renders when both Search Indices and Search Engines empty', () => { + setMockValues({ + searchIndices: [], + searchEngines: [], + }); + setMockActions(mockActions); + const wrapper = shallow(); + + expect(wrapper.find(AddContentEmptyPrompt)).toHaveLength(1); + expect(wrapper.find(EuiBasicTable)).toHaveLength(0); + + expect(wrapper.find(GettingStartedSteps)).toHaveLength(1); + expect(wrapper.find(ElasticsearchResources)).toHaveLength(1); + }); + + it('renders complete empty state when only Search Indices empty', () => { + setMockValues({ + searchIndices: [], + searchEngines, + }); + setMockActions(mockActions); + const wrapper = shallow(); + + expect(wrapper.find(AddContentEmptyPrompt)).toHaveLength(1); + expect(wrapper.find(EuiBasicTable)).toHaveLength(0); + + expect(wrapper.find(GettingStartedSteps)).toHaveLength(1); + expect(wrapper.find(ElasticsearchResources)).toHaveLength(1); + }); + + it('renders when only Search Engines empty', () => { + setMockValues({ + searchIndices, + searchEngines: [], + }); + setMockActions(mockActions); + const wrapper = shallow(); + + expect(wrapper.find(AddContentEmptyPrompt)).toHaveLength(0); + expect(wrapper.find(EuiBasicTable)).toHaveLength(1); + + expect(wrapper.find(GettingStartedSteps)).toHaveLength(1); + expect(wrapper.find(ElasticsearchResources)).toHaveLength(1); + }); + }); + + it('renders with Data', () => { + setMockValues({ + searchIndices, + searchEngines, + }); + setMockActions(mockActions); + + const wrapper = shallow(); + + expect(wrapper.find(AddContentEmptyPrompt)).toHaveLength(0); + expect(wrapper.find(EuiBasicTable)).toHaveLength(1); + + expect(wrapper.find(GettingStartedSteps)).toHaveLength(0); + expect(wrapper.find(ElasticsearchResources)).toHaveLength(0); + + expect(mockActions.initPage).toHaveBeenCalledTimes(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx index 6c82d56572cec..3fc8f4abca0c1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx @@ -5,21 +5,34 @@ * 2.0. */ -import { searchIndices } from '../../__mocks__'; - -import React from 'react'; +import React, { useEffect } from 'react'; import { generatePath } from 'react-router-dom'; -import { EuiBasicTable, EuiButton, HorizontalAlignment } from '@elastic/eui'; +import { useValues, useActions } from 'kea'; + +import { + EuiBasicTable, + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiTitle, + HorizontalAlignment, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { AddContentEmptyPrompt } from '../../../shared/add_content_empty_prompt'; +import { ElasticsearchResources } from '../../../shared/elasticsearch_resources'; +import { GettingStartedSteps } from '../../../shared/getting_started_steps'; import { EuiLinkTo, EuiButtonIconTo } from '../../../shared/react_router_helpers'; import { SEARCH_INDEX_OVERVIEW_PATH, NEW_INDEX_PATH } from '../../routes'; import { SearchIndex } from '../../types'; import { EnterpriseSearchContentPageTemplate } from '../layout/page_template'; +import { SearchIndicesLogic } from './search_indices_logic'; + export const baseBreadcrumbs = [ i18n.translate('xpack.enterpriseSearch.content.searchIndices.content.breadcrumb', { defaultMessage: 'Content', @@ -30,6 +43,22 @@ export const baseBreadcrumbs = [ ]; export const SearchIndices: React.FC = () => { + const { initPage, searchEnginesLoadSuccess, searchIndicesLoadSuccess } = + useActions(SearchIndicesLogic); + const { searchIndices, searchEngines } = useValues(SearchIndicesLogic); + + useEffect(() => { + initPage(); + }, []); + + // TODO This is for easy testing until we have the backend, please remove this before the release + // @ts-ignore + window.contentActions = { + initPage, + searchIndicesLoadSuccess, + searchEnginesLoadSuccess, + }; + // TODO: Replace with a real list of indices const columns = [ { @@ -114,22 +143,72 @@ export const SearchIndices: React.FC = () => { ); - return ( - + +

+ {i18n.translate('xpack.enterpriseSearch.content.searchIndices.searchIndices.stepsTitle', { + defaultMessage: 'Build beautiful search experiences with Enterprise Search', + })} +

+
+ + + + + + + + + + + ); + + const pageTitle = + searchIndices.length !== 0 + ? i18n.translate('xpack.enterpriseSearch.content.searchIndices.searchIndices.pageTitle', { + defaultMessage: 'Content', + }) + : i18n.translate( + 'xpack.enterpriseSearch.content.searchIndices.searchIndices.emptyPageTitle', { - defaultMessage: 'Search indices', + defaultMessage: 'Welcome to Enterprise Search', } - ), - rightSideItems: [createNewIndexButton], - }} - > - -
+ ); + + return ( + <> + + {searchIndices.length !== 0 ? ( + <> + +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.searchIndices.searchIndices.tableTitle', + { + defaultMessage: 'Search Indices', + } + )} +

+
+ + + + ) : ( + + )} + + {(searchEngines.length === 0 || searchIndices.length === 0) && engineSteps} +
+ ) + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices_logic.test.ts new file mode 100644 index 0000000000000..36934bf61183d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices_logic.test.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { LogicMounter } from '../../../__mocks__/kea_logic'; +import { searchIndices, searchEngines } from '../../__mocks__'; + +import { SearchIndicesLogic } from './search_indices_logic'; + +describe('SearchIndicesLogic', () => { + const { mount } = new LogicMounter(SearchIndicesLogic); + + const DEFAULT_VALUES = { + searchEngines: [], + searchIndices: [], + }; + + beforeEach(() => { + jest.clearAllMocks(); + mount(); + }); + + it('has expected default values', () => { + expect(SearchIndicesLogic.values).toEqual(DEFAULT_VALUES); + }); + + describe('actions', () => { + describe('searchIndicesLoadSuccess', () => { + it('should set searchIndices', () => { + SearchIndicesLogic.actions.searchIndicesLoadSuccess(searchIndices); + expect(SearchIndicesLogic.values).toEqual({ + ...DEFAULT_VALUES, + searchIndices, + }); + }); + }); + describe('searchEnginesLoadSuccess', () => { + it('should set searchEngines', () => { + SearchIndicesLogic.actions.searchEnginesLoadSuccess(searchEngines); + expect(SearchIndicesLogic.values).toEqual({ + ...DEFAULT_VALUES, + searchEngines, + }); + }); + }); + }); + + describe.skip('listeners', () => { + describe('loadSearchEngines', () => {}); + describe('loadSearchIndices', () => {}); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices_logic.ts new file mode 100644 index 0000000000000..3ad053b339b63 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices_logic.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + searchIndices as searchIndicesMock, + searchEngines as searchEnginesMock, +} from '../../__mocks__'; + +import { kea, MakeLogicType } from 'kea'; + +import { Engine } from '../../../app_search/components/engine/types'; +import { flashAPIErrors } from '../../../shared/flash_messages'; +import { SearchIndex } from '../../types'; + +export interface SearchIndicesValues { + searchIndices: SearchIndex[]; + searchEngines: Engine[]; +} + +export interface SearchIndicesActions { + initPage(): void; + loadSearchEngines(): void; + searchEnginesLoadSuccess(searchEngines: Engine[]): Engine[]; // TODO proper types when backend ready + loadSearchIndices(): void; + searchIndicesLoadSuccess(searchIndices: SearchIndex[]): SearchIndex[]; // TODO proper types when backend ready +} + +export const SearchIndicesLogic = kea>({ + path: ['enterprise_search', 'content', 'search_indices', 'search_indices_logic'], + actions: { + initPage: true, + loadSearchIndices: true, + searchIndicesLoadSuccess: (searchIndices) => searchIndices, + loadSearchEngines: true, + searchEnginesLoadSuccess: (searchEngines) => searchEngines, + }, + reducers: { + searchIndices: [ + [], + { + searchIndicesLoadSuccess: (_, searchIndices) => searchIndices, + }, + ], + searchEngines: [ + [], + { + searchEnginesLoadSuccess: (_, searchEngines) => searchEngines, + }, + ], + }, + listeners: ({ actions }) => ({ + initPage: async () => { + actions.loadSearchEngines(); + actions.loadSearchIndices(); + }, + loadSearchEngines: async () => { + try { + // TODO replace with actual backend call, add test cases + const response = await Promise.resolve(searchEnginesMock); + actions.searchEnginesLoadSuccess(response); + } catch (e) { + flashAPIErrors(e); + } + }, + loadSearchIndices: async () => { + try { + // TODO replace with actual backend call, add test cases + const response = await Promise.resolve(searchIndicesMock); + actions.searchIndicesLoadSuccess(response); + } catch (e) { + flashAPIErrors(e); + } + }, + }), +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/types.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/types.ts index 48e0b53d35f6c..30af95427fe43 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/types.ts @@ -6,7 +6,7 @@ */ /** - * As of 2022-04-04, this shapre is still in debate. Specifically, the `source_type` will be changing as we get closer to 8.3. + * As of 2022-04-04, this shape is still in debate. Specifically, the `source_type` will be changing as we get closer to 8.3. * These merely serve as placeholders for static data for now. */ export interface SearchIndex { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.test.tsx index b9e23f00f06b5..667a643c13cd4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.test.tsx @@ -23,7 +23,7 @@ describe('AddContentEmptyPrompt', () => { it('renders', () => { expect(wrapper.find('h2').text()).toEqual('Add content to Enterprise Search'); expect(wrapper.find(EuiLinkTo).prop('to')).toEqual( - '/app/enterprise_search/content/search_indices' + '/app/enterprise_search/content/search_indices/new_index' ); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.tsx index 0679739a7683c..6025fbbdb96c0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.tsx @@ -24,7 +24,7 @@ import { import { i18n } from '@kbn/i18n'; import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../common/constants'; -import { SEARCH_INDICES_PATH } from '../../enterprise_search_content/routes'; +import { NEW_INDEX_PATH } from '../../enterprise_search_content/routes'; import { EuiLinkTo } from '../react_router_helpers'; import searchIndicesIllustration from './search_indices.svg'; @@ -58,7 +58,7 @@ export const AddContentEmptyPrompt: React.FC = () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.test.tsx index 0595e39475b4b..200a93041c087 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.test.tsx @@ -18,6 +18,8 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { EuiSteps } from '@elastic/eui'; +import { ENTERPRISE_SEARCH_OVERVIEW_PLUGIN } from '../../../../common/constants'; + import { EuiLinkTo } from '../react_router_helpers'; import { IconRow } from './icon_row'; @@ -48,7 +50,9 @@ describe('GettingStartedSteps', () => { expect(steps[1].title).toEqual('Build a search experience'); expect(steps[1].status).toEqual('incomplete'); - expect(steps[1].children.find(EuiLinkTo).prop('to')).toEqual('/elasticsearch_guide'); + expect(steps[1].children.find(EuiLinkTo).prop('to')).toEqual( + ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.URL + '/elasticsearch_guide' + ); expect(steps[2].title).toEqual('Tune your search relevance'); expect(steps[2].status).toEqual('incomplete'); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.tsx index e9a3f477c7049..a730fa2e7df2a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.tsx @@ -21,13 +21,18 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { ENTERPRISE_SEARCH_OVERVIEW_PLUGIN } from '../../../../common/constants'; import { ELASTICSEARCH_GUIDE_PATH } from '../../enterprise_search_overview/routes'; import { EuiLinkTo } from '../react_router_helpers'; import { IconRow } from './icon_row'; -export const GettingStartedSteps: React.FC = () => { +export interface GettingStartedStepsProps { + step?: 'first' | 'second'; +} + +export const GettingStartedSteps: React.FC = ({ step = 'first' }) => { // TODO replace with logic file const [isPopoverOpen, setIsPopoverOpen] = useState(false); @@ -58,7 +63,7 @@ export const GettingStartedSteps: React.FC = () => { ), - status: 'current', + status: (step === 'first' && 'current') || 'complete', }, { title: i18n.translate( @@ -84,6 +89,7 @@ export const GettingStartedSteps: React.FC = () => { { - +   {i18n.translate( @@ -151,7 +160,7 @@ export const GettingStartedSteps: React.FC = () => { ), - status: 'incomplete', + status: (step === 'second' && 'current') || 'incomplete', }, { title: i18n.translate( diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.test.tsx index b69303aae2106..0213aa26d5ef3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.test.tsx @@ -21,6 +21,8 @@ import { SOURCES_PATH, PRIVATE_SOURCES_PATH, SOURCE_DETAILS_PATH, + getAddPath, + getEditPath, } from './routes'; const TestComponent = ({ id, isOrg }: { id: string; isOrg?: boolean }) => { @@ -86,3 +88,32 @@ describe('getReindexJobRoute', () => { ); }); }); + +describe('getAddPath', () => { + it('should handle a service type', () => { + expect(getAddPath('share_point')).toEqual('/sources/add/share_point'); + }); + + it('should should handle an external service type with no base service type', () => { + expect(getAddPath('external')).toEqual('/sources/add/external'); + }); + + it('should should handle an external service type with a base service type', () => { + expect(getAddPath('external', 'share_point')).toEqual('/sources/add/share_point/external'); + }); + it('should should handle a custom service type with no base service type', () => { + expect(getAddPath('external')).toEqual('/sources/add/external'); + }); + + it('should should handle a custom service type with a base service type', () => { + expect(getAddPath('custom', 'share_point_server')).toEqual( + '/sources/add/share_point_server/custom' + ); + }); +}); + +describe('getEditPath', () => { + it('should handle a service type', () => { + expect(getEditPath('share_point')).toEqual('/settings/connectors/share_point/edit'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts index cbcd1d885b120..fe1be10aa3b06 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts @@ -77,6 +77,14 @@ export const getReindexJobRoute = ( isOrganization: boolean ) => getSourcesPath(generatePath(REINDEX_JOB_PATH, { sourceId, activeReindexJobId }), isOrganization); -export const getAddPath = (serviceType: string): string => `${SOURCES_PATH}/add/${serviceType}`; + +export const getAddPath = (serviceType: string, baseServiceType?: string): string => { + const baseServiceTypePath = baseServiceType + ? `${baseServiceType}/${serviceType}` + : `${serviceType}`; + return `${SOURCES_PATH}/add/${baseServiceTypePath}`; +}; + +// TODO this should handle base service type once we are getting it back from registered external connectors export const getEditPath = (serviceType: string): string => `${ORG_SETTINGS_CONNECTORS_PATH}/${serviceType}/edit`; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts index 19263f057e40f..32353230b36aa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts @@ -30,8 +30,6 @@ export interface Group { createdAt: string; updatedAt: string; contentSources: ContentSource[]; - users: User[]; - usersCount: number; color?: string; } @@ -74,18 +72,14 @@ export interface Configuration { export interface SourceDataItem { name: string; - iconName: string; categories?: string[]; serviceType: string; + baseServiceType?: string; configuration: Configuration; - configured?: boolean; connected?: boolean; features?: Features; objTypes?: string[]; accountContextOnly: boolean; - internalConnectorAvailable?: boolean; - externalConnectorAvailable?: boolean; - customConnectorAvailable?: boolean; isBeta?: boolean; } diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/has_multiple_connector_options.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/has_multiple_connector_options.ts deleted file mode 100644 index fbfda1ddf8d5e..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/has_multiple_connector_options.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 { SourceDataItem } from '../types'; - -export const hasMultipleConnectorOptions = ({ - internalConnectorAvailable, - externalConnectorAvailable, - customConnectorAvailable, -}: SourceDataItem) => - [externalConnectorAvailable, internalConnectorAvailable, customConnectorAvailable].filter( - (available) => !!available - ).length > 1; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/index.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/index.ts index c66a6d1ca0fc0..6f6af758c0283 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/utils/index.ts @@ -11,6 +11,5 @@ export { mimeType } from './mime_types'; export { readUploadedFileAsBase64 } from './read_uploaded_file_as_base64'; export { readUploadedFileAsText } from './read_uploaded_file_as_text'; export { handlePrivateKeyUpload } from './handle_private_key_upload'; -export { hasMultipleConnectorOptions } from './has_multiple_connector_options'; export { isNotNullish } from './is_not_nullish'; export { sortByName } from './sort_by_name'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/add_custom_source.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/add_custom_source.test.tsx index b606f9d7f56fd..9ff64dfe4f65b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/add_custom_source.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/add_custom_source.test.tsx @@ -7,6 +7,7 @@ import '../../../../../../__mocks__/shallow_useeffect.mock'; import { setMockValues } from '../../../../../../__mocks__/kea_logic'; +import { mockUseParams } from '../../../../../../__mocks__/react_router'; import { sourceConfigData } from '../../../../../__mocks__/content_sources.mock'; import React from 'react'; @@ -17,7 +18,6 @@ import { WorkplaceSearchPageTemplate, PersonalDashboardLayout, } from '../../../../../components/layout'; -import { staticSourceData } from '../../../source_data'; import { AddCustomSource } from './add_custom_source'; import { AddCustomSourceSteps } from './add_custom_source_logic'; @@ -25,11 +25,6 @@ import { ConfigureCustom } from './configure_custom'; import { SaveCustom } from './save_custom'; describe('AddCustomSource', () => { - const props = { - sourceData: staticSourceData[0], - initialValues: undefined, - }; - const values = { sourceConfigData, isOrganization: true, @@ -37,17 +32,26 @@ describe('AddCustomSource', () => { beforeEach(() => { setMockValues({ ...values }); + mockUseParams.mockReturnValue({ baseServiceType: 'share_point_server' }); }); it('renders', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(WorkplaceSearchPageTemplate)).toHaveLength(1); }); + it('returns null if there is no matching source data for the service type', () => { + mockUseParams.mockReturnValue({ baseServiceType: 'doesnt_exist' }); + + const wrapper = shallow(); + + expect(wrapper.isEmptyRender()).toBe(true); + }); + it('should show correct layout for personal dashboard', () => { setMockValues({ isOrganization: false }); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(WorkplaceSearchPageTemplate)).toHaveLength(0); expect(wrapper.find(PersonalDashboardLayout)).toHaveLength(1); @@ -55,14 +59,14 @@ describe('AddCustomSource', () => { it('should show Configure Custom for custom configuration step', () => { setMockValues({ currentStep: AddCustomSourceSteps.ConfigureCustomStep }); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(ConfigureCustom)).toHaveLength(1); }); it('should show Save Custom for save custom step', () => { setMockValues({ currentStep: AddCustomSourceSteps.SaveCustomStep }); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(SaveCustom)).toHaveLength(1); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/add_custom_source.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/add_custom_source.tsx index c2f6afba032c7..b15129665a7d4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/add_custom_source.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/add_custom_source.tsx @@ -7,6 +7,8 @@ import React from 'react'; +import { useParams } from 'react-router-dom'; + import { useValues } from 'kea'; import { AppLogic } from '../../../../../app_logic'; @@ -16,27 +18,38 @@ import { } from '../../../../../components/layout'; import { NAV } from '../../../../../constants'; -import { SourceDataItem } from '../../../../../types'; +import { getSourceData } from '../../../source_data'; import { AddCustomSourceLogic, AddCustomSourceSteps } from './add_custom_source_logic'; import { ConfigureCustom } from './configure_custom'; import { SaveCustom } from './save_custom'; -interface Props { - sourceData: SourceDataItem; - initialValue?: string; -} -export const AddCustomSource: React.FC = ({ sourceData, initialValue = '' }) => { - const addCustomSourceLogic = AddCustomSourceLogic({ sourceData, initialValue }); +export const AddCustomSource: React.FC = () => { + const { baseServiceType } = useParams<{ baseServiceType?: string }>(); + const sourceData = getSourceData('custom', baseServiceType); + + const addCustomSourceLogic = AddCustomSourceLogic({ + baseServiceType, + initialValue: sourceData?.name, + }); + const { currentStep } = useValues(addCustomSourceLogic); const { isOrganization } = useValues(AppLogic); + if (!sourceData) { + return null; + } + const Layout = isOrganization ? WorkplaceSearchPageTemplate : PersonalDashboardLayout; return ( - - {currentStep === AddCustomSourceSteps.ConfigureCustomStep && } - {currentStep === AddCustomSourceSteps.SaveCustomStep && } + + {currentStep === AddCustomSourceSteps.ConfigureCustomStep && ( + + )} + {currentStep === AddCustomSourceSteps.SaveCustomStep && ( + + )} ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/add_custom_source_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/add_custom_source_logic.test.ts index d2187bd0b21a1..2ca3462da0f57 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/add_custom_source_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/add_custom_source_logic.test.ts @@ -14,7 +14,6 @@ import { sourceConfigData } from '../../../../../__mocks__/content_sources.mock' import { nextTick } from '@kbn/test-jest-helpers'; -import { docLinks } from '../../../../../../shared/doc_links'; import { itShowsServerErrorAsFlashMessage } from '../../../../../../test_helpers'; jest.mock('../../../../../app_logic', () => ({ @@ -22,35 +21,17 @@ jest.mock('../../../../../app_logic', () => ({ })); import { AppLogic } from '../../../../../app_logic'; -import { SOURCE_NAMES } from '../../../../../constants'; -import { CustomSource, SourceDataItem } from '../../../../../types'; +import { CustomSource } from '../../../../../types'; import { AddCustomSourceLogic, AddCustomSourceSteps } from './add_custom_source_logic'; -const CUSTOM_SOURCE_DATA_ITEM: SourceDataItem = { - name: SOURCE_NAMES.CUSTOM, - iconName: SOURCE_NAMES.CUSTOM, - serviceType: 'custom', - configuration: { - isPublicKey: false, - hasOauthRedirect: false, - needsBaseUrl: false, - documentationUrl: docLinks.workplaceSearchCustomSources, - applicationPortalUrl: '', - }, - accountContextOnly: false, -}; - const DEFAULT_VALUES = { currentStep: AddCustomSourceSteps.ConfigureCustomStep, buttonLoading: false, customSourceNameValue: '', newCustomSource: {} as CustomSource, - sourceData: CUSTOM_SOURCE_DATA_ITEM, }; -const MOCK_PROPS = { initialValue: '', sourceData: CUSTOM_SOURCE_DATA_ITEM }; - const MOCK_NAME = 'name'; describe('AddCustomSourceLogic', () => { @@ -60,7 +41,7 @@ describe('AddCustomSourceLogic', () => { beforeEach(() => { jest.clearAllMocks(); - mount({}, MOCK_PROPS); + mount({}); }); it('has expected default values', () => { @@ -112,12 +93,9 @@ describe('AddCustomSourceLogic', () => { describe('listeners', () => { beforeEach(() => { - mount( - { - customSourceNameValue: MOCK_NAME, - }, - MOCK_PROPS - ); + mount({ + customSourceNameValue: MOCK_NAME, + }); }); describe('organization context', () => { @@ -151,11 +129,7 @@ describe('AddCustomSourceLogic', () => { customSourceNameValue: MOCK_NAME, }, { - ...MOCK_PROPS, - sourceData: { - ...CUSTOM_SOURCE_DATA_ITEM, - serviceType: 'sharepoint-server', - }, + baseServiceType: 'share_point_server', } ); @@ -165,7 +139,7 @@ describe('AddCustomSourceLogic', () => { body: JSON.stringify({ service_type: 'custom', name: MOCK_NAME, - base_service_type: 'sharepoint-server', + base_service_type: 'share_point_server', }), }); }); @@ -199,11 +173,7 @@ describe('AddCustomSourceLogic', () => { customSourceNameValue: MOCK_NAME, }, { - ...MOCK_PROPS, - sourceData: { - ...CUSTOM_SOURCE_DATA_ITEM, - serviceType: 'sharepoint-server', - }, + baseServiceType: 'share_point_server', } ); @@ -215,7 +185,7 @@ describe('AddCustomSourceLogic', () => { body: JSON.stringify({ service_type: 'custom', name: MOCK_NAME, - base_service_type: 'sharepoint-server', + base_service_type: 'share_point_server', }), } ); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/add_custom_source_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/add_custom_source_logic.ts index f85e0761f51b5..5b02fffa5892d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/add_custom_source_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/add_custom_source_logic.ts @@ -10,11 +10,11 @@ import { kea, MakeLogicType } from 'kea'; import { flashAPIErrors, clearFlashMessages } from '../../../../../../shared/flash_messages'; import { HttpLogic } from '../../../../../../shared/http'; import { AppLogic } from '../../../../../app_logic'; -import { CustomSource, SourceDataItem } from '../../../../../types'; +import { CustomSource } from '../../../../../types'; export interface AddCustomSourceProps { - sourceData: SourceDataItem; - initialValue: string; + baseServiceType?: string; + initialValue?: string; } export enum AddCustomSourceSteps { @@ -34,7 +34,6 @@ interface AddCustomSourceValues { currentStep: AddCustomSourceSteps; customSourceNameValue: string; newCustomSource: CustomSource; - sourceData: SourceDataItem; } /** @@ -67,7 +66,7 @@ export const AddCustomSourceLogic = kea< }, ], customSourceNameValue: [ - props.initialValue, + props.initialValue || '', { setCustomSourceNameValue: (_, customSourceNameValue) => customSourceNameValue, }, @@ -78,7 +77,6 @@ export const AddCustomSourceLogic = kea< setNewCustomSource: (_, newCustomSource) => newCustomSource, }, ], - sourceData: [props.sourceData], }), listeners: ({ actions, values, props }) => ({ createContentSource: async () => { @@ -90,21 +88,12 @@ export const AddCustomSourceLogic = kea< const { customSourceNameValue } = values; - const baseParams = { + const params = { service_type: 'custom', name: customSourceNameValue, + base_service_type: props.baseServiceType, }; - // pre-configured custom sources have a serviceType reflecting their target service - // we submit this as `base_service_type` to keep track of - const params = - props.sourceData.serviceType === 'custom' - ? baseParams - : { - ...baseParams, - base_service_type: props.sourceData.serviceType, - }; - try { const response = await HttpLogic.values.http.post(route, { body: JSON.stringify(params), diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/configure_custom.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/configure_custom.test.tsx index 3ed60614d294a..a0713ec530b28 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/configure_custom.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/configure_custom.test.tsx @@ -21,24 +21,24 @@ import { ConfigureCustom } from './configure_custom'; describe('ConfigureCustom', () => { const setCustomSourceNameValue = jest.fn(); const createContentSource = jest.fn(); + const sourceData = staticSourceData[1]; beforeEach(() => { setMockActions({ setCustomSourceNameValue, createContentSource }); setMockValues({ customSourceNameValue: 'name', buttonLoading: false, - sourceData: staticSourceData[1], }); }); it('renders', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(EuiForm)).toHaveLength(1); }); it('handles input change', () => { - const wrapper = shallow(); + const wrapper = shallow(); const text = 'changed for the better'; const input = wrapper.find(EuiFieldText); input.simulate('change', { target: { value: text } }); @@ -47,7 +47,7 @@ describe('ConfigureCustom', () => { }); it('handles form submission', () => { - const wrapper = shallow(); + const wrapper = shallow(); const preventDefault = jest.fn(); wrapper.find('EuiForm').simulate('submit', { preventDefault }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/configure_custom.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/configure_custom.tsx index 024dd698cc0a2..4f673f56231cc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/configure_custom.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/configure_custom.tsx @@ -21,11 +21,13 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; + import { FormattedMessage } from '@kbn/i18n-react'; import { docLinks } from '../../../../../../shared/doc_links'; import connectionIllustration from '../../../../../assets/connection_illustration.svg'; +import { SourceDataItem } from '../../../../../types'; import { SOURCE_NAME_LABEL } from '../../../constants'; import { AddSourceHeader } from '../add_source_header'; @@ -33,9 +35,13 @@ import { CONFIG_CUSTOM_BUTTON, CONFIG_CUSTOM_LINK_TEXT, CONFIG_INTRO_ALT_TEXT } import { AddCustomSourceLogic } from './add_custom_source_logic'; -export const ConfigureCustom: React.FC = () => { +interface ConfigureCustomProps { + sourceData: SourceDataItem; +} + +export const ConfigureCustom: React.FC = ({ sourceData }) => { const { setCustomSourceNameValue, createContentSource } = useActions(AddCustomSourceLogic); - const { customSourceNameValue, buttonLoading, sourceData } = useValues(AddCustomSourceLogic); + const { customSourceNameValue, buttonLoading } = useValues(AddCustomSourceLogic); const handleFormSubmit = (e: FormEvent) => { e.preventDefault(); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/save_custom.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/save_custom.test.tsx index 3de514a3e4d71..8f4e6e7205ef2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/save_custom.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/save_custom.test.tsx @@ -25,18 +25,21 @@ const mockValues = { accessToken: 'token', name: 'name', }, - sourceData: staticCustomSourceData, }; +const sourceData = staticCustomSourceData; + describe('SaveCustom', () => { + beforeAll(() => { + jest.clearAllMocks(); + setMockValues(mockValues); + }); + describe('default behavior', () => { let wrapper: ShallowWrapper; beforeAll(() => { - jest.clearAllMocks(); - setMockValues(mockValues); - - wrapper = shallow(); + wrapper = shallow(); }); it('contains a button back to the sources list', () => { @@ -52,20 +55,14 @@ describe('SaveCustom', () => { let wrapper: ShallowWrapper; beforeAll(() => { - jest.clearAllMocks(); - setMockValues({ - ...mockValues, - sourceData: { - ...staticCustomSourceData, - serviceType: 'sharepoint-server', - configuration: { - ...staticCustomSourceData.configuration, - githubRepository: 'elastic/sharepoint-server-connector', - }, - }, - }); - - wrapper = shallow(); + wrapper = shallow( + + ); }); it('includes a link to provide feedback', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/save_custom.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/save_custom.tsx index 9e5e3ac2782ee..df62d2b2bdf16 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/save_custom.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_custom_source/save_custom.tsx @@ -21,12 +21,14 @@ import { EuiCallOut, EuiLink, } from '@elastic/eui'; + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiButtonTo } from '../../../../../../shared/react_router_helpers'; import { AppLogic } from '../../../../../app_logic'; import { SOURCES_PATH, getSourcesPath } from '../../../../../routes'; +import { SourceDataItem } from '../../../../../types'; import { CustomSourceDeployment } from '../../custom_source_deployment'; @@ -35,10 +37,14 @@ import { SAVE_CUSTOM_BODY1 as READY_TO_ACCEPT_REQUESTS_LABEL } from '../constant import { AddCustomSourceLogic } from './add_custom_source_logic'; -export const SaveCustom: React.FC = () => { - const { newCustomSource, sourceData } = useValues(AddCustomSourceLogic); +interface SaveCustomProps { + sourceData: SourceDataItem; +} + +export const SaveCustom: React.FC = ({ sourceData }) => { + const { newCustomSource } = useValues(AddCustomSourceLogic); const { isOrganization } = useValues(AppLogic); - const { serviceType, name, categories = [] } = sourceData; + const { serviceType, baseServiceType, name, categories = [] } = sourceData; return ( <> @@ -92,10 +98,10 @@ export const SaveCustom: React.FC = () => { - + - {serviceType !== 'custom' && ( + {baseServiceType && ( <> diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_external_connector/external_connector_config.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_external_connector/external_connector_config.test.tsx index 2d8b5192fd3b1..8f517b740b152 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_external_connector/external_connector_config.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_external_connector/external_connector_config.test.tsx @@ -7,6 +7,7 @@ import '../../../../../../__mocks__/shallow_useeffect.mock'; import { setMockActions, setMockValues } from '../../../../../../__mocks__/kea_logic'; +import { mockUseParams } from '../../../../../../__mocks__/react_router'; import { sourceConfigData } from '../../../../../__mocks__/content_sources.mock'; import React from 'react'; @@ -19,24 +20,15 @@ import { WorkplaceSearchPageTemplate, PersonalDashboardLayout, } from '../../../../../components/layout'; -import { staticSourceData } from '../../../source_data'; import { ExternalConnectorConfig } from './external_connector_config'; import { ExternalConnectorFormFields } from './external_connector_form_fields'; describe('ExternalConnectorConfig', () => { - const goBack = jest.fn(); - const onDeleteConfig = jest.fn(); const setExternalConnectorApiKey = jest.fn(); const setExternalConnectorUrl = jest.fn(); const saveExternalConnectorConfig = jest.fn(); - const props = { - sourceData: staticSourceData[0], - goBack, - onDeleteConfig, - }; - const values = { sourceConfigData, buttonLoading: false, @@ -48,37 +40,47 @@ describe('ExternalConnectorConfig', () => { }; beforeEach(() => { + jest.clearAllMocks(); setMockActions({ setExternalConnectorApiKey, setExternalConnectorUrl, saveExternalConnectorConfig, }); setMockValues({ ...values }); + mockUseParams.mockReturnValue({}); + }); + + it('returns null if there is no matching source data for the service type', () => { + mockUseParams.mockReturnValue({ baseServiceType: 'doesnt_exist' }); + + const wrapper = shallow(); + + expect(wrapper.isEmptyRender()).toBe(true); }); it('renders', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(EuiSteps)).toHaveLength(1); expect(wrapper.find(EuiSteps).dive().find(ExternalConnectorFormFields)).toHaveLength(1); }); it('renders organizstion layout', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(WorkplaceSearchPageTemplate)).toHaveLength(1); }); it('should show correct layout for personal dashboard', () => { setMockValues({ ...values, isOrganization: false }); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(WorkplaceSearchPageTemplate)).toHaveLength(0); expect(wrapper.find(PersonalDashboardLayout)).toHaveLength(1); }); it('handles form submission', () => { - const wrapper = shallow(); + const wrapper = shallow(); const preventDefault = jest.fn(); wrapper.find('form').simulate('submit', { preventDefault }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_external_connector/external_connector_config.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_external_connector/external_connector_config.tsx index 5a2558f141ea0..0b4e34f47103b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_external_connector/external_connector_config.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_external_connector/external_connector_config.tsx @@ -7,11 +7,12 @@ import React, { FormEvent } from 'react'; +import { useParams } from 'react-router-dom'; + import { useActions, useValues } from 'kea'; import { EuiButton, - EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiForm, @@ -26,56 +27,41 @@ import { PersonalDashboardLayout, WorkplaceSearchPageTemplate, } from '../../../../../components/layout'; -import { NAV, REMOVE_BUTTON } from '../../../../../constants'; -import { SourceDataItem } from '../../../../../types'; - -import { staticExternalSourceData } from '../../../source_data'; +import { NAV } from '../../../../../constants'; +import { getSourceData } from '../../../source_data'; import { AddSourceHeader } from '../add_source_header'; import { ConfigDocsLinks } from '../config_docs_links'; -import { OAUTH_SAVE_CONFIG_BUTTON, OAUTH_BACK_BUTTON } from '../constants'; +import { OAUTH_SAVE_CONFIG_BUTTON } from '../constants'; import { ExternalConnectorDocumentation } from './external_connector_documentation'; import { ExternalConnectorFormFields } from './external_connector_form_fields'; import { ExternalConnectorLogic } from './external_connector_logic'; -interface SaveConfigProps { - sourceData: SourceDataItem; - goBack?: () => void; - onDeleteConfig?: () => void; -} - -export const ExternalConnectorConfig: React.FC = ({ - sourceData, - goBack, - onDeleteConfig, -}) => { - const serviceType = 'external'; +export const ExternalConnectorConfig: React.FC = () => { + const { baseServiceType } = useParams<{ baseServiceType?: string }>(); + const sourceData = getSourceData('external', baseServiceType); const { saveExternalConnectorConfig } = useActions(ExternalConnectorLogic); - const { - formDisabled, - buttonLoading, - externalConnectorUrl, - externalConnectorApiKey, - sourceConfigData, - urlValid, - } = useValues(ExternalConnectorLogic); + const { formDisabled, buttonLoading, externalConnectorUrl, externalConnectorApiKey, urlValid } = + useValues(ExternalConnectorLogic); const handleFormSubmission = (e: FormEvent) => { e.preventDefault(); saveExternalConnectorConfig({ url: externalConnectorUrl, apiKey: externalConnectorApiKey }); }; - const { name, categories } = sourceConfigData; - const { - configuration: { applicationLinkTitle, applicationPortalUrl }, - } = sourceData; const { isOrganization } = useValues(AppLogic); + if (!sourceData) { + return null; + } + const { - configuration: { documentationUrl }, - } = staticExternalSourceData; + name, + categories = [], + configuration: { applicationLinkTitle, applicationPortalUrl, documentationUrl }, + } = sourceData; const saveButton = ( @@ -83,22 +69,10 @@ export const ExternalConnectorConfig: React.FC = ({ ); - const deleteButton = ( - - {REMOVE_BUTTON} - - ); - - const backButton = {OAUTH_BACK_BUTTON}; - const formActions = ( {saveButton} - - {goBack && backButton} - {onDeleteConfig && deleteButton} - ); @@ -132,11 +106,17 @@ export const ExternalConnectorConfig: React.FC = ({ }, ]; - const header = ; + const header = ( + + ); const Layout = isOrganization ? WorkplaceSearchPageTemplate : PersonalDashboardLayout; return ( - + {header} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_external_connector/external_connector_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_external_connector/external_connector_logic.test.ts index fb09695a3529d..0603b59cc75b0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_external_connector/external_connector_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_external_connector/external_connector_logic.test.ts @@ -36,10 +36,6 @@ describe('ExternalConnectorLogic', () => { formDisabled: true, externalConnectorUrl: '', externalConnectorApiKey: '', - sourceConfigData: { - name: '', - categories: [], - }, urlValid: true, showInsecureUrlCallout: false, insecureUrl: true, @@ -52,7 +48,6 @@ describe('ExternalConnectorLogic', () => { formDisabled: false, insecureUrl: false, dataLoading: false, - sourceConfigData, }; beforeEach(() => { @@ -87,7 +82,6 @@ describe('ExternalConnectorLogic', () => { it('saves the source config', () => { expect(ExternalConnectorLogic.values).toEqual({ ...DEFAULT_VALUES_SUCCESS, - sourceConfigData, }); }); @@ -104,7 +98,6 @@ describe('ExternalConnectorLogic', () => { ...DEFAULT_VALUES_SUCCESS, externalConnectorUrl: '', insecureUrl: true, - sourceConfigData: newSourceConfigData, }); }); it('sets undefined api key to empty string', () => { @@ -119,7 +112,6 @@ describe('ExternalConnectorLogic', () => { expect(ExternalConnectorLogic.values).toEqual({ ...DEFAULT_VALUES_SUCCESS, externalConnectorApiKey: '', - sourceConfigData: newSourceConfigData, }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_external_connector/external_connector_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_external_connector/external_connector_logic.ts index d1e4cf7f4f008..e36b790edd8e9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_external_connector/external_connector_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_external_connector/external_connector_logic.ts @@ -48,7 +48,6 @@ export interface ExternalConnectorValues { externalConnectorApiKey: string; externalConnectorUrl: string; urlValid: boolean; - sourceConfigData: SourceConfigData | Pick; insecureUrl: boolean; showInsecureUrlCallout: boolean; } @@ -107,12 +106,6 @@ export const ExternalConnectorLogic = kea< setShowInsecureUrlCallout: (_, showCallout) => showCallout, }, ], - sourceConfigData: [ - { name: '', categories: [] }, - { - fetchExternalSourceSuccess: (_, sourceConfigData) => sourceConfigData, - }, - ], urlValid: [ true, { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx index a7cfa81d30021..8811a68e49181 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx @@ -11,6 +11,7 @@ import { setMockActions, setMockValues, } from '../../../../../__mocks__/kea_logic'; +import { mockUseParams } from '../../../../../__mocks__/react_router'; import { sourceConfigData } from '../../../../__mocks__/content_sources.mock'; import React from 'react'; @@ -22,13 +23,9 @@ import { PersonalDashboardLayout, } from '../../../../components/layout'; -import { staticSourceData } from '../../source_data'; - import { AddSource } from './add_source'; import { AddSourceSteps } from './add_source_logic'; import { ConfigCompleted } from './config_completed'; -import { ConfigurationChoice } from './configuration_choice'; -import { ConfigurationIntro } from './configuration_intro'; import { ConfigureOauth } from './configure_oauth'; import { ConnectInstance } from './connect_instance'; import { Reauthenticate } from './reauthenticate'; @@ -36,7 +33,7 @@ import { SaveConfig } from './save_config'; describe('AddSourceList', () => { const { navigateToUrl } = mockKibanaValues; - const initializeAddSource = jest.fn(); + const getSourceConfigData = jest.fn(); const setAddSourceStep = jest.fn(); const saveSourceConfig = jest.fn((_, setConfigCompletedStep) => { setConfigCompletedStep(); @@ -47,7 +44,7 @@ describe('AddSourceList', () => { const resetSourcesState = jest.fn(); const mockValues = { - addSourceCurrentStep: AddSourceSteps.ConfigIntroStep, + addSourceCurrentStep: null, sourceConfigData, dataLoading: false, newCustomSource: {}, @@ -56,68 +53,29 @@ describe('AddSourceList', () => { }; beforeEach(() => { + jest.clearAllMocks(); setMockActions({ - initializeAddSource, + getSourceConfigData, setAddSourceStep, saveSourceConfig, createContentSource, resetSourcesState, }); setMockValues(mockValues); - }); - - it('renders default state', () => { - const wrapper = shallow(); - wrapper.find(ConfigurationIntro).prop('advanceStep')(); - - expect(setAddSourceStep).toHaveBeenCalledWith(AddSourceSteps.SaveConfigStep); - expect(initializeAddSource).toHaveBeenCalled(); - }); - - it('renders default state correctly when there are multiple connector options', () => { - const wrapper = shallow( - - ); - wrapper.find(ConfigurationIntro).prop('advanceStep')(); - - expect(setAddSourceStep).toHaveBeenCalledWith(AddSourceSteps.ChoiceStep); - }); - - it('renders default state correctly when there are multiple connector options but external connector is configured', () => { - setMockValues({ ...mockValues, externalConfigured: true }); - const wrapper = shallow( - - ); - wrapper.find(ConfigurationIntro).prop('advanceStep')(); - - expect(setAddSourceStep).toHaveBeenCalledWith(AddSourceSteps.SaveConfigStep); + mockUseParams.mockReturnValue({ serviceType: 'confluence_cloud' }); }); describe('layout', () => { it('renders the default workplace search layout when on an organization view', () => { setMockValues({ ...mockValues, isOrganization: true }); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.type()).toEqual(WorkplaceSearchPageTemplate); }); it('renders the personal dashboard layout when not in an organization', () => { setMockValues({ ...mockValues, isOrganization: false }); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.type()).toEqual(PersonalDashboardLayout); }); @@ -125,7 +83,7 @@ describe('AddSourceList', () => { it('renders a breadcrumb fallback while data is loading', () => { setMockValues({ ...mockValues, dataLoading: true, sourceConfigData: {} }); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.prop('pageChrome')).toEqual(['Sources', 'Add Source', '...']); }); @@ -135,26 +93,24 @@ describe('AddSourceList', () => { ...mockValues, addSourceCurrentStep: AddSourceSteps.ConfigCompletedStep, }); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(ConfigCompleted).prop('showFeedbackLink')).toEqual(false); wrapper.find(ConfigCompleted).prop('advanceStep')(); - expect(navigateToUrl).toHaveBeenCalledWith('/sources/add/confluence_cloud/connect'); expect(setAddSourceStep).toHaveBeenCalledWith(AddSourceSteps.ConnectInstanceStep); }); it('renders Config Completed step with feedback for external connectors', () => { + mockUseParams.mockReturnValue({ serviceType: 'external' }); setMockValues({ ...mockValues, sourceConfigData: { ...sourceConfigData, serviceType: 'external' }, addSourceCurrentStep: AddSourceSteps.ConfigCompletedStep, }); - const wrapper = shallow( - - ); + const wrapper = shallow(); expect(wrapper.find(ConfigCompleted).prop('showFeedbackLink')).toEqual(true); + wrapper.find(ConfigCompleted).prop('advanceStep')(); - expect(navigateToUrl).toHaveBeenCalledWith('/sources/add/confluence_cloud/connect'); expect(setAddSourceStep).toHaveBeenCalledWith(AddSourceSteps.ConnectInstanceStep); }); @@ -163,13 +119,13 @@ describe('AddSourceList', () => { ...mockValues, addSourceCurrentStep: AddSourceSteps.SaveConfigStep, }); - const wrapper = shallow(); + const wrapper = shallow(); const saveConfig = wrapper.find(SaveConfig); saveConfig.prop('advanceStep')(); - saveConfig.prop('goBackStep')!(); - - expect(setAddSourceStep).toHaveBeenCalledWith(AddSourceSteps.ConfigIntroStep); expect(saveSourceConfig).toHaveBeenCalled(); + + saveConfig.prop('goBackStep')!(); + expect(navigateToUrl).toHaveBeenCalledWith('/sources/add/confluence_cloud/intro'); }); it('renders Connect Instance step', () => { @@ -178,10 +134,11 @@ describe('AddSourceList', () => { sourceConfigData, addSourceCurrentStep: AddSourceSteps.ConnectInstanceStep, }); - const wrapper = shallow(); + + const wrapper = shallow(); wrapper.find(ConnectInstance).prop('onFormCreated')('foo'); - expect(navigateToUrl).toHaveBeenCalledWith('/sources/add/confluence_cloud/connect'); + expect(navigateToUrl).toHaveBeenCalledWith('/sources'); }); it('renders Configure Oauth step', () => { @@ -189,11 +146,11 @@ describe('AddSourceList', () => { ...mockValues, addSourceCurrentStep: AddSourceSteps.ConfigureOauthStep, }); - const wrapper = shallow(); + const wrapper = shallow(); wrapper.find(ConfigureOauth).prop('onFormCreated')('foo'); - expect(navigateToUrl).toHaveBeenCalledWith('/sources/add/confluence_cloud/connect'); + expect(navigateToUrl).toHaveBeenCalledWith('/sources'); }); it('renders Reauthenticate step', () => { @@ -201,23 +158,8 @@ describe('AddSourceList', () => { ...mockValues, addSourceCurrentStep: AddSourceSteps.ReauthenticateStep, }); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(Reauthenticate)).toHaveLength(1); }); - - it('renders Config Choice step', () => { - setMockValues({ - ...mockValues, - addSourceCurrentStep: AddSourceSteps.ChoiceStep, - }); - const wrapper = shallow(); - const advance = wrapper.find(ConfigurationChoice).prop('goToInternalStep'); - expect(advance).toBeDefined(); - if (advance) { - advance(); - } - - expect(setAddSourceStep).toHaveBeenCalledWith(AddSourceSteps.SaveConfigStep); - }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx index 4bdf8db217a7b..5b992703def61 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx @@ -7,29 +7,28 @@ import React, { useEffect } from 'react'; +import { useParams } from 'react-router-dom'; + import { useActions, useValues } from 'kea'; import { i18n } from '@kbn/i18n'; import { flashSuccessToast } from '../../../../../shared/flash_messages'; import { KibanaLogic } from '../../../../../shared/kibana'; +import { LicensingLogic } from '../../../../../shared/licensing'; import { AppLogic } from '../../../../app_logic'; import { WorkplaceSearchPageTemplate, PersonalDashboardLayout, } from '../../../../components/layout'; import { NAV } from '../../../../constants'; -import { SOURCES_PATH, getSourcesPath, getAddPath } from '../../../../routes'; - -import { hasMultipleConnectorOptions } from '../../../../utils'; +import { SOURCES_PATH, getSourcesPath, getAddPath, ADD_SOURCE_PATH } from '../../../../routes'; -import { SourcesLogic } from '../../sources_logic'; +import { getSourceData } from '../../source_data'; import { AddSourceHeader } from './add_source_header'; -import { AddSourceLogic, AddSourceProps, AddSourceSteps } from './add_source_logic'; +import { AddSourceLogic, AddSourceSteps } from './add_source_logic'; import { ConfigCompleted } from './config_completed'; -import { ConfigurationChoice } from './configuration_choice'; -import { ConfigurationIntro } from './configuration_intro'; import { ConfigureOauth } from './configure_oauth'; import { ConnectInstance } from './connect_instance'; import { Reauthenticate } from './reauthenticate'; @@ -37,27 +36,42 @@ import { SaveConfig } from './save_config'; import './add_source.scss'; -export const AddSource: React.FC = (props) => { - const { initializeAddSource, setAddSourceStep, saveSourceConfig, resetSourceState } = - useActions(AddSourceLogic); - const { addSourceCurrentStep, sourceConfigData, dataLoading } = useValues(AddSourceLogic); - const { name, categories, needsPermissions, accountContextOnly, privateSourcesEnabled } = - sourceConfigData; - const { serviceType, configuration, features, objTypes } = props.sourceData; - const addPath = getAddPath(serviceType); +export const AddSource: React.FC = () => { + const { serviceType, initialStep } = useParams<{ serviceType: string; initialStep?: string }>(); + const addSourceLogic = AddSourceLogic({ serviceType, initialStep }); + const { getSourceConfigData, setAddSourceStep, saveSourceConfig, resetSourceState } = + useActions(addSourceLogic); + const { addSourceCurrentStep, sourceConfigData, dataLoading } = useValues(addSourceLogic); const { isOrganization } = useValues(AppLogic); - const { externalConfigured } = useValues(SourcesLogic); + const { hasPlatinumLicense } = useValues(LicensingLogic); + const { navigateToUrl } = useValues(KibanaLogic); useEffect(() => { - initializeAddSource(props); + getSourceConfigData(); return resetSourceState; - }, []); + }, [serviceType]); + + const sourceData = getSourceData(serviceType); + + if (!sourceData) { + return null; + } + + const { configuration, features, objTypes } = sourceData; + + const { name, categories, needsPermissions, accountContextOnly, privateSourcesEnabled } = + sourceConfigData; + + if (!hasPlatinumLicense && accountContextOnly) { + navigateToUrl(getSourcesPath(ADD_SOURCE_PATH, isOrganization)); + } - const goToConfigurationIntro = () => setAddSourceStep(AddSourceSteps.ConfigIntroStep); - const goToSaveConfig = () => setAddSourceStep(AddSourceSteps.SaveConfigStep); + const goToConfigurationIntro = () => + KibanaLogic.values.navigateToUrl( + `${getSourcesPath(getAddPath(serviceType), isOrganization)}/intro` + ); const setConfigCompletedStep = () => setAddSourceStep(AddSourceSteps.ConfigCompletedStep); const goToConfigCompleted = () => saveSourceConfig(false, setConfigCompletedStep); - const goToChoice = () => setAddSourceStep(AddSourceSteps.ChoiceStep); const FORM_SOURCE_ADDED_SUCCESS_MESSAGE = i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.contentSource.formSourceAddedSuccessMessage', { @@ -66,11 +80,7 @@ export const AddSource: React.FC = (props) => { } ); - const goToConnectInstance = () => { - setAddSourceStep(AddSourceSteps.ConnectInstanceStep); - KibanaLogic.values.navigateToUrl(`${getSourcesPath(addPath, isOrganization)}/connect`); - }; - + const goToConnectInstance = () => setAddSourceStep(AddSourceSteps.ConnectInstanceStep); const goToFormSourceCreated = () => { KibanaLogic.values.navigateToUrl(`${getSourcesPath(SOURCES_PATH, isOrganization)}`); flashSuccessToast(FORM_SOURCE_ADDED_SUCCESS_MESSAGE); @@ -81,18 +91,6 @@ export const AddSource: React.FC = (props) => { return ( - {addSourceCurrentStep === AddSourceSteps.ConfigIntroStep && ( - - )} {addSourceCurrentStep === AddSourceSteps.SaveConfigStep && ( = (props) => { {addSourceCurrentStep === AddSourceSteps.ReauthenticateStep && ( )} - {addSourceCurrentStep === AddSourceSteps.ChoiceStep && ( - - )} ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_choice.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_choice.test.tsx new file mode 100644 index 0000000000000..75b45da2b38b1 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_choice.test.tsx @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import '../../../../../__mocks__/shallow_useeffect.mock'; +import { setMockValues, mockKibanaValues } from '../../../../../__mocks__/kea_logic'; + +import { mockUseParams } from '../../../../../__mocks__/react_router'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { + WorkplaceSearchPageTemplate, + PersonalDashboardLayout, +} from '../../../../components/layout'; + +import { getSourceData } from '../../source_data'; + +import { AddSourceChoice } from './add_source_choice'; +import { ConfigurationChoice } from './configuration_choice'; + +describe('AddSourceChoice', () => { + const { navigateToUrl } = mockKibanaValues; + + const mockValues = { + isOrganization: true, + hasPlatinumLicense: true, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockUseParams.mockReturnValue({ serviceType: 'share_point' }); + }); + + it('returns null if there is no matching source data for the service type', () => { + mockUseParams.mockReturnValue({ serviceType: 'doesnt_exist' }); + setMockValues(mockValues); + + const wrapper = shallow(); + + expect(wrapper.isEmptyRender()).toBe(true); + }); + + it('redirects to root add source path if user does not have a platinum license and the service is account context only', () => { + mockUseParams.mockReturnValue({ serviceType: 'slack' }); + setMockValues({ ...mockValues, hasPlatinumLicense: false }); + + shallow(); + + expect(navigateToUrl).toHaveBeenCalledWith('/sources/add'); + }); + + describe('layout', () => { + it('renders the default workplace search layout when on an organization view', () => { + setMockValues({ ...mockValues, isOrganization: true }); + const wrapper = shallow(); + + expect(wrapper.type()).toEqual(WorkplaceSearchPageTemplate); + }); + + it('renders the personal dashboard layout when not in an organization', () => { + setMockValues({ ...mockValues, isOrganization: false }); + const wrapper = shallow(); + + expect(wrapper.type()).toEqual(PersonalDashboardLayout); + }); + }); + + it('renders Config Choice step', () => { + setMockValues(mockValues); + const wrapper = shallow(); + + expect(wrapper.find(ConfigurationChoice).prop('sourceData')).toEqual( + getSourceData('share_point') + ); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_choice.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_choice.tsx new file mode 100644 index 0000000000000..1034d207c9907 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_choice.tsx @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useParams } from 'react-router-dom'; + +import { useValues } from 'kea'; + +import { KibanaLogic } from '../../../../../shared/kibana'; +import { LicensingLogic } from '../../../../../shared/licensing'; + +import { AppLogic } from '../../../../app_logic'; +import { + WorkplaceSearchPageTemplate, + PersonalDashboardLayout, +} from '../../../../components/layout'; +import { NAV } from '../../../../constants'; + +import { getSourcesPath, ADD_SOURCE_PATH } from '../../../../routes'; + +import { getSourceData } from '../../source_data'; + +import { ConfigurationChoice } from './configuration_choice'; + +import './add_source.scss'; + +export const AddSourceChoice: React.FC = () => { + const { serviceType } = useParams<{ serviceType: string }>(); + const sourceData = getSourceData(serviceType); + + const { isOrganization } = useValues(AppLogic); + const { hasPlatinumLicense } = useValues(LicensingLogic); + const { navigateToUrl } = useValues(KibanaLogic); + + if (!sourceData) { + return null; + } + + const { name, accountContextOnly } = sourceData; + + if (!hasPlatinumLicense && accountContextOnly) { + navigateToUrl(getSourcesPath(ADD_SOURCE_PATH, isOrganization)); + } + + const Layout = isOrganization ? WorkplaceSearchPageTemplate : PersonalDashboardLayout; + + return ( + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_intro.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_intro.test.tsx new file mode 100644 index 0000000000000..a7eeadf3a615e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_intro.test.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import '../../../../../__mocks__/shallow_useeffect.mock'; +import { setMockValues } from '../../../../../__mocks__/kea_logic'; +import { mockUseParams } from '../../../../../__mocks__/react_router'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { + WorkplaceSearchPageTemplate, + PersonalDashboardLayout, +} from '../../../../components/layout'; + +import { AddSourceIntro } from './add_source_intro'; +import { ConfigurationIntro } from './configuration_intro'; + +describe('AddSourceList', () => { + const mockValues = { + isOrganization: true, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockUseParams.mockReturnValue({ serviceType: 'share_point' }); + }); + + it('returns null if there is no matching source data for the service type', () => { + mockUseParams.mockReturnValue({ serviceType: 'doesnt_exist' }); + setMockValues(mockValues); + + const wrapper = shallow(); + + expect(wrapper.isEmptyRender()).toBe(true); + }); + + it('sends the user to a choice view when there are multiple connector options', () => { + setMockValues(mockValues); + + const wrapper = shallow(); + + expect(wrapper.find(ConfigurationIntro).prop('advanceStepTo')).toEqual( + '/sources/add/share_point/choice' + ); + }); + + it('sends the user to the add source view by default', () => { + mockUseParams.mockReturnValue({ serviceType: 'slack' }); + setMockValues(mockValues); + + const wrapper = shallow(); + + expect(wrapper.find(ConfigurationIntro).prop('advanceStepTo')).toEqual('/sources/add/slack/'); + }); + + describe('layout', () => { + it('renders the default workplace search layout when on an organization view', () => { + setMockValues({ ...mockValues, isOrganization: true }); + + const wrapper = shallow(); + + expect(wrapper.type()).toEqual(WorkplaceSearchPageTemplate); + }); + + it('renders the personal dashboard layout when not in an organization', () => { + setMockValues({ ...mockValues, isOrganization: false }); + + const wrapper = shallow(); + + expect(wrapper.type()).toEqual(PersonalDashboardLayout); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_intro.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_intro.tsx new file mode 100644 index 0000000000000..b375f04a27f0f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_intro.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useParams } from 'react-router-dom'; + +import { useValues } from 'kea'; + +import { KibanaLogic } from '../../../../../shared/kibana'; +import { LicensingLogic } from '../../../../../shared/licensing'; + +import { AppLogic } from '../../../../app_logic'; +import { + WorkplaceSearchPageTemplate, + PersonalDashboardLayout, +} from '../../../../components/layout'; +import { NAV } from '../../../../constants'; +import { getSourcesPath, ADD_SOURCE_PATH, getAddPath } from '../../../../routes'; + +import { getSourceData, hasMultipleConnectorOptions } from '../../source_data'; + +import { AddSourceHeader } from './add_source_header'; +import { ConfigurationIntro } from './configuration_intro'; + +import './add_source.scss'; + +export const AddSourceIntro: React.FC = () => { + const { serviceType } = useParams<{ serviceType: string }>(); + const sourceData = getSourceData(serviceType); + + const { isOrganization } = useValues(AppLogic); + const { hasPlatinumLicense } = useValues(LicensingLogic); + const { navigateToUrl } = useValues(KibanaLogic); + + if (!sourceData) { + return null; + } + + const { name, categories = [], accountContextOnly } = sourceData; + + if (!hasPlatinumLicense && accountContextOnly) { + navigateToUrl(getSourcesPath(ADD_SOURCE_PATH, isOrganization)); + } + + const header = ; + const Layout = isOrganization ? WorkplaceSearchPageTemplate : PersonalDashboardLayout; + const to = + `${getSourcesPath(getAddPath(serviceType), isOrganization)}/` + + (hasMultipleConnectorOptions(serviceType) ? 'choice' : ''); + return ( + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts index 88ca96b8c0fbf..3224628e72c73 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts @@ -15,7 +15,6 @@ import { sourceConfigData } from '../../../../__mocks__/content_sources.mock'; import { nextTick } from '@kbn/test-jest-helpers'; -import { docLinks } from '../../../../../shared/doc_links'; import { itShowsServerErrorAsFlashMessage } from '../../../../../test_helpers'; jest.mock('../../../../app_logic', () => ({ @@ -23,10 +22,9 @@ jest.mock('../../../../app_logic', () => ({ })); import { AppLogic } from '../../../../app_logic'; -import { SOURCE_NAMES, SOURCE_OBJ_TYPES } from '../../../../constants'; import { SOURCES_PATH, PRIVATE_SOURCES_PATH, getSourcesPath } from '../../../../routes'; -import { FeatureIds } from '../../../../types'; import { PERSONAL_DASHBOARD_SOURCE_ERROR } from '../../constants'; +import { staticSourceData } from '../../source_data'; import { SourcesLogic } from '../../sources_logic'; import { ExternalConnectorLogic } from './add_external_connector/external_connector_logic'; @@ -37,7 +35,6 @@ import { SourceConnectData, OrganizationsMap, AddSourceValues, - AddSourceProps, } from './add_source_logic'; describe('AddSourceLogic', () => { @@ -47,8 +44,7 @@ describe('AddSourceLogic', () => { const { clearFlashMessages, flashAPIErrors, setErrorMessage } = mockFlashMessageHelpers; const DEFAULT_VALUES: AddSourceValues = { - addSourceCurrentStep: AddSourceSteps.ConfigIntroStep, - addSourceProps: {} as AddSourceProps, + addSourceCurrentStep: null, dataLoading: true, sectionLoading: true, buttonLoading: false, @@ -62,11 +58,11 @@ describe('AddSourceLogic', () => { sourceConfigData: {} as SourceConfigData, sourceConnectData: {} as SourceConnectData, oauthConfigCompleted: false, - currentServiceType: '', githubOrganizations: [], selectedGithubOrganizationsMap: {} as OrganizationsMap, selectedGithubOrganizations: [], preContentSourceId: '', + sourceData: staticSourceData[0], }; const sourceConnectData = { @@ -79,40 +75,13 @@ describe('AddSourceLogic', () => { serviceType: 'github', githubOrganizations: ['foo', 'bar'], }; - const DEFAULT_SERVICE_TYPE = { - name: SOURCE_NAMES.BOX, - iconName: SOURCE_NAMES.BOX, - serviceType: 'box', - configuration: { - isPublicKey: false, - hasOauthRedirect: true, - needsBaseUrl: false, - documentationUrl: docLinks.workplaceSearchBox, - applicationPortalUrl: 'https://app.box.com/developers/console', - }, - objTypes: [SOURCE_OBJ_TYPES.FOLDERS, SOURCE_OBJ_TYPES.ALL_FILES], - features: { - basicOrgContext: [ - FeatureIds.SyncFrequency, - FeatureIds.SyncedItems, - FeatureIds.GlobalAccessPermissions, - ], - basicOrgContextExcludedFeatures: [FeatureIds.DocumentLevelPermissions], - platinumOrgContext: [FeatureIds.SyncFrequency, FeatureIds.SyncedItems], - platinumPrivateContext: [ - FeatureIds.Private, - FeatureIds.SyncFrequency, - FeatureIds.SyncedItems, - ], - }, - accountContextOnly: false, - }; + const DEFAULT_SERVICE_TYPE = 'box'; beforeEach(() => { jest.clearAllMocks(); ExternalConnectorLogic.mount(); SourcesLogic.mount(); - mount(); + mount({}, { serviceType: 'box' }); }); it('has expected default values', () => { @@ -215,7 +184,6 @@ describe('AddSourceLogic', () => { oauthConfigCompleted: true, dataLoading: false, sectionLoading: false, - currentServiceType: config.serviceType, githubOrganizations: config.githubOrganizations, }); }); @@ -286,140 +254,90 @@ describe('AddSourceLogic', () => { }); describe('listeners', () => { - it('initializeAddSource', () => { - const addSourceProps = { sourceData: DEFAULT_SERVICE_TYPE }; - const getSourceConfigDataSpy = jest.spyOn(AddSourceLogic.actions, 'getSourceConfigData'); - const setAddSourcePropsSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceProps'); - - AddSourceLogic.actions.initializeAddSource(addSourceProps); - - expect(setAddSourcePropsSpy).toHaveBeenCalledWith({ addSourceProps }); - expect(getSourceConfigDataSpy).toHaveBeenCalledWith('box', addSourceProps); - }); - describe('setFirstStep', () => { - it('sets intro as first step', () => { + it('sets save config as first step if unconfigured', () => { + mount( + { + sourceConfigData: { + ...sourceConfigData, + configured: false, + }, + }, + { serviceType: DEFAULT_SERVICE_TYPE } + ); const setAddSourceStepSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceStep'); - const addSourceProps = { sourceData: DEFAULT_SERVICE_TYPE }; - AddSourceLogic.actions.setFirstStep(addSourceProps); - expect(setAddSourceStepSpy).toHaveBeenCalledWith(AddSourceSteps.ConfigIntroStep); + AddSourceLogic.actions.setFirstStep(); + + expect(setAddSourceStepSpy).toHaveBeenCalledWith(AddSourceSteps.SaveConfigStep); }); + it('sets connect as first step', () => { + mount({ sourceConfigData }, { serviceType: DEFAULT_SERVICE_TYPE, initialStep: 'connect' }); const setAddSourceStepSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceStep'); - const addSourceProps = { sourceData: DEFAULT_SERVICE_TYPE, connect: true }; - AddSourceLogic.actions.setFirstStep(addSourceProps); + + AddSourceLogic.actions.setFirstStep(); expect(setAddSourceStepSpy).toHaveBeenCalledWith(AddSourceSteps.ConnectInstanceStep); }); it('sets configure as first step', () => { + mount( + { sourceConfigData }, + { serviceType: DEFAULT_SERVICE_TYPE, initialStep: 'configure' } + ); const setAddSourceStepSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceStep'); - const addSourceProps = { sourceData: DEFAULT_SERVICE_TYPE, configure: true }; - AddSourceLogic.actions.setFirstStep(addSourceProps); + + AddSourceLogic.actions.setFirstStep(); expect(setAddSourceStepSpy).toHaveBeenCalledWith(AddSourceSteps.ConfigureOauthStep); }); - it('sets reAuthenticate as first step', () => { + it('sets reauthenticate as first step', () => { + mount( + { sourceConfigData }, + { serviceType: DEFAULT_SERVICE_TYPE, initialStep: 'reauthenticate' } + ); const setAddSourceStepSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceStep'); - const addSourceProps = { sourceData: DEFAULT_SERVICE_TYPE, reAuthenticate: true }; - AddSourceLogic.actions.setFirstStep(addSourceProps); + + AddSourceLogic.actions.setFirstStep(); expect(setAddSourceStepSpy).toHaveBeenCalledWith(AddSourceSteps.ReauthenticateStep); }); - it('sets SaveConfig as first step for external connectors', () => { - const setAddSourceStepSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceStep'); - const addSourceProps = { - sourceData: { - ...DEFAULT_SERVICE_TYPE, - serviceType: 'external', - }, - }; - AddSourceLogic.actions.setFirstStep(addSourceProps); - expect(setAddSourceStepSpy).toHaveBeenCalledWith(AddSourceSteps.SaveConfigStep); - }); - it('sets SaveConfigStep for when external connector is available and configured', () => { - const setAddSourceStepSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceStep'); - const addSourceProps = { - sourceData: { - ...DEFAULT_SERVICE_TYPE, - externalConnectorAvailable: true, - }, - }; - AddSourceLogic.actions.setSourceConfigData({ - ...sourceConfigData, - serviceType: 'external', - configured: false, - }); - SourcesLogic.mount(); - SourcesLogic.actions.onInitializeSources({ - contentSources: [], - serviceTypes: [ - { - serviceType: 'external', + it('sets connect step if configured', () => { + mount( + { + sourceConfigData: { + ...sourceConfigData, configured: true, }, - ], - } as any); - AddSourceLogic.actions.setFirstStep(addSourceProps); - - expect(setAddSourceStepSpy).toHaveBeenCalledWith(AddSourceSteps.SaveConfigStep); - }); - it('sets Connect step when configured and external connector is available and configured', () => { - const setAddSourceStepSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceStep'); - const addSourceProps = { - sourceData: { - ...DEFAULT_SERVICE_TYPE, - externalConnectorAvailable: true, - configured: true, }, - }; - AddSourceLogic.actions.setSourceConfigData({ - ...sourceConfigData, - serviceType: 'external', - configured: true, - }); - SourcesLogic.mount(); - SourcesLogic.actions.onInitializeSources({ - contentSources: [], - serviceTypes: [ - { - serviceType: 'external', - configured: true, - }, - ], - } as any); - AddSourceLogic.actions.setFirstStep(addSourceProps); + { serviceType: DEFAULT_SERVICE_TYPE } + ); + const setAddSourceStepSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceStep'); + AddSourceLogic.actions.setFirstStep(); expect(setAddSourceStepSpy).toHaveBeenCalledWith(AddSourceSteps.ConnectInstanceStep); }); - it('sets Connect step when external and fully configured', () => { - const setAddSourceStepSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceStep'); - const addSourceProps = { - sourceData: { - ...DEFAULT_SERVICE_TYPE, - serviceType: 'external', - }, - }; - AddSourceLogic.actions.setSourceConfigData({ - ...sourceConfigData, - configured: true, - serviceType: 'external', - configuredFields: { clientId: 'a', clientSecret: 'b' }, - }); - SourcesLogic.mount(); - SourcesLogic.actions.onInitializeSources({ - contentSources: [], - serviceTypes: [ - { + + it('sets connect step if external connector has client id and secret', () => { + mount( + { + sourceConfigData: { + ...sourceConfigData, serviceType: 'external', - configured: true, + configuredFields: { + clientId: 'test-client-id', + clientSecret: 'test-client-secret', + }, }, - ], - } as any); - AddSourceLogic.actions.setFirstStep(addSourceProps); + }, + { serviceType: DEFAULT_SERVICE_TYPE } + ); + const setAddSourceStepSpy = jest.spyOn(AddSourceLogic.actions, 'setAddSourceStep'); + + AddSourceLogic.actions.setFirstStep(); expect(setAddSourceStepSpy).toHaveBeenCalledWith(AddSourceSteps.ConnectInstanceStep); }); @@ -541,30 +459,33 @@ describe('AddSourceLogic', () => { const setSourceConfigDataSpy = jest.spyOn(AddSourceLogic.actions, 'setSourceConfigData'); http.get.mockReturnValue(Promise.resolve(sourceConfigData)); - AddSourceLogic.actions.getSourceConfigData('github'); + AddSourceLogic.actions.getSourceConfigData(); + await nextTick(); + expect(http.get).toHaveBeenCalledWith( - '/internal/workplace_search/org/settings/connectors/github' + '/internal/workplace_search/org/settings/connectors/box' ); - await nextTick(); expect(setSourceConfigDataSpy).toHaveBeenCalledWith(sourceConfigData); }); + it('calls API and sets values and calls setFirstStep if AddSourceProps is provided', async () => { const setSourceConfigDataSpy = jest.spyOn(AddSourceLogic.actions, 'setSourceConfigData'); const setFirstStepSpy = jest.spyOn(AddSourceLogic.actions, 'setFirstStep'); - const addSourceProps = { sourceData: DEFAULT_SERVICE_TYPE }; + http.get.mockReturnValue(Promise.resolve(sourceConfigData)); - AddSourceLogic.actions.getSourceConfigData('github', addSourceProps); + AddSourceLogic.actions.getSourceConfigData(); + await nextTick(); + expect(http.get).toHaveBeenCalledWith( - '/internal/workplace_search/org/settings/connectors/github' + '/internal/workplace_search/org/settings/connectors/box' ); - await nextTick(); expect(setSourceConfigDataSpy).toHaveBeenCalledWith(sourceConfigData); - expect(setFirstStepSpy).toHaveBeenCalledWith(addSourceProps); + expect(setFirstStepSpy).toHaveBeenCalled(); }); itShowsServerErrorAsFlashMessage(http.get, () => { - AddSourceLogic.actions.getSourceConfigData('github'); + AddSourceLogic.actions.getSourceConfigData(); }); }); @@ -579,7 +500,7 @@ describe('AddSourceLogic', () => { ); http.get.mockReturnValue(Promise.resolve(sourceConnectData)); - AddSourceLogic.actions.getSourceConnectData('github', successCallback); + AddSourceLogic.actions.getSourceConnectData(successCallback); const query = { index_permissions: false, @@ -588,7 +509,7 @@ describe('AddSourceLogic', () => { expect(clearFlashMessages).toHaveBeenCalled(); expect(AddSourceLogic.values.buttonLoading).toEqual(true); expect(http.get).toHaveBeenCalledWith( - '/internal/workplace_search/org/sources/github/prepare', + '/internal/workplace_search/org/sources/box/prepare', { query, } @@ -602,7 +523,7 @@ describe('AddSourceLogic', () => { it('passes query params', () => { AddSourceLogic.actions.setSourceSubdomainValue('subdomain'); AddSourceLogic.actions.setSourceIndexPermissionsValue(true); - AddSourceLogic.actions.getSourceConnectData('github', successCallback); + AddSourceLogic.actions.getSourceConnectData(successCallback); const query = { index_permissions: true, @@ -610,7 +531,7 @@ describe('AddSourceLogic', () => { }; expect(http.get).toHaveBeenCalledWith( - '/internal/workplace_search/org/sources/github/prepare', + '/internal/workplace_search/org/sources/box/prepare', { query, } @@ -618,7 +539,7 @@ describe('AddSourceLogic', () => { }); itShowsServerErrorAsFlashMessage(http.get, () => { - AddSourceLogic.actions.getSourceConnectData('github', successCallback); + AddSourceLogic.actions.getSourceConnectData(successCallback); }); }); @@ -833,7 +754,7 @@ describe('AddSourceLogic', () => { const successCallback = jest.fn(); const errorCallback = jest.fn(); - const serviceType = 'zendesk'; + const serviceType = 'box'; const login = 'login'; const password = 'password'; const indexPermissions = false; @@ -859,7 +780,7 @@ describe('AddSourceLogic', () => { const setButtonNotLoadingSpy = jest.spyOn(AddSourceLogic.actions, 'setButtonNotLoading'); http.post.mockReturnValue(Promise.resolve()); - AddSourceLogic.actions.createContentSource(serviceType, successCallback, errorCallback); + AddSourceLogic.actions.createContentSource(successCallback, errorCallback); expect(clearFlashMessages).toHaveBeenCalled(); expect(AddSourceLogic.values.buttonLoading).toEqual(true); @@ -875,7 +796,7 @@ describe('AddSourceLogic', () => { const setButtonNotLoadingSpy = jest.spyOn(AddSourceLogic.actions, 'setButtonNotLoading'); http.post.mockReturnValue(Promise.reject('this is an error')); - AddSourceLogic.actions.createContentSource(serviceType, successCallback, errorCallback); + AddSourceLogic.actions.createContentSource(successCallback, errorCallback); await nextTick(); expect(setButtonNotLoadingSpy).toHaveBeenCalled(); @@ -891,10 +812,10 @@ describe('AddSourceLogic', () => { }); it('getSourceConnectData', () => { - AddSourceLogic.actions.getSourceConnectData('github', jest.fn()); + AddSourceLogic.actions.getSourceConnectData(jest.fn()); expect(http.get).toHaveBeenCalledWith( - '/internal/workplace_search/account/sources/github/prepare', + '/internal/workplace_search/account/sources/box/prepare', { query: {} } ); }); @@ -915,10 +836,10 @@ describe('AddSourceLogic', () => { }); it('createContentSource', () => { - AddSourceLogic.actions.createContentSource('github', jest.fn()); + AddSourceLogic.actions.createContentSource(jest.fn()); expect(http.post).toHaveBeenCalledWith('/internal/workplace_search/account/create_source', { - body: JSON.stringify({ service_type: 'github' }), + body: JSON.stringify({ service_type: 'box' }), }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts index 97a58966ad76a..a087f1b78571b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts @@ -23,6 +23,7 @@ import { AppLogic } from '../../../../app_logic'; import { SOURCES_PATH, PRIVATE_SOURCES_PATH, getSourcesPath, getAddPath } from '../../../../routes'; import { SourceDataItem } from '../../../../types'; import { PERSONAL_DASHBOARD_SOURCE_ERROR } from '../../constants'; +import { getSourceData } from '../../source_data'; import { SourcesLogic } from '../../sources_logic'; import { @@ -31,20 +32,16 @@ import { } from './add_external_connector/external_connector_logic'; export interface AddSourceProps { - sourceData: SourceDataItem; - connect?: boolean; - configure?: boolean; - reAuthenticate?: boolean; + serviceType: string; + initialStep?: string; } export enum AddSourceSteps { - ConfigIntroStep = 'Config Intro', SaveConfigStep = 'Save Config', ConfigCompletedStep = 'Config Completed', ConnectInstanceStep = 'Connect Instance', ConfigureOauthStep = 'Configure Oauth', ReauthenticateStep = 'Reauthenticate', - ChoiceStep = 'Choice', } export interface OauthParams { @@ -57,10 +54,6 @@ export interface OauthParams { } export interface AddSourceActions { - initializeAddSource: (addSourceProps: AddSourceProps) => { addSourceProps: AddSourceProps }; - setAddSourceProps: ({ addSourceProps }: { addSourceProps: AddSourceProps }) => { - addSourceProps: AddSourceProps; - }; setAddSourceStep(addSourceCurrentStep: AddSourceSteps): AddSourceSteps; setSourceConfigData(sourceConfigData: SourceConfigData): SourceConfigData; setSourceConnectData(sourceConnectData: SourceConnectData): SourceConnectData; @@ -76,10 +69,9 @@ export interface AddSourceActions { setSelectedGithubOrganizations(option: string): string; resetSourceState(): void; createContentSource( - serviceType: string, successCallback: () => void, errorCallback?: () => void - ): { serviceType: string; successCallback(): void; errorCallback?(): void }; + ): { successCallback(): void; errorCallback?(): void }; saveSourceConfig( isUpdating: boolean, successCallback?: () => void @@ -89,24 +81,22 @@ export interface AddSourceActions { params: OauthParams, isOrganization: boolean ): { search: Search; params: OauthParams; isOrganization: boolean }; - getSourceConfigData( - serviceType: string, - addSourceProps?: AddSourceProps - ): { serviceType: string; addSourceProps: AddSourceProps | undefined }; - getSourceConnectData( - serviceType: string, - successCallback: (oauthUrl: string) => void - ): { serviceType: string; successCallback(oauthUrl: string): void }; + getSourceConfigData(): void; + getSourceConnectData(successCallback: (oauthUrl: string) => void): { + successCallback(oauthUrl: string): void; + }; getSourceReConnectData(sourceId: string): { sourceId: string }; getPreContentSourceConfigData(): void; setButtonNotLoading(): void; - setFirstStep(addSourceProps: AddSourceProps): { addSourceProps: AddSourceProps }; + setFirstStep(): void; } export interface SourceConfigData { serviceType: string; + baseServiceType?: string; name: string; configured: boolean; + externalConnectorServiceDescribed?: boolean; categories: string[]; needsPermissions?: boolean; privateSourcesEnabled: boolean; @@ -133,8 +123,7 @@ export interface OrganizationsMap { } export interface AddSourceValues { - addSourceProps: AddSourceProps; - addSourceCurrentStep: AddSourceSteps; + addSourceCurrentStep: AddSourceSteps | null; dataLoading: boolean; sectionLoading: boolean; buttonLoading: boolean; @@ -147,12 +136,12 @@ export interface AddSourceValues { indexPermissionsValue: boolean; sourceConfigData: SourceConfigData; sourceConnectData: SourceConnectData; - currentServiceType: string; githubOrganizations: string[]; selectedGithubOrganizationsMap: OrganizationsMap; selectedGithubOrganizations: string[]; preContentSourceId: string; oauthConfigCompleted: boolean; + sourceData: SourceDataItem | null; } interface PreContentSourceResponse { @@ -161,471 +150,436 @@ interface PreContentSourceResponse { githubOrganizations: string[]; } -export const AddSourceLogic = kea>({ - path: ['enterprise_search', 'workplace_search', 'add_source_logic'], - actions: { - initializeAddSource: (addSourceProps: AddSourceProps) => ({ addSourceProps }), - setAddSourceProps: ({ addSourceProps }: { addSourceProps: AddSourceProps }) => ({ - addSourceProps, - }), - setAddSourceStep: (addSourceCurrentStep: AddSourceSteps) => addSourceCurrentStep, - setSourceConfigData: (sourceConfigData: SourceConfigData) => sourceConfigData, - setSourceConnectData: (sourceConnectData: SourceConnectData) => sourceConnectData, - setClientIdValue: (clientIdValue: string) => clientIdValue, - setClientSecretValue: (clientSecretValue: string) => clientSecretValue, - setBaseUrlValue: (baseUrlValue: string) => baseUrlValue, - setSourceLoginValue: (loginValue: string) => loginValue, - setSourcePasswordValue: (passwordValue: string) => passwordValue, - setSourceSubdomainValue: (subdomainValue: string) => subdomainValue, - setSourceIndexPermissionsValue: (indexPermissionsValue: boolean) => indexPermissionsValue, - setPreContentSourceConfigData: (data: PreContentSourceResponse) => data, - setPreContentSourceId: (preContentSourceId: string) => preContentSourceId, - setSelectedGithubOrganizations: (option: string) => option, - getSourceConfigData: (serviceType: string, addSourceProps?: AddSourceProps) => ({ - serviceType, - addSourceProps, - }), - getSourceConnectData: (serviceType: string, successCallback: (oauthUrl: string) => string) => ({ - serviceType, - successCallback, - }), - getSourceReConnectData: (sourceId: string) => ({ sourceId }), - getPreContentSourceConfigData: () => true, - saveSourceConfig: (isUpdating: boolean, successCallback?: () => void) => ({ - isUpdating, - successCallback, +export const AddSourceLogic = kea>( + { + path: ['enterprise_search', 'workplace_search', 'add_source_logic'], + actions: { + setAddSourceStep: (addSourceCurrentStep: AddSourceSteps) => addSourceCurrentStep, + setSourceConfigData: (sourceConfigData: SourceConfigData) => sourceConfigData, + setSourceConnectData: (sourceConnectData: SourceConnectData) => sourceConnectData, + setClientIdValue: (clientIdValue: string) => clientIdValue, + setClientSecretValue: (clientSecretValue: string) => clientSecretValue, + setBaseUrlValue: (baseUrlValue: string) => baseUrlValue, + setSourceLoginValue: (loginValue: string) => loginValue, + setSourcePasswordValue: (passwordValue: string) => passwordValue, + setSourceSubdomainValue: (subdomainValue: string) => subdomainValue, + setSourceIndexPermissionsValue: (indexPermissionsValue: boolean) => indexPermissionsValue, + setPreContentSourceConfigData: (data: PreContentSourceResponse) => data, + setPreContentSourceId: (preContentSourceId: string) => preContentSourceId, + setSelectedGithubOrganizations: (option: string) => option, + getSourceConfigData: () => true, + getSourceConnectData: (successCallback: (oauthUrl: string) => string) => ({ + successCallback, + }), + getSourceReConnectData: (sourceId: string) => ({ sourceId }), + getPreContentSourceConfigData: () => true, + saveSourceConfig: (isUpdating: boolean, successCallback?: () => void) => ({ + isUpdating, + successCallback, + }), + saveSourceParams: (search: Search, params: OauthParams, isOrganization: boolean) => ({ + search, + params, + isOrganization, + }), + createContentSource: (successCallback: () => void, errorCallback?: () => void) => ({ + successCallback, + errorCallback, + }), + resetSourceState: () => true, + setButtonNotLoading: () => true, + setFirstStep: () => true, + }, + reducers: ({ props }) => ({ + addSourceCurrentStep: [ + null, + { + setAddSourceStep: (_, addSourceCurrentStep) => addSourceCurrentStep, + }, + ], + sourceConfigData: [ + {} as SourceConfigData, + { + setSourceConfigData: (_, sourceConfigData) => sourceConfigData, + }, + ], + sourceConnectData: [ + {} as SourceConnectData, + { + setSourceConnectData: (_, sourceConnectData) => sourceConnectData, + }, + ], + dataLoading: [ + true, + { + setSourceConfigData: () => false, + resetSourceState: () => false, + setPreContentSourceConfigData: () => false, + getSourceConfigData: () => true, + }, + ], + buttonLoading: [ + false, + { + setButtonNotLoading: () => false, + setSourceConnectData: () => false, + setSourceConfigData: () => false, + resetSourceState: () => false, + saveSourceConfig: () => true, + getSourceConnectData: () => true, + createContentSource: () => true, + }, + ], + sectionLoading: [ + true, + { + getPreContentSourceConfigData: () => true, + setPreContentSourceConfigData: () => false, + }, + ], + clientIdValue: [ + '', + { + setClientIdValue: (_, clientIdValue) => clientIdValue, + setSourceConfigData: (_, { configuredFields: { clientId } }) => clientId || '', + resetSourceState: () => '', + }, + ], + clientSecretValue: [ + '', + { + setClientSecretValue: (_, clientSecretValue) => clientSecretValue, + setSourceConfigData: (_, { configuredFields: { clientSecret } }) => clientSecret || '', + resetSourceState: () => '', + }, + ], + baseUrlValue: [ + '', + { + setBaseUrlValue: (_, baseUrlValue) => baseUrlValue, + setSourceConfigData: (_, { configuredFields: { baseUrl } }) => baseUrl || '', + resetSourceState: () => '', + }, + ], + loginValue: [ + '', + { + setSourceLoginValue: (_, loginValue) => loginValue, + resetSourceState: () => '', + }, + ], + passwordValue: [ + '', + { + setSourcePasswordValue: (_, passwordValue) => passwordValue, + resetSourceState: () => '', + }, + ], + subdomainValue: [ + '', + { + setSourceSubdomainValue: (_, subdomainValue) => subdomainValue, + resetSourceState: () => '', + }, + ], + indexPermissionsValue: [ + false, + { + setSourceIndexPermissionsValue: (_, indexPermissionsValue) => indexPermissionsValue, + resetSourceState: () => false, + }, + ], + githubOrganizations: [ + [], + { + setPreContentSourceConfigData: (_, { githubOrganizations }) => githubOrganizations, + resetSourceState: () => [], + }, + ], + selectedGithubOrganizationsMap: [ + {} as OrganizationsMap, + { + setSelectedGithubOrganizations: (state, option) => ({ + ...state, + ...{ [option]: !state[option] }, + }), + resetSourceState: () => ({}), + }, + ], + preContentSourceId: [ + '', + { + setPreContentSourceId: (_, preContentSourceId) => preContentSourceId, + setPreContentSourceConfigData: () => '', + resetSourceState: () => '', + }, + ], + oauthConfigCompleted: [ + false, + { + setPreContentSourceConfigData: () => true, + }, + ], + sourceData: [getSourceData(props.serviceType) || null, {}], }), - saveSourceParams: (search: Search, params: OauthParams, isOrganization: boolean) => ({ - search, - params, - isOrganization, + selectors: ({ selectors }) => ({ + selectedGithubOrganizations: [ + () => [selectors.selectedGithubOrganizationsMap], + (orgsMap) => keys(pickBy(orgsMap)), + ], }), - createContentSource: ( - serviceType: string, - successCallback: () => void, - errorCallback?: () => void - ) => ({ serviceType, successCallback, errorCallback }), - resetSourceState: () => true, - setButtonNotLoading: () => false, - setFirstStep: (addSourceProps) => ({ addSourceProps }), - }, - reducers: { - addSourceProps: [ - {} as AddSourceProps, - { - setAddSourceProps: (_, { addSourceProps }) => addSourceProps, - }, - ], - addSourceCurrentStep: [ - AddSourceSteps.ConfigIntroStep, - { - setAddSourceStep: (_, addSourceCurrentStep) => addSourceCurrentStep, - }, - ], - sourceConfigData: [ - {} as SourceConfigData, - { - setSourceConfigData: (_, sourceConfigData) => sourceConfigData, - }, - ], - sourceConnectData: [ - {} as SourceConnectData, - { - setSourceConnectData: (_, sourceConnectData) => sourceConnectData, - }, - ], - dataLoading: [ - true, - { - setSourceConfigData: () => false, - resetSourceState: () => false, - setPreContentSourceConfigData: () => false, - getSourceConfigData: () => true, - }, - ], - buttonLoading: [ - false, - { - setButtonNotLoading: () => false, - setSourceConnectData: () => false, - setSourceConfigData: () => false, - resetSourceState: () => false, - saveSourceConfig: () => true, - getSourceConnectData: () => true, - createContentSource: () => true, - }, - ], - sectionLoading: [ - true, - { - getPreContentSourceConfigData: () => true, - setPreContentSourceConfigData: () => false, - }, - ], - clientIdValue: [ - '', - { - setClientIdValue: (_, clientIdValue) => clientIdValue, - setSourceConfigData: (_, { configuredFields: { clientId } }) => clientId || '', - resetSourceState: () => '', - }, - ], - clientSecretValue: [ - '', - { - setClientSecretValue: (_, clientSecretValue) => clientSecretValue, - setSourceConfigData: (_, { configuredFields: { clientSecret } }) => clientSecret || '', - resetSourceState: () => '', - }, - ], - baseUrlValue: [ - '', - { - setBaseUrlValue: (_, baseUrlValue) => baseUrlValue, - setSourceConfigData: (_, { configuredFields: { baseUrl } }) => baseUrl || '', - resetSourceState: () => '', - }, - ], - loginValue: [ - '', - { - setSourceLoginValue: (_, loginValue) => loginValue, - resetSourceState: () => '', - }, - ], - passwordValue: [ - '', - { - setSourcePasswordValue: (_, passwordValue) => passwordValue, - resetSourceState: () => '', - }, - ], - subdomainValue: [ - '', - { - setSourceSubdomainValue: (_, subdomainValue) => subdomainValue, - resetSourceState: () => '', - }, - ], - indexPermissionsValue: [ - false, - { - setSourceIndexPermissionsValue: (_, indexPermissionsValue) => indexPermissionsValue, - resetSourceState: () => false, - }, - ], - currentServiceType: [ - '', - { - setPreContentSourceConfigData: (_, { serviceType }) => serviceType, - resetSourceState: () => '', - }, - ], - githubOrganizations: [ - [], - { - setPreContentSourceConfigData: (_, { githubOrganizations }) => githubOrganizations, - resetSourceState: () => [], + listeners: ({ actions, values, props }) => ({ + getSourceConfigData: async () => { + const { serviceType } = props; + // TODO: Once multi-config support for connectors is added, this request url will need to include an ID + const route = `/internal/workplace_search/org/settings/connectors/${serviceType}`; + + try { + const response = await HttpLogic.values.http.get(route); + actions.setSourceConfigData(response); + actions.setFirstStep(); + } catch (e) { + flashAPIErrors(e); + } }, - ], - selectedGithubOrganizationsMap: [ - {} as OrganizationsMap, - { - setSelectedGithubOrganizations: (state, option) => ({ - ...state, - ...{ [option]: !state[option] }, - }), - resetSourceState: () => ({}), + getSourceConnectData: async ({ successCallback }) => { + const { serviceType } = props; + clearFlashMessages(); + const { isOrganization } = AppLogic.values; + const { subdomainValue: subdomain, indexPermissionsValue: indexPermissions } = values; + + const route = isOrganization + ? `/internal/workplace_search/org/sources/${serviceType}/prepare` + : `/internal/workplace_search/account/sources/${serviceType}/prepare`; + + const indexPermissionsQuery = isOrganization + ? { index_permissions: indexPermissions } + : undefined; + + const query = subdomain + ? { + ...indexPermissionsQuery, + subdomain, + } + : { ...indexPermissionsQuery }; + + try { + const response = await HttpLogic.values.http.get(route, { + query, + }); + actions.setSourceConnectData(response); + successCallback(response.oauthUrl); + } catch (e) { + flashAPIErrors(e); + } finally { + actions.setButtonNotLoading(); + } }, - ], - preContentSourceId: [ - '', - { - setPreContentSourceId: (_, preContentSourceId) => preContentSourceId, - setPreContentSourceConfigData: () => '', - resetSourceState: () => '', + getSourceReConnectData: async ({ sourceId }) => { + const { isOrganization } = AppLogic.values; + const route = isOrganization + ? `/internal/workplace_search/org/sources/${sourceId}/reauth_prepare` + : `/internal/workplace_search/account/sources/${sourceId}/reauth_prepare`; + + try { + const response = await HttpLogic.values.http.get(route); + actions.setSourceConnectData(response); + } catch (e) { + flashAPIErrors(e); + } }, - ], - oauthConfigCompleted: [ - false, - { - setPreContentSourceConfigData: () => true, + getPreContentSourceConfigData: async () => { + const { isOrganization } = AppLogic.values; + const { preContentSourceId } = values; + const route = isOrganization + ? `/internal/workplace_search/org/pre_sources/${preContentSourceId}` + : `/internal/workplace_search/account/pre_sources/${preContentSourceId}`; + + try { + const response = await HttpLogic.values.http.get(route); + actions.setPreContentSourceConfigData(response); + } catch (e) { + flashAPIErrors(e); + } }, - ], - }, - selectors: ({ selectors }) => ({ - selectedGithubOrganizations: [ - () => [selectors.selectedGithubOrganizationsMap], - (orgsMap) => keys(pickBy(orgsMap)), - ], - }), - listeners: ({ actions, values }) => ({ - initializeAddSource: ({ addSourceProps }) => { - const { serviceType } = addSourceProps.sourceData; - actions.setAddSourceProps({ addSourceProps }); - actions.getSourceConfigData(serviceType, addSourceProps); - }, - getSourceConfigData: async ({ serviceType, addSourceProps }) => { - const route = `/internal/workplace_search/org/settings/connectors/${serviceType}`; - - try { - const response = await HttpLogic.values.http.get(route); - actions.setSourceConfigData(response); - if (addSourceProps) { - actions.setFirstStep(addSourceProps); + saveSourceConfig: async ({ isUpdating, successCallback }) => { + clearFlashMessages(); + const { + sourceConfigData: { serviceType }, + baseUrlValue, + clientIdValue, + clientSecretValue, + sourceConfigData, + } = values; + + const { externalConnectorUrl, externalConnectorApiKey } = ExternalConnectorLogic.values; + if ( + serviceType === 'external' && + externalConnectorUrl && + !isValidExternalUrl(externalConnectorUrl) + ) { + ExternalConnectorLogic.actions.setUrlValidation(false); + actions.setButtonNotLoading(); + return; } - } catch (e) { - flashAPIErrors(e); - } - }, - getSourceConnectData: async ({ serviceType, successCallback }) => { - clearFlashMessages(); - const { isOrganization } = AppLogic.values; - const { subdomainValue: subdomain, indexPermissionsValue: indexPermissions } = values; - - const route = isOrganization - ? `/internal/workplace_search/org/sources/${serviceType}/prepare` - : `/internal/workplace_search/account/sources/${serviceType}/prepare`; - - const indexPermissionsQuery = isOrganization - ? { index_permissions: indexPermissions } - : undefined; - - const query = subdomain - ? { - ...indexPermissionsQuery, - subdomain, + + const route = isUpdating + ? `/internal/workplace_search/org/settings/connectors/${serviceType}` + : '/internal/workplace_search/org/settings/connectors'; + + const http = isUpdating ? HttpLogic.values.http.put : HttpLogic.values.http.post; + + const params = { + base_url: baseUrlValue || undefined, + client_id: clientIdValue || undefined, + client_secret: clientSecretValue || undefined, + service_type: serviceType, + private_key: sourceConfigData.configuredFields?.privateKey, + public_key: sourceConfigData.configuredFields?.publicKey, + consumer_key: sourceConfigData.configuredFields?.consumerKey, + external_connector_url: (serviceType === 'external' && externalConnectorUrl) || undefined, + external_connector_api_key: + (serviceType === 'external' && externalConnectorApiKey) || undefined, + }; + + try { + const response = await http(route, { + body: JSON.stringify(params), + }); + if (successCallback) successCallback(); + if (isUpdating) { + flashSuccessToast( + i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.sources.flashMessages.contentSourceConfigUpdated', + { + defaultMessage: 'Successfully updated configuration.', + } + ) + ); } - : { ...indexPermissionsQuery }; - - try { - const response = await HttpLogic.values.http.get(route, { - query, - }); - actions.setSourceConnectData(response); - successCallback(response.oauthUrl); - } catch (e) { - flashAPIErrors(e); - } finally { - actions.setButtonNotLoading(); - } - }, - getSourceReConnectData: async ({ sourceId }) => { - const { isOrganization } = AppLogic.values; - const route = isOrganization - ? `/internal/workplace_search/org/sources/${sourceId}/reauth_prepare` - : `/internal/workplace_search/account/sources/${sourceId}/reauth_prepare`; - - try { - const response = await HttpLogic.values.http.get(route); - actions.setSourceConnectData(response); - } catch (e) { - flashAPIErrors(e); - } - }, - getPreContentSourceConfigData: async () => { - const { isOrganization } = AppLogic.values; - const { preContentSourceId } = values; - const route = isOrganization - ? `/internal/workplace_search/org/pre_sources/${preContentSourceId}` - : `/internal/workplace_search/account/pre_sources/${preContentSourceId}`; - - try { - const response = await HttpLogic.values.http.get(route); - actions.setPreContentSourceConfigData(response); - } catch (e) { - flashAPIErrors(e); - } - }, - saveSourceConfig: async ({ isUpdating, successCallback }) => { - clearFlashMessages(); - const { - sourceConfigData: { serviceType }, - baseUrlValue, - clientIdValue, - clientSecretValue, - sourceConfigData, - } = values; - - const { externalConnectorUrl, externalConnectorApiKey } = ExternalConnectorLogic.values; - if ( - serviceType === 'external' && - externalConnectorUrl && - !isValidExternalUrl(externalConnectorUrl) - ) { - ExternalConnectorLogic.actions.setUrlValidation(false); - actions.setButtonNotLoading(); - return; - } - - const route = isUpdating - ? `/internal/workplace_search/org/settings/connectors/${serviceType}` - : '/internal/workplace_search/org/settings/connectors'; - - const http = isUpdating ? HttpLogic.values.http.put : HttpLogic.values.http.post; - - const params = { - base_url: baseUrlValue || undefined, - client_id: clientIdValue || undefined, - client_secret: clientSecretValue || undefined, - service_type: serviceType, - private_key: sourceConfigData.configuredFields?.privateKey, - public_key: sourceConfigData.configuredFields?.publicKey, - consumer_key: sourceConfigData.configuredFields?.consumerKey, - external_connector_url: (serviceType === 'external' && externalConnectorUrl) || undefined, - external_connector_api_key: - (serviceType === 'external' && externalConnectorApiKey) || undefined, - }; - - try { - const response = await http(route, { - body: JSON.stringify(params), - }); - if (successCallback) successCallback(); - if (isUpdating) { - flashSuccessToast( - i18n.translate( - 'xpack.enterpriseSearch.workplaceSearch.sources.flashMessages.contentSourceConfigUpdated', - { - defaultMessage: 'Successfully updated configuration.', - } - ) - ); + actions.setSourceConfigData(response); + } catch (e) { + flashAPIErrors(e); + } finally { + actions.setButtonNotLoading(); } - actions.setSourceConfigData(response); - } catch (e) { - flashAPIErrors(e); - } finally { - actions.setButtonNotLoading(); - } - }, - saveSourceParams: async ({ search, params, isOrganization }) => { - const { http } = HttpLogic.values; - const { navigateToUrl } = KibanaLogic.values; - const { setAddedSource } = SourcesLogic.actions; - const query = { ...params }; - const route = '/internal/workplace_search/sources/create'; - - /** + }, + saveSourceParams: async ({ search, params, isOrganization }) => { + const { http } = HttpLogic.values; + const { navigateToUrl } = KibanaLogic.values; + const { setAddedSource } = SourcesLogic.actions; + const query = { ...params }; + const route = '/internal/workplace_search/sources/create'; + + /** There is an extreme edge case where the user is trying to connect Github as source from ent-search, after configuring it in Kibana. When this happens, Github redirects the user from ent-search to Kibana with special error properties in the query params. In this case we need to redirect the user to the app home page and display the error message, and not persist the other query params to the server. */ - if (params.error_description) { - navigateToUrl(isOrganization ? '/' : PRIVATE_SOURCES_PATH); - setErrorMessage( - isOrganization - ? params.error_description - : PERSONAL_DASHBOARD_SOURCE_ERROR(params.error_description) - ); - return; - } - - try { - const response = await http.get<{ - serviceName: string; - indexPermissions: boolean; - serviceType: string; - preContentSourceId: string; - hasConfigureStep: boolean; - }>(route, { query }); - const { serviceName, indexPermissions, serviceType, preContentSourceId, hasConfigureStep } = - response; - - // GitHub requires an intermediate configuration step, where we collect the repos to index. - if (hasConfigureStep && !values.oauthConfigCompleted) { - actions.setPreContentSourceId(preContentSourceId); - navigateToUrl( - getSourcesPath(`${getAddPath('github')}/configure${search}`, isOrganization) + if (params.error_description) { + navigateToUrl(isOrganization ? '/' : PRIVATE_SOURCES_PATH); + setErrorMessage( + isOrganization + ? params.error_description + : PERSONAL_DASHBOARD_SOURCE_ERROR(params.error_description) ); - } else { - setAddedSource(serviceName, indexPermissions, serviceType); + return; + } + + try { + const response = await http.get<{ + serviceName: string; + indexPermissions: boolean; + serviceType: string; + preContentSourceId: string; + hasConfigureStep: boolean; + }>(route, { query }); + const { + serviceName, + indexPermissions, + serviceType, + preContentSourceId, + hasConfigureStep, + } = response; + + // GitHub requires an intermediate configuration step, where we collect the repos to index. + if (hasConfigureStep && !values.oauthConfigCompleted) { + actions.setPreContentSourceId(preContentSourceId); + navigateToUrl( + getSourcesPath(`${getAddPath('github')}/configure${search}`, isOrganization) + ); + } else { + setAddedSource(serviceName, indexPermissions, serviceType); + navigateToUrl(getSourcesPath(SOURCES_PATH, isOrganization)); + } + } catch (e) { navigateToUrl(getSourcesPath(SOURCES_PATH, isOrganization)); + flashAPIErrors(e); } - } catch (e) { - navigateToUrl(getSourcesPath(SOURCES_PATH, isOrganization)); - flashAPIErrors(e); - } - }, - setFirstStep: ({ addSourceProps }) => { - const firstStep = getFirstStep( - addSourceProps, - values.sourceConfigData, - SourcesLogic.values.externalConfigured - ); - actions.setAddSourceStep(firstStep); - }, - createContentSource: async ({ serviceType, successCallback, errorCallback }) => { - clearFlashMessages(); - const { isOrganization } = AppLogic.values; - const route = isOrganization - ? '/internal/workplace_search/org/create_source' - : '/internal/workplace_search/account/create_source'; - - const { - selectedGithubOrganizations: githubOrganizations, - loginValue, - passwordValue, - indexPermissionsValue, - } = values; - - const params = { - service_type: serviceType, - login: loginValue || undefined, - password: passwordValue || undefined, - organizations: githubOrganizations.length > 0 ? githubOrganizations : undefined, - index_permissions: indexPermissionsValue || undefined, - } as { - [key: string]: string | string[] | undefined; - }; - - // Remove undefined values from params - Object.keys(params).forEach((key) => params[key] === undefined && delete params[key]); - - try { - await HttpLogic.values.http.post(route, { - body: JSON.stringify({ ...params }), - }); - successCallback(); - } catch (e) { - flashAPIErrors(e); - if (errorCallback) errorCallback(); - } finally { - actions.setButtonNotLoading(); - } - }, - }), -}); - -const getFirstStep = ( - props: AddSourceProps, - sourceConfigData: SourceConfigData, - externalConfigured: boolean -): AddSourceSteps => { + }, + setFirstStep: () => { + const firstStep = getFirstStep(values.sourceConfigData, props.initialStep); + actions.setAddSourceStep(firstStep); + }, + createContentSource: async ({ successCallback, errorCallback }) => { + const { serviceType } = props; + clearFlashMessages(); + const { isOrganization } = AppLogic.values; + const route = isOrganization + ? '/internal/workplace_search/org/create_source' + : '/internal/workplace_search/account/create_source'; + + const { + selectedGithubOrganizations: githubOrganizations, + loginValue, + passwordValue, + indexPermissionsValue, + } = values; + + const params = { + service_type: serviceType, + login: loginValue || undefined, + password: passwordValue || undefined, + organizations: githubOrganizations.length > 0 ? githubOrganizations : undefined, + index_permissions: indexPermissionsValue || undefined, + } as { + [key: string]: string | string[] | undefined; + }; + + // Remove undefined values from params + Object.keys(params).forEach((key) => params[key] === undefined && delete params[key]); + + try { + await HttpLogic.values.http.post(route, { + body: JSON.stringify({ ...params }), + }); + successCallback(); + } catch (e) { + flashAPIErrors(e); + if (errorCallback) errorCallback(); + } finally { + actions.setButtonNotLoading(); + } + }, + }), + } +); + +const getFirstStep = (sourceConfigData: SourceConfigData, initialStep?: string): AddSourceSteps => { const { - connect, - configure, - reAuthenticate, - sourceData: { serviceType, externalConnectorAvailable }, - } = props; - // We can land on this page from a choice page for multiple types of connectors - // If that's the case we want to skip the intro and configuration, if the external & internal connector have already been configured - const { configuredFields, configured } = sourceConfigData; - if (externalConnectorAvailable && configured && externalConfigured) + serviceType, + configured, + configuredFields: { clientId, clientSecret }, + } = sourceConfigData; + if (initialStep === 'connect') return AddSourceSteps.ConnectInstanceStep; + if (initialStep === 'configure') return AddSourceSteps.ConfigureOauthStep; + if (initialStep === 'reauthenticate') return AddSourceSteps.ReauthenticateStep; + if (serviceType !== 'external' && configured) return AddSourceSteps.ConnectInstanceStep; + + // TODO remove this once external/BYO connectors track `configured` properly + if (serviceType === 'external' && clientId && clientSecret) return AddSourceSteps.ConnectInstanceStep; - if (externalConnectorAvailable && !configured && externalConfigured) - return AddSourceSteps.SaveConfigStep; - if (serviceType === 'external') { - // external connectors can be partially configured, so we need to check which fields are filled - if (configuredFields?.clientId && configuredFields?.clientSecret) { - return AddSourceSteps.ConnectInstanceStep; - } - // Unconfigured external connectors have already shown the intro step before the choice page, so we don't want to show it again - return AddSourceSteps.SaveConfigStep; - } - if (connect) return AddSourceSteps.ConnectInstanceStep; - if (configure) return AddSourceSteps.ConfigureOauthStep; - if (reAuthenticate) return AddSourceSteps.ReauthenticateStep; - return AddSourceSteps.ConfigIntroStep; + + return AddSourceSteps.SaveConfigStep; }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/available_sources_list.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/available_sources_list.test.tsx index 06815ab3330f0..a44b5f54852c9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/available_sources_list.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/available_sources_list.test.tsx @@ -26,7 +26,7 @@ describe('AvailableSourcesList', () => { const wrapper = shallow(); expect(wrapper.find(EuiTitle)).toHaveLength(1); - expect(wrapper.find('[data-test-subj="AvailableSourceListItem"]')).toHaveLength(24); + expect(wrapper.find('[data-test-subj="AvailableSourceListItem"]')).toHaveLength(25); expect(wrapper.find('[data-test-subj="CustomAPISourceLink"]')).toHaveLength(1); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/available_sources_list.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/available_sources_list.tsx index 7dc9ad9ca0f60..9a2787d779070 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/available_sources_list.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/available_sources_list.tsx @@ -18,6 +18,7 @@ import { EuiTitle, EuiText, EuiToolTip, + EuiButtonEmpty, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -43,8 +44,13 @@ interface AvailableSourcesListProps { export const AvailableSourcesList: React.FC = ({ sources }) => { const { hasPlatinumLicense } = useValues(LicensingLogic); - const getSourceCard = ({ name, serviceType, accountContextOnly }: SourceDataItem) => { - const addPath = getAddPath(serviceType); + const getSourceCard = ({ + accountContextOnly, + baseServiceType, + name, + serviceType, + }: SourceDataItem) => { + const addPath = getAddPath(serviceType, baseServiceType); const disabled = !hasPlatinumLicense && accountContextOnly; const connectButton = () => { @@ -61,15 +67,30 @@ export const AvailableSourcesList: React.FC = ({ sour } )} > - - Connect - + + {i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.availableSourceList.connectButtonLabel', + { + defaultMessage: 'Connect', + } + )} + ); } else { return ( - - Connect + + {i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.availableSourceList.connectButtonLabel', + { + defaultMessage: 'Connect', + } + )} ); } @@ -79,7 +100,7 @@ export const AvailableSourcesList: React.FC = ({ sour <> - + {name} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_choice.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_choice.test.tsx index 94821c0561cf4..0ed33a01d606f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_choice.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_choice.test.tsx @@ -5,20 +5,19 @@ * 2.0. */ -import { mockKibanaValues, setMockValues } from '../../../../../__mocks__/kea_logic'; +import { setMockActions, setMockValues } from '../../../../../__mocks__/kea_logic'; import React from 'react'; import { mount } from 'enzyme'; -import { EuiButton } from '@elastic/eui'; +import { EuiButtonTo } from '../../../../../shared/react_router_helpers'; import { staticSourceData } from '../../source_data'; import { ConfigurationChoice } from './configuration_choice'; describe('ConfigurationChoice', () => { - const { navigateToUrl } = mockKibanaValues; const props = { sourceData: staticSourceData[0], }; @@ -28,31 +27,23 @@ describe('ConfigurationChoice', () => { categories: [], }, }; + const mockActions = { + initializeSources: jest.fn(), + resetSourcesState: jest.fn(), + }; beforeEach(() => { - setMockValues(mockValues); jest.clearAllMocks(); + setMockValues(mockValues); + setMockActions(mockActions); }); it('renders internal connector if available', () => { const wrapper = mount(); - expect(wrapper.find('EuiCard')).toHaveLength(1); - expect(wrapper.find(EuiButton)).toHaveLength(1); - }); - it('should navigate to internal connector on internal connector click', () => { - const wrapper = mount(); - const button = wrapper.find(EuiButton); - button.simulate('click'); - expect(navigateToUrl).toHaveBeenCalledWith('/sources/add/box/internal/'); - }); - it('should call prop function when provided on internal connector click', () => { - const advanceSpy = jest.fn(); - const wrapper = mount(); - const button = wrapper.find(EuiButton); - button.simulate('click'); - expect(navigateToUrl).not.toHaveBeenCalled(); - expect(advanceSpy).toHaveBeenCalled(); + const internalConnectorCard = wrapper.find('[data-test-subj="InternalConnectorCard"]'); + expect(internalConnectorCard).toHaveLength(1); + expect(internalConnectorCard.find(EuiButtonTo).prop('to')).toEqual('/sources/add/box/'); }); it('renders external connector if available', () => { @@ -62,32 +53,36 @@ describe('ConfigurationChoice', () => { ...props, sourceData: { ...props.sourceData, - internalConnectorAvailable: false, - externalConnectorAvailable: true, + serviceType: 'share_point', }, }} /> ); - expect(wrapper.find('EuiCard')).toHaveLength(1); - expect(wrapper.find(EuiButton)).toHaveLength(1); + const externalConnectorCard = wrapper.find('[data-test-subj="ExternalConnectorCard"]'); + expect(externalConnectorCard).toHaveLength(1); + expect(externalConnectorCard.find(EuiButtonTo).prop('to')).toEqual( + '/sources/add/share_point/external/connector_registration' + ); }); - it('should navigate to external connector on external connector click', () => { + + it('renders disabled message if external connector is available but user has already configured', () => { + setMockValues({ ...mockValues, externalConfigured: true }); + const wrapper = mount( ); - const button = wrapper.find(EuiButton); - button.simulate('click'); - expect(navigateToUrl).toHaveBeenCalledWith('/sources/add/box/external/'); + + const externalConnectorCard = wrapper.find('[data-test-subj="ExternalConnectorCard"]'); + expect(externalConnectorCard.prop('disabledMessage')).toBeDefined(); }); it('renders custom connector if available', () => { @@ -97,33 +92,16 @@ describe('ConfigurationChoice', () => { ...props, sourceData: { ...props.sourceData, - internalConnectorAvailable: false, - externalConnectorAvailable: false, - customConnectorAvailable: true, + serviceType: 'share_point_server', }, }} /> ); - expect(wrapper.find('EuiCard')).toHaveLength(1); - expect(wrapper.find(EuiButton)).toHaveLength(1); - }); - it('should navigate to custom connector on custom connector click', () => { - const wrapper = mount( - + const customConnectorCard = wrapper.find('[data-test-subj="CustomConnectorCard"]'); + expect(customConnectorCard).toHaveLength(1); + expect(customConnectorCard.find(EuiButtonTo).prop('to')).toEqual( + '/sources/add/share_point_server/custom' ); - const button = wrapper.find(EuiButton); - button.simulate('click'); - expect(navigateToUrl).toHaveBeenCalledWith('/sources/add/box/custom/'); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_choice.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_choice.tsx index 8d8311d2a0a6f..7d5721d8547d2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_choice.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_choice.tsx @@ -5,92 +5,85 @@ * 2.0. */ -import React from 'react'; +import React, { useEffect } from 'react'; -import { useValues } from 'kea'; +import { useActions, useValues } from 'kea'; + +import { EuiCard, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { EuiButton, EuiCard, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { KibanaLogic } from '../../../../../shared/kibana'; +import { EuiButtonTo } from '../../../../../shared/react_router_helpers'; import { AppLogic } from '../../../../app_logic'; import { getAddPath, getSourcesPath } from '../../../../routes'; import { SourceDataItem } from '../../../../types'; -import { AddSourceHeader } from './add_source_header'; -import { AddSourceLogic } from './add_source_logic'; +import { hasCustomConnectorOption, hasExternalConnectorOption } from '../../source_data'; -interface ConfigurationChoiceProps { - sourceData: SourceDataItem; - goToInternalStep?: () => void; -} +import { SourcesLogic } from '../../sources_logic'; + +import { AddSourceHeader } from './add_source_header'; interface CardProps { title: string; description: string; buttonText: string; - onClick: () => void; + to: string; badgeLabel?: string; + disabledMessage?: string; +} + +const ConnectorCard: React.FC = ({ + title, + description, + buttonText, + to, + badgeLabel, + disabledMessage, +}: CardProps) => ( + + + {buttonText} + + } + /> + +); + +interface ConfigurationChoiceProps { + sourceData: SourceDataItem; } export const ConfigurationChoice: React.FC = ({ - sourceData: { - name, - serviceType, - externalConnectorAvailable, - internalConnectorAvailable, - customConnectorAvailable, - }, - goToInternalStep, + sourceData: { name, categories = [], serviceType }, }) => { + const externalConnectorAvailable = hasExternalConnectorOption(serviceType); + const customConnectorAvailable = hasCustomConnectorOption(serviceType); + const { isOrganization } = useValues(AppLogic); - const { sourceConfigData } = useValues(AddSourceLogic); - const { categories } = sourceConfigData; - const goToInternal = goToInternalStep - ? goToInternalStep - : () => - KibanaLogic.values.navigateToUrl( - `${getSourcesPath( - `${getSourcesPath(getAddPath(serviceType), isOrganization)}/internal`, - isOrganization - )}/` - ); - const goToExternal = () => - KibanaLogic.values.navigateToUrl( - `${getSourcesPath( - `${getSourcesPath(getAddPath(serviceType), isOrganization)}/external`, - isOrganization - )}/` - ); - const goToCustom = () => - KibanaLogic.values.navigateToUrl( - `${getSourcesPath( - `${getSourcesPath(getAddPath(serviceType), isOrganization)}/custom`, - isOrganization - )}/` - ); - - const ConnectorCard: React.FC = ({ - title, - description, - buttonText, - onClick, - badgeLabel, - }: CardProps) => ( - - - {buttonText} - - } - /> - - ); + + const { initializeSources, resetSourcesState } = useActions(SourcesLogic); + + const { externalConfigured } = useValues(SourcesLogic); + + useEffect(() => { + initializeSources(); + return resetSourcesState; + }, []); + + const internalTo = `${getSourcesPath(getAddPath(serviceType), isOrganization)}/`; + const externalTo = `${getSourcesPath( + getAddPath('external', serviceType), + isOrganization + )}/connector_registration`; + const customTo = `${getSourcesPath(getAddPath('custom', serviceType), isOrganization)}`; const internalConnectorProps: CardProps = { title: i18n.translate( @@ -118,7 +111,7 @@ export const ConfigurationChoice: React.FC = ({ defaultMessage: 'Recommended', } ), - onClick: goToInternal, + to: internalTo, }; const externalConnectorProps: CardProps = { @@ -141,7 +134,7 @@ export const ConfigurationChoice: React.FC = ({ defaultMessage: 'Instructions', } ), - onClick: goToExternal, + to: externalTo, badgeLabel: i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.contentSource.configExternalChoice.external.betaLabel', { @@ -169,7 +162,7 @@ export const ConfigurationChoice: React.FC = ({ defaultMessage: 'Instructions', } ), - onClick: goToCustom, + to: customTo, }; return ( @@ -177,9 +170,26 @@ export const ConfigurationChoice: React.FC = ({ - {internalConnectorAvailable && } - {externalConnectorAvailable && } - {customConnectorAvailable && } + + {externalConnectorAvailable && ( + + )} + {customConnectorAvailable && ( + + )} ); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_intro.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_intro.test.tsx index b3ce53a0321dc..0f1beff70735c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_intro.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_intro.test.tsx @@ -14,11 +14,10 @@ import { EuiText, EuiTitle } from '@elastic/eui'; import { ConfigurationIntro } from './configuration_intro'; describe('ConfigurationIntro', () => { - const advanceStep = jest.fn(); const props = { header:

Header

, name: 'foo', - advanceStep, + advanceStepTo: '', }; it('renderscontext', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_intro.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_intro.tsx index 5c52537d4a738..e5da9f6e00316 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_intro.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_intro.tsx @@ -9,7 +9,6 @@ import React from 'react'; import { EuiBadge, - EuiButton, EuiFlexGroup, EuiFlexItem, EuiFormRow, @@ -18,9 +17,12 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiButtonTo } from '../../../../../shared/react_router_helpers'; + import connectionIllustration from '../../../../assets/connection_illustration.svg'; import { @@ -37,12 +39,12 @@ import { interface ConfigurationIntroProps { header: React.ReactNode; name: string; - advanceStep(): void; + advanceStepTo: string; } export const ConfigurationIntro: React.FC = ({ name, - advanceStep, + advanceStepTo, header, }) => ( <> @@ -144,11 +146,11 @@ export const ConfigurationIntro: React.FC = ({ - {i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.contentSource.configIntro.configure.button', @@ -157,7 +159,7 @@ export const ConfigurationIntro: React.FC = ({ values: { name }, } )} - + diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configure_oauth.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configure_oauth.test.tsx index 332456cae99ad..c776723377f44 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configure_oauth.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configure_oauth.test.tsx @@ -22,7 +22,7 @@ describe('ConfigureOauth', () => { const onFormCreated = jest.fn(); const getPreContentSourceConfigData = jest.fn(); const setSelectedGithubOrganizations = jest.fn(); - const createContentSource = jest.fn((_, formSubmitSuccess, handleFormSubmitError) => { + const createContentSource = jest.fn((formSubmitSuccess, handleFormSubmitError) => { formSubmitSuccess(); handleFormSubmitError(); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configure_oauth.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configure_oauth.tsx index ce5a92a19e387..af50e8267da2f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configure_oauth.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configure_oauth.tsx @@ -35,12 +35,8 @@ export const ConfigureOauth: React.FC = ({ name, onFormCrea const { getPreContentSourceConfigData, setSelectedGithubOrganizations, createContentSource } = useActions(AddSourceLogic); - const { - currentServiceType, - githubOrganizations, - selectedGithubOrganizationsMap, - sectionLoading, - } = useValues(AddSourceLogic); + const { githubOrganizations, selectedGithubOrganizationsMap, sectionLoading } = + useValues(AddSourceLogic); const checkboxOptions = githubOrganizations.map((item) => ({ id: item, label: item })); @@ -54,7 +50,7 @@ export const ConfigureOauth: React.FC = ({ name, onFormCrea const handleFormSubmit = (e: FormEvent) => { setFormLoading(true); e.preventDefault(); - createContentSource(currentServiceType, formSubmitSuccess, handleFormSubmitError); + createContentSource(formSubmitSuccess, handleFormSubmitError); }; const configfieldsForm = ( diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configured_sources_list.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configured_sources_list.test.tsx index 5b23368289f1a..998a4c1d53b8a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configured_sources_list.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configured_sources_list.test.tsx @@ -11,6 +11,8 @@ import React from 'react'; import { shallow } from 'enzyme'; +import { EuiButtonEmpty } from '@elastic/eui'; + import { EuiButtonEmptyTo } from '../../../../../shared/react_router_helpers'; import { ConfiguredSourcesList } from './configured_sources_list'; @@ -24,47 +26,38 @@ describe('ConfiguredSourcesList', () => { it('renders', () => { const wrapper = shallow(); - expect(wrapper.find('[data-test-subj="UnConnectedTooltip"]')).toHaveLength(20); + expect(wrapper.find('[data-test-subj="UnConnectedTooltip"]')).toHaveLength(21); expect(wrapper.find('[data-test-subj="AccountOnlyTooltip"]')).toHaveLength(2); - expect(wrapper.find('[data-test-subj="ConfiguredSourcesListItem"]')).toHaveLength(23); - }); - - it('does show connect button for a connected external source', () => { - const wrapper = shallow( - - ); - expect(wrapper.find(EuiButtonEmptyTo)).toHaveLength(1); + expect(wrapper.find('[data-test-subj="ConfiguredSourcesListItem"]')).toHaveLength(24); }); - it('does show connect button for an unconnected external source', () => { + it('shows connect button for an source with multiple connector options that routes to choice page', () => { const wrapper = shallow( ); const button = wrapper.find(EuiButtonEmptyTo); expect(button).toHaveLength(1); - expect(button.prop('to')).toEqual('/sources/add/external/connect'); + expect(button.prop('to')).toEqual('/sources/add/share_point/choice'); }); - it('connect button for an unconnected source with multiple connector options routes to choice page', () => { + it('shows connect button for a source without multiple connector options that routes to add page', () => { const wrapper = shallow( { ); const button = wrapper.find(EuiButtonEmptyTo); expect(button).toHaveLength(1); - expect(button.prop('to')).toEqual('/sources/add/share_point/'); + expect(button.prop('to')).toEqual('/sources/add/slack/'); }); - it('connect button for a source with multiple connector options routes to connect page for private sources', () => { + it('disabled when in organization mode and connector is account context only', () => { const wrapper = shallow( ); - const button = wrapper.find(EuiButtonEmptyTo); + const button = wrapper.find(EuiButtonEmpty); expect(button).toHaveLength(1); - expect(button.prop('to')).toEqual('/p/sources/add/share_point/connect'); + expect(button.prop('isDisabled')).toBe(true); }); it('handles empty state', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configured_sources_list.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configured_sources_list.tsx index bbec096ae07d8..820df302725b7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configured_sources_list.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configured_sources_list.tsx @@ -27,7 +27,8 @@ import { EuiButtonEmptyTo } from '../../../../../shared/react_router_helpers'; import { SourceIcon } from '../../../../components/shared/source_icon'; import { getAddPath, getSourcesPath } from '../../../../routes'; import { SourceDataItem } from '../../../../types'; -import { hasMultipleConnectorOptions } from '../../../../utils'; + +import { hasMultipleConnectorOptions } from '../../source_data'; import { CONFIGURED_SOURCES_LIST_UNCONNECTED_TOOLTIP, @@ -72,7 +73,8 @@ export const ConfiguredSourcesList: React.FC = ({ const visibleSources = ( {sources.map((sourceData, i) => { - const { connected, accountContextOnly, name, serviceType, isBeta } = sourceData; + const { connected, accountContextOnly, name, serviceType, isBeta, baseServiceType } = + sourceData; return ( = ({ responsive={false} > - + @@ -128,7 +134,7 @@ export const ConfiguredSourcesList: React.FC = ({ {!connected diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.test.tsx index 3e850277c0b72..992bb561796fe 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.test.tsx @@ -33,10 +33,10 @@ describe('ConnectInstance', () => { const setSourcePasswordValue = jest.fn(); const setSourceSubdomainValue = jest.fn(); const setSourceIndexPermissionsValue = jest.fn(); - const getSourceConnectData = jest.fn((_, redirectOauth) => { + const getSourceConnectData = jest.fn((redirectOauth) => { redirectOauth(); }); - const createContentSource = jest.fn((_, redirectFormCreated) => { + const createContentSource = jest.fn((redirectFormCreated) => { redirectFormCreated(); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.tsx index 352addd8176d8..0a4c1a9692e63 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.tsx @@ -46,7 +46,6 @@ export const ConnectInstance: React.FC = ({ features, objTypes, name, - serviceType, needsPermissions, onFormCreated, header, @@ -74,8 +73,8 @@ export const ConnectInstance: React.FC = ({ const redirectOauth = (oauthUrl: string) => window.location.replace(oauthUrl); const redirectFormCreated = () => onFormCreated(name); - const onOauthFormSubmit = () => getSourceConnectData(serviceType, redirectOauth); - const onCredentialsFormSubmit = () => createContentSource(serviceType, redirectFormCreated); + const onOauthFormSubmit = () => getSourceConnectData(redirectOauth); + const onCredentialsFormSubmit = () => createContentSource(redirectFormCreated); const handleFormSubmit = (e: FormEvent) => { e.preventDefault(); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/source_features.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/source_features.tsx index edfb2897fce15..7a80c9d6980b5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/source_features.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/source_features.tsx @@ -39,7 +39,7 @@ import { SOURCE_FEATURES_GLOBAL_ACCESS_PERMISSIONS_FEATURE_DESCRIPTION, } from './constants'; -interface ConnectInstanceProps { +interface SourceFeatureProps { features?: Features; objTypes?: string[]; name: string; @@ -47,7 +47,7 @@ interface ConnectInstanceProps { type IncludedFeatureIds = Exclude; -export const SourceFeatures: React.FC = ({ features, objTypes, name }) => { +export const SourceFeatures: React.FC = ({ features, objTypes, name }) => { const { hasPlatinumLicense } = useValues(LicensingLogic); const { isOrganization } = useValues(AppLogic); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/custom_source_deployment.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/custom_source_deployment.test.tsx index afacfd0ccbbf9..017a9eb5b5dd0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/custom_source_deployment.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/custom_source_deployment.test.tsx @@ -24,14 +24,6 @@ const customSource = { name: 'name', }; -const preconfiguredSourceData = { - ...staticCustomSourceData, - serviceType: 'sharepoint-server', - configuration: { - ...staticCustomSourceData.configuration, - githubRepository: 'elastic/sharepoint-server-connector', - }, -}; const mockValues = { sourceData: staticCustomSourceData, }; @@ -44,9 +36,7 @@ describe('CustomSourceDeployment', () => { jest.clearAllMocks(); setMockValues(mockValues); - wrapper = shallow( - - ); + wrapper = shallow(); }); it('contains a source identifier', () => { @@ -69,7 +59,7 @@ describe('CustomSourceDeployment', () => { }); wrapper = shallow( - + ); }); @@ -86,9 +76,7 @@ describe('CustomSourceDeployment', () => { jest.clearAllMocks(); setMockValues(mockValues); - const wrapper = shallow( - - ); + const wrapper = shallow(); expect(wrapper.find(EuiPanel).prop('paddingSize')).toEqual('m'); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/custom_source_deployment.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/custom_source_deployment.tsx index 7d34783e998a7..8910a8acd0c5a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/custom_source_deployment.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/custom_source_deployment.tsx @@ -14,17 +14,30 @@ import { EuiLinkTo } from '../../../../shared/react_router_helpers'; import { API_KEY_LABEL } from '../../../constants'; import { API_KEYS_PATH } from '../../../routes'; -import { ContentSource, CustomSource, SourceDataItem } from '../../../types'; +import { ContentSource, CustomSource } from '../../../types'; + +import { getSourceData } from '../source_data'; import { SourceIdentifier } from './source_identifier'; interface Props { source: ContentSource | CustomSource; - sourceData: SourceDataItem; + baseServiceType?: string; small?: boolean; } -export const CustomSourceDeployment: React.FC = ({ source, sourceData, small = false }) => { +export const CustomSourceDeployment: React.FC = ({ + source, + baseServiceType, + small = false, +}) => { const { name, id } = source; + + const sourceData = getSourceData('custom', baseServiceType); + + if (!sourceData) { + return null; + } + const { configuration: { documentationUrl, githubRepository }, } = sourceData; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.test.tsx index 9af4eae693d7c..ae6e516ef7d4a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.test.tsx @@ -16,8 +16,6 @@ import { EuiCallOut, EuiConfirmModal, EuiEmptyPrompt, EuiTable } from '@elastic/ import { ComponentLoader } from '../../../components/shared/component_loader'; -import * as SourceData from '../source_data'; - import { CustomSourceDeployment } from './custom_source_deployment'; import { Overview } from './overview'; @@ -144,33 +142,6 @@ describe('Overview', () => { expect(initializeSourceSynchronization).toHaveBeenCalled(); }); - it('uses a base service type if one is provided', () => { - jest.spyOn(SourceData, 'getSourceData'); - setMockValues({ - ...mockValues, - contentSource: { - ...fullContentSources[0], - baseServiceType: 'share_point_server', - }, - }); - - shallow(); - - expect(SourceData.getSourceData).toHaveBeenCalledWith('share_point_server'); - }); - - it('defaults to the regular service tye', () => { - jest.spyOn(SourceData, 'getSourceData'); - setMockValues({ - ...mockValues, - contentSource: fullContentSources[0], - }); - - shallow(); - - expect(SourceData.getSourceData).toHaveBeenCalledWith('custom'); - }); - describe('custom sources', () => { it('includes deployment instructions', () => { setMockValues({ diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx index 698dc7a60eea4..ac31ee8314fc8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx @@ -81,7 +81,6 @@ import { SOURCE_SYNC_CONFIRM_TITLE, SOURCE_SYNC_CONFIRM_MESSAGE, } from '../constants'; -import { getSourceData } from '../source_data'; import { SourceLogic } from '../source_logic'; import { CustomSourceDeployment } from './custom_source_deployment'; @@ -106,12 +105,10 @@ export const Overview: React.FC = () => { isFederatedSource, isIndexedSource, name, + serviceType, + baseServiceType, } = contentSource; - const serviceType = contentSource.baseServiceType || contentSource.serviceType; - - const sourceData = getSourceData(serviceType); - const [isSyncing, setIsSyncing] = useState(false); const [isModalVisible, setIsModalVisible] = useState(false); const closeModal = () => setIsModalVisible(false); @@ -431,7 +428,7 @@ export const Overview: React.FC = () => { - + ); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx index d660b4499e210..f872648fc101d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx @@ -88,7 +88,7 @@ export const SourceSettings: React.FC = () => { const { isOrganization } = useValues(AppLogic); useEffect(() => { - getSourceConfigData(serviceType); + getSourceConfigData(); }, []); const isGithubApp = diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_data.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_data.tsx index 282de2590df7f..0088e80066a02 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_data.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_data.tsx @@ -17,9 +17,10 @@ import { } from '../../constants'; import { FeatureIds, SourceDataItem } from '../../types'; -export const staticExternalSourceData: SourceDataItem = { +// TODO remove Sharepoint-specific content after BYO connector support +export const staticGenericExternalSourceData: SourceDataItem = { name: SOURCE_NAMES.SHAREPOINT, - iconName: SOURCE_NAMES.SHAREPOINT, + categories: [], serviceType: 'external', configuration: { isPublicKey: false, @@ -40,16 +41,12 @@ export const staticExternalSourceData: SourceDataItem = { platinumPrivateContext: [FeatureIds.Private, FeatureIds.SyncFrequency, FeatureIds.SyncedItems], }, accountContextOnly: false, - internalConnectorAvailable: true, - externalConnectorAvailable: false, - customConnectorAvailable: false, isBeta: true, }; export const staticSourceData: SourceDataItem[] = [ { name: SOURCE_NAMES.BOX, - iconName: SOURCE_NAMES.BOX, serviceType: 'box', configuration: { isPublicKey: false, @@ -74,11 +71,9 @@ export const staticSourceData: SourceDataItem[] = [ ], }, accountContextOnly: false, - internalConnectorAvailable: true, }, { name: SOURCE_NAMES.CONFLUENCE, - iconName: SOURCE_NAMES.CONFLUENCE, serviceType: 'confluence_cloud', configuration: { isPublicKey: false, @@ -108,11 +103,9 @@ export const staticSourceData: SourceDataItem[] = [ ], }, accountContextOnly: false, - internalConnectorAvailable: true, }, { name: SOURCE_NAMES.CONFLUENCE_SERVER, - iconName: SOURCE_NAMES.CONFLUENCE_SERVER, serviceType: 'confluence_server', configuration: { isPublicKey: true, @@ -140,11 +133,9 @@ export const staticSourceData: SourceDataItem[] = [ ], }, accountContextOnly: false, - internalConnectorAvailable: true, }, { name: SOURCE_NAMES.DROPBOX, - iconName: SOURCE_NAMES.DROPBOX, serviceType: 'dropbox', configuration: { isPublicKey: false, @@ -169,11 +160,9 @@ export const staticSourceData: SourceDataItem[] = [ ], }, accountContextOnly: false, - internalConnectorAvailable: true, }, { name: SOURCE_NAMES.GITHUB, - iconName: SOURCE_NAMES.GITHUB, serviceType: 'github', configuration: { isPublicKey: false, @@ -205,11 +194,9 @@ export const staticSourceData: SourceDataItem[] = [ ], }, accountContextOnly: false, - internalConnectorAvailable: true, }, { name: SOURCE_NAMES.GITHUB_ENTERPRISE, - iconName: SOURCE_NAMES.GITHUB_ENTERPRISE, serviceType: 'github_enterprise_server', configuration: { isPublicKey: false, @@ -247,11 +234,9 @@ export const staticSourceData: SourceDataItem[] = [ ], }, accountContextOnly: false, - internalConnectorAvailable: true, }, { name: SOURCE_NAMES.GMAIL, - iconName: SOURCE_NAMES.GMAIL, serviceType: 'gmail', configuration: { isPublicKey: false, @@ -265,11 +250,9 @@ export const staticSourceData: SourceDataItem[] = [ platinumPrivateContext: [FeatureIds.Remote, FeatureIds.Private, FeatureIds.SearchableContent], }, accountContextOnly: true, - internalConnectorAvailable: true, }, { name: SOURCE_NAMES.GOOGLE_DRIVE, - iconName: SOURCE_NAMES.GOOGLE_DRIVE, serviceType: 'google_drive', configuration: { isPublicKey: false, @@ -298,11 +281,9 @@ export const staticSourceData: SourceDataItem[] = [ ], }, accountContextOnly: false, - internalConnectorAvailable: true, }, { name: SOURCE_NAMES.JIRA, - iconName: SOURCE_NAMES.JIRA, serviceType: 'jira_cloud', configuration: { isPublicKey: false, @@ -334,11 +315,9 @@ export const staticSourceData: SourceDataItem[] = [ ], }, accountContextOnly: false, - internalConnectorAvailable: true, }, { name: SOURCE_NAMES.JIRA_SERVER, - iconName: SOURCE_NAMES.JIRA_SERVER, serviceType: 'jira_server', configuration: { isPublicKey: true, @@ -369,13 +348,12 @@ export const staticSourceData: SourceDataItem[] = [ ], }, accountContextOnly: false, - internalConnectorAvailable: true, }, { name: SOURCE_NAMES.NETWORK_DRVE, - iconName: SOURCE_NAMES.NETWORK_DRVE, categories: [SOURCE_CATEGORIES.STORAGE], - serviceType: 'network_drive', // this doesn't exist on the BE + serviceType: 'custom', + baseServiceType: 'network_drive', configuration: { isPublicKey: false, hasOauthRedirect: false, @@ -385,12 +363,9 @@ export const staticSourceData: SourceDataItem[] = [ githubRepository: 'elastic/enterprise-search-network-drive-connector', }, accountContextOnly: false, - internalConnectorAvailable: false, - customConnectorAvailable: true, }, { name: SOURCE_NAMES.ONEDRIVE, - iconName: SOURCE_NAMES.ONEDRIVE, serviceType: 'one_drive', configuration: { isPublicKey: false, @@ -415,17 +390,16 @@ export const staticSourceData: SourceDataItem[] = [ ], }, accountContextOnly: false, - internalConnectorAvailable: true, }, { name: SOURCE_NAMES.OUTLOOK, - iconName: SOURCE_NAMES.OUTLOOK, categories: [ SOURCE_CATEGORIES.COMMUNICATIONS, SOURCE_CATEGORIES.PRODUCTIVITY, SOURCE_CATEGORIES.MICROSOFT, ], - serviceType: 'outlook', // this doesn't exist on the BE + serviceType: 'custom', + baseServiceType: 'outlook', configuration: { isPublicKey: false, hasOauthRedirect: false, @@ -435,12 +409,9 @@ export const staticSourceData: SourceDataItem[] = [ githubRepository: 'elastic/enterprise-search-outlook-connector', }, accountContextOnly: false, - internalConnectorAvailable: false, - customConnectorAvailable: true, }, { name: SOURCE_NAMES.SALESFORCE, - iconName: SOURCE_NAMES.SALESFORCE, serviceType: 'salesforce', configuration: { isPublicKey: false, @@ -472,11 +443,9 @@ export const staticSourceData: SourceDataItem[] = [ ], }, accountContextOnly: false, - internalConnectorAvailable: true, }, { name: SOURCE_NAMES.SALESFORCE_SANDBOX, - iconName: SOURCE_NAMES.SALESFORCE_SANDBOX, serviceType: 'salesforce_sandbox', configuration: { isPublicKey: false, @@ -508,11 +477,9 @@ export const staticSourceData: SourceDataItem[] = [ ], }, accountContextOnly: false, - internalConnectorAvailable: true, }, { name: SOURCE_NAMES.SERVICENOW, - iconName: SOURCE_NAMES.SERVICENOW, serviceType: 'service_now', configuration: { isPublicKey: false, @@ -541,11 +508,9 @@ export const staticSourceData: SourceDataItem[] = [ ], }, accountContextOnly: false, - internalConnectorAvailable: true, }, { name: SOURCE_NAMES.SHAREPOINT, - iconName: SOURCE_NAMES.SHAREPOINT, serviceType: 'share_point', configuration: { isPublicKey: false, @@ -570,13 +535,39 @@ export const staticSourceData: SourceDataItem[] = [ ], }, accountContextOnly: false, - internalConnectorAvailable: true, - externalConnectorAvailable: true, }, - staticExternalSourceData, + { + name: SOURCE_NAMES.SHAREPOINT, + categories: [], + serviceType: 'external', + baseServiceType: 'share_point', + configuration: { + isPublicKey: false, + hasOauthRedirect: true, + needsBaseUrl: false, + documentationUrl: docLinks.workplaceSearchExternalSharePointOnline, + applicationPortalUrl: 'https://portal.azure.com/', + }, + objTypes: [SOURCE_OBJ_TYPES.ALL_STORED_FILES], + features: { + basicOrgContext: [ + FeatureIds.SyncFrequency, + FeatureIds.SyncedItems, + FeatureIds.GlobalAccessPermissions, + ], + basicOrgContextExcludedFeatures: [FeatureIds.DocumentLevelPermissions], + platinumOrgContext: [FeatureIds.SyncFrequency, FeatureIds.SyncedItems], + platinumPrivateContext: [ + FeatureIds.Private, + FeatureIds.SyncFrequency, + FeatureIds.SyncedItems, + ], + }, + accountContextOnly: false, + isBeta: true, + }, { name: SOURCE_NAMES.SHAREPOINT_SERVER, - iconName: SOURCE_NAMES.SHAREPOINT_SERVER, categories: [ SOURCE_CATEGORIES.FILE_SHARING, SOURCE_CATEGORIES.STORAGE, @@ -584,7 +575,8 @@ export const staticSourceData: SourceDataItem[] = [ SOURCE_CATEGORIES.MICROSOFT, SOURCE_CATEGORIES.OFFICE_365, ], - serviceType: 'share_point_server', // this doesn't exist on the BE + serviceType: 'custom', + baseServiceType: 'share_point_server', configuration: { isPublicKey: false, hasOauthRedirect: false, @@ -594,12 +586,9 @@ export const staticSourceData: SourceDataItem[] = [ githubRepository: 'elastic/enterprise-search-sharepoint-server-connector', }, accountContextOnly: false, - internalConnectorAvailable: false, - customConnectorAvailable: true, }, { name: SOURCE_NAMES.SLACK, - iconName: SOURCE_NAMES.SLACK, serviceType: 'slack', configuration: { isPublicKey: false, @@ -617,17 +606,16 @@ export const staticSourceData: SourceDataItem[] = [ platinumPrivateContext: [FeatureIds.Remote, FeatureIds.Private, FeatureIds.SearchableContent], }, accountContextOnly: true, - internalConnectorAvailable: true, }, { name: SOURCE_NAMES.TEAMS, - iconName: SOURCE_NAMES.TEAMS, categories: [ SOURCE_CATEGORIES.COMMUNICATIONS, SOURCE_CATEGORIES.PRODUCTIVITY, SOURCE_CATEGORIES.MICROSOFT, ], - serviceType: 'teams', // this doesn't exist on the BE + serviceType: 'custom', + baseServiceType: 'teams', configuration: { isPublicKey: false, hasOauthRedirect: false, @@ -637,12 +625,9 @@ export const staticSourceData: SourceDataItem[] = [ githubRepository: 'elastic/enterprise-search-teams-connector', }, accountContextOnly: false, - internalConnectorAvailable: false, - customConnectorAvailable: true, }, { name: SOURCE_NAMES.ZENDESK, - iconName: SOURCE_NAMES.ZENDESK, serviceType: 'zendesk', configuration: { isPublicKey: false, @@ -667,13 +652,12 @@ export const staticSourceData: SourceDataItem[] = [ ], }, accountContextOnly: false, - internalConnectorAvailable: true, }, { name: SOURCE_NAMES.ZOOM, - iconName: SOURCE_NAMES.ZOOM, categories: [SOURCE_CATEGORIES.COMMUNICATIONS, SOURCE_CATEGORIES.PRODUCTIVITY], - serviceType: 'zoom', // this doesn't exist on the BE + serviceType: 'custom', + baseServiceType: 'zoom', configuration: { isPublicKey: false, hasOauthRedirect: false, @@ -683,14 +667,12 @@ export const staticSourceData: SourceDataItem[] = [ githubRepository: 'elastic/enterprise-search-zoom-connector', }, accountContextOnly: false, - internalConnectorAvailable: false, - customConnectorAvailable: true, }, + staticGenericExternalSourceData, ]; export const staticCustomSourceData: SourceDataItem = { name: SOURCE_NAMES.CUSTOM, - iconName: SOURCE_NAMES.CUSTOM, categories: ['API', 'Custom'], serviceType: 'custom', configuration: { @@ -701,12 +683,26 @@ export const staticCustomSourceData: SourceDataItem = { applicationPortalUrl: '', }, accountContextOnly: false, - customConnectorAvailable: true, }; -export const getSourceData = (serviceType: string): SourceDataItem => { - return ( - staticSourceData.find((staticSource) => staticSource.serviceType === serviceType) || - staticCustomSourceData +export const getSourceData = ( + serviceType: string, + baseServiceType?: string +): SourceDataItem | undefined => { + if (serviceType === 'custom' && typeof baseServiceType === 'undefined') { + return staticCustomSourceData; + } + return staticSourceData.find( + (staticSource) => + staticSource.serviceType === serviceType && staticSource.baseServiceType === baseServiceType ); }; + +export const hasExternalConnectorOption = (serviceType: string): boolean => + !!getSourceData('external', serviceType); + +export const hasCustomConnectorOption = (serviceType: string): boolean => + !!getSourceData('custom', serviceType); + +export const hasMultipleConnectorOptions = (serviceType: string): boolean => + hasExternalConnectorOption(serviceType) || hasCustomConnectorOption(serviceType); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.test.ts index 0f113ad402f28..0fdb827f6011d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.test.ts @@ -23,7 +23,12 @@ import { itShowsServerErrorAsFlashMessage } from '../../../test_helpers'; import { AppLogic } from '../../app_logic'; import { staticSourceData } from './source_data'; -import { SourcesLogic, fetchSourceStatuses, POLLING_INTERVAL } from './sources_logic'; +import { + SourcesLogic, + fetchSourceStatuses, + POLLING_INTERVAL, + mergeServerAndStaticData, +} from './sources_logic'; describe('SourcesLogic', () => { const { http } = mockHttpValues; @@ -37,8 +42,14 @@ describe('SourcesLogic', () => { const defaultValues = { contentSources: [], privateContentSources: [], - sourceData: staticSourceData.map((data) => ({ ...data, connected: false })), - availableSources: staticSourceData.map((data) => ({ ...data, connected: false })), + sourceData: mergeServerAndStaticData([], staticSourceData, []).map((data) => ({ + ...data, + connected: false, + })), + availableSources: mergeServerAndStaticData([], staticSourceData, []).map((data) => ({ + ...data, + connected: false, + })), configuredSources: [], serviceTypes: [], permissionsModal: null, @@ -322,7 +333,7 @@ describe('SourcesLogic', () => { it('availableSources & configuredSources have correct length', () => { SourcesLogic.actions.onInitializeSources(serverResponse); - expect(SourcesLogic.values.availableSources).toHaveLength(18); + expect(SourcesLogic.values.availableSources).toHaveLength(19); expect(SourcesLogic.values.configuredSources).toHaveLength(5); }); it('externalConfigured is set to true if external is configured', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts index 868831ab7c7fb..0f61ee580f677 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts @@ -51,7 +51,7 @@ export interface IPermissionsModalProps { additionalConfiguration: boolean; } -type CombinedDataItem = SourceDataItem & { connected: boolean }; +type CombinedDataItem = SourceDataItem & Partial & { connected: boolean }; export interface ISourcesValues { contentSources: ContentSourceDetails[]; @@ -145,17 +145,17 @@ export const SourcesLogic = kea>( selectors: ({ selectors }) => ({ availableSources: [ () => [selectors.sourceData], - (sourceData: SourceDataItem[]) => + (sourceData: CombinedDataItem[]) => sortByName(sourceData.filter(({ configured }) => !configured)), ], configuredSources: [ () => [selectors.sourceData], - (sourceData: SourceDataItem[]) => + (sourceData: CombinedDataItem[]) => sortByName(sourceData.filter(({ configured }) => configured)), ], externalConfigured: [ () => [selectors.configuredSources], - (configuredSources: SourceDataItem[]) => + (configuredSources: CombinedDataItem[]) => !!configuredSources.find((item) => item.serviceType === 'external'), ], sourceData: [ @@ -312,9 +312,12 @@ export const mergeServerAndStaticData = ( contentSources: ContentSourceDetails[] ): CombinedDataItem[] => { const unsortedData = staticData.map((staticItem) => { - const serverItem = serverData.find(({ serviceType }) => serviceType === staticItem.serviceType); + const serverItem = staticItem.baseServiceType + ? undefined // static items with base service types will never have matching external connectors, BE doesn't pass us a baseServiceType + : serverData.find(({ serviceType }) => serviceType === staticItem.serviceType); const connectedSource = contentSources.find( - ({ serviceType }) => serviceType === staticItem.serviceType + ({ baseServiceType, serviceType }) => + serviceType === staticItem.serviceType && baseServiceType === staticItem.baseServiceType ); return { ...staticItem, diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.test.tsx index 0fa263beab539..07baa82a5cdb0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.test.tsx @@ -10,11 +10,11 @@ import '../../../__mocks__/shallow_useeffect.mock'; import { setMockValues, setMockActions } from '../../../__mocks__/kea_logic'; import React from 'react'; -import { Route, Switch, Redirect } from 'react-router-dom'; +import { Redirect } from 'react-router-dom'; import { shallow } from 'enzyme'; -import { ADD_SOURCE_PATH, PRIVATE_SOURCES_PATH, SOURCES_PATH, getSourcesPath } from '../../routes'; +import { ADD_SOURCE_PATH, PRIVATE_SOURCES_PATH, getSourcesPath } from '../../routes'; import { SourcesRouter } from './sources_router'; @@ -34,19 +34,13 @@ describe('SourcesRouter', () => { }); it('renders sources routes', () => { - const TOTAL_ROUTES = 103; const wrapper = shallow(); - expect(wrapper.find(Switch)).toHaveLength(1); - expect(wrapper.find(Route)).toHaveLength(TOTAL_ROUTES); - }); - - it('redirects when nonplatinum license and accountOnly context', () => { - setMockValues({ ...mockValues, hasPlatinumLicense: false }); - const wrapper = shallow(); - - expect(wrapper.find(Redirect).last().prop('from')).toEqual(ADD_SOURCE_PATH); - expect(wrapper.find(Redirect).last().prop('to')).toEqual(SOURCES_PATH); + expect(wrapper.find('[data-test-subj="ConnectorIntroRoute"]')).toHaveLength(1); + expect(wrapper.find('[data-test-subj="ConnectorChoiceRoute"]')).toHaveLength(1); + expect(wrapper.find('[data-test-subj="ExternalConnectorConfigRoute"]')).toHaveLength(1); + expect(wrapper.find('[data-test-subj="AddCustomSourceRoute"]')).toHaveLength(2); + expect(wrapper.find('[data-test-subj="AddSourceRoute"]')).toHaveLength(1); }); it('redirects when cannot create sources', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.tsx index 19af955f8780c..4d4ec077213a0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.tsx @@ -11,7 +11,6 @@ import { Redirect, Route, Switch, useLocation } from 'react-router-dom'; import { Location } from 'history'; import { useActions, useValues } from 'kea'; -import { LicensingLogic } from '../../../shared/licensing'; import { AppLogic } from '../../app_logic'; import { GITHUB_ENTERPRISE_SERVER_VIA_APP_SERVICE_TYPE, @@ -24,17 +23,15 @@ import { SOURCES_PATH, getSourcesPath, getAddPath, - ADD_CUSTOM_PATH, } from '../../routes'; -import { hasMultipleConnectorOptions } from '../../utils'; import { AddSource, AddSourceList, GitHubViaApp } from './components/add_source'; import { AddCustomSource } from './components/add_source/add_custom_source'; import { ExternalConnectorConfig } from './components/add_source/add_external_connector'; -import { ConfigurationChoice } from './components/add_source/configuration_choice'; +import { AddSourceChoice } from './components/add_source/add_source_choice'; +import { AddSourceIntro } from './components/add_source/add_source_intro'; import { OrganizationSources } from './organization_sources'; import { PrivateSources } from './private_sources'; -import { staticCustomSourceData, staticSourceData as sources } from './source_data'; import { SourceRouter } from './source_router'; import { SourcesLogic } from './sources_logic'; @@ -42,7 +39,6 @@ import './sources.scss'; export const SourcesRouter: React.FC = () => { const { pathname } = useLocation() as Location; - const { hasPlatinumLicense } = useValues(LicensingLogic); const { resetSourcesState } = useActions(SourcesLogic); const { account: { canCreatePrivateSources }, @@ -82,119 +78,51 @@ export const SourcesRouter: React.FC = () => { - {sources.map((sourceData, i) => { - const { serviceType, externalConnectorAvailable, internalConnectorAvailable } = sourceData; - const path = `${getSourcesPath(getAddPath(serviceType), isOrganization)}`; - const defaultOption = internalConnectorAvailable - ? 'internal' - : externalConnectorAvailable - ? 'external' - : 'custom'; - const showChoice = defaultOption !== 'internal' && hasMultipleConnectorOptions(sourceData); - return ( - - {showChoice ? ( - - ) : ( - - )} - - ); - })} - - + + + + + + + + + + + + + + + + + - {sources - .filter((sourceData) => sourceData.internalConnectorAvailable) - .map((sourceData, i) => { - const { serviceType, accountContextOnly } = sourceData; - return ( - - {!hasPlatinumLicense && accountContextOnly ? ( - - ) : ( - - )} - - ); - })} - {sources - .filter((sourceData) => sourceData.externalConnectorAvailable) - .map((sourceData, i) => { - const { serviceType, accountContextOnly } = sourceData; - - return ( - - {!hasPlatinumLicense && accountContextOnly ? ( - - ) : ( - - )} - - ); - })} - {sources - .filter((sourceData) => sourceData.customConnectorAvailable) - .map((sourceData, i) => { - const { serviceType, accountContextOnly } = sourceData; - return ( - - {!hasPlatinumLicense && accountContextOnly ? ( - - ) : ( - - )} - - ); - })} - {sources.map((sourceData, i) => ( - - - - ))} - {sources.map((sourceData, i) => ( - - - - ))} - {sources.map((sourceData, i) => { - if (sourceData.configuration.needsConfiguration) - return ( - - - - ); - })} {canCreatePrivateSources ? ( diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/groups_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/groups_logic.mock.ts index 0e072210d2489..02e80d9f8c5b5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/groups_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/__mocks__/groups_logic.mock.ts @@ -6,12 +6,11 @@ */ import { DEFAULT_META } from '../../../../shared/constants'; -import { ContentSource, User, Group } from '../../../types'; +import { ContentSource, Group } from '../../../types'; export const mockGroupsValues = { groups: [] as Group[], contentSources: [] as ContentSource[], - users: [] as User[], groupsDataLoading: true, groupListLoading: true, newGroupModalOpen: false, @@ -21,10 +20,6 @@ export const mockGroupsValues = { newGroupNameErrors: [], filterSourcesDropdownOpen: false, filteredSources: [], - filterUsersDropdownOpen: false, - filteredUsers: [], - allGroupUsersLoading: false, - allGroupUsers: [], filterValue: '', groupsMeta: DEFAULT_META, }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_overview.test.tsx index e1ecc47f02669..97b2879ceef53 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_overview.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_overview.test.tsx @@ -91,7 +91,6 @@ describe('GroupOverview', () => { ...mockValues, group: { ...groups[0], - users: [], contentSources: [], }, }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_row.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_row.tsx index effacfa3aa4f8..d118037a2d80c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_row.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_row.tsx @@ -28,12 +28,6 @@ export const NO_SOURCES_MESSAGE = i18n.translate( defaultMessage: 'No organizational content sources', } ); -export const NO_USERS_MESSAGE = i18n.translate( - 'xpack.enterpriseSearch.workplaceSearch.groups.noUsersMessage', - { - defaultMessage: 'No users', - } -); const dateDisplay = (date: string) => moment(date).isAfter(moment().subtract(DAYS_CUTOFF, 'days')) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.tsx index 7af93490b2eb2..5b8b01a4bb1ef 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.tsx @@ -38,7 +38,6 @@ export const Groups: React.FC = () => { page: { total_results: numGroups }, }, filteredSources, - filteredUsers, filterValue, } = useValues(GroupsLogic); @@ -47,7 +46,7 @@ export const Groups: React.FC = () => { useEffect(() => { getSearchResults(true); return resetGroups; - }, [filteredSources, filteredUsers, filterValue]); + }, [filteredSources, filterValue]); if (newGroup && hasMessages) { messages[0].description = ( diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts index 97163f1529938..bc82c95871676 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.test.ts @@ -12,7 +12,6 @@ import { } from '../../../__mocks__/kea_logic'; import { contentSources } from '../../__mocks__/content_sources.mock'; import { groups } from '../../__mocks__/groups.mock'; -import { users } from '../../__mocks__/users.mock'; import { mockGroupsValues } from './__mocks__/groups_logic.mock'; import { nextTick } from '@kbn/test-jest-helpers'; @@ -49,13 +48,12 @@ describe('GroupsLogic', () => { describe('actions', () => { describe('onInitializeGroups', () => { it('sets reducers', () => { - GroupsLogic.actions.onInitializeGroups({ contentSources, users }); + GroupsLogic.actions.onInitializeGroups({ contentSources }); expect(GroupsLogic.values).toEqual({ ...mockGroupsValues, groupsDataLoading: false, contentSources, - users, }); }); }); @@ -103,59 +101,6 @@ describe('GroupsLogic', () => { }); }); - describe('addFilteredUser', () => { - it('sets reducers', () => { - GroupsLogic.actions.addFilteredUser('foo'); - GroupsLogic.actions.addFilteredUser('bar'); - GroupsLogic.actions.addFilteredUser('baz'); - - expect(GroupsLogic.values).toEqual({ - ...mockGroupsValues, - hasFiltersSet: true, - filteredUsers: ['bar', 'baz', 'foo'], - }); - }); - }); - - describe('removeFilteredUser', () => { - it('sets reducers', () => { - GroupsLogic.actions.addFilteredUser('foo'); - GroupsLogic.actions.addFilteredUser('bar'); - GroupsLogic.actions.addFilteredUser('baz'); - GroupsLogic.actions.removeFilteredUser('foo'); - - expect(GroupsLogic.values).toEqual({ - ...mockGroupsValues, - hasFiltersSet: true, - filteredUsers: ['bar', 'baz'], - }); - }); - }); - - describe('setGroupUsers', () => { - it('sets reducers', () => { - GroupsLogic.actions.setGroupUsers(users); - - expect(GroupsLogic.values).toEqual({ - ...mockGroupsValues, - allGroupUsersLoading: false, - allGroupUsers: users, - }); - }); - }); - - describe('setAllGroupLoading', () => { - it('sets reducer', () => { - GroupsLogic.actions.setAllGroupLoading(true); - - expect(GroupsLogic.values).toEqual({ - ...mockGroupsValues, - allGroupUsersLoading: true, - allGroupUsers: [], - }); - }); - }); - describe('setFilterValue', () => { it('sets reducer', () => { GroupsLogic.actions.setFilterValue('foo'); @@ -190,7 +135,6 @@ describe('GroupsLogic', () => { newGroup: groups[0], newGroupNameErrors: [], filteredSources: [], - filteredUsers: [], groupsMeta: DEFAULT_META, }); }); @@ -234,19 +178,6 @@ describe('GroupsLogic', () => { }); }); - describe('closeFilterUsersDropdown', () => { - it('sets reducer', () => { - // Open dropdown first - GroupsLogic.actions.toggleFilterUsersDropdown(); - GroupsLogic.actions.closeFilterUsersDropdown(); - - expect(GroupsLogic.values).toEqual({ - ...mockGroupsValues, - filterUsersDropdownOpen: false, - }); - }); - }); - describe('setGroupsLoading', () => { it('sets reducer', () => { // Set to false first @@ -294,7 +225,6 @@ describe('GroupsLogic', () => { const search = { query: '', content_source_ids: [], - user_ids: [], }; const payload = { @@ -352,22 +282,6 @@ describe('GroupsLogic', () => { }); }); - describe('fetchGroupUsers', () => { - it('calls API and sets values', async () => { - const setGroupUsersSpy = jest.spyOn(GroupsLogic.actions, 'setGroupUsers'); - http.get.mockReturnValue(Promise.resolve(users)); - - GroupsLogic.actions.fetchGroupUsers('123'); - expect(http.get).toHaveBeenCalledWith('/internal/workplace_search/groups/123/group_users'); - await nextTick(); - expect(setGroupUsersSpy).toHaveBeenCalledWith(users); - }); - - itShowsServerErrorAsFlashMessage(http.get, () => { - GroupsLogic.actions.fetchGroupUsers('123'); - }); - }); - describe('saveNewGroup', () => { it('calls API and sets values', async () => { const GROUP_NAME = 'new group'; @@ -430,7 +344,6 @@ describe('GroupsLogic', () => { expect(GroupsLogic.values).toEqual({ ...mockGroupsValues, filteredSources: [], - filteredUsers: [], filterValue: '', groupsMeta: DEFAULT_META, }); @@ -449,17 +362,5 @@ describe('GroupsLogic', () => { expect(clearFlashMessages).toHaveBeenCalled(); }); }); - - describe('toggleFilterUsersDropdown', () => { - it('sets reducer and clears flash messages', () => { - GroupsLogic.actions.toggleFilterUsersDropdown(); - - expect(GroupsLogic.values).toEqual({ - ...mockGroupsValues, - filterUsersDropdownOpen: true, - }); - expect(clearFlashMessages).toHaveBeenCalled(); - }); - }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.ts index c14538346ad31..3e137ea8a6713 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups_logic.ts @@ -18,13 +18,12 @@ import { flashSuccessToast, } from '../../../shared/flash_messages'; import { HttpLogic } from '../../../shared/http'; -import { ContentSource, Group, User } from '../../types'; +import { ContentSource, Group } from '../../types'; export const MAX_NAME_LENGTH = 40; interface GroupsServerData { contentSources: ContentSource[]; - users: User[]; } interface GroupsSearchResponse { @@ -37,10 +36,6 @@ interface GroupsActions { setSearchResults(data: GroupsSearchResponse): GroupsSearchResponse; addFilteredSource(sourceId: string): string; removeFilteredSource(sourceId: string): string; - addFilteredUser(userId: string): string; - removeFilteredUser(userId: string): string; - setGroupUsers(allGroupUsers: User[]): User[]; - setAllGroupLoading(allGroupUsersLoading: boolean): boolean; setFilterValue(filterValue: string): string; setActivePage(activePage: number): number; setNewGroupName(newGroupName: string): string; @@ -49,22 +44,18 @@ interface GroupsActions { openNewGroupModal(): void; closeNewGroupModal(): void; closeFilterSourcesDropdown(): void; - closeFilterUsersDropdown(): void; toggleFilterSourcesDropdown(): void; - toggleFilterUsersDropdown(): void; setGroupsLoading(): void; resetGroupsFilters(): void; resetGroups(): void; initializeGroups(): void; getSearchResults(resetPagination?: boolean): { resetPagination: boolean | undefined }; - fetchGroupUsers(groupId: string): { groupId: string }; saveNewGroup(): void; } interface GroupsValues { groups: Group[]; contentSources: ContentSource[]; - users: User[]; groupsDataLoading: boolean; groupListLoading: boolean; newGroupModalOpen: boolean; @@ -73,10 +64,6 @@ interface GroupsValues { newGroupNameErrors: string[]; filterSourcesDropdownOpen: boolean; filteredSources: string[]; - filterUsersDropdownOpen: boolean; - filteredUsers: string[]; - allGroupUsersLoading: boolean; - allGroupUsers: User[]; filterValue: string; groupsMeta: Meta; hasFiltersSet: boolean; @@ -89,10 +76,6 @@ export const GroupsLogic = kea>({ setSearchResults: (data) => data, addFilteredSource: (sourceId) => sourceId, removeFilteredSource: (sourceId) => sourceId, - addFilteredUser: (userId) => userId, - removeFilteredUser: (userId) => userId, - setGroupUsers: (allGroupUsers) => allGroupUsers, - setAllGroupLoading: (allGroupUsersLoading: boolean) => allGroupUsersLoading, setFilterValue: (filterValue) => filterValue, setActivePage: (activePage) => activePage, setNewGroupName: (newGroupName) => newGroupName, @@ -101,15 +84,12 @@ export const GroupsLogic = kea>({ openNewGroupModal: () => true, closeNewGroupModal: () => true, closeFilterSourcesDropdown: () => true, - closeFilterUsersDropdown: () => true, toggleFilterSourcesDropdown: () => true, - toggleFilterUsersDropdown: () => true, setGroupsLoading: () => true, resetGroupsFilters: () => true, resetGroups: () => true, initializeGroups: () => true, getSearchResults: (resetPagination) => ({ resetPagination }), - fetchGroupUsers: (groupId) => ({ groupId }), saveNewGroup: () => true, }, reducers: { @@ -125,12 +105,6 @@ export const GroupsLogic = kea>({ onInitializeGroups: (_, { contentSources }) => contentSources, }, ], - users: [ - [], - { - onInitializeGroups: (_, { users }) => users, - }, - ], groupsDataLoading: [ true, { @@ -193,36 +167,6 @@ export const GroupsLogic = kea>({ removeFilteredSource: (state, sourceId) => state.filter((id) => id !== sourceId), }, ], - filterUsersDropdownOpen: [ - false, - { - toggleFilterUsersDropdown: (state) => !state, - closeFilterUsersDropdown: () => false, - }, - ], - filteredUsers: [ - [], - { - resetGroupsFilters: () => [], - setNewGroup: () => [], - addFilteredUser: (state, userId) => [...state, userId].sort(), - removeFilteredUser: (state, userId) => state.filter((id) => id !== userId), - }, - ], - allGroupUsersLoading: [ - false, - { - setAllGroupLoading: (_, allGroupUsersLoading) => allGroupUsersLoading, - setGroupUsers: () => false, - }, - ], - allGroupUsers: [ - [], - { - setGroupUsers: (_, allGroupUsers) => allGroupUsers, - setAllGroupLoading: () => [], - }, - ], filterValue: [ '', { @@ -248,8 +192,8 @@ export const GroupsLogic = kea>({ }, selectors: ({ selectors }) => ({ hasFiltersSet: [ - () => [selectors.filteredUsers, selectors.filteredSources], - (filteredUsers, filteredSources) => filteredUsers.length > 0 || filteredSources.length > 0, + () => [selectors.filteredSources], + (filteredSources) => filteredSources.length > 0, ], }), listeners: ({ actions, values }) => ({ @@ -275,7 +219,6 @@ export const GroupsLogic = kea>({ }, filterValue, filteredSources, - filteredUsers, } = values; // Is the user changes the query while on a different page, we want to start back over at 1. @@ -286,7 +229,6 @@ export const GroupsLogic = kea>({ const search = { query: filterValue, content_source_ids: filteredSources, - user_ids: filteredUsers, }; try { @@ -306,17 +248,6 @@ export const GroupsLogic = kea>({ flashAPIErrors(e); } }, - fetchGroupUsers: async ({ groupId }) => { - actions.setAllGroupLoading(true); - try { - const response = await HttpLogic.values.http.get( - `/internal/workplace_search/groups/${groupId}/group_users` - ); - actions.setGroupUsers(response); - } catch (e) { - flashAPIErrors(e); - } - }, saveNewGroup: async () => { try { const response = await HttpLogic.values.http.post( @@ -354,8 +285,5 @@ export const GroupsLogic = kea>({ toggleFilterSourcesDropdown: () => { clearFlashMessages(); }, - toggleFilterUsersDropdown: () => { - clearFlashMessages(); - }, }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/components/source_config.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/components/source_config.test.tsx index 8399df946ea83..bc457ca0a1c00 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/components/source_config.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/components/source_config.test.tsx @@ -8,6 +8,7 @@ import '../../../../__mocks__/shallow_useeffect.mock'; import { setMockValues, setMockActions } from '../../../../__mocks__/kea_logic'; +import { mockUseParams } from '../../../../__mocks__/react_router'; import { sourceConfigData } from '../../../__mocks__/content_sources.mock'; import React from 'react'; @@ -18,8 +19,6 @@ import { EuiCallOut, EuiConfirmModal } from '@elastic/eui'; import { SaveConfig } from '../../content_sources/components/add_source/save_config'; -import { staticSourceData } from '../../content_sources/source_data'; - import { SourceConfig } from './source_config'; describe('SourceConfig', () => { @@ -30,10 +29,11 @@ describe('SourceConfig', () => { beforeEach(() => { setMockValues({ sourceConfigData, dataLoading: false }); setMockActions({ deleteSourceConfig, getSourceConfigData, saveSourceConfig }); + mockUseParams.mockReturnValue({ serviceType: 'share_point' }); }); it('renders', () => { - const wrapper = shallow(); + const wrapper = shallow(); const saveConfig = wrapper.find(SaveConfig); // Trigger modal visibility @@ -43,15 +43,23 @@ describe('SourceConfig', () => { expect(wrapper.find(EuiCallOut)).toHaveLength(0); }); + it('returns null if there is no matching source data for the service type', () => { + mockUseParams.mockReturnValue({ serviceType: 'doesnt_exist' }); + + const wrapper = shallow(); + + expect(wrapper.isEmptyRender()).toBe(true); + }); + it('renders a breadcrumb fallback while data is loading', () => { setMockValues({ dataLoading: true, sourceConfigData: {} }); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.prop('pageChrome')).toEqual(['Settings', 'Content source connectors', '...']); }); it('handles delete click', () => { - const wrapper = shallow(); + const wrapper = shallow(); const saveConfig = wrapper.find(SaveConfig); // Trigger modal visibility @@ -63,7 +71,7 @@ describe('SourceConfig', () => { }); it('saves source config', () => { - const wrapper = shallow(); + const wrapper = shallow(); const saveConfig = wrapper.find(SaveConfig); // Trigger modal visibility @@ -75,7 +83,7 @@ describe('SourceConfig', () => { }); it('cancels and closes modal', () => { - const wrapper = shallow(); + const wrapper = shallow(); const saveConfig = wrapper.find(SaveConfig); // Trigger modal visibility @@ -87,9 +95,8 @@ describe('SourceConfig', () => { }); it('shows feedback link for external sources', () => { - const wrapper = shallow( - - ); + mockUseParams.mockReturnValue({ serviceType: 'external' }); + const wrapper = shallow(); expect(wrapper.find(EuiCallOut)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/components/source_config.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/components/source_config.tsx index 6973732fa6727..76ed6023109d2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/components/source_config.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/components/source_config.tsx @@ -7,6 +7,8 @@ import React, { useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; + import { useActions, useValues } from 'kea'; import { @@ -21,29 +23,34 @@ import { i18n } from '@kbn/i18n'; import { WorkplaceSearchPageTemplate } from '../../../components/layout'; import { NAV, REMOVE_BUTTON, CANCEL_BUTTON } from '../../../constants'; -import { SourceDataItem } from '../../../types'; import { AddSourceHeader } from '../../content_sources/components/add_source/add_source_header'; import { AddSourceLogic } from '../../content_sources/components/add_source/add_source_logic'; import { SaveConfig } from '../../content_sources/components/add_source/save_config'; +import { getSourceData } from '../../content_sources/source_data'; import { SettingsLogic } from '../settings_logic'; -interface SourceConfigProps { - sourceData: SourceDataItem; -} - -export const SourceConfig: React.FC = ({ sourceData }) => { +export const SourceConfig: React.FC = () => { + const { serviceType } = useParams<{ serviceType: string }>(); const [confirmModalVisible, setConfirmModalVisibility] = useState(false); - const { configuration, serviceType } = sourceData; + const addSourceLogic = AddSourceLogic({ serviceType }); const { deleteSourceConfig } = useActions(SettingsLogic); - const { saveSourceConfig, getSourceConfigData } = useActions(AddSourceLogic); + const { saveSourceConfig, getSourceConfigData, resetSourceState } = useActions(addSourceLogic); const { sourceConfigData: { name, categories }, dataLoading, - } = useValues(AddSourceLogic); + } = useValues(addSourceLogic); + const sourceData = getSourceData(serviceType); useEffect(() => { - getSourceConfigData(serviceType); - }, []); + getSourceConfigData(); + return resetSourceState; + }, [serviceType]); + + if (!sourceData) { + return null; + } + + const { configuration } = sourceData; const hideConfirmModal = () => setConfirmModalVisibility(false); const showConfirmModal = () => setConfirmModalVisibility(true); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/settings_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/settings_router.test.tsx index 123167f0ad1d0..604c155215724 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/settings_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/settings_router.test.tsx @@ -10,12 +10,10 @@ import '../../../__mocks__/shallow_useeffect.mock'; import { setMockActions } from '../../../__mocks__/kea_logic'; import React from 'react'; -import { Route, Redirect, Switch } from 'react-router-dom'; +import { Redirect, Switch } from 'react-router-dom'; import { shallow } from 'enzyme'; -import { staticSourceData } from '../content_sources/source_data'; - import { Connectors } from './components/connectors'; import { Customize } from './components/customize'; import { OauthApplication } from './components/oauth_application'; @@ -24,9 +22,6 @@ import { SettingsRouter } from './settings_router'; describe('SettingsRouter', () => { const initializeSettings = jest.fn(); - const NUM_SOURCES = staticSourceData.length; - // Should be 4 routes other than the sources listed: Connectors, Customize, & OauthApplication, & a redirect - const NUM_ROUTES = NUM_SOURCES + 4; beforeEach(() => { setMockActions({ initializeSettings }); @@ -36,11 +31,10 @@ describe('SettingsRouter', () => { const wrapper = shallow(); expect(wrapper.find(Switch)).toHaveLength(1); - expect(wrapper.find(Route)).toHaveLength(NUM_ROUTES); expect(wrapper.find(Redirect)).toHaveLength(1); expect(wrapper.find(Connectors)).toHaveLength(1); expect(wrapper.find(Customize)).toHaveLength(1); expect(wrapper.find(OauthApplication)).toHaveLength(1); - expect(wrapper.find(SourceConfig)).toHaveLength(NUM_SOURCES); + expect(wrapper.find(SourceConfig)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/settings_router.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/settings_router.tsx index 7c5e501d6a2a1..fc250bbfbf4e4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/settings_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/settings/settings_router.tsx @@ -16,7 +16,6 @@ import { ORG_SETTINGS_OAUTH_APPLICATION_PATH, getEditPath, } from '../../routes'; -import { staticSourceData } from '../content_sources/source_data'; import { Connectors } from './components/connectors'; import { Customize } from './components/customize'; @@ -42,11 +41,9 @@ export const SettingsRouter: React.FC = () => { - {staticSourceData.map((sourceData, i) => ( - - - - ))} + + + diff --git a/x-pack/plugins/enterprise_search/server/lib/check_access.test.ts b/x-pack/plugins/enterprise_search/server/lib/check_access.test.ts index 28d9fe363ff0f..3da63a63828ae 100644 --- a/x-pack/plugins/enterprise_search/server/lib/check_access.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/check_access.test.ts @@ -196,6 +196,17 @@ describe('checkAccess', () => { hasWorkplaceSearchAccess: false, }); }); + + it('falls back to no access if response error', async () => { + (callEnterpriseSearchConfigAPI as jest.Mock).mockImplementationOnce(() => ({ + responseStatus: 500, + responseStatusText: 'failed', + })); + expect(await checkAccess(mockDependencies)).toEqual({ + hasAppSearchAccess: false, + hasWorkplaceSearchAccess: false, + }); + }); }); }); }); diff --git a/x-pack/plugins/enterprise_search/server/lib/check_access.ts b/x-pack/plugins/enterprise_search/server/lib/check_access.ts index a77415f2c2f12..444fa9d4fdb29 100644 --- a/x-pack/plugins/enterprise_search/server/lib/check_access.ts +++ b/x-pack/plugins/enterprise_search/server/lib/check_access.ts @@ -98,6 +98,6 @@ export const checkAccess = async ({ // When enterpriseSearch.host is defined in kibana.yml, // make a HTTP call which returns product access - const { access } = (await callEnterpriseSearchConfigAPI({ request, config, log })) || {}; - return access || DENY_ALL_PLUGINS; + const response = (await callEnterpriseSearchConfigAPI({ request, config, log })) || {}; + return 'access' in response ? response.access || DENY_ALL_PLUGINS : DENY_ALL_PLUGINS; }; diff --git a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.test.ts b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.test.ts index 5f8b261f82f16..ad55b41c02cee 100644 --- a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.test.ts @@ -207,8 +207,19 @@ describe('callEnterpriseSearchConfigAPI', () => { (fetch as unknown as jest.Mock).mockReturnValueOnce(Promise.resolve('Bad Data')); expect(await callEnterpriseSearchConfigAPI(mockDependencies)).toEqual({}); expect(mockDependencies.log.error).toHaveBeenCalledWith( - 'Could not perform access check to Enterprise Search: TypeError: response.json is not a function' + 'Could not perform access check to Enterprise Search: 500' + ); + + (fetch as unknown as jest.Mock).mockReturnValueOnce( + Promise.resolve( + new Response('{}', { + status: 500, + statusText: 'I failed', + }) + ) ); + const expected = { responseStatus: 500, responseStatusText: 'I failed' }; + expect(await callEnterpriseSearchConfigAPI(mockDependencies)).toEqual(expected); }); it('handles timeouts', async () => { diff --git a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.ts b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.ts index 7e27480426525..361a5613ab67e 100644 --- a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.ts +++ b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.ts @@ -26,6 +26,10 @@ interface Params { interface Return extends InitialAppData { publicUrl?: string; } +interface ResponseError { + responseStatus: number; + responseStatusText: string; +} /** * Calls an internal Enterprise Search API endpoint which returns @@ -38,7 +42,7 @@ export const callEnterpriseSearchConfigAPI = async ({ config, log, request, -}: Params): Promise => { +}: Params): Promise => { if (!config.host) return {}; const TIMEOUT_WARNING = `Enterprise Search access check took over ${config.accessCheckTimeoutWarning}ms. Please ensure your Enterprise Search server is responding normally and not adversely impacting Kibana load speeds.`; @@ -63,6 +67,14 @@ export const callEnterpriseSearchConfigAPI = async ({ }; const response = await fetch(enterpriseSearchUrl, options); + + if (!response.ok) { + return { + responseStatus: response.status, + responseStatusText: response.statusText, + }; + } + const data = await response.json(); warnMismatchedVersions(data?.version?.number, log); diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/config_data.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/config_data.ts index 95ab8e3c3a5a9..5be5bf8cc0373 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/config_data.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/config_data.ts @@ -17,7 +17,12 @@ export function registerConfigDataRoute({ router, config, log }: RouteDependenci async (context, request, response) => { const data = await callEnterpriseSearchConfigAPI({ request, config, log }); - if (!Object.keys(data).length) { + if ('responseStatus' in data) { + return response.customError({ + statusCode: data.responseStatus, + body: 'Error fetching data from Enterprise Search', + }); + } else if (!Object.keys(data).length) { return response.customError({ statusCode: 502, body: 'Error fetching data from Enterprise Search', diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/groups.test.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/groups.test.ts index 40ee46c7a9ffd..dc1308a4140d3 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/groups.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/groups.test.ts @@ -107,7 +107,6 @@ describe('groups routes', () => { search: { query: 'foo', content_source_ids: ['123', '234'], - user_ids: ['345', '456'], }, }, }; diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/groups.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/groups.ts index c5c161cf7b2f8..8dc153e7a2923 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/groups.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/groups.ts @@ -51,7 +51,6 @@ export function registerSearchGroupsRoute({ search: schema.object({ query: schema.string(), content_source_ids: schema.arrayOf(schema.string()), - user_ids: schema.arrayOf(schema.string()), }), }), }, diff --git a/x-pack/plugins/event_log/common/index.ts b/x-pack/plugins/event_log/common/index.ts index 5910dbe2c5ad7..562a1e4298251 100644 --- a/x-pack/plugins/event_log/common/index.ts +++ b/x-pack/plugins/event_log/common/index.ts @@ -6,3 +6,4 @@ */ export const BASE_EVENT_LOG_API_PATH = '/internal/event_log'; +export { millisToNanos, nanosToMillis } from './lib'; diff --git a/x-pack/plugins/event_log/common/lib/index.ts b/x-pack/plugins/event_log/common/lib/index.ts new file mode 100644 index 0000000000000..40d3fb26189e6 --- /dev/null +++ b/x-pack/plugins/event_log/common/lib/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 { millisToNanos } from './millis_to_nanos'; +export { nanosToMillis } from './nanos_to_millis'; diff --git a/x-pack/plugins/event_log/common/lib/millis_to_nanos.test.ts b/x-pack/plugins/event_log/common/lib/millis_to_nanos.test.ts new file mode 100644 index 0000000000000..4845d79305007 --- /dev/null +++ b/x-pack/plugins/event_log/common/lib/millis_to_nanos.test.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 { millisToNanos } from './millis_to_nanos'; + +describe('millisToNanos', () => { + test('should return "0" when passing 0 millis', () => { + expect(millisToNanos(0)).toEqual('0'); + }); + + test('should return "1000000" when passing in 1 millis', () => { + expect(millisToNanos(1)).toEqual('1000000'); + }); + + test('should return "9007199254740991000000" when passing in 9007199254740991 (Number.MAX_SAFE_INTEGER)', () => { + expect(millisToNanos(9007199254740991)).toEqual('9007199254740991000000'); + }); + + test('should round to "1000000" wheen passing in 0.75 millis', () => { + expect(millisToNanos(0.75)).toEqual('1000000'); + }); +}); diff --git a/x-pack/plugins/event_log/common/lib/millis_to_nanos.ts b/x-pack/plugins/event_log/common/lib/millis_to_nanos.ts new file mode 100644 index 0000000000000..acb9e07f6c5a6 --- /dev/null +++ b/x-pack/plugins/event_log/common/lib/millis_to_nanos.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 function millisToNanos(millis: number): string { + const roundedMillis = Math.round(millis); + if (roundedMillis === 0) { + return '0'; + } + return `${roundedMillis}000000`; +} diff --git a/x-pack/plugins/event_log/common/lib/nanos_to_millis.test.ts b/x-pack/plugins/event_log/common/lib/nanos_to_millis.test.ts new file mode 100644 index 0000000000000..3a04c57b9edbf --- /dev/null +++ b/x-pack/plugins/event_log/common/lib/nanos_to_millis.test.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { nanosToMillis } from './nanos_to_millis'; + +describe('nanosToMillis', () => { + test('should return 0 when passing in "0" nanos', () => { + expect(nanosToMillis('0')).toEqual(0); + }); + + test('should drop decimals when passing in "1" nanos', () => { + expect(nanosToMillis('1')).toEqual(0); + }); + + test('should drop decimals when passing in "1000001" nanos', () => { + expect(nanosToMillis('1000001')).toEqual(1); + }); + + test('should return 9007199254740991 (Number.MAX_SAFE_INTEGER) when passing in "9007199254740991000000" nanos', () => { + expect(nanosToMillis('9007199254740991000000')).toEqual(9007199254740991); + }); + + test('should work when numbers are passed in', () => { + expect(nanosToMillis(0)).toEqual(0); + expect(nanosToMillis(1)).toEqual(0); + expect(nanosToMillis(1000001)).toEqual(1); + }); +}); diff --git a/x-pack/plugins/event_log/common/lib/nanos_to_millis.ts b/x-pack/plugins/event_log/common/lib/nanos_to_millis.ts new file mode 100644 index 0000000000000..a0512fb528a91 --- /dev/null +++ b/x-pack/plugins/event_log/common/lib/nanos_to_millis.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. + */ + +const ONE_MILLION = BigInt(1000 * 1000); + +export function nanosToMillis(nanos: string | number): number { + return Number(BigInt(nanos) / ONE_MILLION); +} diff --git a/x-pack/plugins/event_log/generated/mappings.json b/x-pack/plugins/event_log/generated/mappings.json index 2ceeb99685dda..f2022d78f4aee 100644 --- a/x-pack/plugins/event_log/generated/mappings.json +++ b/x-pack/plugins/event_log/generated/mappings.json @@ -376,4 +376,4 @@ } } } -} +} \ No newline at end of file diff --git a/x-pack/plugins/event_log/generated/schemas.ts b/x-pack/plugins/event_log/generated/schemas.ts index 990db4dd4c4ff..fc8bc1c9022d7 100644 --- a/x-pack/plugins/event_log/generated/schemas.ts +++ b/x-pack/plugins/event_log/generated/schemas.ts @@ -52,7 +52,7 @@ export const EventSchema = schema.maybe( code: ecsString(), created: ecsDate(), dataset: ecsString(), - duration: ecsNumber(), + duration: ecsStringOrNumber(), end: ecsDate(), hash: ecsString(), id: ecsString(), @@ -66,8 +66,8 @@ export const EventSchema = schema.maybe( reference: ecsString(), risk_score: ecsNumber(), risk_score_norm: ecsNumber(), - sequence: ecsNumber(), - severity: ecsNumber(), + sequence: ecsStringOrNumber(), + severity: ecsStringOrNumber(), start: ecsDate(), timezone: ecsString(), type: ecsStringMulti(), @@ -106,7 +106,7 @@ export const EventSchema = schema.maybe( schema.object({ id: ecsString(), scheduled: ecsDate(), - schedule_delay: ecsNumber(), + schedule_delay: ecsStringOrNumber(), }) ), alerting: schema.maybe( @@ -126,20 +126,20 @@ export const EventSchema = schema.maybe( schema.object({ uuid: ecsString(), status: ecsString(), - status_order: ecsNumber(), + status_order: ecsStringOrNumber(), metrics: schema.maybe( schema.object({ - number_of_triggered_actions: ecsNumber(), - number_of_generated_actions: ecsNumber(), - number_of_new_alerts: ecsNumber(), - number_of_active_alerts: ecsNumber(), - number_of_recovered_alerts: ecsNumber(), - total_number_of_alerts: ecsNumber(), - number_of_searches: ecsNumber(), - total_indexing_duration_ms: ecsNumber(), - es_search_duration_ms: ecsNumber(), - total_search_duration_ms: ecsNumber(), - execution_gap_duration_s: ecsNumber(), + number_of_triggered_actions: ecsStringOrNumber(), + number_of_generated_actions: ecsStringOrNumber(), + number_of_new_alerts: ecsStringOrNumber(), + number_of_active_alerts: ecsStringOrNumber(), + number_of_recovered_alerts: ecsStringOrNumber(), + total_number_of_alerts: ecsStringOrNumber(), + number_of_searches: ecsStringOrNumber(), + total_indexing_duration_ms: ecsStringOrNumber(), + es_search_duration_ms: ecsStringOrNumber(), + total_search_duration_ms: ecsStringOrNumber(), + execution_gap_duration_s: ecsStringOrNumber(), }) ), }) @@ -179,6 +179,10 @@ function ecsNumber() { return schema.maybe(schema.number()); } +function ecsStringOrNumber() { + return schema.maybe(schema.oneOf([schema.string(), schema.number()])); +} + function ecsDate() { return schema.maybe(schema.string({ validate: validateDate })); } diff --git a/x-pack/plugins/event_log/scripts/create_schemas.js b/x-pack/plugins/event_log/scripts/create_schemas.js index c86722ccd76c6..1a775b44add8d 100755 --- a/x-pack/plugins/event_log/scripts/create_schemas.js +++ b/x-pack/plugins/event_log/scripts/create_schemas.js @@ -115,7 +115,8 @@ function writeEventLogConfigSchema(elSchema, ecsVersion) { } const StringTypes = new Set(['string', 'keyword', 'text', 'ip']); -const NumberTypes = new Set(['long', 'integer', 'float']); +const NumberTypes = new Set(['integer', 'float']); +const StringOrNumberTypes = new Set(['long']); function augmentMappings(mappings, multiValuedProperties) { for (const prop of multiValuedProperties) { @@ -145,6 +146,11 @@ function generateSchemaLines(lineWriter, prop, mappings) { return; } + if (StringOrNumberTypes.has(mappings.type)) { + lineWriter.addLine(`${propKey}: ecsStringOrNumber(),`); + return; + } + if (mappings.type === 'date') { lineWriter.addLine(`${propKey}: ecsDate(),`); return; @@ -310,6 +316,10 @@ function ecsNumber() { return schema.maybe(schema.number()); } +function ecsStringOrNumber() { + return schema.maybe(schema.oneOf([schema.string(), schema.number()])); +} + function ecsDate() { return schema.maybe(schema.string({ validate: validateDate })); } diff --git a/x-pack/plugins/event_log/server/event_log_client.test.ts b/x-pack/plugins/event_log/server/event_log_client.test.ts index a0d086ceaece4..89f6df538aca5 100644 --- a/x-pack/plugins/event_log/server/event_log_client.test.ts +++ b/x-pack/plugins/event_log/server/event_log_client.test.ts @@ -250,7 +250,7 @@ function fakeEvent(overrides = {}) { action: 'execute', start: '2020-03-30T14:55:47.054Z', end: '2020-03-30T14:55:47.055Z', - duration: 1000000, + duration: '1000000', }, kibana: { namespace: 'default', diff --git a/x-pack/plugins/event_log/server/event_logger.test.ts b/x-pack/plugins/event_log/server/event_logger.test.ts index 636e377ed6636..a9295ada0dd85 100644 --- a/x-pack/plugins/event_log/server/event_logger.test.ts +++ b/x-pack/plugins/event_log/server/event_logger.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { nanosToMillis } from '../common'; import { IEvent, IEventLogger, IEventLogService } from '.'; import { ECS_VERSION } from './types'; import { EventLogService } from './event_log_service'; @@ -137,9 +138,18 @@ describe('EventLogger', () => { expect(timeStopValue).toBeGreaterThanOrEqual(timeStartValue); - const duration = event.event!.duration!; + const duration = Number(event.event!.duration!); expect(duration).toBeGreaterThan(0.95 * delayMS * 1000 * 1000); - expect(duration / (1000 * 1000)).toBeCloseTo(timeStopValue - timeStartValue); + expect(nanosToMillis(duration)).toBeCloseTo(timeStopValue - timeStartValue); + }); + + test('can set specific start time in startTiming', () => { + const event: IEvent = {}; + eventLogger.startTiming(event, new Date('2020-01-01T02:00:00.000Z')); + + const timeStart = event.event!.start!; + expect(timeStart).toBeTruthy(); + expect(timeStart).toEqual('2020-01-01T02:00:00.000Z'); }); test('timing method endTiming() method works when startTiming() is not called', async () => { diff --git a/x-pack/plugins/event_log/server/event_logger.ts b/x-pack/plugins/event_log/server/event_logger.ts index 89d0cbc9ab94d..14cde6c191fa3 100644 --- a/x-pack/plugins/event_log/server/event_logger.ts +++ b/x-pack/plugins/event_log/server/event_logger.ts @@ -13,6 +13,7 @@ import { coerce } from 'semver'; import { Plugin } from './plugin'; import { EsContext } from './es'; import { EventLogService } from './event_log_service'; +import { millisToNanos } from '../common'; import { IEvent, IValidatedEvent, @@ -46,11 +47,12 @@ export class EventLogger implements IEventLogger { this.systemLogger = ctorParams.systemLogger; } - startTiming(event: IEvent): void { + startTiming(event: IEvent, startTime?: Date): void { if (event == null) return; event.event = event.event || {}; - event.event.start = new Date().toISOString(); + const start = startTime ?? new Date(); + event.event.start = start.toISOString(); } stopTiming(event: IEvent): void { @@ -61,7 +63,7 @@ export class EventLogger implements IEventLogger { const end = Date.now(); event.event.end = new Date(end).toISOString(); - event.event.duration = (end - start) * 1000 * 1000; // nanoseconds + event.event.duration = millisToNanos(end - start); } // non-blocking, but spawns an async task to do the work diff --git a/x-pack/plugins/event_log/server/index.ts b/x-pack/plugins/event_log/server/index.ts index c69d7780a6204..cd386483fef8f 100644 --- a/x-pack/plugins/event_log/server/index.ts +++ b/x-pack/plugins/event_log/server/index.ts @@ -9,6 +9,8 @@ import { PluginInitializerContext, PluginConfigDescriptor } from '@kbn/core/serv import { ConfigSchema, IEventLogConfig } from './types'; import { Plugin } from './plugin'; +export { millisToNanos, nanosToMillis } from '../common'; + export type { IEventLogService, IEventLogger, diff --git a/x-pack/plugins/event_log/server/routes/_mock_handler_arguments.ts b/x-pack/plugins/event_log/server/routes/_mock_handler_arguments.ts index 78c0917929c7a..7da8defa9d856 100644 --- a/x-pack/plugins/event_log/server/routes/_mock_handler_arguments.ts +++ b/x-pack/plugins/event_log/server/routes/_mock_handler_arguments.ts @@ -50,7 +50,7 @@ export function fakeEvent(overrides = {}) { action: 'execute', start: '2020-03-30T14:55:47.054Z', end: '2020-03-30T14:55:47.055Z', - duration: 1000000, + duration: '1000000', }, kibana: { saved_objects: [ diff --git a/x-pack/plugins/event_log/server/types.ts b/x-pack/plugins/event_log/server/types.ts index 1336245741bd6..3291f162c09df 100644 --- a/x-pack/plugins/event_log/server/types.ts +++ b/x-pack/plugins/event_log/server/types.ts @@ -66,7 +66,7 @@ export interface IEventLogClient { export interface IEventLogger { logEvent(properties: IEvent): void; - startTiming(event: IEvent): void; + startTiming(event: IEvent, startTime?: Date): void; stopTiming(event: IEvent): void; } diff --git a/x-pack/plugins/event_log/tsconfig.json b/x-pack/plugins/event_log/tsconfig.json index e0e72fdbf6581..28dd8f244a3da 100644 --- a/x-pack/plugins/event_log/tsconfig.json +++ b/x-pack/plugins/event_log/tsconfig.json @@ -12,7 +12,7 @@ "generated/*", // have to declare *.json explicitly due to https://github.com/microsoft/TypeScript/issues/25636 "generated/*.json", - "common/*" + "common/**/*" ], "references": [ { "path": "../../../src/core/tsconfig.json" }, diff --git a/x-pack/plugins/fleet/common/constants/data_streams.ts b/x-pack/plugins/fleet/common/constants/data_streams.ts new file mode 100644 index 0000000000000..bb880af9b3df8 --- /dev/null +++ b/x-pack/plugins/fleet/common/constants/data_streams.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 { schema } from '@kbn/config-schema'; + +export const GetDataStreamsListRequestSchema = { + params: schema.object({ + use_terms_enum: schema.boolean({ defaultValue: false }), + }), +}; diff --git a/x-pack/plugins/fleet/common/constants/epm.ts b/x-pack/plugins/fleet/common/constants/epm.ts index 888850b73b15e..07f6fa048dc42 100644 --- a/x-pack/plugins/fleet/common/constants/epm.ts +++ b/x-pack/plugins/fleet/common/constants/epm.ts @@ -16,6 +16,7 @@ export const FLEET_ENDPOINT_PACKAGE = 'endpoint'; export const FLEET_APM_PACKAGE = 'apm'; export const FLEET_SYNTHETICS_PACKAGE = 'synthetics'; export const FLEET_KUBERNETES_PACKAGE = 'kubernetes'; +export const FLEET_CLOUD_SECURITY_POSTURE_PACKAGE = 'cloud_security_posture'; export const FLEET_ELASTIC_AGENT_DETAILS_DASHBOARD_ID = 'elastic_agent-f47f18cc-9c7d-4278-b2ea-a6dee816d395'; @@ -58,5 +59,4 @@ export const installationStatuses = { Installing: 'installing', InstallFailed: 'install_failed', NotInstalled: 'not_installed', - InstalledBundled: 'installed_bundled', } as const; diff --git a/x-pack/plugins/fleet/common/experimental_features.ts b/x-pack/plugins/fleet/common/experimental_features.ts new file mode 100644 index 0000000000000..3398de60c0786 --- /dev/null +++ b/x-pack/plugins/fleet/common/experimental_features.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. + */ + +export type ExperimentalFeatures = typeof allowedExperimentalValues; + +/** + * A list of allowed values that can be used in `xpack.fleet.enableExperimental`. + * This object is then used to validate and parse the value entered. + */ +export const allowedExperimentalValues = Object.freeze({ + addIntegrationStepsPage: false, +}); + +type ExperimentalConfigKeys = Array; +type Mutable = { -readonly [P in keyof T]: T[P] }; + +const FleetInvalidExperimentalValue = class extends Error {}; +const allowedKeys = Object.keys(allowedExperimentalValues) as Readonly; + +/** + * Parses the string value used in `xpack.fleet.enableExperimental` kibana configuration, + * which should be a string of values delimited by a comma (`,`) + * + * @param configValue + * @throws FleetInvalidExperimentalValue + */ +export const parseExperimentalConfigValue = (configValue: string[]): ExperimentalFeatures => { + const enabledFeatures: Mutable> = {}; + + for (const value of configValue) { + if (!isValidExperimentalValue(value)) { + throw new FleetInvalidExperimentalValue(`[${value}] is not a supported experimental feature`); + } + + enabledFeatures[value as keyof ExperimentalFeatures] = true; + } + + return { + ...allowedExperimentalValues, + ...enabledFeatures, + }; +}; + +export const isValidExperimentalValue = (value: string) => { + return allowedKeys.includes(value as keyof ExperimentalFeatures); +}; + +export const getExperimentalAllowedValues = (): string[] => [...allowedKeys]; diff --git a/x-pack/plugins/fleet/common/index.ts b/x-pack/plugins/fleet/common/index.ts index 46a8e2d01fc96..60bbcafc48842 100644 --- a/x-pack/plugins/fleet/common/index.ts +++ b/x-pack/plugins/fleet/common/index.ts @@ -11,6 +11,7 @@ export * from './constants'; export * from './services'; export * from './types'; +export * from './experimental_features'; export type { FleetAuthz } from './authz'; export { calculateAuthz } from './authz'; export { createFleetAuthzMock } from './mocks'; diff --git a/x-pack/plugins/fleet/common/types/index.ts b/x-pack/plugins/fleet/common/types/index.ts index d04855480dd62..ce1cb5a294f80 100644 --- a/x-pack/plugins/fleet/common/types/index.ts +++ b/x-pack/plugins/fleet/common/types/index.ts @@ -32,6 +32,7 @@ export interface FleetConfigType { packages?: PreconfiguredPackage[]; outputs?: PreconfiguredOutput[]; agentIdVerificationEnabled?: boolean; + enableExperimental?: string[]; developer?: { disableRegistryVersionCheck?: boolean; allowAgentUpgradeSourceUri?: boolean; diff --git a/x-pack/plugins/fleet/common/types/models/agent.ts b/x-pack/plugins/fleet/common/types/models/agent.ts index 018f591fef79c..d41a08b8b4755 100644 --- a/x-pack/plugins/fleet/common/types/models/agent.ts +++ b/x-pack/plugins/fleet/common/types/models/agent.ts @@ -11,8 +11,6 @@ import type { AGENT_TYPE_TEMPORARY, } from '../../constants'; -import type { FullAgentPolicy } from './agent_policy'; - export type AgentType = | typeof AGENT_TYPE_EPHEMERAL | typeof AGENT_TYPE_PERMANENT @@ -41,7 +39,11 @@ export type AgentActionType = export interface NewAgentAction { type: AgentActionType; data?: any; + ack_data?: any; sent_at?: string; + agents: string[]; + created_at?: string; + id?: string; } export interface AgentAction extends NewAgentAction { @@ -49,41 +51,10 @@ export interface AgentAction extends NewAgentAction { data?: any; sent_at?: string; id: string; - agent_id: string; - created_at: string; - ack_data?: any; -} - -export interface AgentPolicyAction extends NewAgentAction { - id: string; - type: AgentActionType; - data: { - policy: FullAgentPolicy; - }; - policy_id: string; - policy_revision: number; created_at: string; ack_data?: any; } -interface CommonAgentActionSOAttributes { - type: AgentActionType; - sent_at?: string; - timestamp?: string; - created_at: string; - data?: string; - ack_data?: string; -} - -export type AgentActionSOAttributes = CommonAgentActionSOAttributes & { - agent_id: string; -}; -export type AgentPolicyActionSOAttributes = CommonAgentActionSOAttributes & { - policy_id: string; - policy_revision: number; -}; -export type BaseAgentActionSOAttributes = AgentActionSOAttributes | AgentPolicyActionSOAttributes; - export interface AgentMetadata { [x: string]: any; } @@ -104,6 +75,7 @@ interface AgentBase { last_checkin_status?: 'error' | 'online' | 'degraded' | 'updating'; user_provided_metadata: AgentMetadata; local_metadata: AgentMetadata; + tags?: string[]; } export interface Agent extends AgentBase { @@ -216,6 +188,10 @@ export interface FleetServerAgent { * The last acknowledged action sequence number for the Elastic Agent */ action_seq_no?: number; + /** + * A list of tags used for organizing/filtering agents + */ + tags?: string[]; } /** * An Elastic Agent metadata @@ -268,6 +244,17 @@ export interface FleetServerAgentAction { * The Agent IDs the action is intended for. No support for json.RawMessage with the current generator. Could be useful to lazy parse the agent ids */ agents?: string[]; + + /** + * Date when the agent should execute that agent. This field could be altered by Fleet server for progressive rollout of the action. + */ + start_time?: string; + + /** + * Minimun execution duration in seconds, used for progressive rollout of the action. + */ + minimum_execution_duration?: number; + /** * The opaque payload. */ diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index 5217a6232a18c..2359b979d0a17 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -45,11 +45,7 @@ export interface DefaultPackagesInstallationError { export type InstallType = 'reinstall' | 'reupdate' | 'rollback' | 'update' | 'install' | 'unknown'; export type InstallSource = 'registry' | 'upload' | 'bundled'; -export type EpmPackageInstallStatus = - | 'installed' - | 'installing' - | 'install_failed' - | 'installed_bundled'; +export type EpmPackageInstallStatus = 'installed' | 'installing' | 'install_failed'; export type DetailViewPanelName = 'overview' | 'policies' | 'assets' | 'settings' | 'custom'; export type ServiceName = 'kibana' | 'elasticsearch'; @@ -431,8 +427,7 @@ export type Installable = | InstalledRegistry | Installing | NotInstalled - | InstallFailed - | InstalledBundled; + | InstallFailed; export type InstallStatusExcluded = T & { status: undefined; @@ -443,10 +438,6 @@ export type InstalledRegistry = T & { savedObject: SavedObject; }; -export type InstalledBundled = T & { - status: InstallationStatus['InstalledBundled']; -}; - export type Installing = T & { status: InstallationStatus['Installing']; savedObject: SavedObject; diff --git a/x-pack/plugins/fleet/common/types/rest_spec/agent.ts b/x-pack/plugins/fleet/common/types/rest_spec/agent.ts index 40570bc599053..aa256db95634a 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/agent.ts @@ -34,7 +34,7 @@ export interface GetOneAgentResponse { export interface PostNewAgentActionRequest { body: { - action: NewAgentAction; + action: Omit; }; params: { agentId: string; diff --git a/x-pack/plugins/fleet/cypress/README.md b/x-pack/plugins/fleet/cypress/README.md index e9bb299ca905e..94de6b38c47ec 100644 --- a/x-pack/plugins/fleet/cypress/README.md +++ b/x-pack/plugins/fleet/cypress/README.md @@ -113,13 +113,13 @@ We use es_archiver to manage the data that our Cypress tests need. 3. When you are sure that you have all the data you need run the following command from: `x-pack/plugins/fleet` ```sh -node ../../../scripts/es_archiver save --dir ../../test/fleet_cypress/es_archives --config ../../../test/functional/config.js --es-url http://:@: +node ../../../scripts/es_archiver save --dir ../../test/fleet_cypress/es_archives --config ../../../test/functional/config.base.js --es-url http://:@: ``` Example: ```sh -node ../../../scripts/es_archiver save custom_rules ".kibana",".siem-signal*" --dir ../../test/fleet_cypress/es_archives --config ../../../test/functional/config.js --es-url http://elastic:changeme@localhost:9220 +node ../../../scripts/es_archiver save custom_rules ".kibana",".siem-signal*" --dir ../../test/fleet_cypress/es_archives --config ../../../test/functional/config.base.js --es-url http://elastic:changeme@localhost:9220 ``` Note that the command will create the folder if it does not exist. diff --git a/x-pack/plugins/fleet/public/applications/fleet/app.tsx b/x-pack/plugins/fleet/public/applications/fleet/app.tsx index b871f6d4e690b..eb8b01d831cd5 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/app.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/app.tsx @@ -8,7 +8,7 @@ import type { FunctionComponent } from 'react'; import React, { memo, useEffect, useState } from 'react'; import type { AppMountParameters } from '@kbn/core/public'; -import { EuiCode, EuiEmptyPrompt, EuiErrorBoundary, EuiPanel } from '@elastic/eui'; +import { EuiCode, EuiEmptyPrompt, EuiErrorBoundary, EuiPanel, EuiPortal } from '@elastic/eui'; import type { History } from 'history'; import { Router, Redirect, Route, Switch, useRouteMatch } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -27,7 +27,7 @@ import type { FleetConfigType, FleetStartServices } from '../../plugin'; import { PackageInstallProvider } from '../integrations/hooks'; -import { useAuthz } from './hooks'; +import { useAuthz, useFlyoutContext } from './hooks'; import { ConfigContext, @@ -38,8 +38,15 @@ import { useBreadcrumbs, useStartServices, UIExtensionsContext, + FlyoutContextProvider, } from './hooks'; -import { Error, Loading, FleetSetupLoading } from './components'; +import { + Error, + Loading, + FleetSetupLoading, + AgentEnrollmentFlyout, + FleetServerFlyout, +} from './components'; import type { UIExtensionsStorage } from './types'; import { FLEET_ROUTING_PATHS } from './constants'; @@ -251,7 +258,7 @@ export const FleetAppContext: React.FC<{ notifications={startServices.notifications} theme$={theme$} > - {children} + {children} @@ -295,6 +302,8 @@ const FleetTopNav = memo( export const AppRoutes = memo( ({ setHeaderActionMenu }: { setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'] }) => { + const flyoutContext = useFlyoutContext(); + return ( <> @@ -343,6 +352,22 @@ export const AppRoutes = memo( }} /> + + {flyoutContext.isEnrollmentFlyoutOpen && ( + + flyoutContext.closeEnrollmentFlyout()} + /> + + )} + + {flyoutContext.isFleetServerFlyoutOpen && ( + + flyoutContext.closeFleetServerFlyout()} /> + + )} ); } diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx index 87b4a1bda7ff7..5c5f87b19f977 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx @@ -57,6 +57,7 @@ export const AdvancedTab: React.FunctionComponent = () => { serviceToken, fleetServerHost: fleetServerHostForm.fleetServerHost, fleetServerPolicyId, + deploymentMode, disabled: !Boolean(serviceToken), }), getConfirmFleetServerConnectionStep({ isFleetServerReady, disabled: !Boolean(serviceToken) }), diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx index cf8abc2fe9e16..758a34113efcd 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx @@ -29,6 +29,7 @@ export const QuickStartTab: React.FunctionComponent = () => { fleetServerHost: quickStartCreateForm.fleetServerHost, fleetServerPolicyId: quickStartCreateForm.fleetServerPolicyId, serviceToken: quickStartCreateForm.serviceToken, + deploymentMode: 'quickstart', disabled: quickStartCreateForm.status !== 'success', }), getConfirmFleetServerConnectionStep({ diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx index e64e23f039f89..70753e37f8e8a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx @@ -104,8 +104,13 @@ export const AddFleetServerHostStepContent = ({ /> - - + + = ({ isFleetServerReady }) => { - const addAgentFlyout = useContext(agentFlyoutContext); + const flyoutContext = useFlyoutContext(); return isFleetServerReady ? ( <> @@ -53,7 +53,7 @@ const ConfirmFleetServerConnectionStepContent: React.FunctionComponent<{ - + ), }; @@ -51,7 +56,8 @@ const InstallFleetServerStepContent: React.FunctionComponent<{ serviceToken?: string; fleetServerHost?: string; fleetServerPolicyId?: string; -}> = ({ serviceToken, fleetServerHost, fleetServerPolicyId }) => { + deploymentMode: DeploymentMode; +}> = ({ serviceToken, fleetServerHost, fleetServerPolicyId, deploymentMode }) => { const kibanaVersion = useKibanaVersion(); const { output } = useDefaultOutput(); @@ -63,7 +69,7 @@ const InstallFleetServerStepContent: React.FunctionComponent<{ serviceToken ?? '', fleetServerPolicyId, fleetServerHost, - false, + deploymentMode === 'production', output?.ca_trusted_fingerprint, kibanaVersion ); diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.test.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.test.ts index 89a246c5c6265..12c1af65f9555 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.test.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.test.ts @@ -17,9 +17,9 @@ describe('getInstallCommandForPlatform', () => { ); expect(res).toMatchInlineSnapshot(` - "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--linux-x86_64.zip \\\\ - tar xzvf elastic-agent--linux-x86_64.zip \\\\ - cd elastic-agent--linux-x86_64 \\\\ + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--linux-x86_64.zip + tar xzvf elastic-agent--linux-x86_64.zip + cd elastic-agent--linux-x86_64 sudo ./elastic-agent install \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1" @@ -34,9 +34,9 @@ describe('getInstallCommandForPlatform', () => { ); expect(res).toMatchInlineSnapshot(` - "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--darwin-x86_64.tar.gz \\\\ - tar xzvf elastic-agent--darwin-x86_64.tar.gz \\\\ - cd elastic-agent--darwin-x86_64 \\\\ + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--darwin-x86_64.tar.gz + tar xzvf elastic-agent--darwin-x86_64.tar.gz + cd elastic-agent--darwin-x86_64 sudo ./elastic-agent install \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1" @@ -51,9 +51,9 @@ describe('getInstallCommandForPlatform', () => { ); expect(res).toMatchInlineSnapshot(` - "wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--windows-x86_64.tar.gz -OutFile elastic-agent--windows-x86_64.tar.gz \` - Expand-Archive .\\\\elastic-agent--windows-x86_64.tar.gz \` - cd elastic-agent--windows-x86_64\` + "wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--windows-x86_64.tar.gz -OutFile elastic-agent--windows-x86_64.tar.gz + Expand-Archive .\\\\elastic-agent--windows-x86_64.tar.gz + cd elastic-agent--windows-x86_64 .\\\\elastic-agent.exe install \` --fleet-server-es=http://elasticsearch:9200 \` --fleet-server-service-token=service-token-1" @@ -68,9 +68,9 @@ describe('getInstallCommandForPlatform', () => { ); expect(res).toMatchInlineSnapshot(` - "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--x86_64.rpm \\\\ - tar xzvf elastic-agent--x86_64.rpm \\\\ - cd elastic-agent--x86_64 \\\\ + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--x86_64.rpm + tar xzvf elastic-agent--x86_64.rpm + cd elastic-agent--x86_64 sudo elastic-agent enroll \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1" @@ -85,9 +85,9 @@ describe('getInstallCommandForPlatform', () => { ); expect(res).toMatchInlineSnapshot(` - "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--amd64.deb \\\\ - tar xzvf elastic-agent--amd64.deb \\\\ - cd elastic-agent--amd64 \\\\ + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--amd64.deb + tar xzvf elastic-agent--amd64.deb + cd elastic-agent--amd64 sudo elastic-agent enroll \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1" @@ -106,9 +106,9 @@ describe('getInstallCommandForPlatform', () => { ); expect(res).toMatchInlineSnapshot(` - "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--linux-x86_64.zip \\\\ - tar xzvf elastic-agent--linux-x86_64.zip \\\\ - cd elastic-agent--linux-x86_64 \\\\ + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--linux-x86_64.zip + tar xzvf elastic-agent--linux-x86_64.zip + cd elastic-agent--linux-x86_64 sudo ./elastic-agent install \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ @@ -127,9 +127,9 @@ describe('getInstallCommandForPlatform', () => { ); expect(res).toMatchInlineSnapshot(` - "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--linux-x86_64.zip \\\\ - tar xzvf elastic-agent--linux-x86_64.zip \\\\ - cd elastic-agent--linux-x86_64 \\\\ + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--linux-x86_64.zip + tar xzvf elastic-agent--linux-x86_64.zip + cd elastic-agent--linux-x86_64 sudo ./elastic-agent install \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ @@ -146,9 +146,9 @@ describe('getInstallCommandForPlatform', () => { ); expect(res).toMatchInlineSnapshot(` - "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--darwin-x86_64.tar.gz \\\\ - tar xzvf elastic-agent--darwin-x86_64.tar.gz \\\\ - cd elastic-agent--darwin-x86_64 \\\\ + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--darwin-x86_64.tar.gz + tar xzvf elastic-agent--darwin-x86_64.tar.gz + cd elastic-agent--darwin-x86_64 sudo ./elastic-agent install \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ @@ -165,9 +165,9 @@ describe('getInstallCommandForPlatform', () => { ); expect(res).toMatchInlineSnapshot(` - "wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--windows-x86_64.tar.gz -OutFile elastic-agent--windows-x86_64.tar.gz \` - Expand-Archive .\\\\elastic-agent--windows-x86_64.tar.gz \` - cd elastic-agent--windows-x86_64\` + "wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--windows-x86_64.tar.gz -OutFile elastic-agent--windows-x86_64.tar.gz + Expand-Archive .\\\\elastic-agent--windows-x86_64.tar.gz + cd elastic-agent--windows-x86_64 .\\\\elastic-agent.exe install \` --fleet-server-es=http://elasticsearch:9200 \` --fleet-server-service-token=service-token-1 \` @@ -184,9 +184,9 @@ describe('getInstallCommandForPlatform', () => { ); expect(res).toMatchInlineSnapshot(` - "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--x86_64.rpm \\\\ - tar xzvf elastic-agent--x86_64.rpm \\\\ - cd elastic-agent--x86_64 \\\\ + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--x86_64.rpm + tar xzvf elastic-agent--x86_64.rpm + cd elastic-agent--x86_64 sudo elastic-agent enroll \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ @@ -203,9 +203,9 @@ describe('getInstallCommandForPlatform', () => { ); expect(res).toMatchInlineSnapshot(` - "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--amd64.deb \\\\ - tar xzvf elastic-agent--amd64.deb \\\\ - cd elastic-agent--amd64 \\\\ + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--amd64.deb + tar xzvf elastic-agent--amd64.deb + cd elastic-agent--amd64 sudo elastic-agent enroll \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ @@ -226,9 +226,9 @@ describe('getInstallCommandForPlatform', () => { ); expect(res).toMatchInlineSnapshot(` - "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--linux-x86_64.zip \\\\ - tar xzvf elastic-agent--linux-x86_64.zip \\\\ - cd elastic-agent--linux-x86_64 \\\\ + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--linux-x86_64.zip + tar xzvf elastic-agent--linux-x86_64.zip + cd elastic-agent--linux-x86_64 sudo ./elastic-agent install--url=http://fleetserver:8220 \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ @@ -251,9 +251,9 @@ describe('getInstallCommandForPlatform', () => { ); expect(res).toMatchInlineSnapshot(` - "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--darwin-x86_64.tar.gz \\\\ - tar xzvf elastic-agent--darwin-x86_64.tar.gz \\\\ - cd elastic-agent--darwin-x86_64 \\\\ + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--darwin-x86_64.tar.gz + tar xzvf elastic-agent--darwin-x86_64.tar.gz + cd elastic-agent--darwin-x86_64 sudo ./elastic-agent install --url=http://fleetserver:8220 \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ @@ -276,9 +276,9 @@ describe('getInstallCommandForPlatform', () => { ); expect(res).toMatchInlineSnapshot(` - "wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--windows-x86_64.tar.gz -OutFile elastic-agent--windows-x86_64.tar.gz \` - Expand-Archive .\\\\elastic-agent--windows-x86_64.tar.gz \` - cd elastic-agent--windows-x86_64\` + "wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--windows-x86_64.tar.gz -OutFile elastic-agent--windows-x86_64.tar.gz + Expand-Archive .\\\\elastic-agent--windows-x86_64.tar.gz + cd elastic-agent--windows-x86_64 .\\\\elastic-agent.exe install --url=http://fleetserver:8220 \` --fleet-server-es=http://elasticsearch:9200 \` --fleet-server-service-token=service-token-1 \` @@ -301,9 +301,9 @@ describe('getInstallCommandForPlatform', () => { ); expect(res).toMatchInlineSnapshot(` - "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--x86_64.rpm \\\\ - tar xzvf elastic-agent--x86_64.rpm \\\\ - cd elastic-agent--x86_64 \\\\ + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--x86_64.rpm + tar xzvf elastic-agent--x86_64.rpm + cd elastic-agent--x86_64 sudo elastic-agent enroll --url=http://fleetserver:8220 \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ @@ -326,9 +326,9 @@ describe('getInstallCommandForPlatform', () => { ); expect(res).toMatchInlineSnapshot(` - "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--amd64.deb \\\\ - tar xzvf elastic-agent--amd64.deb \\\\ - cd elastic-agent--amd64 \\\\ + "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--amd64.deb + tar xzvf elastic-agent--amd64.deb + cd elastic-agent--amd64 sudo elastic-agent enroll --url=http://fleetserver:8220 \\\\ --fleet-server-es=http://elasticsearch:9200 \\\\ --fleet-server-service-token=service-token-1 \\\\ diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.ts index 525af7cf95103..ed38478c3a3ee 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.ts @@ -67,12 +67,12 @@ export function getInstallCommandForPlatform( `wget ${artifact.fullUrl} -OutFile ${artifact.filename}`, `Expand-Archive .\\${artifact.filename}`, `cd ${artifact.unpackedDir}`, - ].join(` ${newLineSeparator}`) + ].join(`\n`) : [ `curl -L -O ${artifact.fullUrl}`, `tar xzvf ${artifact.filename}`, `cd ${artifact.unpackedDir}`, - ].join(` ${newLineSeparator}`); + ].join(`\n`); const commandArguments = []; @@ -108,11 +108,11 @@ export function getInstallCommandForPlatform( }, ''); const commands = { - linux: `${downloadCommand} ${newLineSeparator}sudo ./elastic-agent install${commandArgumentsStr}`, - mac: `${downloadCommand} ${newLineSeparator}sudo ./elastic-agent install ${commandArgumentsStr}`, - windows: `${downloadCommand}${newLineSeparator}.\\elastic-agent.exe install ${commandArgumentsStr}`, - deb: `${downloadCommand} ${newLineSeparator}sudo elastic-agent enroll ${commandArgumentsStr}`, - rpm: `${downloadCommand} ${newLineSeparator}sudo elastic-agent enroll ${commandArgumentsStr}`, + linux: `${downloadCommand}\nsudo ./elastic-agent install${commandArgumentsStr}`, + mac: `${downloadCommand}\nsudo ./elastic-agent install ${commandArgumentsStr}`, + windows: `${downloadCommand}\n.\\elastic-agent.exe install ${commandArgumentsStr}`, + deb: `${downloadCommand}\nsudo elastic-agent enroll ${commandArgumentsStr}`, + rpm: `${downloadCommand}\nsudo elastic-agent enroll ${commandArgumentsStr}`, }; return commands[platform]; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx index 14fc3eb1d24c3..cc7a9bec975ef 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx @@ -84,25 +84,27 @@ const AgentPolicyLogsNotEnabledCallout: React.FunctionComponent<{ agentPolicy: A /> } > - - - - ), - }} - /> + {agentPolicy.is_managed ? null : ( + + + + ), + }} + /> + )} ); @@ -278,9 +280,9 @@ export const AgentLogsUI: React.FunctionComponent = memo( return ( - {agentPolicy && - !agentPolicy.monitoring_enabled?.includes('logs') && - !agentPolicy.is_managed && } + {agentPolicy && !agentPolicy.monitoring_enabled?.includes('logs') && ( + + )} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agents_selection_status.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agents_selection_status.tsx new file mode 100644 index 0000000000000..57728f275ccb5 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agents_selection_status.tsx @@ -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 React from 'react'; +import styled from 'styled-components'; +import { EuiFlexGroup, EuiFlexItem, EuiText, EuiButtonEmpty } from '@elastic/eui'; +import { FormattedMessage, FormattedNumber } from '@kbn/i18n-react'; + +import { SO_SEARCH_LIMIT } from '../../../../constants'; +import type { Agent } from '../../../../types'; + +import type { SelectionMode } from './types'; + +const Divider = styled.div` + width: 0; + height: ${(props) => props.theme.eui.euiSizeL}; + border-left: ${(props) => props.theme.eui.euiBorderThin}; +`; + +const FlexItem = styled(EuiFlexItem)` + height: ${(props) => props.theme.eui.euiSizeL}; +`; + +const Button = styled(EuiButtonEmpty)` + .euiButtonEmpty__text { + font-size: ${(props) => props.theme.eui.euiFontSizeXS}; + } +`; + +export const AgentsSelectionStatus: React.FunctionComponent<{ + totalAgents: number; + selectableAgents: number; + selectionMode: SelectionMode; + setSelectionMode: (mode: SelectionMode) => void; + selectedAgents: Agent[]; + setSelectedAgents: (agents: Agent[]) => void; +}> = ({ + totalAgents, + selectableAgents, + selectionMode, + setSelectionMode, + selectedAgents, + setSelectedAgents, +}) => { + const showSelectEverything = + selectionMode === 'manual' && + selectedAgents.length === selectableAgents && + selectableAgents < totalAgents; + + return ( + <> + + + + {totalAgents > SO_SEARCH_LIMIT ? ( + , + total: , + }} + /> + ) : ( + + )} + + + {(selectionMode === 'manual' && selectedAgents.length) || + (selectionMode === 'query' && totalAgents > 0) ? ( + <> + + + + + + + + + {showSelectEverything ? ( + <> + + + + + + + + ) : null} + + + + + + + + ) : ( + + )} + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.test.tsx new file mode 100644 index 0000000000000..9ddf8608fc015 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.test.tsx @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { ThemeProvider } from 'styled-components'; + +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; + +import { coreMock } from '@kbn/core/public/mocks'; +import { registerTestBed } from '@kbn/test-jest-helpers'; + +import type { Agent } from '../../../../types'; + +import { FleetStatusProvider, ConfigContext, KibanaVersionContext } from '../../../../../../hooks'; + +import { getMockTheme } from '../../../../../../mocks'; + +import { AgentBulkActions } from './bulk_actions'; +import type { Props } from './bulk_actions'; + +const mockTheme = getMockTheme({ + eui: { + euiSize: '10px', + }, +}); + +const TestComponent = (props: Props) => ( + + + + + + + + + + + +); + +describe('AgentBulkActions', () => { + it('should show no Actions button when no agent is selected', async () => { + const selectedAgents: Agent[] = []; + const props: Props = { + totalAgents: 10, + totalInactiveAgents: 2, + selectionMode: 'manual', + currentQuery: '', + selectedAgents, + refreshAgents: () => undefined, + }; + const testBed = registerTestBed(TestComponent)(props); + const { exists } = testBed; + + expect(exists('agentBulkActionsButton')).toBe(false); + }); + + it('should show an Actions button when at least an agent is selected', async () => { + const selectedAgents: Agent[] = [ + { + id: 'Agent1', + status: 'online', + packages: ['system'], + type: 'PERMANENT', + active: true, + enrolled_at: `${Date.now()}`, + user_provided_metadata: {}, + local_metadata: {}, + }, + ]; + const props: Props = { + totalAgents: 10, + totalInactiveAgents: 2, + selectionMode: 'manual', + currentQuery: '', + selectedAgents, + refreshAgents: () => undefined, + }; + const testBed = registerTestBed(TestComponent)(props); + const { exists } = testBed; + + expect(exists('agentBulkActionsButton')).not.toBeNull(); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.tsx index 6fd34f0239996..a2515b51814ee 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/bulk_actions.tsx @@ -10,16 +10,14 @@ import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, - EuiText, EuiPopover, EuiContextMenu, - EuiButtonEmpty, + EuiButton, EuiIcon, EuiPortal, } from '@elastic/eui'; -import { FormattedMessage, FormattedNumber } from '@kbn/i18n-react'; +import { FormattedMessage } from '@kbn/i18n-react'; -import { SO_SEARCH_LIMIT } from '../../../../constants'; import type { Agent } from '../../../../types'; import { AgentReassignAgentPolicyModal, @@ -28,43 +26,26 @@ import { } from '../../components'; import { useKibanaVersion } from '../../../../hooks'; -const Divider = styled.div` - width: 0; - height: ${(props) => props.theme.eui.euiSizeL}; - border-left: ${(props) => props.theme.eui.euiBorderThin}; -`; +import type { SelectionMode } from './types'; const FlexItem = styled(EuiFlexItem)` height: ${(props) => props.theme.eui.euiSizeL}; `; - -const Button = styled(EuiButtonEmpty)` - .euiButtonEmpty__text { - font-size: ${(props) => props.theme.eui.euiFontSizeXS}; - } -`; - -export type SelectionMode = 'manual' | 'query'; - -export const AgentBulkActions: React.FunctionComponent<{ +export interface Props { totalAgents: number; totalInactiveAgents: number; - selectableAgents: number; selectionMode: SelectionMode; - setSelectionMode: (mode: SelectionMode) => void; currentQuery: string; selectedAgents: Agent[]; - setSelectedAgents: (agents: Agent[]) => void; refreshAgents: () => void; -}> = ({ +} + +export const AgentBulkActions: React.FunctionComponent = ({ totalAgents, totalInactiveAgents, - selectableAgents, selectionMode, - setSelectionMode, currentQuery, selectedAgents, - setSelectedAgents, refreshAgents, }) => { const kibanaVersion = useKibanaVersion(); @@ -92,6 +73,7 @@ export const AgentBulkActions: React.FunctionComponent<{ name: ( ), @@ -106,6 +88,7 @@ export const AgentBulkActions: React.FunctionComponent<{ name: ( ), @@ -120,6 +103,7 @@ export const AgentBulkActions: React.FunctionComponent<{ name: ( ), @@ -130,29 +114,10 @@ export const AgentBulkActions: React.FunctionComponent<{ setIsUpgradeModalOpen(true); }, }, - { - name: ( - - ), - icon: , - onClick: () => { - closeMenu(); - setSelectionMode('manual'); - setSelectedAgents([]); - }, - }, ], }, ]; - const showSelectEverything = - selectionMode === 'manual' && - selectedAgents.length === selectableAgents && - selectableAgents < totalAgents; - const totalActiveAgents = totalAgents - totalInactiveAgents; const agentCount = selectionMode === 'manual' ? selectedAgents.length : totalActiveAgents; const agents = selectionMode === 'manual' ? selectedAgents : currentQuery; @@ -196,51 +161,25 @@ export const AgentBulkActions: React.FunctionComponent<{ )} - - - {totalAgents > SO_SEARCH_LIMIT ? ( - , - total: , - }} - /> - ) : ( - - )} - - {(selectionMode === 'manual' && selectedAgents.length) || (selectionMode === 'query' && totalAgents > 0) ? ( <> - - - - + } isOpen={isMenuOpen} closePopover={closeMenu} @@ -250,22 +189,6 @@ export const AgentBulkActions: React.FunctionComponent<{ - {showSelectEverything ? ( - - - - ) : null} ) : ( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/empty_prompt.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/empty_prompt.tsx new file mode 100644 index 0000000000000..256ab2a0bacd1 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/empty_prompt.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +export const EmptyPrompt: React.FunctionComponent<{ + hasFleetAllPrivileges: boolean; + setEnrollmentFlyoutState: ( + value: React.SetStateAction<{ + isOpen: boolean; + selectedPolicyId?: string | undefined; + }> + ) => void; +}> = ({ hasFleetAllPrivileges, setEnrollmentFlyoutState }) => { + return ( + + + + } + actions={ + hasFleetAllPrivileges ? ( + setEnrollmentFlyoutState({ isOpen: true })} + > + + + ) : null + } + /> + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.tsx index 8965ae9bbc44f..60a97845312e8 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.tsx @@ -19,10 +19,13 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { AgentPolicy } from '../../../../types'; +import type { Agent, AgentPolicy } from '../../../../types'; import { AgentEnrollmentFlyout, SearchBar } from '../../../../components'; import { AGENTS_INDEX } from '../../../../constants'; +import { AgentBulkActions } from './bulk_actions'; +import type { SelectionMode } from './types'; + const statusFilters = [ { status: 'healthy', @@ -67,6 +70,15 @@ export const SearchAndFilterBar: React.FunctionComponent<{ onSelectedStatusChange: (selectedStatus: string[]) => void; showUpgradeable: boolean; onShowUpgradeableChange: (showUpgradeable: boolean) => void; + tags: string[]; + selectedTags: string[]; + onSelectedTagsChange: (selectedTags: string[]) => void; + totalAgents: number; + totalInactiveAgents: number; + selectionMode: SelectionMode; + currentQuery: string; + selectedAgents: Agent[]; + refreshAgents: () => void; }> = ({ agentPolicies, draftKuery, @@ -78,6 +90,15 @@ export const SearchAndFilterBar: React.FunctionComponent<{ onSelectedStatusChange, showUpgradeable, onShowUpgradeableChange, + tags, + selectedTags, + onSelectedTagsChange, + totalAgents, + totalInactiveAgents, + selectionMode, + currentQuery, + selectedAgents, + refreshAgents, }) => { const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState(false); @@ -85,7 +106,9 @@ export const SearchAndFilterBar: React.FunctionComponent<{ const [isAgentPoliciesFilterOpen, setIsAgentPoliciesFilterOpen] = useState(false); // Status for filtering - const [isStatusFilterOpen, setIsStatutsFilterOpen] = useState(false); + const [isStatusFilterOpen, setIsStatusFilterOpen] = useState(false); + + const [isTagsFilterOpen, setIsTagsFilterOpen] = useState(false); // Add a agent policy id to current search const addAgentPolicyFilter = (policyId: string) => { @@ -99,6 +122,14 @@ export const SearchAndFilterBar: React.FunctionComponent<{ ); }; + const addTagsFilter = (tag: string) => { + onSelectedTagsChange([...selectedTags, tag]); + }; + + const removeTagsFilter = (tag: string) => { + onSelectedTagsChange(selectedTags.filter((t) => t !== tag)); + }; + return ( <> {isEnrollmentFlyoutOpen ? ( @@ -131,7 +162,7 @@ export const SearchAndFilterBar: React.FunctionComponent<{ button={ setIsStatutsFilterOpen(!isStatusFilterOpen)} + onClick={() => setIsStatusFilterOpen(!isStatusFilterOpen)} isSelected={isStatusFilterOpen} hasActiveFilters={selectedStatus.length > 0} disabled={agentPolicies.length === 0} @@ -144,7 +175,7 @@ export const SearchAndFilterBar: React.FunctionComponent<{ } isOpen={isStatusFilterOpen} - closePopover={() => setIsStatutsFilterOpen(false)} + closePopover={() => setIsStatusFilterOpen(false)} panelPaddingSize="none" >
@@ -165,6 +196,46 @@ export const SearchAndFilterBar: React.FunctionComponent<{ ))}
+ setIsTagsFilterOpen(!isTagsFilterOpen)} + isSelected={isTagsFilterOpen} + hasActiveFilters={selectedTags.length > 0} + numFilters={selectedTags.length} + disabled={tags.length === 0} + data-test-subj="agentList.tagsFilter" + > + + + } + isOpen={isTagsFilterOpen} + closePopover={() => setIsTagsFilterOpen(false)} + panelPaddingSize="none" + > +
+ {tags.map((tag, index) => ( + { + if (selectedTags.includes(tag)) { + removeTagsFilter(tag); + } else { + addTagsFilter(tag); + } + }} + > + {tag} + + ))} +
+
+ + +
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/table_header.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/table_header.tsx index ff809d360e744..4e2f058596cf0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/table_header.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/table_header.tsx @@ -11,51 +11,41 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import type { Agent, SimplifiedAgentStatus } from '../../../../types'; import { AgentStatusBar } from './status_bar'; -import { AgentBulkActions } from './bulk_actions'; +import { AgentsSelectionStatus } from './agents_selection_status'; import {} from '@elastic/eui'; import { AgentStatusBadges } from './status_badges'; - -export type SelectionMode = 'manual' | 'query'; +import type { SelectionMode } from './types'; export const AgentTableHeader: React.FunctionComponent<{ agentStatus?: { [k in SimplifiedAgentStatus]: number }; showInactive: boolean; totalAgents: number; - totalInactiveAgents: number; selectableAgents: number; selectionMode: SelectionMode; setSelectionMode: (mode: SelectionMode) => void; - currentQuery: string; selectedAgents: Agent[]; setSelectedAgents: (agents: Agent[]) => void; - refreshAgents: () => void; }> = ({ agentStatus, totalAgents, - totalInactiveAgents, selectableAgents, selectionMode, setSelectionMode, - currentQuery, selectedAgents, setSelectedAgents, - refreshAgents, showInactive, }) => { return ( <> - diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/table_row_actions.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/table_row_actions.tsx new file mode 100644 index 0000000000000..be620f0044cd1 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/table_row_actions.tsx @@ -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 React, { useState } from 'react'; +import { EuiContextMenuItem } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import type { Agent, AgentPolicy } from '../../../../types'; +import { useAuthz, useLink, useKibanaVersion } from '../../../../hooks'; +import { ContextMenuActions } from '../../../../components'; +import { isAgentUpgradeable } from '../../../../services'; + +export const TableRowActions: React.FunctionComponent<{ + agent: Agent; + agentPolicy?: AgentPolicy; + onReassignClick: () => void; + onUnenrollClick: () => void; + onUpgradeClick: () => void; +}> = ({ agent, agentPolicy, onReassignClick, onUnenrollClick, onUpgradeClick }) => { + const { getHref } = useLink(); + const hasFleetAllPrivileges = useAuthz().fleet.all; + + const isUnenrolling = agent.status === 'unenrolling'; + const kibanaVersion = useKibanaVersion(); + const [isMenuOpen, setIsMenuOpen] = useState(false); + const menuItems = [ + + + , + ]; + + if (agentPolicy?.is_managed === false) { + menuItems.push( + { + onReassignClick(); + }} + disabled={!agent.active} + key="reassignPolicy" + > + + , + { + onUnenrollClick(); + }} + > + {isUnenrolling ? ( + + ) : ( + + )} + , + { + onUpgradeClick(); + }} + > + + + ); + } + return ( + setIsMenuOpen(isOpen)} + items={menuItems} + /> + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags.test.tsx new file mode 100644 index 0000000000000..a764485544bd7 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags.test.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; + +import { Tags } from './tags'; + +describe('Tags', () => { + describe('when list is short', () => { + it('renders a comma-separated list of tags', () => { + const tags = ['tag1', 'tag2']; + render(); + + expect(screen.getByTestId('agentTags')).toHaveTextContent('tag1, tag2'); + }); + }); + + describe('when list is long', () => { + it('renders a truncated list of tags with full list displayed in tooltip on hover', async () => { + const tags = ['tag1', 'tag2', 'tag3', 'tag4', 'tag5']; + render(); + + const tagsNode = screen.getByTestId('agentTags'); + + expect(tagsNode).toHaveTextContent('tag1, tag2, tag3 + 2 more'); + + fireEvent.mouseEnter(tagsNode); + await waitFor(() => { + screen.getByTestId('agentTagsTooltip'); + }); + + expect(screen.getByTestId('agentTagsTooltip')).toHaveTextContent( + 'tag1, tag2, tag3, tag4, tag5' + ); + }); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags.tsx new file mode 100644 index 0000000000000..7650b0d942180 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/tags.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiToolTip } from '@elastic/eui'; +import { take } from 'lodash'; +import React from 'react'; + +interface Props { + tags: string[]; +} + +const MAX_TAGS_TO_DISPLAY = 3; + +export const Tags: React.FunctionComponent = ({ tags }) => { + return ( + <> + {tags.length > MAX_TAGS_TO_DISPLAY ? ( + <> + {tags.join(', ')}}> + + {take(tags, 3).join(', ')} + {tags.length - MAX_TAGS_TO_DISPLAY} more + + + + ) : ( + {tags.join(', ')} + )} + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/types.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/types.tsx new file mode 100644 index 0000000000000..7e22c67b09acb --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/types.tsx @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type SelectionMode = 'manual' | 'query'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx index d5757419e8ea7..660a06911a5f0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx @@ -5,17 +5,14 @@ * 2.0. */ -import React, { useState, useMemo, useCallback, useRef, useEffect, useContext } from 'react'; +import React, { useState, useMemo, useCallback, useRef, useEffect } from 'react'; import { EuiBasicTable, - EuiButton, - EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, EuiText, - EuiContextMenuItem, EuiIcon, EuiPortal, } from '@elastic/eui'; @@ -34,12 +31,9 @@ import { useBreadcrumbs, useKibanaVersion, useStartServices, + useFlyoutContext, } from '../../../hooks'; -import { - AgentEnrollmentFlyout, - AgentPolicySummaryLine, - ContextMenuActions, -} from '../../../components'; +import { AgentEnrollmentFlyout, AgentPolicySummaryLine } from '../../../components'; import { AgentStatusKueryHelper, isAgentUpgradeable } from '../../../services'; import { AGENTS_PREFIX, FLEET_SERVER_PACKAGE } from '../../../constants'; import { @@ -52,95 +46,15 @@ import { } from '../components'; import { useFleetServerUnhealthy } from '../hooks/use_fleet_server_unhealthy'; -import { agentFlyoutContext } from '..'; - import { AgentTableHeader } from './components/table_header'; -import type { SelectionMode } from './components/bulk_actions'; +import type { SelectionMode } from './components/types'; import { SearchAndFilterBar } from './components/search_and_filter_bar'; +import { Tags } from './components/tags'; +import { TableRowActions } from './components/table_row_actions'; +import { EmptyPrompt } from './components/empty_prompt'; const REFRESH_INTERVAL_MS = 30000; -const RowActions = React.memo<{ - agent: Agent; - agentPolicy?: AgentPolicy; - refresh: () => void; - onReassignClick: () => void; - onUnenrollClick: () => void; - onUpgradeClick: () => void; -}>(({ agent, agentPolicy, refresh, onReassignClick, onUnenrollClick, onUpgradeClick }) => { - const { getHref } = useLink(); - const hasFleetAllPrivileges = useAuthz().fleet.all; - - const isUnenrolling = agent.status === 'unenrolling'; - const kibanaVersion = useKibanaVersion(); - const [isMenuOpen, setIsMenuOpen] = useState(false); - const menuItems = [ - - - , - ]; - - if (agentPolicy?.is_managed === false) { - menuItems.push( - { - onReassignClick(); - }} - disabled={!agent.active} - key="reassignPolicy" - > - - , - { - onUnenrollClick(); - }} - > - {isUnenrolling ? ( - - ) : ( - - )} - , - { - onUpgradeClick(); - }} - > - - - ); - } - return ( - setIsMenuOpen(isOpen)} - items={menuItems} - /> - ); -}); - function safeMetadata(val: any) { if (typeof val !== 'string') { return '-'; @@ -184,14 +98,21 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { // Status for filtering const [selectedStatus, setSelectedStatus] = useState([]); + const [selectedTags, setSelectedTags] = useState([]); + const isUsingFilter = - search.trim() || selectedAgentPolicies.length || selectedStatus.length || showUpgradeable; + search.trim() || + selectedAgentPolicies.length || + selectedStatus.length || + selectedTags.length || + showUpgradeable; const clearFilters = useCallback(() => { setDraftKuery(''); setSearch(''); setSelectedAgentPolicies([]); setSelectedStatus([]); + setSelectedTags([]); setShowUpgradeable(false); }, [setSearch, setDraftKuery, setSelectedAgentPolicies, setSelectedStatus, setShowUpgradeable]); @@ -203,7 +124,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { isOpen: false, }); - const flyoutContext = useContext(agentFlyoutContext); + const flyoutContext = useFlyoutContext(); // Agent actions states const [agentToReassign, setAgentToReassign] = useState(undefined); @@ -221,6 +142,13 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { .map((agentPolicy) => `"${agentPolicy}"`) .join(' or ')})`; } + + if (selectedTags.length) { + kueryBuilder = `${kueryBuilder} ${AGENTS_PREFIX}.tags : (${selectedTags + .map((tag) => `"${tag}"`) + .join(' or ')})`; + } + if (selectedStatus.length) { const kueryStatus = selectedStatus .map((status) => { @@ -250,7 +178,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { } return kueryBuilder; - }, [selectedStatus, selectedAgentPolicies, search]); + }, [search, selectedAgentPolicies, selectedTags, selectedStatus]); const showInactive = useMemo(() => { return selectedStatus.includes('inactive'); @@ -260,6 +188,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { const [agentsStatus, setAgentsStatus] = useState< { [key in SimplifiedAgentStatus]: number } | undefined >(); + const [allTags, setAllTags] = useState(); const [isLoading, setIsLoading] = useState(false); const [totalAgents, setTotalAgents] = useState(0); const [totalInactiveAgents, setTotalInactiveAgents] = useState(0); @@ -310,6 +239,16 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { inactive: agentsRequest.data.totalInactive, }); + // Only set tags on the first request - we don't want the list of tags to update based + // on the returned set of agents from the API + if (allTags === undefined) { + const newAllTags = Array.from( + new Set(agentsRequest.data.items.flatMap((agent) => agent.tags ?? [])) + ); + + setAllTags(newAllTags); + } + setAgents(agentsRequest.data.items); setTotalAgents(agentsRequest.data.total); setTotalInactiveAgents(agentsRequest.data.totalInactive); @@ -323,7 +262,15 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { setIsLoading(false); } fetchDataAsync(); - }, [pagination, kuery, showInactive, showUpgradeable, notifications.toasts]); + }, [ + pagination.currentPage, + pagination.pageSize, + kuery, + showInactive, + showUpgradeable, + allTags, + notifications.toasts, + ]); // Send request to get agent list and status useEffect(() => { @@ -382,7 +329,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { // Fleet server unhealthy status const { isUnhealthy: isFleetServerUnhealthy } = useFleetServerUnhealthy(); const onClickAddFleetServer = useCallback(() => { - flyoutContext?.openFleetServerFlyout(); + flyoutContext.openFleetServerFlyout(); }, [flyoutContext]); const columns = [ @@ -405,6 +352,14 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { }), render: (active: boolean, agent: any) => , }, + { + field: 'tags', + width: '240px', + name: i18n.translate('xpack.fleet.agentList.tagsColumnTitle', { + defaultMessage: 'Tags', + }), + render: (tags: string[] = [], agent: any) => , + }, { field: 'policy_id', name: i18n.translate('xpack.fleet.agentList.policyColumnTitle', { @@ -479,10 +434,9 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { ? agentPoliciesIndexedById[agent.policy_id] : undefined; return ( - fetchData()} onReassignClick={() => setAgentToReassign(agent)} onUnenrollClick={() => setAgentToUnenroll(agent)} onUpgradeClick={() => setAgentToUpgrade(agent)} @@ -495,30 +449,6 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { }, ]; - const emptyPrompt = ( - - - - } - actions={ - hasFleetAllPrivileges ? ( - setEnrollmentFlyoutState({ isOpen: true })} - > - - - ) : null - } - /> - ); - return ( <> {enrollmentFlyout.isOpen ? ( @@ -592,6 +522,15 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { onSelectedStatusChange={setSelectedStatus} showUpgradeable={showUpgradeable} onShowUpgradeableChange={setShowUpgradeable} + tags={allTags ?? []} + selectedTags={selectedTags} + onSelectedTagsChange={setSelectedTags} + totalAgents={totalAgents} + totalInactiveAgents={totalInactiveAgents} + selectionMode={selectionMode} + currentQuery={kuery} + selectedAgents={selectedAgents} + refreshAgents={() => fetchData()} /> @@ -599,12 +538,10 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { { if (tableRef?.current) { @@ -612,7 +549,6 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { setSelectionMode('manual'); } }} - refreshAgents={() => fetchData()} /> @@ -645,7 +581,10 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { }} /> ) : ( - emptyPrompt + ) } items={ diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/enrollment_recommendation.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/enrollment_recommendation.tsx index 86990d84d5130..409a259f934dd 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/enrollment_recommendation.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/enrollment_recommendation.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useContext } from 'react'; +import React from 'react'; import { EuiButton, EuiButtonEmpty, @@ -17,14 +17,12 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useStartServices } from '../../../../hooks'; - -import { agentFlyoutContext } from '../..'; +import { useFlyoutContext, useStartServices } from '../../../../hooks'; export const EnrollmentRecommendation: React.FunctionComponent<{ showStandaloneTab: () => void; }> = ({ showStandaloneTab }) => { - const flyoutContext = useContext(agentFlyoutContext); + const flyoutContext = useFlyoutContext(); const { docLinks } = useStartServices(); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_reassign_policy_modal/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_reassign_policy_modal/index.tsx index 566bc7f4363ab..5e74b86728247 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_reassign_policy_modal/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_reassign_policy_modal/index.tsx @@ -99,6 +99,7 @@ export const AgentReassignAgentPolicyModal: React.FunctionComponent = ({ return ( = ({ return ( = ({ return ( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/fleet_server_upgrade_modal.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/fleet_server_upgrade_modal.tsx index 5902f73cae3bc..0f9a8a3bbdd50 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/fleet_server_upgrade_modal.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/fleet_server_upgrade_modal.tsx @@ -164,7 +164,7 @@ export const FleetServerUpgradeModal: React.FunctionComponent = ({ onClos ), link: ( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx index 57da2fcf36d76..78ceb6293d3ce 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx @@ -5,14 +5,21 @@ * 2.0. */ -import React, { createContext, useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { Router, Route, Switch, useHistory } from 'react-router-dom'; -import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiPortal } from '@elastic/eui'; +import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { FLEET_ROUTING_PATHS } from '../../constants'; -import { Loading, Error, AgentEnrollmentFlyout, FleetServerFlyout } from '../../components'; -import { useConfig, useFleetStatus, useBreadcrumbs, useAuthz, useGetSettings } from '../../hooks'; +import { Loading, Error } from '../../components'; +import { + useConfig, + useFleetStatus, + useBreadcrumbs, + useAuthz, + useGetSettings, + useFlyoutContext, +} from '../../hooks'; import { DefaultLayout, WithoutHeaderLayout } from '../../layouts'; import { AgentListPage } from './agent_list_page'; @@ -21,30 +28,16 @@ import { AgentDetailsPage } from './agent_details_page'; import { NoAccessPage } from './error_pages/no_access'; import { FleetServerUpgradeModal } from './components/fleet_server_upgrade_modal'; -// TODO: Move all instances of toggling these flyouts to a global context object to avoid cases in which -// we can render duplicate "stacked" flyouts -export const agentFlyoutContext = createContext< - | { - openEnrollmentFlyout: () => void; - closeEnrollmentFlyout: () => void; - openFleetServerFlyout: () => void; - closeFleetServerFlyout: () => void; - } - | undefined ->(undefined); - export const AgentsApp: React.FunctionComponent = () => { useBreadcrumbs('agent_list'); const history = useHistory(); const { agents } = useConfig(); const hasFleetAllPrivileges = useAuthz().fleet.all; const fleetStatus = useFleetStatus(); + const flyoutContext = useFlyoutContext(); const settings = useGetSettings(); - const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState(false); - const [isFleetServerFlyoutOpen, setIsFleetServerFlyoutOpen] = useState(false); - const [fleetServerModalVisible, setFleetServerModalVisible] = useState(false); const onCloseFleetServerModal = useCallback(() => { setFleetServerModalVisible(false); @@ -100,7 +93,7 @@ export const AgentsApp: React.FunctionComponent = () => { setIsEnrollmentFlyoutOpen(true)} + onClick={() => flyoutContext.openEnrollmentFlyout()} data-test-subj="addAgentBtnTop" > @@ -111,49 +104,24 @@ export const AgentsApp: React.FunctionComponent = () => { ) : undefined; return ( - setIsEnrollmentFlyoutOpen(true), - closeEnrollmentFlyout: () => setIsEnrollmentFlyoutOpen(false), - openFleetServerFlyout: () => setIsFleetServerFlyoutOpen(true), - closeFleetServerFlyout: () => setIsFleetServerFlyoutOpen(false), - }} - > - - - - - - - - {fleetServerModalVisible && ( - - )} - {hasOnlyFleetServerMissingRequirement ? ( - - ) : ( - - )} - - - - - {isEnrollmentFlyoutOpen && ( - - setIsEnrollmentFlyoutOpen(false)} - /> - - )} - - {isFleetServerFlyoutOpen && ( - - setIsFleetServerFlyoutOpen(false)} /> - - )} - - + + + + + + + + {fleetServerModalVisible && ( + + )} + {hasOnlyFleetServerMissingRequirement ? ( + + ) : ( + + )} + + + + ); }; diff --git a/x-pack/plugins/fleet/public/applications/integrations/app.tsx b/x-pack/plugins/fleet/public/applications/integrations/app.tsx index 717e528443a3f..fb0d7f625488a 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/app.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/app.tsx @@ -7,7 +7,7 @@ import React, { memo } from 'react'; import type { AppMountParameters } from '@kbn/core/public'; -import { EuiErrorBoundary } from '@elastic/eui'; +import { EuiErrorBoundary, EuiPortal } from '@elastic/eui'; import type { History } from 'history'; import { Router, Redirect, Route, Switch } from 'react-router-dom'; import useObservable from 'react-use/lib/useObservable'; @@ -22,14 +22,17 @@ import type { FleetConfigType, FleetStartServices } from '../../plugin'; import { ConfigContext, FleetStatusProvider, KibanaVersionContext } from '../../hooks'; -import { AgentPolicyContextProvider } from './hooks'; +import { FleetServerFlyout } from '../fleet/components'; + +import { AgentPolicyContextProvider, useFlyoutContext } from './hooks'; import { INTEGRATIONS_ROUTING_PATHS, pagePathGetters } from './constants'; import type { UIExtensionsStorage } from './types'; import { EPMApp } from './sections/epm'; -import { PackageInstallProvider, UIExtensionsContext } from './hooks'; +import { PackageInstallProvider, UIExtensionsContext, FlyoutContextProvider } from './hooks'; import { IntegrationsHeader } from './components/header'; +import { AgentEnrollmentFlyout } from './components'; const EmptyContext = () => <>; @@ -81,9 +84,11 @@ export const IntegrationsAppContext: React.FC<{ notifications={startServices.notifications} theme$={theme$} > - - {children} - + + + {children} + + @@ -104,6 +109,8 @@ export const IntegrationsAppContext: React.FC<{ ); export const AppRoutes = memo(() => { + const flyoutContext = useFlyoutContext(); + return ( <> @@ -131,6 +138,22 @@ export const AppRoutes = memo(() => { }} /> + + {flyoutContext.isEnrollmentFlyoutOpen && ( + + flyoutContext.closeEnrollmentFlyout()} + /> + + )} + + {flyoutContext.isFleetServerFlyoutOpen && ( + + flyoutContext.closeFleetServerFlyout()} /> + + )} ); }); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/hooks.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/hooks.tsx index 159de12ff578c..886ce736b3625 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/hooks.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/hooks.tsx @@ -9,11 +9,13 @@ import { i18n } from '@kbn/i18n'; import type { PackagePolicy, AgentPolicy } from '../../types'; import { sendGetOneAgentPolicy, useStartServices } from '../../hooks'; - -import { FLEET_KUBERNETES_PACKAGE } from '../../../common'; +import { FLEET_KUBERNETES_PACKAGE, FLEET_CLOUD_SECURITY_POSTURE_PACKAGE } from '../../../common'; import type { K8sMode } from './types'; +// Packages that requires custom elastic-agent manifest +const K8S_PACKAGES = new Set([FLEET_KUBERNETES_PACKAGE, FLEET_CLOUD_SECURITY_POSTURE_PACKAGE]); + export function useAgentPolicyWithPackagePolicies(policyId?: string) { const [agentPolicyWithPackagePolicies, setAgentPolicy] = useState(null); const core = useStartServices(); @@ -64,4 +66,4 @@ export function useIsK8sPolicy(agentPolicy?: AgentPolicy) { return { isK8s }; } -const isK8sPackage = (pkg: PackagePolicy) => pkg.package?.name === FLEET_KUBERNETES_PACKAGE; +const isK8sPackage = (pkg: PackagePolicy) => K8S_PACKAGES.has(pkg.package?.name as string); diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx b/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx index 5b777803552fb..ca7293a8c99c9 100644 --- a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx +++ b/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx @@ -26,8 +26,9 @@ tar xzvf elastic-agent-${kibanaVersion}-darwin-x86_64.tar.gz cd elastic-agent-${kibanaVersion}-darwin-x86_64 sudo ./elastic-agent install ${enrollArgs}`; - const windowsCommand = `wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-windows-x86_64.zip -OutFile elastic-agent-${kibanaVersion}-windows-x86_64.zip -Expand-Archive .\\elastic-agent-${kibanaVersion}-windows-x86_64.zip + const windowsCommand = `$ProgressPreference = 'SilentlyContinue' +wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-windows-x86_64.zip -OutFile elastic-agent-${kibanaVersion}-windows-x86_64.zip +Expand-Archive .\\elastic-agent-${kibanaVersion}-windows-x86_64.zip -DestinationPath . cd elastic-agent-${kibanaVersion}-windows-x86_64 .\\elastic-agent.exe install ${enrollArgs}`; diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx b/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx index 75378cdc86378..2d9326cf6cbb1 100644 --- a/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx +++ b/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx @@ -23,8 +23,9 @@ tar xzvf elastic-agent-${kibanaVersion}-darwin-x86_64.tar.gz cd elastic-agent-${kibanaVersion}-darwin-x86_64 sudo ./elastic-agent install`; - const STANDALONE_RUN_INSTRUCTIONS_WINDOWS = `wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-windows-x86_64.zip -OutFile elastic-agent-${kibanaVersion}-windows-x86_64.zip -Expand-Archive .\elastic-agent-${kibanaVersion}-windows-x86_64.zip + const STANDALONE_RUN_INSTRUCTIONS_WINDOWS = `$ProgressPreference = 'SilentlyContinue' +wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-${kibanaVersion}-windows-x86_64.zip -OutFile elastic-agent-${kibanaVersion}-windows-x86_64.zip +Expand-Archive .\elastic-agent-${kibanaVersion}-windows-x86_64.zip -DestinationPath . cd elastic-agent-${kibanaVersion}-windows-x86_64 .\\elastic-agent.exe install`; diff --git a/x-pack/plugins/fleet/public/components/platform_selector.tsx b/x-pack/plugins/fleet/public/components/platform_selector.tsx index ae18f56b4b3ac..0209bf8e31fd6 100644 --- a/x-pack/plugins/fleet/public/components/platform_selector.tsx +++ b/x-pack/plugins/fleet/public/components/platform_selector.tsx @@ -50,6 +50,14 @@ export const PlatformSelector: React.FunctionComponent = ({ /> ); + const commandsByPlatform: Record = { + linux: linuxCommand, + mac: macCommand, + windows: windowsCommand, + deb: linuxDebCommand, + rpm: linuxRpmCommand, + }; + return ( <> {isK8s ? ( @@ -67,39 +75,22 @@ export const PlatformSelector: React.FunctionComponent = ({ })} /> - {platform === 'linux' && ( - - {linuxCommand} - - )} - {platform === 'mac' && ( - - {macCommand} - - )} - {platform === 'windows' && ( - - {windowsCommand} - - )} - {platform === 'deb' && ( - <> - {systemPackageCallout} - - - {linuxDebCommand} - - - )} - {platform === 'rpm' && ( + {(platform === 'deb' || platform === 'rpm') && ( <> {systemPackageCallout} - - {linuxRpmCommand} - )} + + {commandsByPlatform[platform]} + )} diff --git a/x-pack/plugins/fleet/public/hooks/index.ts b/x-pack/plugins/fleet/public/hooks/index.ts index 5c995131396b4..579d1ab5bc3de 100644 --- a/x-pack/plugins/fleet/public/hooks/index.ts +++ b/x-pack/plugins/fleet/public/hooks/index.ts @@ -27,3 +27,4 @@ export * from './use_platform'; export * from './use_agent_policy_refresh'; export * from './use_package_installations'; export * from './use_agent_enrollment_flyout_data'; +export * from './use_flyout_context'; diff --git a/x-pack/plugins/fleet/public/hooks/use_flyout_context.tsx b/x-pack/plugins/fleet/public/hooks/use_flyout_context.tsx new file mode 100644 index 0000000000000..0ddc358ab2fbf --- /dev/null +++ b/x-pack/plugins/fleet/public/hooks/use_flyout_context.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { createContext, useContext, useState } from 'react'; + +const agentFlyoutContext = createContext< + | { + isEnrollmentFlyoutOpen: boolean; + openEnrollmentFlyout: () => void; + closeEnrollmentFlyout: () => void; + isFleetServerFlyoutOpen: boolean; + openFleetServerFlyout: () => void; + closeFleetServerFlyout: () => void; + } + | undefined +>(undefined); + +export const FlyoutContextProvider: React.FunctionComponent = ({ children }) => { + const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState(false); + const [isFleetServerFlyoutOpen, setIsFleetServerFlyoutOpen] = useState(false); + + return ( + setIsEnrollmentFlyoutOpen(true), + closeEnrollmentFlyout: () => setIsEnrollmentFlyoutOpen(false), + isFleetServerFlyoutOpen, + openFleetServerFlyout: () => setIsFleetServerFlyoutOpen(true), + closeFleetServerFlyout: () => setIsFleetServerFlyoutOpen(false), + }} + > + {children} + + ); +}; + +export const useFlyoutContext = () => { + const context = useContext(agentFlyoutContext); + + if (!context) { + throw new Error('useFlyoutContext must be used within a FlyoutContextProvider'); + } + + return context; +}; diff --git a/x-pack/plugins/fleet/public/mocks.ts b/x-pack/plugins/fleet/public/mocks.ts index 92b002a437649..8088f430ee81e 100644 --- a/x-pack/plugins/fleet/public/mocks.ts +++ b/x-pack/plugins/fleet/public/mocks.ts @@ -5,8 +5,14 @@ * 2.0. */ +import type { EuiTheme } from '@kbn/kibana-react-plugin/common'; +import type { RecursivePartial } from '@elastic/eui/src/components/common'; + import { createStartMock } from './mock'; +export const getMockTheme = (partialTheme: RecursivePartial): EuiTheme => + partialTheme as EuiTheme; + export const fleetMock = { createStartMock, }; diff --git a/x-pack/plugins/fleet/public/plugin.ts b/x-pack/plugins/fleet/public/plugin.ts index c94f2ce139ab6..34fa9d2cbcfc8 100644 --- a/x-pack/plugins/fleet/public/plugin.ts +++ b/x-pack/plugins/fleet/public/plugin.ts @@ -49,8 +49,14 @@ import { setupRouteService, appRoutesService, calculateAuthz, + parseExperimentalConfigValue, +} from '../common'; +import type { + CheckPermissionsResponse, + PostFleetSetupResponse, + FleetAuthz, + ExperimentalFeatures, } from '../common'; -import type { CheckPermissionsResponse, PostFleetSetupResponse, FleetAuthz } from '../common'; import type { FleetConfigType } from '../common/types'; @@ -60,6 +66,7 @@ import { setHttpClient } from './hooks/use_request'; import { createPackageSearchProvider } from './search_provider'; import { TutorialDirectoryHeaderLink, TutorialModuleNotice } from './components/home_integration'; import { createExtensionRegistrationCallback } from './services/ui_extensions'; +import { ExperimentalFeaturesService } from './services/experimental_features'; import type { UIExtensionRegistrationCallback, UIExtensionsStorage } from './types'; import { LazyCustomLogsAssetsExtension } from './lazy_custom_logs_assets_extension'; @@ -113,10 +120,12 @@ export class FleetPlugin implements Plugin(); + this.experimentalFeatures = parseExperimentalConfigValue(this.config.enableExperimental || []); this.kibanaVersion = initializerContext.env.packageInfo.version; } @@ -251,6 +260,7 @@ export class FleetPlugin implements Plugin core.http.get(appRoutesService.getCheckPermissionsPath()) diff --git a/x-pack/plugins/fleet/public/services/experimental_features.ts b/x-pack/plugins/fleet/public/services/experimental_features.ts new file mode 100644 index 0000000000000..afe5d0d65e3fd --- /dev/null +++ b/x-pack/plugins/fleet/public/services/experimental_features.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ExperimentalFeatures } from '../../common/experimental_features'; + +export class ExperimentalFeaturesService { + private static experimentalFeatures?: ExperimentalFeatures; + + public static init(experimentalFeatures: ExperimentalFeatures) { + this.experimentalFeatures = experimentalFeatures; + } + + public static get(): ExperimentalFeatures { + if (!this.experimentalFeatures) { + this.throwUninitializedError(); + } + + return this.experimentalFeatures; + } + + private static throwUninitializedError(): never { + throw new Error('Experimental features services not initialized'); + } +} diff --git a/x-pack/plugins/fleet/public/services/index.ts b/x-pack/plugins/fleet/public/services/index.ts index 9de918d01d707..2c1bedeaef82c 100644 --- a/x-pack/plugins/fleet/public/services/index.ts +++ b/x-pack/plugins/fleet/public/services/index.ts @@ -12,6 +12,7 @@ export type { PackagePolicyConfigValidationResults, PackagePolicyInputValidationResults, } from '../../common'; +export { ExperimentalFeaturesService } from './experimental_features'; export { AgentStatusKueryHelper, agentPolicyRouteService, diff --git a/x-pack/plugins/fleet/server/index.ts b/x-pack/plugins/fleet/server/index.ts index 3228c03959322..97ff8ade05859 100644 --- a/x-pack/plugins/fleet/server/index.ts +++ b/x-pack/plugins/fleet/server/index.ts @@ -11,6 +11,9 @@ import { schema } from '@kbn/config-schema'; import type { TypeOf } from '@kbn/config-schema'; import type { PluginConfigDescriptor, PluginInitializerContext } from '@kbn/core/server'; +import { getExperimentalAllowedValues, isValidExperimentalValue } from '../common'; +const allowedExperimentalValues = getExperimentalAllowedValues(); + import { PreconfiguredPackagesSchema, PreconfiguredAgentPoliciesSchema, @@ -139,6 +142,27 @@ export const config: PluginConfigDescriptor = { allowAgentUpgradeSourceUri: schema.boolean({ defaultValue: false }), bundledPackageLocation: schema.string({ defaultValue: DEFAULT_BUNDLED_PACKAGE_LOCATION }), }), + /** + * For internal use. A list of string values (comma delimited) that will enable experimental + * type of functionality that is not yet released. + * + * @example + * xpack.fleet.enableExperimental: + * - feature1 + * - feature2 + */ + enableExperimental: schema.arrayOf(schema.string(), { + defaultValue: () => [], + validate(list) { + for (const key of list) { + if (!isValidExperimentalValue(key)) { + return `[${key}] is not allowed. Allowed values are: ${allowedExperimentalValues.join( + ', ' + )}`; + } + } + }, + }), }), }; diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index 01fe5b48f9e31..f3423fb3f1c0f 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -22,7 +22,7 @@ import type { PackagePolicyServiceInterface } from '../services/package_policy'; import type { AgentPolicyServiceInterface } from '../services'; import type { FleetAppContext } from '../plugin'; import { createMockTelemetryEventsSender } from '../telemetry/__mocks__'; -import type { FleetConfigType } from '../../common'; +import type { FleetConfigType, ExperimentalFeatures } from '../../common'; import { createFleetAuthzMock } from '../../common'; import { agentServiceMock } from '../services/agents/agent_service.mock'; import type { FleetRequestHandlerContext } from '../types'; @@ -61,6 +61,7 @@ export const createAppContextStartContractMock = ( securitySetup: securityMock.createSetup(), securityStart: securityMock.createStart(), logger: loggingSystemMock.create().get(), + experimentalFeatures: {} as ExperimentalFeatures, isProductionMode: true, configInitialValue: { agents: { enabled: true, elasticsearch: {} }, diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index c9e3b9388087d..ea38068a7613c 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -42,8 +42,8 @@ import type { CloudSetup } from '@kbn/cloud-plugin/server'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/server'; -import type { FleetConfigType, FleetAuthz } from '../common'; -import { INTEGRATIONS_PLUGIN_ID } from '../common'; +import type { FleetConfigType, FleetAuthz, ExperimentalFeatures } from '../common'; +import { INTEGRATIONS_PLUGIN_ID, parseExperimentalConfigValue } from '../common'; import { PLUGIN_ID, @@ -119,6 +119,7 @@ export interface FleetAppContext { securityStart: SecurityPluginStart; config$?: Observable; configInitialValue: FleetConfigType; + experimentalFeatures: ExperimentalFeatures; savedObjects: SavedObjectsServiceStart; isProductionMode: PluginInitializerContext['env']['mode']['prod']; kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; @@ -392,6 +393,9 @@ export class FleetPlugin securityStart: plugins.security, configInitialValue: this.configInitialValue, config$: this.config$, + experimentalFeatures: parseExperimentalConfigValue( + this.configInitialValue.enableExperimental || [] + ), savedObjects: core.savedObjects, isProductionMode: this.isProductionMode, kibanaVersion: this.kibanaVersion, diff --git a/x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts b/x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts index 4f3cad9edab26..80a6eac2d81b0 100644 --- a/x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts @@ -33,7 +33,7 @@ export const postNewAgentActionHandlerBuilder = function ( const savedAgentAction = await actionsService.createAgentAction(esClient, { created_at: new Date().toISOString(), ...newAgentAction, - agent_id: agent.id, + agents: [agent.id], }); const body: PostNewAgentActionResponse = { diff --git a/x-pack/plugins/fleet/server/routes/data_streams/handlers.ts b/x-pack/plugins/fleet/server/routes/data_streams/handlers.ts index 2d01344a930aa..ad3b356828d72 100644 --- a/x-pack/plugins/fleet/server/routes/data_streams/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/data_streams/handlers.ts @@ -6,13 +6,16 @@ */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { keyBy, keys, merge } from 'lodash'; -import type { RequestHandler } from '@kbn/core/server'; +import type { RequestHandler, ElasticsearchClient } from '@kbn/core/server'; + +import type { TypeOf } from '@kbn/config-schema'; import type { DataStream } from '../../types'; import { KibanaSavedObjectType } from '../../../common'; import type { GetDataStreamsResponse } from '../../../common'; import { getPackageSavedObjects } from '../../services/epm/packages/get'; import { defaultIngestErrorHandler } from '../../errors'; +import type { GetDataStreamsListRequestSchema } from '../../../common/constants/data_streams'; const DATA_STREAM_INDEX_PATTERN = 'logs-*-*,metrics-*-*,traces-*-*,synthetics-*-*'; @@ -37,11 +40,139 @@ interface ESDataStreamInfo { hidden: boolean; } +async function getMetadataFromTermsEnum({ + dataStreamName, + esClient, +}: { + dataStreamName: string; + esClient: ElasticsearchClient; +}) { + const [maxEventIngestedResponse, namespaceResponse, datasetResponse, typeResponse] = + await Promise.all([ + esClient.search({ + size: 1, + index: dataStreamName, + sort: { + // @ts-expect-error Type '{ 'event.ingested': string; }' is not assignable to type 'string | string[] | undefined'. + 'event.ingested': 'desc', + }, + _source: false, + fields: ['event.ingested'], + }), + esClient.termsEnum({ + index: dataStreamName, + field: 'data_stream.namespace', + }), + esClient.termsEnum({ + index: dataStreamName, + field: 'data_stream.dataset', + }), + esClient.termsEnum({ + index: dataStreamName, + field: 'data_stream.type', + }), + ]); + + const maxIngested = new Date( + maxEventIngestedResponse.hits.hits[0]?.fields!['event.ingested'] + ).getTime(); + + const namespace = namespaceResponse.terms[0] ?? ''; + const dataset = datasetResponse.terms[0] ?? ''; + const type = typeResponse.terms[0] ?? ''; + + return { + maxIngested, + namespace, + dataset, + type, + }; +} + +async function getMetadataFromAggregations({ + dataStreamName, + esClient, +}: { + dataStreamName: string; + esClient: ElasticsearchClient; +}) { + // Query backing indices to extract data stream dataset, namespace, and type values + const { aggregations: dataStreamAggs } = await esClient.search({ + index: dataStreamName, + body: { + size: 0, + query: { + bool: { + filter: [ + { + exists: { + field: 'data_stream.namespace', + }, + }, + { + exists: { + field: 'data_stream.dataset', + }, + }, + ], + }, + }, + aggs: { + maxIngestedTimestamp: { + max: { + field: 'event.ingested', + }, + }, + dataset: { + terms: { + field: 'data_stream.dataset', + size: 1, + }, + }, + namespace: { + terms: { + field: 'data_stream.namespace', + size: 1, + }, + }, + type: { + terms: { + field: 'data_stream.type', + size: 1, + }, + }, + }, + }, + }); + + const { maxIngestedTimestamp } = dataStreamAggs as Record< + string, + estypes.AggregationsRateAggregate + >; + const { dataset, namespace, type } = dataStreamAggs as Record< + string, + estypes.AggregationsMultiBucketAggregateBase<{ key?: string; value?: number }> + >; + + const maxIngested = maxIngestedTimestamp?.value; + + return { + maxIngested, + dataset: (dataset.buckets as Array<{ key?: string; value?: number }>)[0]?.key || '', + namespace: (namespace.buckets as Array<{ key?: string; value?: number }>)[0]?.key || '', + type: (type.buckets as Array<{ key?: string; value?: number }>)[0]?.key || '', + }; +} + export const getListHandler: RequestHandler = async (context, request, response) => { // Query datastreams as the current user as the Kibana internal user may not have all the required permission const { savedObjects, elasticsearch } = await context.core; const esClient = elasticsearch.client.asCurrentUser; + const { use_terms_enum: useTermsEnum } = request.params as TypeOf< + typeof GetDataStreamsListRequestSchema['params'] + >; + const body: GetDataStreamsResponse = { data_streams: [], }; @@ -127,75 +258,18 @@ export const getListHandler: RequestHandler = async (context, request, response) dashboards: [], }; - // Query backing indices to extract data stream dataset, namespace, and type values - const { aggregations: dataStreamAggs } = await esClient.search({ - index: dataStream.name, - body: { - size: 0, - query: { - bool: { - filter: [ - { - exists: { - field: 'data_stream.namespace', - }, - }, - { - exists: { - field: 'data_stream.dataset', - }, - }, - ], - }, - }, - aggs: { - maxIngestedTimestamp: { - max: { - field: 'event.ingested', - }, - }, - dataset: { - terms: { - field: 'data_stream.dataset', - size: 1, - }, - }, - namespace: { - terms: { - field: 'data_stream.namespace', - size: 1, - }, - }, - type: { - terms: { - field: 'data_stream.type', - size: 1, - }, - }, - }, - }, - }); - - const { maxIngestedTimestamp } = dataStreamAggs as Record< - string, - estypes.AggregationsRateAggregate - >; - const { dataset, namespace, type } = dataStreamAggs as Record< - string, - estypes.AggregationsMultiBucketAggregateBase<{ key?: string; value?: number }> - >; + const { maxIngested, namespace, dataset, type } = useTermsEnum + ? await getMetadataFromTermsEnum({ dataStreamName: dataStream.name, esClient }) + : await getMetadataFromAggregations({ dataStreamName: dataStream.name, esClient }); // some integrations e.g custom logs don't have event.ingested - if (maxIngestedTimestamp?.value) { - dataStreamResponse.last_activity_ms = maxIngestedTimestamp?.value; + if (maxIngested) { + dataStreamResponse.last_activity_ms = maxIngested; } - dataStreamResponse.dataset = - (dataset.buckets as Array<{ key?: string; value?: number }>)[0]?.key || ''; - dataStreamResponse.namespace = - (namespace.buckets as Array<{ key?: string; value?: number }>)[0]?.key || ''; - dataStreamResponse.type = - (type.buckets as Array<{ key?: string; value?: number }>)[0]?.key || ''; + dataStreamResponse.dataset = dataset; + dataStreamResponse.namespace = namespace; + dataStreamResponse.type = type; // Find package saved object const pkgName = dataStreamResponse.package; diff --git a/x-pack/plugins/fleet/server/routes/data_streams/index.ts b/x-pack/plugins/fleet/server/routes/data_streams/index.ts index ddefc537ba207..d7491d87e2a17 100644 --- a/x-pack/plugins/fleet/server/routes/data_streams/index.ts +++ b/x-pack/plugins/fleet/server/routes/data_streams/index.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { GetDataStreamsListRequestSchema } from '../../../common/constants/data_streams'; import { DATA_STREAM_API_ROUTES } from '../../constants'; import type { FleetAuthzRouter } from '../security'; @@ -15,7 +16,7 @@ export const registerRoutes = (router: FleetAuthzRouter) => { router.get( { path: DATA_STREAM_API_ROUTES.LIST_PATTERN, - validate: false, + validate: GetDataStreamsListRequestSchema, fleetAuthz: { fleet: { all: true }, }, diff --git a/x-pack/plugins/fleet/server/services/agents/actions.ts b/x-pack/plugins/fleet/server/services/agents/actions.ts index 3ea8060e8e492..7a13e1612cb0c 100644 --- a/x-pack/plugins/fleet/server/services/agents/actions.ts +++ b/x-pack/plugins/fleet/server/services/agents/actions.ts @@ -8,20 +8,26 @@ import uuid from 'uuid'; import type { ElasticsearchClient } from '@kbn/core/server'; -import type { Agent, AgentAction, FleetServerAgentAction } from '../../../common/types/models'; +import type { + Agent, + AgentAction, + NewAgentAction, + FleetServerAgentAction, +} from '../../../common/types/models'; import { AGENT_ACTIONS_INDEX } from '../../../common/constants'; const ONE_MONTH_IN_MS = 2592000000; export async function createAgentAction( esClient: ElasticsearchClient, - newAgentAction: Omit + newAgentAction: NewAgentAction ): Promise { - const id = uuid.v4(); + const id = newAgentAction.id ?? uuid.v4(); + const timestamp = new Date().toISOString(); const body: FleetServerAgentAction = { - '@timestamp': new Date().toISOString(), + '@timestamp': timestamp, expiration: new Date(Date.now() + ONE_MONTH_IN_MS).toISOString(), - agents: [newAgentAction.agent_id], + agents: newAgentAction.agents, action_id: id, data: newAgentAction.data, type: newAgentAction.type, @@ -37,6 +43,7 @@ export async function createAgentAction( return { id, ...newAgentAction, + created_at: timestamp, }; } @@ -62,7 +69,7 @@ export async function bulkCreateAgentActions( const body: FleetServerAgentAction = { '@timestamp': new Date().toISOString(), expiration: new Date(Date.now() + ONE_MONTH_IN_MS).toISOString(), - agents: [action.agent_id], + agents: action.agents, action_id: action.id, data: action.data, type: action.type, diff --git a/x-pack/plugins/fleet/server/services/agents/reassign.ts b/x-pack/plugins/fleet/server/services/agents/reassign.ts index d342e8d54bb84..c842bfb8f72c7 100644 --- a/x-pack/plugins/fleet/server/services/agents/reassign.ts +++ b/x-pack/plugins/fleet/server/services/agents/reassign.ts @@ -20,7 +20,7 @@ import { bulkUpdateAgents, } from './crud'; import type { GetAgentsOptions } from '.'; -import { createAgentAction, bulkCreateAgentActions } from './actions'; +import { createAgentAction } from './actions'; import { searchHitToAgent } from './helpers'; export async function reassignAgent( @@ -42,7 +42,7 @@ export async function reassignAgent( }); await createAgentAction(esClient, { - agent_id: agentId, + agents: [agentId], created_at: new Date().toISOString(), type: 'POLICY_REASSIGN', }); @@ -161,14 +161,11 @@ export async function reassignAgents( }); const now = new Date().toISOString(); - await bulkCreateAgentActions( - esClient, - agentsToUpdate.map((agent) => ({ - agent_id: agent.id, - created_at: now, - type: 'POLICY_REASSIGN', - })) - ); + await createAgentAction(esClient, { + agents: agentsToUpdate.map((agent) => agent.id), + created_at: now, + type: 'POLICY_REASSIGN', + }); return { items: orderedOut }; } diff --git a/x-pack/plugins/fleet/server/services/agents/saved_objects.ts b/x-pack/plugins/fleet/server/services/agents/saved_objects.ts index a26194ef6ddeb..596c7db5d8472 100644 --- a/x-pack/plugins/fleet/server/services/agents/saved_objects.ts +++ b/x-pack/plugins/fleet/server/services/agents/saved_objects.ts @@ -5,18 +5,9 @@ * 2.0. */ -import Boom from '@hapi/boom'; import type { SavedObject } from '@kbn/core/server'; -import type { - Agent, - AgentSOAttributes, - AgentAction, - AgentPolicyAction, - AgentActionSOAttributes, - AgentPolicyActionSOAttributes, - BaseAgentActionSOAttributes, -} from '../../types'; +import type { Agent, AgentSOAttributes } from '../../types'; export function savedObjectToAgent(so: SavedObject): Agent { if (so.error) { @@ -33,58 +24,3 @@ export function savedObjectToAgent(so: SavedObject): Agent { packages: so.attributes.packages ?? [], }; } - -export function savedObjectToAgentAction(so: SavedObject): AgentAction; -export function savedObjectToAgentAction( - so: SavedObject -): AgentPolicyAction; -export function savedObjectToAgentAction( - so: SavedObject -): AgentAction | AgentPolicyAction { - if (so.error) { - if (so.error.statusCode === 404) { - throw Boom.notFound(so.error.message); - } - - throw new Error(so.error.message); - } - - // If it's an AgentPolicyAction - if (isPolicyActionSavedObject(so)) { - return { - id: so.id, - type: so.attributes.type, - created_at: so.attributes.created_at, - policy_id: so.attributes.policy_id, - policy_revision: so.attributes.policy_revision, - data: so.attributes.data ? JSON.parse(so.attributes.data) : undefined, - ack_data: so.attributes.ack_data ? JSON.parse(so.attributes.ack_data) : undefined, - }; - } - - if (!isAgentActionSavedObject(so)) { - throw new Error(`Malformed saved object AgentAction ${so.id}`); - } - - // If it's an AgentAction - return { - id: so.id, - type: so.attributes.type, - created_at: so.attributes.created_at, - agent_id: so.attributes.agent_id, - data: so.attributes.data ? JSON.parse(so.attributes.data) : undefined, - ack_data: so.attributes.ack_data ? JSON.parse(so.attributes.ack_data) : undefined, - }; -} - -export function isAgentActionSavedObject( - so: SavedObject -): so is SavedObject { - return (so.attributes as AgentActionSOAttributes).agent_id !== undefined; -} - -export function isPolicyActionSavedObject( - so: SavedObject -): so is SavedObject { - return (so.attributes as AgentPolicyActionSOAttributes).policy_id !== undefined; -} diff --git a/x-pack/plugins/fleet/server/services/agents/unenroll.test.ts b/x-pack/plugins/fleet/server/services/agents/unenroll.test.ts index e6327c16c3ccc..45f40916598a1 100644 --- a/x-pack/plugins/fleet/server/services/agents/unenroll.test.ts +++ b/x-pack/plugins/fleet/server/services/agents/unenroll.test.ts @@ -96,7 +96,7 @@ describe('unenrollAgents (plural)', () => { await unenrollAgents(soClient, esClient, { agentIds: idsToUnenroll }); // calls ES update with correct values - const calledWith = esClient.bulk.mock.calls[1][0]; + const calledWith = esClient.bulk.mock.calls[0][0]; const ids = (calledWith as estypes.BulkRequest)?.body ?.filter((i: any) => i.update !== undefined) .map((i: any) => i.update._id); @@ -116,7 +116,7 @@ describe('unenrollAgents (plural)', () => { // calls ES update with correct values const onlyRegular = [agentInRegularDoc._id, agentInRegularDoc2._id]; - const calledWith = esClient.bulk.mock.calls[1][0]; + const calledWith = esClient.bulk.mock.calls[0][0]; const ids = (calledWith as estypes.BulkRequest)?.body ?.filter((i: any) => i.update !== undefined) .map((i: any) => i.update._id); @@ -175,7 +175,7 @@ describe('unenrollAgents (plural)', () => { await unenrollAgents(soClient, esClient, { agentIds: idsToUnenroll, force: true }); // calls ES update with correct values - const calledWith = esClient.bulk.mock.calls[1][0]; + const calledWith = esClient.bulk.mock.calls[0][0]; const ids = (calledWith as estypes.BulkRequest)?.body ?.filter((i: any) => i.update !== undefined) .map((i: any) => i.update._id); @@ -232,18 +232,6 @@ describe('unenrollAgents (plural)', () => { function createClientMock() { const soClientMock = savedObjectsClientMock.create(); - // need to mock .create & bulkCreate due to (bulk)createAgentAction(s) in unenrollAgent(s) - // @ts-expect-error - soClientMock.create.mockResolvedValue({ attributes: { agent_id: 'tata' } }); - soClientMock.bulkCreate.mockImplementation(async ([{ type, attributes }]) => { - return { - saved_objects: [await soClientMock.create(type, attributes)], - }; - }); - soClientMock.bulkUpdate.mockResolvedValue({ - saved_objects: [], - }); - soClientMock.get.mockImplementation(async (_, id) => { switch (id) { case regularAgentPolicySO.id: diff --git a/x-pack/plugins/fleet/server/services/agents/unenroll.ts b/x-pack/plugins/fleet/server/services/agents/unenroll.ts index 461caff1ada6c..92dd0f1ba22f8 100644 --- a/x-pack/plugins/fleet/server/services/agents/unenroll.ts +++ b/x-pack/plugins/fleet/server/services/agents/unenroll.ts @@ -11,7 +11,7 @@ import type { Agent, BulkActionResult } from '../../types'; import * as APIKeyService from '../api_keys'; import { HostedAgentPolicyRestrictionRelatedError } from '../../errors'; -import { createAgentAction, bulkCreateAgentActions } from './actions'; +import { createAgentAction } from './actions'; import type { GetAgentsOptions } from './crud'; import { getAgentById, @@ -53,7 +53,7 @@ export async function unenrollAgent( } const now = new Date().toISOString(); await createAgentAction(esClient, { - agent_id: agentId, + agents: [agentId], created_at: now, type: 'UNENROLL', }); @@ -105,14 +105,11 @@ export async function unenrollAgents( await invalidateAPIKeysForAgents(agentsToUpdate); } else { // Create unenroll action for each agent - await bulkCreateAgentActions( - esClient, - agentsToUpdate.map((agent) => ({ - agent_id: agent.id, - created_at: now, - type: 'UNENROLL', - })) - ); + await createAgentAction(esClient, { + agents: agentsToUpdate.map((agent) => agent.id), + created_at: now, + type: 'UNENROLL', + }); } // Update the necessary agents diff --git a/x-pack/plugins/fleet/server/services/agents/upgrade.ts b/x-pack/plugins/fleet/server/services/agents/upgrade.ts index 36568ca6e0004..00470d5e25f8d 100644 --- a/x-pack/plugins/fleet/server/services/agents/upgrade.ts +++ b/x-pack/plugins/fleet/server/services/agents/upgrade.ts @@ -17,7 +17,7 @@ import { import { isAgentUpgradeable } from '../../../common/services'; import { appContextService } from '../app_context'; -import { bulkCreateAgentActions, createAgentAction } from './actions'; +import { createAgentAction } from './actions'; import type { GetAgentsOptions } from './crud'; import { getAgentDocuments, @@ -59,7 +59,7 @@ export async function sendUpgradeAgentAction({ } await createAgentAction(esClient, { - agent_id: agentId, + agents: [agentId], created_at: now, data, ack_data: data, @@ -75,8 +75,8 @@ export async function sendUpgradeAgentsActions( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, options: ({ agents: Agent[] } | GetAgentsOptions) & { - sourceUri: string | undefined; version: string; + sourceUri?: string | undefined; force?: boolean; } ) { @@ -158,16 +158,13 @@ export async function sendUpgradeAgentsActions( source_uri: options.sourceUri, }; - await bulkCreateAgentActions( - esClient, - agentsToUpdate.map((agent) => ({ - agent_id: agent.id, - created_at: now, - data, - ack_data: data, - type: 'UPGRADE', - })) - ); + await createAgentAction(esClient, { + created_at: now, + data, + ack_data: data, + type: 'UPGRADE', + agents: agentsToUpdate.map((agent) => agent.id), + }); await bulkUpdateAgents( esClient, diff --git a/x-pack/plugins/fleet/server/services/app_context.ts b/x-pack/plugins/fleet/server/services/app_context.ts index 71409f8d115c6..088e4d7c8b3f0 100644 --- a/x-pack/plugins/fleet/server/services/app_context.ts +++ b/x-pack/plugins/fleet/server/services/app_context.ts @@ -26,7 +26,7 @@ import type { SecurityPluginStart, SecurityPluginSetup } from '@kbn/security-plu import type { CloudSetup } from '@kbn/cloud-plugin/server'; -import type { FleetConfigType } from '../../common'; +import type { FleetConfigType, ExperimentalFeatures } from '../../common'; import type { ExternalCallback, ExternalCallbacksStorage, @@ -43,6 +43,7 @@ class AppContextService { private encryptedSavedObjectsSetup: EncryptedSavedObjectsPluginSetup | undefined; private data: DataPluginStart | undefined; private esClient: ElasticsearchClient | undefined; + private experimentalFeatures?: ExperimentalFeatures; private securitySetup: SecurityPluginSetup | undefined; private securityStart: SecurityPluginStart | undefined; private config$?: Observable; @@ -65,6 +66,7 @@ class AppContextService { this.securitySetup = appContext.securitySetup; this.securityStart = appContext.securityStart; this.savedObjects = appContext.savedObjects; + this.experimentalFeatures = appContext.experimentalFeatures; this.isProductionMode = appContext.isProductionMode; this.cloud = appContext.cloud; this.logger = appContext.logger; @@ -126,6 +128,13 @@ class AppContextService { return this.config$; } + public getExperimentalFeatures() { + if (!this.experimentalFeatures) { + throw new Error('experimentalFeatures not set.'); + } + return this.experimentalFeatures; + } + public getSavedObjects() { if (!this.savedObjects) { throw new Error('Saved objects start service not set.'); diff --git a/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts b/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts index 77fe33df4ca48..74cf93c1c6bcb 100644 --- a/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts +++ b/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts @@ -5,8 +5,7 @@ * 2.0. */ -export const elasticAgentStandaloneManifest = ` ---- +export const elasticAgentStandaloneManifest = `--- apiVersion: apps/v1 kind: DaemonSet metadata: @@ -66,6 +65,11 @@ spec: - name: proc mountPath: /hostfs/proc readOnly: true + - name: etc-kubernetes + mountPath: /hostfs/etc/kubernetes + - name: var-lib + mountPath: /hostfs/var/lib + readOnly: true - name: cgroup mountPath: /hostfs/sys/fs/cgroup readOnly: true @@ -75,6 +79,15 @@ spec: - name: varlog mountPath: /var/log readOnly: true + - name: passwd + mountPath: /hostfs/etc/passwd + readOnly: true + - name: group + mountPath: /hostfs/etc/group + readOnly: true + - name: systemd + mountPath: /hostfs/etc/systemd + readOnly: true volumes: - name: datastreams configMap: @@ -83,6 +96,18 @@ spec: - name: proc hostPath: path: /proc + - name: etc-kubernetes + hostPath: + path: /etc/kubernetes + - name: var-lib + hostPath: + path: /var/lib + - name: passwd + hostPath: + path: /etc/passwd + - name: group + hostPath: + path: /etc/group - name: cgroup hostPath: path: /sys/fs/cgroup @@ -92,6 +117,9 @@ spec: - name: varlog hostPath: path: /var/log + - name: systemd + hostPath: + path: /etc/systemd --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -149,6 +177,7 @@ rules: - pods - services - configmaps + - serviceaccounts verbs: ["get", "list", "watch"] # Enable this rule only if planing to use kubernetes_secrets provider #- apiGroups: [""] @@ -181,6 +210,23 @@ rules: - "/metrics" verbs: - get + # required for cloudbeat + - apiGroups: ["rbac.authorization.k8s.io"] + resources: + - clusterrolebindings + - clusterroles + - rolebindings + - roles + verbs: ["get", "list", "watch"] + - apiGroups: ["networking.k8s.io"] + resources: + - ingressclasses + - ingresses + verbs: ["get", "list", "watch"] + - apiGroups: ["policy"] + resources: + - podsecuritypolicies + verbs: ["get", "list", "watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -222,7 +268,8 @@ metadata: --- `; -export const elasticAgentManagedManifest = `apiVersion: apps/v1 +export const elasticAgentManagedManifest = `--- +apiVersion: apps/v1 kind: DaemonSet metadata: name: elastic-agent @@ -285,6 +332,11 @@ spec: - name: proc mountPath: /hostfs/proc readOnly: true + - name: etc-kubernetes + mountPath: /hostfs/etc/kubernetes + - name: var-lib + mountPath: /hostfs/var/lib + readOnly: true - name: cgroup mountPath: /hostfs/sys/fs/cgroup readOnly: true @@ -294,10 +346,31 @@ spec: - name: varlog mountPath: /var/log readOnly: true + - name: passwd + mountPath: /hostfs/etc/passwd + readOnly: true + - name: group + mountPath: /hostfs/etc/group + readOnly: true + - name: systemd + mountPath: /hostfs/etc/systemd + readOnly: true volumes: - name: proc hostPath: path: /proc + - name: etc-kubernetes + hostPath: + path: /etc/kubernetes + - name: var-lib + hostPath: + path: /var/lib + - name: passwd + hostPath: + path: /etc/passwd + - name: group + hostPath: + path: /etc/group - name: cgroup hostPath: path: /sys/fs/cgroup @@ -307,6 +380,9 @@ spec: - name: varlog hostPath: path: /var/log + - name: systemd + hostPath: + path: /etc/systemd --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -364,6 +440,7 @@ rules: - pods - services - configmaps + - serviceaccounts verbs: ["get", "list", "watch"] # Enable this rule only if planing to use kubernetes_secrets provider #- apiGroups: [""] @@ -396,6 +473,23 @@ rules: - "/metrics" verbs: - get + # required for cloudbeat + - apiGroups: ["rbac.authorization.k8s.io"] + resources: + - clusterrolebindings + - clusterroles + - rolebindings + - roles + verbs: ["get", "list", "watch"] + - apiGroups: ["networking.k8s.io"] + resources: + - ingressclasses + - ingresses + verbs: ["get", "list", "watch"] + - apiGroups: ["policy"] + resources: + - podsecuritypolicies + verbs: ["get", "list", "watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -435,5 +529,4 @@ metadata: labels: k8s-app: elastic-agent --- - `; diff --git a/x-pack/plugins/fleet/server/services/epm/archive/storage.ts b/x-pack/plugins/fleet/server/services/epm/archive/storage.ts index cb9f5650550e8..2c313f2f2761d 100644 --- a/x-pack/plugins/fleet/server/services/epm/archive/storage.ts +++ b/x-pack/plugins/fleet/server/services/epm/archive/storage.ts @@ -123,7 +123,7 @@ export async function saveArchiveEntries(opts: { }) ); - const results = await savedObjectsClient.bulkCreate(bulkBody); + const results = await savedObjectsClient.bulkCreate(bulkBody, { refresh: false }); return results; } diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/datastream_ilm/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/datastream_ilm/install.ts index 4f18966a61307..c6be2dfedb1df 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/datastream_ilm/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/datastream_ilm/install.ts @@ -13,14 +13,13 @@ import type { InstallablePackage, RegistryDataStream, } from '../../../../../common/types/models'; -import { getInstallation } from '../../packages'; -import { saveInstalledEsRefs } from '../../packages/install'; +import { updateEsAssetReferences } from '../../packages/install'; import { getAsset } from '../transform/common'; import { getESAssetMetadata } from '../meta'; import { retryTransientEsErrors } from '../retry'; -import { deleteIlmRefs, deleteIlms } from './remove'; +import { deleteIlms } from './remove'; interface IlmInstallation { installationName: string; @@ -37,24 +36,39 @@ export const installIlmForDataStream = async ( paths: string[], esClient: ElasticsearchClient, savedObjectsClient: SavedObjectsClientContract, - logger: Logger + logger: Logger, + esReferences: EsAssetReference[] ) => { - const installation = await getInstallation({ savedObjectsClient, pkgName: registryPackage.name }); - let previousInstalledIlmEsAssets: EsAssetReference[] = []; - if (installation) { - previousInstalledIlmEsAssets = installation.installed_es.filter( - ({ type, id }) => type === ElasticsearchAssetType.dataStreamIlmPolicy - ); - } + const previousInstalledIlmEsAssets = esReferences.filter( + ({ type }) => type === ElasticsearchAssetType.dataStreamIlmPolicy + ); // delete all previous ilm await deleteIlms( esClient, previousInstalledIlmEsAssets.map((asset) => asset.id) ); + + if (previousInstalledIlmEsAssets.length > 0) { + // remove the saved object reference + esReferences = await updateEsAssetReferences( + savedObjectsClient, + registryPackage.name, + esReferences, + { + assetsToRemove: previousInstalledIlmEsAssets, + } + ); + } + // install the latest dataset const dataStreams = registryPackage.data_streams; - if (!dataStreams?.length) return []; + if (!dataStreams?.length) + return { + installedIlms: [], + esReferences, + }; + const dataStreamIlmPaths = paths.filter((path) => isDataStreamIlm(path)); let installedIlms: EsAssetReference[] = []; if (dataStreamIlmPaths.length > 0) { @@ -77,12 +91,17 @@ export const installIlmForDataStream = async ( return acc; }, []); - await saveInstalledEsRefs(savedObjectsClient, registryPackage.name, ilmRefs); + esReferences = await updateEsAssetReferences( + savedObjectsClient, + registryPackage.name, + esReferences, + { assetsToAdd: ilmRefs } + ); const ilmInstallations: IlmInstallation[] = ilmPathDatasets.map( (ilmPathDataset: IlmPathDataset) => { const content = JSON.parse(getAsset(ilmPathDataset.path).toString('utf-8')); - content.policy._meta = getESAssetMetadata({ packageName: installation?.name }); + content.policy._meta = getESAssetMetadata({ packageName: registryPackage.name }); return { installationName: getIlmNameForInstallation(ilmPathDataset), @@ -98,22 +117,7 @@ export const installIlmForDataStream = async ( installedIlms = await Promise.all(installationPromises).then((results) => results.flat()); } - if (previousInstalledIlmEsAssets.length > 0) { - const currentInstallation = await getInstallation({ - savedObjectsClient, - pkgName: registryPackage.name, - }); - - // remove the saved object reference - await deleteIlmRefs( - savedObjectsClient, - currentInstallation?.installed_es || [], - registryPackage.name, - previousInstalledIlmEsAssets.map((asset) => asset.id), - installedIlms.map((installed) => installed.id) - ); - } - return installedIlms; + return { installedIlms, esReferences }; }; async function handleIlmInstall({ diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/datastream_ilm/remove.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/datastream_ilm/remove.ts index 1d98a9339c907..331088d195d0b 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/datastream_ilm/remove.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/datastream_ilm/remove.ts @@ -5,11 +5,7 @@ * 2.0. */ -import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; - -import { ElasticsearchAssetType } from '../../../../types'; -import type { EsAssetReference } from '../../../../types'; -import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../../../common/constants'; +import type { ElasticsearchClient } from '@kbn/core/server'; export const deleteIlms = async (esClient: ElasticsearchClient, ilmPolicyIds: string[]) => { await Promise.all( @@ -26,24 +22,3 @@ export const deleteIlms = async (esClient: ElasticsearchClient, ilmPolicyIds: st }) ); }; - -export const deleteIlmRefs = async ( - savedObjectsClient: SavedObjectsClientContract, - installedEsAssets: EsAssetReference[], - pkgName: string, - installedEsIdToRemove: string[], - currentInstalledEsIlmIds: string[] -) => { - const seen = new Set(); - const filteredAssets = installedEsAssets.filter(({ type, id }) => { - if (type !== ElasticsearchAssetType.dataStreamIlmPolicy) return true; - const add = - (currentInstalledEsIlmIds.includes(id) || !installedEsIdToRemove.includes(id)) && - !seen.has(id); - seen.add(id); - return add; - }); - return savedObjectsClient.update(PACKAGES_SAVED_OBJECT_TYPE, pkgName, { - installed_es: filteredAssets, - }); -}; diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ilm/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ilm/install.ts index 9b64ec89507dc..3aa86b526addd 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ilm/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ilm/install.ts @@ -5,12 +5,13 @@ * 2.0. */ -import type { ElasticsearchClient, Logger } from '@kbn/core/server'; +import type { ElasticsearchClient, Logger, SavedObjectsClientContract } from '@kbn/core/server'; -import type { InstallablePackage } from '../../../../types'; +import type { EsAssetReference, InstallablePackage } from '../../../../types'; import { ElasticsearchAssetType } from '../../../../types'; import { getAsset, getPathParts } from '../../archive'; +import { updateEsAssetReferences } from '../../packages/install'; import { getESAssetMetadata } from '../meta'; import { retryTransientEsErrors } from '../retry'; @@ -18,25 +19,40 @@ export async function installILMPolicy( packageInfo: InstallablePackage, paths: string[], esClient: ElasticsearchClient, - logger: Logger -) { + savedObjectsClient: SavedObjectsClientContract, + logger: Logger, + esReferences: EsAssetReference[] +): Promise { const ilmPaths = paths.filter((path) => isILMPolicy(path)); - if (!ilmPaths.length) return; - await Promise.all( - ilmPaths.map(async (path) => { - const body = JSON.parse(getAsset(path).toString('utf-8')); + if (!ilmPaths.length) return esReferences; + + const ilmPolicies = ilmPaths.map((path) => { + const body = JSON.parse(getAsset(path).toString('utf-8')); + + body.policy._meta = getESAssetMetadata({ packageName: packageInfo.name }); + + const { file } = getPathParts(path); + const name = file.substr(0, file.lastIndexOf('.')); - body.policy._meta = getESAssetMetadata({ packageName: packageInfo.name }); + return { name, body }; + }); - const { file } = getPathParts(path); - const name = file.substr(0, file.lastIndexOf('.')); + esReferences = await updateEsAssetReferences(savedObjectsClient, packageInfo.name, esReferences, { + assetsToAdd: ilmPolicies.map((policy) => ({ + type: ElasticsearchAssetType.ilmPolicy, + id: policy.name, + })), + }); + + await Promise.all( + ilmPolicies.map(async (policy) => { try { await retryTransientEsErrors( () => esClient.transport.request({ method: 'PUT', - path: '/_ilm/policy/' + name, - body, + path: '/_ilm/policy/' + policy.name, + body: policy.body, }), { logger } ); @@ -45,6 +61,8 @@ export async function installILMPolicy( } }) ); + + return esReferences; } const isILMPolicy = (path: string) => { diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/index.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/index.ts index 574534290214a..5f093a19157f9 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/index.ts @@ -5,6 +5,6 @@ * 2.0. */ -export { installPipelines, isTopLevelPipeline } from './install'; +export { prepareToInstallPipelines, isTopLevelPipeline } from './install'; export { deletePreviousPipelines, deletePipeline } from './remove'; diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/install.ts index c6830d5bb9a03..da035a44c9921 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/install.ts @@ -6,14 +6,12 @@ */ import type { TransportRequestOptions } from '@elastic/elasticsearch'; -import type { ElasticsearchClient, Logger, SavedObjectsClientContract } from '@kbn/core/server'; +import type { ElasticsearchClient, Logger } from '@kbn/core/server'; import { ElasticsearchAssetType } from '../../../../types'; import type { EsAssetReference, RegistryDataStream, InstallablePackage } from '../../../../types'; import { getAsset, getPathParts } from '../../archive'; import type { ArchiveEntry } from '../../archive'; -import { saveInstalledEsRefs } from '../../packages/install'; -import { getInstallationObject } from '../../packages'; import { FLEET_FINAL_PIPELINE_CONTENT, FLEET_FINAL_PIPELINE_ID, @@ -24,8 +22,6 @@ import { appendMetadataToIngestPipeline } from '../meta'; import { retryTransientEsErrors } from '../retry'; -import { deletePipelineRefs } from './remove'; - interface RewriteSubstitution { source: string; target: string; @@ -39,22 +35,23 @@ export const isTopLevelPipeline = (path: string) => { ); }; -export const installPipelines = async ( +export const prepareToInstallPipelines = ( installablePackage: InstallablePackage, - paths: string[], - esClient: ElasticsearchClient, - savedObjectsClient: SavedObjectsClientContract, - logger: Logger -) => { + paths: string[] +): { + assetsToAdd: EsAssetReference[]; + install: (esClient: ElasticsearchClient, logger: Logger) => Promise; +} => { // unlike other ES assets, pipeline names are versioned so after a template is updated // it can be created pointing to the new template, without removing the old one and effecting data // so do not remove the currently installed pipelines here const dataStreams = installablePackage.data_streams; - const { name: pkgName, version: pkgVersion } = installablePackage; + const { version: pkgVersion } = installablePackage; const pipelinePaths = paths.filter((path) => isPipeline(path)); const topLevelPipelinePaths = paths.filter((path) => isTopLevelPipeline(path)); - if (!dataStreams?.length && topLevelPipelinePaths.length === 0) return []; + if (!dataStreams?.length && topLevelPipelinePaths.length === 0) + return { assetsToAdd: [], install: () => Promise.resolve() }; // get and save pipeline refs before installing pipelines let pipelineRefs = dataStreams @@ -67,7 +64,7 @@ export const installPipelines = async ( const nameForInstallation = getPipelineNameForInstallation({ pipelineName: name, dataStream, - packageVersion: installablePackage.version, + packageVersion: pkgVersion, }); return { id: nameForInstallation, type: ElasticsearchAssetType.ingestPipeline }; }); @@ -80,57 +77,48 @@ export const installPipelines = async ( const { name } = getNameAndExtension(path); const nameForInstallation = getPipelineNameForInstallation({ pipelineName: name, - packageVersion: installablePackage.version, + packageVersion: pkgVersion, }); return { id: nameForInstallation, type: ElasticsearchAssetType.ingestPipeline }; }); pipelineRefs = [...pipelineRefs, ...topLevelPipelineRefs]; - // check that we don't duplicate the pipeline refs if the user is reinstalling - const installedPkg = await getInstallationObject({ - savedObjectsClient, - pkgName, - }); - if (!installedPkg) throw new Error("integration wasn't found while installing pipelines"); - // remove the current pipeline refs, if any exist, associated with this version before saving new ones so no duplicates occur - await deletePipelineRefs( - savedObjectsClient, - installedPkg.attributes.installed_es, - pkgName, - pkgVersion - ); - await saveInstalledEsRefs(savedObjectsClient, installablePackage.name, pipelineRefs); - const pipelines = dataStreams - ? dataStreams.reduce>>((acc, dataStream) => { - if (dataStream.ingest_pipeline) { - acc.push( - installAllPipelines({ - dataStream, - esClient, - logger, - paths: pipelinePaths, - installablePackage, - }) - ); - } - return acc; - }, []) - : []; - - if (topLevelPipelinePaths) { - pipelines.push( - installAllPipelines({ - dataStream: undefined, - esClient, - logger, - paths: topLevelPipelinePaths, - installablePackage, - }) - ); - } + return { + assetsToAdd: pipelineRefs, + install: async (esClient, logger) => { + const pipelines = dataStreams + ? dataStreams.reduce>>((acc, dataStream) => { + if (dataStream.ingest_pipeline) { + acc.push( + installAllPipelines({ + dataStream, + esClient, + logger, + paths: pipelinePaths, + installablePackage, + }) + ); + } + return acc; + }, []) + : []; + + if (topLevelPipelinePaths) { + pipelines.push( + installAllPipelines({ + dataStream: undefined, + esClient, + logger, + paths: topLevelPipelinePaths, + installablePackage, + }) + ); + } - return await Promise.all(pipelines).then((results) => results.flat()); + await Promise.all(pipelines); + }, + }; }; export function rewriteIngestPipeline( diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/remove.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/remove.ts index e9d693bdbfaa8..7e2b6c121bbab 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/remove.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/remove.ts @@ -10,54 +10,38 @@ import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/ import { appContextService } from '../../..'; import { ElasticsearchAssetType } from '../../../../types'; import { IngestManagerError } from '../../../../errors'; -import { getInstallation } from '../../packages/get'; -import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../../../common'; import type { EsAssetReference } from '../../../../../common'; +import { updateEsAssetReferences } from '../../packages/install'; export const deletePreviousPipelines = async ( esClient: ElasticsearchClient, savedObjectsClient: SavedObjectsClientContract, pkgName: string, - previousPkgVersion: string + previousPkgVersion: string, + esReferences: EsAssetReference[] ) => { const logger = appContextService.getLogger(); - const installation = await getInstallation({ savedObjectsClient, pkgName }); - if (!installation) return; - const installedEsAssets = installation.installed_es; - const installedPipelines = installedEsAssets.filter( + const installedPipelines = esReferences.filter( ({ type, id }) => type === ElasticsearchAssetType.ingestPipeline && id.includes(previousPkgVersion) ); - const deletePipelinePromises = installedPipelines.map(({ type, id }) => { - return deletePipeline(esClient, id); - }); - try { - await Promise.all(deletePipelinePromises); - } catch (e) { - logger.error(e); - } try { - await deletePipelineRefs(savedObjectsClient, installedEsAssets, pkgName, previousPkgVersion); + await Promise.all( + installedPipelines.map(({ type, id }) => { + return deletePipeline(esClient, id); + }) + ); } catch (e) { logger.error(e); } -}; -export const deletePipelineRefs = async ( - savedObjectsClient: SavedObjectsClientContract, - installedEsAssets: EsAssetReference[], - pkgName: string, - pkgVersion: string -) => { - const filteredAssets = installedEsAssets.filter(({ type, id }) => { - if (type !== ElasticsearchAssetType.ingestPipeline) return true; - if (!id.includes(pkgVersion)) return true; - return false; - }); - return savedObjectsClient.update(PACKAGES_SAVED_OBJECT_TYPE, pkgName, { - installed_es: filteredAssets, + return await updateEsAssetReferences(savedObjectsClient, pkgName, esReferences, { + assetsToRemove: esReferences.filter(({ type, id }) => { + return type === ElasticsearchAssetType.ingestPipeline && id.includes(previousPkgVersion); + }), }); }; + export async function deletePipeline(esClient: ElasticsearchClient, id: string): Promise { // '*' shouldn't ever appear here, but it still would delete all ingest pipelines if (id && id !== '*') { diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ml_model/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ml_model/install.ts index 13b3de989e620..630433e18ce39 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ml_model/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ml_model/install.ts @@ -8,13 +8,14 @@ import type { ElasticsearchClient, Logger, SavedObjectsClientContract } from '@kbn/core/server'; import { errors } from '@elastic/elasticsearch'; -import { saveInstalledEsRefs } from '../../packages/install'; import { getPathParts } from '../../archive'; import { ElasticsearchAssetType } from '../../../../../common/types/models'; import type { EsAssetReference, InstallablePackage } from '../../../../../common/types/models'; import { retryTransientEsErrors } from '../retry'; +import { updateEsAssetReferences } from '../../packages/install'; + import { getAsset } from './common'; interface MlModelInstallation { @@ -27,11 +28,11 @@ export const installMlModel = async ( paths: string[], esClient: ElasticsearchClient, savedObjectsClient: SavedObjectsClientContract, - logger: Logger + logger: Logger, + esReferences: EsAssetReference[] ) => { const mlModelPath = paths.find((path) => isMlModel(path)); - const installedMlModels: EsAssetReference[] = []; if (mlModelPath !== undefined) { const content = getAsset(mlModelPath).toString('utf-8'); const pathParts = mlModelPath.split('/'); @@ -43,17 +44,22 @@ export const installMlModel = async ( }; // get and save ml model refs before installing ml model - await saveInstalledEsRefs(savedObjectsClient, installablePackage.name, [mlModelRef]); + esReferences = await updateEsAssetReferences( + savedObjectsClient, + installablePackage.name, + esReferences, + { assetsToAdd: [mlModelRef] } + ); const mlModel: MlModelInstallation = { installationName: modelId, content, }; - const result = await handleMlModelInstall({ esClient, logger, mlModel }); - installedMlModels.push(result); + await handleMlModelInstall({ esClient, logger, mlModel }); } - return installedMlModels; + + return esReferences; }; const isMlModel = (path: string) => { diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.test.ts index 1ade871b27ef6..df1c87105bcf2 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.test.ts @@ -36,6 +36,18 @@ describe('buildDefaultSettings', () => { name: 'field2Boolean', type: 'boolean', }, + { + name: 'field3Text', + type: 'text', + }, + { + name: 'field4MatchOnlyText', + type: 'match_only_text', + }, + { + name: 'field5Wildcard', + type: 'wildcard', + }, ], }); @@ -49,6 +61,9 @@ describe('buildDefaultSettings', () => { "query": Object { "default_field": Array [ "field1Keyword", + "field3Text", + "field4MatchOnlyText", + "field5Wildcard", ], }, }, diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.ts index 7f8e8e8544109..5accf7b120f9e 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.ts @@ -8,7 +8,7 @@ import { appContextService } from '../../../app_context'; import type { Field, Fields } from '../../fields/field'; -const QUERY_DEFAULT_FIELD_TYPES = ['keyword', 'text']; +const QUERY_DEFAULT_FIELD_TYPES = ['keyword', 'text', 'match_only_text', 'wildcard']; const QUERY_DEFAULT_FIELD_LIMIT = 1024; const flattenFieldsToNameAndType = ( diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.test.ts index 48f070434530a..3478da69bf721 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.test.ts @@ -4,30 +4,19 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { loggerMock } from '@kbn/logging-mocks'; - import { createAppContextStartContractMock } from '../../../../mocks'; import { appContextService } from '../../..'; import type { RegistryDataStream } from '../../../../types'; -import type { Field } from '../../fields/field'; -import { installTemplate } from './install'; +import { prepareTemplate } from './install'; -describe('EPM install', () => { +describe('EPM index template install', () => { beforeEach(async () => { appContextService.start(createAppContextStartContractMock()); }); - it('tests installPackage to use correct priority and index_patterns for data stream with dataset_is_prefix not set', async () => { - const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - esClient.indices.getIndexTemplate.mockImplementation(() => - elasticsearchServiceMock.createSuccessTransportRequestPromise({ index_templates: [] }) - ); - - const fields: Field[] = []; + it('tests prepareTemplate to use correct priority and index_patterns for data stream with dataset_is_prefix not set', async () => { const dataStreamDatasetIsPrefixUnset = { type: 'metrics', dataset: 'package.dataset', @@ -43,29 +32,14 @@ describe('EPM install', () => { }; const templateIndexPatternDatasetIsPrefixUnset = 'metrics-package.dataset-*'; const templatePriorityDatasetIsPrefixUnset = 200; - await installTemplate({ - esClient, - logger: loggerMock.create(), - fields, - dataStream: dataStreamDatasetIsPrefixUnset, - packageVersion: pkg.version, - packageName: pkg.name, - }); - - const sentTemplate = ( - esClient.indices.putIndexTemplate.mock.calls[0][0] as estypes.IndicesPutIndexTemplateRequest - ).body; - - expect(sentTemplate).toBeDefined(); - expect(sentTemplate?.priority).toBe(templatePriorityDatasetIsPrefixUnset); - expect(sentTemplate?.index_patterns).toEqual([templateIndexPatternDatasetIsPrefixUnset]); + const { + indexTemplate: { indexTemplate }, + } = prepareTemplate({ pkg, dataStream: dataStreamDatasetIsPrefixUnset }); + expect(indexTemplate.priority).toBe(templatePriorityDatasetIsPrefixUnset); + expect(indexTemplate.index_patterns).toEqual([templateIndexPatternDatasetIsPrefixUnset]); }); - it('tests installPackage to use correct priority and index_patterns for data stream with dataset_is_prefix set to false', async () => { - const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - esClient.indices.getIndexTemplate.mockResponse({ index_templates: [] }); - - const fields: Field[] = []; + it('tests prepareTemplate to use correct priority and index_patterns for data stream with dataset_is_prefix set to false', async () => { const dataStreamDatasetIsPrefixFalse = { type: 'metrics', dataset: 'package.dataset', @@ -82,29 +56,15 @@ describe('EPM install', () => { }; const templateIndexPatternDatasetIsPrefixFalse = 'metrics-package.dataset-*'; const templatePriorityDatasetIsPrefixFalse = 200; - await installTemplate({ - esClient, - logger: loggerMock.create(), - fields, - dataStream: dataStreamDatasetIsPrefixFalse, - packageVersion: pkg.version, - packageName: pkg.name, - }); - - const sentTemplate = ( - esClient.indices.putIndexTemplate.mock.calls[0][0] as estypes.IndicesPutIndexTemplateRequest - ).body; + const { + indexTemplate: { indexTemplate }, + } = prepareTemplate({ pkg, dataStream: dataStreamDatasetIsPrefixFalse }); - expect(sentTemplate).toBeDefined(); - expect(sentTemplate?.priority).toBe(templatePriorityDatasetIsPrefixFalse); - expect(sentTemplate?.index_patterns).toEqual([templateIndexPatternDatasetIsPrefixFalse]); + expect(indexTemplate.priority).toBe(templatePriorityDatasetIsPrefixFalse); + expect(indexTemplate.index_patterns).toEqual([templateIndexPatternDatasetIsPrefixFalse]); }); - it('tests installPackage to use correct priority and index_patterns for data stream with dataset_is_prefix set to true', async () => { - const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - esClient.indices.getIndexTemplate.mockResponse({ index_templates: [] }); - - const fields: Field[] = []; + it('tests prepareTemplate to use correct priority and index_patterns for data stream with dataset_is_prefix set to true', async () => { const dataStreamDatasetIsPrefixTrue = { type: 'metrics', dataset: 'package.dataset', @@ -121,75 +81,11 @@ describe('EPM install', () => { }; const templateIndexPatternDatasetIsPrefixTrue = 'metrics-package.dataset.*-*'; const templatePriorityDatasetIsPrefixTrue = 150; - await installTemplate({ - esClient, - logger: loggerMock.create(), - fields, - dataStream: dataStreamDatasetIsPrefixTrue, - packageVersion: pkg.version, - packageName: pkg.name, - }); - - const sentTemplate = ( - esClient.indices.putIndexTemplate.mock.calls[0][0] as estypes.IndicesPutIndexTemplateRequest - ).body; - - expect(sentTemplate).toBeDefined(); - expect(sentTemplate?.priority).toBe(templatePriorityDatasetIsPrefixTrue); - expect(sentTemplate?.index_patterns).toEqual([templateIndexPatternDatasetIsPrefixTrue]); - }); - - it('tests installPackage remove the aliases property if the property existed', async () => { - const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - - esClient.indices.getIndexTemplate.mockResponse({ - index_templates: [ - { - name: 'metrics-package.dataset', - // @ts-expect-error not full interface - index_template: { - index_patterns: ['metrics-package.dataset-*'], - template: { aliases: {} }, - }, - }, - ], - }); - - const fields: Field[] = []; - const dataStreamDatasetIsPrefixUnset = { - type: 'metrics', - dataset: 'package.dataset', - title: 'test data stream', - release: 'experimental', - package: 'package', - path: 'path', - ingest_pipeline: 'default', - } as RegistryDataStream; - const pkg = { - name: 'package', - version: '0.0.1', - }; - const templateIndexPatternDatasetIsPrefixUnset = 'metrics-package.dataset-*'; - const templatePriorityDatasetIsPrefixUnset = 200; - await installTemplate({ - esClient, - logger: loggerMock.create(), - fields, - dataStream: dataStreamDatasetIsPrefixUnset, - packageVersion: pkg.version, - packageName: pkg.name, - }); - - const removeAliases = ( - esClient.indices.putIndexTemplate.mock.calls[0][0] as estypes.IndicesPutIndexTemplateRequest - ).body; - expect(removeAliases?.template?.aliases).not.toBeDefined(); + const { + indexTemplate: { indexTemplate }, + } = prepareTemplate({ pkg, dataStream: dataStreamDatasetIsPrefixTrue }); - const sentTemplate = ( - esClient.indices.putIndexTemplate.mock.calls[1][0] as estypes.IndicesPutIndexTemplateRequest - ).body; - expect(sentTemplate).toBeDefined(); - expect(sentTemplate?.priority).toBe(templatePriorityDatasetIsPrefixUnset); - expect(sentTemplate?.index_patterns).toEqual([templateIndexPatternDatasetIsPrefixUnset]); + expect(indexTemplate.priority).toBe(templatePriorityDatasetIsPrefixTrue); + expect(indexTemplate.index_patterns).toEqual([templateIndexPatternDatasetIsPrefixTrue]); }); }); diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts index 6d953835dfe6c..df6d9d84a08c5 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts @@ -7,7 +7,7 @@ import { merge } from 'lodash'; import Boom from '@hapi/boom'; -import type { ElasticsearchClient, Logger, SavedObjectsClientContract } from '@kbn/core/server'; +import type { ElasticsearchClient, Logger } from '@kbn/core/server'; import { ElasticsearchAssetType } from '../../../../types'; import type { @@ -16,17 +16,16 @@ import type { RegistryElasticsearch, InstallablePackage, IndexTemplate, - PackageInfo, IndexTemplateMappings, TemplateMapEntry, TemplateMap, + EsAssetReference, + PackageInfo, } from '../../../../types'; import { loadFieldsFromYaml, processFields } from '../../fields/field'; -import type { Field } from '../../fields/field'; import { getPipelineNameForInstallation } from '../ingest_pipeline/install'; import { getAsset, getPathParts } from '../../archive'; -import { removeAssetTypesFromInstalledEs, saveInstalledEsRefs } from '../../packages/install'; import { FLEET_COMPONENT_TEMPLATES, PACKAGE_TEMPLATE_SUFFIX, @@ -36,8 +35,6 @@ import { import { getESAssetMetadata } from '../meta'; import { retryTransientEsErrors } from '../retry'; -import { getPackageInfo } from '../../packages'; - import { generateMappings, generateTemplateName, @@ -49,57 +46,55 @@ import { buildDefaultSettings } from './default_settings'; const FLEET_COMPONENT_TEMPLATE_NAMES = FLEET_COMPONENT_TEMPLATES.map((tmpl) => tmpl.name); -export const installTemplates = async ( +export const prepareToInstallTemplates = ( installablePackage: InstallablePackage, - esClient: ElasticsearchClient, - logger: Logger, paths: string[], - savedObjectsClient: SavedObjectsClientContract -): Promise => { - // install any pre-built index template assets, - // atm, this is only the base package's global index templates - // Install component templates first, as they are used by the index templates - await installPreBuiltComponentTemplates(paths, esClient, logger); - await installPreBuiltTemplates(paths, esClient, logger); - + esReferences: EsAssetReference[] +): { + assetsToAdd: EsAssetReference[]; + assetsToRemove: EsAssetReference[]; + install: (esClient: ElasticsearchClient, logger: Logger) => Promise; +} => { // remove package installation's references to index templates - await removeAssetTypesFromInstalledEs(savedObjectsClient, installablePackage.name, [ - ElasticsearchAssetType.indexTemplate, - ElasticsearchAssetType.componentTemplate, - ]); + const assetsToRemove = esReferences.filter( + ({ type }) => + type === ElasticsearchAssetType.indexTemplate || + type === ElasticsearchAssetType.componentTemplate + ); + // build templates per data stream from yml files const dataStreams = installablePackage.data_streams; - if (!dataStreams) return []; - - const packageInfo = await getPackageInfo({ - savedObjectsClient, - pkgName: installablePackage.name, - pkgVersion: installablePackage.version, - }); + if (!dataStreams) return { assetsToAdd: [], assetsToRemove, install: () => Promise.resolve([]) }; - const installedTemplatesNested = await Promise.all( - dataStreams.map((dataStream) => - installTemplateForDataStream({ - pkg: packageInfo, - esClient, - logger, - dataStream, - }) - ) + const templates = dataStreams.map((dataStream) => + prepareTemplate({ pkg: installablePackage, dataStream }) ); - const installedTemplates = installedTemplatesNested.flat(); - - // get template refs to save - const installedIndexTemplateRefs = getAllTemplateRefs(installedTemplates); + const assetsToAdd = getAllTemplateRefs(templates.map((template) => template.indexTemplate)); - // add package installation's references to index templates - await saveInstalledEsRefs( - savedObjectsClient, - installablePackage.name, - installedIndexTemplateRefs - ); + return { + assetsToAdd, + assetsToRemove, + install: async (esClient, logger) => { + // install any pre-built index template assets, + // atm, this is only the base package's global index templates + // Install component templates first, as they are used by the index templates + await installPreBuiltComponentTemplates(paths, esClient, logger); + await installPreBuiltTemplates(paths, esClient, logger); + + await Promise.all( + templates.map((template) => + installComponentAndIndexTemplateForDataStream({ + esClient, + logger, + componentTemplates: template.componentTemplates, + indexTemplate: template.indexTemplate, + }) + ) + ); - return installedTemplates; + return templates.map((template) => template.indexTemplate); + }, + }; }; const installPreBuiltTemplates = async ( @@ -181,31 +176,24 @@ const isComponentTemplate = (path: string) => { }; /** - * installTemplateForDataStream installs one template for each data stream + * installComponentAndIndexTemplateForDataStream installs one template for each data stream * * The template is currently loaded with the pkgkey-package-data_stream */ -export async function installTemplateForDataStream({ - pkg, +export async function installComponentAndIndexTemplateForDataStream({ esClient, logger, - dataStream, + componentTemplates, + indexTemplate, }: { - pkg: PackageInfo; esClient: ElasticsearchClient; logger: Logger; - dataStream: RegistryDataStream; -}): Promise { - const fields = await loadFieldsFromYaml(pkg, dataStream.path); - return installTemplate({ - esClient, - logger, - fields, - dataStream, - packageVersion: pkg.version, - packageName: pkg.name, - }); + componentTemplates: TemplateMap; + indexTemplate: IndexTemplateEntry; +}) { + await installDataStreamComponentTemplates({ esClient, logger, componentTemplates }); + await installTemplate({ esClient, logger, template: indexTemplate }); } function putComponentTemplate( @@ -285,49 +273,33 @@ function buildComponentTemplates(params: { return templatesMap; } -async function installDataStreamComponentTemplates(params: { - mappings: IndexTemplateMappings; - templateName: string; - registryElasticsearch: RegistryElasticsearch | undefined; +async function installDataStreamComponentTemplates({ + esClient, + logger, + componentTemplates, +}: { esClient: ElasticsearchClient; logger: Logger; - packageName: string; - defaultSettings: IndexTemplate['template']['settings']; + componentTemplates: TemplateMap; }) { - const { - templateName, - registryElasticsearch, - esClient, - packageName, - defaultSettings, - logger, - mappings, - } = params; - const componentTemplates = buildComponentTemplates({ - mappings, - templateName, - registryElasticsearch, - packageName, - defaultSettings, - }); - const templateEntries = Object.entries(componentTemplates); // TODO: Check return values for errors await Promise.all( - templateEntries.map(async ([name, body]) => { + Object.entries(componentTemplates).map(async ([name, body]) => { if (isUserSettingsTemplate(name)) { - // look for existing user_settings template - const result = await retryTransientEsErrors( - () => esClient.cluster.getComponentTemplate({ name }, { ignore: [404] }), - { logger } - ); - const hasUserSettingsTemplate = result.component_templates?.length === 1; - if (!hasUserSettingsTemplate) { - // only add if one isn't already present + try { + // Attempt to create custom component templates, ignore if they already exist const { clusterPromise } = putComponentTemplate(esClient, logger, { body, name, + create: true, }); - return clusterPromise; + return await clusterPromise; + } catch (e) { + if (e?.statusCode === 400 && e.body?.error?.reason.includes('already exists')) { + // ignore + } else { + throw e; + } } } else { const { clusterPromise } = putComponentTemplate(esClient, logger, { body, name }); @@ -335,8 +307,6 @@ async function installDataStreamComponentTemplates(params: { } }) ); - - return { componentTemplateNames: Object.keys(componentTemplates) }; } export async function ensureDefaultComponentTemplates( @@ -380,21 +350,15 @@ export async function ensureComponentTemplate( return { isCreated: !existingTemplate }; } -export async function installTemplate({ - esClient, - logger, - fields, +export function prepareTemplate({ + pkg, dataStream, - packageVersion, - packageName, }: { - esClient: ElasticsearchClient; - logger: Logger; - fields: Field[]; + pkg: Pick; dataStream: RegistryDataStream; - packageVersion: string; - packageName: string; -}): Promise { +}): { componentTemplates: TemplateMap; indexTemplate: IndexTemplateEntry } { + const { name: packageName, version: packageVersion } = pkg; + const fields = loadFieldsFromYaml(pkg, dataStream.path); const validFields = processFields(fields); const mappings = generateMappings(validFields); const templateName = generateTemplateName(dataStream); @@ -410,44 +374,6 @@ export async function installTemplate({ }); } - // Datastream now throw an error if the aliases field is present so ensure that we remove that field. - const getTemplateRes = await retryTransientEsErrors( - () => - esClient.indices.getIndexTemplate( - { - name: templateName, - }, - { - ignore: [404], - } - ), - { logger } - ); - - const existingIndexTemplate = getTemplateRes?.index_templates?.[0]; - if ( - existingIndexTemplate && - existingIndexTemplate.name === templateName && - existingIndexTemplate?.index_template?.template?.aliases - ) { - const updateIndexTemplateParams = { - name: templateName, - body: { - ...existingIndexTemplate.index_template, - template: { - ...existingIndexTemplate.index_template.template, - // Remove the aliases field - aliases: undefined, - }, - }, - }; - - await retryTransientEsErrors( - () => esClient.indices.putIndexTemplate(updateIndexTemplateParams, { ignore: [404] }), - { logger } - ); - } - const defaultSettings = buildDefaultSettings({ templateName, packageName, @@ -456,40 +382,51 @@ export async function installTemplate({ ilmPolicy: dataStream.ilm_policy, }); - const { componentTemplateNames } = await installDataStreamComponentTemplates({ + const componentTemplates = buildComponentTemplates({ + defaultSettings, mappings, + packageName, templateName, registryElasticsearch: dataStream.elasticsearch, - esClient, - logger, - packageName, - defaultSettings, }); const template = getTemplate({ templateIndexPattern, pipelineName, packageName, - composedOfTemplates: componentTemplateNames, + composedOfTemplates: Object.keys(componentTemplates), templatePriority, hidden: dataStream.hidden, }); + return { + componentTemplates, + indexTemplate: { + templateName, + indexTemplate: template, + }, + }; +} + +async function installTemplate({ + esClient, + logger, + template, +}: { + esClient: ElasticsearchClient; + logger: Logger; + template: IndexTemplateEntry; +}) { // TODO: Check return values for errors const esClientParams = { - name: templateName, - body: template, + name: template.templateName, + body: template.indexTemplate, }; await retryTransientEsErrors( () => esClient.indices.putIndexTemplate(esClientParams, { ignore: [404] }), { logger } ); - - return { - templateName, - indexTemplate: template, - }; } export function getAllTemplateRefs(installedTemplates: IndexTemplateEntry[]) { diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/install.ts index fea12f4b139c6..ab8f60e172dcb 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/install.ts @@ -8,7 +8,7 @@ import type { ElasticsearchClient, Logger, SavedObjectsClientContract } from '@kbn/core/server'; import { errors } from '@elastic/elasticsearch'; -import { saveInstalledEsRefs } from '../../packages/install'; +import { updateEsAssetReferences } from '../../packages/install'; import { getPathParts } from '../../archive'; import { ElasticsearchAssetType } from '../../../../../common/types/models'; import type { EsAssetReference, InstallablePackage } from '../../../../../common/types/models'; @@ -18,7 +18,7 @@ import { getESAssetMetadata } from '../meta'; import { retryTransientEsErrors } from '../retry'; -import { deleteTransforms, deleteTransformRefs } from './remove'; +import { deleteTransforms } from './remove'; import { getAsset } from './common'; interface TransformInstallation { @@ -31,12 +31,14 @@ export const installTransform = async ( paths: string[], esClient: ElasticsearchClient, savedObjectsClient: SavedObjectsClientContract, - logger: Logger + logger: Logger, + esReferences?: EsAssetReference[] ) => { const installation = await getInstallation({ savedObjectsClient, pkgName: installablePackage.name, }); + esReferences = esReferences ?? installation?.installed_es ?? []; let previousInstalledTransformEsAssets: EsAssetReference[] = []; if (installation) { previousInstalledTransformEsAssets = installation.installed_es.filter( @@ -71,7 +73,14 @@ export const installTransform = async ( }, []); // get and save transform refs before installing transforms - await saveInstalledEsRefs(savedObjectsClient, installablePackage.name, transformRefs); + esReferences = await updateEsAssetReferences( + savedObjectsClient, + installablePackage.name, + esReferences, + { + assetsToAdd: transformRefs, + } + ); const transforms: TransformInstallation[] = transformPaths.map((path: string) => { const content = JSON.parse(getAsset(path).toString('utf-8')); @@ -95,21 +104,17 @@ export const installTransform = async ( } if (previousInstalledTransformEsAssets.length > 0) { - const currentInstallation = await getInstallation({ + esReferences = await updateEsAssetReferences( savedObjectsClient, - pkgName: installablePackage.name, - }); - - // remove the saved object reference - await deleteTransformRefs( - savedObjectsClient, - currentInstallation?.installed_es || [], installablePackage.name, - previousInstalledTransformEsAssets.map((asset) => asset.id), - installedTransforms.map((installed) => installed.id) + esReferences, + { + assetsToRemove: previousInstalledTransformEsAssets, + } ); } - return installedTransforms; + + return { installedTransforms, esReferences }; }; export const isTransform = (path: string) => { diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform.test.ts index 16384b8bfba19..74e49031861c1 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform.test.ts @@ -34,8 +34,10 @@ import { appContextService } from '../../../app_context'; import { getESAssetMetadata } from '../meta'; -import { installTransform } from './install'; +import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../../constants'; + import { getAsset } from './common'; +import { installTransform } from './install'; describe('test transform install', () => { let esClient: ReturnType; @@ -46,6 +48,12 @@ describe('test transform install', () => { (getInstallation as jest.MockedFunction).mockReset(); (getInstallationObject as jest.MockedFunction).mockReset(); savedObjectsClient = savedObjectsClientMock.create(); + savedObjectsClient.update.mockImplementation(async (type, id, attributes) => ({ + type: PACKAGES_SAVED_OBJECT_TYPE, + id: 'endpoint', + attributes, + references: [], + })); }); afterEach(() => { @@ -158,7 +166,8 @@ describe('test transform install', () => { ], esClient, savedObjectsClient, - loggerMock.create() + loggerMock.create(), + previousInstallation.installed_es ); expect(esClient.transform.getTransform.mock.calls).toEqual([ @@ -255,6 +264,9 @@ describe('test transform install', () => { }, ], }, + { + refresh: false, + }, ], [ 'epm-packages', @@ -266,15 +278,18 @@ describe('test transform install', () => { type: 'ingest_pipeline', }, { - id: 'endpoint.metadata_current-default-0.16.0-dev.0', + id: 'endpoint.metadata-default-0.16.0-dev.0', type: 'transform', }, { - id: 'endpoint.metadata-default-0.16.0-dev.0', + id: 'endpoint.metadata_current-default-0.16.0-dev.0', type: 'transform', }, ], }, + { + refresh: false, + }, ], ]); }); @@ -331,7 +346,8 @@ describe('test transform install', () => { ['endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/default.json'], esClient, savedObjectsClient, - loggerMock.create() + loggerMock.create(), + previousInstallation.installed_es ); const meta = getESAssetMetadata({ packageName: 'endpoint' }); @@ -363,6 +379,9 @@ describe('test transform install', () => { { id: 'endpoint.metadata_current-default-0.16.0-dev.0', type: 'transform' }, ], }, + { + refresh: false, + }, ], ]); }); @@ -443,7 +462,8 @@ describe('test transform install', () => { [], esClient, savedObjectsClient, - loggerMock.create() + loggerMock.create(), + previousInstallation.installed_es ); expect(esClient.transform.getTransform.mock.calls).toEqual([ @@ -492,6 +512,9 @@ describe('test transform install', () => { { installed_es: [], }, + { + refresh: false, + }, ], ]); }); @@ -559,7 +582,8 @@ describe('test transform install', () => { ['endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/default.json'], esClient, savedObjectsClient, - loggerMock.create() + loggerMock.create(), + previousInstallation.installed_es ); const meta = getESAssetMetadata({ packageName: 'endpoint' }); @@ -586,6 +610,9 @@ describe('test transform install', () => { { id: 'endpoint.metadata_current-default-0.16.0-dev.0', type: 'transform' }, ], }, + { + refresh: false, + }, ], ]); }); diff --git a/x-pack/plugins/fleet/server/services/epm/fields/field.ts b/x-pack/plugins/fleet/server/services/epm/fields/field.ts index f1ad96504594e..0e00840b0c74e 100644 --- a/x-pack/plugins/fleet/server/services/epm/fields/field.ts +++ b/x-pack/plugins/fleet/server/services/epm/fields/field.ts @@ -261,12 +261,12 @@ const isFields = (path: string) => { * Gets all field files, optionally filtered by dataset, extracts .yml files, merges them together */ -export const loadFieldsFromYaml = async ( - pkg: PackageInfo, +export const loadFieldsFromYaml = ( + pkg: Pick, datasetName?: string -): Promise => { +): Field[] => { // Fetch all field definition files - const fieldDefinitionFiles = await getAssetsData(pkg, isFields, datasetName); + const fieldDefinitionFiles = getAssetsData(pkg, isFields, datasetName); return fieldDefinitionFiles.reduce((acc, file) => { // Make sure it is defined as it is optional. Should never happen. if (file.buffer) { diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts index ce8d7e7be2bb9..b9582ce1cf148 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts @@ -21,9 +21,11 @@ import { partition } from 'lodash'; import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../../../common'; import { getAsset, getPathParts } from '../../archive'; import { KibanaAssetType, KibanaSavedObjectType } from '../../../../types'; -import type { AssetType, AssetReference, AssetParts } from '../../../../types'; +import type { AssetType, AssetReference, AssetParts, Installation } from '../../../../types'; import { savedObjectTypes } from '../../packages'; import { indexPatternTypes, getIndexPatternSavedObjects } from '../index_pattern/install'; +import { saveKibanaAssetsRefs } from '../../packages/install'; +import { deleteKibanaSavedObjectsAssets } from '../../packages/remove'; type SavedObjectsImporterContract = Pick; const formatImportErrorsForLog = (errors: SavedObjectsImportFailure[]) => @@ -121,6 +123,41 @@ export async function installKibanaAssets(options: { return installedAssets; } + +export async function installKibanaAssetsAndReferences({ + savedObjectsClient, + savedObjectsImporter, + logger, + pkgName, + paths, + installedPkg, +}: { + savedObjectsClient: SavedObjectsClientContract; + savedObjectsImporter: Pick; + logger: Logger; + pkgName: string; + paths: string[]; + installedPkg?: SavedObject; +}) { + const kibanaAssets = await getKibanaAssets(paths); + if (installedPkg) await deleteKibanaSavedObjectsAssets({ savedObjectsClient, installedPkg }); + // save new kibana refs before installing the assets + const installedKibanaAssetsRefs = await saveKibanaAssetsRefs( + savedObjectsClient, + pkgName, + kibanaAssets + ); + + await installKibanaAssets({ + logger, + savedObjectsImporter, + pkgName, + kibanaAssets, + }); + + return installedKibanaAssetsRefs; +} + export const deleteKibanaInstalledRefs = async ( savedObjectsClient: SavedObjectsClientContract, pkgName: string, @@ -230,6 +267,7 @@ export async function installKibanaSavedObjects({ overwrite: true, readStream: createListStream(toBeSavedObjects), createNewCopies: false, + refresh: false, }) ); diff --git a/x-pack/plugins/fleet/server/services/epm/package_service.test.ts b/x-pack/plugins/fleet/server/services/epm/package_service.test.ts index 31bf9e47a4ae0..782af2860d2e3 100644 --- a/x-pack/plugins/fleet/server/services/epm/package_service.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/package_service.test.ts @@ -50,6 +50,7 @@ function getTest( spy: jest.SpyInstance; spyArgs: any[]; spyResponse: any; + expectedReturnValue: any; }; switch (testKey) { @@ -65,6 +66,7 @@ function getTest( }, ], spyResponse: { name: 'getInstallation test' }, + expectedReturnValue: { name: 'getInstallation test' }, }; break; case testKeys[1]: @@ -82,6 +84,7 @@ function getTest( }, ], spyResponse: { name: 'ensureInstalledPackage test' }, + expectedReturnValue: { name: 'ensureInstalledPackage test' }, }; break; case testKeys[2]: @@ -91,6 +94,7 @@ function getTest( spy: jest.spyOn(epmRegistry, 'fetchFindLatestPackageOrThrow'), spyArgs: ['package name'], spyResponse: { name: 'fetchFindLatestPackage test' }, + expectedReturnValue: { name: 'fetchFindLatestPackage test' }, }; break; case testKeys[3]: @@ -103,6 +107,10 @@ function getTest( packageInfo: { name: 'getRegistryPackage test' }, paths: ['/some/test/path'], }, + expectedReturnValue: { + packageInfo: { name: 'getRegistryPackage test' }, + paths: ['/some/test/path'], + }, }; break; case testKeys[4]: @@ -122,7 +130,14 @@ function getTest( args: [pkg, paths], spy: jest.spyOn(epmTransformsInstall, 'installTransform'), spyArgs: [pkg, paths, mocks.esClient, mocks.soClient, mocks.logger], - spyResponse: [ + spyResponse: { + installedTransforms: [ + { + name: 'package name', + }, + ], + }, + expectedReturnValue: [ { name: 'package name', }, @@ -176,10 +191,13 @@ describe('PackageService', () => { soClient: mockSoClient, logger: mockLogger, }; - const { method, args, spy, spyArgs, spyResponse } = getTest(mockClients, testKey); + const { method, args, spy, spyArgs, spyResponse, expectedReturnValue } = getTest( + mockClients, + testKey + ); spy.mockResolvedValue(spyResponse); - await expect(method(...args)).resolves.toEqual(spyResponse); + await expect(method(...args)).resolves.toEqual(expectedReturnValue); expect(spy).toHaveBeenCalledWith(...spyArgs); }); }); @@ -193,10 +211,13 @@ describe('PackageService', () => { soClient: mockSoClient, logger: mockLogger, }; - const { method, args, spy, spyArgs, spyResponse } = getTest(mockClients, testKey); + const { method, args, spy, spyArgs, spyResponse, expectedReturnValue } = getTest( + mockClients, + testKey + ); spy.mockResolvedValue(spyResponse); - await expect(method(...args)).resolves.toEqual(spyResponse); + await expect(method(...args)).resolves.toEqual(expectedReturnValue); expect(spy).toHaveBeenCalledWith(...spyArgs); }); }); 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 573ca3508e947..e16d4954f0b9d 100644 --- a/x-pack/plugins/fleet/server/services/epm/package_service.ts +++ b/x-pack/plugins/fleet/server/services/epm/package_service.ts @@ -146,14 +146,15 @@ class PackageClientImpl implements PackageClient { return installedAssets; } - #reinstallTransforms(packageInfo: InstallablePackage, paths: string[]) { - return installTransform( + async #reinstallTransforms(packageInfo: InstallablePackage, paths: string[]) { + const { installedTransforms } = await installTransform( packageInfo, paths, this.internalEsClient, this.internalSoClient, this.logger ); + return installedTransforms; } #runPreflight() { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.test.ts index c0e4404345902..db9803ea70f3a 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.test.ts @@ -21,16 +21,15 @@ jest.mock('./install'); jest.mock('./get'); import { updateCurrentWriteIndices } from '../elasticsearch/template/template'; -import { installKibanaAssets } from '../kibana/assets/install'; +import { installKibanaAssetsAndReferences } from '../kibana/assets/install'; import { _installPackage } from './_install_package'; const mockedUpdateCurrentWriteIndices = updateCurrentWriteIndices as jest.MockedFunction< typeof updateCurrentWriteIndices >; -const mockedGetKibanaAssets = installKibanaAssets as jest.MockedFunction< - typeof installKibanaAssets ->; +const mockedInstallKibanaAssetsAndReferences = + installKibanaAssetsAndReferences as jest.MockedFunction; function sleep(millis: number) { return new Promise((resolve) => setTimeout(resolve, millis)); @@ -50,7 +49,7 @@ describe('_installPackage', () => { }); it('handles errors from installKibanaAssets', async () => { // force errors from this function - mockedGetKibanaAssets.mockImplementation(async () => { + mockedInstallKibanaAssetsAndReferences.mockImplementation(async () => { throw new Error('mocked async error A: should be caught'); }); diff --git a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts index 34ada19685f83..0124bff41736f 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts @@ -22,16 +22,15 @@ import { import type { InstallablePackage, InstallSource, PackageAssetReference } from '../../../../common'; import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../constants'; import type { AssetReference, Installation, InstallType } from '../../../types'; -import { installTemplates } from '../elasticsearch/template/install'; +import { prepareToInstallTemplates } from '../elasticsearch/template/install'; import { removeLegacyTemplates } from '../elasticsearch/template/remove_legacy'; import { - installPipelines, + prepareToInstallPipelines, isTopLevelPipeline, deletePreviousPipelines, } from '../elasticsearch/ingest_pipeline'; -import { getAllTemplateRefs } from '../elasticsearch/template/install'; import { installILMPolicy } from '../elasticsearch/ilm/install'; -import { installKibanaAssets, getKibanaAssets } from '../kibana/assets/install'; +import { installKibanaAssetsAndReferences } from '../kibana/assets/install'; import { updateCurrentWriteIndices } from '../elasticsearch/template/template'; import { installTransform } from '../elasticsearch/transform/install'; import { installMlModel } from '../elasticsearch/ml_model'; @@ -40,13 +39,12 @@ import { saveArchiveEntries } from '../archive/storage'; import { ConcurrentInstallOperationError } from '../../../errors'; import { packagePolicyService } from '../..'; -import { createInstallation, saveKibanaAssetsRefs, updateVersion } from './install'; -import { deleteKibanaSavedObjectsAssets } from './remove'; +import { createInstallation, updateEsAssetReferences } from './install'; +import { withPackageSpan } from './utils'; // this is only exported for testing // use a leading underscore to indicate it's not the supported path // only the more explicit `installPackage*` functions should be used - export async function _installPackage({ savedObjectsClient, savedObjectsImporter, @@ -106,62 +104,88 @@ export async function _installPackage({ }); } - const kibanaAssets = await getKibanaAssets(paths); - if (installedPkg) await deleteKibanaSavedObjectsAssets({ savedObjectsClient, installedPkg }); - // save new kibana refs before installing the assets - const installedKibanaAssetsRefs = await saveKibanaAssetsRefs( - savedObjectsClient, - pkgName, - kibanaAssets + const kibanaAssetPromise = withPackageSpan('Install Kibana assets', () => + installKibanaAssetsAndReferences({ + savedObjectsClient, + savedObjectsImporter, + pkgName, + paths, + installedPkg, + logger, + }) ); + // Necessary to avoid async promise rejection warning + // See https://stackoverflow.com/questions/40920179/should-i-refrain-from-handling-promise-rejection-asynchronously + kibanaAssetPromise.catch(() => {}); - await installKibanaAssets({ - logger, - savedObjectsImporter, - pkgName, - kibanaAssets, - }); + // Use a shared array that is updated by each operation. This allows each operation to accurately update the + // installation object with it's references without requiring a refresh of the SO index on each update (faster). + let esReferences = installedPkg?.attributes.installed_es ?? []; // the rest of the installation must happen in sequential order // currently only the base package has an ILM policy // at some point ILM policies can be installed/modified // per data stream and we should then save them - await installILMPolicy(packageInfo, paths, esClient, logger); - - const installedDataStreamIlm = await installIlmForDataStream( - packageInfo, - paths, - esClient, - savedObjectsClient, - logger + esReferences = await withPackageSpan('Install ILM policies', () => + installILMPolicy(packageInfo, paths, esClient, savedObjectsClient, logger, esReferences) ); + ({ esReferences } = await withPackageSpan('Install Data Stream ILM policies', () => + installIlmForDataStream( + packageInfo, + paths, + esClient, + savedObjectsClient, + logger, + esReferences + ) + )); + // installs ml models - const installedMlModel = await installMlModel( - packageInfo, - paths, - esClient, - savedObjectsClient, - logger + esReferences = await withPackageSpan('Install ML models', () => + installMlModel(packageInfo, paths, esClient, savedObjectsClient, logger, esReferences) ); - // installs versionized pipelines without removing currently installed ones - const installedPipelines = await installPipelines( - packageInfo, - paths, - esClient, + /** + * In order to install assets in parallel, we need to split the preparation step from the installation step. This + * allows us to know which asset references are going to be installed so that we can save them on the packages + * SO before installation begins. In the case of a failure during installing any individual asset, we'll have the + * references necessary to remove any assets in that were successfully installed during the rollback phase. + * + * This split of prepare/install could be extended to all asset types. Besides performance, it also allows us to + * more easily write unit tests against the asset generation code without needing to mock ES responses. + */ + const preparedIngestPipelines = prepareToInstallPipelines(packageInfo, paths); + const preparedIndexTemplates = prepareToInstallTemplates(packageInfo, paths, esReferences); + + // Update the references for the templates and ingest pipelines together. Need to be done togther to avoid race + // conditions on updating the installed_es field at the same time + // These must be saved before we actually attempt to install the templates or pipelines so that we know what to + // cleanup in the case that a single asset fails to install. + esReferences = await updateEsAssetReferences( savedObjectsClient, - logger - ); - // install or update the templates referencing the newly installed pipelines - const installedTemplates = await installTemplates( - packageInfo, - esClient, - logger, - paths, - savedObjectsClient + packageInfo.name, + esReferences, + { + assetsToRemove: preparedIndexTemplates.assetsToRemove, + assetsToAdd: [ + ...preparedIngestPipelines.assetsToAdd, + ...preparedIndexTemplates.assetsToAdd, + ], + } ); + // Install index templates and ingest pipelines in parallel since they typically take the longest + const [installedTemplates] = await Promise.all([ + withPackageSpan('Install index templates', () => + preparedIndexTemplates.install(esClient, logger) + ), + // installs versionized pipelines without removing currently installed ones + withPackageSpan('Install ingest pipelines', () => + preparedIngestPipelines.install(esClient, logger) + ), + ]); + try { await removeLegacyTemplates({ packageInfo, esClient, logger }); } catch (e) { @@ -169,16 +193,14 @@ export async function _installPackage({ } // update current backing indices of each data stream - await updateCurrentWriteIndices(esClient, logger, installedTemplates); - - const installedTransforms = await installTransform( - packageInfo, - paths, - esClient, - savedObjectsClient, - logger + await withPackageSpan('Update write indices', () => + updateCurrentWriteIndices(esClient, logger, installedTemplates) ); + ({ esReferences } = await withPackageSpan('Install transforms', () => + installTransform(packageInfo, paths, esClient, savedObjectsClient, logger, esReferences) + )); + // If this is an update or retrying an update, delete the previous version's pipelines // Top-level pipeline assets will not be removed on upgrade as of ml model package addition which requires previous // assets to remain installed. This is a temporary solution - more robust solution tracked here https://github.com/elastic/kibana/issues/115035 @@ -187,30 +209,38 @@ export async function _installPackage({ (installType === 'update' || installType === 'reupdate') && installedPkg ) { - await deletePreviousPipelines( - esClient, - savedObjectsClient, - pkgName, - installedPkg.attributes.version + esReferences = await withPackageSpan('Delete previous ingest pipelines', () => + deletePreviousPipelines( + esClient, + savedObjectsClient, + pkgName, + installedPkg!.attributes.version, + esReferences + ) ); } // pipelines from a different version may have installed during a failed update if (installType === 'rollback' && installedPkg) { - await deletePreviousPipelines( - esClient, - savedObjectsClient, - pkgName, - installedPkg.attributes.install_version + esReferences = await withPackageSpan('Delete previous ingest pipelines', () => + deletePreviousPipelines( + esClient, + savedObjectsClient, + pkgName, + installedPkg!.attributes.install_version, + esReferences + ) ); } - const installedTemplateRefs = getAllTemplateRefs(installedTemplates); - const packageAssetResults = await saveArchiveEntries({ - savedObjectsClient, - paths, - packageInfo, - installSource, - }); + const installedKibanaAssetsRefs = await kibanaAssetPromise; + const packageAssetResults = await withPackageSpan('Update archive entries', () => + saveArchiveEntries({ + savedObjectsClient, + paths, + packageInfo, + installSource, + }) + ); const packageAssetRefs: PackageAssetReference[] = packageAssetResults.saved_objects.map( (result) => ({ id: result.id, @@ -218,39 +248,30 @@ export async function _installPackage({ }) ); - // update to newly installed version when all assets are successfully installed - if (installedPkg) await updateVersion(savedObjectsClient, pkgName, pkgVersion); - - const updatedPackage = await savedObjectsClient.update( - PACKAGES_SAVED_OBJECT_TYPE, - pkgName, - { + const updatedPackage = await withPackageSpan('Update install status', () => + savedObjectsClient.update(PACKAGES_SAVED_OBJECT_TYPE, pkgName, { + version: pkgVersion, install_version: pkgVersion, install_status: 'installed', package_assets: packageAssetRefs, - } + }) ); // If the package is flagged with the `keep_policies_up_to_date` flag, upgrade its // associated package policies after installation if (updatedPackage.attributes.keep_policies_up_to_date) { - const policyIdsToUpgrade = await packagePolicyService.listIds(savedObjectsClient, { - page: 1, - perPage: SO_SEARCH_LIMIT, - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${pkgName}`, - }); + await withPackageSpan('Upgrade package policies', async () => { + const policyIdsToUpgrade = await packagePolicyService.listIds(savedObjectsClient, { + page: 1, + perPage: SO_SEARCH_LIMIT, + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${pkgName}`, + }); - await packagePolicyService.upgrade(savedObjectsClient, esClient, policyIdsToUpgrade.items); + await packagePolicyService.upgrade(savedObjectsClient, esClient, policyIdsToUpgrade.items); + }); } - return [ - ...installedKibanaAssetsRefs, - ...installedPipelines, - ...installedDataStreamIlm, - ...installedTemplateRefs, - ...installedTransforms, - ...installedMlModel, - ]; + return [...installedKibanaAssetsRefs, ...esReferences]; } catch (err) { if (savedObjectsClient.errors.isConflictError(err)) { throw new ConcurrentInstallOperationError( diff --git a/x-pack/plugins/fleet/server/services/epm/packages/assets.ts b/x-pack/plugins/fleet/server/services/epm/packages/assets.ts index c939ce093a65c..d67e76f90e551 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/assets.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/assets.ts @@ -17,7 +17,7 @@ import type { ArchiveEntry } from '../archive'; // and different package and version structure export function getAssets( - packageInfo: PackageInfo, + packageInfo: Pick, filter = (path: string): boolean => true, datasetName?: string ): string[] { @@ -51,11 +51,11 @@ export function getAssets( // ASK: Does getAssetsData need an installSource now? // if so, should it be an Installation vs InstallablePackage or add another argument? -export async function getAssetsData( - packageInfo: PackageInfo, +export function getAssetsData( + packageInfo: Pick, filter = (path: string): boolean => true, datasetName?: string -): Promise { +): ArchiveEntry[] { // Gather all asset data const assets = getAssets(packageInfo, filter, datasetName); const entries: ArchiveEntry[] = assets.map((path) => { 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 3ecec951dde7e..c7fc01c89eb06 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -5,6 +5,7 @@ * 2.0. */ +import apm from 'elastic-apm-node'; import { i18n } from '@kbn/i18n'; import semverLt from 'semver/functions/lt'; import type Boom from '@hapi/boom'; @@ -16,6 +17,8 @@ import type { import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants'; +import pRetry from 'p-retry'; + import { generateESIndexPatterns } from '../elasticsearch/template/template'; import type { BulkInstallPackageInfo, @@ -28,13 +31,7 @@ import { IngestManagerError, PackageOutdatedError } from '../../../errors'; import { PACKAGES_SAVED_OBJECT_TYPE, MAX_TIME_COMPLETE_INSTALL } from '../../../constants'; import type { KibanaAssetType } from '../../../types'; import { licenseService } from '../..'; -import type { - Installation, - AssetType, - EsAssetReference, - InstallType, - InstallResult, -} from '../../../types'; +import type { Installation, EsAssetReference, InstallType, InstallResult } from '../../../types'; import { appContextService } from '../../app_context'; import * as Registry from '../registry'; import { @@ -250,6 +247,10 @@ async function installPackageFromRegistry({ // TODO: change epm API to /packageName/version so we don't need to do this const { pkgName, pkgVersion } = Registry.splitPkgKey(pkgkey); + // Workaround apm issue with async spans: https://github.com/elastic/apm-agent-nodejs/issues/2611 + await Promise.resolve(); + const span = apm.startSpan(`Install package from registry ${pkgName}@${pkgVersion}`, 'package'); + // if an error happens during getInstallType, report that we don't know let installType: InstallType = 'unknown'; @@ -260,11 +261,20 @@ async function installPackageFromRegistry({ const installedPkg = await getInstallationObject({ savedObjectsClient, pkgName }); installType = getInstallType({ pkgVersion, installedPkg }); - // get latest package version - const latestPackage = await Registry.fetchFindLatestPackageOrThrow(pkgName, { - ignoreConstraints, + span?.addLabels({ + packageName: pkgName, + packageVersion: pkgVersion, + installType, }); + // get latest package version and requested version in parallel for performance + const [latestPackage, { paths, packageInfo }] = await Promise.all([ + Registry.fetchFindLatestPackageOrThrow(pkgName, { + ignoreConstraints, + }), + Registry.getRegistryPackage(pkgName, pkgVersion), + ]); + // let the user install if using the force flag or needing to reinstall or install a previous version due to failed update const installOutOfDateVersionOk = force || ['reinstall', 'reupdate', 'rollback'].includes(installType); @@ -308,9 +318,6 @@ async function installPackageFromRegistry({ ); } - // get package info - const { paths, packageInfo } = await Registry.getRegistryPackage(pkgName, pkgVersion); - if (!licenseService.hasAtLeast(packageInfo.license || 'basic')) { const err = new Error(`Requires ${packageInfo.license} license`); sendEvent({ @@ -326,7 +333,7 @@ async function installPackageFromRegistry({ // try installing the package, if there was an error, call error handler and rethrow // @ts-expect-error status is string instead of InstallResult.status 'installed' | 'already_installed' - return _installPackage({ + return await _installPackage({ savedObjectsClient, savedObjectsImporter, esClient, @@ -377,6 +384,8 @@ async function installPackageFromRegistry({ installType, installSource: 'registry', }; + } finally { + span?.end(); } } @@ -395,6 +404,10 @@ async function installPackageByUpload({ contentType, spaceId, }: InstallUploadedArchiveParams): Promise { + // Workaround apm issue with async spans: https://github.com/elastic/apm-agent-nodejs/issues/2611 + await Promise.resolve(); + const span = apm.startSpan(`Install package from upload`, 'package'); + const logger = appContextService.getLogger(); // if an error happens during getInstallType, report that we don't know let installType: InstallType = 'unknown'; @@ -409,6 +422,12 @@ async function installPackageByUpload({ installType = getInstallType({ pkgVersion: packageInfo.version, installedPkg }); + span?.addLabels({ + packageName: packageInfo.name, + packageVersion: packageInfo.version, + installType, + }); + telemetryEvent.packageName = packageInfo.name; telemetryEvent.newVersion = packageInfo.version; telemetryEvent.installType = installType; @@ -434,7 +453,7 @@ async function installPackageByUpload({ .createImporter(savedObjectsClient); // @ts-expect-error status is string instead of InstallResult.status 'installed' | 'already_installed' - return _installPackage({ + return await _installPackage({ savedObjectsClient, savedObjectsImporter, esClient, @@ -466,6 +485,8 @@ async function installPackageByUpload({ errorMessage: e.message, }); return { error: e, installType, installSource: 'upload' }; + } finally { + span?.end(); } } @@ -607,22 +628,60 @@ export const saveKibanaAssetsRefs = async ( kibanaAssets: Record ) => { const assetRefs = Object.values(kibanaAssets).flat().map(toAssetReference); - await savedObjectsClient.update(PACKAGES_SAVED_OBJECT_TYPE, pkgName, { - installed_kibana: assetRefs, - }); + // Because Kibana assets are installed in parallel with ES assets with refresh: false, we almost always run into an + // issue that causes a conflict error due to this issue: https://github.com/elastic/kibana/issues/126240. This is safe + // to retry constantly until it succeeds to optimize this critical user journey path as much as possible. + pRetry( + () => + savedObjectsClient.update( + PACKAGES_SAVED_OBJECT_TYPE, + pkgName, + { + installed_kibana: assetRefs, + }, + { refresh: false } + ), + { retries: 20 } // Use a number of retries higher than the number of es asset update operations + ); + return assetRefs; }; -export const saveInstalledEsRefs = async ( +/** + * Utility function for updating the installed_es field of a package + */ +export const updateEsAssetReferences = async ( savedObjectsClient: SavedObjectsClientContract, pkgName: string, - installedAssets: EsAssetReference[] -) => { - const installedPkg = await getInstallationObject({ savedObjectsClient, pkgName }); - const installedAssetsToSave = installedPkg?.attributes.installed_es.concat(installedAssets); + currentAssets: EsAssetReference[], + { + assetsToAdd = [], + assetsToRemove = [], + refresh = false, + }: { + assetsToAdd?: EsAssetReference[]; + assetsToRemove?: EsAssetReference[]; + /** + * Whether or not the update should force a refresh on the SO index. + * Defaults to `false` for faster updates, should only be `wait_for` if the update needs to be queried back from ES + * immediately. + */ + refresh?: 'wait_for' | false; + } +): Promise => { + const withAssetsRemoved = currentAssets.filter(({ type, id }) => { + if ( + assetsToRemove.some( + ({ type: removeType, id: removeId }) => removeType === type && removeId === id + ) + ) { + return false; + } + return true; + }); const deduplicatedAssets = - installedAssetsToSave?.reduce((acc, currentAsset) => { + [...withAssetsRemoved, ...assetsToAdd].reduce((acc, currentAsset) => { const foundAsset = acc.find((asset: EsAssetReference) => asset.id === currentAsset.id); if (!foundAsset) { return acc.concat([currentAsset]); @@ -631,27 +690,30 @@ export const saveInstalledEsRefs = async ( } }, [] as EsAssetReference[]) || []; - await savedObjectsClient.update(PACKAGES_SAVED_OBJECT_TYPE, pkgName, { - installed_es: deduplicatedAssets, - }); - return installedAssets; -}; - -export const removeAssetTypesFromInstalledEs = async ( - savedObjectsClient: SavedObjectsClientContract, - pkgName: string, - assetTypes: AssetType[] -) => { - const installedPkg = await getInstallationObject({ savedObjectsClient, pkgName }); - const installedAssets = installedPkg?.attributes.installed_es; - if (!installedAssets?.length) return; - const installedAssetsToSave = installedAssets?.filter( - (asset) => !assetTypes.includes(asset.type) - ); + const { + attributes: { installed_es: updatedAssets }, + } = + // Because Kibana assets are installed in parallel with ES assets with refresh: false, we almost always run into an + // issue that causes a conflict error due to this issue: https://github.com/elastic/kibana/issues/126240. This is safe + // to retry constantly until it succeeds to optimize this critical user journey path as much as possible. + await pRetry( + () => + savedObjectsClient.update( + PACKAGES_SAVED_OBJECT_TYPE, + pkgName, + { + installed_es: deduplicatedAssets, + }, + { + refresh, + } + ), + // Use a lower number of retries for ES assets since they're installed in serial and can only conflict with + // the single Kibana update call. + { retries: 5 } + ); - return savedObjectsClient.update(PACKAGES_SAVED_OBJECT_TYPE, pkgName, { - installed_es: installedAssetsToSave, - }); + return updatedAssets ?? []; }; export async function ensurePackagesCompletedInstall( diff --git a/x-pack/plugins/fleet/server/services/epm/packages/remove.ts b/x-pack/plugins/fleet/server/services/epm/packages/remove.ts index 7edf5b6020be8..95e65acfebef6 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/remove.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/remove.ts @@ -130,6 +130,8 @@ function deleteESAssets( return deleteTransforms(esClient, [id]); } else if (assetType === ElasticsearchAssetType.dataStreamIlmPolicy) { return deleteIlms(esClient, [id]); + } else if (assetType === ElasticsearchAssetType.ilmPolicy) { + return deleteIlms(esClient, [id]); } else if (assetType === ElasticsearchAssetType.mlModel) { return deleteMlModel(esClient, [id]); } diff --git a/x-pack/plugins/fleet/server/services/epm/packages/utils.ts b/x-pack/plugins/fleet/server/services/epm/packages/utils.ts new file mode 100644 index 0000000000000..0cb97ca007daf --- /dev/null +++ b/x-pack/plugins/fleet/server/services/epm/packages/utils.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { withSpan } from '@kbn/apm-utils'; + +export const withPackageSpan = (stepName: string, func: () => Promise) => + withSpan({ name: stepName, type: 'package' }, func); diff --git a/x-pack/plugins/fleet/server/services/epm/registry/index.ts b/x-pack/plugins/fleet/server/services/epm/registry/index.ts index 182f20297afd5..1074e975d3f6f 100644 --- a/x-pack/plugins/fleet/server/services/epm/registry/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/registry/index.ts @@ -37,6 +37,8 @@ import { PackageNotFoundError, PackageCacheError, RegistryResponseError } from ' import { getBundledPackageByName } from '../packages/bundled_packages'; +import { withPackageSpan } from '../packages/utils'; + import { fetchUrl, getResponse, getResponseStream } from './requests'; import { getRegistryUrl } from './registry_url'; @@ -75,42 +77,44 @@ async function _fetchFindLatestPackage( packageName: string, options?: FetchFindLatestPackageOptions ) { - const logger = appContextService.getLogger(); - const { ignoreConstraints = false } = options ?? {}; + return withPackageSpan(`Find latest package ${packageName}`, async () => { + const logger = appContextService.getLogger(); + const { ignoreConstraints = false } = options ?? {}; - const bundledPackage = await getBundledPackageByName(packageName); + const bundledPackage = await getBundledPackageByName(packageName); - const registryUrl = getRegistryUrl(); - const url = new URL(`${registryUrl}/search?package=${packageName}&experimental=true`); + const registryUrl = getRegistryUrl(); + const url = new URL(`${registryUrl}/search?package=${packageName}&experimental=true`); - if (!ignoreConstraints) { - setKibanaVersion(url); - } + if (!ignoreConstraints) { + setKibanaVersion(url); + } - try { - const res = await fetchUrl(url.toString(), 1); - const searchResults: RegistryPackage[] = JSON.parse(res); + try { + const res = await fetchUrl(url.toString(), 1); + const searchResults: RegistryPackage[] = JSON.parse(res); - const latestPackageFromRegistry = searchResults[0] ?? null; + const latestPackageFromRegistry = searchResults[0] ?? null; - if (bundledPackage && semverGte(bundledPackage.version, latestPackageFromRegistry.version)) { - return bundledPackage; - } + if (bundledPackage && semverGte(bundledPackage.version, latestPackageFromRegistry.version)) { + return bundledPackage; + } - return latestPackageFromRegistry; - } catch (error) { - logger.error( - `Failed to fetch latest version of ${packageName} from registry: ${error.message}` - ); + return latestPackageFromRegistry; + } catch (error) { + logger.error( + `Failed to fetch latest version of ${packageName} from registry: ${error.message}` + ); - // Fall back to the bundled version of the package if it exists - if (bundledPackage) { - return bundledPackage; - } + // Fall back to the bundled version of the package if it exists + if (bundledPackage) { + return bundledPackage; + } - // Otherwise, return null and allow callers to determine whether they'll consider this an error or not - return null; - } + // Otherwise, return null and allow callers to determine whether they'll consider this an error or not + return null; + } + }); } export async function fetchFindLatestPackageOrThrow( @@ -148,7 +152,8 @@ export async function fetchFindLatestPackageOrUndefined( export async function fetchInfo(pkgName: string, pkgVersion: string): Promise { const registryUrl = getRegistryUrl(); try { - const res = await fetchUrl(`${registryUrl}/package/${pkgName}/${pkgVersion}`).then(JSON.parse); + // Trailing slash avoids 301 redirect / extra hop + const res = await fetchUrl(`${registryUrl}/package/${pkgName}/${pkgVersion}/`).then(JSON.parse); return res; } catch (err) { @@ -207,12 +212,14 @@ export async function fetchCategories( } export async function getInfo(name: string, version: string) { - let packageInfo = getPackageInfo({ name, version }); - if (!packageInfo) { - packageInfo = await fetchInfo(name, version); - setPackageInfo({ name, version, packageInfo }); - } - return packageInfo as RegistryPackage; + return withPackageSpan('Fetch package info', async () => { + let packageInfo = getPackageInfo({ name, version }); + if (!packageInfo) { + packageInfo = await fetchInfo(name, version); + setPackageInfo({ name, version, packageInfo }); + } + return packageInfo as RegistryPackage; + }); } export async function getRegistryPackage( @@ -222,14 +229,19 @@ export async function getRegistryPackage( const installSource = 'registry'; let paths = getArchiveFilelist({ name, version }); if (!paths || paths.length === 0) { - const { archiveBuffer, archivePath } = await fetchArchiveBuffer(name, version); - paths = await unpackBufferToCache({ - name, - version, - installSource, - archiveBuffer, - contentType: ensureContentType(archivePath), - }); + const { archiveBuffer, archivePath } = await withPackageSpan( + 'Fetch package archive from registry', + () => fetchArchiveBuffer(name, version) + ); + paths = await withPackageSpan('Unpack archive', () => + unpackBufferToCache({ + name, + version, + installSource, + archiveBuffer, + contentType: ensureContentType(archivePath), + }) + ); } const packageInfo = await getInfo(name, version); diff --git a/x-pack/plugins/fleet/server/types/index.tsx b/x-pack/plugins/fleet/server/types/index.tsx index 6356ff8aa6cac..37dde581d4b8f 100644 --- a/x-pack/plugins/fleet/server/types/index.tsx +++ b/x-pack/plugins/fleet/server/types/index.tsx @@ -12,10 +12,6 @@ export type { AgentStatus, AgentType, AgentAction, - AgentPolicyAction, - BaseAgentActionSOAttributes, - AgentActionSOAttributes, - AgentPolicyActionSOAttributes, PackagePolicy, PackagePolicyInput, PackagePolicyInputStream, diff --git a/x-pack/plugins/graph/README.md b/x-pack/plugins/graph/README.md index a0d7eb25ff987..b17f114a8f01f 100644 --- a/x-pack/plugins/graph/README.md +++ b/x-pack/plugins/graph/README.md @@ -10,8 +10,8 @@ Graph shows only up in the side bar if your server is running on a platinum or t * Run type check `node scripts/type_check.js --project=x-pack/tsconfig.json` * Run linter `node scripts/eslint.js x-pack/plugins/graph` * Run functional tests (make sure to stop dev server) - * Server `cd x-pack && node ./scripts/functional_tests_server.js` - * Tests `cd x-pack && node ../scripts/functional_test_runner.js --config ./test/functional/config.js --grep=graph` + * Server `node ./scripts/functional_tests_server.js --config x-pack/test/functional/apps/graph/config.ts` + * Tests `node scripts/functional_test_runner.js --config x-pack/test/functional/apps/graph/config.ts` ## Folder structure diff --git a/x-pack/plugins/graph/public/components/_search_bar.scss b/x-pack/plugins/graph/public/components/_search_bar.scss index 4b41dbc9bba0b..c555c0af2d077 100644 --- a/x-pack/plugins/graph/public/components/_search_bar.scss +++ b/x-pack/plugins/graph/public/components/_search_bar.scss @@ -1,7 +1,12 @@ .gphSearchBar__datasourceButton { - height: 100% !important; + max-width: 320px; + + @include euiBreakpoint('xs', 's') { + width: 100%; + max-width: none; + } } .gphSearchBar__datasourceButtonTooltip { padding: 0; -} \ No newline at end of file +} diff --git a/x-pack/plugins/graph/public/components/search_bar.test.tsx b/x-pack/plugins/graph/public/components/search_bar.test.tsx index c05da957599c9..559ff33330eab 100644 --- a/x-pack/plugins/graph/public/components/search_bar.test.tsx +++ b/x-pack/plugins/graph/public/components/search_bar.test.tsx @@ -7,7 +7,7 @@ import { mountWithIntl } from '@kbn/test-jest-helpers'; import { SearchBar, SearchBarProps, SearchBarComponent, SearchBarStateProps } from './search_bar'; -import React, { Component, ReactElement } from 'react'; +import React, { Component } from 'react'; import { DocLinksStart, HttpStart, @@ -203,9 +203,7 @@ describe('search_bar', () => { // pick the button component out of the tree because // it's part of a popover and thus not covered by enzyme - ( - instance.find(QueryStringInput).prop('prepend') as ReactElement - ).props.children.props.onClick(); + instance.find('[data-test-subj="graphDatasourceButton"]').first().simulate('click'); expect(openSourceModal).toHaveBeenCalled(); }); diff --git a/x-pack/plugins/graph/public/components/search_bar.tsx b/x-pack/plugins/graph/public/components/search_bar.tsx index 762a2e87d2a5a..046ed05977c79 100644 --- a/x-pack/plugins/graph/public/components/search_bar.tsx +++ b/x-pack/plugins/graph/public/components/search_bar.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiButtonEmpty, EuiToolTip } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiToolTip } from '@elastic/eui'; import React, { useState, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; @@ -110,6 +110,47 @@ export function SearchBarComponent(props: SearchBarStateProps & SearchBarProps) }} > + + + { + confirmWipeWorkspace( + () => + openSourceModal({ overlays, savedObjects, uiSettings }, onIndexPatternSelected), + i18n.translate('xpack.graph.clearWorkspace.confirmText', { + defaultMessage: + 'If you change data sources, your current fields and vertices will be reset.', + }), + { + confirmButtonText: i18n.translate( + 'xpack.graph.clearWorkspace.confirmButtonLabel', + { + defaultMessage: 'Change data source', + } + ), + title: i18n.translate('xpack.graph.clearWorkspace.modalTitle', { + defaultMessage: 'Unsaved changes', + }), + } + ); + }} + > + {currentIndexPattern + ? currentIndexPattern.title + : // This branch will be shown if the user exits the + // initial picker modal + i18n.translate('xpack.graph.bar.pickSourceLabel', { + defaultMessage: 'Select a data source', + })} + + + - { - confirmWipeWorkspace( - () => - openSourceModal( - { overlays, savedObjects, uiSettings }, - onIndexPatternSelected - ), - i18n.translate('xpack.graph.clearWorkspace.confirmText', { - defaultMessage: - 'If you change data sources, your current fields and vertices will be reset.', - }), - { - confirmButtonText: i18n.translate( - 'xpack.graph.clearWorkspace.confirmButtonLabel', - { - defaultMessage: 'Change data source', - } - ), - title: i18n.translate('xpack.graph.clearWorkspace.modalTitle', { - defaultMessage: 'Unsaved changes', - }), - } - ); - }} - > - {currentIndexPattern - ? currentIndexPattern.title - : // This branch will be shown if the user exits the - // initial picker modal - i18n.translate('xpack.graph.bar.pickSourceLabel', { - defaultMessage: 'Select a data source', - })} - - - } onChange={setQuery} /> diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/snapshot_policies_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/snapshot_policies_field.tsx index 62f322e4f48f3..e89189df7667b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/snapshot_policies_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/snapshot_policies_field.tsx @@ -159,7 +159,7 @@ export const SnapshotPoliciesField: React.FunctionComponent = () => { id="xpack.indexLifecycleMgmt.editPolicy.deletePhase.waitForSnapshotDescription" defaultMessage="Specify a snapshot policy to be executed before the deletion of the index. This ensures that a snapshot of the deleted index is available." />{' '} - + } titleSize="xs" diff --git a/x-pack/plugins/infra/kibana.json b/x-pack/plugins/infra/kibana.json index 2eec94187900f..d9d432fc702e4 100644 --- a/x-pack/plugins/infra/kibana.json +++ b/x-pack/plugins/infra/kibana.json @@ -9,7 +9,6 @@ "spaces", "embeddable", "data", - "dataEnhanced", "dataViews", "visTypeTimeseries", "alerting", diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/osquery/index.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/osquery/index.tsx index 1bd6cfd353140..ce023064edee1 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/osquery/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/osquery/index.tsx @@ -48,7 +48,7 @@ const TabComponent = (props: TabProps) => { return ( - + ); }, [OsqueryAction, loading, metadata]); diff --git a/x-pack/plugins/infra/public/types.ts b/x-pack/plugins/infra/public/types.ts index ab7df75592712..b5147f343ea5b 100644 --- a/x-pack/plugins/infra/public/types.ts +++ b/x-pack/plugins/infra/public/types.ts @@ -20,7 +20,6 @@ import type { TriggersAndActionsUIPublicPluginSetup, TriggersAndActionsUIPublicPluginStart, } from '@kbn/triggers-actions-ui-plugin/public'; -import type { DataEnhancedSetup, DataEnhancedStart } from '@kbn/data-enhanced-plugin/public'; import { MlPluginSetup, MlPluginStart } from '@kbn/ml-plugin/public'; import type { ObservabilityPublicSetup, @@ -52,7 +51,6 @@ export interface InfraClientStartExports { } export interface InfraClientSetupDeps { - dataEnhanced: DataEnhancedSetup; home?: HomePublicPluginSetup; observability: ObservabilityPublicSetup; triggersActionsUi: TriggersAndActionsUIPublicPluginSetup; @@ -64,7 +62,6 @@ export interface InfraClientSetupDeps { export interface InfraClientStartDeps { data: DataPublicPluginStart; - dataEnhanced: DataEnhancedStart; dataViews: DataViewsPublicPluginStart; observability: ObservabilityPublicStart; spaces: SpacesPluginStart; diff --git a/x-pack/plugins/infra/server/lib/metrics/make_get_metric_indices.test.ts b/x-pack/plugins/infra/server/lib/metrics/make_get_metric_indices.test.ts new file mode 100644 index 0000000000000..9dedf9b5afaa0 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/metrics/make_get_metric_indices.test.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { savedObjectsClientMock } from '@kbn/core/server/mocks'; +import { defaultSourceConfiguration, InfraSource } from '../sources'; +import { createInfraSourcesMock } from '../sources/mocks'; +import { makeGetMetricIndices } from './make_get_metric_indices'; + +describe('getMetricIndices', () => { + it('should return the indices from a resolved configuration', async () => { + const sourceConfiguration: InfraSource = { + id: 'default', + origin: 'stored', + configuration: defaultSourceConfiguration, + }; + const infraSourcesMock = createInfraSourcesMock(); + infraSourcesMock.getSourceConfiguration.mockResolvedValueOnce(sourceConfiguration); + + const getMetricIndices = makeGetMetricIndices(infraSourcesMock); + + const savedObjectsClient = savedObjectsClientMock.create(); + const metricIndices = await getMetricIndices(savedObjectsClient); + + expect(metricIndices).toEqual(defaultSourceConfiguration.metricAlias); + }); +}); diff --git a/x-pack/plugins/infra/server/lib/metrics/make_get_metric_indices.ts b/x-pack/plugins/infra/server/lib/metrics/make_get_metric_indices.ts new file mode 100644 index 0000000000000..a6ff7e96a55a0 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/metrics/make_get_metric_indices.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SavedObjectsClientContract } from '@kbn/core/server'; +import type { IInfraSources } from '../sources'; + +export function makeGetMetricIndices(metricSources: IInfraSources) { + return async (savedObjectsClient: SavedObjectsClientContract, sourceId: string = 'default') => { + const source = await metricSources.getSourceConfiguration(savedObjectsClient, sourceId); + return source.configuration.metricAlias; + }; +} diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts index 8eb2bac57dc56..bfd7113ec4dc0 100644 --- a/x-pack/plugins/infra/server/plugin.ts +++ b/x-pack/plugins/infra/server/plugin.ts @@ -7,8 +7,6 @@ import { Server } from '@hapi/hapi'; import { schema } from '@kbn/config-schema'; -import { i18n } from '@kbn/i18n'; -import { Logger } from '@kbn/logging'; import { CoreStart, Plugin, @@ -16,6 +14,8 @@ import { PluginInitializerContext, } from '@kbn/core/server'; import { handleEsError } from '@kbn/es-ui-shared-plugin/server'; +import { i18n } from '@kbn/i18n'; +import { Logger } from '@kbn/logging'; import { LOGS_FEATURE_ID, METRICS_FEATURE_ID } from '../common/constants'; import { defaultLogViewsStaticConfig } from '../common/log_views'; import { publicConfigKeys } from '../common/plugin_config_types'; @@ -35,6 +35,7 @@ import { InfraFieldsDomain } from './lib/domains/fields_domain'; import { InfraLogEntriesDomain } from './lib/domains/log_entries_domain'; import { InfraMetricsDomain } from './lib/domains/metrics_domain'; import { InfraBackendLibs, InfraDomainLibs } from './lib/infra_types'; +import { makeGetMetricIndices } from './lib/metrics/make_get_metric_indices'; import { infraSourceConfigurationSavedObjectType, InfraSources } from './lib/sources'; import { InfraSourceStatus } from './lib/source_status'; import { logViewSavedObjectType } from './saved_objects'; @@ -236,6 +237,7 @@ export class InfraServerPlugin return { logViews, + getMetricIndices: makeGetMetricIndices(this.libs.sources), }; } diff --git a/x-pack/plugins/infra/server/types.ts b/x-pack/plugins/infra/server/types.ts index e3792c8977cfe..108575c0f8324 100644 --- a/x-pack/plugins/infra/server/types.ts +++ b/x-pack/plugins/infra/server/types.ts @@ -5,7 +5,11 @@ * 2.0. */ -import type { CoreSetup, CustomRequestHandlerContext } from '@kbn/core/server'; +import type { + CoreSetup, + CustomRequestHandlerContext, + SavedObjectsClientContract, +} from '@kbn/core/server'; import type { SearchRequestHandlerContext } from '@kbn/data-plugin/server'; import type { MlPluginSetup } from '@kbn/ml-plugin/server'; import type { InfraStaticSourceConfiguration } from '../common/source_configuration/source_configuration'; @@ -27,6 +31,10 @@ export interface InfraPluginSetup { export interface InfraPluginStart { logViews: LogViewsServiceStart; + getMetricIndices: ( + savedObjectsClient: SavedObjectsClientContract, + sourceId?: string + ) => Promise; } export type MlSystem = ReturnType; diff --git a/x-pack/plugins/infra/tsconfig.json b/x-pack/plugins/infra/tsconfig.json index b508b8fb6da80..370644367b441 100644 --- a/x-pack/plugins/infra/tsconfig.json +++ b/x-pack/plugins/infra/tsconfig.json @@ -24,7 +24,6 @@ { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, { "path": "../../../src/plugins/usage_collection/tsconfig.json" }, { "path": "../../../src/plugins/vis_types/timeseries/tsconfig.json" }, - { "path": "../data_enhanced/tsconfig.json" }, { "path": "../alerting/tsconfig.json" }, { "path": "../features/tsconfig.json" }, { "path": "../license_management/tsconfig.json" }, diff --git a/x-pack/plugins/lens/common/embeddable_factory/index.ts b/x-pack/plugins/lens/common/embeddable_factory/index.ts index c4b03ae280778..8ddddf654b017 100644 --- a/x-pack/plugins/lens/common/embeddable_factory/index.ts +++ b/x-pack/plugins/lens/common/embeddable_factory/index.ts @@ -7,9 +7,10 @@ import { SerializableRecord, Serializable } from '@kbn/utility-types'; import { SavedObjectReference } from '@kbn/core/types'; -import { EmbeddableStateWithType } from '@kbn/embeddable-plugin/common'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/server'; +import type { + EmbeddableStateWithType, + EmbeddableRegistryDefinition, +} from '@kbn/embeddable-plugin/common'; export type LensEmbeddablePersistableState = EmbeddableStateWithType & { attributes: SerializableRecord; diff --git a/x-pack/plugins/lens/common/expressions/counter_rate/counter_rate.test.ts b/x-pack/plugins/lens/common/expressions/counter_rate/counter_rate.test.ts index 32a37e0cf949e..9f19b5d052c68 100644 --- a/x-pack/plugins/lens/common/expressions/counter_rate/counter_rate.test.ts +++ b/x-pack/plugins/lens/common/expressions/counter_rate/counter_rate.test.ts @@ -7,8 +7,7 @@ import { counterRate, CounterRateArgs } from '.'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Datatable } from '@kbn/expressions-plugin/public'; +import { Datatable } from '@kbn/expressions-plugin/common'; import { functionWrapper } from '@kbn/expressions-plugin/common/expression_functions/specs/tests/utils'; describe('lens_counter_rate', () => { diff --git a/x-pack/plugins/lens/common/expressions/datatable/datatable.ts b/x-pack/plugins/lens/common/expressions/datatable/datatable.ts index 84aba75239ed0..4f04fc1753c82 100644 --- a/x-pack/plugins/lens/common/expressions/datatable/datatable.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/datatable.ts @@ -39,7 +39,7 @@ export const getDatatable = ( ): DatatableExpressionFunction => ({ name: 'lens_datatable', type: 'render', - inputTypes: ['lens_multitable'], + inputTypes: ['datatable'], help: i18n.translate('xpack.lens.datatable.expressionHelpLabel', { defaultMessage: 'Datatable renderer', }), diff --git a/x-pack/plugins/lens/common/expressions/datatable/datatable_fn.ts b/x-pack/plugins/lens/common/expressions/datatable/datatable_fn.ts index 713f929d74420..464c70a412397 100644 --- a/x-pack/plugins/lens/common/expressions/datatable/datatable_fn.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/datatable_fn.ts @@ -8,8 +8,8 @@ import { cloneDeep } from 'lodash'; import { i18n } from '@kbn/i18n'; import { prepareLogTable } from '@kbn/visualizations-plugin/common/utils'; -import type { DatatableColumnMeta, ExecutionContext } from '@kbn/expressions-plugin'; -import { FormatFactory, LensMultiTable } from '../../types'; +import type { Datatable, DatatableColumnMeta, ExecutionContext } from '@kbn/expressions-plugin'; +import { FormatFactory } from '../../types'; import { transposeTable } from './transpose_helpers'; import { computeSummaryRowForColumn } from './summary'; import { getSortingCriteria } from './sorting'; @@ -23,11 +23,13 @@ export const datatableFn = ( getFormatFactory: (context: ExecutionContext) => FormatFactory | Promise ): DatatableExpressionFunction['fn'] => - async (data, args, context) => { - const [firstTable] = Object.values(data.tables); + async (table, args, context) => { if (context?.inspectorAdapters?.tables) { + context.inspectorAdapters.tables.reset(); + context.inspectorAdapters.tables.allowCsvExport = true; + const logTable = prepareLogTable( - Object.values(data.tables)[0], + table, [ [ args.columns.map((column) => column.columnId), @@ -42,27 +44,27 @@ export const datatableFn = context.inspectorAdapters.tables.logDatatable('default', logTable); } - let untransposedData: LensMultiTable | undefined; + let untransposedData: Datatable | undefined; // do the sorting at this level to propagate it also at CSV download const [layerId] = Object.keys(context.inspectorAdapters.tables || {}); const formatters: Record> = {}; const formatFactory = await getFormatFactory(context); - firstTable.columns.forEach((column) => { + table.columns.forEach((column) => { formatters[column.id] = formatFactory(column.meta?.params); }); const hasTransposedColumns = args.columns.some((c) => c.isTransposed); if (hasTransposedColumns) { // store original shape of data separately - untransposedData = cloneDeep(data); + untransposedData = cloneDeep(table); // transposes table and args inplace - transposeTable(args, firstTable, formatters); + transposeTable(args, table, formatters); } const { sortingColumnId: sortBy, sortingDirection: sortDirection } = args; - const columnsReverseLookup = firstTable.columns.reduce< + const columnsReverseLookup = table.columns.reduce< Record >((memo, { id, name, meta }, i) => { memo[id] = { name, index: i, meta }; @@ -73,7 +75,7 @@ export const datatableFn = for (const column of columnsWithSummary) { column.summaryRowValue = computeSummaryRowForColumn( column, - firstTable, + table, formatters, formatFactory({ id: 'number' }) ); @@ -92,20 +94,21 @@ export const datatableFn = sortDirection ); // replace the table here - context.inspectorAdapters.tables[layerId].rows = (firstTable.rows || []) + context.inspectorAdapters.tables[layerId].rows = (table.rows || []) .slice() .sort(sortingCriteria); // replace also the local copy - firstTable.rows = context.inspectorAdapters.tables[layerId].rows; + table.rows = context.inspectorAdapters.tables[layerId].rows; } else { args.sortingColumnId = undefined; args.sortingDirection = 'none'; } + return { type: 'render', as: 'lens_datatable_renderer', value: { - data, + data: table, untransposedData, args, }, diff --git a/x-pack/plugins/lens/common/expressions/datatable/types.ts b/x-pack/plugins/lens/common/expressions/datatable/types.ts index 9e6315bb856d0..a78018fca90f6 100644 --- a/x-pack/plugins/lens/common/expressions/datatable/types.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/types.ts @@ -5,13 +5,12 @@ * 2.0. */ -import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin'; -import type { LensMultiTable } from '../../types'; +import type { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin'; import type { DatatableArgs } from './datatable'; export interface DatatableProps { - data: LensMultiTable; - untransposedData?: LensMultiTable; + data: Datatable; + untransposedData?: Datatable; args: DatatableArgs; } @@ -23,7 +22,7 @@ export interface DatatableRender { export type DatatableExpressionFunction = ExpressionFunctionDefinition< 'lens_datatable', - LensMultiTable, + Datatable, DatatableArgs, Promise >; diff --git a/x-pack/plugins/lens/common/expressions/expression_types/index.ts b/x-pack/plugins/lens/common/expressions/expression_types/index.ts deleted file mode 100644 index 78821e429fa8f..0000000000000 --- a/x-pack/plugins/lens/common/expressions/expression_types/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { lensMultitable } from './lens_multitable'; -export type { LensMultitableExpressionTypeDefinition } from './lens_multitable'; diff --git a/x-pack/plugins/lens/common/expressions/expression_types/lens_multitable.ts b/x-pack/plugins/lens/common/expressions/expression_types/lens_multitable.ts deleted file mode 100644 index 2e1ce28534a27..0000000000000 --- a/x-pack/plugins/lens/common/expressions/expression_types/lens_multitable.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. - */ - -import { ExpressionTypeDefinition } from '@kbn/expressions-plugin/common'; -import { LensMultiTable } from '../../types'; - -const name = 'lens_multitable'; - -type Input = LensMultiTable; - -export type LensMultitableExpressionTypeDefinition = ExpressionTypeDefinition< - typeof name, - Input, - Input ->; - -export const lensMultitable: LensMultitableExpressionTypeDefinition = { - name, - to: { - datatable: (input: Input) => { - return Object.values(input.tables)[0]; - }, - }, -}; diff --git a/x-pack/plugins/lens/common/expressions/format_column/format_column.test.ts b/x-pack/plugins/lens/common/expressions/format_column/format_column.test.ts index 63e32ffbf1df6..d0db49f4afaae 100644 --- a/x-pack/plugins/lens/common/expressions/format_column/format_column.test.ts +++ b/x-pack/plugins/lens/common/expressions/format_column/format_column.test.ts @@ -5,8 +5,7 @@ * 2.0. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Datatable, DatatableColumn } from '@kbn/expressions-plugin/public'; +import type { Datatable, DatatableColumn } from '@kbn/expressions-plugin/common'; import { functionWrapper } from '@kbn/expressions-plugin/common/expression_functions/specs/tests/utils'; import { formatColumn } from '.'; diff --git a/x-pack/plugins/lens/common/expressions/index.ts b/x-pack/plugins/lens/common/expressions/index.ts index 47ff8318447b2..2007a61b11bf9 100644 --- a/x-pack/plugins/lens/common/expressions/index.ts +++ b/x-pack/plugins/lens/common/expressions/index.ts @@ -8,8 +8,5 @@ export * from './counter_rate'; export * from './format_column'; export * from './rename_columns'; -export * from './merge_tables'; export * from './time_scale'; export * from './datatable'; - -export * from './expression_types'; diff --git a/x-pack/plugins/lens/common/expressions/merge_tables/index.ts b/x-pack/plugins/lens/common/expressions/merge_tables/index.ts deleted file mode 100644 index 2e0dbc4b44264..0000000000000 --- a/x-pack/plugins/lens/common/expressions/merge_tables/index.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import type { - ExpressionFunctionDefinition, - Datatable, - ExecutionContext, -} from '@kbn/expressions-plugin/common'; -import { toAbsoluteDates } from '@kbn/data-plugin/common'; -import type { ExpressionValueSearchContext } from '@kbn/data-plugin/common'; - -import type { Adapters } from '@kbn/inspector-plugin/common'; -import type { LensMultiTable } from '../../types'; - -interface MergeTables { - layerIds: string[]; - tables: Datatable[]; -} - -export const mergeTables: ExpressionFunctionDefinition< - 'lens_merge_tables', - ExpressionValueSearchContext | null, - MergeTables, - LensMultiTable, - ExecutionContext -> = { - name: 'lens_merge_tables', - type: 'lens_multitable', - help: i18n.translate('xpack.lens.functions.mergeTables.help', { - defaultMessage: - 'A helper to merge any number of kibana tables into a single table and expose it via inspector adapter', - }), - args: { - layerIds: { - types: ['string'], - help: '', - multi: true, - }, - tables: { - types: ['datatable'], - help: '', - multi: true, - }, - }, - inputTypes: ['kibana_context', 'null'], - fn(input, { layerIds, tables }, context) { - const resultTables: Record = {}; - - if (context.inspectorAdapters?.tables) { - context.inspectorAdapters.tables.reset(); - context.inspectorAdapters.tables.allowCsvExport = true; - } - - tables.forEach((table, index) => { - resultTables[layerIds[index]] = table; - }); - - return { - type: 'lens_multitable', - tables: resultTables, - dateRange: getDateRange(input), - }; - }, -}; - -function getDateRange(value?: ExpressionValueSearchContext | null) { - if (!value || !value.timeRange) { - return; - } - - const dateRange = toAbsoluteDates(value.timeRange); - - if (!dateRange) { - return; - } - - return { - fromDate: dateRange.from, - toDate: dateRange.to, - }; -} diff --git a/x-pack/plugins/lens/common/expressions/merge_tables/merge_tables.test.ts b/x-pack/plugins/lens/common/expressions/merge_tables/merge_tables.test.ts deleted file mode 100644 index 4c8a3bf9aa310..0000000000000 --- a/x-pack/plugins/lens/common/expressions/merge_tables/merge_tables.test.ts +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import moment from 'moment'; -import { mergeTables } from '.'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ExpressionValueSearchContext } from '@kbn/data-plugin/public'; -import { - Datatable, - ExecutionContext, - DefaultInspectorAdapters, - TablesAdapter, -} from '@kbn/expressions-plugin'; - -describe('lens_merge_tables', () => { - const sampleTable1: Datatable = { - type: 'datatable', - columns: [ - { id: 'bucket', name: 'A', meta: { type: 'string' } }, - { id: 'count', name: 'Count', meta: { type: 'number' } }, - ], - rows: [ - { bucket: 'a', count: 5 }, - { bucket: 'b', count: 10 }, - ], - }; - - const sampleTable2: Datatable = { - type: 'datatable', - columns: [ - { id: 'bucket', name: 'C', meta: { type: 'string' } }, - { id: 'avg', name: 'Average', meta: { type: 'number' } }, - ], - rows: [ - { bucket: 'a', avg: 2.5 }, - { bucket: 'b', avg: 9 }, - ], - }; - - it('should produce a row with the nested table as defined', () => { - expect( - mergeTables.fn( - null, - { layerIds: ['first', 'second'], tables: [sampleTable1, sampleTable2] }, - // eslint-disable-next-line - {} as any - ) - ).toEqual({ - tables: { first: sampleTable1, second: sampleTable2 }, - type: 'lens_multitable', - }); - }); - - it('should reset the current tables in the tables inspector', () => { - const adapters = { - tables: new TablesAdapter(), - } as DefaultInspectorAdapters; - - const resetSpy = jest.spyOn(adapters.tables, 'reset'); - - mergeTables.fn(null, { layerIds: ['first', 'second'], tables: [sampleTable1, sampleTable2] }, { - inspectorAdapters: adapters, - } as ExecutionContext); - expect(resetSpy).toHaveBeenCalled(); - }); - - it('should pass the date range along', () => { - expect( - mergeTables.fn( - { - type: 'kibana_context', - timeRange: { - from: '2019-01-01T05:00:00.000Z', - to: '2020-01-01T05:00:00.000Z', - }, - }, - { layerIds: ['first', 'second'], tables: [] }, - // eslint-disable-next-line - {} as any - ) - ).toMatchInlineSnapshot(` - Object { - "dateRange": Object { - "fromDate": 2019-01-01T05:00:00.000Z, - "toDate": 2020-01-01T05:00:00.000Z, - }, - "tables": Object {}, - "type": "lens_multitable", - } - `); - }); - - it('should handle this week now/w', () => { - const { dateRange } = mergeTables.fn( - { - type: 'kibana_context', - timeRange: { - from: 'now/w', - to: 'now/w', - }, - }, - { layerIds: ['first', 'second'], tables: [] }, - // eslint-disable-next-line - {} as any - ); - - expect(moment.duration(moment().startOf('week').diff(dateRange!.fromDate)).asDays()).toEqual(0); - - expect(moment.duration(moment().endOf('week').diff(dateRange!.toDate)).asDays()).toEqual(0); - }); -}); diff --git a/x-pack/plugins/lens/common/expressions/time_scale/time_scale.test.ts b/x-pack/plugins/lens/common/expressions/time_scale/time_scale.test.ts index dd3e18c720c0a..e7fdd720d075c 100644 --- a/x-pack/plugins/lens/common/expressions/time_scale/time_scale.test.ts +++ b/x-pack/plugins/lens/common/expressions/time_scale/time_scale.test.ts @@ -6,10 +6,8 @@ */ import moment from 'moment'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import type { Datatable } from '@kbn/expressions-plugin/public'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import type { TimeRange } from '@kbn/data-plugin/public'; +import type { Datatable } from '@kbn/expressions-plugin/common'; +import type { TimeRange } from '@kbn/data-plugin/common'; import { functionWrapper } from '@kbn/expressions-plugin/common/expression_functions/specs/tests/utils'; // mock the specific inner variable: diff --git a/x-pack/plugins/lens/common/types.ts b/x-pack/plugins/lens/common/types.ts index f4111d51e2918..d7432e0f10b6f 100644 --- a/x-pack/plugins/lens/common/types.ts +++ b/x-pack/plugins/lens/common/types.ts @@ -10,8 +10,8 @@ import { Position } from '@elastic/charts'; import { $Values } from '@kbn/utility-types'; import type { CustomPaletteParams, PaletteOutput } from '@kbn/coloring'; import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; -import type { Datatable } from '@kbn/expressions-plugin/common'; import type { ColorMode } from '@kbn/charts-plugin/common'; +import { LegendSize } from '@kbn/visualizations-plugin/common'; import { CategoryDisplay, layerTypes, @@ -40,15 +40,6 @@ export interface PersistableFilter extends Filter { meta: PersistableFilterMeta; } -export interface LensMultiTable { - type: 'lens_multitable'; - tables: Record; - dateRange?: { - fromDate: Date; - toDate: Date; - }; -} - export type SortingHint = 'version'; export type CustomPaletteParamsConfig = CustomPaletteParams & { @@ -57,8 +48,7 @@ export type CustomPaletteParamsConfig = CustomPaletteParams & { export type LayerType = typeof layerTypes[keyof typeof layerTypes]; -// Shared by XY Chart and Heatmap as for now -export type ValueLabelConfig = 'hide' | 'inside' | 'outside'; +export type ValueLabelConfig = 'hide' | 'show'; export type PieChartType = $Values; export type CategoryDisplayType = $Values; @@ -84,7 +74,7 @@ export interface SharedPieLayerState { percentDecimals?: number; emptySizeRatio?: number; legendMaxLines?: number; - legendSize?: number; + legendSize?: LegendSize; truncateLegend?: boolean; } diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json index 8ed33fb304525..adf791e8d2f48 100644 --- a/x-pack/plugins/lens/kibana.json +++ b/x-pack/plugins/lens/kibana.json @@ -16,10 +16,12 @@ "visualizations", "dashboard", "uiActions", + "uiActionsEnhanced", "embeddable", "share", "presentationUtil", "dataViewFieldEditor", + "dataViewEditor", "expressionGauge", "expressionHeatmap", "eventAnnotation", diff --git a/x-pack/plugins/lens/public/app_plugin/app.scss b/x-pack/plugins/lens/public/app_plugin/app.scss index 99684a8b983c7..58ecce5592937 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.scss +++ b/x-pack/plugins/lens/public/app_plugin/app.scss @@ -10,10 +10,6 @@ flex-direction: column; height: 100%; overflow: hidden; - - > .kbnTopNavMenu__wrapper { - border-bottom: $euiBorderThin; - } } .lnsApp__frame { diff --git a/x-pack/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/plugins/lens/public/app_plugin/app.test.tsx index bfad8dcd3f0ee..6e8cc4315ad8b 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.test.tsx @@ -380,6 +380,75 @@ describe('Lens App', () => { }); }); + describe('TopNavMenu#dataViewPickerProps', () => { + it('calls the nav component with the correct dataview picker props if no permissions are given', async () => { + const { instance, lensStore } = await mountWith({ preloadedState: {} }); + const document = { + savedObjectId: defaultSavedObjectId, + state: { + query: 'fake query', + filters: [{ query: { match_phrase: { src: 'test' } } }], + }, + references: [{ type: 'index-pattern', id: '1', name: 'index-pattern-0' }], + } as unknown as Document; + + act(() => { + lensStore.dispatch( + setState({ + query: 'fake query' as unknown as Query, + persistedDoc: document, + }) + ); + }); + instance.update(); + const props = instance + .find('[data-test-subj="lnsApp_topNav"]') + .prop('dataViewPickerComponentProps') as TopNavMenuData[]; + expect(props).toEqual( + expect.objectContaining({ + currentDataViewId: 'mockip', + onChangeDataView: expect.any(Function), + onDataViewCreated: expect.any(Function), + onAddField: undefined, + }) + ); + }); + + it('calls the nav component with the correct dataview picker props if permissions are given', async () => { + const { instance, lensStore, services } = await mountWith({ preloadedState: {} }); + services.dataViewFieldEditor.userPermissions.editIndexPattern = () => true; + const document = { + savedObjectId: defaultSavedObjectId, + state: { + query: 'fake query', + filters: [{ query: { match_phrase: { src: 'test' } } }], + }, + references: [{ type: 'index-pattern', id: '1', name: 'index-pattern-0' }], + } as unknown as Document; + + act(() => { + lensStore.dispatch( + setState({ + query: 'fake query' as unknown as Query, + persistedDoc: document, + }) + ); + }); + instance.update(); + const props = instance + .find('[data-test-subj="lnsApp_topNav"]') + .prop('dataViewPickerComponentProps') as TopNavMenuData[]; + expect(props).toEqual( + expect.objectContaining({ + currentDataViewId: 'mockip', + onChangeDataView: expect.any(Function), + onDataViewCreated: expect.any(Function), + onAddField: expect.any(Function), + }) + ); + }); + }); + describe('persistence', () => { it('passes query and indexPatterns to TopNavMenu', async () => { const { instance, lensStore, services } = await mountWith({ preloadedState: {} }); diff --git a/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx index e532c82b7b3be..4ae1b8860c878 100644 --- a/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx +++ b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx @@ -7,7 +7,7 @@ import { isEqual } from 'lodash'; import { i18n } from '@kbn/i18n'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react'; import { useStore } from 'react-redux'; import { TopNavMenuData } from '@kbn/navigation-plugin/public'; import { downloadMultipleAs } from '@kbn/share-plugin/public'; @@ -16,6 +16,7 @@ import { exporters } from '@kbn/data-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { trackUiEvent } from '../lens_ui_telemetry'; +import type { StateSetter } from '../types'; import { LensAppServices, LensTopNavActions, @@ -29,8 +30,15 @@ import { useLensDispatch, LensAppState, DispatchSetState, + updateDatasourceState, } from '../state_management'; -import { getIndexPatternsObjects, getIndexPatternsIds, getResolvedDateRange } from '../utils'; +import { + getIndexPatternsObjects, + getIndexPatternsIds, + getResolvedDateRange, + handleIndexPatternChange, + refreshIndexPatternsList, +} from '../utils'; import { combineQueryAndFilters, getLayerMetaInfo } from './show_underlying_data'; function getLensTopNavConfig(options: { @@ -222,6 +230,8 @@ export const LensTopNavMenu = ({ attributeService, discover, dashboardFeatureFlag, + dataViewFieldEditor, + dataViewEditor, dataViews, } = useKibana().services; @@ -232,7 +242,11 @@ export const LensTopNavMenu = ({ ); const [indexPatterns, setIndexPatterns] = useState([]); + const [currentIndexPattern, setCurrentIndexPattern] = useState(); const [rejectedIndexPatterns, setRejectedIndexPatterns] = useState([]); + const editPermission = dataViewFieldEditor.userPermissions.editIndexPattern(); + const closeFieldEditor = useRef<() => void | undefined>(); + const closeDataViewEditor = useRef<() => void | undefined>(); const { isSaveable, @@ -293,6 +307,20 @@ export const LensTopNavMenu = ({ dataViews, ]); + useEffect(() => { + if (indexPatterns.length > 0) { + setCurrentIndexPattern(indexPatterns[0]); + } + }, [indexPatterns]); + + useEffect(() => { + return () => { + // Make sure to close the editors when unmounting + closeFieldEditor.current?.(); + closeDataViewEditor.current?.(); + }; + }, []); + const { TopNavMenu } = navigation.ui; const { from, to } = data.query.timefilter.timefilter.getTime(); @@ -576,6 +604,123 @@ export const LensTopNavMenu = ({ }); }, [data.query.filterManager, data.query.queryString, dispatchSetState]); + const setDatasourceState: StateSetter = useMemo(() => { + return (updater) => { + dispatch( + updateDatasourceState({ + updater, + datasourceId: activeDatasourceId!, + clearStagedPreview: true, + }) + ); + }; + }, [activeDatasourceId, dispatch]); + + const refreshFieldList = useCallback(async () => { + if (currentIndexPattern && currentIndexPattern.id) { + refreshIndexPatternsList({ + activeDatasources: Object.keys(datasourceStates).reduce( + (acc, datasourceId) => ({ + ...acc, + [datasourceId]: datasourceMap[datasourceId], + }), + {} + ), + indexPatternId: currentIndexPattern.id, + setDatasourceState, + }); + } + // start a new session so all charts are refreshed + data.search.session.start(); + }, [ + currentIndexPattern, + data.search.session, + datasourceMap, + datasourceStates, + setDatasourceState, + ]); + + const editField = useMemo( + () => + editPermission + ? async (fieldName?: string, uiAction: 'edit' | 'add' = 'edit') => { + if (currentIndexPattern?.id) { + const indexPatternInstance = await data.dataViews.get(currentIndexPattern?.id); + closeFieldEditor.current = dataViewFieldEditor.openEditor({ + ctx: { + dataView: indexPatternInstance, + }, + fieldName, + onSave: async () => { + refreshFieldList(); + }, + }); + } + } + : undefined, + [editPermission, currentIndexPattern?.id, data.dataViews, dataViewFieldEditor, refreshFieldList] + ); + + const addField = useMemo( + () => (editPermission && editField ? () => editField(undefined, 'add') : undefined), + [editField, editPermission] + ); + + const createNewDataView = useCallback(() => { + const dataViewEditPermission = dataViewEditor.userPermissions.editDataView; + if (!dataViewEditPermission) { + return; + } + closeDataViewEditor.current = dataViewEditor.openEditor({ + onSave: async (dataView) => { + if (dataView.id) { + handleIndexPatternChange({ + activeDatasources: Object.keys(datasourceStates).reduce( + (acc, datasourceId) => ({ + ...acc, + [datasourceId]: datasourceMap[datasourceId], + }), + {} + ), + datasourceStates, + indexPatternId: dataView.id, + setDatasourceState, + }); + refreshFieldList(); + } + }, + }); + }, [dataViewEditor, datasourceMap, datasourceStates, refreshFieldList, setDatasourceState]); + + const dataViewPickerProps = { + trigger: { + label: currentIndexPattern?.title || '', + 'data-test-subj': 'lns-dataView-switch-link', + title: currentIndexPattern?.title || '', + }, + currentDataViewId: currentIndexPattern?.id, + onAddField: addField, + onDataViewCreated: createNewDataView, + onChangeDataView: (newIndexPatternId: string) => { + const currentDataView = indexPatterns.find( + (indexPattern) => indexPattern.id === newIndexPatternId + ); + setCurrentIndexPattern(currentDataView); + handleIndexPatternChange({ + activeDatasources: Object.keys(datasourceStates).reduce( + (acc, datasourceId) => ({ + ...acc, + [datasourceId]: datasourceMap[datasourceId], + }), + {} + ), + datasourceStates, + indexPatternId: newIndexPatternId, + setDatasourceState, + }); + }, + }; + return ( ip.isTimeBased()) || Boolean( @@ -607,6 +753,7 @@ export const LensTopNavMenu = ({ data-test-subj="lnsApp_topNav" screenTitle={'lens'} appName={'lens'} + displayStyle="detached" /> ); }; diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx index f7d865e92853e..6ddd49a7e5df0 100644 --- a/x-pack/plugins/lens/public/app_plugin/mounter.tsx +++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx @@ -84,6 +84,8 @@ export async function getLensServices( notifications: coreStart.notifications, savedObjectsClient: coreStart.savedObjects.client, presentationUtil: startDependencies.presentationUtil, + dataViewEditor: startDependencies.dataViewEditor, + dataViewFieldEditor: startDependencies.dataViewFieldEditor, dashboard: startDependencies.dashboard, getOriginatingAppName: () => { return embeddableEditorIncomingState?.originatingApp diff --git a/x-pack/plugins/lens/public/app_plugin/types.ts b/x-pack/plugins/lens/public/app_plugin/types.ts index 94507754b893f..abb6cfa6a06a6 100644 --- a/x-pack/plugins/lens/public/app_plugin/types.ts +++ b/x-pack/plugins/lens/public/app_plugin/types.ts @@ -30,6 +30,8 @@ import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public' import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import type { DashboardFeatureFlagConfig } from '@kbn/dashboard-plugin/public'; import type { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; +import type { IndexPatternFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public'; +import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; import { VisualizeFieldContext, ACTION_VISUALIZE_LENS_FIELD } from '@kbn/ui-actions-plugin/public'; import { ACTION_CONVERT_TO_LENS } from '@kbn/visualizations-plugin/public'; import type { EmbeddableEditorState, EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public'; @@ -140,6 +142,8 @@ export interface LensAppServices { // Temporarily required until the 'by value' paradigm is default. dashboardFeatureFlag: DashboardFeatureFlagConfig; + dataViewEditor: DataViewEditorStart; + dataViewFieldEditor: IndexPatternFieldEditorStart; } export interface LensTopNavTooltips { diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts b/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts index 118abdcb77c8a..f11fc098b50d9 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts @@ -17,7 +17,6 @@ import { createGridHideHandler, createTransposeColumnFilterHandler, } from './table_actions'; -import { LensMultiTable } from '../../../common'; import { LensGridDirection, ColumnConfig } from '../../../common/expressions'; function getDefaultConfig(): ColumnConfig { @@ -49,17 +48,8 @@ function createTableRef( }; } -function createUntransposedRef(options?: { - withDate: boolean; -}): React.MutableRefObject { - return { - current: { - type: 'lens_multitable', - tables: { - first: createTableRef(options).current, - }, - }, - }; +function createUntransposedRef(options?: { withDate: boolean }): React.MutableRefObject { + return { current: createTableRef(options).current }; } describe('Table actions', () => { @@ -147,13 +137,13 @@ describe('Table actions', () => { it('should set a filter on click with the correct configuration', () => { const onClickValue = jest.fn(); const tableRef = createUntransposedRef({ withDate: true }); - tableRef.current.tables.first.rows = [{ a: 123456 }]; + tableRef.current.rows = [{ a: 123456 }]; const filterHandle = createTransposeColumnFilterHandler(onClickValue, tableRef); filterHandle( [ { - originalBucketColumn: tableRef.current.tables.first.columns[0], + originalBucketColumn: tableRef.current.columns[0], value: 123456, }, ], @@ -164,7 +154,7 @@ describe('Table actions', () => { { column: 0, row: 0, - table: tableRef.current.tables.first, + table: tableRef.current, value: 123456, }, ], @@ -175,13 +165,13 @@ describe('Table actions', () => { it('should set a negate filter on click with the correct configuration', () => { const onClickValue = jest.fn(); const tableRef = createUntransposedRef({ withDate: true }); - tableRef.current.tables.first.rows = [{ a: 123456 }]; + tableRef.current.rows = [{ a: 123456 }]; const filterHandle = createTransposeColumnFilterHandler(onClickValue, tableRef); filterHandle( [ { - originalBucketColumn: tableRef.current.tables.first.columns[0], + originalBucketColumn: tableRef.current.columns[0], value: 123456, }, ], @@ -192,7 +182,7 @@ describe('Table actions', () => { { column: 0, row: 0, - table: tableRef.current.tables.first, + table: tableRef.current, value: 123456, }, ], @@ -204,7 +194,7 @@ describe('Table actions', () => { const onClickValue = jest.fn(); const tableRef = createUntransposedRef({ withDate: false }); const filterHandle = createTransposeColumnFilterHandler(onClickValue, tableRef); - tableRef.current.tables.first.columns = [ + tableRef.current.columns = [ { id: 'a', name: 'a', @@ -220,7 +210,7 @@ describe('Table actions', () => { }, }, ]; - tableRef.current.tables.first.rows = [ + tableRef.current.rows = [ { a: 'a1', b: 'b1', @@ -242,11 +232,11 @@ describe('Table actions', () => { filterHandle( [ { - originalBucketColumn: tableRef.current.tables.first.columns[0], + originalBucketColumn: tableRef.current.columns[0], value: 'a2', }, { - originalBucketColumn: tableRef.current.tables.first.columns[1], + originalBucketColumn: tableRef.current.columns[1], value: 'b3', }, ], @@ -257,13 +247,13 @@ describe('Table actions', () => { { column: 0, row: 1, - table: tableRef.current.tables.first, + table: tableRef.current, value: 'a2', }, { column: 1, row: 2, - table: tableRef.current.tables.first, + table: tableRef.current, value: 'b3', }, ], diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts b/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts index 817b1d10fd82e..ba7e957de083a 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts @@ -7,8 +7,7 @@ import type { EuiDataGridSorting } from '@elastic/eui'; import type { Datatable, DatatableColumn } from '@kbn/expressions-plugin'; -import type { LensFilterEvent } from '../../types'; -import type { LensMultiTable } from '../../../common'; +import { ClickTriggerEvent } from '@kbn/charts-plugin/public'; import type { LensResizeAction, LensSortAction, LensToggleAction } from './types'; import type { ColumnConfig, LensGridDirection } from '../../../common/expressions'; import { getOriginalId } from '../../../common/expressions'; @@ -72,10 +71,10 @@ export const createGridHideHandler = export const createGridFilterHandler = ( tableRef: React.MutableRefObject, - onClickValue: (data: LensFilterEvent['data']) => void + onClickValue: (data: ClickTriggerEvent['data']) => void ) => (field: string, value: unknown, colIndex: number, rowIndex: number, negate: boolean = false) => { - const data: LensFilterEvent['data'] = { + const data: ClickTriggerEvent['data'] = { negate, data: [ { @@ -92,17 +91,17 @@ export const createGridFilterHandler = export const createTransposeColumnFilterHandler = ( - onClickValue: (data: LensFilterEvent['data']) => void, - untransposedDataRef: React.MutableRefObject + onClickValue: (data: ClickTriggerEvent['data']) => void, + untransposedDataRef: React.MutableRefObject ) => ( bucketValues: Array<{ originalBucketColumn: DatatableColumn; value: unknown }>, negate: boolean = false ) => { if (!untransposedDataRef.current) return; - const originalTable = Object.values(untransposedDataRef.current.tables)[0]; + const originalTable = untransposedDataRef.current; - const data: LensFilterEvent['data'] = { + const data: ClickTriggerEvent['data'] = { negate, data: bucketValues.map(({ originalBucketColumn, value }) => { const columnIndex = originalTable.columns.findIndex( diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx index 96ffbf371525e..bcf6f50d2bd46 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx @@ -20,59 +20,53 @@ import { VisualizationContainer } from '../../visualization_container'; import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; import { LensIconChartDatatable } from '../../assets/chart_datatable'; import { DataContext, DatatableComponent } from './table_basic'; -import { LensMultiTable } from '../../../common'; import { DatatableProps } from '../../../common/expressions'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { IUiSettingsClient } from '@kbn/core/public'; -import { RenderMode } from '@kbn/expressions-plugin'; +import { Datatable, RenderMode } from '@kbn/expressions-plugin'; import { LENS_EDIT_PAGESIZE_ACTION } from './constants'; function sampleArgs() { const indexPatternId = 'indexPatternId'; - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - l1: { - type: 'datatable', - columns: [ - { - id: 'a', - name: 'a', - meta: { - type: 'string', - source: 'esaggs', - field: 'a', - sourceParams: { type: 'terms', indexPatternId }, - }, - }, - { - id: 'b', - name: 'b', - meta: { - type: 'date', - field: 'b', - source: 'esaggs', - sourceParams: { - type: 'date_histogram', - indexPatternId, - }, - }, - }, - { - id: 'c', - name: 'c', - meta: { - type: 'number', - source: 'esaggs', - field: 'c', - sourceParams: { indexPatternId, type: 'count' }, - }, + const data: Datatable = { + type: 'datatable', + columns: [ + { + id: 'a', + name: 'a', + meta: { + type: 'string', + source: 'esaggs', + field: 'a', + sourceParams: { type: 'terms', indexPatternId }, + }, + }, + { + id: 'b', + name: 'b', + meta: { + type: 'date', + field: 'b', + source: 'esaggs', + sourceParams: { + type: 'date_histogram', + indexPatternId, }, - ], - rows: [{ a: 'shoes', b: 1588024800000, c: 3 }], + }, }, - }, + { + id: 'c', + name: 'c', + meta: { + type: 'number', + source: 'esaggs', + field: 'c', + sourceParams: { indexPatternId, type: 'count' }, + }, + }, + ], + rows: [{ a: 'shoes', b: 1588024800000, c: 3 }], }; const args: DatatableProps['args'] = { @@ -175,13 +169,7 @@ describe('DatatableComponent', () => { const wrapper = mountWithIntl( ({ convert: (x) => x } as IFieldFormat)} dispatchEvent={onDispatchEvent} @@ -206,7 +194,7 @@ describe('DatatableComponent', () => { { column: 0, row: 0, - table: data.tables.l1, + table: data, value: 'shoes', }, ], @@ -220,13 +208,7 @@ describe('DatatableComponent', () => { const wrapper = mountWithIntl( ({ convert: (x) => x } as IFieldFormat)} dispatchEvent={onDispatchEvent} @@ -251,7 +233,7 @@ describe('DatatableComponent', () => { { column: 1, row: 0, - table: data.tables.l1, + table: data, value: 1588024800000, }, ], @@ -261,35 +243,30 @@ describe('DatatableComponent', () => { }); test('it invokes executeTriggerActions with correct context on click on timefield from range', async () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - l1: { - type: 'datatable', - columns: [ - { - id: 'a', - name: 'a', - meta: { - type: 'date', - source: 'esaggs', - field: 'a', - sourceParams: { type: 'date_range', indexPatternId: 'a' }, - }, - }, - { - id: 'b', - name: 'b', - meta: { - type: 'number', - source: 'esaggs', - sourceParams: { type: 'count', indexPatternId: 'a' }, - }, - }, - ], - rows: [{ a: 1588024800000, b: 3 }], + const data: Datatable = { + type: 'datatable', + columns: [ + { + id: 'a', + name: 'a', + meta: { + type: 'date', + source: 'esaggs', + field: 'a', + sourceParams: { type: 'date_range', indexPatternId: 'a' }, + }, }, - }, + { + id: 'b', + name: 'b', + meta: { + type: 'number', + source: 'esaggs', + sourceParams: { type: 'count', indexPatternId: 'a' }, + }, + }, + ], + rows: [{ a: 1588024800000, b: 3 }], }; const args: DatatableProps['args'] = { @@ -305,13 +282,7 @@ describe('DatatableComponent', () => { const wrapper = mountWithIntl( ({ convert: (x) => x } as IFieldFormat)} dispatchEvent={onDispatchEvent} @@ -336,7 +307,7 @@ describe('DatatableComponent', () => { { column: 0, row: 0, - table: data.tables.l1, + table: data, value: 1588024800000, }, ], @@ -350,13 +321,7 @@ describe('DatatableComponent', () => { const wrapper = mountWithIntl( ({ convert: (x) => x } as IFieldFormat)} dispatchEvent={onDispatchEvent} @@ -377,14 +342,9 @@ describe('DatatableComponent', () => { test('it shows emptyPlaceholder for undefined bucketed data', () => { const { args, data } = sampleArgs(); - const emptyData: LensMultiTable = { + const emptyData: Datatable = { ...data, - tables: { - l1: { - ...data.tables.l1, - rows: [{ a: undefined, b: undefined, c: 0 }], - }, - }, + rows: [{ a: undefined, b: undefined, c: 0 }], }; const component = shallow( @@ -551,8 +511,7 @@ describe('DatatableComponent', () => { test('it detect last_value filtered metric type', () => { const { data, args } = sampleArgs(); - const table = data.tables.l1; - const column = table.columns[1]; + const column = data.columns[1]; column.meta = { ...column.meta, @@ -560,7 +519,7 @@ describe('DatatableComponent', () => { type: 'number', sourceParams: { ...column.meta.sourceParams, type: 'filtered_metric' }, }; - table.rows[0].b = 'Hello'; + data.rows[0].b = 'Hello'; const wrapper = shallow( { ); // mnake a copy of the data, changing only the name of the first column const newData = copyData(data); - newData.tables.l1.columns[0].name = 'new a'; + newData.columns[0].name = 'new a'; wrapper.setProps({ data: newData }); wrapper.update(); diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx index 76d677f74fe91..cf6cd1c635cee 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx @@ -20,7 +20,8 @@ import { EuiDataGridStyle, } from '@elastic/eui'; import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; -import type { LensFilterEvent, LensTableRowContextMenuEvent } from '../../types'; +import { ClickTriggerEvent } from '@kbn/charts-plugin/public'; +import type { LensTableRowContextMenuEvent } from '../../types'; import type { FormatFactory } from '../../../common'; import type { LensGridDirection } from '../../../common/expressions'; import { VisualizationContainer } from '../../visualization_container'; @@ -58,8 +59,6 @@ const PAGE_SIZE_OPTIONS = [DEFAULT_PAGE_SIZE, 20, 30, 50, 100]; export const DatatableComponent = (props: DatatableRenderProps) => { const dataGridRef = useRef(null); - const [firstTable] = Object.values(props.data.tables); - const isInteractive = props.interactive; const [columnConfig, setColumnConfig] = useState({ @@ -67,7 +66,7 @@ export const DatatableComponent = (props: DatatableRenderProps) => { sortingColumnId: props.args.sortingColumnId, sortingDirection: props.args.sortingDirection, }); - const [firstLocalTable, updateTable] = useState(firstTable); + const [firstLocalTable, updateTable] = useState(props.data); // ** Pagination config const [pagination, setPagination] = useState<{ pageIndex: number; pageSize: number } | undefined>( @@ -94,8 +93,8 @@ export const DatatableComponent = (props: DatatableRenderProps) => { }, [props.args.columns, props.args.sortingColumnId, props.args.sortingDirection]); useDeepCompareEffect(() => { - updateTable(firstTable); - }, [firstTable]); + updateTable(props.data); + }, [props.data]); const firstTableRef = useRef(firstLocalTable); firstTableRef.current = firstLocalTable; @@ -120,7 +119,7 @@ export const DatatableComponent = (props: DatatableRenderProps) => { ); const onClickValue = useCallback( - (data: LensFilterEvent['data']) => { + (data: ClickTriggerEvent['data']) => { dispatchEvent({ name: 'filter', data }); }, [dispatchEvent] @@ -193,7 +192,7 @@ export const DatatableComponent = (props: DatatableRenderProps) => { const isEmpty = firstLocalTable.rows.length === 0 || (bucketColumns.length && - firstTable.rows.every((row) => bucketColumns.every((col) => row[col] == null))); + props.data.rows.every((row) => bucketColumns.every((col) => row[col] == null))); const visibleColumns = useMemo( () => @@ -252,10 +251,10 @@ export const DatatableComponent = (props: DatatableRenderProps) => { columnConfig.columns .filter(({ columnId }) => isNumericMap[columnId]) .map(({ columnId }) => columnId), - firstTable, + props.data, getOriginalId ); - }, [firstTable, isNumericMap, columnConfig]); + }, [props.data, isNumericMap, columnConfig]); const headerRowHeight = props.args.headerRowHeight ?? 'single'; const headerRowLines = props.args.headerRowHeightLines ?? 1; @@ -375,7 +374,7 @@ export const DatatableComponent = (props: DatatableRenderProps) => { .map((config) => ({ columnId: config.columnId, summaryRowValue: config.summaryRowValue, - ...getFinalSummaryConfiguration(config.columnId, config, firstTable), + ...getFinalSummaryConfiguration(config.columnId, config, props.data), })) .filter(({ summaryRow }) => summaryRow !== 'none'); @@ -401,7 +400,7 @@ export const DatatableComponent = (props: DatatableRenderProps) => { ) : null; }; } - }, [columnConfig.columns, alignments, firstTable, columns]); + }, [columnConfig.columns, alignments, props.data, columns]); if (isEmpty) { return ( diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx index 8a899780515b2..3e798c5813041 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx @@ -6,56 +6,51 @@ */ import type { DatatableProps } from '../../common/expressions'; -import type { LensMultiTable } from '../../common'; import { createMockExecutionContext } from '@kbn/expressions-plugin/common/mocks'; import type { FormatFactory } from '../../common'; import { getDatatable } from '../../common/expressions'; +import { Datatable } from '@kbn/expressions-plugin'; function sampleArgs() { const indexPatternId = 'indexPatternId'; - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - l1: { - type: 'datatable', - columns: [ - { - id: 'a', - name: 'a', - meta: { - type: 'string', - source: 'esaggs', - field: 'a', - sourceParams: { type: 'terms', indexPatternId }, - }, - }, - { - id: 'b', - name: 'b', - meta: { - type: 'date', - field: 'b', - source: 'esaggs', - sourceParams: { - type: 'date_histogram', - indexPatternId, - }, - }, - }, - { - id: 'c', - name: 'c', - meta: { - type: 'number', - source: 'esaggs', - field: 'c', - sourceParams: { indexPatternId, type: 'count' }, - }, + const data: Datatable = { + type: 'datatable', + columns: [ + { + id: 'a', + name: 'a', + meta: { + type: 'string', + source: 'esaggs', + field: 'a', + sourceParams: { type: 'terms', indexPatternId }, + }, + }, + { + id: 'b', + name: 'b', + meta: { + type: 'date', + field: 'b', + source: 'esaggs', + sourceParams: { + type: 'date_histogram', + indexPatternId, }, - ], - rows: [{ a: 'shoes', b: 1588024800000, c: 3 }], + }, + }, + { + id: 'c', + name: 'c', + meta: { + type: 'number', + source: 'esaggs', + field: 'c', + sourceParams: { indexPatternId, type: 'count' }, + }, }, - }, + ], + rows: [{ a: 'shoes', b: 1588024800000, c: 3 }], }; const args: DatatableProps['args'] = { diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index 1053eadce1363..3ae8057114198 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -46,16 +46,15 @@ export const getDatatableRenderer = (dependencies: { // ROW_CLICK_TRIGGER trigger. let rowHasRowClickTriggerActions: boolean[] = []; if (hasCompatibleActions) { - const table = Object.values(config.data.tables)[0]; - if (!!table) { + if (!!config.data) { rowHasRowClickTriggerActions = await Promise.all( - table.rows.map(async (row, rowIndex) => { + config.data.rows.map(async (row, rowIndex) => { try { const hasActions = await hasCompatibleActions({ name: 'tableRowContextMenuClick', data: { rowIndex, - table, + table: config.data, columns: config.args.columns.map((column) => column.columnId), }, }); diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx index 8087f43b90e72..4cc44a1b70293 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx @@ -326,7 +326,12 @@ export const getDatatableVisualization = ({ } }, - toExpression(state, datasourceLayers, { title, description } = {}): Ast | null { + toExpression( + state, + datasourceLayers, + { title, description } = {}, + datasourceExpressionsByLayers = {} + ): Ast | null { const { sortedColumns, datasource } = getDataSourceAndSortedColumns(state, datasourceLayers, state.layerId) || {}; @@ -346,9 +351,12 @@ export const getDatatableVisualization = ({ .filter((columnId) => datasource!.getOperationForColumnId(columnId)) .map((columnId) => columnMap[columnId]); + const datasourceExpression = datasourceExpressionsByLayers[state.layerId]; + return { type: 'expression', chain: [ + ...(datasourceExpression?.chain ?? []), { type: 'function', function: 'lens_datatable', diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index e404faacb8f97..c577bf89d6bd1 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -145,8 +145,6 @@ export function LayerPanel( const isEmptyLayer = !groups.some((d) => d.accessors.length > 0); const { activeId, activeGroup } = activeDimension; - const { setDimension, removeDimension } = activeVisualization; - const allAccessors = groups.flatMap((group) => group.accessors.map((accessor) => accessor.columnId) ); @@ -209,7 +207,7 @@ export function LayerPanel( previousColumn = typeof dropResult === 'object' ? dropResult.deleted : undefined; } } - const newVisState = setDimension({ + const newVisState = activeVisualization.setDimension({ columnId, groupId, layerId: targetLayerId, @@ -221,7 +219,7 @@ export function LayerPanel( if (typeof dropResult === 'object') { // When a column is moved, we delete the reference to the old updateVisualization( - removeDimension({ + activeVisualization.removeDimension({ columnId: dropResult.deleted, layerId: targetLayerId, prevState: newVisState, @@ -234,7 +232,7 @@ export function LayerPanel( } } else { if (dropType === 'duplicate_compatible' || dropType === 'reorder') { - const newVisState = setDimension({ + const newVisState = activeVisualization.setDimension({ columnId, groupId, layerId: targetLayerId, @@ -247,16 +245,15 @@ export function LayerPanel( } }; }, [ - framePublicAPI, + layerDatasource, + setNextFocusedButtonId, groups, layerDatasourceOnDrop, + layerDatasourceDropProps, + activeVisualization, props.visualizationState, + framePublicAPI, updateVisualization, - setDimension, - removeDimension, - layerDatasourceDropProps, - setNextFocusedButtonId, - layerDatasource, ]); const isDimensionPanelOpen = Boolean(activeId); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx index 91ca494866f30..796128df989b4 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx @@ -32,7 +32,6 @@ import { EditorFrame, EditorFrameProps } from './editor_frame'; import { DatasourcePublicAPI, DatasourceSuggestion, Visualization } from '../../types'; import { act } from 'react-dom/test-utils'; import { coreMock } from '@kbn/core/public/mocks'; -import { fromExpression } from '@kbn/interpreter'; import { createMockVisualization, createMockDatasource, @@ -49,6 +48,7 @@ import { expressionsPluginMock } from '@kbn/expressions-plugin/public/mocks'; import { mockDataPlugin, mountWithProvider } from '../../mocks'; import { setState } from '../../state_management'; import { getLensInspectorService } from '../../lens_inspector_service'; +import { toExpression } from '@kbn/interpreter'; function generateSuggestion(state = {}): DatasourceSuggestion { return { @@ -209,10 +209,20 @@ describe('editor_frame', () => { it('should render the resulting expression using the expression renderer', async () => { mockDatasource.getLayers.mockReturnValue(['first']); - const props = { + const props: EditorFrameProps = { ...getDefaultProps(), visualizationMap: { - testVis: { ...mockVisualization, toExpression: () => 'testVis' }, + testVis: { + ...mockVisualization, + toExpression: (state, datasourceLayers, attrs, datasourceExpressionsByLayers = {}) => + toExpression({ + type: 'expression', + chain: [ + ...(datasourceExpressionsByLayers.first?.chain ?? []), + { type: 'function', function: 'testVis', arguments: {} }, + ], + }), + }, }, datasourceMap: { testDatasource: { @@ -242,137 +252,10 @@ describe('editor_frame', () => { instance.update(); expect(instance.find(expressionRendererMock).prop('expression')).toMatchInlineSnapshot(` - "kibana - | lens_merge_tables layerIds=\\"first\\" tables={datasource} + "datasource | testVis" `); }); - - it('should render individual expression for each given layer', async () => { - mockDatasource.toExpression.mockReturnValue('datasource'); - mockDatasource2.toExpression.mockImplementation((_state, layerId) => `datasource_${layerId}`); - mockDatasource.initialize.mockImplementation((initialState) => Promise.resolve(initialState)); - mockDatasource.getLayers.mockReturnValue(['first', 'second']); - mockDatasource2.initialize.mockImplementation((initialState) => - Promise.resolve(initialState) - ); - mockDatasource2.getLayers.mockReturnValue(['third']); - - const props = { - ...getDefaultProps(), - visualizationMap: { - testVis: { ...mockVisualization, toExpression: () => 'testVis' }, - }, - datasourceMap: { - testDatasource: { - ...mockDatasource, - toExpression: () => 'datasource', - }, - testDatasource2: { - ...mockDatasource2, - toExpression: () => 'datasource_second', - }, - }, - - ExpressionRenderer: expressionRendererMock, - }; - - instance = ( - await mountWithProvider(, { - preloadedState: { - visualization: { activeId: 'testVis', state: {} }, - datasourceStates: { - testDatasource: { - isLoading: false, - state: { - internalState1: '', - }, - }, - testDatasource2: { - isLoading: false, - state: { - internalState1: '', - }, - }, - }, - }, - }) - ).instance; - - instance.update(); - - expect( - fromExpression(instance.find(expressionRendererMock).prop('expression') as string) - ).toEqual({ - type: 'expression', - chain: expect.arrayContaining([ - expect.objectContaining({ - arguments: expect.objectContaining({ layerIds: ['first', 'second', 'third'] }), - }), - ]), - }); - expect(fromExpression(instance.find(expressionRendererMock).prop('expression') as string)) - .toMatchInlineSnapshot(` - Object { - "chain": Array [ - Object { - "arguments": Object {}, - "function": "kibana", - "type": "function", - }, - Object { - "arguments": Object { - "layerIds": Array [ - "first", - "second", - "third", - ], - "tables": Array [ - Object { - "chain": Array [ - Object { - "arguments": Object {}, - "function": "datasource", - "type": "function", - }, - ], - "type": "expression", - }, - Object { - "chain": Array [ - Object { - "arguments": Object {}, - "function": "datasource", - "type": "function", - }, - ], - "type": "expression", - }, - Object { - "chain": Array [ - Object { - "arguments": Object {}, - "function": "datasource_second", - "type": "function", - }, - ], - "type": "expression", - }, - ], - }, - "function": "lens_merge_tables", - "type": "function", - }, - Object { - "arguments": Object {}, - "function": "testVis", - "type": "function", - }, - ], - "type": "expression", - } - `); - }); }); describe('state update', () => { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/expression_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/expression_helpers.ts index cecf75cd58676..367d156929714 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/expression_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/expression_helpers.ts @@ -5,20 +5,23 @@ * 2.0. */ -import { Ast, AstFunction, fromExpression } from '@kbn/interpreter'; +import { Ast, fromExpression } from '@kbn/interpreter'; import { DatasourceStates } from '../../state_management'; import { Visualization, DatasourceMap, DatasourceLayers } from '../../types'; -export function prependDatasourceExpression( - visualizationExpression: Ast | string | null, +export function getDatasourceExpressionsByLayers( datasourceMap: DatasourceMap, datasourceStates: DatasourceStates -): Ast | null { +): null | Record { const datasourceExpressions: Array<[string, Ast | string]> = []; Object.entries(datasourceMap).forEach(([datasourceId, datasource]) => { - const state = datasourceStates[datasourceId].state; - const layers = datasource.getLayers(datasourceStates[datasourceId].state); + const state = datasourceStates[datasourceId]?.state; + if (!state) { + return; + } + + const layers = datasource.getLayers(state); layers.forEach((layerId) => { const result = datasource.toExpression(state, layerId); @@ -28,35 +31,17 @@ export function prependDatasourceExpression( }); }); - if (datasourceExpressions.length === 0 || visualizationExpression === null) { + if (datasourceExpressions.length === 0) { return null; } - const parsedDatasourceExpressions: Array<[string, Ast]> = datasourceExpressions.map( - ([layerId, expr]) => [layerId, typeof expr === 'string' ? fromExpression(expr) : expr] - ); - - const datafetchExpression: AstFunction = { - type: 'function', - function: 'lens_merge_tables', - arguments: { - layerIds: parsedDatasourceExpressions.map(([id]) => id), - tables: parsedDatasourceExpressions.map(([id, expr]) => expr), - }, - }; - const parsedVisualizationExpression = - typeof visualizationExpression === 'string' - ? fromExpression(visualizationExpression) - : visualizationExpression; - - return { - type: 'expression', - chain: [ - { type: 'function', function: 'kibana', arguments: {} }, - datafetchExpression, - ...parsedVisualizationExpression.chain, - ], - }; + return datasourceExpressions.reduce( + (exprs, [layerId, expr]) => ({ + ...exprs, + [layerId]: typeof expr === 'string' ? fromExpression(expr) : expr, + }), + {} + ); } export function buildExpression({ @@ -79,16 +64,27 @@ export function buildExpression({ if (visualization === null) { return null; } - const visualizationExpression = visualization.toExpression(visualizationState, datasourceLayers, { - title, - description, - }); - const completeExpression = prependDatasourceExpression( - visualizationExpression, + const datasourceExpressionsByLayers = getDatasourceExpressionsByLayers( datasourceMap, datasourceStates ); - return completeExpression; + const visualizationExpression = visualization.toExpression( + visualizationState, + datasourceLayers, + { + title, + description, + }, + datasourceExpressionsByLayers ?? undefined + ); + + if (datasourceExpressionsByLayers === null || visualizationExpression === null) { + return null; + } + + return typeof visualizationExpression === 'string' + ? fromExpression(visualizationExpression) + : visualizationExpression; } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.scss index b49c77bb8b419..cab1a78294a86 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.scss @@ -96,7 +96,7 @@ a tilemap in an iframe: https://github.com/elastic/kibana/issues/16457 */ .lnsFrameLayout__sidebar--right { flex-basis: 25%; background-color: lightOrDarkTheme($euiColorLightestShade, $euiColorInk); - min-width: $lnsPanelMinWidth + $euiSizeXL; + min-width: $lnsPanelMinWidth + 70; max-width: $euiFormMaxWidth + $euiSizeXXL; max-height: 100%; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.test.tsx index cda496aa693e8..7eff9a5961e83 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.test.tsx @@ -386,9 +386,7 @@ describe('suggestion_panel', () => { const passedExpression = (expressionRendererMock as jest.Mock).mock.calls[0][0].expression; expect(passedExpression).toMatchInlineSnapshot(` - "kibana - | lens_merge_tables layerIds=\\"first\\" tables={datasource_expression} - | test + "test | expression" `); }); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx index 770d25be5388f..abd6da25c52ea 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx @@ -22,7 +22,7 @@ import { EuiText, } from '@elastic/eui'; import { IconType } from '@elastic/eui/src/components/icon/icon'; -import { Ast, toExpression } from '@kbn/interpreter'; +import { Ast, fromExpression, toExpression } from '@kbn/interpreter'; import { i18n } from '@kbn/i18n'; import classNames from 'classnames'; import { ExecutionContextSearch } from '@kbn/data-plugin/public'; @@ -39,7 +39,7 @@ import { DatasourceLayers, } from '../../types'; import { getSuggestions, switchToSuggestion } from './suggestion_helpers'; -import { prependDatasourceExpression } from './expression_helpers'; +import { getDatasourceExpressionsByLayers } from './expression_helpers'; import { trackUiEvent, trackSuggestionEvent } from '../../lens_ui_telemetry'; import { getMissingIndexPattern, @@ -485,6 +485,7 @@ function getPreviewExpression( visualizableState: VisualizableState, visualization: Visualization, datasources: Record, + datasourceStates: DatasourceStates, frame: FramePublicAPI ) { if (!visualization.toPreviewExpression) { @@ -518,9 +519,15 @@ function getPreviewExpression( }); } + const datasourceExpressionsByLayers = getDatasourceExpressionsByLayers( + datasources, + datasourceStates + ); + return visualization.toPreviewExpression( visualizableState.visualizationState, - suggestionFrameApi.datasourceLayers + suggestionFrameApi.datasourceLayers, + datasourceExpressionsByLayers ?? undefined ); } @@ -534,10 +541,21 @@ function preparePreviewExpression( const suggestionDatasourceId = visualizableState.datasourceId; const suggestionDatasourceState = visualizableState.datasourceState; + const datasourceStatesWithSuggestions = suggestionDatasourceId + ? { + ...datasourceStates, + [suggestionDatasourceId]: { + isLoading: false, + state: suggestionDatasourceState, + }, + } + : datasourceStates; + const expression = getPreviewExpression( visualizableState, visualization, datasourceMap, + datasourceStatesWithSuggestions, framePublicAPI ); @@ -545,19 +563,5 @@ function preparePreviewExpression( return; } - const expressionWithDatasource = prependDatasourceExpression( - expression, - datasourceMap, - suggestionDatasourceId - ? { - ...datasourceStates, - [suggestionDatasourceId]: { - isLoading: false, - state: suggestionDatasourceState, - }, - } - : datasourceStates - ); - - return expressionWithDatasource; + return typeof expression === 'string' ? fromExpression(expression) : expression; } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx index 6e546459a7011..d12d4beb02f2c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx @@ -26,7 +26,6 @@ jest.mock('../../../debounced_component', () => { import { WorkspacePanel } from './workspace_panel'; import { ReactWrapper } from 'enzyme'; import { DragDrop, ChildDragDropProvider } from '../../../drag_drop'; -import { fromExpression } from '@kbn/interpreter'; import { buildExistsFilter } from '@kbn/es-query'; import { coreMock } from '@kbn/core/public/mocks'; import { DataView } from '@kbn/data-views-plugin/public'; @@ -44,6 +43,7 @@ import { import { getLensInspectorService } from '../../../lens_inspector_service'; import { inspectorPluginMock } from '@kbn/inspector-plugin/public/mocks'; import { disableAutoApply, enableAutoApply } from '../../../state_management/lens_slice'; +import { Ast, toExpression } from '@kbn/interpreter'; const defaultPermissions: Record>> = { navLinks: { management: true }, @@ -73,6 +73,19 @@ const defaultProps = { toggleFullscreen: jest.fn(), }; +const toExpr = ( + datasourceExpressionsByLayers: Record, + fn: string = 'testVis', + layerId: string = 'first' +) => + toExpression({ + type: 'expression', + chain: [ + ...(datasourceExpressionsByLayers[layerId]?.chain ?? []), + { type: 'function', function: fn, arguments: {} }, + ], + }); + const SELECTORS = { applyChangesButton: 'button[data-test-subj="lnsApplyChanges__toolbar"]', dragDropPrompt: '[data-test-subj="workspace-drag-drop-prompt"]', @@ -148,7 +161,11 @@ describe('workspace_panel', () => { 'testVis' }, + testVis: { + ...mockVisualization, + toExpression: (state, datasourceLayers, attrs, datasourceExpressionsByLayers = {}) => + toExpr(datasourceExpressionsByLayers), + }, }} />, @@ -177,7 +194,11 @@ describe('workspace_panel', () => { }} framePublicAPI={framePublicAPI} visualizationMap={{ - testVis: { ...mockVisualization, toExpression: () => 'testVis' }, + testVis: { + ...mockVisualization, + toExpression: (state, datasourceLayers, attrs, datasourceExpressionsByLayers = {}) => + toExpr(datasourceExpressionsByLayers), + }, }} ExpressionRenderer={expressionRendererMock} /> @@ -188,8 +209,7 @@ describe('workspace_panel', () => { instance.update(); expect(instance.find(expressionRendererMock).prop('expression')).toMatchInlineSnapshot(` - "kibana - | lens_merge_tables layerIds=\\"first\\" tables={datasource} + "datasource | testVis" `); }); @@ -210,7 +230,11 @@ describe('workspace_panel', () => { }} framePublicAPI={framePublicAPI} visualizationMap={{ - testVis: { ...mockVisualization, toExpression: () => 'testVis' }, + testVis: { + ...mockVisualization, + toExpression: (state, datasourceLayers, attrs, datasourceExpressionsByLayers = {}) => + toExpr(datasourceExpressionsByLayers), + }, }} ExpressionRenderer={expressionRendererMock} />, @@ -228,26 +252,28 @@ describe('workspace_panel', () => { // allows initial render expect(getExpression()).toMatchInlineSnapshot(` - "kibana - | lens_merge_tables layerIds=\\"first\\" tables={datasource} - | testVis" - `); + "datasource + | testVis" + `); mockDatasource.toExpression.mockReturnValue('new-datasource'); act(() => { instance.setProps({ visualizationMap: { - testVis: { ...mockVisualization, toExpression: () => 'new-vis' }, + testVis: { + ...mockVisualization, + toExpression: (state, datasourceLayers, attrs, datasourceExpressionsByLayers = {}) => + toExpr(datasourceExpressionsByLayers, 'new-vis'), + } as Visualization, }, }); }); instance.update(); expect(getExpression()).toMatchInlineSnapshot(` - "kibana - | lens_merge_tables layerIds=\\"first\\" tables={datasource} - | testVis" - `); + "datasource + | testVis" + `); act(() => { mounted.lensStore.dispatch(applyChanges()); @@ -256,16 +282,19 @@ describe('workspace_panel', () => { // should update expect(getExpression()).toMatchInlineSnapshot(` - "kibana - | lens_merge_tables layerIds=\\"first\\" tables={new-datasource} - | new-vis" - `); + "new-datasource + | new-vis" + `); mockDatasource.toExpression.mockReturnValue('other-new-datasource'); act(() => { instance.setProps({ visualizationMap: { - testVis: { ...mockVisualization, toExpression: () => 'other-new-vis' }, + testVis: { + ...mockVisualization, + toExpression: (state, datasourceLayers, attrs, datasourceExpressionsByLayers = {}) => + toExpr(datasourceExpressionsByLayers, 'other-new-vis'), + } as Visualization, }, }); mounted.lensStore.dispatch(enableAutoApply()); @@ -274,10 +303,9 @@ describe('workspace_panel', () => { // reenabling auto-apply triggers an update as well expect(getExpression()).toMatchInlineSnapshot(` - "kibana - | lens_merge_tables layerIds=\\"first\\" tables={other-new-datasource} - | other-new-vis" - `); + "other-new-datasource + | other-new-vis" + `); }); it('should base saveability on working changes when auto-apply disabled', async () => { @@ -305,7 +333,11 @@ describe('workspace_panel', () => { }} framePublicAPI={framePublicAPI} visualizationMap={{ - testVis: { ...mockVisualization, toExpression: () => 'testVis' }, + testVis: { + ...mockVisualization, + toExpression: (state, datasourceLayers, attrs, datasourceExpressionsByLayers = {}) => + toExpr(datasourceExpressionsByLayers), + }, }} ExpressionRenderer={expressionRendererMock} /> @@ -318,10 +350,9 @@ describe('workspace_panel', () => { // allows initial render expect(instance.find(expressionRendererMock).prop('expression')).toMatchInlineSnapshot(` - "kibana - | lens_merge_tables layerIds=\\"first\\" tables={datasource} - | testVis" - `); + "datasource + | testVis" + `); expect(isSaveable()).toBe(true); act(() => { @@ -499,90 +530,6 @@ describe('workspace_panel', () => { }); }); - it('should include data fetching for each layer in the expression', async () => { - const mockDatasource2 = createMockDatasource('a'); - const framePublicAPI = createMockFramePublicAPI(); - framePublicAPI.datasourceLayers = { - first: mockDatasource.publicAPIMock, - second: mockDatasource2.publicAPIMock, - }; - mockDatasource.toExpression.mockReturnValue('datasource'); - mockDatasource.getLayers.mockReturnValue(['first']); - - mockDatasource2.toExpression.mockReturnValue('datasource2'); - mockDatasource2.getLayers.mockReturnValue(['second', 'third']); - - const mounted = await mountWithProvider( - 'testVis' }, - }} - ExpressionRenderer={expressionRendererMock} - />, - - { - preloadedState: { - datasourceStates: { - testDatasource: { - state: {}, - isLoading: false, - }, - mock2: { - state: {}, - isLoading: false, - }, - }, - }, - } - ); - instance = mounted.instance; - instance.update(); - - const ast = fromExpression(instance.find(expressionRendererMock).prop('expression') as string); - - expect(ast.chain[1].arguments.layerIds).toEqual(['first', 'second', 'third']); - expect(ast.chain[1].arguments.tables).toMatchInlineSnapshot(` - Array [ - Object { - "chain": Array [ - Object { - "arguments": Object {}, - "function": "datasource", - "type": "function", - }, - ], - "type": "expression", - }, - Object { - "chain": Array [ - Object { - "arguments": Object {}, - "function": "datasource2", - "type": "function", - }, - ], - "type": "expression", - }, - Object { - "chain": Array [ - Object { - "arguments": Object {}, - "function": "datasource2", - "type": "function", - }, - ], - "type": "expression", - }, - ] - `); - }); - it('should run the expression again if the date range changes', async () => { const framePublicAPI = createMockFramePublicAPI(); framePublicAPI.datasourceLayers = { diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index fe26e635542aa..d706b7d484c63 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -51,6 +51,7 @@ import type { ThemeServiceStart, } from '@kbn/core/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; +import { BrushTriggerEvent, ClickTriggerEvent } from '@kbn/charts-plugin/public'; import { Document } from '../persistence'; import { ExpressionWrapper, ExpressionWrapperProps } from './expression_wrapper'; import { @@ -58,8 +59,6 @@ import { isLensFilterEvent, isLensEditEvent, isLensTableRowContextMenuClickEvent, - LensBrushEvent, - LensFilterEvent, LensTableRowContextMenuEvent, VisualizationMap, Visualization, @@ -94,9 +93,9 @@ interface LensBaseEmbeddableInput extends EmbeddableInput { renderMode?: RenderMode; style?: React.CSSProperties; className?: string; - onBrushEnd?: (data: LensBrushEvent['data']) => void; + onBrushEnd?: (data: BrushTriggerEvent['data']) => void; onLoad?: (isLoading: boolean) => void; - onFilter?: (data: LensFilterEvent['data']) => void; + onFilter?: (data: ClickTriggerEvent['data']) => void; onTableRowClick?: (data: LensTableRowContextMenuEvent['data']) => void; } @@ -348,6 +347,7 @@ export class Embeddable if (!this.savedVis || !this.savedVis.visualizationType) { return []; } + return this.deps.visualizationMap[this.savedVis.visualizationType]?.triggers || []; } @@ -458,6 +458,7 @@ export class Embeddable this.embeddableTitle = this.getTitle(); isDirty = true; } + return isDirty; } @@ -529,6 +530,7 @@ export class Embeddable interactive={!input.disableTriggers} renderMode={input.renderMode} syncColors={input.syncColors} + syncTooltips={input.syncTooltips} hasCompatibleActions={this.hasCompatibleActions} className={input.className} style={input.style} diff --git a/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx b/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx index c2b9d1d2dbb31..27094d154efd2 100644 --- a/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx +++ b/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx @@ -38,6 +38,7 @@ export interface ExpressionWrapperProps { onRender$: () => void; renderMode?: RenderMode; syncColors?: boolean; + syncTooltips?: boolean; hasCompatibleActions?: ReactExpressionRendererProps['hasCompatibleActions']; style?: React.CSSProperties; className?: string; @@ -110,6 +111,7 @@ export function ExpressionWrapper({ onRender$, renderMode, syncColors, + syncTooltips, hasCompatibleActions, style, className, @@ -138,6 +140,7 @@ export function ExpressionWrapper({ inspectorAdapters={lensInspector.adapters} renderMode={renderMode} syncColors={syncColors} + syncTooltips={syncTooltips} executionContext={executionContext} renderError={(errorMessage, error) => { onRuntimeError(); diff --git a/x-pack/plugins/lens/public/expressions.ts b/x-pack/plugins/lens/public/expressions.ts index ba8c07078a208..a52d835e3f002 100644 --- a/x-pack/plugins/lens/public/expressions.ts +++ b/x-pack/plugins/lens/public/expressions.ts @@ -8,22 +8,17 @@ import type { ExpressionsSetup } from '@kbn/expressions-plugin/public'; import { getDatatable } from '../common/expressions/datatable/datatable'; import { datatableColumn } from '../common/expressions/datatable/datatable_column'; -import { mergeTables } from '../common/expressions/merge_tables'; import { renameColumns } from '../common/expressions/rename_columns/rename_columns'; import { formatColumn } from '../common/expressions/format_column'; import { counterRate } from '../common/expressions/counter_rate'; import { getTimeScale } from '../common/expressions/time_scale/time_scale'; -import { lensMultitable } from '../common/expressions'; export const setupExpressions = ( expressions: ExpressionsSetup, formatFactory: Parameters[0], getTimeZone: Parameters[0] ) => { - [lensMultitable].forEach((expressionType) => expressions.registerType(expressionType)); - [ - mergeTables, counterRate, formatColumn, renameColumns, diff --git a/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx b/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx index cb6e6e030eaac..96f4ef7daf89b 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx +++ b/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx @@ -5,10 +5,11 @@ * 2.0. */ -import React, { memo } from 'react'; +import React, { memo, useState } from 'react'; import { EuiFlexGroup, EuiFlexItem, IconType } from '@elastic/eui'; import { Position } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; import type { VisualizationToolbarProps } from '../types'; import { LegendSettingsPopover, @@ -49,6 +50,11 @@ export const HeatmapToolbar = memo( state, frame.datasourceLayers ).truncateText; + + const legendSize = state?.legend.legendSize; + + const [hadAutoLegendSize] = useState(() => legendSize === LegendSize.AUTO); + return ( @@ -62,11 +68,11 @@ export const HeatmapToolbar = memo( buttonDataTestSubj="lnsVisualOptionsButton" > { setState({ ...state, - gridConfig: { ...state.gridConfig, isCellLabelVisible: newMode === 'inside' }, + gridConfig: { ...state.gridConfig, isCellLabelVisible: newMode === 'show' }, }); }} /> @@ -112,16 +118,17 @@ export const HeatmapToolbar = memo( legend: { ...state.legend, shouldTruncate: !current }, }); }} - legendSize={state?.legend.legendSize} - onLegendSizeChange={(legendSize) => { + legendSize={legendSize} + onLegendSizeChange={(newLegendSize) => { setState({ ...state, legend: { ...state.legend, - legendSize, + legendSize: newLegendSize, }, }); }} + showAutoLegendSizeOption={hadAutoLegendSize} /> diff --git a/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx b/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx index 23b484f4cfd13..46c86d8c0adb0 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx @@ -295,8 +295,14 @@ export const getHeatmapVisualization = ({ } }, - toExpression(state, datasourceLayers, attributes): Ast | null { + toExpression( + state, + datasourceLayers, + attributes, + datasourceExpressionsByLayers = {} + ): Ast | null { const datasource = datasourceLayers[state.layerId]; + const datasourceExpression = datasourceExpressionsByLayers[state.layerId]; const originalOrder = datasource.getTableSpec().map(({ columnId }) => columnId); // When we add a column it could be empty, and therefore have no order @@ -304,9 +310,11 @@ export const getHeatmapVisualization = ({ if (!originalOrder || !state.valueAccessor) { return null; } + return { type: 'expression', chain: [ + ...(datasourceExpression?.chain ?? []), { type: 'function', function: FUNCTION_NAME, @@ -382,8 +390,9 @@ export const getHeatmapVisualization = ({ }; }, - toPreviewExpression(state, datasourceLayers): Ast | null { + toPreviewExpression(state, datasourceLayers, datasourceExpressionsByLayers = {}): Ast | null { const datasource = datasourceLayers[state.layerId]; + const datasourceExpression = datasourceExpressionsByLayers[state.layerId]; const originalOrder = datasource.getTableSpec().map(({ columnId }) => columnId); // When we add a column it could be empty, and therefore have no order @@ -395,6 +404,7 @@ export const getHeatmapVisualization = ({ return { type: 'expression', chain: [ + ...(datasourceExpression?.chain ?? []), { type: 'function', function: FUNCTION_NAME, diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index 942df72957fc7..edf57ba703a2e 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -15,6 +15,7 @@ export type { XYState, XYReferenceLineLayerConfig, XYLayerConfig, + ValidLayer, XYDataLayerConfig, XYAnnotationLayerConfig, } from './xy_visualization/types'; @@ -70,7 +71,7 @@ export type { } from './indexpattern_datasource/types'; export type { XYArgs, - YConfig, + ExtendedYConfig, XYRender, LayerType, YAxisMode, @@ -80,28 +81,26 @@ export type { YScaleType, XScaleType, AxisConfig, - ValidLayer, XYCurveType, XYChartProps, LegendConfig, IconPosition, - YConfigResult, + ExtendedYConfigResult, DataLayerArgs, - LensMultiTable, ValueLabelMode, AxisExtentMode, + DataLayerConfig, FittingFunction, AxisExtentConfig, LegendConfigResult, AxesSettingsConfig, GridlinesConfigResult, - DataLayerConfigResult, TickLabelsConfigResult, AxisExtentConfigResult, ReferenceLineLayerArgs, LabelsOrientationConfig, + ReferenceLineLayerConfig, LabelsOrientationConfigResult, - ReferenceLineLayerConfigResult, AxisTitlesVisibilityConfigResult, } from '@kbn/expression-xy-plugin/common'; export type { LensEmbeddableInput } from './embeddable'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx index 1baad07b2198c..ae087221fd49a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx @@ -7,8 +7,9 @@ import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; -import { EuiPopover, EuiPopoverTitle, EuiSelectable, EuiSelectableProps } from '@elastic/eui'; +import { EuiPopover, EuiPopoverTitle, EuiSelectableProps } from '@elastic/eui'; import { ToolbarButton, ToolbarButtonProps } from '@kbn/kibana-react-plugin/public'; +import { DataViewsList } from '@kbn/unified-search-plugin/public'; import { IndexPatternRef } from './types'; import { trackUiEvent } from '../lens_ui_telemetry'; @@ -67,50 +68,26 @@ export function ChangeIndexPattern({ isOpen={isPopoverOpen} closePopover={() => setPopoverIsOpen(false)} display="block" - panelPaddingSize="s" + panelPaddingSize="none" ownFocus >
- + {i18n.translate('xpack.lens.indexPattern.changeDataViewTitle', { defaultMessage: 'Data view', })} - - {...selectableProps} - searchable - singleSelection="always" - options={indexPatternRefs.map(({ title, id }) => ({ - key: id, - label: title, - value: id, - checked: id === indexPatternId ? 'on' : undefined, - }))} - onChange={(choices) => { - const choice = choices.find(({ checked }) => checked) as unknown as { - value: string; - }; + + { trackUiEvent('indexpattern_changed'); - onChangeIndexPattern(choice.value); + onChangeIndexPattern(newId); setPopoverIsOpen(false); }} - searchProps={{ - compressed: true, - ...(selectableProps ? selectableProps.searchProps : undefined), - }} - > - {(list, search) => ( - <> - {search} - {list} - - )} - + currentDataViewId={indexPatternId} + selectableProps={selectableProps} + />
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx index 512ef627c9116..9aaaf9c128a11 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx @@ -6,7 +6,6 @@ */ import React from 'react'; -import { waitFor } from '@testing-library/react'; import ReactDOM from 'react-dom'; import { createMockedDragDropContext } from './mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; @@ -19,7 +18,6 @@ import { act } from 'react-dom/test-utils'; import { coreMock } from '@kbn/core/public/mocks'; import { IndexPatternPrivateState } from './types'; import { mountWithIntl, shallowWithIntl } from '@kbn/test-jest-helpers'; -import { ChangeIndexPattern } from './change_indexpattern'; import { EuiProgress, EuiLoadingSpinner } from '@elastic/eui'; import { documentField } from './document_field'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; @@ -328,14 +326,6 @@ describe('IndexPattern Data Panel', () => { expect(wrapper.find('[data-test-subj="indexPattern-no-indexpatterns"]')).toHaveLength(1); }); - it('should call setState when the index pattern is switched', async () => { - const wrapper = shallowWithIntl(); - - wrapper.find(ChangeIndexPattern).prop('onChangeIndexPattern')('2'); - - expect(defaultProps.onChangeIndexPattern).toHaveBeenCalledWith('2'); - }); - describe('loading existence data', () => { function testProps() { const setState = jest.fn(); @@ -853,90 +843,5 @@ describe('IndexPattern Data Panel', () => { 'memory', ]); }); - describe('edit field list', () => { - beforeEach(() => { - props.indexPatternFieldEditor.userPermissions.editIndexPattern = () => true; - }); - it('should call field editor plugin on clicking add button', async () => { - const mockIndexPattern = {}; - (props.dataViews.get as jest.Mock).mockImplementation(() => - Promise.resolve(mockIndexPattern) - ); - const wrapper = mountWithIntl(); - act(() => { - const popoverTrigger = wrapper.find( - '[data-test-subj="lnsIndexPatternActions-popover"] button' - ); - popoverTrigger.simulate('click'); - }); - - wrapper.update(); - act(() => { - wrapper.find('[data-test-subj="indexPattern-add-field"]').first().simulate('click'); - }); - // wait for indx pattern to be loaded - await waitFor(() => { - expect(props.indexPatternFieldEditor.openEditor).toHaveBeenCalledWith( - expect.objectContaining({ - ctx: expect.objectContaining({ - dataView: mockIndexPattern, - }), - }) - ); - }); - }); - - it('should reload index pattern if callback gets called', async () => { - const mockIndexPattern = { - id: '1', - fields: [ - { - name: 'fieldOne', - aggregatable: true, - }, - ], - metaFields: [], - }; - (props.dataViews.get as jest.Mock).mockImplementation(() => - Promise.resolve(mockIndexPattern) - ); - const wrapper = mountWithIntl(); - - act(() => { - const popoverTrigger = wrapper.find( - '[data-test-subj="lnsIndexPatternActions-popover"] button' - ); - popoverTrigger.simulate('click'); - }); - - wrapper.update(); - act(() => { - wrapper.find('[data-test-subj="indexPattern-add-field"]').first().simulate('click'); - }); - - // wait for indx pattern to be loaded - await act(async () => await new Promise((r) => setTimeout(r, 0))); - - await (props.indexPatternFieldEditor.openEditor as jest.Mock).mock.calls[0][0].onSave(); - // wait for indx pattern to be loaded - await act(async () => await new Promise((r) => setTimeout(r, 0))); - expect(props.onUpdateIndexPattern).toHaveBeenCalledWith( - expect.objectContaining({ - fields: [ - expect.objectContaining({ - name: 'fieldOne', - }), - expect.anything(), - ], - }) - ); - }); - - it('should not render add button without permissions', () => { - props.indexPatternFieldEditor.userPermissions.editIndexPattern = () => false; - const wrapper = mountWithIntl(); - expect(wrapper.find('[data-test-subj="indexPattern-add-field"]').exists()).toBe(false); - }); - }); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx index ab437b9328e7e..d4cdca9a4c7fa 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx @@ -20,7 +20,6 @@ import { EuiFilterGroup, EuiFilterButton, EuiScreenReaderOnly, - EuiButtonIcon, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import type { EsQueryConfig, Query, Filter } from '@kbn/es-query'; @@ -47,6 +46,8 @@ import { trackUiEvent } from '../lens_ui_telemetry'; import { loadIndexPatterns, syncExistingFields } from './loader'; import { fieldExists } from './pure_helpers'; import { Loader } from '../loader'; +import { LensFieldIcon } from './lens_field_icon'; +import { FieldGroups, FieldList } from './field_list'; export type Props = Omit, 'core'> & { data: DataPublicPluginStart; @@ -61,9 +62,6 @@ export type Props = Omit, 'co core: CoreStart; indexPatternFieldEditor: IndexPatternFieldEditorStart; }; -import { LensFieldIcon } from './lens_field_icon'; -import { ChangeIndexPattern } from './change_indexpattern'; -import { FieldGroups, FieldList } from './field_list'; function sortFields(fieldA: IndexPatternField, fieldB: IndexPatternField) { return fieldA.displayName.localeCompare(fieldB.displayName, undefined, { sensitivity: 'base' }); @@ -573,11 +571,6 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ [currentIndexPattern.id, dataViews, editPermission, indexPatternFieldEditor, refreshFieldList] ); - const addField = useMemo( - () => (editPermission && editField ? () => editField(undefined, 'add') : undefined), - [editField, editPermission] - ); - const fieldProps = useMemo( () => ({ core, @@ -603,8 +596,6 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ ] ); - const [popoverOpen, setPopoverOpen] = useState(false); - return ( - - - - { - onChangeIndexPattern(newId); - clearLocalState(); - }} - /> - - {addField && ( - - { - setPopoverOpen(false); - }} - ownFocus - data-test-subj="lnsIndexPatternActions-popover" - button={ - { - setPopoverOpen(!popoverOpen); - }} - /> - } - > - { - setPopoverOpen(false); - addField(); - }} - > - {i18n.translate('xpack.lens.indexPatterns.addFieldButton', { - defaultMessage: 'Add field to data view', - })} - , - { - setPopoverOpen(false); - core.application.navigateToApp('management', { - path: `/kibana/indexPatterns/patterns/${currentIndexPattern.id}`, - }); - }} - > - {i18n.translate('xpack.lens.indexPatterns.manageFieldButton', { - defaultMessage: 'Manage data view fields', - })} - , - ]} - /> - - - )} - - (null); + useEffect(() => { + return () => { + if (helpPopoverContainer.current) { + ReactDOM.unmountComponentAtNode(helpPopoverContainer.current); + document.body.removeChild(helpPopoverContainer.current); + } + }; + }, []); // Operations are compatible if they match inputs. They are always compatible in // the empty state. Field-based operations are not compatible with field-less operations. @@ -309,6 +320,45 @@ export function DimensionEditor(props: DimensionEditorProps) { compatibleWithCurrentField ? '' : ' incompatible' }`, [`aria-pressed`]: isActive, + extraAction: operationDefinitionMap[operationType].helpComponent + ? { + color: 'primary', + onClick: (e) => { + if (!helpPopoverContainer.current) { + const container = document.createElement('div'); + helpPopoverContainer.current = container; + document.body.appendChild(container); + const HelpComponent = operationDefinitionMap[operationType].helpComponent!; + const element = ( + { + if (helpPopoverContainer.current) { + ReactDOM.unmountComponentAtNode(helpPopoverContainer.current); + document.body.removeChild(helpPopoverContainer.current); + helpPopoverContainer.current = null; + } + }} + > + + + ); + ReactDOM.render(element, helpPopoverContainer.current); + } else { + ReactDOM.unmountComponentAtNode(helpPopoverContainer.current); + document.body.removeChild(helpPopoverContainer.current); + helpPopoverContainer.current = null; + } + }, + iconType: 'documentation', + iconSize: 's', + 'aria-label': i18n.translate('xpack.lens.indexPattern.helpLabel', { + defaultMessage: 'Function help', + }), + } + : undefined, onClick() { if ( ['none', 'fullReference', 'managedReference'].includes( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index 3ab3633725678..7f21cf21000b1 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -635,7 +635,7 @@ describe('IndexPatternDimensionEditorPanel', () => { act(() => { wrapper - .find('input[data-test-subj="indexPattern-label-edit"]') + .find('input[data-test-subj="column-label-edit"]') .simulate('change', { target: { value: 'New Label' } }); }); @@ -739,7 +739,7 @@ describe('IndexPatternDimensionEditorPanel', () => { act(() => { wrapper - .find('input[data-test-subj="indexPattern-label-edit"]') + .find('input[data-test-subj="column-label-edit"]') .simulate('change', { target: { value: 'Sum of bytes' } }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/help_popover.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/help_popover.tsx index 88fb96b58cd21..a1bbc7ccd55f1 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/help_popover.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/help_popover.tsx @@ -12,6 +12,8 @@ import { EuiLinkButtonProps, EuiPopover, EuiPopoverProps, + EuiWrappingPopover, + EuiWrappingPopoverProps, EuiPopoverTitle, EuiText, } from '@elastic/eui'; @@ -75,3 +77,43 @@ export const HelpPopover = ({ ); }; + +export const WrappingHelpPopover = ({ + anchorPosition, + button, + children, + closePopover, + isOpen, + title, +}: { + anchorPosition?: EuiWrappingPopoverProps['anchorPosition']; + button: EuiWrappingPopoverProps['button']; + children: ReactNode; + closePopover: EuiWrappingPopoverProps['closePopover']; + isOpen: EuiWrappingPopoverProps['isOpen']; + title?: string; +}) => { + useEffect(() => { + if (isOpen) { + trackUiEvent('open_help_popover'); + } + }, [isOpen]); + return ( + + {title && {title}} + + + {children} + + + ); +}; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts index db10c420b90de..6806b1ce47795 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts @@ -383,6 +383,11 @@ describe('IndexPattern Data Source', () => { expect(indexPatternDatasource.toExpression(state, 'first')).toMatchInlineSnapshot(` Object { "chain": Array [ + Object { + "arguments": Object {}, + "function": "kibana", + "type": "function", + }, Object { "arguments": Object { "aggs": Array [ @@ -552,7 +557,7 @@ describe('IndexPattern Data Source', () => { const state = enrichBaseState(queryBaseState); const ast = indexPatternDatasource.toExpression(state, 'first') as Ast; - expect(ast.chain[0].arguments.timeFields).toEqual(['timestamp', 'another_datefield']); + expect(ast.chain[1].arguments.timeFields).toEqual(['timestamp', 'another_datefield']); }); it('should pass time shift parameter to metric agg functions', async () => { @@ -589,7 +594,7 @@ describe('IndexPattern Data Source', () => { const state = enrichBaseState(queryBaseState); const ast = indexPatternDatasource.toExpression(state, 'first') as Ast; - expect((ast.chain[0].arguments.aggs[1] as Ast).chain[0].arguments.timeShift).toEqual(['1d']); + expect((ast.chain[1].arguments.aggs[1] as Ast).chain[0].arguments.timeShift).toEqual(['1d']); }); it('should wrap filtered metrics in filtered metric aggregation', async () => { @@ -638,7 +643,7 @@ describe('IndexPattern Data Source', () => { const state = enrichBaseState(queryBaseState); const ast = indexPatternDatasource.toExpression(state, 'first') as Ast; - expect(ast.chain[0].arguments.aggs[0]).toMatchInlineSnapshot(` + expect(ast.chain[1].arguments.aggs[0]).toMatchInlineSnapshot(` Object { "chain": Array [ Object { @@ -898,8 +903,8 @@ describe('IndexPattern Data Source', () => { const state = enrichBaseState(queryBaseState); const ast = indexPatternDatasource.toExpression(state, 'first') as Ast; - expect(ast.chain[0].arguments.metricsAtAllLevels).toEqual([false]); - expect(JSON.parse(ast.chain[1].arguments.idMap[0] as string)).toEqual({ + expect(ast.chain[1].arguments.metricsAtAllLevels).toEqual([false]); + expect(JSON.parse(ast.chain[2].arguments.idMap[0] as string)).toEqual({ 'col-0-0': expect.objectContaining({ id: 'bucket1' }), 'col-1-1': expect.objectContaining({ id: 'bucket2' }), 'col-2-2': expect.objectContaining({ id: 'metric' }), @@ -939,8 +944,8 @@ describe('IndexPattern Data Source', () => { const state = enrichBaseState(queryBaseState); const ast = indexPatternDatasource.toExpression(state, 'first') as Ast; - expect(ast.chain[0].arguments.timeFields).toEqual(['timestamp']); - expect(ast.chain[0].arguments.timeFields).not.toContain('timefield'); + expect(ast.chain[1].arguments.timeFields).toEqual(['timestamp']); + expect(ast.chain[1].arguments.timeFields).not.toContain('timefield'); }); describe('references', () => { @@ -988,7 +993,7 @@ describe('IndexPattern Data Source', () => { const ast = indexPatternDatasource.toExpression(state, 'first') as Ast; // @ts-expect-error we can't isolate just the reference type expect(operationDefinitionMap.testReference.toExpression).toHaveBeenCalled(); - expect(ast.chain[2]).toEqual('mock'); + expect(ast.chain[3]).toEqual('mock'); }); it('should keep correct column mapping keys with reference columns present', async () => { @@ -1021,7 +1026,7 @@ describe('IndexPattern Data Source', () => { const state = enrichBaseState(queryBaseState); const ast = indexPatternDatasource.toExpression(state, 'first') as Ast; - expect(JSON.parse(ast.chain[1].arguments.idMap[0] as string)).toEqual({ + expect(JSON.parse(ast.chain[2].arguments.idMap[0] as string)).toEqual({ 'col-0-0': expect.objectContaining({ id: 'col1', }), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index b72519c2191be..8ed569ddfd328 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -37,6 +37,7 @@ import { changeLayerIndexPattern, extractReferences, injectReferences, + loadIndexPatterns, } from './loader'; import { toExpression } from './to_expression'; import { @@ -183,6 +184,10 @@ export function getIndexPatternDatasource({ return extractReferences(state); }, + getCurrentIndexPatternId(state: IndexPatternPrivateState) { + return state.currentIndexPatternId; + }, + insertLayer(state: IndexPatternPrivateState, newLayerId: string) { return { ...state, @@ -235,6 +240,7 @@ export function getIndexPatternDatasource({ if (staticValue == null) { return state; } + return mergeLayer({ state, layerId, @@ -445,6 +451,30 @@ export function getIndexPatternDatasource({ : undefined; }, + updateCurrentIndexPatternId: ({ state, indexPatternId, setState }) => { + handleChangeIndexPattern(indexPatternId, state, setState); + }, + + refreshIndexPatternsList: async ({ indexPatternId, setState }) => { + const newlyMappedIndexPattern = await loadIndexPatterns({ + indexPatternsService: dataViews, + cache: {}, + patterns: [indexPatternId], + }); + const indexPatternRefs = await dataViews.getIdsWithTitle(); + const indexPattern = newlyMappedIndexPattern[indexPatternId]; + setState((s) => { + return { + ...s, + indexPatterns: { + ...s.indexPatterns, + [indexPattern.id]: indexPattern, + }, + indexPatternRefs, + }; + }); + }, + // Reset the temporary invalid state when closing the editor, but don't // update the state if it's not needed updateStateOnCloseDimension: ({ state, layerId }) => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.test.tsx index 91b9de58bdaa1..dba57f2fcb03e 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.test.tsx @@ -11,6 +11,7 @@ import { IndexPatternLayerPanelProps, LayerPanel } from './layerpanel'; import { shallowWithIntl as shallow } from '@kbn/test-jest-helpers'; import { ShallowWrapper } from 'enzyme'; import { EuiSelectable } from '@elastic/eui'; +import { DataViewsList } from '@kbn/unified-search-plugin/public'; import { ChangeIndexPattern } from './change_indexpattern'; import { getFieldByNameFactory } from './pure_helpers'; import { TermsIndexPatternColumn } from './operations'; @@ -212,7 +213,14 @@ describe('Layer Data Panel', () => { }); function getIndexPatternPickerList(instance: ShallowWrapper) { - return instance.find(ChangeIndexPattern).first().dive().find(EuiSelectable); + return instance + .find(ChangeIndexPattern) + .first() + .dive() + .find(DataViewsList) + .first() + .dive() + .find(EuiSelectable); } function selectIndexPatternPickerOption(instance: ShallowWrapper, selectedLabel: string) { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.tsx index f8548321e49bd..efa1ef509b12d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.tsx @@ -22,7 +22,6 @@ export function LayerPanel({ state, layerId, onChangeIndexPattern }: IndexPatter const layer = state.layers[layerId]; const indexPattern = state.indexPatterns[layer.indexPatternId]; - const notFoundTitleLabel = i18n.translate('xpack.lens.layerPanel.missingDataView', { defaultMessage: 'Data view not found', }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx index aa68c8409ad80..cf96bcd11b788 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx @@ -28,7 +28,6 @@ import { combineErrorMessages, } from '../helpers'; import { adjustTimeScaleOnOtherColumnChange } from '../../time_scale_utils'; -import { HelpPopover, HelpPopoverButton } from '../../../help_popover'; import type { OperationDefinition, ParamEditorProps } from '..'; import { getDisallowedPreviousShiftMessage } from '../../../time_shift_utils'; @@ -129,7 +128,10 @@ export const movingAverageOperation: OperationDefinition< getDisallowedPreviousShiftMessage(layer, columnId), ]); }, - getHelpMessage: () => , + helpComponent: () => , + helpComponentTitle: i18n.translate('xpack.lens.indexPattern.movingAverage.titleHelp', { + defaultMessage: 'How moving average works', + }), getDisabledStatus(indexPattern, layer, layerType) { const opName = i18n.translate('xpack.lens.indexPattern.movingAverage', { defaultMessage: 'Moving average', @@ -217,27 +219,8 @@ function MovingAverageParamEditor({ } const MovingAveragePopup = () => { - const [isPopoverOpen, setIsPopoverOpen] = useState(false); return ( - { - setIsPopoverOpen(!isPopoverOpen); - }} - > - {i18n.translate('xpack.lens.indexPattern.movingAverage.helpText', { - defaultMessage: 'How it works', - })} - - } - closePopover={() => setIsPopoverOpen(false)} - isOpen={isPopoverOpen} - title={i18n.translate('xpack.lens.indexPattern.movingAverage.titleHelp', { - defaultMessage: 'How moving average works', - })} - > + <>

{ defaultMessage="The first moving average value starts at the second item." />

-
+ ); }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx index d4491fbba00cf..e1e5f39a8cc48 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import type { DateHistogramIndexPatternColumn } from './date_histogram'; import { dateHistogramOperation } from '.'; -import { shallow } from 'enzyme'; +import { mount, shallow } from 'enzyme'; import { EuiSwitch } from '@elastic/eui'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from '@kbn/core/public'; @@ -18,6 +18,7 @@ import { dataPluginMock, getCalculateAutoTimeExpression } from '@kbn/data-plugin import { createMockedIndexPattern } from '../../mocks'; import type { IndexPatternLayer, IndexPattern } from '../../types'; import { getFieldByNameFactory } from '../../pure_helpers'; +import { act } from 'react-dom/test-utils'; const dataStart = dataPluginMock.createStartContract(); const unifiedSearchStart = unifiedSearchPluginMock.createStartContract(); @@ -312,8 +313,9 @@ describe('date_histogram', () => { /> ); - expect(instance.find('[data-test-subj="lensDateHistogramValue"]').prop('value')).toEqual(42); - expect(instance.find('[data-test-subj="lensDateHistogramUnit"]').prop('value')).toEqual('w'); + expect( + instance.find('[data-test-subj="lensDateHistogramInterval"]').prop('selectedOptions') + ).toEqual([expect.objectContaining({ label: '42w' })]); }); it('should render current value for other index pattern', () => { @@ -348,11 +350,12 @@ describe('date_histogram', () => { /> ); - expect(instance.find('[data-test-subj="lensDateHistogramValue"]').prop('value')).toEqual(''); - expect(instance.find('[data-test-subj="lensDateHistogramUnit"]').prop('value')).toEqual('d'); + expect( + instance.find('[data-test-subj="lensDateHistogramInterval"]').prop('selectedOptions') + ).toEqual([expect.objectContaining({ key: 'd' })]); }); - it('should render disabled switch and no time interval control for auto interval', () => { + it('should render time interval control set to auto for auto interval', () => { const thirdLayer: IndexPatternLayer = { indexPatternId: '1', columnOrder: ['col1'], @@ -382,9 +385,9 @@ describe('date_histogram', () => { indexPattern={indexPattern1} /> ); - expect(instance.find('[data-test-subj="lensDateHistogramValue"]').exists()).toBeFalsy(); - expect(instance.find('[data-test-subj="lensDateHistogramUnit"]').exists()).toBeFalsy(); - expect(instance.find(EuiSwitch).at(1).prop('checked')).toBe(false); + expect( + instance.find('[data-test-subj="lensDateHistogramInterval"]').prop('selectedOptions') + ).toEqual([expect.objectContaining({ key: 'auto' })]); }); it('should allow switching to manual interval', () => { @@ -461,7 +464,7 @@ describe('date_histogram', () => { ); instance .find(EuiSwitch) - .at(2) + .at(1) .simulate('change', { target: { checked: false }, }); @@ -502,16 +505,14 @@ describe('date_histogram', () => { indexPattern={{ ...indexPattern1, timeFieldName: undefined }} /> ); - instance - .find(EuiSwitch) - .at(1) - .simulate('change', { - target: { checked: false }, - }); + ( + instance + .find('[data-test-subj="lensDateHistogramInterval"]') + .prop('onChange') as unknown as (v: Array<{ key: string }>) => void + )([{ key: 'auto' }]); expect(updateLayerSpy).toHaveBeenCalled(); const newLayer = updateLayerSpy.mock.calls[0][0]; expect(newLayer).toHaveProperty('columns.col1.params.ignoreTimeRange', false); - expect(newLayer).toHaveProperty('columns.col1.params.interval', 'auto'); }); it('turns off drop partial bucket on tuning off time range ignore', () => { @@ -560,12 +561,14 @@ describe('date_histogram', () => { currentColumn={layer.columns.col1 as DateHistogramIndexPatternColumn} /> ); - instance.find('[data-test-subj="lensDateHistogramValue"]').simulate('change', { - target: { - value: '2', - }, - }); - expect(updateLayerSpy).toHaveBeenCalledWith(layerWithInterval('1w')); + ( + instance.find('[data-test-subj="lensDateHistogramInterval"]').prop('onCreateOption') as ( + s: string + ) => void + )('2w'); + expect( + instance.find('[data-test-subj="lensDateHistogramInterval"]').prop('isInvalid') + ).toBeTruthy(); }); it('should display error if an invalid interval is specified', () => { @@ -580,7 +583,9 @@ describe('date_histogram', () => { currentColumn={testLayer.columns.col1 as DateHistogramIndexPatternColumn} /> ); - expect(instance.find('[data-test-subj="lensDateHistogramError"]').exists()).toBeTruthy(); + expect( + instance.find('[data-test-subj="lensDateHistogramInterval"]').prop('isInvalid') + ).toBeTruthy(); }); it('should not display error if interval value is blank', () => { @@ -595,7 +600,9 @@ describe('date_histogram', () => { currentColumn={testLayer.columns.col1 as DateHistogramIndexPatternColumn} /> ); - expect(instance.find('[data-test-subj="lensDateHistogramError"]').exists()).toBeFalsy(); + expect( + instance.find('[data-test-subj="lensDateHistogramInterval"]').prop('isInvalid') + ).toBeFalsy(); }); it('should display error if interval value is 0', () => { @@ -610,12 +617,14 @@ describe('date_histogram', () => { currentColumn={testLayer.columns.col1 as DateHistogramIndexPatternColumn} /> ); - expect(instance.find('[data-test-subj="lensDateHistogramError"]').exists()).toBeTruthy(); + expect( + instance.find('[data-test-subj="lensDateHistogramInterval"]').prop('isInvalid') + ).toBeTruthy(); }); - it('should update the unit', () => { + it('should update the unit', async () => { const updateLayerSpy = jest.fn(); - const instance = shallow( + const instance = mount( { currentColumn={layer.columns.col1 as DateHistogramIndexPatternColumn} /> ); - instance.find('[data-test-subj="lensDateHistogramUnit"]').simulate('change', { - target: { - value: 'd', - }, + act(() => { + ( + instance + .find('[data-test-subj="lensDateHistogramInterval"]') + .at(0) + .prop('onCreateOption') as (s: string) => void + )('42d'); }); - expect(updateLayerSpy).toHaveBeenCalledWith(layerWithInterval('42d')); + expect(updateLayerSpy.mock.calls[0][0](layer)).toEqual(layerWithInterval('42d')); }); it('should update the value', () => { const updateLayerSpy = jest.fn(); const testLayer = layerWithInterval('42d'); - const instance = shallow( + const instance = mount( { currentColumn={testLayer.columns.col1 as DateHistogramIndexPatternColumn} /> ); - instance.find('[data-test-subj="lensDateHistogramValue"]').simulate('change', { - target: { - value: '9', - }, - }); - expect(updateLayerSpy).toHaveBeenCalledWith(layerWithInterval('9d')); + act(() => + ( + instance + .find('[data-test-subj="lensDateHistogramInterval"]') + .at(0) + .prop('onCreateOption') as (s: string) => void + )('9d') + ); + expect(updateLayerSpy.mock.calls[0][0](layer)).toEqual(layerWithInterval('9d')); }); it('should not render options if they are restricted', () => { @@ -695,7 +710,7 @@ describe('date_histogram', () => { /> ); - expect(instance.find('[data-test-subj="lensDateHistogramValue"]').exists()).toBeFalsy(); + expect(instance.find('[data-test-subj="lensDateHistogramInterval"]').exists()).toBeFalsy(); }); it('should allow the drop of partial buckets', () => { @@ -735,7 +750,7 @@ describe('date_histogram', () => { target: { checked: true }, }); expect(updateLayerSpy).toHaveBeenCalled(); - const newLayer = updateLayerSpy.mock.calls[0][0]; + const newLayer = updateLayerSpy.mock.calls[0][0](layer); expect(newLayer).toHaveProperty('columns.col1.params.dropPartials', true); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx index f96e584326340..3b6d75879640d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx @@ -5,38 +5,32 @@ * 2.0. */ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiBasicTable, EuiCode, - EuiFieldNumber, - EuiFlexGroup, - EuiFlexItem, + EuiComboBox, EuiFormRow, EuiIconTip, - EuiSelect, - EuiSpacer, EuiSwitch, EuiSwitchEvent, - EuiTextColor, } from '@elastic/eui'; import { AggFunctionsMapping, - DataPublicPluginStart, + AggParamOption, IndexPatternAggRestrictions, search, UI_SETTINGS, } from '@kbn/data-plugin/public'; -import { extendedBoundsToAst } from '@kbn/data-plugin/common'; +import { extendedBoundsToAst, intervalOptions } from '@kbn/data-plugin/common'; import { buildExpressionFunction } from '@kbn/expressions-plugin/public'; import { updateColumnParam } from '../layer_helpers'; import { OperationDefinition, ParamEditorProps } from '.'; import { FieldBasedIndexPatternColumn } from './column_types'; import { getInvalidFieldMessage, getSafeName } from './helpers'; -import { HelpPopover, HelpPopoverButton } from '../../help_popover'; import { IndexPatternLayer } from '../../types'; import { TooltipWrapper } from '../../../shared_components'; @@ -96,7 +90,6 @@ export const dateHistogramOperation: OperationDefinition< ) || []), getMultipleDateHistogramsErrorMessage(layer, columnId) || '', ].filter(Boolean), - getHelpMessage: (props) => , getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type }) => { if ( (type === 'date' || type === 'date_range') && @@ -187,54 +180,81 @@ export const dateHistogramOperation: OperationDefinition< const intervalIsRestricted = field!.aggregationRestrictions && field!.aggregationRestrictions.date_histogram; - const interval = parseInterval(currentColumn.params.interval); + const [intervalInput, setIntervalInput] = useState(currentColumn.params.interval); + const interval = intervalInput === autoInterval ? autoInterval : parseInterval(intervalInput); // We force the interval value to 1 if it's empty, since that is the ES behavior, // and the isValidInterval function doesn't handle the empty case properly. Fixing // isValidInterval involves breaking changes in other areas. - const isValid = isValidInterval( - `${interval.value === '' ? '1' : interval.value}${interval.unit}`, - restrictedInterval(field!.aggregationRestrictions) - ); + const isValid = + (!currentColumn.params.ignoreTimeRange && intervalInput === autoInterval) || + (interval !== autoInterval && + intervalInput !== '' && + isValidInterval( + `${interval.value === '' ? '1' : interval.value}${interval.unit}`, + restrictedInterval(field!.aggregationRestrictions) + )); - const onChangeAutoInterval = useCallback( + const onChangeDropPartialBuckets = useCallback( (ev: EuiSwitchEvent) => { - const { fromDate, toDate } = dateRange; - const value = ev.target.checked - ? data.search.aggs.calculateAutoTimeExpression({ from: fromDate, to: toDate }) || '1h' - : autoInterval; - updateLayer( + updateLayer((newLayer) => updateColumnParam({ - layer: updateColumnParam({ layer, columnId, paramName: 'interval', value }), + layer: newLayer, columnId, - paramName: 'ignoreTimeRange', - value: false, + paramName: 'dropPartials', + value: ev.target.checked, }) ); }, - [dateRange, data.search.aggs, updateLayer, layer, columnId] + [columnId, updateLayer] ); - const onChangeDropPartialBuckets = useCallback( - (ev: EuiSwitchEvent) => { - updateLayer( - updateColumnParam({ - layer, - columnId, - paramName: 'dropPartials', - value: ev.target.checked, - }) + const setInterval = useCallback( + (newInterval: typeof interval) => { + const isCalendarInterval = + newInterval !== autoInterval && calendarOnlyIntervals.has(newInterval.unit); + const value = + newInterval === autoInterval + ? autoInterval + : `${isCalendarInterval ? '1' : newInterval.value}${newInterval.unit || 'd'}`; + + updateLayer((newLayer) => + updateColumnParam({ layer: newLayer, columnId, paramName: 'interval', value }) ); }, - [columnId, layer, updateLayer] + [columnId, updateLayer] ); - const setInterval = (newInterval: typeof interval) => { - const isCalendarInterval = calendarOnlyIntervals.has(newInterval.unit); - const value = `${isCalendarInterval ? '1' : newInterval.value}${newInterval.unit || 'd'}`; + const options = (intervalOptions || []) + .filter((option) => option.val !== autoInterval) + .map((option: AggParamOption) => { + return { label: option.display, key: option.val }; + }, []); - updateLayer(updateColumnParam({ layer, columnId, paramName: 'interval', value })); - }; + options.unshift({ + label: i18n.translate('xpack.lens.indexPattern.autoIntervalLabel', { + defaultMessage: 'Auto ({interval})', + values: { + interval: + data.search.aggs.calculateAutoTimeExpression({ + from: dateRange.fromDate, + to: dateRange.toDate, + }) || '1h', + }, + }), + key: autoInterval, + }); + + const definedOption = options.find((o) => o.key === intervalInput); + const selectedOptions = definedOption + ? [definedOption] + : [{ label: intervalInput, key: intervalInput }]; + + useEffect(() => { + if (isValid && intervalInput !== currentColumn.params.interval) { + setInterval(parseInterval(intervalInput)); + } + }, [intervalInput, isValid, currentColumn.params.interval, setInterval]); const bindToGlobalTimePickerValue = indexPattern.timeFieldName === field?.name || !currentColumn.params.ignoreTimeRange; @@ -263,187 +283,122 @@ export const dateHistogramOperation: OperationDefinition< /> - {!intervalIsRestricted && ( - - + {intervalIsRestricted ? ( + - - )} - {currentColumn.params.interval !== autoInterval && ( - <> - - {intervalIsRestricted ? ( - - ) : ( - <> - - - { - const newInterval = { - ...interval, - value: e.target.value, - }; - setInterval(newInterval); - }} - step={1} - /> - - - { - const newInterval = { - ...interval, - unit: e.target.value, - }; - setInterval(newInterval); - }} - isInvalid={!isValid} - options={[ - { - value: 'ms', - text: i18n.translate( - 'xpack.lens.indexPattern.dateHistogram.milliseconds', - { - defaultMessage: 'milliseconds', - } - ), - }, - { - value: 's', - text: i18n.translate('xpack.lens.indexPattern.dateHistogram.seconds', { - defaultMessage: 'seconds', - }), - }, - { - value: 'm', - text: i18n.translate('xpack.lens.indexPattern.dateHistogram.minutes', { - defaultMessage: 'minutes', - }), - }, - { - value: 'h', - text: i18n.translate('xpack.lens.indexPattern.dateHistogram.hours', { - defaultMessage: 'hours', - }), - }, - { - value: 'd', - text: i18n.translate('xpack.lens.indexPattern.dateHistogram.days', { - defaultMessage: 'days', - }), - }, - { - value: 'w', - text: i18n.translate('xpack.lens.indexPattern.dateHistogram.week', { - defaultMessage: 'week', - }), - }, - { - value: 'M', - text: i18n.translate('xpack.lens.indexPattern.dateHistogram.month', { - defaultMessage: 'month', - }), - }, - // Quarterly intervals appear to be unsupported by esaggs - { - value: 'y', - text: i18n.translate('xpack.lens.indexPattern.dateHistogram.year', { - defaultMessage: 'year', - }), - }, - ]} - /> - - - {!isValid && ( - <> - - - {i18n.translate('xpack.lens.indexPattern.invalidInterval', { - defaultMessage: 'Invalid interval value', - })} - - - )} - - )} - - - - {i18n.translate( - 'xpack.lens.indexPattern.dateHistogram.bindToGlobalTimePicker', - { - defaultMessage: 'Bind to global time picker', - } - )}{' '} - - - } - disabled={indexPattern.timeFieldName === field?.name} - checked={bindToGlobalTimePickerValue} - onChange={() => { + ) : ( + { + const newValue = opts.length ? opts[0].key! : ''; + setIntervalInput(newValue); + if (newValue === autoInterval && currentColumn.params.ignoreTimeRange) { updateLayer( updateColumnParam({ layer, columnId, paramName: 'ignoreTimeRange', - value: !currentColumn.params.ignoreTimeRange, + value: false, }) ); - }} - compressed - /> - - - )} + } + }} + onCreateOption={(customValue: string) => setIntervalInput(customValue.trim())} + options={options} + selectedOptions={selectedOptions} + singleSelection={{ asPlainText: true }} + placeholder={i18n.translate( + 'xpack.lens.indexPattern.dateHistogram.selectIntervalPlaceholder', + { + defaultMessage: 'Select an interval', + } + )} + /> + )} + + + + {i18n.translate('xpack.lens.indexPattern.dateHistogram.bindToGlobalTimePicker', { + defaultMessage: 'Bind to global time picker', + })}{' '} + + + } + disabled={indexPattern.timeFieldName === field?.name} + checked={bindToGlobalTimePickerValue} + onChange={() => { + let newLayer = updateColumnParam({ + layer, + columnId, + paramName: 'ignoreTimeRange', + value: !currentColumn.params.ignoreTimeRange, + }); + if ( + !currentColumn.params.ignoreTimeRange && + currentColumn.params.interval === autoInterval + ) { + const newFixedInterval = + data.search.aggs.calculateAutoTimeExpression({ + from: dateRange.fromDate, + to: dateRange.toDate, + }) || '1h'; + newLayer = updateColumnParam({ + layer: newLayer, + columnId, + paramName: 'interval', + value: newFixedInterval, + }); + setIntervalInput(newFixedInterval); + } + updateLayer(newLayer); + }} + compressed + /> + ); }, + helpComponentTitle: i18n.translate('xpack.lens.indexPattern.dateHistogram.titleHelp', { + defaultMessage: 'How auto date histogram works', + }), + helpComponent() { + const infiniteBound = i18n.translate('xpack.lens.indexPattern.dateHistogram.moreThanYear', { + defaultMessage: 'More than a year', + }); + const upToLabel = i18n.translate('xpack.lens.indexPattern.dateHistogram.upTo', { + defaultMessage: 'Up to', + }); + + return ( + <> +

+ {i18n.translate('xpack.lens.indexPattern.dateHistogram.autoBasicExplanation', { + defaultMessage: 'The auto date histogram splits a data field into buckets by interval.', + })} +

+ +

+ {UI_SETTINGS.HISTOGRAM_MAX_BARS}, + targetBarSetting: {UI_SETTINGS.HISTOGRAM_BAR_TARGET}, + }} + /> +

+ +

+ {i18n.translate('xpack.lens.indexPattern.dateHistogram.autoAdvancedExplanation', { + defaultMessage: 'The interval follows this logic:', + })} +

+ + ({ + bound: typeof bound === 'number' ? infiniteBound : `${upToLabel} ${boundLabel}`, + interval: intervalLabel, + }))} + columns={[ + { + field: 'bound', + name: i18n.translate('xpack.lens.indexPattern.dateHistogram.autoBoundHeader', { + defaultMessage: 'Target interval measured', + }), + }, + { + field: 'interval', + name: i18n.translate('xpack.lens.indexPattern.dateHistogram.autoIntervalHeader', { + defaultMessage: 'Interval used', + }), + }, + ]} + /> + + ); + }, }; function parseInterval(currentInterval: string) { @@ -491,79 +505,3 @@ function restrictedInterval(aggregationRestrictions?: Partial { - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const infiniteBound = i18n.translate('xpack.lens.indexPattern.dateHistogram.moreThanYear', { - defaultMessage: 'More than a year', - }); - const upToLabel = i18n.translate('xpack.lens.indexPattern.dateHistogram.upTo', { - defaultMessage: 'Up to', - }); - - return ( - { - setIsPopoverOpen(!isPopoverOpen); - }} - > - {i18n.translate('xpack.lens.indexPattern.dateHistogram.autoHelpText', { - defaultMessage: 'How it works', - })} - - } - closePopover={() => setIsPopoverOpen(false)} - isOpen={isPopoverOpen} - title={i18n.translate('xpack.lens.indexPattern.dateHistogram.titleHelp', { - defaultMessage: 'How auto date histogram works', - })} - > -

- {i18n.translate('xpack.lens.indexPattern.dateHistogram.autoBasicExplanation', { - defaultMessage: 'The auto date histogram splits a data field into buckets by interval.', - })} -

- -

- {UI_SETTINGS.HISTOGRAM_MAX_BARS}, - targetBarSetting: {UI_SETTINGS.HISTOGRAM_BAR_TARGET}, - }} - /> -

- -

- {i18n.translate('xpack.lens.indexPattern.dateHistogram.autoAdvancedExplanation', { - defaultMessage: 'The interval follows this logic:', - })} -

- - ({ - bound: typeof bound === 'number' ? infiniteBound : `${upToLabel} ${boundLabel}`, - interval: intervalLabel, - }))} - columns={[ - { - field: 'bound', - name: i18n.translate('xpack.lens.indexPattern.dateHistogram.autoBoundHeader', { - defaultMessage: 'Target interval measured', - }), - }, - { - field: 'interval', - name: i18n.translate('xpack.lens.indexPattern.dateHistogram.autoIntervalHeader', { - defaultMessage: 'Interval used', - }), - }, - ]} - /> -
- ); -}; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts index e62bab6bd808b..36039a058908b 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts @@ -364,6 +364,14 @@ interface BaseOperationDefinitionProps * are not pass the transferable checks */ getNonTransferableFields?: (column: C, indexPattern: IndexPattern) => string[]; + /** + * Component rendered as inline help + */ + helpComponent?: React.ComponentType<{}>; + /** + * Title for the help component + */ + helpComponentTitle?: string; } interface BaseBuildColumnArgs { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts index aee1abd34e7cb..0307e748ac1fb 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts @@ -277,6 +277,7 @@ function getExpressionForLayer( return { type: 'expression', chain: [ + { type: 'function', function: 'kibana', arguments: {} }, buildExpressionFunction('esaggs', { index: buildExpression([ buildExpressionFunction( diff --git a/x-pack/plugins/lens/public/metric_visualization/visualization.tsx b/x-pack/plugins/lens/public/metric_visualization/visualization.tsx index 62c607f69265e..786d5b588baef 100644 --- a/x-pack/plugins/lens/public/metric_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/visualization.tsx @@ -15,6 +15,7 @@ import { PaletteOutput, PaletteRegistry, CUSTOM_PALETTE, shiftPalette } from '@k import { ThemeServiceStart } from '@kbn/core/public'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { ColorMode, CustomPaletteState } from '@kbn/charts-plugin/common'; +import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public'; import { getSuggestions } from './metric_suggestions'; import { LensIconChartMetric } from '../assets/chart_metric'; import { Visualization, OperationMetadata, DatasourceLayers } from '../types'; @@ -49,13 +50,15 @@ const toExpression = ( paletteService: PaletteRegistry, state: MetricState, datasourceLayers: DatasourceLayers, - attributes?: Partial> + attributes?: Partial>, + datasourceExpressionsByLayers: Record | undefined = {} ): Ast | null => { if (!state.accessor) { return null; } const [datasource] = Object.values(datasourceLayers); + const datasourceExpression = datasourceExpressionsByLayers[state.layerId]; const operation = datasource && datasource.getOperationForColumnId(state.accessor); const stops = state.palette?.params?.stops || []; @@ -99,6 +102,7 @@ const toExpression = ( return { type: 'expression', chain: [ + ...(datasourceExpression?.chain ?? []), { type: 'function', function: 'metricVis', @@ -225,6 +229,7 @@ export const getMetricVisualization = ({ } ); }, + triggers: [VIS_EVENT_TO_TRIGGER.filter], getConfiguration(props) { const hasColoring = props.state.palette != null; @@ -271,10 +276,23 @@ export const getMetricVisualization = ({ } }, - toExpression: (state, datasourceLayers, attributes) => - toExpression(paletteService, state, datasourceLayers, { ...attributes }), - toPreviewExpression: (state, datasourceLayers) => - toExpression(paletteService, state, datasourceLayers, { mode: 'reduced' }), + toExpression: (state, datasourceLayers, attributes, datasourceExpressionsByLayers) => + toExpression( + paletteService, + state, + datasourceLayers, + { ...attributes }, + datasourceExpressionsByLayers + ), + + toPreviewExpression: (state, datasourceLayers, datasourceExpressionsByLayers) => + toExpression( + paletteService, + state, + datasourceLayers, + { mode: 'reduced' }, + datasourceExpressionsByLayers + ), setDimension({ prevState, columnId }) { return { ...prevState, accessor: columnId }; diff --git a/x-pack/plugins/lens/public/mocks/datasource_mock.ts b/x-pack/plugins/lens/public/mocks/datasource_mock.ts index c30b39476b1ab..cf25828d3322c 100644 --- a/x-pack/plugins/lens/public/mocks/datasource_mock.ts +++ b/x-pack/plugins/lens/public/mocks/datasource_mock.ts @@ -36,6 +36,7 @@ export function createMockDatasource(id: string): DatasourceMock { initialize: jest.fn((_state?) => Promise.resolve()), renderDataPanel: jest.fn(), renderLayerPanel: jest.fn(), + getCurrentIndexPatternId: jest.fn(), toExpression: jest.fn((_frame, _state) => null), insertLayer: jest.fn((_state, _newLayerId) => ({})), removeLayer: jest.fn((_state, _layerId) => {}), diff --git a/x-pack/plugins/lens/public/mocks/index.ts b/x-pack/plugins/lens/public/mocks/index.ts index ab6f2066e8804..58ef8ce05c613 100644 --- a/x-pack/plugins/lens/public/mocks/index.ts +++ b/x-pack/plugins/lens/public/mocks/index.ts @@ -31,14 +31,14 @@ export type FrameMock = jest.Mocked; export const createMockFramePublicAPI = (): FrameMock => ({ datasourceLayers: {}, - dateRange: { fromDate: 'now-7d', toDate: 'now' }, + dateRange: { fromDate: '2022-03-17T08:25:00.000Z', toDate: '2022-04-17T08:25:00.000Z' }, }); export type FrameDatasourceMock = jest.Mocked; export const createMockFrameDatasourceAPI = (): FrameDatasourceMock => ({ datasourceLayers: {}, - dateRange: { fromDate: 'now-7d', toDate: 'now' }, + dateRange: { fromDate: '2022-03-17T08:25:00.000Z', toDate: '2022-04-17T08:25:00.000Z' }, query: { query: '', language: 'lucene' }, filters: [], }); diff --git a/x-pack/plugins/lens/public/mocks/services_mock.tsx b/x-pack/plugins/lens/public/mocks/services_mock.tsx index f5e94d374481a..800ec3dee25b1 100644 --- a/x-pack/plugins/lens/public/mocks/services_mock.tsx +++ b/x-pack/plugins/lens/public/mocks/services_mock.tsx @@ -10,6 +10,8 @@ import { Subject } from 'rxjs'; import { coreMock } from '@kbn/core/public/mocks'; import { navigationPluginMock } from '@kbn/navigation-plugin/public/mocks'; import { UI_SETTINGS } from '@kbn/data-plugin/public'; +import { indexPatternFieldEditorPluginMock } from '@kbn/data-view-field-editor-plugin/public/mocks'; +import { indexPatternEditorPluginMock } from '@kbn/data-view-editor-plugin/public/mocks'; import { inspectorPluginMock } from '@kbn/inspector-plugin/public/mocks'; import { spacesPluginMock } from '@kbn/spaces-plugin/public/mocks'; import { dashboardPluginMock } from '@kbn/dashboard-plugin/public/mocks'; @@ -155,5 +157,7 @@ export function makeDefaultServices( clear: jest.fn(), }, spaces: spacesPluginMock.createStartContract(), + dataViewFieldEditor: indexPatternFieldEditorPluginMock.createStartContract(), + dataViewEditor: indexPatternEditorPluginMock.createStartContract(), }; } diff --git a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts b/x-pack/plugins/lens/public/pie_visualization/to_expression.ts index 9f506c7beb878..574f61bfc9232 100644 --- a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/pie_visualization/to_expression.ts @@ -134,20 +134,22 @@ const generateCommonArguments: GenerateExpressionAstArguments = ( layer, datasourceLayers, paletteService -) => ({ - labels: generateCommonLabelsAstArgs(state, attributes, layer), - buckets: operations.map((o) => o.columnId).map(prepareDimension), - metric: layer.metric ? [prepareDimension(layer.metric)] : [], - legendDisplay: [attributes.isPreview ? LegendDisplay.HIDE : layer.legendDisplay], - legendPosition: [layer.legendPosition || Position.Right], - maxLegendLines: [layer.legendMaxLines ?? 1], - legendSize: layer.legendSize ? [layer.legendSize] : [], - nestedLegend: [!!layer.nestedLegend], - truncateLegend: [ - layer.truncateLegend ?? getDefaultVisualValuesForLayer(state, datasourceLayers).truncateText, - ], - palette: generatePaletteAstArguments(paletteService, state.palette), -}); +) => { + return { + labels: generateCommonLabelsAstArgs(state, attributes, layer), + buckets: operations.map((o) => o.columnId).map(prepareDimension), + metric: layer.metric ? [prepareDimension(layer.metric)] : [], + legendDisplay: [attributes.isPreview ? LegendDisplay.HIDE : layer.legendDisplay], + legendPosition: [layer.legendPosition || Position.Right], + maxLegendLines: [layer.legendMaxLines ?? 1], + legendSize: layer.legendSize ? [layer.legendSize] : [], + nestedLegend: [!!layer.nestedLegend], + truncateLegend: [ + layer.truncateLegend ?? getDefaultVisualValuesForLayer(state, datasourceLayers).truncateText, + ], + palette: generatePaletteAstArguments(paletteService, state.palette), + }; +}; const generatePieVisAst: GenerateExpressionAstFunction = (...rest) => ({ type: 'expression', @@ -245,7 +247,8 @@ function expressionHelper( state: PieVisualizationState, datasourceLayers: DatasourceLayers, paletteService: PaletteRegistry, - attributes: Attributes = { isPreview: false } + attributes: Attributes = { isPreview: false }, + datasourceExpressionsByLayers: Record ): Ast | null { const layer = state.layers[0]; const datasource = datasourceLayers[layer.layerId]; @@ -261,26 +264,55 @@ function expressionHelper( if (!layer.metric || !operations.length) { return null; } + const visualizationAst = generateExprAst( + state, + attributes, + operations, + layer, + datasourceLayers, + paletteService + ); - return generateExprAst(state, attributes, operations, layer, datasourceLayers, paletteService); + const datasourceAst = datasourceExpressionsByLayers[layer.layerId]; + return { + type: 'expression', + chain: [ + ...(datasourceAst ? datasourceAst.chain : []), + ...(visualizationAst ? visualizationAst.chain : []), + ], + }; } export function toExpression( state: PieVisualizationState, datasourceLayers: DatasourceLayers, paletteService: PaletteRegistry, - attributes: Partial<{ title: string; description: string }> = {} + attributes: Partial<{ title: string; description: string }> = {}, + datasourceExpressionsByLayers: Record | undefined = {} ) { - return expressionHelper(state, datasourceLayers, paletteService, { - ...attributes, - isPreview: false, - }); + return expressionHelper( + state, + datasourceLayers, + paletteService, + { + ...attributes, + isPreview: false, + }, + datasourceExpressionsByLayers + ); } export function toPreviewExpression( state: PieVisualizationState, datasourceLayers: DatasourceLayers, - paletteService: PaletteRegistry + paletteService: PaletteRegistry, + datasourceExpressionsByLayers: Record | undefined = {} ) { - return expressionHelper(state, datasourceLayers, paletteService, { isPreview: true }); + return expressionHelper( + state, + datasourceLayers, + paletteService, + { isPreview: true }, + datasourceExpressionsByLayers + ); } diff --git a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx b/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx index 67659d91ecefe..fefa01d708dc7 100644 --- a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx @@ -6,7 +6,7 @@ */ import './toolbar.scss'; -import React, { useCallback } from 'react'; +import React, { useCallback, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, @@ -18,6 +18,7 @@ import { } from '@elastic/eui'; import type { Position } from '@elastic/charts'; import type { PaletteRegistry } from '@kbn/coloring'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; import { DEFAULT_PERCENT_DECIMALS } from './constants'; import { PartitionChartsMeta } from './partition_charts_meta'; import { LegendDisplay, PieVisualizationState, SharedPieLayerState } from '../../common'; @@ -73,6 +74,10 @@ export function PieToolbar(props: VisualizationToolbarProps legendSize === LegendSize.AUTO); + const onStateChange = useCallback( (part: Record) => { setState({ @@ -259,8 +264,9 @@ export function PieToolbar(props: VisualizationToolbarProps
); diff --git a/x-pack/plugins/lens/public/pie_visualization/visualization.tsx b/x-pack/plugins/lens/public/pie_visualization/visualization.tsx index 665fd5522c36f..927c67dce68b7 100644 --- a/x-pack/plugins/lens/public/pie_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/visualization.tsx @@ -241,9 +241,11 @@ export const getPieVisualization = ({ return state?.layers.find(({ layerId: id }) => id === layerId)?.layerType; }, - toExpression: (state, layers, attributes) => - toExpression(state, layers, paletteService, attributes), - toPreviewExpression: (state, layers) => toPreviewExpression(state, layers, paletteService), + toExpression: (state, layers, attributes, datasourceExpressionsByLayers) => + toExpression(state, layers, paletteService, attributes, datasourceExpressionsByLayers), + + toPreviewExpression: (state, layers, datasourceExpressionsByLayers) => + toPreviewExpression(state, layers, paletteService, datasourceExpressionsByLayers), renderToolbar(domElement, props) { render( diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index b39c14cd82454..e3c879d864a46 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -32,6 +32,7 @@ import type { EventAnnotationPluginSetup } from '@kbn/event-annotation-plugin/pu import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public'; import { IndexPatternFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public'; +import { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; import type { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import { AppNavLinkStatus } from '@kbn/core/public'; import { @@ -43,6 +44,7 @@ import { VISUALIZE_EDITOR_TRIGGER } from '@kbn/visualizations-plugin/public'; import { createStartServicesGetter } from '@kbn/kibana-utils-plugin/public'; import type { DiscoverSetup, DiscoverStart } from '@kbn/discover-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import { AdvancedUiActionsSetup } from '@kbn/ui-actions-enhanced-plugin/public'; import type { EditorFrameService as EditorFrameServiceType } from './editor_frame_service'; import type { IndexPatternDatasource as IndexPatternDatasourceType, @@ -92,6 +94,7 @@ import type { SaveModalContainerProps } from './app_plugin/save_modal_container' import { setupExpressions } from './expressions'; import { getSearchProvider } from './search_provider'; +import { OpenInDiscoverDrilldown } from './trigger_actions/open_in_discover_drilldown'; export interface LensPluginSetupDependencies { urlForwarding: UrlForwardingSetup; @@ -105,6 +108,7 @@ export interface LensPluginSetupDependencies { globalSearch?: GlobalSearchPluginSetup; usageCollection?: UsageCollectionSetup; discover?: DiscoverSetup; + uiActionsEnhanced: AdvancedUiActionsSetup; } export interface LensPluginStartDependencies { @@ -123,6 +127,7 @@ export interface LensPluginStartDependencies { savedObjectsTagging?: SavedObjectTaggingPluginStart; presentationUtil: PresentationUtilPluginStart; dataViewFieldEditor: IndexPatternFieldEditorStart; + dataViewEditor: DataViewEditorStart; inspector: InspectorStartContract; spaces: SpacesPluginStart; usageCollection?: UsageCollectionStart; @@ -222,6 +227,7 @@ export class LensPlugin { private heatmapVisualization: HeatmapVisualizationType | undefined; private gaugeVisualization: GaugeVisualizationType | undefined; private topNavMenuEntries: LensTopNavMenuEntryGenerator[] = []; + private hasDiscoverAccess: boolean = false; private stopReportManager?: () => void; @@ -238,6 +244,8 @@ export class LensPlugin { eventAnnotation, globalSearch, usageCollection, + uiActionsEnhanced, + discover, }: LensPluginSetupDependencies ) { const startServices = createStartServicesGetter(core.getStartServices); @@ -283,6 +291,15 @@ export class LensPlugin { visualizations.registerAlias(getLensAliasConfig()); + if (discover) { + uiActionsEnhanced.registerDrilldown( + new OpenInDiscoverDrilldown({ + discover, + hasDiscoverAccess: () => this.hasDiscoverAccess, + }) + ); + } + setupExpressions( expressions, () => startServices().plugins.fieldFormats.deserialize, @@ -425,6 +442,7 @@ export class LensPlugin { } start(core: CoreStart, startDependencies: LensPluginStartDependencies): LensPublicStart { + this.hasDiscoverAccess = core.application.capabilities.discover.show as boolean; // unregisters the Visualize action and registers the lens one if (startDependencies.uiActions.hasAction(ACTION_VISUALIZE_FIELD)) { startDependencies.uiActions.unregisterAction(ACTION_VISUALIZE_FIELD); @@ -441,10 +459,7 @@ export class LensPlugin { startDependencies.uiActions.addTriggerAction( CONTEXT_MENU_TRIGGER, - createOpenInDiscoverAction( - startDependencies.discover!, - core.application.capabilities.discover.show as boolean - ) + createOpenInDiscoverAction(startDependencies.discover!, this.hasDiscoverAccess) ); return { diff --git a/x-pack/plugins/lens/public/shared_components/legend_action_popover.tsx b/x-pack/plugins/lens/public/shared_components/legend_action_popover.tsx index fa7e12083435c..889c7697b8f68 100644 --- a/x-pack/plugins/lens/public/shared_components/legend_action_popover.tsx +++ b/x-pack/plugins/lens/public/shared_components/legend_action_popover.tsx @@ -9,7 +9,7 @@ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiContextMenuPanelDescriptor, EuiIcon, EuiPopover, EuiContextMenu } from '@elastic/eui'; import { useLegendAction } from '@elastic/charts'; -import type { LensFilterEvent } from '../types'; +import { ClickTriggerEvent } from '@kbn/charts-plugin/public'; export interface LegendActionPopoverProps { /** @@ -19,11 +19,11 @@ export interface LegendActionPopoverProps { /** * Callback on filter value */ - onFilter: (data: LensFilterEvent['data']) => void; + onFilter: (data: ClickTriggerEvent['data']) => void; /** * Determines the filter event data */ - context: LensFilterEvent['data']; + context: ClickTriggerEvent['data']; } export const LegendActionPopover: React.FunctionComponent = ({ diff --git a/x-pack/plugins/lens/public/shared_components/legend_location_settings.tsx b/x-pack/plugins/lens/public/shared_components/legend_location_settings.tsx index 7372b727268bd..5e4ef239b4295 100644 --- a/x-pack/plugins/lens/public/shared_components/legend_location_settings.tsx +++ b/x-pack/plugins/lens/public/shared_components/legend_location_settings.tsx @@ -30,11 +30,11 @@ export interface LegendLocationSettingsProps { /** * Sets the vertical alignment for legend inside chart */ - verticalAlignment?: VerticalAlignment; + verticalAlignment?: typeof VerticalAlignment.Top | typeof VerticalAlignment.Bottom; /** * Sets the vertical alignment for legend inside chart */ - horizontalAlignment?: HorizontalAlignment; + horizontalAlignment?: typeof HorizontalAlignment.Left | typeof HorizontalAlignment.Right; /** * Callback on horizontal alignment option change */ diff --git a/x-pack/plugins/lens/public/shared_components/legend_settings_popover.test.tsx b/x-pack/plugins/lens/public/shared_components/legend_settings_popover.test.tsx index 9bf9a1885e6ac..777f2860cb8b6 100644 --- a/x-pack/plugins/lens/public/shared_components/legend_settings_popover.test.tsx +++ b/x-pack/plugins/lens/public/shared_components/legend_settings_popover.test.tsx @@ -36,6 +36,7 @@ describe('Legend Settings', () => { props = { legendOptions, mode: 'auto', + showAutoLegendSizeOption: true, onDisplayChange: jest.fn(), onPositionChange: jest.fn(), onLegendSizeChange: jest.fn(), diff --git a/x-pack/plugins/lens/public/shared_components/legend_settings_popover.tsx b/x-pack/plugins/lens/public/shared_components/legend_settings_popover.tsx index 560a695eaa089..e0dd990f0a99e 100644 --- a/x-pack/plugins/lens/public/shared_components/legend_settings_popover.tsx +++ b/x-pack/plugins/lens/public/shared_components/legend_settings_popover.tsx @@ -16,6 +16,7 @@ import { } from '@elastic/eui'; import { Position, VerticalAlignment, HorizontalAlignment } from '@elastic/charts'; import { ToolbarButtonProps } from '@kbn/kibana-react-plugin/public'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; import { ToolbarPopover } from '.'; import { LegendLocationSettings } from './legend_location_settings'; import { ColumnsNumberSetting } from './columns_number_setting'; @@ -58,11 +59,11 @@ export interface LegendSettingsPopoverProps { /** * Sets the vertical alignment for legend inside chart */ - verticalAlignment?: VerticalAlignment; + verticalAlignment?: typeof VerticalAlignment.Top | typeof VerticalAlignment.Bottom; /** * Sets the vertical alignment for legend inside chart */ - horizontalAlignment?: HorizontalAlignment; + horizontalAlignment?: typeof HorizontalAlignment.Left | typeof HorizontalAlignment.Right; /** * Callback on horizontal alignment option change */ @@ -122,11 +123,16 @@ export interface LegendSettingsPopoverProps { /** * Legend size in pixels */ - legendSize?: number; + legendSize?: LegendSize; /** * Callback on legend size change */ - onLegendSizeChange: (size?: number) => void; + onLegendSizeChange: (size?: LegendSize) => void; + /** + * Whether to show auto legend size option. Should only be true for pre 8.3 visualizations that already had it as their setting. + * (We're trying to get people to stop using it so it can eventually be removed.) + */ + showAutoLegendSizeOption: boolean; } const DEFAULT_TRUNCATE_LINES = 1; @@ -185,6 +191,7 @@ export const LegendSettingsPopover: React.FunctionComponent {}, legendSize, onLegendSizeChange, + showAutoLegendSizeOption, }) => { return ( - + {location !== 'inside' && ( + + )} {location && ( { + it('renders nothing if not vertical legend', () => { + const instance = shallow( + {}} + isVerticalLegend={false} + showAutoOption={false} + /> + ); + + expect(instance.html()).toBeNull(); + }); + + it('defaults to correct value', () => { + const instance = shallow( + {}} + isVerticalLegend={true} + showAutoOption={false} + /> + ); + + expect(instance.find(EuiSuperSelect).props().valueOfSelected).toBe( + DEFAULT_LEGEND_SIZE.toString() + ); + }); + + it('reflects current setting in select', () => { + const CURRENT_SIZE = LegendSize.SMALL; + + const instance = shallow( + {}} + isVerticalLegend={true} + showAutoOption={false} + /> + ); + + expect(instance.find(EuiSuperSelect).props().valueOfSelected).toBe(CURRENT_SIZE); + }); + + it('allows user to select a new option', () => { + const onSizeChange = jest.fn(); + + const instance = shallow( + + ); + + const onChange = instance.find(EuiSuperSelect).props().onChange; + + onChange(LegendSize.EXTRA_LARGE); + onChange(DEFAULT_LEGEND_SIZE); + + expect(onSizeChange).toHaveBeenNthCalledWith(1, LegendSize.EXTRA_LARGE); + expect(onSizeChange).toHaveBeenNthCalledWith(2, undefined); + }); + + it('hides "auto" option if visualization not using it', () => { + const getOptions = (showAutoOption: boolean) => + shallow( + {}} + isVerticalLegend={true} + showAutoOption={showAutoOption} + /> + ) + .find(EuiSuperSelect) + .props().options; + + const autoOption = expect.objectContaining({ value: LegendSize.AUTO }); + + expect(getOptions(true)).toContainEqual(autoOption); + expect(getOptions(false)).not.toContainEqual(autoOption); + }); +}); diff --git a/x-pack/plugins/lens/public/shared_components/legend_size_settings.tsx b/x-pack/plugins/lens/public/shared_components/legend_size_settings.tsx index 53da283de0b68..15e74f2601b2b 100644 --- a/x-pack/plugins/lens/public/shared_components/legend_size_settings.tsx +++ b/x-pack/plugins/lens/public/shared_components/legend_size_settings.tsx @@ -8,48 +8,36 @@ import React, { useEffect, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiSuperSelect } from '@elastic/eui'; - -export enum LegendSizes { - AUTO = '0', - SMALL = '80', - MEDIUM = '130', - LARGE = '180', - EXTRA_LARGE = '230', -} +import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/public'; interface LegendSizeSettingsProps { - legendSize: number | undefined; - onLegendSizeChange: (size?: number) => void; + legendSize?: LegendSize; + onLegendSizeChange: (size?: LegendSize) => void; isVerticalLegend: boolean; + showAutoOption: boolean; } -const legendSizeOptions: Array<{ value: LegendSizes; inputDisplay: string }> = [ - { - value: LegendSizes.AUTO, - inputDisplay: i18n.translate('xpack.lens.shared.legendSizeSetting.legendSizeOptions.auto', { - defaultMessage: 'Auto', - }), - }, +const legendSizeOptions: Array<{ value: LegendSize; inputDisplay: string }> = [ { - value: LegendSizes.SMALL, + value: LegendSize.SMALL, inputDisplay: i18n.translate('xpack.lens.shared.legendSizeSetting.legendSizeOptions.small', { defaultMessage: 'Small', }), }, { - value: LegendSizes.MEDIUM, + value: LegendSize.MEDIUM, inputDisplay: i18n.translate('xpack.lens.shared.legendSizeSetting.legendSizeOptions.medium', { defaultMessage: 'Medium', }), }, { - value: LegendSizes.LARGE, + value: LegendSize.LARGE, inputDisplay: i18n.translate('xpack.lens.shared.legendSizeSetting.legendSizeOptions.large', { defaultMessage: 'Large', }), }, { - value: LegendSizes.EXTRA_LARGE, + value: LegendSize.EXTRA_LARGE, inputDisplay: i18n.translate( 'xpack.lens.shared.legendSizeSetting.legendSizeOptions.extraLarge', { @@ -63,6 +51,7 @@ export const LegendSizeSettings = ({ legendSize, onLegendSizeChange, isVerticalLegend, + showAutoOption, }: LegendSizeSettingsProps) => { useEffect(() => { if (legendSize && !isVerticalLegend) { @@ -71,12 +60,27 @@ export const LegendSizeSettings = ({ }, [isVerticalLegend, legendSize, onLegendSizeChange]); const onLegendSizeOptionChange = useCallback( - (option) => onLegendSizeChange(Number(option) || undefined), + (option: LegendSize) => onLegendSizeChange(option === DEFAULT_LEGEND_SIZE ? undefined : option), [onLegendSizeChange] ); if (!isVerticalLegend) return null; + const options = showAutoOption + ? [ + { + value: LegendSize.AUTO, + inputDisplay: i18n.translate( + 'xpack.lens.shared.legendSizeSetting.legendSizeOptions.auto', + { + defaultMessage: 'Auto', + } + ), + }, + ...legendSizeOptions, + ] + : legendSizeOptions; + return ( diff --git a/x-pack/plugins/lens/public/shared_components/name_input.tsx b/x-pack/plugins/lens/public/shared_components/name_input.tsx index 0b65b26021628..9502c7df93d55 100644 --- a/x-pack/plugins/lens/public/shared_components/name_input.tsx +++ b/x-pack/plugins/lens/public/shared_components/name_input.tsx @@ -35,8 +35,9 @@ export const NameInput = ({ fullWidth > { handleInputChange(e.target.value); diff --git a/x-pack/plugins/lens/public/shared_components/value_labels_settings.test.tsx b/x-pack/plugins/lens/public/shared_components/value_labels_settings.test.tsx index 7f5ebcf7aa281..0d01463d333b0 100644 --- a/x-pack/plugins/lens/public/shared_components/value_labels_settings.test.tsx +++ b/x-pack/plugins/lens/public/shared_components/value_labels_settings.test.tsx @@ -38,7 +38,7 @@ describe('Value labels Settings', () => { }); it('should render the passed value if given', () => { - const component = shallow(); + const component = shallow(); expect( component.find('[data-test-subj="lens-value-labels-visibility-btn"]').prop('idSelected') ).toEqual(`value_labels_inside`); diff --git a/x-pack/plugins/lens/public/shared_components/value_labels_settings.tsx b/x-pack/plugins/lens/public/shared_components/value_labels_settings.tsx index 64d9f5475379a..f5378a2e3ba01 100644 --- a/x-pack/plugins/lens/public/shared_components/value_labels_settings.tsx +++ b/x-pack/plugins/lens/public/shared_components/value_labels_settings.tsx @@ -26,7 +26,7 @@ const valueLabelsOptions: Array<{ }, { id: `value_labels_inside`, - value: 'inside', + value: 'show', label: i18n.translate('xpack.lens.shared.valueLabelsVisibility.inside', { defaultMessage: 'Show', }), diff --git a/x-pack/plugins/lens/public/trigger_actions/open_in_discover_action.test.ts b/x-pack/plugins/lens/public/trigger_actions/open_in_discover_action.test.ts index 084bd65b70d31..eebdf04337f69 100644 --- a/x-pack/plugins/lens/public/trigger_actions/open_in_discover_action.test.ts +++ b/x-pack/plugins/lens/public/trigger_actions/open_in_discover_action.test.ts @@ -83,6 +83,7 @@ describe('open in discover action', () => { const embeddable = { getViewUnderlyingDataArgs: jest.fn(() => viewUnderlyingDataArgs), + type: 'lens', }; const discoverUrl = 'https://discover-redirect-url'; diff --git a/x-pack/plugins/lens/public/trigger_actions/open_in_discover_action.ts b/x-pack/plugins/lens/public/trigger_actions/open_in_discover_action.ts index bd666f52bf0bc..54a24aac269b5 100644 --- a/x-pack/plugins/lens/public/trigger_actions/open_in_discover_action.ts +++ b/x-pack/plugins/lens/public/trigger_actions/open_in_discover_action.ts @@ -5,17 +5,23 @@ * 2.0. */ -import type { IEmbeddable } from '@kbn/embeddable-plugin/public'; import { i18n } from '@kbn/i18n'; import { createAction } from '@kbn/ui-actions-plugin/public'; import type { DiscoverStart } from '@kbn/discover-plugin/public'; -import type { Embeddable } from '../embeddable'; -import { DOC_TYPE } from '../../common'; +import { IEmbeddable } from '@kbn/embeddable-plugin/public'; +import { execute, isCompatible } from './open_in_discover_helpers'; const ACTION_OPEN_IN_DISCOVER = 'ACTION_OPEN_IN_DISCOVER'; -export const createOpenInDiscoverAction = (discover: DiscoverStart, hasDiscoverAccess: boolean) => - createAction<{ embeddable: IEmbeddable }>({ +interface Context { + embeddable: IEmbeddable; +} + +export const createOpenInDiscoverAction = ( + discover: Pick, + hasDiscoverAccess: boolean +) => + createAction({ type: ACTION_OPEN_IN_DISCOVER, id: ACTION_OPEN_IN_DISCOVER, order: 19, // right after Inspect which is 20 @@ -24,18 +30,10 @@ export const createOpenInDiscoverAction = (discover: DiscoverStart, hasDiscoverA i18n.translate('xpack.lens.app.exploreDataInDiscover', { defaultMessage: 'Explore data in Discover', }), - isCompatible: async (context: { embeddable: IEmbeddable }) => { - if (!hasDiscoverAccess) return false; - return ( - context.embeddable.type === DOC_TYPE && - (await (context.embeddable as Embeddable).canViewUnderlyingData()) - ); + isCompatible: async (context: Context) => { + return isCompatible({ hasDiscoverAccess, discover, embeddable: context.embeddable }); }, - execute: async (context: { embeddable: Embeddable }) => { - const args = context.embeddable.getViewUnderlyingDataArgs()!; - const discoverUrl = discover.locator?.getRedirectUrl({ - ...args, - }); - window.open(discoverUrl, '_blank'); + execute: async (context: Context) => { + return execute({ ...context, discover, hasDiscoverAccess }); }, }); diff --git a/x-pack/plugins/lens/public/trigger_actions/open_in_discover_drilldown.test.tsx b/x-pack/plugins/lens/public/trigger_actions/open_in_discover_drilldown.test.tsx new file mode 100644 index 0000000000000..bd1fc948eb937 --- /dev/null +++ b/x-pack/plugins/lens/public/trigger_actions/open_in_discover_drilldown.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FormEvent } from 'react'; +import { IEmbeddable, EmbeddableInput } from '@kbn/embeddable-plugin/public'; +import { DiscoverSetup } from '@kbn/discover-plugin/public'; +import { execute, isCompatible } from './open_in_discover_helpers'; +import { mount } from 'enzyme'; +import { Filter } from '@kbn/es-query'; +import { + ActionFactoryContext, + CollectConfigProps, + OpenInDiscoverDrilldown, +} from './open_in_discover_drilldown'; + +jest.mock('./open_in_discover_helpers', () => ({ + isCompatible: jest.fn(() => true), + execute: jest.fn(), +})); + +describe('open in discover drilldown', () => { + let drilldown: OpenInDiscoverDrilldown; + beforeEach(() => { + drilldown = new OpenInDiscoverDrilldown({ + discover: {} as DiscoverSetup, + hasDiscoverAccess: () => true, + }); + }); + it('provides UI to edit config', () => { + const Component = (drilldown as unknown as { ReactCollectConfig: React.FC }) + .ReactCollectConfig; + const setConfig = jest.fn(); + const instance = mount( + + ); + instance.find('EuiSwitch').prop('onChange')!({} as unknown as FormEvent<{}>); + expect(setConfig).toHaveBeenCalledWith({ openInNewTab: true }); + }); + it('calls through to isCompatible helper', () => { + const filters: Filter[] = [{ meta: { disabled: false } }]; + drilldown.isCompatible( + { openInNewTab: true }, + { embeddable: { type: 'lens' } as IEmbeddable, filters } + ); + expect(isCompatible).toHaveBeenCalledWith(expect.objectContaining({ filters })); + }); + it('calls through to execute helper', () => { + const filters: Filter[] = [{ meta: { disabled: false } }]; + drilldown.execute( + { openInNewTab: true }, + { embeddable: { type: 'lens' } as IEmbeddable, filters } + ); + expect(execute).toHaveBeenCalledWith( + expect.objectContaining({ filters, openInSameTab: false }) + ); + }); +}); diff --git a/x-pack/plugins/lens/public/trigger_actions/open_in_discover_drilldown.tsx b/x-pack/plugins/lens/public/trigger_actions/open_in_discover_drilldown.tsx new file mode 100644 index 0000000000000..d957b9cafd4be --- /dev/null +++ b/x-pack/plugins/lens/public/trigger_actions/open_in_discover_drilldown.tsx @@ -0,0 +1,139 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { IEmbeddable, EmbeddableInput } from '@kbn/embeddable-plugin/public'; +import { + Query, + Filter, + TimeRange, + extractTimeRange, + APPLY_FILTER_TRIGGER, +} from '@kbn/data-plugin/public'; +import { CollectConfigProps as CollectConfigPropsBase } from '@kbn/kibana-utils-plugin/public'; +import { reactToUiComponent } from '@kbn/kibana-react-plugin/public'; +import { + UiActionsEnhancedDrilldownDefinition as Drilldown, + UiActionsEnhancedBaseActionFactoryContext as BaseActionFactoryContext, +} from '@kbn/ui-actions-enhanced-plugin/public'; +import { EuiFormRow, EuiSwitch } from '@elastic/eui'; +import { DiscoverSetup } from '@kbn/discover-plugin/public'; +import { ApplyGlobalFilterActionContext } from '@kbn/unified-search-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { execute, isCompatible, isLensEmbeddable } from './open_in_discover_helpers'; + +interface EmbeddableQueryInput extends EmbeddableInput { + query?: Query; + filters?: Filter[]; + timeRange?: TimeRange; +} + +/** @internal */ +export type EmbeddableWithQueryInput = IEmbeddable; + +interface UrlDrilldownDeps { + discover: Pick; + hasDiscoverAccess: () => boolean; +} + +export type ActionContext = ApplyGlobalFilterActionContext; + +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions +export type Config = { + openInNewTab: boolean; +}; + +export type OpenInDiscoverTrigger = typeof APPLY_FILTER_TRIGGER; + +export interface ActionFactoryContext extends BaseActionFactoryContext { + embeddable?: EmbeddableWithQueryInput; +} +export type CollectConfigProps = CollectConfigPropsBase; + +const OPEN_IN_DISCOVER_DRILLDOWN = 'OPEN_IN_DISCOVER_DRILLDOWN'; + +export class OpenInDiscoverDrilldown + implements Drilldown +{ + public readonly id = OPEN_IN_DISCOVER_DRILLDOWN; + + constructor(private readonly deps: UrlDrilldownDeps) {} + + public readonly order = 8; + + public readonly getDisplayName = () => + i18n.translate('xpack.lens.app.exploreDataInDiscoverDrilldown', { + defaultMessage: 'Open in Discover', + }); + + public readonly euiIcon = 'discoverApp'; + + supportedTriggers(): OpenInDiscoverTrigger[] { + return [APPLY_FILTER_TRIGGER]; + } + + private readonly ReactCollectConfig: React.FC = ({ + config, + onConfig, + context, + }) => { + return ( + + onConfig({ ...config, openInNewTab: !config.openInNewTab })} + data-test-subj="openInDiscoverDrilldownOpenInNewTab" + /> + + ); + }; + + public readonly CollectConfig = reactToUiComponent(this.ReactCollectConfig); + + public readonly createConfig = () => ({ + openInNewTab: true, + }); + + public readonly isConfigValid = (config: Config): config is Config => { + return true; + }; + + public readonly isCompatible = async (config: Config, context: ActionContext) => { + return isCompatible({ + discover: this.deps.discover, + hasDiscoverAccess: this.deps.hasDiscoverAccess(), + ...context, + embeddable: context.embeddable as IEmbeddable, + ...config, + }); + }; + + public readonly isConfigurable = (context: ActionFactoryContext) => { + return this.deps.hasDiscoverAccess() && isLensEmbeddable(context.embeddable as IEmbeddable); + }; + + public readonly execute = async (config: Config, context: ActionContext) => { + const { restOfFilters: filters, timeRange: timeRange } = extractTimeRange( + context.filters, + context.timeFieldName + ); + execute({ + discover: this.deps.discover, + hasDiscoverAccess: this.deps.hasDiscoverAccess(), + ...context, + embeddable: context.embeddable as IEmbeddable, + openInSameTab: !config.openInNewTab, + filters, + timeRange, + }); + }; +} diff --git a/x-pack/plugins/lens/public/trigger_actions/open_in_discover_helpers.ts b/x-pack/plugins/lens/public/trigger_actions/open_in_discover_helpers.ts new file mode 100644 index 0000000000000..87f0931f1a3db --- /dev/null +++ b/x-pack/plugins/lens/public/trigger_actions/open_in_discover_helpers.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DiscoverSetup } from '@kbn/discover-plugin/public'; +import { Filter } from '@kbn/es-query'; +import { TimeRange } from '@kbn/data-plugin/public'; +import { IEmbeddable } from '@kbn/embeddable-plugin/public'; +import type { Embeddable } from '../embeddable'; +import { DOC_TYPE } from '../../common'; + +interface Context { + embeddable: IEmbeddable; + filters?: Filter[]; + timeRange?: TimeRange; + openInSameTab?: boolean; + hasDiscoverAccess: boolean; + discover: Pick; +} + +export function isLensEmbeddable(embeddable: IEmbeddable): embeddable is Embeddable { + return embeddable.type === DOC_TYPE; +} + +export async function isCompatible({ hasDiscoverAccess, embeddable }: Context) { + if (!hasDiscoverAccess) return false; + return isLensEmbeddable(embeddable) && (await embeddable.canViewUnderlyingData()); +} + +export function execute({ embeddable, discover, timeRange, filters, openInSameTab }: Context) { + if (!isLensEmbeddable(embeddable)) { + // shouldn't be executed because of the isCompatible check + throw new Error('Can only be executed in the context of Lens visualization'); + } + const args = embeddable.getViewUnderlyingDataArgs(); + if (!args) { + // shouldn't be executed because of the isCompatible check + throw new Error('Underlying data is not ready'); + } + const discoverUrl = discover.locator?.getRedirectUrl({ + ...args, + timeRange: timeRange || args.timeRange, + filters: [...(filters || []), ...args.filters], + }); + window.open(discoverUrl, !openInSameTab ? '_blank' : '_self'); +} diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 22d794e803df1..1f2ee1266ddb7 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -23,12 +23,12 @@ import type { } from '@kbn/expressions-plugin/public'; import type { VisualizeEditorLayersContext } from '@kbn/visualizations-plugin/public'; import type { Query } from '@kbn/data-plugin/public'; -import type { RangeSelectContext, ValueClickContext } from '@kbn/embeddable-plugin/public'; import type { UiActionsStart, RowClickContext, VisualizeFieldContext, } from '@kbn/ui-actions-plugin/public'; +import { ClickTriggerEvent, BrushTriggerEvent } from '@kbn/charts-plugin/public'; import { DraggingIdentifier, DragDropIdentifier, DragContextState } from './drag_drop'; import type { DateRange, LayerType, SortingHint } from '../common'; import type { @@ -223,6 +223,7 @@ export interface Datasource { // Given the current state, which parts should be saved? getPersistableState: (state: T) => { state: P; savedObjectReferences: SavedObjectReference[] }; + getCurrentIndexPatternId: (state: T) => string; insertLayer: (state: T, newLayerId: string) => T; removeLayer: (state: T, layerId: string) => T; @@ -274,6 +275,14 @@ export interface Datasource { state: T; }) => T | undefined; + updateCurrentIndexPatternId?: (props: { + indexPatternId: string; + state: T; + setState: StateSetter; + }) => void; + + refreshIndexPatternsList?: (props: { indexPatternId: string; setState: StateSetter }) => void; + toExpression: (state: T, layerId: string) => ExpressionAstExpression | string | null; getDatasourceSuggestionsForField: ( @@ -525,7 +534,7 @@ export interface OperationDescriptor extends Operation { export interface VisualizationConfigProps { layerId: string; - frame: Pick; + frame: FramePublicAPI; state: T; } @@ -588,7 +597,7 @@ export type VisualizationDimensionGroupConfig = SharedDimensionProps & { labels?: { buttonAriaLabel: string; buttonLabel: string }; }; -interface VisualizationDimensionChangeProps { +export interface VisualizationDimensionChangeProps { layerId: string; columnId: string; prevState: T; @@ -887,7 +896,8 @@ export interface Visualization { toExpression: ( state: T, datasourceLayers: DatasourceLayers, - attributes?: Partial<{ title: string; description: string }> + attributes?: Partial<{ title: string; description: string }>, + datasourceExpressionsByLayers?: Record ) => ExpressionAstExpression | string | null; /** * Expression to render a preview version of the chart in very constrained space. @@ -895,7 +905,8 @@ export interface Visualization { */ toPreviewExpression?: ( state: T, - datasourceLayers: DatasourceLayers + datasourceLayers: DatasourceLayers, + datasourceExpressionsByLayers?: Record ) => ExpressionAstExpression | string | null; /** * The frame will call this function on all visualizations at few stages (pre-build/build error) in order @@ -922,16 +933,6 @@ export interface Visualization { onEditAction?: (state: T, event: LensEditEvent) => T; } -export interface LensFilterEvent { - name: 'filter'; - data: ValueClickContext['data']; -} - -export interface LensBrushEvent { - name: 'brush'; - data: RangeSelectContext['data']; -} - // Use same technique as TriggerContext export interface LensEditContextMapping { [LENS_EDIT_SORT_ACTION]: LensSortActionData; @@ -958,11 +959,11 @@ export interface LensTableRowContextMenuEvent { data: RowClickContext['data']; } -export function isLensFilterEvent(event: ExpressionRendererEvent): event is LensFilterEvent { +export function isLensFilterEvent(event: ExpressionRendererEvent): event is ClickTriggerEvent { return event.name === 'filter'; } -export function isLensBrushEvent(event: ExpressionRendererEvent): event is LensBrushEvent { +export function isLensBrushEvent(event: ExpressionRendererEvent): event is BrushTriggerEvent { return event.name === 'brush'; } @@ -974,7 +975,7 @@ export function isLensEditEvent( export function isLensTableRowContextMenuClickEvent( event: ExpressionRendererEvent -): event is LensBrushEvent { +): event is BrushTriggerEvent { return event.name === 'tableRowContextMenuClick'; } @@ -986,8 +987,8 @@ export function isLensTableRowContextMenuClickEvent( export interface ILensInterpreterRenderHandlers extends IInterpreterRenderHandlers { event: ( event: - | LensFilterEvent - | LensBrushEvent + | ClickTriggerEvent + | BrushTriggerEvent | LensEditEvent | LensTableRowContextMenuEvent ) => void; diff --git a/x-pack/plugins/lens/public/utils.ts b/x-pack/plugins/lens/public/utils.ts index b5ada350b2aaa..0b650ccbedbc0 100644 --- a/x-pack/plugins/lens/public/utils.ts +++ b/x-pack/plugins/lens/public/utils.ts @@ -12,14 +12,9 @@ import type { TimefilterContract } from '@kbn/data-plugin/public'; import type { IUiSettingsClient, SavedObjectReference } from '@kbn/core/public'; import type { DataView, DataViewsContract } from '@kbn/data-views-plugin/public'; import { search } from '@kbn/data-plugin/public'; +import { BrushTriggerEvent, ClickTriggerEvent } from '@kbn/charts-plugin/public'; import type { Document } from './persistence/saved_object_store'; -import type { - Datasource, - DatasourceMap, - LensBrushEvent, - LensFilterEvent, - Visualization, -} from './types'; +import type { Datasource, DatasourceMap, Visualization, StateSetter } from './types'; import type { DatasourceStates, VisualizationState } from './state_management'; export function getVisualizeGeoFieldMessage(fieldType: string) { @@ -63,6 +58,43 @@ export const getInitialDatasourceId = (datasourceMap: DatasourceMap, doc?: Docum return (doc && getActiveDatasourceIdFromDoc(doc)) || Object.keys(datasourceMap)[0] || null; }; +export function handleIndexPatternChange({ + activeDatasources, + datasourceStates, + indexPatternId, + setDatasourceState, +}: { + activeDatasources: Record; + datasourceStates: DatasourceStates; + indexPatternId: string; + setDatasourceState: StateSetter; +}): void { + Object.entries(activeDatasources).forEach(([id, datasource]) => { + datasource?.updateCurrentIndexPatternId?.({ + state: datasourceStates[id].state, + indexPatternId, + setState: setDatasourceState, + }); + }); +} + +export function refreshIndexPatternsList({ + activeDatasources, + indexPatternId, + setDatasourceState, +}: { + activeDatasources: Record; + indexPatternId: string; + setDatasourceState: StateSetter; +}): void { + Object.entries(activeDatasources).forEach(([id, datasource]) => { + datasource?.refreshIndexPatternsList?.({ + indexPatternId, + setState: setDatasourceState, + }); + }); +} + export function getIndexPatternsIds({ activeDatasources, datasourceStates, @@ -70,17 +102,21 @@ export function getIndexPatternsIds({ activeDatasources: Record; datasourceStates: DatasourceStates; }): string[] { + let currentIndexPatternId: string | undefined; const references: SavedObjectReference[] = []; Object.entries(activeDatasources).forEach(([id, datasource]) => { const { savedObjectReferences } = datasource.getPersistableState(datasourceStates[id].state); + const indexPatternId = datasource.getCurrentIndexPatternId(datasourceStates[id].state); + currentIndexPatternId = indexPatternId; references.push(...savedObjectReferences); }); - - const uniqueFilterableIndexPatternIds = uniq( - references.filter(({ type }) => type === 'index-pattern').map(({ id }) => id) - ); - - return uniqueFilterableIndexPatternIds; + const referencesIds = references + .filter(({ type }) => type === 'index-pattern') + .map(({ id }) => id); + if (currentIndexPatternId) { + referencesIds.unshift(currentIndexPatternId); + } + return uniq(referencesIds); } export async function getIndexPatternsObjects( @@ -111,7 +147,7 @@ export function getRemoveOperation( return layerCount === 1 ? 'clear' : 'remove'; } -export function inferTimeField(context: LensBrushEvent['data'] | LensFilterEvent['data']) { +export function inferTimeField(context: BrushTriggerEvent['data'] | ClickTriggerEvent['data']) { const tablesAndColumns = 'table' in context ? [{ table: context.table, column: context.column }] diff --git a/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx b/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx index 74f7d518e1166..e85ef81a5dd8c 100644 --- a/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx @@ -113,9 +113,11 @@ const toExpression = ( paletteService: PaletteRegistry, state: GaugeVisualizationState, datasourceLayers: DatasourceLayers, - attributes?: Partial> + attributes?: Partial>, + datasourceExpressionsByLayers: Record | undefined = {} ): Ast | null => { const datasource = datasourceLayers[state.layerId]; + const datasourceExpression = datasourceExpressionsByLayers[state.layerId]; const originalOrder = datasource.getTableSpec().map(({ columnId }) => columnId); if (!originalOrder || !state.metricAccessor) { @@ -125,6 +127,7 @@ const toExpression = ( return { type: 'expression', chain: [ + ...(datasourceExpression?.chain ?? []), { type: 'function', function: EXPRESSION_GAUGE_NAME, @@ -420,10 +423,17 @@ export const getGaugeVisualization = ({ } }, - toExpression: (state, datasourceLayers, attributes) => - toExpression(paletteService, state, datasourceLayers, { ...attributes }), - toPreviewExpression: (state, datasourceLayers) => - toExpression(paletteService, state, datasourceLayers), + toExpression: (state, datasourceLayers, attributes, datasourceExpressionsByLayers = {}) => + toExpression( + paletteService, + state, + datasourceLayers, + { ...attributes }, + datasourceExpressionsByLayers + ), + + toPreviewExpression: (state, datasourceLayers, datasourceExpressionsByLayers = {}) => + toExpression(paletteService, state, datasourceLayers, undefined, datasourceExpressionsByLayers), getErrorMessages(state) { // not possible to break it? diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap index 256df38ffa5b3..afdfd8e200100 100644 --- a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap +++ b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap @@ -30,9 +30,6 @@ Object { "curveType": Array [ "LINEAR", ], - "description": Array [ - "", - ], "emphasizeFitting": Array [ true, ], @@ -135,6 +132,12 @@ Object { "splitAccessor": Array [ "d", ], + "table": Array [ + Object { + "chain": Array [], + "type": "expression", + }, + ], "xAccessor": Array [ "a", ], @@ -146,7 +149,7 @@ Object { "linear", ], }, - "function": "dataLayer", + "function": "extendedDataLayer", "type": "function", }, ], @@ -204,9 +207,6 @@ Object { "type": "expression", }, ], - "title": Array [ - "", - ], "valueLabels": Array [ "hide", ], @@ -263,7 +263,7 @@ Object { "", ], }, - "function": "xyVis", + "function": "layeredXyVis", "type": "function", }, ], diff --git a/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx index fe120dc71b17a..f9f4b9da5342a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx @@ -7,7 +7,11 @@ import { i18n } from '@kbn/i18n'; import moment from 'moment'; -import { defaultAnnotationColor } from '@kbn/event-annotation-plugin/public'; +import { + defaultAnnotationColor, + defaultAnnotationRangeColor, + isRangeAnnotation, +} from '@kbn/event-annotation-plugin/public'; import { layerTypes } from '../../../common'; import type { FramePublicAPI, Visualization } from '../../types'; import { isHorizontalChart } from '../state_helpers'; @@ -29,6 +33,13 @@ export const defaultAnnotationLabel = i18n.translate('xpack.lens.xyChart.default defaultMessage: 'Event', }); +export const defaultRangeAnnotationLabel = i18n.translate( + 'xpack.lens.xyChart.defaultRangeAnnotationLabel', + { + defaultMessage: 'Event range', + } +); + export function getStaticDate(dataLayers: XYDataLayerConfig[], frame: FramePublicAPI) { const dataLayersId = dataLayers.map(({ layerId }) => layerId); const { activeData, dateRange } = frame; @@ -124,6 +135,7 @@ export const setAnnotationsDimension: Visualization['setDimension'] = ( : undefined; let resultAnnotations = [...inputAnnotations] as XYAnnotationLayerConfig['annotations']; + if (!currentConfig) { resultAnnotations.push({ label: defaultAnnotationLabel, @@ -161,7 +173,9 @@ export const getAnnotationsAccessorColorConfig = (layer: XYAnnotationLayerConfig return { columnId: annotation.id, triggerIcon: annotation.isHidden ? ('invisible' as const) : ('color' as const), - color: annotation?.color || defaultAnnotationColor, + color: + annotation?.color || + (isRangeAnnotation(annotation) ? defaultAnnotationRangeColor : defaultAnnotationColor), }; }); }; diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts index db8ae7122c538..73b355bce7ed2 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { DataLayerConfigResult } from '@kbn/expression-xy-plugin/common'; import { layerTypes } from '../../common'; import { Datatable } from '@kbn/expressions-plugin/public'; import { getAxesConfiguration } from './axes_configuration'; +import { XYDataLayerConfig } from './types'; describe('axes_configuration', () => { const tables: Record = { @@ -219,8 +219,7 @@ describe('axes_configuration', () => { }, }; - const sampleLayer: DataLayerConfigResult = { - type: 'dataLayer', + const sampleLayer: XYDataLayerConfig = { layerId: 'first', layerType: layerTypes.DATA, seriesType: 'line', diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts index a329c12b083a5..67febcd4b9d00 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts @@ -6,9 +6,10 @@ */ import { getColorAssignments } from './color_assignment'; -import type { FormatFactory, LensMultiTable } from '../../common'; +import type { FormatFactory } from '../../common'; import { layerTypes } from '../../common'; import { XYDataLayerConfig } from './types'; +import { Datatable } from '@kbn/expressions-plugin'; describe('color_assignment', () => { const layers: XYDataLayerConfig[] = [ @@ -30,8 +31,7 @@ describe('color_assignment', () => { }, ]; - const data: LensMultiTable = { - type: 'lens_multitable', + const data: { tables: Record } = { tables: { '1': { type: 'datatable', diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx index c7e615bb29e12..5ddb1fcc043e7 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx @@ -7,7 +7,7 @@ import { groupBy, partition } from 'lodash'; import { i18n } from '@kbn/i18n'; -import type { YAxisMode, YConfig } from '@kbn/expression-xy-plugin/common'; +import type { YAxisMode, ExtendedYConfig } from '@kbn/expression-xy-plugin/common'; import { Datatable } from '@kbn/expressions-plugin/public'; import { layerTypes } from '../../common'; import type { DatasourceLayers, FramePublicAPI, Visualization } from '../types'; @@ -34,7 +34,7 @@ export interface ReferenceLineBase { * * what groups are current defined in data layers * * what existing reference line are currently defined in reference layers */ -export function getGroupsToShow( +export function getGroupsToShow( referenceLayers: T[], state: XYState | undefined, datasourceLayers: DatasourceLayers, @@ -104,6 +104,7 @@ export function getStaticValue( untouchedDataLayers, accessors, } = getAccessorCriteriaForGroup(groupId, dataLayers, activeData); + if ( groupId === 'x' && filteredLayers.length && @@ -111,6 +112,7 @@ export function getStaticValue( ) { return fallbackValue; } + const computedValue = computeStaticValueForGroup( filteredLayers, accessors, @@ -118,6 +120,7 @@ export function getStaticValue( groupId !== 'x', // histogram axis should compute the min based on the current data groupId !== 'x' ); + return computedValue ?? fallbackValue; } @@ -165,6 +168,7 @@ export function computeOverallDataDomain( const accessorMap = new Set(accessorIds); let min: number | undefined; let max: number | undefined; + const [stacked, unstacked] = partition( dataLayers, ({ seriesType }) => isStackedChart(seriesType) && allowStacking @@ -268,13 +272,17 @@ export const getReferenceSupportedLayer = ( label: 'x' as const, }, ]; + const referenceLineGroups = getGroupsRelatedToData( referenceLineGroupIds, state, frame?.datasourceLayers || {}, frame?.activeData ); - const dataLayers = getDataLayers(state?.layers || []); + + const layers = state?.layers || []; + const dataLayers = getDataLayers(layers); + const filledDataLayers = dataLayers.filter( ({ accessors, xAccessor }) => accessors.length || xAccessor ); @@ -289,7 +297,7 @@ export const getReferenceSupportedLayer = ( groupId: id, columnId: generateId(), dataType: 'number', - label: getAxisName(label, { isHorizontal: isHorizontalChart(state?.layers || []) }), + label: getAxisName(label, { isHorizontal: isHorizontalChart(layers) }), staticValue: getStaticValue( dataLayers, label, @@ -317,6 +325,7 @@ export const getReferenceSupportedLayer = ( initialDimensions, }; }; + export const setReferenceDimension: Visualization['setDimension'] = ({ prevState, layerId, @@ -397,6 +406,7 @@ export const getReferenceConfiguration = ({ return axisMode; } ); + const groupsToShow = getGroupsToShow( [ // When a reference layer panel is added, a static reference line should automatically be included by default @@ -422,7 +432,7 @@ export const getReferenceConfiguration = ({ ], state, frame.datasourceLayers, - frame?.activeData + frame.activeData ); const isHorizontal = isHorizontalChart(state.layers); return { diff --git a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts index 06ea897afa1e7..f37ad12f606b1 100644 --- a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts +++ b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts @@ -6,7 +6,7 @@ */ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; -import type { SeriesType, YConfig, ValidLayer } from '@kbn/expression-xy-plugin/common'; +import type { SeriesType, ExtendedYConfig } from '@kbn/expression-xy-plugin/common'; import type { FramePublicAPI, DatasourcePublicAPI } from '../types'; import { visualizationTypes, @@ -58,7 +58,8 @@ export const getSeriesColor = (layer: XYLayerConfig, accessor: string) => { return null; } return ( - layer?.yConfig?.find((yConfig: YConfig) => yConfig.forAccessor === accessor)?.color || null + layer?.yConfig?.find((yConfig: ExtendedYConfig) => yConfig.forAccessor === accessor)?.color || + null ); }; @@ -79,7 +80,7 @@ export const getColumnToLabelMap = ( }; export function hasHistogramSeries( - layers: ValidLayer[] = [], + layers: XYDataLayerConfig[] = [], datasourceLayers?: FramePublicAPI['datasourceLayers'] ) { if (!datasourceLayers) { @@ -87,7 +88,11 @@ export function hasHistogramSeries( } const validLayers = layers.filter(({ accessors }) => accessors.length); - return validLayers.some(({ layerId, xAccessor }: ValidLayer) => { + return validLayers.some(({ layerId, xAccessor }: XYDataLayerConfig) => { + if (!xAccessor) { + return false; + } + const xAxisOperation = datasourceLayers[layerId].getOperationForColumnId(xAccessor); return ( xAxisOperation && diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts index 7cd9a43540c0a..d39b9914f0eef 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Ast } from '@kbn/interpreter'; +import { Ast, fromExpression } from '@kbn/interpreter'; import { Position } from '@elastic/charts'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { getXyVisualization } from './xy_visualization'; @@ -28,6 +28,8 @@ describe('#toExpression', () => { let mockDatasource: ReturnType; let frame: ReturnType; + let datasourceExpressionsByLayers: Record; + beforeEach(() => { frame = createMockFramePublicAPI(); mockDatasource = createMockDatasource('testDatasource'); @@ -46,6 +48,23 @@ describe('#toExpression', () => { frame.datasourceLayers = { first: mockDatasource.publicAPIMock, }; + + const datasourceExpression = mockDatasource.toExpression( + frame.datasourceLayers.first, + 'first' + ) ?? { + type: 'expression', + chain: [], + }; + const exprAst = + typeof datasourceExpression === 'string' + ? fromExpression(datasourceExpression) + : datasourceExpression; + + datasourceExpressionsByLayers = { + first: exprAst, + referenceLine: exprAst, + }; }); it('should map to a valid AST', () => { @@ -82,7 +101,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) ).toMatchSnapshot(); }); @@ -106,7 +127,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) as Ast ).chain[0].arguments.fittingFunction[0] ).toEqual('None'); @@ -129,7 +152,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) as Ast; expect( (expression.chain[0].arguments.axisTitlesVisibilitySettings[0] as Ast).chain[0].arguments @@ -157,7 +182,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) as Ast; expect((expression.chain[0].arguments.layers[0] as Ast).chain[0].arguments.xAccessor).toEqual( [] @@ -182,7 +209,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) ).toBeNull(); }); @@ -204,7 +233,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers )! as Ast; expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('b'); @@ -241,7 +272,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) as Ast; expect( (expression.chain[0].arguments.tickLabelsVisibilitySettings[0] as Ast).chain[0].arguments @@ -269,7 +302,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) as Ast; expect((expression.chain[0].arguments.labelsOrientation[0] as Ast).chain[0].arguments).toEqual({ x: [0], @@ -295,7 +330,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) as Ast; expect( (expression.chain[0].arguments.gridlinesVisibilitySettings[0] as Ast).chain[0].arguments @@ -310,7 +347,7 @@ describe('#toExpression', () => { const expression = xyVisualization.toExpression( { legend: { position: Position.Bottom, isVisible: true }, - valueLabels: 'inside', + valueLabels: 'show', preferredSeriesType: 'bar', layers: [ { @@ -323,16 +360,18 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) as Ast; - expect(expression.chain[0].arguments.valueLabels[0] as Ast).toEqual('inside'); + expect(expression.chain[0].arguments.valueLabels[0] as Ast).toEqual('show'); }); it('should compute the correct series color fallback based on the layer type', () => { const expression = xyVisualization.toExpression( { legend: { position: Position.Bottom, isVisible: true }, - valueLabels: 'inside', + valueLabels: 'show', preferredSeriesType: 'bar', layers: [ { @@ -352,7 +391,9 @@ describe('#toExpression', () => { }, ], }, - { ...frame.datasourceLayers, referenceLine: mockDatasource.publicAPIMock } + { ...frame.datasourceLayers, referenceLine: mockDatasource.publicAPIMock }, + undefined, + datasourceExpressionsByLayers ) as Ast; function getYConfigColorForLayer(ast: Ast, index: number) { diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index eeb56bd7ee115..cf9a441fc6f53 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -10,13 +10,15 @@ import { ScaleType } from '@elastic/charts'; import type { PaletteRegistry } from '@kbn/coloring'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; -import type { ValidLayer, YConfig } from '@kbn/expression-xy-plugin/common'; +import { LegendSize } from '@kbn/visualizations-plugin/common/constants'; +import type { AxisExtentConfig, YConfig, ExtendedYConfig } from '@kbn/expression-xy-plugin/common'; import { State, XYDataLayerConfig, XYReferenceLineLayerConfig, XYAnnotationLayerConfig, } from './types'; +import type { ValidXYDataLayerConfig } from './types'; import { OperationMetadata, DatasourcePublicAPI, DatasourceLayers } from '../types'; import { getColumnToLabelMap } from './state_helpers'; import { hasIcon } from './xy_config_panel/shared/icon_select'; @@ -28,7 +30,7 @@ import { getReferenceLayers, getAnnotationsLayers, } from './visualization_helpers'; -import { getUniqueLabels, defaultAnnotationLabel } from './annotations/helpers'; +import { getUniqueLabels } from './annotations/helpers'; import { layerTypes } from '../../common'; export const getSortedAccessors = ( @@ -50,6 +52,7 @@ export const toExpression = ( datasourceLayers: DatasourceLayers, paletteService: PaletteRegistry, attributes: Partial<{ title: string; description: string }> = {}, + datasourceExpressionsByLayers: Record, eventAnnotationService: EventAnnotationServiceType ): Ast | null => { if (!state || !state.layers.length) { @@ -73,7 +76,7 @@ export const toExpression = ( metadata, datasourceLayers, paletteService, - attributes, + datasourceExpressionsByLayers, eventAnnotationService ); }; @@ -83,7 +86,7 @@ const simplifiedLayerExpression = { [layerTypes.REFERENCELINE]: (layer: XYReferenceLineLayerConfig) => ({ ...layer, hide: true, - yConfig: layer.yConfig?.map(({ lineWidth, ...rest }) => ({ + yConfig: layer.yConfig?.map(({ ...rest }) => ({ ...rest, lineWidth: 1, icon: undefined, @@ -93,12 +96,6 @@ const simplifiedLayerExpression = { [layerTypes.ANNOTATIONS]: (layer: XYAnnotationLayerConfig) => ({ ...layer, hide: true, - annotations: layer.annotations?.map(({ lineWidth, ...rest }) => ({ - ...rest, - lineWidth: 1, - icon: undefined, - textVisibility: false, - })), }), }; @@ -106,6 +103,7 @@ export function toPreviewExpression( state: State, datasourceLayers: DatasourceLayers, paletteService: PaletteRegistry, + datasourceExpressionsByLayers: Record, eventAnnotationService: EventAnnotationServiceType ) { return toExpression( @@ -122,6 +120,7 @@ export function toPreviewExpression( datasourceLayers, paletteService, {}, + datasourceExpressionsByLayers, eventAnnotationService ); } @@ -157,11 +156,13 @@ export const buildExpression = ( metadata: Record>, datasourceLayers: DatasourceLayers, paletteService: PaletteRegistry, - attributes: Partial<{ title: string; description: string }> = {}, + datasourceExpressionsByLayers: Record, eventAnnotationService: EventAnnotationServiceType ): Ast | null => { - const validDataLayers = getDataLayers(state.layers) - .filter((layer): layer is ValidLayer => Boolean(layer.accessors.length)) + const validDataLayers: ValidXYDataLayerConfig[] = getDataLayers(state.layers) + .filter((layer): layer is ValidXYDataLayerConfig => + Boolean(layer.accessors.length) + ) .map((layer) => ({ ...layer, accessors: getSortedAccessors(datasourceLayers[layer.layerId], layer), @@ -194,10 +195,8 @@ export const buildExpression = ( chain: [ { type: 'function', - function: 'xyVis', + function: 'layeredXyVis', arguments: { - title: [attributes?.title || ''], - description: [attributes?.description || ''], xTitle: [state.xTitle || ''], yTitle: [state.yTitle || ''], yRightTitle: [state.yRightTitle || ''], @@ -213,20 +212,27 @@ export const buildExpression = ( showSingleSeries: state.legend.showSingleSeries ? [state.legend.showSingleSeries] : [], - position: [state.legend.position], + position: !state.legend.isInside ? [state.legend.position] : [], isInside: state.legend.isInside ? [state.legend.isInside] : [], - legendSize: state.legend.legendSize ? [state.legend.legendSize] : [], - horizontalAlignment: state.legend.horizontalAlignment - ? [state.legend.horizontalAlignment] - : [], - verticalAlignment: state.legend.verticalAlignment - ? [state.legend.verticalAlignment] + legendSize: state.legend.isInside + ? [LegendSize.AUTO] + : state.legend.legendSize + ? [state.legend.legendSize] : [], + horizontalAlignment: + state.legend.horizontalAlignment && state.legend.isInside + ? [state.legend.horizontalAlignment] + : [], + verticalAlignment: + state.legend.verticalAlignment && state.legend.isInside + ? [state.legend.verticalAlignment] + : [], // ensure that even if the user types more than 5 columns // we will only show 5 - floatingColumns: state.legend.floatingColumns - ? [Math.min(5, state.legend.floatingColumns)] - : [], + floatingColumns: + state.legend.floatingColumns && state.legend.isInside + ? [Math.min(5, state.legend.floatingColumns)] + : [], maxLines: state.legend.maxLines ? [state.legend.maxLines] : [], shouldTruncate: [ state.legend.shouldTruncate ?? @@ -242,50 +248,8 @@ export const buildExpression = ( emphasizeFitting: [state.emphasizeFitting || false], curveType: [state.curveType || 'LINEAR'], fillOpacity: [state.fillOpacity || 0.3], - yLeftExtent: [ - { - type: 'expression', - chain: [ - { - type: 'function', - function: 'axisExtentConfig', - arguments: { - mode: [state?.yLeftExtent?.mode || 'full'], - lowerBound: - state?.yLeftExtent?.lowerBound !== undefined - ? [state?.yLeftExtent?.lowerBound] - : [], - upperBound: - state?.yLeftExtent?.upperBound !== undefined - ? [state?.yLeftExtent?.upperBound] - : [], - }, - }, - ], - }, - ], - yRightExtent: [ - { - type: 'expression', - chain: [ - { - type: 'function', - function: 'axisExtentConfig', - arguments: { - mode: [state?.yRightExtent?.mode || 'full'], - lowerBound: - state?.yRightExtent?.lowerBound !== undefined - ? [state?.yRightExtent?.lowerBound] - : [], - upperBound: - state?.yRightExtent?.upperBound !== undefined - ? [state?.yRightExtent?.upperBound] - : [], - }, - }, - ], - }, - ], + yLeftExtent: [axisExtentConfigToExpression(state.yLeftExtent, validDataLayers)], + yRightExtent: [axisExtentConfigToExpression(state.yRightExtent, validDataLayers)], axisTitlesVisibilitySettings: [ { type: 'expression', @@ -359,13 +323,15 @@ export const buildExpression = ( layer, datasourceLayers[layer.layerId], metadata, - paletteService + paletteService, + datasourceExpressionsByLayers[layer.layerId] ) ), ...validReferenceLayers.map((layer) => referenceLineLayerToExpression( layer, - datasourceLayers[(layer as XYReferenceLineLayerConfig).layerId] + datasourceLayers[(layer as XYReferenceLineLayerConfig).layerId], + datasourceExpressionsByLayers[layer.layerId] ) ), ...validAnnotationsLayers.map((layer) => @@ -380,23 +346,25 @@ export const buildExpression = ( const referenceLineLayerToExpression = ( layer: XYReferenceLineLayerConfig, - datasourceLayer: DatasourcePublicAPI + datasourceLayer: DatasourcePublicAPI, + datasourceExpression: Ast ): Ast => { return { type: 'expression', chain: [ { type: 'function', - function: 'referenceLineLayer', + function: 'extendedReferenceLineLayer', arguments: { layerId: [layer.layerId], yConfig: layer.yConfig ? layer.yConfig.map((yConfig) => - yConfigToExpression(yConfig, defaultReferenceLineColor) + extendedYConfigToExpression(yConfig, defaultReferenceLineColor) ) : [], accessors: layer.accessors, columnToLabel: [JSON.stringify(getColumnToLabelMap(layer, datasourceLayer))], + ...(datasourceExpression ? { table: [datasourceExpression] } : {}), }, }, ], @@ -412,24 +380,12 @@ const annotationLayerToExpression = ( chain: [ { type: 'function', - function: 'annotationLayer', + function: 'extendedAnnotationLayer', arguments: { hide: [Boolean(layer.hide)], layerId: [layer.layerId], annotations: layer.annotations - ? layer.annotations.map( - (ann): Ast => - eventAnnotationService.toExpression({ - time: ann.key.timestamp, - label: ann.label || defaultAnnotationLabel, - textVisibility: ann.textVisibility, - icon: ann.icon, - lineStyle: ann.lineStyle, - lineWidth: ann.lineWidth, - color: ann.color, - isHidden: Boolean(ann.isHidden), - }) - ) + ? layer.annotations.map((ann): Ast => eventAnnotationService.toExpression(ann)) : [], }, }, @@ -438,10 +394,11 @@ const annotationLayerToExpression = ( }; const dataLayerToExpression = ( - layer: ValidLayer, + layer: ValidXYDataLayerConfig, datasourceLayer: DatasourcePublicAPI, metadata: Record>, - paletteService: PaletteRegistry + paletteService: PaletteRegistry, + datasourceExpression: Ast ): Ast => { const columnToLabel = getColumnToLabelMap(layer, datasourceLayer); @@ -459,7 +416,7 @@ const dataLayerToExpression = ( chain: [ { type: 'function', - function: 'dataLayer', + function: 'extendedDataLayer', arguments: { layerId: [layer.layerId], hide: [Boolean(layer.hide)], @@ -476,6 +433,7 @@ const dataLayerToExpression = ( seriesType: [layer.seriesType], accessors: layer.accessors, columnToLabel: [JSON.stringify(columnToLabel)], + ...(datasourceExpression ? { table: [datasourceExpression] } : {}), palette: [ { type: 'expression', @@ -514,6 +472,23 @@ const yConfigToExpression = (yConfig: YConfig, defaultColor?: string): Ast => { { type: 'function', function: 'yConfig', + arguments: { + forAccessor: [yConfig.forAccessor], + axisMode: yConfig.axisMode ? [yConfig.axisMode] : [], + color: yConfig.color ? [yConfig.color] : defaultColor ? [defaultColor] : [], + }, + }, + ], + }; +}; + +const extendedYConfigToExpression = (yConfig: ExtendedYConfig, defaultColor?: string): Ast => { + return { + type: 'expression', + chain: [ + { + type: 'function', + function: 'extendedYConfig', arguments: { forAccessor: [yConfig.forAccessor], axisMode: yConfig.axisMode ? [yConfig.axisMode] : [], @@ -532,3 +507,21 @@ const yConfigToExpression = (yConfig: YConfig, defaultColor?: string): Ast => { ], }; }; + +const axisExtentConfigToExpression = ( + extent: AxisExtentConfig | undefined, + layers: ValidXYDataLayerConfig[] +): Ast => ({ + type: 'expression', + chain: [ + { + type: 'function', + function: 'axisExtentConfig', + arguments: { + mode: [extent?.mode ?? 'full'], + lowerBound: extent?.lowerBound !== undefined ? [extent?.lowerBound] : [], + upperBound: extent?.upperBound !== undefined ? [extent?.upperBound] : [], + }, + }, + ], +}); diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index 276e338807a35..b96ddf1aaee2d 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -16,7 +16,10 @@ import type { FittingFunction, LabelsOrientationConfig, EndValue, + ExtendedYConfig, YConfig, + YScaleType, + XScaleType, } from '@kbn/expression-xy-plugin/common'; import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; import { LensIconChartArea } from '../assets/chart_area'; @@ -43,12 +46,16 @@ export interface XYDataLayerConfig { yConfig?: YConfig[]; splitAccessor?: string; palette?: PaletteOutput; + yScaleType?: YScaleType; + xScaleType?: XScaleType; + isHistogram?: boolean; + columnToLabel?: string; } export interface XYReferenceLineLayerConfig { layerId: string; accessors: string[]; - yConfig?: YConfig[]; + yConfig?: ExtendedYConfig[]; layerType: 'referenceLine'; } @@ -64,6 +71,13 @@ export type XYLayerConfig = | XYReferenceLineLayerConfig | XYAnnotationLayerConfig; +export interface ValidXYDataLayerConfig extends XYDataLayerConfig { + xAccessor: NonNullable; + layerId: string; +} + +export type ValidLayer = ValidXYDataLayerConfig | XYReferenceLineLayerConfig; + // Persisted parts of the state export interface XYState { preferredSeriesType: SeriesType; diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index d67adb3b860e3..6c3fe3dcaea7f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -16,7 +16,12 @@ import { ThemeServiceStart } from '@kbn/core/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public'; -import { FillStyle, SeriesType, YAxisMode, YConfig } from '@kbn/expression-xy-plugin/common'; +import { + FillStyle, + SeriesType, + YAxisMode, + ExtendedYConfig, +} from '@kbn/expression-xy-plugin/common'; import { getSuggestions } from './xy_suggestions'; import { XyToolbar } from './xy_config_panel'; import { DimensionEditor } from './xy_config_panel/dimension_editor'; @@ -61,7 +66,7 @@ import { } from './visualization_helpers'; import { groupAxesByType } from './axes_configuration'; import { XYState } from './types'; -import { ReferenceLinePanel } from './xy_config_panel/reference_line_panel'; +import { ReferenceLinePanel } from './xy_config_panel/reference_line_config_panel'; import { AnnotationsPanel } from './xy_config_panel/annotations_config_panel'; import { DimensionTrigger } from '../shared_components/dimension_trigger'; import { defaultAnnotationLabel } from './annotations/helpers'; @@ -295,6 +300,7 @@ export const getXyVisualization = ({ setDimension(props) { const { prevState, layerId, columnId, groupId } = props; + const foundLayer: XYLayerConfig | undefined = prevState.layers.find( (l) => l.layerId === layerId ); @@ -333,7 +339,7 @@ export const getXyVisualization = ({ } const isReferenceLine = metrics.some((metric) => metric.agg === 'static_value'); const axisMode = axisPosition as YAxisMode; - const yConfig = metrics.map((metric, idx) => { + const yConfig = metrics.map((metric, idx) => { return { color: metric.color, forAccessor: metric.accessor ?? foundLayer.accessors[idx], @@ -444,7 +450,7 @@ export const getXyVisualization = ({ const groupsAvailable = getGroupsAvailableInData( getDataLayers(prevState.layers), frame.datasourceLayers, - frame?.activeData + frame.activeData ); if ( @@ -508,10 +514,24 @@ export const getXyVisualization = ({ ); }, - toExpression: (state, layers, attributes) => - toExpression(state, layers, paletteService, attributes, eventAnnotationService), - toPreviewExpression: (state, layers) => - toPreviewExpression(state, layers, paletteService, eventAnnotationService), + toExpression: (state, layers, attributes, datasourceExpressionsByLayers = {}) => + toExpression( + state, + layers, + paletteService, + attributes, + datasourceExpressionsByLayers, + eventAnnotationService + ), + + toPreviewExpression: (state, layers, datasourceExpressionsByLayers = {}) => + toPreviewExpression( + state, + layers, + paletteService, + datasourceExpressionsByLayers, + eventAnnotationService + ), getErrorMessages(state, datasourceLayers) { // Data error handling below here @@ -592,10 +612,12 @@ export const getXyVisualization = ({ ...getDataLayers(state.layers), ...getReferenceLayers(state.layers), ].filter(({ accessors }) => accessors.length > 0); + const accessorsWithArrayValues = []; + for (const layer of filteredLayers) { const { layerId, accessors } = layer; - const rows = frame.activeData[layerId] && frame.activeData[layerId].rows; + const rows = frame.activeData?.[layerId] && frame.activeData[layerId].rows; if (!rows) { break; } @@ -607,6 +629,7 @@ export const getXyVisualization = ({ } } } + return accessorsWithArrayValues.map((label) => ( { + return new Color(transparentize(color, 0.1)).hexa(); +}; + +export const toLineAnnotationColor = (color = defaultAnnotationRangeColor) => { + return new Color(transparentize(color, 1)).hex(); +}; + +export const getEndTimestamp = ( + startTime: string, + { activeData, dateRange }: FramePublicAPI, + dataLayers: XYDataLayerConfig[] +) => { + const startTimeNumber = moment(startTime).valueOf(); + const dateRangeFraction = + (moment(dateRange.toDate).valueOf() - moment(dateRange.fromDate).valueOf()) * 0.1; + const fallbackValue = moment(startTimeNumber + dateRangeFraction).toISOString(); + const dataLayersId = dataLayers.map(({ layerId }) => layerId); + if ( + !dataLayersId.length || + !activeData || + Object.entries(activeData) + .filter(([key]) => dataLayersId.includes(key)) + .every(([, { rows }]) => !rows || !rows.length) + ) { + return fallbackValue; + } + const xColumn = activeData?.[dataLayersId[0]].columns.find( + (column) => column.id === dataLayers[0].xAccessor + ); + if (!xColumn) { + return fallbackValue; + } + + const dateInterval = search.aggs.getDateHistogramMetaDataByDatatableColumn(xColumn)?.interval; + if (!dateInterval) return fallbackValue; + const intervalDuration = search.aggs.parseInterval(dateInterval); + if (!intervalDuration) return fallbackValue; + return moment(startTimeNumber + 3 * intervalDuration.as('milliseconds')).toISOString(); +}; + +const sanitizeProperties = (annotation: EventAnnotationConfig) => { + if (isRangeAnnotation(annotation)) { + const rangeAnnotation: RangeEventAnnotationConfig = pick(annotation, [ + 'label', + 'key', + 'id', + 'isHidden', + 'color', + 'outside', + ]); + return rangeAnnotation; + } else { + const lineAnnotation: PointInTimeEventAnnotationConfig = pick(annotation, [ + 'id', + 'label', + 'key', + 'isHidden', + 'lineStyle', + 'lineWidth', + 'color', + 'icon', + 'textVisibility', + ]); + return lineAnnotation; + } +}; + +export const AnnotationsPanel = ( + props: VisualizationDimensionEditorProps & { + formatFactory: FormatFactory; + paletteService: PaletteRegistry; + } +) => { + const { state, setState, layerId, accessor, frame } = props; + const isHorizontal = isHorizontalChart(state.layers); + + const { inputValue: localState, handleInputChange: setLocalState } = useDebouncedValue({ + value: state, + onChange: setState, + }); + + const index = localState.layers.findIndex((l) => l.layerId === layerId); + const localLayer = localState.layers.find( + (l) => l.layerId === layerId + ) as XYAnnotationLayerConfig; + + const currentAnnotation = localLayer.annotations?.find((c) => c.id === accessor); + + const isRange = isRangeAnnotation(currentAnnotation); + + const setAnnotations = useCallback( + (annotation) => { + if (annotation == null) { + return; + } + const newConfigs = [...(localLayer.annotations || [])]; + const existingIndex = newConfigs.findIndex((c) => c.id === accessor); + if (existingIndex !== -1) { + newConfigs[existingIndex] = sanitizeProperties({ + ...newConfigs[existingIndex], + ...annotation, + }); + } else { + throw new Error( + 'should never happen because annotation is created before config panel is opened' + ); + } + setLocalState(updateLayer(localState, { ...localLayer, annotations: newConfigs }, index)); + }, + [accessor, index, localState, localLayer, setLocalState] + ); + + return ( + <> + + {isRange ? ( + <> + { + if (date) { + const currentEndTime = moment(currentAnnotation?.key.endTimestamp).valueOf(); + if (currentEndTime < date.valueOf()) { + const currentStartTime = moment(currentAnnotation?.key.timestamp).valueOf(); + const dif = currentEndTime - currentStartTime; + setAnnotations({ + key: { + ...(currentAnnotation?.key || { type: 'range' }), + timestamp: date.toISOString(), + endTimestamp: moment(date.valueOf() + dif).toISOString(), + }, + }); + } else { + setAnnotations({ + key: { + ...(currentAnnotation?.key || { type: 'range' }), + timestamp: date.toISOString(), + }, + }); + } + } + }} + label={i18n.translate('xpack.lens.xyChart.annotationDate', { + defaultMessage: 'Annotation date', + })} + /> + { + if (date) { + const currentStartTime = moment(currentAnnotation?.key.timestamp).valueOf(); + if (currentStartTime > date.valueOf()) { + const currentEndTime = moment(currentAnnotation?.key.endTimestamp).valueOf(); + const dif = currentEndTime - currentStartTime; + setAnnotations({ + key: { + ...(currentAnnotation?.key || { type: 'range' }), + endTimestamp: date.toISOString(), + timestamp: moment(date.valueOf() - dif).toISOString(), + }, + }); + } else { + setAnnotations({ + key: { + ...(currentAnnotation?.key || { type: 'range' }), + endTimestamp: date.toISOString(), + }, + }); + } + } + }} + /> + + ) : ( + { + if (date) { + setAnnotations({ + key: { + ...(currentAnnotation?.key || { type: 'point_in_time' }), + timestamp: date.toISOString(), + }, + }); + } + }} + /> + )} + + + + + { + setAnnotations({ label: value }); + }} + /> + {!isRange && ( + + )} + {!isRange && ( + + )} + {!isRange && ( + + )} + + {isRange && ( + + { + setAnnotations({ + outside: id === `lens_xyChart_fillStyle_outside`, + }); + }} + isFullWidth + /> + + )} + + + setAnnotations({ isHidden: ev.target.checked })} + /> + + + ); +}; + +const ConfigPanelApplyAsRangeSwitch = ({ + annotation, + onChange, + frame, + state, +}: { + annotation?: EventAnnotationConfig; + onChange: (annotations: Partial | undefined) => void; + frame: FramePublicAPI; + state: XYState; +}) => { + const isRange = isRangeAnnotation(annotation); + return ( + + + {i18n.translate('xpack.lens.xyChart.applyAsRange', { + defaultMessage: 'Apply as range', + })} + + } + checked={isRange} + onChange={() => { + if (isRange) { + const newPointAnnotation: PointInTimeEventAnnotationConfig = { + key: { + type: 'point_in_time', + timestamp: annotation.key.timestamp, + }, + id: annotation.id, + label: + annotation.label === defaultRangeAnnotationLabel + ? defaultAnnotationLabel + : annotation.label, + color: toLineAnnotationColor(annotation.color), + isHidden: annotation.isHidden, + }; + onChange(newPointAnnotation); + } else if (annotation) { + const fromTimestamp = moment(annotation?.key.timestamp); + const dataLayers = getDataLayers(state.layers); + const newRangeAnnotation: RangeEventAnnotationConfig = { + key: { + type: 'range', + timestamp: annotation.key.timestamp, + endTimestamp: getEndTimestamp(fromTimestamp.toISOString(), frame, dataLayers), + }, + id: annotation.id, + label: + annotation.label === defaultAnnotationLabel + ? defaultRangeAnnotationLabel + : annotation.label, + color: toRangeAnnotationColor(annotation.color), + isHidden: annotation.isHidden, + }; + onChange(newRangeAnnotation); + } + }} + compressed + /> + + ); +}; + +const ConfigPanelRangeDatePicker = ({ + value, + label, + prependLabel, + onChange, + dataTestSubj = 'lnsXY_annotation_date_picker', +}: { + value: moment.Moment; + prependLabel?: string; + label?: string; + onChange: (val: moment.Moment | null) => void; + dataTestSubj?: string; +}) => { + return ( + + {prependLabel ? ( + {prependLabel} + } + > + + + ) : ( + + )} + + ); +}; + +const ConfigPanelHideSwitch = ({ + value, + onChange, +}: { + value: boolean; + onChange: (event: EuiSwitchEvent) => void; +}) => { + return ( + + + + ); +}; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/icon_set.ts b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/icon_set.ts index 87813ec12913e..32721285a4477 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/icon_set.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/icon_set.ts @@ -4,10 +4,13 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { i18n } from '@kbn/i18n'; +import { AvailableAnnotationIcon } from '@kbn/event-annotation-plugin/common'; import { IconTriangle, IconCircle } from '../../../assets/annotation_icons'; +import { IconSet } from '../shared/icon_select'; -export const annotationsIconSet = [ +export const annotationsIconSet: IconSet = [ { value: 'asterisk', label: i18n.translate('xpack.lens.xyChart.iconSelect.asteriskIconLabel', { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.scss b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.scss new file mode 100644 index 0000000000000..3a0f4b944aa6c --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.scss @@ -0,0 +1,12 @@ +.lnsRowCompressedMargin+.lnsRowCompressedMargin { + margin-top: $euiSizeS; +} + +.lnsConfigPanelNoPadding { + padding: 0; +} + +.lnsConfigPanelDate__label { + min-width: 56px; // makes both labels ("from" and "to") the same width + text-align: center; +} \ No newline at end of file diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.test.tsx new file mode 100644 index 0000000000000..f97b4009e3e3e --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.test.tsx @@ -0,0 +1,222 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { mountWithIntl as mount } from '@kbn/test-jest-helpers'; +import { AnnotationsPanel } from '.'; +import { FramePublicAPI } from '../../../types'; +import { layerTypes } from '../../..'; +import { createMockFramePublicAPI } from '../../../mocks'; +import { State } from '../../types'; +import { Position } from '@elastic/charts'; +import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; +import moment from 'moment'; + +jest.mock('lodash', () => { + const original = jest.requireActual('lodash'); + + return { + ...original, + debounce: (fn: unknown) => fn, + }; +}); + +const customLineStaticAnnotation = { + id: 'ann1', + key: { type: 'point_in_time' as const, timestamp: '2022-03-18T08:25:00.000Z' }, + label: 'Event', + icon: 'triangle' as const, + color: 'red', + lineStyle: 'dashed' as const, + lineWidth: 3, +}; + +describe('AnnotationsPanel', () => { + let frame: FramePublicAPI; + + function testState(): State { + return { + legend: { isVisible: true, position: Position.Right }, + valueLabels: 'hide', + preferredSeriesType: 'bar', + layers: [ + { + layerType: layerTypes.ANNOTATIONS, + layerId: 'annotation', + annotations: [customLineStaticAnnotation], + }, + ], + }; + } + + beforeEach(() => { + frame = createMockFramePublicAPI(); + frame.datasourceLayers = {}; + }); + describe('Dimension Editor', () => { + test('shows correct options for line annotations', () => { + const state = testState(); + const component = mount( + + ); + + expect( + component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-time"]').prop('selected') + ).toEqual(moment('2022-03-18T08:25:00.000Z')); + expect( + component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-fromTime"]').exists() + ).toBeFalsy(); + expect( + component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-toTime"]').exists() + ).toBeFalsy(); + expect( + component.find('EuiSwitch[data-test-subj="lns-xyAnnotation-rangeSwitch"]').prop('checked') + ).toEqual(false); + expect( + component.find('EuiFieldText[data-test-subj="column-label-edit"]').prop('value') + ).toEqual('Event'); + expect( + component.find('EuiComboBox[data-test-subj="lns-icon-select"]').prop('selectedOptions') + ).toEqual([{ label: 'Triangle', value: 'triangle' }]); + expect(component.find('TextDecorationSetting').exists()).toBeTruthy(); + expect(component.find('LineStyleSettings').exists()).toBeTruthy(); + expect( + component.find('EuiButtonGroup[data-test-subj="lns-xyAnnotation-fillStyle"]').exists() + ).toBeFalsy(); + }); + test('shows correct options for range annotations', () => { + const state = testState(); + state.layers[0] = { + annotations: [ + { + color: 'red', + icon: 'triangle', + id: 'ann1', + isHidden: undefined, + key: { + endTimestamp: '2022-03-21T10:49:00.000Z', + timestamp: '2022-03-18T08:25:00.000Z', + type: 'range', + }, + label: 'Event range', + lineStyle: 'dashed', + lineWidth: 3, + }, + ], + layerId: 'annotation', + layerType: 'annotations', + }; + const component = mount( + + ); + + expect( + component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-fromTime"]').prop('selected') + ).toEqual(moment('2022-03-18T08:25:00.000Z')); + expect( + component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-toTime"]').prop('selected') + ).toEqual(moment('2022-03-21T10:49:00.000Z')); + expect( + component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-time"]').exists() + ).toBeFalsy(); + expect( + component.find('EuiSwitch[data-test-subj="lns-xyAnnotation-rangeSwitch"]').prop('checked') + ).toEqual(true); + expect( + component.find('EuiFieldText[data-test-subj="column-label-edit"]').prop('value') + ).toEqual('Event range'); + expect(component.find('EuiComboBox[data-test-subj="lns-icon-select"]').exists()).toBeFalsy(); + expect(component.find('TextDecorationSetting').exists()).toBeFalsy(); + expect(component.find('LineStyleSettings').exists()).toBeFalsy(); + expect(component.find('[data-test-subj="lns-xyAnnotation-fillStyle"]').exists()).toBeTruthy(); + }); + + test('calculates correct endTimstamp and transparent color when switching for range annotation and back', () => { + const state = testState(); + const setState = jest.fn(); + const component = mount( + + ); + component.find('button[data-test-subj="lns-xyAnnotation-rangeSwitch"]').simulate('click'); + + expect(setState).toBeCalledWith({ + ...state, + layers: [ + { + annotations: [ + { + color: '#FF00001A', + id: 'ann1', + isHidden: undefined, + label: 'Event range', + key: { + endTimestamp: '2022-03-21T10:49:00.000Z', + timestamp: '2022-03-18T08:25:00.000Z', + type: 'range', + }, + }, + ], + layerId: 'annotation', + layerType: 'annotations', + }, + ], + }); + component.find('button[data-test-subj="lns-xyAnnotation-rangeSwitch"]').simulate('click'); + expect(setState).toBeCalledWith({ + ...state, + layers: [ + { + annotations: [ + { + color: '#FF0000', + id: 'ann1', + isHidden: undefined, + key: { + timestamp: '2022-03-18T08:25:00.000Z', + type: 'point_in_time', + }, + label: 'Event', + }, + ], + layerId: 'annotation', + layerType: 'annotations', + }, + ], + }); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.tsx index 6435f4c7ba2b8..bd63354936703 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.tsx @@ -5,183 +5,4 @@ * 2.0. */ -import React, { useCallback } from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiDatePicker, EuiFormRow, EuiSwitch, EuiSwitchEvent } from '@elastic/eui'; -import type { PaletteRegistry } from '@kbn/coloring'; -import moment from 'moment'; -import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common/types'; -import type { VisualizationDimensionEditorProps } from '../../../types'; -import { State, XYState, XYAnnotationLayerConfig } from '../../types'; -import { FormatFactory } from '../../../../common'; -import { DimensionEditorSection, NameInput, useDebouncedValue } from '../../../shared_components'; -import { isHorizontalChart } from '../../state_helpers'; -import { defaultAnnotationLabel } from '../../annotations/helpers'; -import { ColorPicker } from '../color_picker'; -import { IconSelectSetting, TextDecorationSetting } from '../shared/marker_decoration_settings'; -import { LineStyleSettings } from '../shared/line_style_settings'; -import { updateLayer } from '..'; -import { annotationsIconSet } from './icon_set'; - -export const AnnotationsPanel = ( - props: VisualizationDimensionEditorProps & { - formatFactory: FormatFactory; - paletteService: PaletteRegistry; - } -) => { - const { state, setState, layerId, accessor } = props; - const isHorizontal = isHorizontalChart(state.layers); - - const { inputValue: localState, handleInputChange: setLocalState } = useDebouncedValue({ - value: state, - onChange: setState, - }); - - const index = localState.layers.findIndex((l) => l.layerId === layerId); - const localLayer = localState.layers.find( - (l) => l.layerId === layerId - ) as XYAnnotationLayerConfig; - - const currentAnnotations = localLayer.annotations?.find((c) => c.id === accessor); - - const setAnnotations = useCallback( - (annotations: Partial | undefined) => { - if (annotations == null) { - return; - } - const newConfigs = [...(localLayer.annotations || [])]; - const existingIndex = newConfigs.findIndex((c) => c.id === accessor); - if (existingIndex !== -1) { - newConfigs[existingIndex] = { ...newConfigs[existingIndex], ...annotations }; - } else { - return; // that should never happen because annotations are created before annotations panel is opened - } - setLocalState(updateLayer(localState, { ...localLayer, annotations: newConfigs }, index)); - }, - [accessor, index, localState, localLayer, setLocalState] - ); - - return ( - <> - - { - if (date) { - setAnnotations({ - key: { - ...(currentAnnotations?.key || { type: 'point_in_time' }), - timestamp: date.toISOString(), - }, - }); - } - }} - label={i18n.translate('xpack.lens.xyChart.annotationDate', { - defaultMessage: 'Annotation date', - })} - /> - - - { - setAnnotations({ label: value }); - }} - /> - - - - - setAnnotations({ isHidden: ev.target.checked })} - /> - - - ); -}; - -const ConfigPanelDatePicker = ({ - value, - label, - onChange, -}: { - value: moment.Moment; - label: string; - onChange: (val: moment.Moment | null) => void; -}) => { - return ( - - - - ); -}; - -const ConfigPanelHideSwitch = ({ - value, - onChange, -}: { - value: boolean; - onChange: (event: EuiSwitchEvent) => void; -}) => { - return ( - - - - ); -}; +export { AnnotationsPanel } from './annotations_panel'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx index 37ee50f527d43..00f03f261ef7b 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx @@ -5,23 +5,26 @@ * 2.0. */ -import React, { useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; +import chroma from 'chroma-js'; import { i18n } from '@kbn/i18n'; -import { EuiFormRow, EuiColorPicker, EuiColorPickerProps, EuiToolTip, EuiIcon } from '@elastic/eui'; +import { + EuiFormRow, + EuiColorPicker, + EuiColorPickerProps, + EuiToolTip, + EuiIcon, + euiPaletteColorBlind, +} from '@elastic/eui'; import type { PaletteRegistry } from '@kbn/coloring'; -import { defaultAnnotationColor } from '@kbn/event-annotation-plugin/public'; import type { VisualizationDimensionEditorProps } from '../../types'; -import { State, XYDataLayerConfig } from '../types'; +import { State } from '../types'; import { FormatFactory } from '../../../common'; import { getSeriesColor } from '../state_helpers'; -import { - defaultReferenceLineColor, - getAccessorColorConfig, - getColorAssignments, -} from '../color_assignment'; +import { getAccessorColorConfig, getColorAssignments } from '../color_assignment'; import { getSortedAccessors } from '../to_expression'; import { TooltipWrapper } from '../../shared_components'; -import { isReferenceLayer, isAnnotationsLayer, getDataLayers } from '../visualization_helpers'; +import { getDataLayers, isDataLayer } from '../visualization_helpers'; const tooltipContent = { auto: i18n.translate('xpack.lens.configPanel.color.tooltip.auto', { @@ -47,6 +50,8 @@ export const ColorPicker = ({ disableHelpTooltip, disabled, setConfig, + showAlpha, + defaultColor, }: VisualizationDimensionEditorProps & { formatFactory: FormatFactory; paletteService: PaletteRegistry; @@ -54,6 +59,8 @@ export const ColorPicker = ({ disableHelpTooltip?: boolean; disabled?: boolean; setConfig: (config: { color?: string }) => void; + showAlpha?: boolean; + defaultColor?: string; }) => { const index = state.layers.findIndex((l) => l.layerId === layerId); const layer = state.layers[index]; @@ -61,38 +68,47 @@ export const ColorPicker = ({ const overwriteColor = getSeriesColor(layer, accessor); const currentColor = useMemo(() => { if (overwriteColor || !frame.activeData) return overwriteColor; - if (isReferenceLayer(layer)) { - return defaultReferenceLineColor; - } else if (isAnnotationsLayer(layer)) { - return defaultAnnotationColor; + if (defaultColor) { + return defaultColor; } - - const dataLayer: XYDataLayerConfig = layer; - const sortedAccessors: string[] = getSortedAccessors( - frame.datasourceLayers[layer.layerId] ?? layer.accessors, - layer - ); - - const colorAssignments = getColorAssignments( - getDataLayers(state.layers), - { tables: frame.activeData }, - formatFactory - ); - const mappedAccessors = getAccessorColorConfig( - colorAssignments, - frame, - { - ...dataLayer, - accessors: sortedAccessors.filter((sorted) => dataLayer.accessors.includes(sorted)), - }, - paletteService - ); - - return mappedAccessors.find((a) => a.columnId === accessor)?.color || null; - }, [overwriteColor, frame, paletteService, state.layers, accessor, formatFactory, layer]); + if (isDataLayer(layer)) { + const sortedAccessors: string[] = getSortedAccessors( + frame.datasourceLayers[layer.layerId] ?? layer.accessors, + layer + ); + const colorAssignments = getColorAssignments( + getDataLayers(state.layers), + { tables: frame.activeData ?? {} }, + formatFactory + ); + const mappedAccessors = getAccessorColorConfig( + colorAssignments, + frame, + { + ...layer, + accessors: sortedAccessors.filter((sorted) => layer.accessors.includes(sorted)), + }, + paletteService + ); + return mappedAccessors.find((a) => a.columnId === accessor)?.color || null; + } + }, [ + overwriteColor, + frame, + paletteService, + state.layers, + accessor, + formatFactory, + layer, + defaultColor, + ]); const [color, setColor] = useState(currentColor); + useEffect(() => { + setColor(currentColor); + }, [currentColor]); + const handleColor: EuiColorPickerProps['onChange'] = (text, output) => { setColor(text); if (output.isValid || text === '') { @@ -107,8 +123,11 @@ export const ColorPicker = ({ defaultMessage: 'Series color', }); + const currentColorAlpha = color ? chroma(color).alpha() : 1; + const colorPicker = ( chroma(c).alpha(currentColorAlpha).hex()) + } /> ); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx index 39d08e4c710bd..eec3335c5b44c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx @@ -9,7 +9,7 @@ import React, { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow, htmlIdGenerator } from '@elastic/eui'; import type { PaletteRegistry } from '@kbn/coloring'; -import { YAxisMode, YConfig } from '@kbn/expression-xy-plugin/common'; +import { YAxisMode, ExtendedYConfig } from '@kbn/expression-xy-plugin/common'; import type { VisualizationDimensionEditorProps } from '../../types'; import { State, XYState, XYDataLayerConfig } from '../types'; import { FormatFactory } from '../../../common'; @@ -17,7 +17,7 @@ import { isHorizontalChart } from '../state_helpers'; import { ColorPicker } from './color_picker'; import { PalettePicker, useDebouncedValue } from '../../shared_components'; import { isAnnotationsLayer, isReferenceLayer } from '../visualization_helpers'; -import { ReferenceLinePanel } from './reference_line_panel'; +import { ReferenceLinePanel } from './reference_line_config_panel'; import { AnnotationsPanel } from './annotations_config_panel'; type UnwrapArray = T extends Array ? P : T; @@ -57,7 +57,7 @@ export function DimensionEditor( const axisMode = localYConfig?.axisMode || 'auto'; const setConfig = useCallback( - (yConfig: Partial | undefined) => { + (yConfig: Partial | undefined) => { if (yConfig == null) { return; } diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx index b61f4694f8a91..bd39a61b08acd 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx @@ -5,11 +5,12 @@ * 2.0. */ -import React, { memo, useCallback } from 'react'; +import React, { memo, useCallback, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { Position, ScaleType, VerticalAlignment, HorizontalAlignment } from '@elastic/charts'; +import { Position, ScaleType } from '@elastic/charts'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { AxesSettingsConfig, AxisExtentConfig } from '@kbn/expression-xy-plugin/common'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; import type { VisualizationToolbarProps, FramePublicAPI } from '../../types'; import { State, XYState } from '../types'; import { isHorizontalChart } from '../state_helpers'; @@ -21,6 +22,7 @@ import { getScaleType } from '../to_expression'; import { TooltipWrapper } from '../../shared_components'; import { getDefaultVisualValuesForLayer } from '../../shared_components/datasource_default_values'; import { getDataLayers } from '../visualization_helpers'; +import { LegendSettingsPopoverProps } from '../../shared_components/legend_settings_popover'; type UnwrapArray = T extends Array ? P : T; type AxesSettingsConfigKeys = keyof AxesSettingsConfig; @@ -293,6 +295,10 @@ export const XyToolbar = memo(function XyToolbar( props.frame.datasourceLayers ).truncateText; + const legendSize = state.legend.legendSize; + + const [hadAutoLegendSize] = useState(() => legendSize === LegendSize.AUTO); + return ( @@ -380,8 +386,10 @@ export const XyToolbar = memo(function XyToolbar( }} onAlignmentChange={(value) => { const [vertical, horizontal] = value.split('_'); - const verticalAlignment = vertical as VerticalAlignment; - const horizontalAlignment = horizontal as HorizontalAlignment; + const verticalAlignment = vertical as LegendSettingsPopoverProps['verticalAlignment']; + const horizontalAlignment = + horizontal as LegendSettingsPopoverProps['horizontalAlignment']; + setState({ ...state, legend: { ...state.legend, verticalAlignment, horizontalAlignment }, @@ -395,16 +403,17 @@ export const XyToolbar = memo(function XyToolbar( valuesInLegend: !state.valuesInLegend, }); }} - legendSize={state.legend.legendSize} - onLegendSizeChange={(legendSize) => { + legendSize={legendSize} + onLegendSizeChange={(newLegendSize) => { setState({ ...state, legend: { ...state.legend, - legendSize, + legendSize: newLegendSize, }, }); }} + showAutoLegendSizeOption={hadAutoLegendSize} />
diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx index 367785da2d6b1..1fbb2af8f1e98 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx @@ -11,14 +11,14 @@ import { EuiIcon, EuiPopover, EuiSelectable, EuiText, EuiPopoverTitle } from '@e import { SeriesType } from '@kbn/expression-xy-plugin/common'; import { ToolbarButton } from '@kbn/kibana-react-plugin/public'; import type { VisualizationLayerWidgetProps, VisualizationType } from '../../types'; -import { State, visualizationTypes, XYDataLayerConfig } from '../types'; +import { State, visualizationTypes } from '../types'; import { isHorizontalChart, isHorizontalSeries } from '../state_helpers'; import { trackUiEvent } from '../../lens_ui_telemetry'; import { StaticHeader } from '../../shared_components'; import { LensIconChartBarReferenceLine } from '../../assets/chart_bar_reference_line'; import { LensIconChartBarAnnotations } from '../../assets/chart_bar_annotations'; import { updateLayer } from '.'; -import { isAnnotationsLayer, isReferenceLayer } from '../visualization_helpers'; +import { isAnnotationsLayer, isDataLayer, isReferenceLayer } from '../visualization_helpers'; export function LayerHeader(props: VisualizationLayerWidgetProps) { const layer = props.state.layers.find((l) => l.layerId === props.layerId); @@ -58,7 +58,7 @@ function AnnotationsLayerHeader() { function DataLayerHeader(props: VisualizationLayerWidgetProps) { const [isPopoverOpen, setPopoverIsOpen] = useState(false); const { state, layerId } = props; - const layers = state.layers as XYDataLayerConfig[]; + const layers = state.layers.filter(isDataLayer); const index = layers.findIndex((l) => l.layerId === layerId); const layer = layers[index]; const currentVisType = visualizationTypes.find(({ id }) => id === layer.seriesType)!; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/icon_set.ts b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/icon_set.ts new file mode 100644 index 0000000000000..eda5d06cd3ef1 --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/icon_set.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { AvailableReferenceLineIcon } from '@kbn/expression-xy-plugin/common'; +import { IconSet } from '../shared/icon_select'; + +export const referenceLineIconsSet: IconSet = [ + { + value: 'empty', + label: i18n.translate('xpack.lens.xyChart.iconSelect.noIconLabel', { + defaultMessage: 'None', + }), + }, + { + value: 'asterisk', + label: i18n.translate('xpack.lens.xyChart.iconSelect.asteriskIconLabel', { + defaultMessage: 'Asterisk', + }), + }, + { + value: 'bell', + label: i18n.translate('xpack.lens.xyChart.iconSelect.bellIconLabel', { + defaultMessage: 'Bell', + }), + }, + { + value: 'bolt', + label: i18n.translate('xpack.lens.xyChart.iconSelect.boltIconLabel', { + defaultMessage: 'Bolt', + }), + }, + { + value: 'bug', + label: i18n.translate('xpack.lens.xyChart.iconSelect.bugIconLabel', { + defaultMessage: 'Bug', + }), + }, + { + value: 'editorComment', + label: i18n.translate('xpack.lens.xyChart.iconSelect.commentIconLabel', { + defaultMessage: 'Comment', + }), + }, + { + value: 'alert', + label: i18n.translate('xpack.lens.xyChart.iconSelect.alertIconLabel', { + defaultMessage: 'Alert', + }), + }, + { + value: 'flag', + label: i18n.translate('xpack.lens.xyChart.iconSelect.flagIconLabel', { + defaultMessage: 'Flag', + }), + }, + { + value: 'tag', + label: i18n.translate('xpack.lens.xyChart.iconSelect.tagIconLabel', { + defaultMessage: 'Tag', + }), + }, +]; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/index.tsx new file mode 100644 index 0000000000000..4297f7d35cd6c --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/index.tsx @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { ReferenceLinePanel } from './reference_line_panel'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/reference_line_panel.tsx similarity index 80% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx rename to x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/reference_line_panel.tsx index d5a3e8ffd7a46..e25c191d2bec1 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/reference_line_panel.tsx @@ -9,22 +9,24 @@ import React, { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; import type { PaletteRegistry } from '@kbn/coloring'; -import { FillStyle, YConfig } from '@kbn/expression-xy-plugin/common'; -import type { VisualizationDimensionEditorProps } from '../../types'; -import { State, XYState, XYReferenceLineLayerConfig } from '../types'; -import { FormatFactory } from '../../../common'; +import { FillStyle, ExtendedYConfig } from '@kbn/expression-xy-plugin/common'; +import type { VisualizationDimensionEditorProps } from '../../../types'; +import { State, XYState, XYReferenceLineLayerConfig } from '../../types'; +import { FormatFactory } from '../../../../common'; -import { ColorPicker } from './color_picker'; -import { updateLayer } from '.'; -import { useDebouncedValue } from '../../shared_components'; -import { idPrefix } from './dimension_editor'; -import { isHorizontalChart } from '../state_helpers'; +import { ColorPicker } from '../color_picker'; +import { updateLayer } from '..'; +import { useDebouncedValue } from '../../../shared_components'; +import { idPrefix } from '../dimension_editor'; +import { isHorizontalChart } from '../../state_helpers'; import { IconSelectSetting, MarkerDecorationPosition, TextDecorationSetting, -} from './shared/marker_decoration_settings'; -import { LineStyleSettings } from './shared/line_style_settings'; +} from '../shared/marker_decoration_settings'; +import { LineStyleSettings } from '../shared/line_style_settings'; +import { referenceLineIconsSet } from './icon_set'; +import { defaultReferenceLineColor } from '../../color_assignment'; export const ReferenceLinePanel = ( props: VisualizationDimensionEditorProps & { @@ -50,7 +52,7 @@ export const ReferenceLinePanel = ( ); const setConfig = useCallback( - (yConfig: Partial | undefined) => { + (yConfig: Partial | undefined) => { if (yConfig == null) { return; } @@ -74,7 +76,11 @@ export const ReferenceLinePanel = ( return ( <> - + | undefined) => void; + currentConfig?: ExtendedYConfig; + setConfig: (yConfig: Partial | undefined) => void; isHorizontal: boolean; }) => { return ( diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/icon_select.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/icon_select.tsx index e2b5923e81031..74eb23e3e8297 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/icon_select.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/icon_select.tsx @@ -6,71 +6,20 @@ */ import React from 'react'; -import { i18n } from '@kbn/i18n'; import { EuiComboBox, EuiFlexGroup, EuiFlexItem, EuiIcon, IconType } from '@elastic/eui'; +import { AvailableReferenceLineIcon } from '@kbn/expression-xy-plugin/common'; export function hasIcon(icon: string | undefined): icon is string { return icon != null && icon !== 'empty'; } -export type IconSet = Array<{ value: string; label: string; icon?: IconType }>; - -export const euiIconsSet = [ - { - value: 'empty', - label: i18n.translate('xpack.lens.xyChart.iconSelect.noIconLabel', { - defaultMessage: 'None', - }), - }, - { - value: 'asterisk', - label: i18n.translate('xpack.lens.xyChart.iconSelect.asteriskIconLabel', { - defaultMessage: 'Asterisk', - }), - }, - { - value: 'bell', - label: i18n.translate('xpack.lens.xyChart.iconSelect.bellIconLabel', { - defaultMessage: 'Bell', - }), - }, - { - value: 'bolt', - label: i18n.translate('xpack.lens.xyChart.iconSelect.boltIconLabel', { - defaultMessage: 'Bolt', - }), - }, - { - value: 'bug', - label: i18n.translate('xpack.lens.xyChart.iconSelect.bugIconLabel', { - defaultMessage: 'Bug', - }), - }, - { - value: 'editorComment', - label: i18n.translate('xpack.lens.xyChart.iconSelect.commentIconLabel', { - defaultMessage: 'Comment', - }), - }, - { - value: 'alert', - label: i18n.translate('xpack.lens.xyChart.iconSelect.alertIconLabel', { - defaultMessage: 'Alert', - }), - }, - { - value: 'flag', - label: i18n.translate('xpack.lens.xyChart.iconSelect.flagIconLabel', { - defaultMessage: 'Flag', - }), - }, - { - value: 'tag', - label: i18n.translate('xpack.lens.xyChart.iconSelect.tagIconLabel', { - defaultMessage: 'Tag', - }), - }, -]; +export type IconSet = Array<{ + value: T; + label: string; + icon?: T | IconType; + shouldRotate?: boolean; + canFill?: boolean; +}>; const IconView = (props: { value?: string; label: string; icon?: IconType }) => { if (!props.value) return null; @@ -84,24 +33,33 @@ const IconView = (props: { value?: string; label: string; icon?: IconType }) => ); }; -export const IconSelect = ({ +export function IconSelect({ value, onChange, - customIconSet = euiIconsSet, + customIconSet, + defaultIcon = 'empty', }: { - value?: string; - onChange: (newIcon: string) => void; - customIconSet?: IconSet; -}) => { + value?: Icon; + onChange: (newIcon: Icon) => void; + customIconSet: IconSet; + defaultIcon?: string; +}) { const selectedIcon = customIconSet.find((option) => value === option.value) || - customIconSet.find((option) => option.value === 'empty')!; + customIconSet.find((option) => option.value === defaultIcon)!; return ( { onChange(selection[0].value!); }} @@ -115,4 +73,4 @@ export const IconSelect = ({ } /> ); -}; +} diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx index b780737877388..64b00ef246161 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx @@ -71,22 +71,22 @@ function getIconPositionOptions({ isHorizontal, axisMode }: LabelConfigurationOp ]; } -interface MarkerDecorationConfig { +export interface MarkerDecorationConfig { axisMode?: YAxisMode; - icon?: string; + icon?: T; iconPosition?: IconPosition; textVisibility?: boolean; } -export const TextDecorationSetting = ({ +export function TextDecorationSetting({ currentConfig, setConfig, customIconSet, }: { - currentConfig?: MarkerDecorationConfig; - setConfig: (config: MarkerDecorationConfig) => void; - customIconSet?: IconSet; -}) => { + currentConfig?: MarkerDecorationConfig; + setConfig: (config: MarkerDecorationConfig) => void; + customIconSet?: IconSet; +}) { return ( ); -}; +} -export const IconSelectSetting = ({ +export function IconSelectSetting({ currentConfig, setConfig, customIconSet, + defaultIcon = 'empty', }: { - currentConfig?: MarkerDecorationConfig; - setConfig: (config: MarkerDecorationConfig) => void; - customIconSet?: IconSet; -}) => { + currentConfig?: MarkerDecorationConfig; + setConfig: (config: MarkerDecorationConfig) => void; + customIconSet: IconSet; + defaultIcon?: string; +}) { return ( { @@ -154,17 +157,17 @@ export const IconSelectSetting = ({ /> ); -}; +} -export const MarkerDecorationPosition = ({ +export function MarkerDecorationPosition({ currentConfig, setConfig, isHorizontal, }: { - currentConfig?: MarkerDecorationConfig; - setConfig: (config: MarkerDecorationConfig) => void; + currentConfig?: MarkerDecorationConfig; + setConfig: (config: MarkerDecorationConfig) => void; isHorizontal: boolean; -}) => { +}) { return ( <> {hasIcon(currentConfig?.icon) || currentConfig?.textVisibility ? ( @@ -210,4 +213,4 @@ export const MarkerDecorationPosition = ({ ) : null} ); -}; +} diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx index d4edb960e70f7..ba8a246043bf2 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx @@ -7,7 +7,6 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { ValidLayer } from '@kbn/expression-xy-plugin/common'; import { ToolbarPopover, TooltipWrapper, ValueLabelsSettings } from '../../../shared_components'; import { MissingValuesOptions } from './missing_values_option'; import { LineCurveOption } from './line_curve_option'; @@ -67,9 +66,7 @@ export const VisualOptionsPopover: React.FC = ({ ['area_stacked', 'area', 'area_percentage_stacked'].includes(seriesType) ); - const isHistogramSeries = Boolean( - hasHistogramSeries(dataLayers as ValidLayer[], datasourceLayers) - ); + const isHistogramSeries = Boolean(hasHistogramSeries(dataLayers, datasourceLayers)); const isValueLabelsEnabled = !hasNonBarSeries && hasBarNotStacked && !isHistogramSeries; const isFittingEnabled = hasNonBarSeries; diff --git a/x-pack/plugins/lens/readme.md b/x-pack/plugins/lens/readme.md index c85005c09754e..84cea6feead06 100644 --- a/x-pack/plugins/lens/readme.md +++ b/x-pack/plugins/lens/readme.md @@ -14,8 +14,9 @@ Run all tests from the `x-pack` root directory - Unit tests: `yarn test:jest x-pack/plugins/lens` - Functional tests: - Run `node scripts/functional_tests_server` - - Run `node ../scripts/functional_test_runner.js --config ./test/functional/config.js --grep="lens app"` - - You may want to comment out all imports except for Lens in the config file. + - Run `node ../scripts/functional_test_runner.js --config ./test/functional/apps/lens/group1/config.ts` + - Run `node ../scripts/functional_test_runner.js --config ./test/functional/apps/lens/group2/config.ts` + - Run `node ../scripts/functional_test_runner.js --config ./test/functional/apps/lens/group3/config.ts` - API Functional tests: - Run `node scripts/functional_tests_server` - Run `node ../scripts/functional_test_runner.js --config ./test/api_integration/config.ts --grep=Lens` diff --git a/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts b/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts index 0f996e428ed2c..dc3933d852979 100644 --- a/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts +++ b/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts @@ -14,6 +14,8 @@ import { import { DOC_TYPE } from '../../common'; import { commonEnhanceTableRowHeight, + commonPreserveOldLegendSizeDefault, + commonFixValueLabelsInXY, commonLockOldMetricVisSettings, commonMakeReversePaletteAsCustom, commonRemoveTimezoneDateHistogramParam, @@ -35,6 +37,7 @@ import { VisState716, VisState810, VisStatePre715, + VisStatePre830, } from '../migrations/types'; import { extract, inject } from '../../common/embeddable_factory'; @@ -95,10 +98,15 @@ export const makeLensEmbeddableFactory = } as unknown as SerializableRecord; }, '8.2.0': (state) => { - const lensState = state as unknown as { attributes: LensDocShape810 }; + const lensState = state as unknown as { + attributes: LensDocShape810; + }; let migratedLensState = commonSetLastValueShowArrayValues(lensState.attributes); - migratedLensState = commonEnhanceTableRowHeight(migratedLensState); + migratedLensState = commonEnhanceTableRowHeight( + migratedLensState as LensDocShape810 + ); migratedLensState = commonSetIncludeEmptyRowsDateHistogram(migratedLensState); + return { ...lensState, attributes: migratedLensState, @@ -106,7 +114,11 @@ export const makeLensEmbeddableFactory = }, '8.3.0': (state) => { const lensState = state as unknown as { attributes: LensDocShape810 }; - const migratedLensState = commonLockOldMetricVisSettings(lensState.attributes); + let migratedLensState = commonLockOldMetricVisSettings(lensState.attributes); + migratedLensState = commonPreserveOldLegendSizeDefault(migratedLensState); + migratedLensState = commonFixValueLabelsInXY( + migratedLensState as LensDocShape810 + ); return { ...lensState, attributes: migratedLensState, diff --git a/x-pack/plugins/lens/server/expressions/expressions.ts b/x-pack/plugins/lens/server/expressions/expressions.ts index b124a9cbaf33b..d5bb5587b982e 100644 --- a/x-pack/plugins/lens/server/expressions/expressions.ts +++ b/x-pack/plugins/lens/server/expressions/expressions.ts @@ -13,7 +13,6 @@ import { renameColumns, getTimeScale, getDatatable, - lensMultitable, } from '../../common/expressions'; import { getFormatFactory, getTimeZoneFactory } from './utils'; @@ -23,8 +22,6 @@ export const setupExpressions = ( core: CoreSetup, expressions: ExpressionsServerSetup ) => { - [lensMultitable].forEach((expressionType) => expressions.registerType(expressionType)); - [ counterRate, formatColumn, diff --git a/x-pack/plugins/lens/server/migrations/common_migrations.ts b/x-pack/plugins/lens/server/migrations/common_migrations.ts index df0ce94abca84..10fc9b25e6f34 100644 --- a/x-pack/plugins/lens/server/migrations/common_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/common_migrations.ts @@ -25,8 +25,11 @@ import { VisState716, VisState810, VisState820, + VisState830, CustomVisualizationMigrations, LensDocShape810, + LensDocShape830, + VisStatePre830, } from './types'; import { DOCUMENT_FIELD_NAME, layerTypes, MetricState } from '../../common'; import { LensDocShape } from './saved_object_migrations'; @@ -194,9 +197,7 @@ export const commonRenameFilterReferences = (attributes: LensDocShape715): LensD return newAttributes as LensDocShape810; }; -export const commonSetLastValueShowArrayValues = ( - attributes: LensDocShape810 -): LensDocShape810 => { +export const commonSetLastValueShowArrayValues = (attributes: LensDocShape810): LensDocShape810 => { const newAttributes = cloneDeep(attributes); for (const layer of Object.values(newAttributes.state.datasourceStates.indexpattern.layers)) { for (const column of Object.values(layer.columns)) { @@ -215,19 +216,19 @@ export const commonEnhanceTableRowHeight = ( attributes: LensDocShape810 ): LensDocShape810 => { if (attributes.visualizationType !== 'lnsDatatable') { - return attributes; + return attributes as LensDocShape810; } const visState810 = attributes.state.visualization as VisState810; const newAttributes = cloneDeep(attributes); const vizState = newAttributes.state.visualization as VisState820; vizState.rowHeight = visState810.fitRowToContent ? 'auto' : 'single'; vizState.rowHeightLines = visState810.fitRowToContent ? 2 : 1; - return newAttributes; + return newAttributes as LensDocShape810; }; export const commonSetIncludeEmptyRowsDateHistogram = ( attributes: LensDocShape810 -): LensDocShape810 => { +): LensDocShape810 => { const newAttributes = cloneDeep(attributes); for (const layer of Object.values(newAttributes.state.datasourceStates.indexpattern.layers)) { for (const column of Object.values(layer.columns)) { @@ -241,17 +242,49 @@ export const commonSetIncludeEmptyRowsDateHistogram = ( export const commonLockOldMetricVisSettings = ( attributes: LensDocShape810 -): LensDocShape810 => { +): LensDocShape830 => { const newAttributes = cloneDeep(attributes); if (newAttributes.visualizationType !== 'lnsMetric') { - return newAttributes; + return newAttributes as LensDocShape830; } const visState = newAttributes.state.visualization as MetricState; visState.textAlign = visState.textAlign ?? 'center'; visState.titlePosition = visState.titlePosition ?? 'bottom'; visState.size = visState.size ?? 'xl'; - return newAttributes; + return newAttributes as LensDocShape830; +}; + +export const commonPreserveOldLegendSizeDefault = ( + attributes: LensDocShape810 +): LensDocShape830 => { + const newAttributes = cloneDeep(attributes); + + const pixelsToLegendSize: Record = { + undefined: 'auto', + '80': 'small', + '130': 'medium', + '180': 'large', + '230': 'xlarge', + }; + + if (['lnsXY', 'lnsHeatmap'].includes(newAttributes.visualizationType + '')) { + const legendConfig = (newAttributes.state.visualization as { legend: { legendSize: number } }) + .legend; + (legendConfig.legendSize as unknown as string) = + pixelsToLegendSize[String(legendConfig.legendSize)]; + } + + if (newAttributes.visualizationType === 'lnsPie') { + const layers = (newAttributes.state.visualization as { layers: Array<{ legendSize: number }> }) + .layers; + + layers.forEach((layer) => { + (layer.legendSize as unknown as string) = pixelsToLegendSize[String(layer.legendSize)]; + }); + } + + return newAttributes as LensDocShape830; }; const getApplyCustomVisualizationMigrationToLens = (id: string, migration: MigrateFunction) => { @@ -343,3 +376,25 @@ export const fixLensTopValuesCustomFormatting = (attributes: LensDocShape810): L ); return newAttributes as LensDocShape810; }; + +export const commonFixValueLabelsInXY = ( + attributes: LensDocShape830 +): LensDocShape830 => { + if (attributes.visualizationType !== 'lnsXY') { + return attributes as LensDocShape830; + } + + const newAttributes: LensDocShape830 = cloneDeep(attributes); + const { visualization } = newAttributes.state; + const { valueLabels } = visualization; + return { + ...newAttributes, + state: { + ...newAttributes.state, + visualization: { + ...visualization, + valueLabels: valueLabels && valueLabels !== 'hide' ? 'show' : valueLabels, + }, + }, + }; +}; diff --git a/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts b/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts index 7fb9c350a0fa5..d43d4c4cb2a38 100644 --- a/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts +++ b/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts @@ -21,6 +21,7 @@ import { VisStatePre715, VisState810, VisState820, + VisState830, } from './types'; import { layerTypes, MetricState } from '../../common'; import { Filter } from '@kbn/es-query'; @@ -2064,6 +2065,7 @@ describe('Lens migrations', () => { expect(layer2Columns['4'].params).toHaveProperty('includeEmptyRows', true); }); }); + describe('8.3.0 old metric visualization defaults', () => { const context = { log: { warning: () => {} } } as unknown as SavedObjectMigrationContext; const example = { @@ -2113,4 +2115,121 @@ describe('Lens migrations', () => { expect(visState.size).toBe('s'); }); }); + + describe('8.3.0 - convert legend sizes to strings', () => { + const context = { log: { warning: () => {} } } as unknown as SavedObjectMigrationContext; + const migrate = migrations['8.3.0']; + + const autoLegendSize = 'auto'; + const largeLegendSize = 'large'; + const largeLegendSizePx = 180; + + it('works for XY visualization and heatmap', () => { + const getDoc = (type: string, legendSize: number | undefined) => + ({ + attributes: { + visualizationType: type, + state: { + visualization: { + legend: { + legendSize, + }, + }, + }, + }, + } as unknown as SavedObjectUnsanitizedDoc); + + expect( + migrate(getDoc('lnsXY', undefined), context).attributes.state.visualization.legend + .legendSize + ).toBe(autoLegendSize); + expect( + migrate(getDoc('lnsXY', largeLegendSizePx), context).attributes.state.visualization.legend + .legendSize + ).toBe(largeLegendSize); + + expect( + migrate(getDoc('lnsHeatmap', undefined), context).attributes.state.visualization.legend + .legendSize + ).toBe(autoLegendSize); + expect( + migrate(getDoc('lnsHeatmap', largeLegendSizePx), context).attributes.state.visualization + .legend.legendSize + ).toBe(largeLegendSize); + }); + + it('works for pie visualization', () => { + const pieVisDoc = { + attributes: { + visualizationType: 'lnsPie', + state: { + visualization: { + layers: [ + { + legendSize: undefined, + }, + { + legendSize: largeLegendSizePx, + }, + ], + }, + }, + }, + } as unknown as SavedObjectUnsanitizedDoc; + + expect(migrate(pieVisDoc, context).attributes.state.visualization.layers).toEqual([ + { legendSize: autoLegendSize }, + { legendSize: largeLegendSize }, + ]); + }); + }); + + describe('8.3.0 valueLabels in XY', () => { + const context = { log: { warning: () => {} } } as unknown as SavedObjectMigrationContext; + const example = { + type: 'lens', + id: 'mocked-saved-object-id', + attributes: { + savedObjectId: '1', + title: 'MyRenamedOps', + description: '', + visualizationType: 'lnsXY', + state: { + visualization: { + valueLabels: 'inside', + legend: {}, + }, + }, + }, + } as unknown as SavedObjectUnsanitizedDoc; + + it('migrates valueLabels from `inside` to `show`', () => { + const result = migrations['8.3.0'](example, context) as ReturnType< + SavedObjectMigrationFn + >; + const visState = result.attributes.state.visualization as VisState830; + expect(visState.valueLabels).toBe('show'); + }); + + it("doesn't migrate valueLabels with `hide` value", () => { + const result = migrations['8.3.0']( + { + ...example, + attributes: { + ...example.attributes, + state: { + ...example.attributes.state, + visualization: { + ...(example.attributes.state.visualization as Record), + valueLabels: 'hide', + }, + }, + }, + }, + context + ) as ReturnType>; + const visState = result.attributes.state.visualization as VisState830; + expect(visState.valueLabels).toBe('hide'); + }); + }); }); diff --git a/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts index f5fa4b27447ac..3870bab9fad65 100644 --- a/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts @@ -13,9 +13,7 @@ import { SavedObjectReference, SavedObjectUnsanitizedDoc, } from '@kbn/core/server'; -import { Filter } from '@kbn/es-query'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Query } from '@kbn/data-plugin/public'; +import type { Query, Filter } from '@kbn/es-query'; import { mergeSavedObjectMigrationMaps } from '@kbn/core/server'; import { MigrateFunctionsObject } from '@kbn/kibana-utils-plugin/common'; import { PersistableFilter } from '../../common'; @@ -30,6 +28,11 @@ import { VisState716, CustomVisualizationMigrations, LensDocShape810, + LensDocShape830, + XYVisualizationStatePre830, + XYVisualizationState830, + VisState810, + VisState820, } from './types'; import { commonRenameOperationsForFormula, @@ -44,7 +47,9 @@ import { commonSetLastValueShowArrayValues, commonEnhanceTableRowHeight, commonSetIncludeEmptyRowsDateHistogram, + commonFixValueLabelsInXY, commonLockOldMetricVisSettings, + commonPreserveOldLegendSizeDefault, } from './common_migrations'; interface LensDocShapePre710 { @@ -475,7 +480,10 @@ const setLastValueShowArrayValues: SavedObjectMigrationFn = (doc) => { +const enhanceTableRowHeight: SavedObjectMigrationFn< + LensDocShape810, + LensDocShape810 +> = (doc) => { const newDoc = cloneDeep(doc); return { ...newDoc, attributes: commonEnhanceTableRowHeight(newDoc.attributes) }; }; @@ -486,10 +494,22 @@ const setIncludeEmptyRowsDateHistogram: SavedObjectMigrationFn, + LensDocShape830 +> = (doc) => { + const newDoc = cloneDeep(doc); + return { ...newDoc, attributes: commonFixValueLabelsInXY(newDoc.attributes) }; +}; + const lockOldMetricVisSettings: SavedObjectMigrationFn = ( doc ) => ({ ...doc, attributes: commonLockOldMetricVisSettings(doc.attributes) }); +const preserveOldLegendSizeDefault: SavedObjectMigrationFn = ( + doc +) => ({ ...doc, attributes: commonPreserveOldLegendSizeDefault(doc.attributes) }); + const lensMigrations: SavedObjectMigrationMap = { '7.7.0': removeInvalidAccessors, // The order of these migrations matter, since the timefield migration relies on the aggConfigs @@ -509,7 +529,7 @@ const lensMigrations: SavedObjectMigrationMap = { setIncludeEmptyRowsDateHistogram, enhanceTableRowHeight ), - '8.3.0': lockOldMetricVisSettings, + '8.3.0': flow(lockOldMetricVisSettings, preserveOldLegendSizeDefault, fixValueLabelsInXY), }; export const getAllMigrations = ( diff --git a/x-pack/plugins/lens/server/migrations/types.ts b/x-pack/plugins/lens/server/migrations/types.ts index 9b66f7023cdeb..6b38bb4b4f631 100644 --- a/x-pack/plugins/lens/server/migrations/types.ts +++ b/x-pack/plugins/lens/server/migrations/types.ts @@ -6,11 +6,9 @@ */ import type { PaletteOutput, CustomPaletteParams } from '@kbn/coloring'; -import { Filter } from '@kbn/es-query'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Query } from '@kbn/data-plugin/public'; +import type { Query, Filter } from '@kbn/es-query'; import type { MigrateFunctionsObject } from '@kbn/kibana-utils-plugin/common'; -import type { LayerType, PersistableFilter } from '../../common'; +import type { LayerType, PersistableFilter, ValueLabelConfig } from '../../common'; export type CustomVisualizationMigrations = Record MigrateFunctionsObject>; @@ -204,7 +202,7 @@ export type LensDocShape810 = Omit< 'filters' | 'state' > & { filters: Filter[]; - state: Omit & { + state: Omit['state'], 'datasourceStates'> & { datasourceStates: { indexpattern: Omit & { layers: Record< @@ -258,3 +256,16 @@ export interface VisState820 { rowHeight: 'auto' | 'single' | 'custom'; rowHeightLines: number; } + +export type LensDocShape830 = LensDocShape810; + +export interface XYVisualizationStatePre830 extends VisState820 { + valueLabels: 'hide' | 'inside' | 'outside'; +} + +export interface XYVisualizationState830 extends VisState820 { + valueLabels: ValueLabelConfig; +} + +export type VisStatePre830 = XYVisualizationStatePre830; +export type VisState830 = XYVisualizationState830; diff --git a/x-pack/plugins/lens/tsconfig.json b/x-pack/plugins/lens/tsconfig.json index 380d387249e17..20def97df7aed 100644 --- a/x-pack/plugins/lens/tsconfig.json +++ b/x-pack/plugins/lens/tsconfig.json @@ -12,6 +12,7 @@ { "path": "../../../src/core/tsconfig.json" }, { "path": "../task_manager/tsconfig.json" }, { "path": "../global_search/tsconfig.json" }, + { "path": "../ui_actions_enhanced/tsconfig.json" }, { "path": "../saved_objects_tagging/tsconfig.json" }, { "path": "../../../src/plugins/data/tsconfig.json" }, { "path": "../../../src/plugins/data_views/tsconfig.json" }, @@ -28,13 +29,14 @@ { "path": "../../../src/plugins/saved_objects/tsconfig.json" }, { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, - { "path": "../../../src/plugins/embeddable/tsconfig.json" }, - { "path": "../../../src/plugins/presentation_util/tsconfig.json" }, - { "path": "../../../src/plugins/field_formats/tsconfig.json" }, - { "path": "../../../src/plugins/chart_expressions/expression_xy/tsconfig.json" }, - { "path": "../../../src/plugins/chart_expressions/expression_heatmap/tsconfig.json" }, - { "path": "../../../src/plugins/chart_expressions/expression_gauge/tsconfig.json" }, - { "path": "../../../src/plugins/event_annotation/tsconfig.json" }, + { "path": "../../../src/plugins/embeddable/tsconfig.json"}, + { "path": "../../../src/plugins/presentation_util/tsconfig.json"}, + { "path": "../../../src/plugins/field_formats/tsconfig.json"}, + { "path": "../../../src/plugins/chart_expressions/expression_heatmap/tsconfig.json"}, + { "path": "../../../src/plugins/chart_expressions/expression_gauge/tsconfig.json"}, + { "path": "../../../src/plugins/data_view_editor/tsconfig.json"}, + { "path": "../../../src/plugins/event_annotation/tsconfig.json"}, + { "path": "../../../src/plugins/chart_expressions/expression_xy/tsconfig.json"}, { "path": "../../../src/plugins/unified_search/tsconfig.json" } ] } diff --git a/x-pack/plugins/licensing/common/register_analytics_context_provider.test.ts b/x-pack/plugins/licensing/common/register_analytics_context_provider.test.ts new file mode 100644 index 0000000000000..7edccfd319c91 --- /dev/null +++ b/x-pack/plugins/licensing/common/register_analytics_context_provider.test.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 { firstValueFrom, ReplaySubject, Subject } from 'rxjs'; +import type { ILicense } from './types'; +import { registerAnalyticsContextProvider } from './register_analytics_context_provider'; + +describe('registerAnalyticsContextProvider', () => { + const analyticsClientMock = { + registerContextProvider: jest.fn(), + }; + + let license$: Subject; + + beforeEach(() => { + jest.clearAllMocks(); + license$ = new ReplaySubject(1); + registerAnalyticsContextProvider(analyticsClientMock, license$); + }); + + test('should register the analytics context provider', () => { + expect(analyticsClientMock.registerContextProvider).toHaveBeenCalledTimes(1); + }); + + test('emits a context value the moment license emits', async () => { + license$.next({ + uid: 'uid', + status: 'active', + isActive: true, + type: 'basic', + signature: 'signature', + isAvailable: true, + toJSON: jest.fn(), + getUnavailableReason: jest.fn(), + hasAtLeast: jest.fn(), + check: jest.fn(), + getFeature: jest.fn(), + }); + await expect( + firstValueFrom(analyticsClientMock.registerContextProvider.mock.calls[0][0].context$) + ).resolves.toEqual({ + license_id: 'uid', + license_status: 'active', + license_type: 'basic', + }); + }); +}); diff --git a/x-pack/plugins/licensing/common/register_analytics_context_provider.ts b/x-pack/plugins/licensing/common/register_analytics_context_provider.ts new file mode 100644 index 0000000000000..60f3fbbb3e603 --- /dev/null +++ b/x-pack/plugins/licensing/common/register_analytics_context_provider.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 type { Observable } from 'rxjs'; +import { map } from 'rxjs'; +import type { AnalyticsClient } from '@kbn/analytics-client'; +import type { ILicense } from './types'; + +export function registerAnalyticsContextProvider( + // Using `AnalyticsClient` from the package to be able to implement this method in the `common` dir. + analytics: Pick, + license$: Observable +) { + analytics.registerContextProvider({ + name: 'license info', + context$: license$.pipe( + map((license) => ({ + license_id: license.uid, + license_status: license.status, + license_type: license.type, + })) + ), + schema: { + license_id: { + type: 'keyword', + _meta: { description: 'The license ID', optional: true }, + }, + license_status: { + type: 'keyword', + _meta: { description: 'The license Status (active/invalid/expired)', optional: true }, + }, + license_type: { + type: 'keyword', + _meta: { + description: 'The license Type (basic/standard/gold/platinum/enterprise/trial)', + optional: true, + }, + }, + }, + }); +} diff --git a/x-pack/plugins/licensing/public/plugin.ts b/x-pack/plugins/licensing/public/plugin.ts index 9ef27e22657af..3953a29a08214 100644 --- a/x-pack/plugins/licensing/public/plugin.ts +++ b/x-pack/plugins/licensing/public/plugin.ts @@ -15,6 +15,7 @@ import { License } from '../common/license'; import { mountExpiredBanner } from './expired_banner'; import { FeatureUsageService } from './services'; import type { PublicLicenseJSON } from '../common/types'; +import { registerAnalyticsContextProvider } from '../common/register_analytics_context_provider'; export const licensingSessionStorageKey = 'xpack.licensing'; @@ -82,6 +83,8 @@ export class LicensingPlugin implements Plugin { if (license.isAvailable) { this.prevSignature = license.signature; diff --git a/x-pack/plugins/licensing/server/plugin.ts b/x-pack/plugins/licensing/server/plugin.ts index 98dd1e7cbbb93..aaeeb4e058008 100644 --- a/x-pack/plugins/licensing/server/plugin.ts +++ b/x-pack/plugins/licensing/server/plugin.ts @@ -21,6 +21,7 @@ import { IClusterClient, } from '@kbn/core/server'; +import { registerAnalyticsContextProvider } from '../common/register_analytics_context_provider'; import { ILicense, PublicLicense, @@ -120,6 +121,8 @@ export class LicensingPlugin implements Plugin; diff --git a/x-pack/plugins/maps/common/descriptor_types/layer_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/layer_descriptor_types.ts index 1cd8b254f9e14..996e3d7303b82 100644 --- a/x-pack/plugins/maps/common/descriptor_types/layer_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/layer_descriptor_types.ts @@ -35,11 +35,17 @@ export type TileMetaFeature = Feature & { 'hits.total.value': number; // For _mvt requests with "aggs" property in request: aggregation statistics returned in the pattern outined below + // aggregations._count.avg + // aggregations._count.count // aggregations._count.min // aggregations._count.max + // aggregations._count.sum + // aggregations..avg + // aggregations..count // aggregations..min // aggregations..max - [key: string]: number | string; + // aggregations..sum + [key: string]: number | string | boolean; }; }; diff --git a/x-pack/plugins/maps/common/embeddable/extract.ts b/x-pack/plugins/maps/common/embeddable/extract.ts index e73b1566c0289..d329aefe7cff6 100644 --- a/x-pack/plugins/maps/common/embeddable/extract.ts +++ b/x-pack/plugins/maps/common/embeddable/extract.ts @@ -5,8 +5,7 @@ * 2.0. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/server'; +import { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/common'; import { MapEmbeddablePersistableState } from './types'; import { MapSavedObjectAttributes } from '../map_saved_object_type'; import { extractReferences } from '../migrations/references'; diff --git a/x-pack/plugins/maps/common/embeddable/inject.ts b/x-pack/plugins/maps/common/embeddable/inject.ts index 2e1892b95a0f1..4bb26dd00d28d 100644 --- a/x-pack/plugins/maps/common/embeddable/inject.ts +++ b/x-pack/plugins/maps/common/embeddable/inject.ts @@ -5,10 +5,9 @@ * 2.0. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/server'; -import { MapEmbeddablePersistableState } from './types'; -import { MapSavedObjectAttributes } from '../map_saved_object_type'; +import type { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/common'; +import type { MapEmbeddablePersistableState } from './types'; +import type { MapSavedObjectAttributes } from '../map_saved_object_type'; import { extractReferences, injectReferences } from '../migrations/references'; export const inject: EmbeddableRegistryDefinition['inject'] = (state, references) => { diff --git a/x-pack/plugins/maps/kibana.json b/x-pack/plugins/maps/kibana.json index 4c4ca64f7ac07..edbf4df979f7b 100644 --- a/x-pack/plugins/maps/kibana.json +++ b/x-pack/plugins/maps/kibana.json @@ -25,7 +25,8 @@ "mapsEms", "savedObjects", "share", - "presentationUtil" + "presentationUtil", + "screenshotMode" ], "optionalPlugins": [ "cloud", diff --git a/x-pack/plugins/maps/public/actions/data_request_actions.ts b/x-pack/plugins/maps/public/actions/data_request_actions.ts index 5788c9360fdf2..d2cb416dcbe20 100644 --- a/x-pack/plugins/maps/public/actions/data_request_actions.ts +++ b/x-pack/plugins/maps/public/actions/data_request_actions.ts @@ -24,6 +24,7 @@ import { getDataRequestDescriptor, getLayerById, getLayerList, + getEditState, } from '../selectors/map_selectors'; import { cancelRequest, @@ -67,6 +68,7 @@ export type DataRequestContext = { dataFilters: DataFilters; forceRefreshDueToDrawing: boolean; // Boolean signaling data request triggered by a user updating layer features via drawing tools. When true, layer will re-load regardless of "source.applyForceRefresh" flag. isForceRefresh: boolean; // Boolean signaling data request triggered by auto-refresh timer or user clicking refresh button. When true, layer will re-load only when "source.applyForceRefresh" flag is set to true. + isFeatureEditorOpenForLayer: boolean; // Boolean signaling that feature editor menu is open for a layer. When true, layer will ignore all global and layer filtering so drawn features are displayed and not filtered out. }; export function clearDataRequests(layer: ILayer) { @@ -145,6 +147,7 @@ function getDataRequestContext( dispatch(registerCancelCallback(requestToken, callback)), forceRefreshDueToDrawing, isForceRefresh, + isFeatureEditorOpenForLayer: getEditState(getState())?.layerId === layerId, }; } diff --git a/x-pack/plugins/maps/public/actions/map_actions.ts b/x-pack/plugins/maps/public/actions/map_actions.ts index 8a16d036393ad..e8585560238fd 100644 --- a/x-pack/plugins/maps/public/actions/map_actions.ts +++ b/x-pack/plugins/maps/public/actions/map_actions.ts @@ -58,6 +58,7 @@ import { autoFitToBounds, syncDataForAllLayers, syncDataForLayerDueToDrawing, + syncDataForLayerId, } from './data_request_actions'; import { addLayer, addLayerWithoutDataSync } from './layer_actions'; import { MapSettings } from '../reducers/map'; @@ -394,7 +395,7 @@ export function setEditLayerToSelectedLayer() { } export function updateEditLayer(layerId: string | null) { - return (dispatch: Dispatch) => { + return (dispatch: ThunkDispatch) => { if (layerId !== null) { dispatch({ type: SET_OPEN_TOOLTIPS, openTooltips: [] }); } @@ -406,6 +407,7 @@ export function updateEditLayer(layerId: string | null) { type: UPDATE_EDIT_STATE, editState: layerId ? { layerId } : undefined, }); + dispatch(syncDataForLayerId(layerId, false)); }; } diff --git a/x-pack/plugins/maps/public/classes/fields/agg/agg_field.ts b/x-pack/plugins/maps/public/classes/fields/agg/agg_field.ts index 13b40962c5461..9202e09bab67d 100644 --- a/x-pack/plugins/maps/public/classes/fields/agg/agg_field.ts +++ b/x-pack/plugins/maps/public/classes/fields/agg/agg_field.ts @@ -13,6 +13,7 @@ import { isMetricCountable } from '../../util/is_metric_countable'; import { CountAggFieldParams } from './agg_field_types'; import { addFieldToDSL, getField } from '../../../../common/elasticsearch_util'; import { IField } from '../field'; +import { getAggRange } from '../../util/tile_meta_feature_utils'; const TERMS_AGG_SHARD_SIZE = 5; @@ -111,15 +112,6 @@ export class AggField extends CountAggField { } pluckRangeFromTileMetaFeature(metaFeature: TileMetaFeature) { - const minField = `aggregations.${this.getName()}.min`; - const maxField = `aggregations.${this.getName()}.max`; - return metaFeature.properties && - typeof metaFeature.properties[minField] === 'number' && - typeof metaFeature.properties[maxField] === 'number' - ? { - min: metaFeature.properties[minField] as number, - max: metaFeature.properties[maxField] as number, - } - : null; + return getAggRange(metaFeature, this.getName()); } } diff --git a/x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts b/x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts index 03c0d3f0791a5..4b136ab950ffa 100644 --- a/x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts +++ b/x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts @@ -13,6 +13,7 @@ import { TileMetaFeature } from '../../../../common/descriptor_types'; import { ITooltipProperty, TooltipProperty } from '../../tooltips/tooltip_property'; import { ESAggTooltipProperty } from '../../tooltips/es_agg_tooltip_property'; import { IESAggField, CountAggFieldParams } from './agg_field_types'; +import { getAggRange } from '../../util/tile_meta_feature_utils'; // Agg without field. Essentially a count-aggregation. export class CountAggField implements IESAggField { @@ -116,15 +117,6 @@ export class CountAggField implements IESAggField { } pluckRangeFromTileMetaFeature(metaFeature: TileMetaFeature) { - const minField = `aggregations._count.min`; - const maxField = `aggregations._count.max`; - return metaFeature.properties && - typeof metaFeature.properties[minField] === 'number' && - typeof metaFeature.properties[maxField] === 'number' - ? { - min: metaFeature.properties[minField] as number, - max: metaFeature.properties[maxField] as number, - } - : null; + return getAggRange(metaFeature, '_count'); } } diff --git a/x-pack/plugins/maps/public/classes/layers/__fixtures__/mock_sync_context.ts b/x-pack/plugins/maps/public/classes/layers/__fixtures__/mock_sync_context.ts index b81ba6c854629..4d1f23599f48d 100644 --- a/x-pack/plugins/maps/public/classes/layers/__fixtures__/mock_sync_context.ts +++ b/x-pack/plugins/maps/public/classes/layers/__fixtures__/mock_sync_context.ts @@ -20,6 +20,7 @@ export class MockSyncContext implements DataRequestContext { updateSourceData: (newData: unknown) => void; forceRefreshDueToDrawing: boolean; isForceRefresh: boolean; + isFeatureEditorOpenForLayer: boolean; constructor({ dataFilters }: { dataFilters: Partial }) { const mapFilters: DataFilters = { @@ -44,5 +45,6 @@ export class MockSyncContext implements DataRequestContext { this.updateSourceData = sinon.spy(); this.forceRefreshDueToDrawing = false; this.isForceRefresh = false; + this.isFeatureEditorOpenForLayer = false; } } diff --git a/x-pack/plugins/maps/public/classes/layers/build_vector_request_meta.ts b/x-pack/plugins/maps/public/classes/layers/build_vector_request_meta.ts index 52dd93033020d..e8d74003c6790 100644 --- a/x-pack/plugins/maps/public/classes/layers/build_vector_request_meta.ts +++ b/x-pack/plugins/maps/public/classes/layers/build_vector_request_meta.ts @@ -16,7 +16,8 @@ export function buildVectorRequestMeta( fieldNames: string[], dataFilters: DataFilters, sourceQuery: Query | null | undefined, - isForceRefresh: boolean + isForceRefresh: boolean, + isFeatureEditorOpenForLayer: boolean ): VectorSourceRequestMeta { return { ...dataFilters, @@ -28,5 +29,6 @@ export function buildVectorRequestMeta( sourceMeta: source.getSyncMeta(), applyForceRefresh: source.isESSource() ? source.getApplyForceRefresh() : false, isForceRefresh, + isFeatureEditorOpenForLayer, }; } diff --git a/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.test.ts b/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.test.ts index 8b27bacff8ecb..21c9c1f79d970 100644 --- a/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.test.ts @@ -6,14 +6,18 @@ */ import { SOURCE_TYPES } from '../../../../common/constants'; -import { DataFilters, XYZTMSSourceDescriptor } from '../../../../common/descriptor_types'; +import { + DataFilters, + LayerDescriptor, + XYZTMSSourceDescriptor, +} from '../../../../common/descriptor_types'; import { ILayer } from '../layer'; import { EmsVectorTileLayer } from './ems_vector_tile_layer'; import { DataRequestContext } from '../../../actions'; import { EMSTMSSource } from '../../sources/ems_tms_source'; describe('EmsVectorTileLayer', () => { - it('should correctly inject tileLayerId in meta', async () => { + test('should correctly inject tileLayerId in meta', async () => { const layer: ILayer = new EmsVectorTileLayer({ source: { getTileLayerId: () => { @@ -50,4 +54,34 @@ describe('EmsVectorTileLayer', () => { expect(actualMeta).toStrictEqual({ tileLayerId: 'myTileLayerId' }); expect(actualErrorMessage).toStrictEqual('network error'); }); + + describe('isInitialDataLoadComplete', () => { + test('should return false when tile loading has not started', () => { + const layer = new EmsVectorTileLayer({ + source: {} as unknown as EMSTMSSource, + layerDescriptor: {} as unknown as LayerDescriptor, + }); + expect(layer.isInitialDataLoadComplete()).toBe(false); + }); + + test('should return false when tiles are loading', () => { + const layer = new EmsVectorTileLayer({ + source: {} as unknown as EMSTMSSource, + layerDescriptor: { + __areTilesLoaded: false, + } as unknown as LayerDescriptor, + }); + expect(layer.isInitialDataLoadComplete()).toBe(false); + }); + + test('should return true when tiles are loaded', () => { + const layer = new EmsVectorTileLayer({ + source: {} as unknown as EMSTMSSource, + layerDescriptor: { + __areTilesLoaded: true, + } as unknown as LayerDescriptor, + }); + expect(layer.isInitialDataLoadComplete()).toBe(true); + }); + }); }); diff --git a/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.tsx b/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.tsx index 428156165c4c3..646ccb3c09acd 100644 --- a/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/ems_vector_tile_layer/ems_vector_tile_layer.tsx @@ -67,6 +67,10 @@ export class EmsVectorTileLayer extends AbstractLayer { this._style = new TileStyle(); } + isInitialDataLoadComplete(): boolean { + return !!this._descriptor.__areTilesLoaded; + } + getSource(): EMSTMSSource { return super.getSource() as EMSTMSSource; } diff --git a/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts b/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts index 66aef6d38803f..d1247590af2e9 100644 --- a/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts @@ -11,10 +11,16 @@ import { HeatmapStyle } from '../../styles/heatmap/heatmap_style'; import { LAYER_TYPE } from '../../../../common/constants'; import { HeatmapLayerDescriptor } from '../../../../common/descriptor_types'; import { ESGeoGridSource } from '../../sources/es_geo_grid_source'; -import { syncBoundsData, MvtSourceData, syncMvtSourceData } from '../vector_layer'; +import { + NO_RESULTS_ICON_AND_TOOLTIPCONTENT, + syncBoundsData, + MvtSourceData, + syncMvtSourceData, +} from '../vector_layer'; import { DataRequestContext } from '../../../actions'; import { buildVectorRequestMeta } from '../build_vector_request_meta'; import { IMvtVectorSource } from '../../sources/vector_source'; +import { getAggsMeta } from '../../util/tile_meta_feature_utils'; export class HeatmapLayer extends AbstractLayer { private readonly _style: HeatmapStyle; @@ -48,6 +54,11 @@ export class HeatmapLayer extends AbstractLayer { } } + getLayerIcon(isTocIcon: boolean) { + const { docCount } = getAggsMeta(this._getMetaFromTiles()); + return docCount === 0 ? NO_RESULTS_ICON_AND_TOOLTIPCONTENT : super.getLayerIcon(isTocIcon); + } + getSource(): ESGeoGridSource { return super.getSource() as ESGeoGridSource; } @@ -89,7 +100,8 @@ export class HeatmapLayer extends AbstractLayer { this.getSource().getFieldNames(), syncContext.dataFilters, this.getQuery(), - syncContext.isForceRefresh + syncContext.isForceRefresh, + syncContext.isFeatureEditorOpenForLayer ), source: this.getSource() as IMvtVectorSource, syncContext, diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts index e46c670b677ba..b1fddcca5d5f2 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts @@ -298,7 +298,8 @@ export class BlendedVectorLayer extends GeoJsonVectorLayer implements IVectorLay syncContext.isForceRefresh, syncContext.dataFilters, this.getSource(), - this.getCurrentStyle() + this.getCurrentStyle(), + syncContext.isFeatureEditorOpenForLayer ); const source = this.getSource(); diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/bounds_data.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/bounds_data.ts index 29ecf1635d626..7136c9d0c2235 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/bounds_data.ts +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/bounds_data.ts @@ -22,7 +22,13 @@ export async function syncBoundsData({ source: IVectorSource; sourceQuery: Query | null; }): Promise { - const { startLoading, stopLoading, registerCancelCallback, dataFilters } = syncContext; + const { + startLoading, + stopLoading, + registerCancelCallback, + dataFilters, + isFeatureEditorOpenForLayer, + } = syncContext; const requestToken = Symbol(`${SOURCE_BOUNDS_DATA_REQUEST_ID}-${layerId}`); @@ -37,6 +43,7 @@ export async function syncBoundsData({ joinKeyFilter: dataFilters.joinKeyFilter, applyGlobalQuery: source.getApplyGlobalQuery(), applyGlobalTime: source.getApplyGlobalTime(), + isFeatureEditorOpenForLayer, }; let bounds = null; diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx index c6f9a282d0fa4..bc7ba78c84d98 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx @@ -237,7 +237,8 @@ export class GeoJsonVectorLayer extends AbstractVectorLayer { syncContext.isForceRefresh, syncContext.dataFilters, source, - style + style, + syncContext.isFeatureEditorOpenForLayer ), syncContext, source, diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_source_data.test.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_source_data.test.ts index 4b45adc8848bd..ccc73f94aac57 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_source_data.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_source_data.test.ts @@ -59,6 +59,7 @@ describe('syncMvtSourceData', () => { fieldNames: [], sourceMeta: {}, isForceRefresh: false, + isFeatureEditorOpenForLayer: false, }, source: mockSource, syncContext, @@ -90,6 +91,7 @@ describe('syncMvtSourceData', () => { fieldNames: [], sourceMeta: {}, isForceRefresh: false, + isFeatureEditorOpenForLayer: false, }; await syncMvtSourceData({ @@ -131,6 +133,7 @@ describe('syncMvtSourceData', () => { fieldNames: [], sourceMeta: {}, isForceRefresh: false, + isFeatureEditorOpenForLayer: false, }; await syncMvtSourceData({ @@ -169,6 +172,7 @@ describe('syncMvtSourceData', () => { fieldNames: [], sourceMeta: {}, isForceRefresh: false, + isFeatureEditorOpenForLayer: false, }; await syncMvtSourceData({ @@ -215,6 +219,7 @@ describe('syncMvtSourceData', () => { fieldNames: [], sourceMeta: {}, isForceRefresh: false, + isFeatureEditorOpenForLayer: false, }; await syncMvtSourceData({ @@ -253,6 +258,7 @@ describe('syncMvtSourceData', () => { fieldNames: [], sourceMeta: {}, isForceRefresh: false, + isFeatureEditorOpenForLayer: false, }; await syncMvtSourceData({ @@ -291,6 +297,7 @@ describe('syncMvtSourceData', () => { fieldNames: [], sourceMeta: {}, isForceRefresh: false, + isFeatureEditorOpenForLayer: false, }; await syncMvtSourceData({ diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx index 27d377851152e..d9ee5207b29f3 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx @@ -17,6 +17,8 @@ import { shallow } from 'enzyme'; import { Feature } from 'geojson'; import { MVTSingleLayerVectorSource } from '../../../sources/mvt_single_layer_vector_source'; +import { IVectorSource } from '../../../sources/vector_source'; +import { InnerJoin } from '../../../joins/inner_join'; import { TiledSingleLayerVectorSourceDescriptor, VectorLayerDescriptor, @@ -93,3 +95,107 @@ describe('getFeatureById', () => { expect(feature).toEqual(null); }); }); + +describe('isInitialDataLoadComplete', () => { + const sourceDataRequestDescriptor = { + data: {}, + dataId: 'source', + dataRequestMeta: {}, + dataRequestMetaAtStart: undefined, + dataRequestToken: undefined, + }; + test('should return false when tile loading has not started', () => { + const layer = new MvtVectorLayer({ + customIcons: [], + layerDescriptor: { + __dataRequests: [sourceDataRequestDescriptor], + } as unknown as VectorLayerDescriptor, + source: {} as unknown as IVectorSource, + }); + expect(layer.isInitialDataLoadComplete()).toBe(false); + }); + + test('should return false when tiles are loading', () => { + const layer = new MvtVectorLayer({ + customIcons: [], + layerDescriptor: { + __areTilesLoaded: false, + __dataRequests: [sourceDataRequestDescriptor], + } as unknown as VectorLayerDescriptor, + source: {} as unknown as IVectorSource, + }); + expect(layer.isInitialDataLoadComplete()).toBe(false); + }); + + test('should return true when tiles are loaded', () => { + const layer = new MvtVectorLayer({ + customIcons: [], + layerDescriptor: { + __areTilesLoaded: true, + __dataRequests: [sourceDataRequestDescriptor], + } as unknown as VectorLayerDescriptor, + source: {} as unknown as IVectorSource, + }); + expect(layer.isInitialDataLoadComplete()).toBe(true); + }); + + test('should return false when tiles are loaded but join is loading', () => { + const layer = new MvtVectorLayer({ + customIcons: [], + joins: [ + { + hasCompleteConfig: () => { + return true; + }, + getSourceDataRequestId: () => { + return 'join_source_a0b0da65-5e1a-4967-9dbe-74f24391afe2'; + }, + } as unknown as InnerJoin, + ], + layerDescriptor: { + __areTilesLoaded: true, + __dataRequests: [ + sourceDataRequestDescriptor, + { + dataId: 'join_source_a0b0da65-5e1a-4967-9dbe-74f24391afe2', + dataRequestMetaAtStart: {}, + dataRequestToken: Symbol('join request'), + }, + ], + } as unknown as VectorLayerDescriptor, + source: {} as unknown as IVectorSource, + }); + expect(layer.isInitialDataLoadComplete()).toBe(false); + }); + + test('should return true when tiles are loaded and joins are loaded', () => { + const layer = new MvtVectorLayer({ + customIcons: [], + joins: [ + { + hasCompleteConfig: () => { + return true; + }, + getSourceDataRequestId: () => { + return 'join_source_a0b0da65-5e1a-4967-9dbe-74f24391afe2'; + }, + } as unknown as InnerJoin, + ], + layerDescriptor: { + __areTilesLoaded: true, + __dataRequests: [ + sourceDataRequestDescriptor, + { + data: {}, + dataId: 'join_source_a0b0da65-5e1a-4967-9dbe-74f24391afe2', + dataRequestMeta: {}, + dataRequestMetaAtStart: undefined, + dataRequestToken: undefined, + }, + ], + } as unknown as VectorLayerDescriptor, + source: {} as unknown as IVectorSource, + }); + expect(layer.isInitialDataLoadComplete()).toBe(true); + }); +}); diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.tsx index a7a190c1c5b56..adb211f8f9420 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.tsx @@ -29,7 +29,6 @@ import { DataRequestContext } from '../../../../actions'; import { DataRequestMeta, StyleMetaDescriptor, - TileMetaFeature, VectorLayerDescriptor, } from '../../../../../common/descriptor_types'; import { ESSearchSource } from '../../../sources/es_search_source'; @@ -39,10 +38,14 @@ import { LayerIcon } from '../../layer'; import { MvtSourceData, syncMvtSourceData } from './mvt_source_data'; import { PropertiesMap } from '../../../../../common/elasticsearch_util'; import { pluckStyleMeta } from './pluck_style_meta'; +import { + ES_MVT_HITS_TOTAL_RELATION, + ES_MVT_HITS_TOTAL_VALUE, + ES_MVT_META_LAYER_NAME, + getAggsMeta, + getHitsMeta, +} from '../../../util/tile_meta_feature_utils'; -export const ES_MVT_META_LAYER_NAME = 'meta'; -const ES_MVT_HITS_TOTAL_RELATION = 'hits.total.relation'; -const ES_MVT_HITS_TOTAL_VALUE = 'hits.total.value'; const MAX_RESULT_WINDOW_DATA_REQUEST_ID = 'maxResultWindow'; export class MvtVectorLayer extends AbstractVectorLayer { @@ -68,6 +71,12 @@ export class MvtVectorLayer extends AbstractVectorLayer { this._source = args.source as IMvtVectorSource; } + isInitialDataLoadComplete(): boolean { + return this._descriptor.__areTilesLoaded === undefined || !this._descriptor.__areTilesLoaded + ? false + : super.isInitialDataLoadComplete(); + } + async getBounds(syncContext: DataRequestContext) { // Add filter to narrow bounds to features with matching join keys let joinKeyFilter; @@ -116,19 +125,15 @@ export class MvtVectorLayer extends AbstractVectorLayer { // // TODO ES MVT specific - move to es_tiled_vector_layer implementation // - - const tileMetaFeatures = this._getMetaFromTiles(); - if (!tileMetaFeatures.length) { - return NO_RESULTS_ICON_AND_TOOLTIPCONTENT; - } - - if (this.getSource().getType() !== SOURCE_TYPES.ES_SEARCH) { - // aggregation ES sources are never trimmed - return { - icon: this.getCurrentStyle().getIcon(false), - tooltipContent: null, - areResultsTrimmed: false, - }; + if (this.getSource().getType() === SOURCE_TYPES.ES_GEO_GRID) { + const { docCount } = getAggsMeta(this._getMetaFromTiles()); + return docCount === 0 + ? NO_RESULTS_ICON_AND_TOOLTIPCONTENT + : { + icon: this.getCurrentStyle().getIcon(false), + tooltipContent: null, + areResultsTrimmed: false, + }; } const maxResultWindow = this._getMaxResultWindow(); @@ -140,28 +145,16 @@ export class MvtVectorLayer extends AbstractVectorLayer { }; } - let totalFeaturesCount = 0; - let tilesWithFeatures = 0; - tileMetaFeatures.forEach((tileMeta: Feature) => { - const count = - tileMeta && tileMeta.properties ? tileMeta.properties[ES_MVT_HITS_TOTAL_VALUE] : 0; - if (count > 0) { - totalFeaturesCount += count; - tilesWithFeatures++; - } - }); + const { totalFeaturesCount, tilesWithFeatures, tilesWithTrimmedResults } = getHitsMeta( + this._getMetaFromTiles(), + maxResultWindow + ); if (totalFeaturesCount === 0) { return NO_RESULTS_ICON_AND_TOOLTIPCONTENT; } - const areResultsTrimmed: boolean = tileMetaFeatures.some((tileMeta: TileMetaFeature) => { - if (tileMeta?.properties?.[ES_MVT_HITS_TOTAL_RELATION] === 'gte') { - return tileMeta?.properties?.[ES_MVT_HITS_TOTAL_VALUE] >= maxResultWindow + 1; - } else { - return false; - } - }); + const areResultsTrimmed = tilesWithTrimmedResults > 0; // Documents may be counted multiple times if geometry crosses tile boundaries. const canMultiCountShapes = @@ -232,7 +225,8 @@ export class MvtVectorLayer extends AbstractVectorLayer { syncContext.isForceRefresh, syncContext.dataFilters, this.getSource(), - this.getCurrentStyle() + this.getCurrentStyle(), + syncContext.isFeatureEditorOpenForLayer ), source: this.getSource() as IMvtVectorSource, syncContext, diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx index 1b178d204f6e2..aee4312713b7d 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx @@ -366,7 +366,8 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { isForceRefresh: boolean, dataFilters: DataFilters, source: IVectorSource, - style: IVectorStyle + style: IVectorStyle, + isFeatureEditorOpenForLayer: boolean ): Promise { const fieldNames = [ ...source.getFieldNames(), @@ -378,7 +379,14 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { if (timesliceMaskFieldName) { fieldNames.push(timesliceMaskFieldName); } - return buildVectorRequestMeta(source, fieldNames, dataFilters, this.getQuery(), isForceRefresh); + return buildVectorRequestMeta( + source, + fieldNames, + dataFilters, + this.getQuery(), + isForceRefresh, + isFeatureEditorOpenForLayer + ); } async _syncSourceStyleMeta( @@ -542,6 +550,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { registerCancelCallback, dataFilters, isForceRefresh, + isFeatureEditorOpenForLayer, }: { join: InnerJoin } & DataRequestContext): Promise { const joinSource = join.getRightJoinSource(); const sourceDataId = join.getSourceDataRequestId(); @@ -552,7 +561,8 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { joinSource.getFieldNames(), dataFilters, joinSource.getWhereQuery(), - isForceRefresh + isForceRefresh, + isFeatureEditorOpenForLayer ) as VectorJoinSourceRequestMeta; const prevDataRequest = this.getDataRequest(sourceDataId); diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts index bcbc50c7e0090..7110473b11261 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts @@ -178,6 +178,7 @@ describe('ESGeoGridSource', () => { sourceMeta: null, zoom: 0, isForceRefresh: false, + isFeatureEditorOpenForLayer: false, }; describe('getGeoJsonWithMeta', () => { diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.test.ts index 54071848f9a40..2df2e119df30c 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.test.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.test.ts @@ -106,6 +106,7 @@ describe('ESSearchSource', () => { applyGlobalTime: true, applyForceRefresh: true, isForceRefresh: false, + isFeatureEditorOpenForLayer: false, }; it('Should only include required props', async () => { diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts index c0eef8ba1ca22..944bf0ee3e0b1 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts @@ -230,7 +230,17 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource allFilters.push(extentFilter); } - if (searchFilters.applyGlobalTime && (await this.isTimeAware())) { + + let isFeatureEditorOpenForLayer = false; + if ('isFeatureEditorOpenForLayer' in searchFilters) { + isFeatureEditorOpenForLayer = searchFilters.isFeatureEditorOpenForLayer; + } + + if ( + searchFilters.applyGlobalTime && + (await this.isTimeAware()) && + !isFeatureEditorOpenForLayer + ) { const timeRange = searchFilters.timeslice ? { from: new Date(searchFilters.timeslice.from).toISOString(), @@ -250,11 +260,11 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource searchSource.setField('index', indexPattern); searchSource.setField('size', limit); searchSource.setField('filter', allFilters); - if (searchFilters.applyGlobalQuery) { + if (searchFilters.applyGlobalQuery && !isFeatureEditorOpenForLayer) { searchSource.setField('query', searchFilters.query); } - if (searchFilters.sourceQuery) { + if (searchFilters.sourceQuery && !isFeatureEditorOpenForLayer) { const layerSearchSource = searchService.searchSource.createEmpty(); layerSearchSource.setField('index', indexPattern); diff --git a/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.tsx b/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.tsx index 79830068628e5..43a2a00ca59e1 100644 --- a/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.tsx @@ -42,6 +42,7 @@ export interface BoundsRequestMeta { sourceQuery?: Query; timeFilters: TimeRange; timeslice?: Timeslice; + isFeatureEditorOpenForLayer: boolean; joinKeyFilter?: Filter; } diff --git a/x-pack/plugins/maps/public/classes/util/can_skip_fetch.ts b/x-pack/plugins/maps/public/classes/util/can_skip_fetch.ts index 69a5c73ba2933..71d4730880b96 100644 --- a/x-pack/plugins/maps/public/classes/util/can_skip_fetch.ts +++ b/x-pack/plugins/maps/public/classes/util/can_skip_fetch.ts @@ -86,6 +86,10 @@ export async function canSkipSourceUpdate({ return false; } + if (prevMeta.isFeatureEditorOpenForLayer !== nextRequestMeta.isFeatureEditorOpenForLayer) { + return false; + } + let updateDueToApplyGlobalTime = false; let updateDueToTime = false; let updateDueToTimeslice = false; diff --git a/x-pack/plugins/maps/public/classes/util/tile_meta_feature_utils.test.ts b/x-pack/plugins/maps/public/classes/util/tile_meta_feature_utils.test.ts new file mode 100644 index 0000000000000..c812dfa6b94f2 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/util/tile_meta_feature_utils.test.ts @@ -0,0 +1,378 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TileMetaFeature } from '../../../common/descriptor_types'; +import { getAggsMeta, getAggRange, getHitsMeta } from './tile_meta_feature_utils'; + +describe('getAggsMeta', () => { + test('should extract doc_count = 0 from meta features when there are no matches', () => { + const metaFeatures = [ + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-101.25, 31.952162238024968], + [-95.625, 31.952162238024968], + [-95.625, 36.597889133070225], + [-101.25, 36.597889133070225], + [-101.25, 31.952162238024968], + ], + ], + }, + properties: { + '_shards.failed': 0, + '_shards.skipped': 0, + '_shards.successful': 1, + '_shards.total': 1, + 'aggregations._count.count': 0, + 'aggregations._count.sum': 0, + 'hits.total.relation': 'eq', + 'hits.total.value': 0, + timed_out: false, + took: 1, + }, + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-106.875, 31.952162238024968], + [-101.25, 31.952162238024968], + [-101.25, 36.597889133070225], + [-106.875, 36.597889133070225], + [-106.875, 31.952162238024968], + ], + ], + }, + properties: { + '_shards.failed': 0, + '_shards.skipped': 0, + '_shards.successful': 1, + '_shards.total': 1, + 'aggregations._count.count': 0, + 'aggregations._count.sum': 0, + 'hits.total.relation': 'eq', + 'hits.total.value': 0, + timed_out: false, + took: 1, + }, + }, + ] as TileMetaFeature[]; + const { docCount } = getAggsMeta(metaFeatures); + expect(docCount).toBe(0); + }); + + test('should extract doc_count from meta features', () => { + const metaFeatures = [ + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-106.875, 36.597889133070225], + [-101.25, 36.597889133070225], + [-101.25, 40.979898069620134], + [-106.875, 40.979898069620134], + [-106.875, 36.597889133070225], + ], + ], + }, + properties: { + '_shards.failed': 0, + '_shards.skipped': 0, + '_shards.successful': 1, + '_shards.total': 1, + 'aggregations._count.avg': 92.3133514986376, + 'aggregations._count.count': 9175, + 'aggregations._count.max': 8297, + 'aggregations._count.min': 1, + 'aggregations._count.sum': 846975, + 'hits.total.relation': 'gte', + 'hits.total.value': 10000, + timed_out: false, + took: 968, + }, + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-112.5, 36.597889133070225], + [-106.875, 36.597889133070225], + [-106.875, 40.979898069620134], + [-112.5, 40.979898069620134], + [-112.5, 36.597889133070225], + ], + ], + }, + properties: { + '_shards.failed': 0, + '_shards.skipped': 0, + '_shards.successful': 1, + '_shards.total': 1, + 'aggregations._count.avg': 41.19310344827586, + 'aggregations._count.count': 4495, + 'aggregations._count.max': 2398, + 'aggregations._count.min': 1, + 'aggregations._count.sum': 185163, + 'hits.total.relation': 'gte', + 'hits.total.value': 10000, + timed_out: false, + took: 522, + }, + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-106.875, 40.979898069620134], + [-101.25, 40.979898069620134], + [-101.25, 45.08903556483102], + [-106.875, 45.08903556483102], + [-106.875, 40.979898069620134], + ], + ], + }, + properties: { + '_shards.failed': 0, + '_shards.skipped': 0, + '_shards.successful': 1, + '_shards.total': 1, + 'aggregations._count.avg': 9.938271604938272, + 'aggregations._count.count': 81, + 'aggregations._count.max': 96, + 'aggregations._count.min': 1, + 'aggregations._count.sum': 805, + 'hits.total.relation': 'eq', + 'hits.total.value': 792, + timed_out: false, + took: 12, + }, + }, + ] as TileMetaFeature[]; + const { docCount } = getAggsMeta(metaFeatures); + expect(docCount).toBe(1032943); + }); +}); + +test('getAggRange', () => { + const metaFeature = { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-106.875, 40.979898069620134], + [-101.25, 40.979898069620134], + [-101.25, 45.08903556483102], + [-106.875, 45.08903556483102], + [-106.875, 40.979898069620134], + ], + ], + }, + properties: { + '_shards.failed': 0, + '_shards.skipped': 0, + '_shards.successful': 1, + '_shards.total': 1, + 'aggregations._count.avg': 9.938271604938272, + 'aggregations._count.count': 81, + 'aggregations._count.max': 96, + 'aggregations._count.min': 1, + 'aggregations._count.sum': 805, + 'hits.total.relation': 'eq', + 'hits.total.value': 792, + timed_out: false, + took: 12, + }, + } as TileMetaFeature; + expect(getAggRange(metaFeature, '_count')).toEqual({ + max: 96, + min: 1, + }); +}); + +describe('getHitsMeta', () => { + test('should extract 0 total hits from meta features when there are no matches', () => { + const metaFeatures = [ + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-101.25, 31.952162238024968], + [-95.625, 31.952162238024968], + [-95.625, 36.597889133070225], + [-101.25, 36.597889133070225], + [-101.25, 31.952162238024968], + ], + ], + }, + properties: { + '_shards.failed': 0, + '_shards.skipped': 0, + '_shards.successful': 1, + '_shards.total': 1, + 'hits.total.relation': 'eq', + 'hits.total.value': 0, + timed_out: false, + took: 7, + }, + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-106.875, 31.952162238024968], + [-101.25, 31.952162238024968], + [-101.25, 36.597889133070225], + [-106.875, 36.597889133070225], + [-106.875, 31.952162238024968], + ], + ], + }, + properties: { + '_shards.failed': 0, + '_shards.skipped': 0, + '_shards.successful': 1, + '_shards.total': 1, + 'hits.total.relation': 'eq', + 'hits.total.value': 0, + timed_out: false, + took: 7, + }, + }, + ] as TileMetaFeature[]; + expect(getHitsMeta(metaFeatures, 10000)).toEqual({ + tilesWithFeatures: 0, + tilesWithTrimmedResults: 0, + totalFeaturesCount: 0, + }); + }); + + test('should extract hits meta from meta features', () => { + const metaFeatures = [ + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-106.875, 36.99268153210721], + [-102.03826904296875, 36.99268153210721], + [-102.03826904296875, 40.979898069620134], + [-106.875, 40.979898069620134], + [-106.875, 36.99268153210721], + ], + ], + }, + properties: { + '_shards.failed': 0, + '_shards.skipped': 0, + '_shards.successful': 1, + '_shards.total': 1, + 'hits.total.relation': 'gte', + 'hits.total.value': 10001, + timed_out: false, + took: 1571, + }, + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-109.05853271484375, 36.99706886366255], + [-106.875, 36.99706886366255], + [-106.875, 40.95086262813277], + [-109.05853271484375, 40.95086262813277], + [-109.05853271484375, 36.99706886366255], + ], + ], + }, + properties: { + '_shards.failed': 0, + '_shards.skipped': 0, + '_shards.successful': 1, + '_shards.total': 1, + 'hits.total.relation': 'gte', + 'hits.total.value': 10001, + timed_out: false, + took: 1039, + }, + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-101.25, 40.979898069620134], + [-95.625, 40.979898069620134], + [-95.625, 45.08903556483102], + [-101.25, 45.08903556483102], + [-101.25, 40.979898069620134], + ], + ], + }, + properties: { + '_shards.failed': 0, + '_shards.skipped': 0, + '_shards.successful': 1, + '_shards.total': 1, + 'hits.total.relation': 'eq', + 'hits.total.value': 0, + timed_out: false, + took: 7, + }, + }, + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-107.7923583984375, 40.991301354803056], + [-107.05078125, 40.991301354803056], + [-107.05078125, 41.0037390532903], + [-107.7923583984375, 41.0037390532903], + [-107.7923583984375, 40.991301354803056], + ], + ], + }, + properties: { + '_shards.failed': 0, + '_shards.skipped': 0, + '_shards.successful': 1, + '_shards.total': 1, + 'hits.total.relation': 'eq', + 'hits.total.value': 28, + timed_out: false, + took: 10, + }, + }, + ] as TileMetaFeature[]; + expect(getHitsMeta(metaFeatures, 10000)).toEqual({ + tilesWithFeatures: 3, + tilesWithTrimmedResults: 2, + totalFeaturesCount: 20030, + }); + }); +}); diff --git a/x-pack/plugins/maps/public/classes/util/tile_meta_feature_utils.ts b/x-pack/plugins/maps/public/classes/util/tile_meta_feature_utils.ts new file mode 100644 index 0000000000000..ac12399a2e011 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/util/tile_meta_feature_utils.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TileMetaFeature } from '../../../common/descriptor_types'; + +// Elasticsearch vector tile API returns "meta" layer containing a single metadata feature for each tile request +// This file contains utility methods for pulling values out of metadata features +// Placing logic in a single file to provide a centrialize location and avoid scattering logic throughout plugin + +export const ES_MVT_META_LAYER_NAME = 'meta'; +export const ES_MVT_HITS_TOTAL_RELATION = 'hits.total.relation'; +export const ES_MVT_HITS_TOTAL_VALUE = 'hits.total.value'; + +export function getAggsMeta(metaFeatures: TileMetaFeature[]): { docCount: number } { + let docCount = 0; + metaFeatures.forEach((metaFeature) => { + const count = metaFeature.properties + ? (metaFeature.properties['aggregations._count.sum'] as number) + : 0; + if (count > 0) { + docCount += count; + } + }); + + return { docCount }; +} + +export function getHitsMeta( + metaFeatures: TileMetaFeature[], + maxResultWindow: number +): { totalFeaturesCount: number; tilesWithFeatures: number; tilesWithTrimmedResults: number } { + let totalFeaturesCount = 0; + let tilesWithFeatures = 0; + let tilesWithTrimmedResults = 0; + metaFeatures.forEach((metaFeature) => { + const count = metaFeature.properties ? metaFeature.properties[ES_MVT_HITS_TOTAL_VALUE] : 0; + if (count > 0) { + totalFeaturesCount += count; + tilesWithFeatures++; + } + + if ( + metaFeature?.properties?.[ES_MVT_HITS_TOTAL_RELATION] === 'gte' && + metaFeature?.properties?.[ES_MVT_HITS_TOTAL_VALUE] >= maxResultWindow + 1 + ) { + tilesWithTrimmedResults++; + } + }); + return { totalFeaturesCount, tilesWithFeatures, tilesWithTrimmedResults }; +} + +export function getAggRange( + metaFeature: TileMetaFeature, + subAggName: string +): { min: number; max: number } | null { + const minField = `aggregations.${subAggName}.min`; + const maxField = `aggregations.${subAggName}.max`; + return metaFeature.properties && + typeof metaFeature.properties[minField] === 'number' && + typeof metaFeature.properties[maxField] === 'number' + ? { + min: metaFeature.properties[minField] as number, + max: metaFeature.properties[maxField] as number, + } + : null; +} diff --git a/x-pack/plugins/maps/public/components/global_filter_checkbox.tsx b/x-pack/plugins/maps/public/components/global_filter_checkbox.tsx index 96805e0c6b5ec..502cc1973d329 100644 --- a/x-pack/plugins/maps/public/components/global_filter_checkbox.tsx +++ b/x-pack/plugins/maps/public/components/global_filter_checkbox.tsx @@ -13,27 +13,37 @@ interface Props { applyGlobalQuery: boolean; label: string; setApplyGlobalQuery: (applyGlobalQuery: boolean) => void; + isFeatureEditorOpenForLayer?: boolean; } -export function GlobalFilterCheckbox({ applyGlobalQuery, label, setApplyGlobalQuery }: Props) { +export function GlobalFilterCheckbox({ + applyGlobalQuery, + label, + setApplyGlobalQuery, + isFeatureEditorOpenForLayer, +}: Props) { const onApplyGlobalQueryChange = (event: EuiSwitchEvent) => { setApplyGlobalQuery(event.target.checked); }; + const tooltipMessage = isFeatureEditorOpenForLayer + ? i18n.translate('xpack.maps.filterEditor.isGlobalSearchNotApplied', { + defaultMessage: 'Global search is not applied to the layer while editing features', + }) + : i18n.translate('xpack.maps.filterEditor.applyGlobalFilterHelp', { + defaultMessage: 'When enabled, results narrowed by global search', + }); + return ( - + diff --git a/x-pack/plugins/maps/public/components/global_time_checkbox.tsx b/x-pack/plugins/maps/public/components/global_time_checkbox.tsx index 70d6859b8b02a..4db771c1edbaf 100644 --- a/x-pack/plugins/maps/public/components/global_time_checkbox.tsx +++ b/x-pack/plugins/maps/public/components/global_time_checkbox.tsx @@ -12,27 +12,37 @@ interface Props { applyGlobalTime: boolean; label: string; setApplyGlobalTime: (applyGlobalTime: boolean) => void; + isFeatureEditorOpenForLayer?: boolean; } -export function GlobalTimeCheckbox({ applyGlobalTime, label, setApplyGlobalTime }: Props) { +export function GlobalTimeCheckbox({ + applyGlobalTime, + label, + setApplyGlobalTime, + isFeatureEditorOpenForLayer, +}: Props) { const onApplyGlobalTimeChange = (event: EuiSwitchEvent) => { setApplyGlobalTime(event.target.checked); }; + const tooltipMessage = isFeatureEditorOpenForLayer + ? i18n.translate('xpack.maps.filterEditor.isGlobalTimeNotApplied', { + defaultMessage: 'Global time is not applied to the layer while editing features', + }) + : i18n.translate('xpack.maps.filterEditor.applyGlobalTimeHelp', { + defaultMessage: 'When enabled, results narrowed by global time', + }); + return ( - + diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/filter_editor/filter_editor.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/filter_editor/filter_editor.tsx index afb18aa582a3c..9e3edeb1fc255 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/filter_editor/filter_editor.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/filter_editor/filter_editor.tsx @@ -35,6 +35,7 @@ export interface Props { layer: ILayer; setLayerQuery: (id: string, query: Query) => void; updateSourceProp: (layerId: string, propName: string, value: unknown) => void; + isFeatureEditorOpenForLayer: boolean; } interface State { @@ -157,6 +158,15 @@ export class FilterEditor extends Component { } _renderQuery() { + if (this.props.isFeatureEditorOpenForLayer) { + return ( + + ); + } + const query = this.props.layer.getQuery(); if (!query || !query.query) { return ( @@ -199,6 +209,7 @@ export class FilterEditor extends Component { onClick={this._toggle} data-test-subj="mapLayerPanelOpenFilterEditorButton" iconType={openButtonIcon} + disabled={this.props.isFeatureEditorOpenForLayer} > {openButtonLabel} @@ -213,6 +224,7 @@ export class FilterEditor extends Component { })} applyGlobalTime={this.props.layer.getSource().getApplyGlobalTime()} setApplyGlobalTime={this._onApplyGlobalTimeChange} + isFeatureEditorOpenForLayer={this.props.isFeatureEditorOpenForLayer} /> ) : null; @@ -244,6 +256,7 @@ export class FilterEditor extends Component { })} applyGlobalQuery={this.props.layer.getSource().getApplyGlobalQuery()} setApplyGlobalQuery={this._onApplyGlobalQueryChange} + isFeatureEditorOpenForLayer={this.props.isFeatureEditorOpenForLayer} /> {globalTimeCheckbox} diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/filter_editor/index.ts b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/filter_editor/index.ts index 7c3db15f10236..51b19e7b05c96 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/filter_editor/index.ts +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/filter_editor/index.ts @@ -10,13 +10,15 @@ import { ThunkDispatch } from 'redux-thunk'; import { connect } from 'react-redux'; import type { Query } from '@kbn/data-plugin/public'; import { FilterEditor } from './filter_editor'; -import { getSelectedLayer } from '../../../selectors/map_selectors'; +import { getEditState, getSelectedLayer } from '../../../selectors/map_selectors'; import { setLayerQuery, updateSourceProp } from '../../../actions'; import { MapStoreState } from '../../../reducers/store'; function mapStateToProps(state: MapStoreState) { + const layer = getSelectedLayer(state)!; return { - layer: getSelectedLayer(state)!, + layer, + isFeatureEditorOpenForLayer: getEditState(state)?.layerId === layer.getId(), }; } diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/join_editor.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/join_editor.tsx index cfa46ecf32b32..2865e2d8099d1 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/join_editor.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/join_editor.tsx @@ -79,6 +79,7 @@ export function JoinEditor({ joins, layer, onChange, leftJoinFields, layerDispla function renderContent() { const disabledReason = layer.getJoinsDisabledReason(); + return disabledReason ? ( {disabledReason} ) : ( diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker.test.ts b/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker.test.ts index ffc6459262c8b..6485582149db7 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker.test.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker.test.ts @@ -113,7 +113,7 @@ describe('TileStatusTracker', () => { expect(loadedMap.get('foo')).toBe(true); expect(loadedMap.get('bar')).toBe(false); // still outstanding tile requests - expect(loadedMap.has('foobar')).toBe(true); // never received tile requests + expect(loadedMap.has('foobar')).toBe(false); // never received tile requests, status should not have been reported for layer (aa11BarTile as { tile: { aborted: boolean } })!.tile.aborted = true; // abort tile mockMbMap.emit('sourcedataloading', createMockMbDataEvent('barsource', 'af1d')); @@ -125,7 +125,7 @@ describe('TileStatusTracker', () => { expect(loadedMap.get('foo')).toBe(false); // still outstanding tile requests expect(loadedMap.get('bar')).toBe(true); // tiles were aborted or errored - expect(loadedMap.has('foobar')).toBe(true); // never received tile requests + expect(loadedMap.has('foobar')).toBe(false); // never received tile requests, status should not have been reported for layer }); test('should cleanup listeners on destroy', async () => { diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker.ts b/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker.ts index 94a4344bac009..c349ef0ede3b6 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker.ts @@ -29,8 +29,21 @@ interface Tile { } export class TileStatusTracker { - private _tileCache: Tile[]; - private _tileErrorCache: Record; + // Tile cache tracks active tile requests + // 'sourcedataloading' event adds tile request to cache + // 'sourcedata' and 'error' events remove tile request from cache + // Tile requests with 'aborted' status are removed from cache when reporting 'areTilesLoaded' status + private _tileCache: Tile[] = []; + + // Tile error cache tracks tile request errors per layer + // Error cache is cleared when map center tile changes + private _tileErrorCache: Record = {}; + + // Layer cache tracks layers that have requested one or more tiles + // Layer cache is used so that only a layer that has requested one or more tiles reports 'areTilesLoaded' status + // layer cache is never cleared + private _layerCache: Map = new Map(); + private _prevCenterTileKey?: string; private readonly _mbMap: MapboxMap; private readonly _updateTileStatus: ( @@ -48,6 +61,14 @@ export class TileStatusTracker { e.tile && (e.source.type === 'vector' || e.source.type === 'raster') ) { + const targetLayer = this._getCurrentLayerList().find((layer) => { + return layer.ownsMbSourceId(e.sourceId); + }); + const layerId = targetLayer ? targetLayer.getId() : undefined; + if (layerId && !this._layerCache.has(layerId)) { + this._layerCache.set(layerId, true); + } + const tracked = this._tileCache.find((tile) => { return ( tile.mbKey === (e.tile.tileID.key as unknown as string) && tile.mbSourceId === e.sourceId @@ -127,8 +148,6 @@ export class TileStatusTracker { updateTileStatus: (layer: ILayer, areTilesLoaded: boolean, errorMessage?: string) => void; getCurrentLayerList: () => ILayer[]; }) { - this._tileCache = []; - this._tileErrorCache = {}; this._updateTileStatus = updateTileStatus; this._getCurrentLayerList = getCurrentLayerList; @@ -146,6 +165,12 @@ export class TileStatusTracker { const layerList = this._getCurrentLayerList(); for (let i = 0; i < layerList.length; i++) { const layer: ILayer = layerList[i]; + + if (!this._layerCache.has(layer.getId())) { + // do not report status for layers that have not started loading tiles. + continue; + } + let atLeastOnePendingTile = false; for (let j = 0; j < this._tileCache.length; j++) { const tile = this._tileCache[j]; diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/utils.ts b/x-pack/plugins/maps/public/connected_components/mb_map/utils.ts index 5fc9fd62b20c2..552a618e11f7e 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/utils.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/utils.ts @@ -9,7 +9,7 @@ import type { Map as MbMap } from '@kbn/mapbox-gl'; import { TileMetaFeature } from '../../../common/descriptor_types'; import { isGlDrawLayer } from './sort_layers'; import { ILayer } from '../../classes/layers/layer'; -import { ES_MVT_META_LAYER_NAME } from '../../classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer'; +import { ES_MVT_META_LAYER_NAME } from '../../classes/util/tile_meta_feature_utils'; export function removeOrphanedSourcesAndLayers( mbMap: MbMap, diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/__snapshots__/toc_entry.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/__snapshots__/toc_entry.test.tsx.snap index 5beaf12029d2f..cec85cb0e1cd6 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/__snapshots__/toc_entry.test.tsx.snap +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/__snapshots__/toc_entry.test.tsx.snap @@ -11,7 +11,6 @@ exports[`TOCEntry is rendered 1`] = ` > {}, showTOCDetails: () => {}, - editModeActiveForLayer: false, + isFeatureEditorOpenForLayer: false, cancelEditing: () => {}, }; diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry.tsx index aeba28cf033bd..65431432d8c6d 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry.tsx +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry.tsx @@ -31,7 +31,7 @@ export interface ReduxStateProps { hasDirtyStateSelector: boolean; isLegendDetailsOpen: boolean; isEditButtonDisabled: boolean; - editModeActiveForLayer: boolean; + isFeatureEditorOpenForLayer: boolean; } export interface ReduxDispatchProps { @@ -282,7 +282,6 @@ export class TOCEntry extends Component { openLayerSettings={this._openLayerPanelWithCheck} isEditButtonDisabled={this.props.isEditButtonDisabled} supportsFitToBounds={this.state.supportsFitToBounds} - editModeActiveForLayer={this.props.editModeActiveForLayer} /> {this._renderQuickActions()} @@ -317,7 +316,7 @@ export class TOCEntry extends Component { 'mapTocEntry-isSelected': this.props.layer.isPreviewLayer() || (this.props.selectedLayer && this.props.selectedLayer.getId() === this.props.layer.getId()), - 'mapTocEntry-isInEditingMode': this.props.editModeActiveForLayer, + 'mapTocEntry-isInEditingMode': this.props.isFeatureEditorOpenForLayer, }); return ( @@ -334,7 +333,7 @@ export class TOCEntry extends Component { {this._renderCancelModal()} - {this.props.editModeActiveForLayer && ( + {this.props.isFeatureEditorOpenForLayer && (
diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx index ae62b75400769..cb2697663766a 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx @@ -48,7 +48,6 @@ const defaultProps = { enableShapeEditing: () => {}, enablePointEditing: () => {}, openLayerSettings: () => {}, - editModeActiveForLayer: false, numLayers: 2, showThisLayerOnly: () => {}, }; @@ -105,9 +104,7 @@ describe('TOCEntryActionsPopover', () => { }); test('should disable Edit features when edit mode active for layer', async () => { - const component = shallow( - - ); + const component = shallow(); // Ensure all promises resolve await new Promise((resolve) => process.nextTick(resolve)); diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx index 3f3b7558409f8..5e33931a8943e 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx @@ -36,7 +36,6 @@ export interface Props { showThisLayerOnly: (layerId: string) => void; supportsFitToBounds: boolean; toggleVisible: (layerId: string) => void; - editModeActiveForLayer: boolean; numLayers: number; } @@ -90,9 +89,7 @@ export class TOCEntryActionsPopover extends Component { } if ( - (source as ESSearchSource).getApplyGlobalQuery() || (source as ESSearchSource).getSyncMeta().scalingType === SCALING_TYPES.CLUSTERS || - (await vectorLayer.isFilteredByGlobalTime()) || vectorLayer.isPreviewLayer() || !vectorLayer.isVisible() || vectorLayer.hasJoins() @@ -194,9 +191,9 @@ export class TOCEntryActionsPopover extends Component { ? null : i18n.translate('xpack.maps.layerTocActions.editFeaturesTooltip.disabledMessage', { defaultMessage: - 'Edit features only supported for document layers without clustering, term joins, time filtering, or global search.', + 'Edit features is only supported for layers without clustering and term joins', }), - disabled: !this.state.isFeatureEditingEnabled || this.props.editModeActiveForLayer, + disabled: !this.state.isFeatureEditingEnabled, onClick: async () => { this._closePopover(); const supportedShapeTypes = await ( diff --git a/x-pack/plugins/maps/public/lens/choropleth_chart/choropleth_chart.tsx b/x-pack/plugins/maps/public/lens/choropleth_chart/choropleth_chart.tsx index 5089a8cc6c8d5..467c2ce92ff6e 100644 --- a/x-pack/plugins/maps/public/lens/choropleth_chart/choropleth_chart.tsx +++ b/x-pack/plugins/maps/public/lens/choropleth_chart/choropleth_chart.tsx @@ -45,12 +45,10 @@ export function ChoroplethChart({ return null; } - const table = data.tables[args.layerId]; - let emsLayerId = args.emsLayerId ? args.emsLayerId : emsWorldLayerId; let emsField = args.emsField ? args.emsField : 'iso2'; if (!args.emsLayerId || !args.emsField) { - const emsSuggestion = getEmsSuggestion(emsFileLayers, table, args.regionAccessor); + const emsSuggestion = getEmsSuggestion(emsFileLayers, data, args.regionAccessor); if (emsSuggestion) { emsLayerId = emsSuggestion.layerId; emsField = emsSuggestion.field; @@ -66,7 +64,7 @@ export function ChoroplethChart({ defaultMessage: '{emsLayerLabel} by {accessorLabel}', values: { emsLayerLabel, - accessorLabel: getAccessorLabel(table, args.valueAccessor), + accessorLabel: getAccessorLabel(data, args.valueAccessor), }, }) : '', @@ -76,16 +74,16 @@ export function ChoroplethChart({ right: { id: args.valueAccessor, type: SOURCE_TYPES.TABLE_SOURCE, - __rows: table.rows, + __rows: data.rows, __columns: [ { name: args.regionAccessor, - label: getAccessorLabel(table, args.regionAccessor), + label: getAccessorLabel(data, args.regionAccessor), type: 'string', }, { name: args.valueAccessor, - label: getAccessorLabel(table, args.valueAccessor), + label: getAccessorLabel(data, args.valueAccessor), type: 'number', }, ], diff --git a/x-pack/plugins/maps/public/lens/choropleth_chart/expression_function.ts b/x-pack/plugins/maps/public/lens/choropleth_chart/expression_function.ts index 7ed1ddfbd4381..989cc06c5d53b 100644 --- a/x-pack/plugins/maps/public/lens/choropleth_chart/expression_function.ts +++ b/x-pack/plugins/maps/public/lens/choropleth_chart/expression_function.ts @@ -6,8 +6,8 @@ */ import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; +import { Datatable } from '@kbn/expressions-plugin/common'; import { i18n } from '@kbn/i18n'; -import type { LensMultiTable } from '@kbn/lens-plugin/common'; import { prepareLogTable } from '@kbn/visualizations-plugin/common/utils'; import type { ChoroplethChartConfig, ChoroplethChartProps } from './types'; import { RENDERER_ID } from './expression_renderer'; @@ -20,7 +20,7 @@ interface ChoroplethChartRender { export const getExpressionFunction = (): ExpressionFunctionDefinition< 'lens_choropleth_chart', - LensMultiTable, + Datatable, Omit, ChoroplethChartRender > => ({ @@ -57,11 +57,14 @@ export const getExpressionFunction = (): ExpressionFunctionDefinition< help: 'Value accessor identifies the value column', }, }, - inputTypes: ['lens_multitable'], + inputTypes: ['datatable'], fn(data, args, handlers) { if (handlers?.inspectorAdapters?.tables) { + handlers.inspectorAdapters.tables.reset(); + handlers.inspectorAdapters.tables.allowCsvExport = true; + const logTable = prepareLogTable( - Object.values(data.tables)[0], + data, [ [ args.valueAccessor ? [args.valueAccessor] : undefined, @@ -88,6 +91,6 @@ export const getExpressionFunction = (): ExpressionFunctionDefinition< data, args, }, - } as ChoroplethChartRender; + }; }, }); diff --git a/x-pack/plugins/maps/public/lens/choropleth_chart/types.ts b/x-pack/plugins/maps/public/lens/choropleth_chart/types.ts index 79c05a93ef2d4..7dc9a16056e77 100644 --- a/x-pack/plugins/maps/public/lens/choropleth_chart/types.ts +++ b/x-pack/plugins/maps/public/lens/choropleth_chart/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { LensMultiTable } from '@kbn/lens-plugin/common'; +import { Datatable } from '@kbn/expressions-plugin/common'; export interface ChoroplethChartState { layerId: string; @@ -21,6 +21,6 @@ export interface ChoroplethChartConfig extends ChoroplethChartState { } export interface ChoroplethChartProps { - data: LensMultiTable; + data: Datatable; args: ChoroplethChartConfig; } diff --git a/x-pack/plugins/maps/public/lens/choropleth_chart/visualization.tsx b/x-pack/plugins/maps/public/lens/choropleth_chart/visualization.tsx index cbac26f220163..54f459c3f7b38 100644 --- a/x-pack/plugins/maps/public/lens/choropleth_chart/visualization.tsx +++ b/x-pack/plugins/maps/public/lens/choropleth_chart/visualization.tsx @@ -138,14 +138,16 @@ export const getVisualization = ({ } }, - toExpression: (state, datasourceLayers, attributes) => { + toExpression: (state, datasourceLayers, attributes, datasourceExpressionsByLayers = {}) => { if (!state.regionAccessor || !state.valueAccessor) { return null; } + const datasourceExpression = datasourceExpressionsByLayers[state.layerId]; return { type: 'expression', chain: [ + ...(datasourceExpression ? datasourceExpression.chain : []), { type: 'function', function: 'lens_choropleth_chart', diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 5e9662c543641..b5b232aeeaae6 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -41,6 +41,7 @@ import type { SecurityPluginStart } from '@kbn/security-plugin/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import type { CloudSetup } from '@kbn/cloud-plugin/public'; import type { LensPublicSetup } from '@kbn/lens-plugin/public'; +import { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/public'; import { createRegionMapFn, regionMapRenderer, @@ -88,6 +89,7 @@ export interface MapsPluginSetupDependencies { share: SharePluginSetup; licensing: LicensingPluginSetup; usageCollection?: UsageCollectionSetup; + screenshotMode: ScreenshotModePluginSetup; } export interface MapsPluginStartDependencies { @@ -144,7 +146,15 @@ export class MapsPlugin registerLicensedFeatures(plugins.licensing); const config = this._initializerContext.config.get(); - setMapAppConfig(config); + setMapAppConfig({ + ...config, + + // Override this when we know we are taking a screenshot (i.e. no user interaction) + // to avoid a blank-canvas issue when rendering maps on a PDF + preserveDrawingBuffer: plugins.screenshotMode.isScreenshotMode() + ? true + : config.preserveDrawingBuffer, + }); const locator = plugins.share.url.locators.create( new MapsAppLocatorDefinition({ diff --git a/x-pack/plugins/maps/tsconfig.json b/x-pack/plugins/maps/tsconfig.json index 5d5f4223fab9a..57cc09dec4b16 100644 --- a/x-pack/plugins/maps/tsconfig.json +++ b/x-pack/plugins/maps/tsconfig.json @@ -33,6 +33,7 @@ { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, { "path": "../../../src/plugins/shared_ux/tsconfig.json" }, + { "path": "../../../src/plugins/screenshot_mode/tsconfig.json" }, { "path": "../cloud/tsconfig.json" }, { "path": "../features/tsconfig.json" }, { "path": "../lens/tsconfig.json" }, diff --git a/x-pack/plugins/ml/common/constants/locator.ts b/x-pack/plugins/ml/common/constants/locator.ts index ae45bec27deb6..0a1c2638e684a 100644 --- a/x-pack/plugins/ml/common/constants/locator.ts +++ b/x-pack/plugins/ml/common/constants/locator.ts @@ -38,6 +38,7 @@ export const ML_PAGES = { */ DATA_VISUALIZER_INDEX_VIEWER: 'jobs/new_job/datavisualizer', ANOMALY_DETECTION_CREATE_JOB: `jobs/new_job`, + ANOMALY_DETECTION_CREATE_JOB_RECOGNIZER: `jobs/new_job/recognize`, ANOMALY_DETECTION_CREATE_JOB_ADVANCED: `jobs/new_job/advanced`, ANOMALY_DETECTION_CREATE_JOB_SELECT_TYPE: `jobs/new_job/step/job_type`, ANOMALY_DETECTION_CREATE_JOB_SELECT_INDEX: `jobs/new_job/step/index_or_search`, diff --git a/x-pack/plugins/ml/common/constants/trained_models.ts b/x-pack/plugins/ml/common/constants/trained_models.ts index 79fe65936b231..75ea25dcc9efa 100644 --- a/x-pack/plugins/ml/common/constants/trained_models.ts +++ b/x-pack/plugins/ml/common/constants/trained_models.ts @@ -22,10 +22,10 @@ export type TrainedModelType = typeof TRAINED_MODEL_TYPE[keyof typeof TRAINED_MO export const SUPPORTED_PYTORCH_TASKS = { NER: 'ner', - // ZERO_SHOT_CLASSIFICATION: 'zero_shot_classification', - // CLASSIFICATION_LABELS: 'classification_labels', - // TEXT_CLASSIFICATION: 'text_classification', - // TEXT_EMBEDDING: 'text_embedding', + ZERO_SHOT_CLASSIFICATION: 'zero_shot_classification', + TEXT_CLASSIFICATION: 'text_classification', + TEXT_EMBEDDING: 'text_embedding', + FILL_MASK: 'fill_mask', } as const; export type SupportedPytorchTasksType = typeof SUPPORTED_PYTORCH_TASKS[keyof typeof SUPPORTED_PYTORCH_TASKS]; diff --git a/x-pack/plugins/ml/common/openapi/README.md b/x-pack/plugins/ml/common/openapi/README.md new file mode 100644 index 0000000000000..28246e8b43d3c --- /dev/null +++ b/x-pack/plugins/ml/common/openapi/README.md @@ -0,0 +1,20 @@ +# OpenAPI (Experimental) + +The current self-contained spec file can be used for online tools like those found at https://openapi.tools/. This spec is experimental and may be incomplete or change later. + +A guide about the openApi specification can be found at [https://swagger.io/docs/specification/about/](https://swagger.io/docs/specification/about/). + + ## Tools + +It is possible to validate the docs before bundling them by running the following command in the `x-pack/plugins/ml/common/openapi/` folder: + ``` + npx swagger-cli validate ml_apis.yaml + ``` + +Then generate the `bundled` files with the following commands: + + ``` + npx @redocly/openapi-cli bundle --ext yaml --output bundled.yaml ml_apis.yaml + npx @redocly/openapi-cli bundle --ext json --output bundled.json ml_apis.yaml + ``` + diff --git a/x-pack/plugins/ml/common/openapi/bundled.json b/x-pack/plugins/ml/common/openapi/bundled.json new file mode 100644 index 0000000000000..455bf02bbd232 --- /dev/null +++ b/x-pack/plugins/ml/common/openapi/bundled.json @@ -0,0 +1,225 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Machine learning APIs", + "description": "Kibana APIs for the machine learning feature", + "version": "1.0.0", + "license": { + "name": "Elastic License 2.0", + "url": "https://www.elastic.co/licensing/elastic-license" + } + }, + "tags": [ + { + "name": "ml", + "description": "Machine learning" + } + ], + "servers": [ + { + "url": "https://localhost:5601/" + } + ], + "paths": { + "/s/{spaceId}/api/ml/saved_objects/sync": { + "get": { + "description": "Synchronizes Kibana saved objects for machine learning jobs and trained models. You must have `all` privileges for the **Machine Learning** feature in the **Analytics** section of the Kibana feature privileges. This API runs automatically when you start Kibana and periodically thereafter.\n", + "parameters": [ + { + "$ref": "#/components/parameters/spaceParam" + }, + { + "$ref": "#/components/parameters/simulateParam" + } + ], + "responses": { + "200": { + "description": "Indicates a successful call", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/syncResponse" + } + } + } + } + } + } + } + }, + "/api/ml/saved_objects/sync": { + "get": { + "description": "Synchronizes Kibana saved objects for machine learning jobs and trained models. You must have `all` privileges for the **Machine Learning** feature in the **Analytics** section of the Kibana feature privileges. This API runs automatically when you start Kibana and periodically thereafter.\n", + "parameters": [ + { + "$ref": "#/components/parameters/simulateParam" + } + ], + "responses": { + "200": { + "description": "Indicates a successful call", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/syncResponse" + } + } + } + } + } + } + } + } + }, + "components": { + "parameters": { + "spaceParam": { + "in": "path", + "name": "spaceId", + "description": "An identifier for the space.", + "required": true, + "schema": { + "type": "string" + } + }, + "simulateParam": { + "in": "query", + "name": "simulate", + "description": "When true, simulates the synchronization by returning only the list actions that would be performed.", + "required": false, + "schema": { + "type": "boolean" + }, + "example": "true" + } + }, + "securitySchemes": { + "basicAuth": { + "type": "http", + "scheme": "basic" + } + }, + "schemas": { + "syncResponse": { + "type": "object", + "properties": { + "datafeedsAdded": { + "type": "object", + "description": "If a saved object for an anomaly detection job is missing a datafeed identifier, it is added. This list contains the datafeed identifiers and indicates whether the synchronization was successful.", + "additionalProperties": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + } + } + } + }, + "datafeedsRemoved": { + "type": "object", + "description": "If a saved object for an anomaly detection job references a datafeed that no longer exists, it is deleted. This list contains the datafeed identifiers and indicates whether the synchronization was successful.", + "additionalProperties": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + } + } + } + }, + "savedObjectsCreated": { + "type": "object", + "description": "If saved objects are missing for machine learning jobs or trained models, they are created. This list contains the job and model identifiers and indicates whether the synchronization was successful.", + "properties": { + "anomaly-detector": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + } + } + } + }, + "data-frame-analytics": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + } + } + } + }, + "trained-model": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + } + } + } + } + } + }, + "savedObjectsDeleted": { + "type": "object", + "description": "If saved objects exist for machine learning jobs or trained models that no longer exist, they are deleted. This list contains the job and model identifiers and indicates whether the synchronization was successful.", + "properties": { + "anomaly-detector": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + } + } + } + }, + "data-frame-analytics": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + } + } + } + }, + "trained-model": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + } + } + } + } + } + } + } + } + } + }, + "security": [ + { + "basicAuth": [] + } + ] +} \ No newline at end of file diff --git a/x-pack/plugins/ml/common/openapi/bundled.yaml b/x-pack/plugins/ml/common/openapi/bundled.yaml new file mode 100644 index 0000000000000..235979ff1651c --- /dev/null +++ b/x-pack/plugins/ml/common/openapi/bundled.yaml @@ -0,0 +1,167 @@ +openapi: 3.0.1 +info: + title: Machine learning APIs + description: Kibana APIs for the machine learning feature + version: 1.0.0 + license: + name: Elastic License 2.0 + url: https://www.elastic.co/licensing/elastic-license +tags: + - name: ml + description: Machine learning +servers: + - url: https://localhost:5601/ +paths: + /s/{spaceId}/api/ml/saved_objects/sync: + get: + description: > + Synchronizes Kibana saved objects for machine learning jobs and trained + models. You must have `all` privileges for the **Machine Learning** + feature in the **Analytics** section of the Kibana feature privileges. + This API runs automatically when you start Kibana and periodically + thereafter. + parameters: + - $ref: '#/components/parameters/spaceParam' + - $ref: '#/components/parameters/simulateParam' + responses: + '200': + description: Indicates a successful call + content: + application/json: + schema: + items: + $ref: '#/components/schemas/syncResponse' + /api/ml/saved_objects/sync: + get: + description: > + Synchronizes Kibana saved objects for machine learning jobs and trained + models. You must have `all` privileges for the **Machine Learning** + feature in the **Analytics** section of the Kibana feature privileges. + This API runs automatically when you start Kibana and periodically + thereafter. + parameters: + - $ref: '#/components/parameters/simulateParam' + responses: + '200': + description: Indicates a successful call + content: + application/json: + schema: + items: + $ref: '#/components/schemas/syncResponse' +components: + parameters: + spaceParam: + in: path + name: spaceId + description: An identifier for the space. + required: true + schema: + type: string + simulateParam: + in: query + name: simulate + description: >- + When true, simulates the synchronization by returning only the list + actions that would be performed. + required: false + schema: + type: boolean + example: 'true' + securitySchemes: + basicAuth: + type: http + scheme: basic + schemas: + syncResponse: + type: object + properties: + datafeedsAdded: + type: object + description: >- + If a saved object for an anomaly detection job is missing a datafeed + identifier, it is added. This list contains the datafeed identifiers + and indicates whether the synchronization was successful. + additionalProperties: + type: object + properties: + success: + type: boolean + datafeedsRemoved: + type: object + description: >- + If a saved object for an anomaly detection job references a datafeed + that no longer exists, it is deleted. This list contains the + datafeed identifiers and indicates whether the synchronization was + successful. + additionalProperties: + type: object + properties: + success: + type: boolean + savedObjectsCreated: + type: object + description: >- + If saved objects are missing for machine learning jobs or trained + models, they are created. This list contains the job and model + identifiers and indicates whether the synchronization was + successful. + properties: + anomaly-detector: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + data-frame-analytics: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + trained-model: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + savedObjectsDeleted: + type: object + description: >- + If saved objects exist for machine learning jobs or trained models + that no longer exist, they are deleted. This list contains the job + and model identifiers and indicates whether the synchronization was + successful. + properties: + anomaly-detector: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + data-frame-analytics: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + trained-model: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true +security: + - basicAuth: [] diff --git a/x-pack/plugins/ml/common/openapi/ml_apis.yaml b/x-pack/plugins/ml/common/openapi/ml_apis.yaml new file mode 100644 index 0000000000000..827aa1075960d --- /dev/null +++ b/x-pack/plugins/ml/common/openapi/ml_apis.yaml @@ -0,0 +1,146 @@ +openapi: 3.0.1 +info: + title: Machine learning APIs + description: Kibana APIs for the machine learning feature + version: "1.0.0" + license: + name: Elastic License 2.0 + url: https://www.elastic.co/licensing/elastic-license +tags: + - name: ml + description: Machine learning +servers: + - url: https://localhost:5601/ +paths: + /s/{spaceId}/api/ml/saved_objects/sync: + get: + description: > + Synchronizes Kibana saved objects for machine learning jobs and trained models. + You must have `all` privileges for the **Machine Learning** feature in the **Analytics** section of the Kibana feature privileges. + This API runs automatically when you start Kibana and periodically thereafter. + parameters: + - $ref: '#/components/parameters/spaceParam' + - $ref: '#/components/parameters/simulateParam' + responses: + '200': + description: Indicates a successful call + content: + application/json: + schema: + items: + $ref: '#/components/schemas/syncResponse' + /api/ml/saved_objects/sync: + get: + description: > + Synchronizes Kibana saved objects for machine learning jobs and trained models. + You must have `all` privileges for the **Machine Learning** feature in the **Analytics** section of the Kibana feature privileges. + This API runs automatically when you start Kibana and periodically thereafter. + parameters: + - $ref: '#/components/parameters/simulateParam' + responses: + '200': + description: Indicates a successful call + content: + application/json: + schema: + items: + $ref: '#/components/schemas/syncResponse' +components: + parameters: + spaceParam: + in: path + name: spaceId + description: An identifier for the space. + required: true + schema: + type: string + simulateParam: + in: query + name: simulate + description: When true, simulates the synchronization by returning only the list actions that would be performed. + required: false + schema: + type: boolean + example: 'true' + securitySchemes: + basicAuth: + type: http + scheme: basic + schemas: + syncResponse: + type: object + properties: + datafeedsAdded: + type: object + description: If a saved object for an anomaly detection job is missing a datafeed identifier, it is added. This list contains the datafeed identifiers and indicates whether the synchronization was successful. + additionalProperties: + type: object + properties: + success: + type: boolean + datafeedsRemoved: + type: object + description: If a saved object for an anomaly detection job references a datafeed that no longer exists, it is deleted. This list contains the datafeed identifiers and indicates whether the synchronization was successful. + additionalProperties: + type: object + properties: + success: + type: boolean + savedObjectsCreated: + type: object + description: If saved objects are missing for machine learning jobs or trained models, they are created. This list contains the job and model identifiers and indicates whether the synchronization was successful. + properties: + anomaly-detector: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + data-frame-analytics: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + trained-model: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + savedObjectsDeleted: + type: object + description: If saved objects exist for machine learning jobs or trained models that no longer exist, they are deleted. This list contains the job and model identifiers and indicates whether the synchronization was successful. + properties: + anomaly-detector: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + data-frame-analytics: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true + trained-model: + type: object + additionalProperties: + type: object + properties: + success: + type: boolean + example: true +security: + - basicAuth: [] \ No newline at end of file diff --git a/x-pack/plugins/ml/common/types/anomalies.ts b/x-pack/plugins/ml/common/types/anomalies.ts index 58d5e9df130af..9b6218e8c3f34 100644 --- a/x-pack/plugins/ml/common/types/anomalies.ts +++ b/x-pack/plugins/ml/common/types/anomalies.ts @@ -278,6 +278,11 @@ export interface AnomaliesTableRecord { * which can be plotted by the ML UI in an anomaly chart. */ isTimeSeriesViewRecord?: boolean; + + /** + * Returns true if the anomaly record represented by the table row can be shown in the maps plugin + */ + isGeoRecord?: boolean; } export type PartitionFieldsType = typeof PARTITION_FIELDS[number]; diff --git a/x-pack/plugins/ml/common/types/locator.ts b/x-pack/plugins/ml/common/types/locator.ts index 933862085b3c6..33ec94b825303 100644 --- a/x-pack/plugins/ml/common/types/locator.ts +++ b/x-pack/plugins/ml/common/types/locator.ts @@ -46,6 +46,7 @@ export interface MlGenericUrlPageState extends MlIndexBasedSearchState { export type MlGenericUrlState = MLPageState< | typeof ML_PAGES.DATA_VISUALIZER_INDEX_VIEWER | typeof ML_PAGES.ANOMALY_DETECTION_CREATE_JOB + | typeof ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_RECOGNIZER | typeof ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_ADVANCED | typeof ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_TYPE | typeof ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_INDEX diff --git a/x-pack/plugins/ml/common/util/errors/errors.test.ts b/x-pack/plugins/ml/common/util/errors/errors.test.ts index 625374857c7ce..e299c67d882f4 100644 --- a/x-pack/plugins/ml/common/util/errors/errors.test.ts +++ b/x-pack/plugins/ml/common/util/errors/errors.test.ts @@ -7,7 +7,7 @@ import Boom from '@hapi/boom'; -import { extractErrorMessage, MLHttpFetchError, MLResponseError, EsErrorBody } from '.'; +import { extractErrorMessage, MLHttpFetchError, EsErrorBody } from '.'; describe('ML - error message utils', () => { describe('extractErrorMessage', () => { @@ -39,7 +39,7 @@ describe('ML - error message utils', () => { expect(extractErrorMessage(stringMessage)).toBe(testMsg); // kibana error without attributes - const bodyWithoutAttributes: MLHttpFetchError = { + const bodyWithoutAttributes: MLHttpFetchError = { name: 'name', req: {} as Request, request: {} as Request, @@ -53,7 +53,7 @@ describe('ML - error message utils', () => { expect(extractErrorMessage(bodyWithoutAttributes)).toBe(testMsg); // kibana error with attributes - const bodyWithAttributes: MLHttpFetchError = { + const bodyWithAttributes: MLHttpFetchError = { name: 'name', req: {} as Request, request: {} as Request, diff --git a/x-pack/plugins/ml/common/util/errors/types.ts b/x-pack/plugins/ml/common/util/errors/types.ts index d1f0d3216f42e..fafbeeb4c1a12 100644 --- a/x-pack/plugins/ml/common/util/errors/types.ts +++ b/x-pack/plugins/ml/common/util/errors/types.ts @@ -45,16 +45,13 @@ export interface MLErrorObject { fullError?: EsErrorBody; } -export interface MLHttpFetchError extends HttpFetchError { +export interface MLHttpFetchErrorBase extends HttpFetchError { body: T; } -export type ErrorType = - | MLHttpFetchError - | EsErrorBody - | Boom.Boom - | string - | undefined; +export type MLHttpFetchError = MLHttpFetchErrorBase; + +export type ErrorType = MLHttpFetchError | EsErrorBody | Boom.Boom | string | undefined; export function isEsErrorBody(error: any): error is EsErrorBody { return error && error.error?.reason !== undefined; diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js index 9e84dccf12b28..c24e294b54a5d 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js @@ -18,6 +18,7 @@ import { formatHumanReadableDateTime, formatHumanReadableDateTimeSeconds, } from '../../../../common/util/date_utils'; +import { ML_JOB_AGGREGATION } from '../../../../common/constants/aggregation_types'; import { DescriptionCell } from './description_cell'; import { DetectorCell } from './detector_cell'; @@ -47,7 +48,8 @@ function showLinksMenuForItem(item, showViewSeriesLink) { canConfigureRules || (showViewSeriesLink && item.isTimeSeriesViewRecord) || item.entityName === 'mlcategory' || - item.customUrls !== undefined + item.customUrls !== undefined || + item.detector.includes(ML_JOB_AGGREGATION.LAT_LONG) ); } diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx index 1cc63a2688bf5..ac67dbe35d9ec 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx @@ -8,7 +8,9 @@ import { cloneDeep } from 'lodash'; import moment from 'moment'; import rison, { RisonValue } from 'rison-node'; +import { escapeKuery } from '@kbn/es-query'; import React, { FC, useEffect, useMemo, useState } from 'react'; +import { APP_ID as MAPS_APP_ID } from '@kbn/maps-plugin/common'; import { EuiButtonIcon, EuiContextMenuItem, @@ -20,8 +22,10 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { ES_FIELD_TYPES } from '@kbn/field-types'; +import { MAPS_APP_LOCATOR } from '@kbn/maps-plugin/public'; import { mlJobService } from '../../services/job_service'; import { getDataViewIdFromName } from '../../util/index_utils'; +import { getInitialAnomaliesLayers } from '../../../maps/util'; import { formatHumanReadableDateTimeSeconds, timeFormatter, @@ -33,7 +37,7 @@ import { getUrlForRecord, openCustomUrlWindow } from '../../util/custom_url_util import { ML_APP_LOCATOR, ML_PAGES } from '../../../../common/constants/locator'; import { SEARCH_QUERY_LANGUAGE } from '../../../../common/constants/search'; // @ts-ignore -import { escapeDoubleQuotes } from '../../explorer/explorer_utils'; +import { escapeDoubleQuotes, getDateFormatTz } from '../../explorer/explorer_utils'; import { isCategorizationAnomaly, isRuleSupported } from '../../../../common/util/anomaly_utils'; import { checkPermission } from '../../capabilities/check_capabilities'; import type { @@ -49,6 +53,7 @@ import type { AnomaliesTableRecord } from '../../../../common/types/anomalies'; interface LinksMenuProps { anomaly: AnomaliesTableRecord; bounds: TimeRangeBounds; + showMapsLink: boolean; showViewSeriesLink: boolean; isAggregatedData: boolean; interval: 'day' | 'hour' | 'second'; @@ -66,9 +71,39 @@ export const LinksMenuUI = (props: LinksMenuProps) => { const kibana = useMlKibana(); const { - services: { share, application }, + services: { data, share, application }, } = kibana; + const getMapsLink = async (anomaly: AnomaliesTableRecord) => { + const initialLayers = getInitialAnomaliesLayers(anomaly.jobId); + const anomalyBucketStartMoment = moment(anomaly.time).tz(getDateFormatTz()); + const anomalyBucketStart = anomalyBucketStartMoment.toISOString(); + const anomalyBucketEnd = anomalyBucketStartMoment + .add(anomaly.source.bucket_span, 'seconds') + .subtract(1, 'ms') + .toISOString(); + const timeRange = data.query.timefilter.timefilter.getTime(); + + // Set 'from' in timeRange to start bucket time for the specific anomaly + timeRange.from = anomalyBucketStart; + timeRange.to = anomalyBucketEnd; + + const locator = share.url.locators.get(MAPS_APP_LOCATOR); + const location = await locator?.getLocation({ + initialLayers, + timeRange, + ...(anomaly.entityName && anomaly.entityValue + ? { + query: { + language: SEARCH_QUERY_LANGUAGE.KUERY, + query: `${escapeKuery(anomaly.entityName)}:${escapeKuery(anomaly.entityValue)}`, + }, + } + : {}), + }); + return location; + }; + useEffect(() => { let unmounted = false; const discoverLocator = share.url.locators.get('DISCOVER_APP_LOCATOR'); @@ -561,23 +596,44 @@ export const LinksMenuUI = (props: LinksMenuProps) => { ); } - if (showViewSeriesLink === true && anomaly.isTimeSeriesViewRecord === true) { - items.push( - { - closePopover(); - viewSeries(); - }} - data-test-subj="mlAnomaliesListRowActionViewSeriesButton" - > - - - ); + if (showViewSeriesLink === true) { + if (anomaly.isTimeSeriesViewRecord) { + items.push( + { + closePopover(); + viewSeries(); + }} + data-test-subj="mlAnomaliesListRowActionViewSeriesButton" + > + + + ); + } + + if (anomaly.isGeoRecord === true) { + items.push( + { + const mapsLink = await getMapsLink(anomaly); + await application.navigateToApp(MAPS_APP_ID, { path: mapsLink?.path }); + }} + data-test-subj="mlAnomaliesListRowActionViewInMapsButton" + > + + + ); + } } if (application.capabilities.discover?.show && isCategorizationAnomalyRecord) { @@ -639,8 +695,8 @@ export const LinksMenuUI = (props: LinksMenuProps) => { return ( ); diff --git a/x-pack/plugins/ml/public/application/components/link_card/link_card.tsx b/x-pack/plugins/ml/public/application/components/link_card/link_card.tsx index ae5c4f0ab9ad9..3b3d6a2fdeb5c 100644 --- a/x-pack/plugins/ml/public/application/components/link_card/link_card.tsx +++ b/x-pack/plugins/ml/public/application/components/link_card/link_card.tsx @@ -39,7 +39,7 @@ export const LinkCard: FC = ({ onClick, href, isDisabled, - 'data-test-subj': dateTestSubj, + 'data-test-subj': dataTestSubj, }) => { const linkHrefAndOnClickProps = { ...(href ? { href } : {}), @@ -58,7 +58,7 @@ export const LinkCard: FC = ({ background: 'transparent', outline: 'none', }} - data-test-subj={dateTestSubj} + data-test-subj={dataTestSubj} color="subdued" {...linkHrefAndOnClickProps} > diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/file_datavisualizer.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/file_datavisualizer.tsx index 4c1d8b478f971..9fcf0df33a0af 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/file_datavisualizer.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/file_datavisualizer.tsx @@ -8,7 +8,11 @@ import React, { FC, Fragment, useState, useEffect, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { ResultLink, FileDataVisualizerSpec } from '@kbn/data-visualizer-plugin/public'; +import type { + FileDataVisualizerSpec, + GetAdditionalLinksParams, + GetAdditionalLinks, +} from '@kbn/data-visualizer-plugin/public'; import { useTimefilter } from '../../contexts/kibana'; import { HelpMenu } from '../../components/help_menu'; import { useMlKibana, useMlLocator } from '../../contexts/kibana'; @@ -19,11 +23,6 @@ import { mlNodesAvailable, getMlNodeCount } from '../../ml_nodes_check/check_ml_ import { checkPermission } from '../../capabilities/check_capabilities'; import { MlPageHeader } from '../../components/page_header'; -interface GetUrlParams { - indexPatternId: string; - globalState: any; -} - export const FileDataVisualizerPage: FC = () => { useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false }); const { @@ -40,60 +39,62 @@ export const FileDataVisualizerPage: FC = () => { const [FileDataVisualizer, setFileDataVisualizer] = useState(null); - const links: ResultLink[] = useMemo( + const getAdditionalLinks: GetAdditionalLinks = useMemo( () => [ - { - id: 'create_ml_job', - title: i18n.translate('xpack.ml.fileDatavisualizer.actionsPanel.anomalyDetectionTitle', { - defaultMessage: 'Create new ML job', - }), - description: '', - icon: 'machineLearningApp', - type: 'file', - getUrl: async ({ indexPatternId, globalState }: GetUrlParams) => { - return await mlLocator.getUrl({ - page: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_TYPE, - pageState: { - index: indexPatternId, - globalState, - }, - }); - }, - canDisplay: async ({ indexPatternId }) => { - try { - const { timeFieldName } = await getDataView(indexPatternId); - return ( - isFullLicense() && - timeFieldName !== undefined && - checkPermission('canCreateJob') && - mlNodesAvailable() - ); - } catch (error) { - return false; - } + async ({ dataViewId, globalState }: GetAdditionalLinksParams) => [ + { + id: 'create_ml_job', + title: i18n.translate('xpack.ml.fileDatavisualizer.actionsPanel.anomalyDetectionTitle', { + defaultMessage: 'Create ML job', + }), + description: '', + icon: 'machineLearningApp', + type: 'file', + getUrl: async () => { + return await mlLocator.getUrl({ + page: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_TYPE, + pageState: { + index: dataViewId, + globalState, + }, + }); + }, + canDisplay: async () => { + try { + const { timeFieldName } = await getDataView(dataViewId); + return ( + isFullLicense() && + timeFieldName !== undefined && + checkPermission('canCreateJob') && + mlNodesAvailable() + ); + } catch (error) { + return false; + } + }, }, - }, - { - id: 'open_in_data_viz', - title: i18n.translate('xpack.ml.fileDatavisualizer.actionsPanel.dataframeTitle', { - defaultMessage: 'Open in Data Visualizer', - }), - description: '', - icon: 'dataVisualizer', - type: 'file', - getUrl: async ({ indexPatternId, globalState }: GetUrlParams) => { - return await mlLocator.getUrl({ - page: ML_PAGES.DATA_VISUALIZER_INDEX_VIEWER, - pageState: { - index: indexPatternId, - globalState, - }, - }); + { + id: 'open_in_data_viz', + title: i18n.translate('xpack.ml.fileDatavisualizer.actionsPanel.dataframeTitle', { + defaultMessage: 'Open in Data Visualizer', + }), + description: '', + icon: 'dataVisualizer', + type: 'file', + getUrl: async () => { + return await mlLocator.getUrl({ + page: ML_PAGES.DATA_VISUALIZER_INDEX_VIEWER, + pageState: { + index: dataViewId, + globalState, + }, + }); + }, + canDisplay: async () => dataViewId !== '', }, - canDisplay: async ({ indexPatternId }) => indexPatternId !== '', - }, + ], ], - [] + [mlLocator] ); useEffect(() => { @@ -114,7 +115,7 @@ export const FileDataVisualizerPage: FC = () => { defaultMessage="Data Visualizer" /> - + ) : null} diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/index_data_visualizer.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/index_data_visualizer.tsx index 5a041df220df9..ae5c7c9948f18 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/index_data_visualizer.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/index_data_visualizer.tsx @@ -5,28 +5,38 @@ * 2.0. */ -import React, { FC, Fragment, useEffect, useState, useMemo } from 'react'; +import React, { FC, Fragment, useEffect, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { ResultLink, IndexDataVisualizerSpec } from '@kbn/data-visualizer-plugin/public'; +import type { + IndexDataVisualizerSpec, + ResultLink, + GetAdditionalLinks, + GetAdditionalLinksParams, +} from '@kbn/data-visualizer-plugin/public'; import { useMlKibana, useTimefilter, useMlLocator } from '../../contexts/kibana'; import { HelpMenu } from '../../components/help_menu'; import { ML_PAGES } from '../../../../common/constants/locator'; import { isFullLicense } from '../../license'; import { mlNodesAvailable, getMlNodeCount } from '../../ml_nodes_check/check_ml_nodes'; import { checkPermission } from '../../capabilities/check_capabilities'; - import { MlPageHeader } from '../../components/page_header'; -interface GetUrlParams { - indexPatternId: string; - globalState: any; +interface RecognizerModule { + id: string; + title: string; + query: Record; + description: string; + logo: { + icon: string; + }; } export const IndexDataVisualizerPage: FC = () => { useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false }); const { services: { + http, docLinks, dataVisualizer, data: { @@ -48,8 +58,12 @@ export const IndexDataVisualizerPage: FC = () => { } }, []); - const links: ResultLink[] = useMemo( - () => [ + const getAsyncMLCards = async ({ + dataViewId, + dataViewTitle, + globalState, + }: GetAdditionalLinksParams): Promise => { + return [ { id: 'create_ml_ad_job', title: i18n.translate('xpack.ml.indexDatavisualizer.actionsPanel.anomalyDetectionTitle', { @@ -64,18 +78,18 @@ export const IndexDataVisualizerPage: FC = () => { ), icon: 'createAdvancedJob', type: 'file', - getUrl: async ({ indexPatternId, globalState }: GetUrlParams) => { + getUrl: async () => { return await mlLocator.getUrl({ page: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_ADVANCED, pageState: { - index: indexPatternId, + index: dataViewId, globalState, }, }); }, - canDisplay: async ({ indexPatternId }) => { + canDisplay: async () => { try { - const { timeFieldName } = await getDataView(indexPatternId); + const { timeFieldName } = await getDataView(dataViewId); return ( isFullLicense() && timeFieldName !== undefined && @@ -86,7 +100,7 @@ export const IndexDataVisualizerPage: FC = () => { return false; } }, - dataTestSubj: 'dataVisualizerCreateAdvancedJobCard', + 'data-test-subj': 'dataVisualizerCreateAdvancedJobCard', }, { id: 'create_ml_dfa_job', @@ -101,11 +115,11 @@ export const IndexDataVisualizerPage: FC = () => { ), icon: 'classificationJob', type: 'file', - getUrl: async ({ indexPatternId, globalState }: GetUrlParams) => { + getUrl: async () => { return await mlLocator.getUrl({ page: ML_PAGES.DATA_FRAME_ANALYTICS_CREATE_JOB, pageState: { - index: indexPatternId, + index: dataViewId, globalState, }, }); @@ -115,12 +129,57 @@ export const IndexDataVisualizerPage: FC = () => { isFullLicense() && checkPermission('canCreateDataFrameAnalytics') && mlNodesAvailable() ); }, - dataTestSubj: 'dataVisualizerCreateDataFrameAnalyticsCard', + 'data-test-subj': 'dataVisualizerCreateDataFrameAnalyticsCard', }, - ], - [] - ); + ]; + }; + const getAsyncRecognizedModuleCards = async (params: GetAdditionalLinksParams) => { + const { dataViewId, dataViewTitle } = params; + const modules = await http.fetch( + `/api/ml/modules/recognize/${dataViewTitle}`, + { + method: 'GET', + } + ); + return modules?.map( + (m): ResultLink => ({ + id: m.id, + title: m.title, + description: m.description, + icon: m.logo.icon, + type: 'index', + getUrl: async () => { + return await mlLocator.getUrl({ + page: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_RECOGNIZER, + pageState: { + id: m.id, + index: dataViewId, + }, + }); + }, + canDisplay: async () => { + try { + const { timeFieldName } = await getDataView(dataViewId); + return ( + isFullLicense() && + timeFieldName !== undefined && + checkPermission('canCreateJob') && + mlNodesAvailable() + ); + } catch (error) { + return false; + } + }, + 'data-test-subj': m.id, + }) + ); + }; + + const getAdditionalLinks: GetAdditionalLinks = useMemo( + () => [getAsyncRecognizedModuleCards, getAsyncMLCards], + [mlLocator] + ); return IndexDataVisualizer ? ( {IndexDataVisualizer !== null ? ( @@ -131,7 +190,7 @@ export const IndexDataVisualizerPage: FC = () => { defaultMessage="Data Visualizer" /> - + ) : null} diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js index ddaaa55e7a3a4..d3dc2681cf4c5 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js @@ -16,7 +16,6 @@ import { EuiFlexItem, EuiIconTip, EuiToolTip, - htmlIdGenerator, } from '@elastic/eui'; import { @@ -36,15 +35,13 @@ import { MlTooltipComponent } from '../../components/chart_tooltip'; import { withKibana } from '@kbn/kibana-react-plugin/public'; import { useMlKibana } from '../../contexts/kibana'; import { ML_JOB_AGGREGATION } from '../../../../common/constants/aggregation_types'; -import { AnomalySource } from '../../../maps/anomaly_source'; -import { CUSTOM_COLOR_RAMP } from '../../../maps/anomaly_layer_wizard_factory'; -import { LAYER_TYPE, APP_ID as MAPS_APP_ID } from '@kbn/maps-plugin/common'; +import { getInitialAnomaliesLayers } from '../../../maps/util'; +import { APP_ID as MAPS_APP_ID } from '@kbn/maps-plugin/common'; import { MAPS_APP_LOCATOR } from '@kbn/maps-plugin/public'; import { ExplorerChartsErrorCallOuts } from './explorer_charts_error_callouts'; import { addItemToRecentlyAccessed } from '../../util/recently_accessed'; import { EmbeddedMapComponentWrapper } from './explorer_chart_embedded_map'; import { useActiveCursor } from '@kbn/charts-plugin/public'; -import { ML_ANOMALY_LAYERS } from '../../../maps/util'; import { Chart, Settings } from '@elastic/charts'; import useObservable from 'react-use/lib/useObservable'; @@ -114,59 +111,7 @@ function ExplorerChartContainer({ const getMapsLink = useCallback(async () => { const { queryString, query } = getEntitiesQuery(series); - const initialLayers = []; - const typicalStyle = { - type: 'VECTOR', - properties: { - fillColor: { - type: 'STATIC', - options: { - color: '#98A2B2', - }, - }, - lineColor: { - type: 'STATIC', - options: { - color: '#fff', - }, - }, - lineWidth: { - type: 'STATIC', - options: { - size: 2, - }, - }, - iconSize: { - type: 'STATIC', - options: { - size: 6, - }, - }, - }, - }; - - const style = { - type: 'VECTOR', - properties: { - fillColor: CUSTOM_COLOR_RAMP, - lineColor: CUSTOM_COLOR_RAMP, - }, - isTimeAware: false, - }; - - for (const layer in ML_ANOMALY_LAYERS) { - if (ML_ANOMALY_LAYERS.hasOwnProperty(layer)) { - initialLayers.push({ - id: htmlIdGenerator()(), - type: LAYER_TYPE.GEOJSON_VECTOR, - sourceDescriptor: AnomalySource.createDescriptor({ - jobId: series.jobId, - typicalActual: ML_ANOMALY_LAYERS[layer], - }), - style: ML_ANOMALY_LAYERS[layer] === ML_ANOMALY_LAYERS.TYPICAL ? typicalStyle : style, - }); - } - } + const initialLayers = getInitialAnomaliesLayers(series.jobId); const locator = share.url.locators.get(MAPS_APP_LOCATOR); const location = await locator.getLocation({ diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts b/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts index d2c7c3f1fe2d2..6aa3444c9caaf 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts +++ b/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts @@ -19,6 +19,7 @@ import { } from '../../../common/constants/search'; import { EntityField, getEntityFieldList } from '../../../common/util/anomaly_utils'; import { extractErrorMessage } from '../../../common/util/errors'; +import { ML_JOB_AGGREGATION } from '../../../common/constants/aggregation_types'; import { isSourceDataChartableForDetector, isModelPlotChartableForDetector, @@ -495,6 +496,8 @@ export async function loadAnomaliesTableData( } anomaly.isTimeSeriesViewRecord = isChartable; + anomaly.isGeoRecord = + detector !== undefined && detector.function === ML_JOB_AGGREGATION.LAT_LONG; if (mlJobService.customUrlsByJob[jobId] !== undefined) { anomaly.customUrls = mlJobService.customUrlsByJob[jobId]; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/extract_job_details.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/extract_job_details.js index fd00058e64713..13f7bb58a0f44 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/extract_job_details.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/extract_job_details.js @@ -99,6 +99,14 @@ export function extractJobDetails(job, basePath, refreshJobList) { return ['', ]; }), }; + if (job.alerting_rules) { + // remove the alerting_rules list from the general section + // so not to show it twice. + const i = general.items.findIndex((item) => item[0] === 'alerting_rules'); + if (i >= 0) { + general.items.splice(i, 1); + } + } const detectors = { id: 'detectors', diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts index 1e28ac6ef8a2d..a0ca9e4502cc2 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts @@ -24,11 +24,7 @@ import { useEffect, useMemo } from 'react'; import { DEFAULT_MODEL_MEMORY_LIMIT } from '../../../../../../../common/constants/new_job'; import { ml } from '../../../../../services/ml_api_service'; import { JobValidator, VALIDATION_DELAY_MS } from '../../job_validator/job_validator'; -import { - MLHttpFetchError, - MLResponseError, - extractErrorMessage, -} from '../../../../../../../common/util/errors'; +import { MLHttpFetchError, extractErrorMessage } from '../../../../../../../common/util/errors'; import { useMlKibana } from '../../../../../contexts/kibana'; import { JobCreator } from '../job_creator'; @@ -41,10 +37,10 @@ export const modelMemoryEstimatorProvider = ( jobValidator: JobValidator ) => { const modelMemoryCheck$ = new Subject(); - const error$ = new Subject>(); + const error$ = new Subject(); return { - get error$(): Observable> { + get error$(): Observable { return error$.asObservable(); }, get updates$(): Observable { diff --git a/x-pack/plugins/ml/public/application/routing/ml_page_wrapper.tsx b/x-pack/plugins/ml/public/application/routing/ml_page_wrapper.tsx index f1674a12b77c6..4c2a6d0058edc 100644 --- a/x-pack/plugins/ml/public/application/routing/ml_page_wrapper.tsx +++ b/x-pack/plugins/ml/public/application/routing/ml_page_wrapper.tsx @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import React, { FC } from 'react'; import { TrackApplicationView } from '@kbn/usage-collection-plugin/public'; diff --git a/x-pack/plugins/ml/public/application/routing/use_active_route.ts b/x-pack/plugins/ml/public/application/routing/use_active_route.ts index 925db8185c379..9183e45c3d0ae 100644 --- a/x-pack/plugins/ml/public/application/routing/use_active_route.ts +++ b/x-pack/plugins/ml/public/application/routing/use_active_route.ts @@ -8,11 +8,21 @@ import { useLocation, useRouteMatch } from 'react-router-dom'; import { keyBy } from 'lodash'; import { useMemo } from 'react'; +import { useExecutionContext } from '@kbn/kibana-react-plugin/public'; +import { useMlKibana } from '../contexts/kibana'; import type { MlRoute } from './router'; +/** + * Provides an active route of the ML app. + * @param routesList + */ export const useActiveRoute = (routesList: MlRoute[]): MlRoute => { const { pathname } = useLocation(); + const { + services: { executionContext }, + } = useMlKibana(); + /** * Temp fix for routes with params. */ @@ -30,8 +40,14 @@ export const useActiveRoute = (routesList: MlRoute[]): MlRoute => { } // Remove trailing slash from the pathname const pathnameKey = pathname.replace(/\/$/, ''); - return routesMap[pathnameKey]; + return routesMap[pathnameKey] ?? routesMap['/overview']; }, [pathname]); - return activeRoute ?? routesMap['/overview']; + useExecutionContext(executionContext, { + name: 'Machine Learning', + type: 'application', + page: activeRoute?.path, + }); + + return activeRoute; }; diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts index aefdf0ce6e431..d15c500ddb9c4 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts @@ -47,6 +47,10 @@ export interface InferenceStatsResponse { trained_model_stats: TrainedModelStat[]; } +export interface MlInferTrainedModelDeploymentResponse { + inference_results: estypes.MlInferTrainedModelDeploymentResponse[]; +} + /** * Service with APIs calls to perform inference operations. * @param httpService @@ -143,22 +147,13 @@ export function trainedModelsApiProvider(httpService: HttpService) { inferTrainedModel(modelId: string, payload: any, timeout?: string) { const body = JSON.stringify(payload); - return httpService.http({ + return httpService.http({ path: `${apiBasePath}/trained_models/infer/${modelId}`, method: 'POST', body, ...(timeout ? { query: { timeout } as HttpFetchQuery } : {}), }); }, - - ingestPipelineSimulate(payload: estypes.IngestSimulateRequest['body']) { - const body = JSON.stringify(payload); - return httpService.http({ - path: `${apiBasePath}/trained_models/ingest_pipeline_simulate`, - method: 'POST', - body, - }); - }, }; } diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/components/add_item_popover/__snapshots__/add_item_popover.test.js.snap b/x-pack/plugins/ml/public/application/settings/filter_lists/components/add_item_popover/__snapshots__/add_item_popover.test.js.snap index 969406724537d..45d9296f1752e 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/components/add_item_popover/__snapshots__/add_item_popover.test.js.snap +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/components/add_item_popover/__snapshots__/add_item_popover.test.js.snap @@ -25,6 +25,7 @@ exports[`AddItemPopover calls addItems with multiple items on clicking Add butto display="inlineBlock" hasArrow={true} id="add_item_popover" + initialFocus="#filter_list_add_item_input_row" isOpen={false} ownFocus={true} panelClassName="ml-add-filter-item-popover" @@ -37,6 +38,7 @@ exports[`AddItemPopover calls addItems with multiple items on clicking Add butto fullWidth={false} hasChildLabel={true} hasEmptyLabelSpace={false} + id="filter_list_add_item_input_row" label={
; @@ -184,10 +184,8 @@ export const ModelsList: FC = ({ } } - // Need to fetch state for 3rd party models to enable/disable actions - await fetchModelsStats( - newItems.filter((v) => v.model_type.includes(TRAINED_MODEL_TYPE.PYTORCH)) - ); + // Need to fetch state for all models to enable/disable actions + await fetchModelsStats(newItems); setItems(newItems); @@ -484,6 +482,7 @@ export const ModelsList: FC = ({ isPrimary: true, available: isTestable, onClick: setShowTestFlyout, + enabled: isTestEnabled, }, ] as Array>) ); diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/index.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/index.ts index da7c12c1c0c58..533d8e2315a9e 100644 --- a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/index.ts +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/index.ts @@ -6,4 +6,4 @@ */ export { TestTrainedModelFlyout } from './test_flyout'; -export { isTestable } from './utils'; +export { isTestable, isTestEnabled } from './utils'; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/index.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/index.ts new file mode 100644 index 0000000000000..c6cde8da39469 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { NerInference } from './ner'; +import { + TextClassificationInference, + ZeroShotClassificationInference, + FillMaskInference, + LangIdentInference, +} from './text_classification'; +import { TextEmbeddingInference } from './text_embedding'; + +export type InferrerType = + | NerInference + | TextClassificationInference + | TextEmbeddingInference + | ZeroShotClassificationInference + | FillMaskInference + | LangIdentInference; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/inference_base.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/inference_base.ts index 777ca2d314c4d..e3b502a10f6ce 100644 --- a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/inference_base.ts +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/inference_base.ts @@ -5,19 +5,44 @@ * 2.0. */ +import { BehaviorSubject } from 'rxjs'; import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { MLHttpFetchError } from '../../../../../../common/util/errors'; +import { SupportedPytorchTasksType } from '../../../../../../common/constants/trained_models'; import { trainedModelsApiProvider } from '../../../../services/ml_api_service/trained_models'; +export type InferenceType = + | SupportedPytorchTasksType + | keyof estypes.AggregationsInferenceConfigContainer; + const DEFAULT_INPUT_FIELD = 'text_field'; -export type FormattedNerResp = Array<{ +export type FormattedNerResponse = Array<{ value: string; entity: estypes.MlTrainedModelEntities | null; }>; +export interface InferResponse { + inputText: string; + response: T; + rawResponse: U; +} + +export enum RUNNING_STATE { + STOPPED, + RUNNING, + FINISHED, + FINISHED_WITH_ERRORS, +} + export abstract class InferenceBase { + protected abstract inferenceType: InferenceType; protected readonly inputField: string; + public inputText$ = new BehaviorSubject(''); + public inferenceResult$ = new BehaviorSubject(null); + public inferenceError$ = new BehaviorSubject(null); + public runningState$ = new BehaviorSubject(RUNNING_STATE.STOPPED); constructor( protected trainedModelsApi: ReturnType, @@ -26,5 +51,49 @@ export abstract class InferenceBase { this.inputField = model.input?.field_names[0] ?? DEFAULT_INPUT_FIELD; } - protected abstract infer(inputText: string): Promise; + public setStopped() { + this.inferenceError$.next(null); + this.runningState$.next(RUNNING_STATE.STOPPED); + } + public setRunning() { + this.inferenceError$.next(null); + this.runningState$.next(RUNNING_STATE.RUNNING); + } + + public setFinished() { + this.runningState$.next(RUNNING_STATE.FINISHED); + } + + public setFinishedWithErrors(error: MLHttpFetchError) { + this.inferenceError$.next(error); + this.runningState$.next(RUNNING_STATE.FINISHED_WITH_ERRORS); + } + + protected abstract getInputComponent(): JSX.Element; + protected abstract getOutputComponent(): JSX.Element; + + protected abstract infer(): Promise; + + protected getInferenceConfig(): estypes.AggregationsClassificationInferenceOptions | undefined { + return this.model.inference_config[ + this.inferenceType as keyof estypes.AggregationsInferenceConfigContainer + ]; + } + + protected getNumTopClassesConfig(defaultOverride = 5) { + const options: estypes.AggregationsClassificationInferenceOptions | undefined = + this.getInferenceConfig(); + + if (options?.num_top_classes !== undefined && options?.num_top_classes > 0) { + return {}; + } + + return { + inference_config: { + [this.inferenceType]: { + num_top_classes: defaultOverride, + }, + }, + }; + } } diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/inference_input_form.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/inference_input_form.tsx index 6503486d98211..0ee33791ef55e 100644 --- a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/inference_input_form.tsx +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/inference_input_form.tsx @@ -5,25 +5,20 @@ * 2.0. */ -import React, { FC, useState } from 'react'; +import React, { FC, useState, useMemo } from 'react'; -import { i18n } from '@kbn/i18n'; +import useObservable from 'react-use/lib/useObservable'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiSpacer, EuiTextArea, EuiButton, EuiTabs, EuiTab } from '@elastic/eui'; - -import { LangIdentInference } from './lang_ident/lang_ident_inference'; -import { NerInference } from './ner/ner_inference'; -import type { FormattedLangIdentResp } from './lang_ident/lang_ident_inference'; -import type { FormattedNerResp } from './ner/ner_inference'; - -import { MLJobEditor } from '../../../../jobs/jobs_list/components/ml_job_editor'; +import { EuiSpacer, EuiButton, EuiTabs, EuiTab } from '@elastic/eui'; import { extractErrorMessage } from '../../../../../../common/util/errors'; import { ErrorMessage } from '../inference_error'; import { OutputLoadingContent } from '../output_loading'; +import { RUNNING_STATE } from './inference_base'; +import { RawOutput } from './raw_output'; +import type { InferrerType } from '.'; interface Props { - inferrer: LangIdentInference | NerInference; - getOutputComponent(output: any): JSX.Element; + inferrer: InferrerType; } enum TAB { @@ -31,61 +26,41 @@ enum TAB { RAW, } -export const InferenceInputForm: FC = ({ inferrer, getOutputComponent }) => { - const [inputText, setInputText] = useState(''); - const [isRunning, setIsRunning] = useState(false); - const [output, setOutput] = useState(null); - const [rawOutput, setRawOutput] = useState(null); +export const InferenceInputForm: FC = ({ inferrer }) => { const [selectedTab, setSelectedTab] = useState(TAB.TEXT); - const [showOutput, setShowOutput] = useState(false); const [errorText, setErrorText] = useState(null); + const runningState = useObservable(inferrer.runningState$); + const inputText = useObservable(inferrer.inputText$); + const inputComponent = useMemo(() => inferrer.getInputComponent(), []); + const outputComponent = useMemo(() => inferrer.getOutputComponent(), []); + async function run() { - setShowOutput(true); - setOutput(null); - setRawOutput(null); - setIsRunning(true); setErrorText(null); try { - const { response, rawResponse } = await inferrer.infer(inputText); - setOutput(response); - setRawOutput(JSON.stringify(rawResponse, null, 2)); + await inferrer.infer(); } catch (e) { - setIsRunning(false); - setOutput(null); setErrorText(extractErrorMessage(e)); - setRawOutput(JSON.stringify(e.body ?? e, null, 2)); } - setIsRunning(false); } return ( <> - { - setInputText(e.target.value); - }} - /> + <>{inputComponent}
- {showOutput === true ? ( + {runningState !== RUNNING_STATE.STOPPED ? ( <> @@ -94,7 +69,7 @@ export const InferenceInputForm: FC = ({ inferrer, getOutputComponent }) onClick={setSelectedTab.bind(null, TAB.TEXT)} > @@ -103,7 +78,7 @@ export const InferenceInputForm: FC = ({ inferrer, getOutputComponent }) onClick={setSelectedTab.bind(null, TAB.RAW)} > @@ -113,16 +88,16 @@ export const InferenceInputForm: FC = ({ inferrer, getOutputComponent }) {selectedTab === TAB.TEXT ? ( <> - {errorText !== null ? ( + {runningState === RUNNING_STATE.RUNNING ? : null} + + {errorText !== null || runningState === RUNNING_STATE.FINISHED_WITH_ERRORS ? ( - ) : output === null ? ( - - ) : ( - <>{getOutputComponent(output)} - )} + ) : null} + + {runningState === RUNNING_STATE.FINISHED ? <>{outputComponent} : null} ) : ( - + )} ) : null} diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/index.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/index.ts deleted file mode 100644 index b3439d90e8828..0000000000000 --- a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export type { FormattedLangIdentResp } from './lang_ident_inference'; -export { LangIdentInference } from './lang_ident_inference'; -export { LangIdentOutput } from './lang_ident_output'; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_ident_inference.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_ident_inference.ts deleted file mode 100644 index 9108a59197617..0000000000000 --- a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_ident_inference.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; - -import { InferenceBase } from '../inference_base'; - -export type FormattedLangIdentResp = Array<{ - className: string; - classProbability: number; - classScore: number; -}>; - -interface InferResponse { - response: FormattedLangIdentResp; - rawResponse: estypes.IngestSimulateResponse; -} - -export class LangIdentInference extends InferenceBase { - public async infer(inputText: string) { - const payload: estypes.IngestSimulateRequest['body'] = { - pipeline: { - processors: [ - { - inference: { - model_id: this.model.model_id, - inference_config: { - // @ts-expect-error classification missing from type - classification: { - num_top_classes: 3, - }, - }, - field_mappings: { - contents: this.inputField, - }, - target_field: '_ml.lang_ident', - }, - }, - ], - }, - docs: [ - { - _source: { - contents: inputText, - }, - }, - ], - }; - const resp = await this.trainedModelsApi.ingestPipelineSimulate(payload); - if (resp.docs.length) { - const topClasses = resp.docs[0].doc?._source._ml?.lang_ident?.top_classes ?? []; - - return { - response: topClasses.map((t: any) => ({ - className: t.class_name, - classProbability: t.class_probability, - classScore: t.class_score, - })), - rawResponse: resp, - }; - } - return { response: [], rawResponse: resp }; - } -} diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_ident_output.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_ident_output.tsx deleted file mode 100644 index e4968bc516f83..0000000000000 --- a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_ident_output.tsx +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { FC } from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiSpacer, EuiBasicTable, EuiTitle } from '@elastic/eui'; - -import type { FormattedLangIdentResp } from './lang_ident_inference'; -import { getLanguage } from './lang_codes'; - -const PROBABILITY_SIG_FIGS = 3; - -export const LangIdentOutput: FC<{ result: FormattedLangIdentResp }> = ({ result }) => { - if (result.length === 0) { - return null; - } - - const lang = getLanguage(result[0].className); - - const items = result.map(({ className, classProbability }, i) => { - return { - noa: `${i + 1}`, - className: getLanguage(className), - classProbability: `${Number(classProbability).toPrecision(PROBABILITY_SIG_FIGS)}`, - }; - }); - - const columns = [ - { - field: 'noa', - name: '#', - width: '5%', - truncateText: false, - isExpander: false, - }, - { - field: 'className', - name: i18n.translate( - 'xpack.ml.trainedModels.testModelsFlyout.langIdent.output.language_title', - { - defaultMessage: 'Language', - } - ), - width: '30%', - truncateText: false, - isExpander: false, - }, - { - field: 'classProbability', - name: i18n.translate( - 'xpack.ml.trainedModels.testModelsFlyout.langIdent.output.probability_title', - { - defaultMessage: 'Probability', - } - ), - truncateText: false, - isExpander: false, - }, - ]; - - const title = - lang !== 'unknown' - ? i18n.translate('xpack.ml.trainedModels.testModelsFlyout.langIdent.output.title', { - defaultMessage: 'This looks like {lang}', - values: { lang }, - }) - : i18n.translate('xpack.ml.trainedModels.testModelsFlyout.langIdent.output.titleUnknown', { - defaultMessage: 'Language code unknown: {code}', - values: { code: result[0].className }, - }); - - return ( - <> - -

{title}

-
- - - - - ); -}; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/index.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/index.ts index 38ddad8bdeb80..5e5a029997965 100644 --- a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/index.ts +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/index.ts @@ -5,6 +5,6 @@ * 2.0. */ -export type { FormattedNerResp } from './ner_inference'; +export type { FormattedNerResponse, NerResponse } from './ner_inference'; export { NerInference } from './ner_inference'; -export { NerOutput } from './ner_output'; +export { getNerOutputComponent } from './ner_output'; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/ner_inference.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/ner_inference.ts index e4dcfcc2c6333..13f07d8c88770 100644 --- a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/ner_inference.ts +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/ner_inference.ts @@ -7,29 +7,61 @@ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { InferenceBase } from '../inference_base'; +import { InferenceBase, InferResponse } from '../inference_base'; +import { getGeneralInputComponent } from '../text_input'; +import { getNerOutputComponent } from './ner_output'; +import { MlInferTrainedModelDeploymentResponse } from '../../../../../services/ml_api_service/trained_models'; +import { SUPPORTED_PYTORCH_TASKS } from '../../../../../../../common/constants/trained_models'; -export type FormattedNerResp = Array<{ +export type FormattedNerResponse = Array<{ value: string; entity: estypes.MlTrainedModelEntities | null; }>; -interface InferResponse { - response: FormattedNerResp; - rawResponse: estypes.MlInferTrainedModelDeploymentResponse; -} +export type NerResponse = InferResponse< + FormattedNerResponse, + MlInferTrainedModelDeploymentResponse +>; + +export class NerInference extends InferenceBase { + protected inferenceType = SUPPORTED_PYTORCH_TASKS.NER; -export class NerInference extends InferenceBase { - public async infer(inputText: string) { - const payload = { docs: { [this.inputField]: inputText } }; - const resp = await this.trainedModelsApi.inferTrainedModel(this.model.model_id, payload, '30s'); + public async infer() { + try { + this.setRunning(); + const inputText = this.inputText$.getValue(); + const payload = { docs: [{ [this.inputField]: inputText }] }; + const resp = await this.trainedModelsApi.inferTrainedModel( + this.model.model_id, + payload, + '30s' + ); + + const processedResponse: NerResponse = { + response: parseResponse(resp), + rawResponse: resp, + inputText, + }; + this.inferenceResult$.next(processedResponse); + this.setFinished(); + return processedResponse; + } catch (error) { + this.setFinishedWithErrors(error); + throw error; + } + } + + public getInputComponent(): JSX.Element { + return getGeneralInputComponent(this); + } - return { response: parseResponse(resp), rawResponse: resp }; + public getOutputComponent(): JSX.Element { + return getNerOutputComponent(this); } } -function parseResponse(resp: estypes.MlInferTrainedModelDeploymentResponse): FormattedNerResp { - const { predicted_value: predictedValue, entities } = resp; +function parseResponse(resp: MlInferTrainedModelDeploymentResponse): FormattedNerResponse { + const [{ predicted_value: predictedValue, entities }] = resp.inference_results; const splitWordsAndEntitiesRegex = /(\[.*?\]\(.*?&.*?\))/; const matchEntityRegex = /(\[.*?\])\((.*?)&(.*?)\)/; if (predictedValue === undefined || entities === undefined) { diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/ner_output.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/ner_output.tsx index e9db3fa8efd36..76c154e1000b1 100644 --- a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/ner_output.tsx +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/ner/ner_output.tsx @@ -7,6 +7,7 @@ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import React, { FC, ReactNode } from 'react'; +import useObservable from 'react-use/lib/useObservable'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiHorizontalRule, @@ -21,7 +22,7 @@ import { useCurrentEuiTheme, EuiThemeType, } from '../../../../../components/color_range_legend/use_color_range'; -import type { FormattedNerResp } from './ner_inference'; +import type { NerInference } from './ner_inference'; const ICON_PADDING = '2px'; const PROBABILITY_SIG_FIGS = 3; @@ -60,10 +61,18 @@ const UNKNOWN_ENTITY_TYPE = { borderColor: 'euiColorVis5', }; -export const NerOutput: FC<{ result: FormattedNerResp }> = ({ result }) => { +export const getNerOutputComponent = (inferrer: NerInference) => ; + +const NerOutput: FC<{ inferrer: NerInference }> = ({ inferrer }) => { const { euiTheme } = useCurrentEuiTheme(); + const result = useObservable(inferrer.inferenceResult$); + + if (!result) { + return null; + } + const lineSplit: JSX.Element[] = []; - result.forEach(({ value, entity }) => { + result.response.forEach(({ value, entity }) => { if (entity === null) { const lines = value .split(/(\n)/) diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/raw_output.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/raw_output.tsx new file mode 100644 index 0000000000000..4a82dcb82aa65 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/raw_output.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, { FC } from 'react'; +import { Observable } from 'rxjs'; +import useObservable from 'react-use/lib/useObservable'; +import { MLJobEditor } from '../../../../jobs/jobs_list/components/ml_job_editor'; + +import type { InferrerType } from '.'; +import { NerResponse } from './ner'; +import { TextClassificationResponse } from './text_classification'; +import { TextEmbeddingResponse } from './text_embedding'; +import { RUNNING_STATE } from './inference_base'; + +type InferenceResponse = NerResponse | TextClassificationResponse | TextEmbeddingResponse; + +export const RawOutput: FC<{ + inferrer: InferrerType; +}> = ({ inferrer }) => { + const inferenceError = useObservable(inferrer.inferenceError$); + const runningState = useObservable(inferrer.runningState$); + const inferenceResult = useObservable(inferrer.inferenceResult$ as Observable); + + if ( + (runningState === RUNNING_STATE.FINISHED_WITH_ERRORS && !inferenceError) || + (runningState === RUNNING_STATE.FINISHED && !inferenceResult) + ) { + return null; + } + + const rawResponse = + runningState === RUNNING_STATE.FINISHED_WITH_ERRORS + ? JSON.stringify(inferenceError?.body ?? inferenceError, null, 2) + : JSON.stringify(inferenceResult?.rawResponse, null, 2); + + return ( + <> + + + ); +}; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/common.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/common.ts new file mode 100644 index 0000000000000..ab136900c7d1e --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/common.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { InferResponse } from '../inference_base'; + +const PROBABILITY_SIG_FIGS = 3; + +export interface RawTextClassificationResponse { + inference_results: Array<{ + predicted_value: string; + prediction_probability: number; + top_classes?: Array<{ + class_name: string; + class_probability: number; + class_score: number; + }>; + }>; +} + +export type FormattedTextClassificationResponse = Array<{ + value: string; + predictionProbability: number; +}>; + +export type TextClassificationResponse = InferResponse< + FormattedTextClassificationResponse, + RawTextClassificationResponse +>; + +export function processResponse( + resp: RawTextClassificationResponse, + model: estypes.MlTrainedModelConfig, + inputText: string +): TextClassificationResponse { + const { + inference_results: [inferenceResults], + } = resp; + const labels: string[] = + // @ts-expect-error inference config is wrong + model.inference_config.text_classification?.classification_labels ?? []; + + let formattedResponse = [ + { + value: inferenceResults.predicted_value, + predictionProbability: inferenceResults.prediction_probability, + }, + ]; + + if (inferenceResults.top_classes !== undefined) { + // if num_top_classes has been specified in the model, + // base the returned results on this list + formattedResponse = inferenceResults.top_classes.map((topClass) => { + return { + value: topClass.class_name, + predictionProbability: topClass.class_probability, + }; + }); + } else if (labels.length === 2) { + // otherwise, if the config only contains two classification_labels + // we can safely assume the non-top value and return two results + formattedResponse = labels.map((value) => { + const predictionProbability = + inferenceResults.predicted_value === value + ? inferenceResults.prediction_probability + : 1 - inferenceResults.prediction_probability; + + return { + value, + predictionProbability, + }; + }); + } + + return { + response: formattedResponse + .map(({ value, predictionProbability }) => ({ + value, + predictionProbability: Number(predictionProbability.toPrecision(PROBABILITY_SIG_FIGS)), + })) + .sort((a, b) => a.predictionProbability - b.predictionProbability) + .reverse(), + rawResponse: resp, + inputText, + }; +} diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/fill_mask_inference.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/fill_mask_inference.ts new file mode 100644 index 0000000000000..bb4feaffffb38 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/fill_mask_inference.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { InferenceBase } from '../inference_base'; +import type { TextClassificationResponse, RawTextClassificationResponse } from './common'; +import { processResponse } from './common'; +import { getGeneralInputComponent } from '../text_input'; +import { getFillMaskOutputComponent } from './fill_mask_output'; +import { SUPPORTED_PYTORCH_TASKS } from '../../../../../../../common/constants/trained_models'; + +const MASK = '[MASK]'; + +export class FillMaskInference extends InferenceBase { + protected inferenceType = SUPPORTED_PYTORCH_TASKS.FILL_MASK; + + public async infer() { + try { + this.setRunning(); + const inputText = this.inputText$.getValue(); + const payload = { + docs: [{ [this.inputField]: inputText }], + ...this.getNumTopClassesConfig(), + }; + const resp = (await this.trainedModelsApi.inferTrainedModel( + this.model.model_id, + payload, + '30s' + )) as unknown as RawTextClassificationResponse; + + const processedResponse = processResponse(resp, this.model, inputText); + this.inferenceResult$.next(processedResponse); + this.setFinished(); + + return processedResponse; + } catch (error) { + this.setFinishedWithErrors(error); + throw error; + } + } + + public predictedValue() { + const result = this.inferenceResult$.value; + if (result === null) { + return ''; + } + return result.response[0]?.value + ? result.inputText.replace(MASK, result.response[0].value) + : result.inputText; + } + + public getInputComponent(): JSX.Element { + const placeholder = i18n.translate( + 'xpack.ml.trainedModels.testModelsFlyout.langIdent.inputText', + { + defaultMessage: 'Mask token: [MASK]. e.g. Paris is the [MASK] of France.', + } + ); + + return getGeneralInputComponent(this, placeholder); + } + + public getOutputComponent(): JSX.Element { + return getFillMaskOutputComponent(this); + } +} diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/fill_mask_output.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/fill_mask_output.tsx new file mode 100644 index 0000000000000..62a5a957f8a38 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/fill_mask_output.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useMemo } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import { EuiSpacer, EuiTitle } from '@elastic/eui'; + +import type { FillMaskInference } from './fill_mask_inference'; +import { TextClassificationOutput } from './text_classification_output'; + +export const getFillMaskOutputComponent = (inferrer: FillMaskInference) => ( + +); + +const FillMaskOutput: FC<{ + inferrer: FillMaskInference; +}> = ({ inferrer }) => { + const result = useObservable(inferrer.inferenceResult$); + const title = useMemo(() => inferrer.predictedValue(), []); + + if (!result) { + return null; + } + + return ( + <> + +

{title}

+
+ + + + + ); +}; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/index.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/index.ts new file mode 100644 index 0000000000000..5274333a235cd --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/index.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. + */ + +export type { TextClassificationResponse, FormattedTextClassificationResponse } from './common'; + +export { TextClassificationInference } from './text_classification_inference'; +export { getTextClassificationOutputComponent } from './text_classification_output'; + +export { ZeroShotClassificationInference } from './zero_shot_classification_inference'; +export { getZeroShotClassificationInput } from './zero_shot_classification_input'; + +export { FillMaskInference } from './fill_mask_inference'; +export { getFillMaskOutputComponent } from './fill_mask_output'; + +export { LangIdentInference } from './lang_ident_inference'; +export { getLangIdentOutputComponent } from './lang_ident_output'; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_codes.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/lang_codes.ts similarity index 100% rename from x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/lang_ident/lang_codes.ts rename to x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/lang_codes.ts diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/lang_ident_inference.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/lang_ident_inference.ts new file mode 100644 index 0000000000000..a56d4a3598a66 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/lang_ident_inference.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 { InferenceBase, InferenceType } from '../inference_base'; +import { processResponse } from './common'; +import { getGeneralInputComponent } from '../text_input'; +import { getLangIdentOutputComponent } from './lang_ident_output'; +import type { TextClassificationResponse, RawTextClassificationResponse } from './common'; + +export class LangIdentInference extends InferenceBase { + protected inferenceType: InferenceType = 'classification'; + + public async infer() { + try { + this.setRunning(); + const inputText = this.inputText$.getValue(); + const payload = { + docs: [{ [this.inputField]: inputText }], + ...this.getNumTopClassesConfig(), + }; + const resp = (await this.trainedModelsApi.inferTrainedModel( + this.model.model_id, + payload, + '30s' + )) as unknown as RawTextClassificationResponse; + + const processedResponse: TextClassificationResponse = processResponse( + resp, + this.model, + inputText + ); + this.inferenceResult$.next(processedResponse); + this.setFinished(); + + return processedResponse; + } catch (error) { + this.setFinishedWithErrors(error); + throw error; + } + } + + public getInputComponent(): JSX.Element { + return getGeneralInputComponent(this); + } + + public getOutputComponent(): JSX.Element { + return getLangIdentOutputComponent(this); + } +} diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/lang_ident_output.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/lang_ident_output.tsx new file mode 100644 index 0000000000000..a4f2a6e2884e1 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/lang_ident_output.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import { i18n } from '@kbn/i18n'; +import { EuiSpacer, EuiTitle } from '@elastic/eui'; + +import type { LangIdentInference } from './lang_ident_inference'; +import { getLanguage } from './lang_codes'; +import { getTextClassificationOutputComponent } from './text_classification_output'; + +export const getLangIdentOutputComponent = (inferrer: LangIdentInference) => ( + +); + +const LangIdentOutput: FC<{ inferrer: LangIdentInference }> = ({ inferrer }) => { + const result = useObservable(inferrer.inferenceResult$); + if (!result || result.response.length === 0) { + return null; + } + + const lang = getLanguage(result.response[0].value); + + const title = + lang !== 'unknown' + ? i18n.translate('xpack.ml.trainedModels.testModelsFlyout.langIdent.output.title', { + defaultMessage: 'This looks like {lang}', + values: { lang }, + }) + : i18n.translate('xpack.ml.trainedModels.testModelsFlyout.langIdent.output.titleUnknown', { + defaultMessage: 'Language code unknown: {code}', + values: { code: result.response[0].value }, + }); + + return ( + <> + +

{title}

+
+ + + {getTextClassificationOutputComponent(inferrer)} + + ); +}; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/text_classification_inference.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/text_classification_inference.ts new file mode 100644 index 0000000000000..33aa1c0f1c86d --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/text_classification_inference.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { InferenceBase } from '../inference_base'; +import { processResponse } from './common'; +import type { TextClassificationResponse, RawTextClassificationResponse } from './common'; +import { getGeneralInputComponent } from '../text_input'; +import { getTextClassificationOutputComponent } from './text_classification_output'; +import { SUPPORTED_PYTORCH_TASKS } from '../../../../../../../common/constants/trained_models'; + +export class TextClassificationInference extends InferenceBase { + protected inferenceType = SUPPORTED_PYTORCH_TASKS.TEXT_CLASSIFICATION; + + public async infer() { + try { + this.setRunning(); + const inputText = this.inputText$.getValue(); + const payload = { + docs: [{ [this.inputField]: inputText }], + ...this.getNumTopClassesConfig(), + }; + const resp = (await this.trainedModelsApi.inferTrainedModel( + this.model.model_id, + payload, + '30s' + )) as unknown as RawTextClassificationResponse; + + const processedResponse: TextClassificationResponse = processResponse( + resp, + this.model, + inputText + ); + this.inferenceResult$.next(processedResponse); + this.setFinished(); + + return processedResponse; + } catch (error) { + this.setFinishedWithErrors(error); + throw error; + } + } + + public getInputComponent(): JSX.Element { + return getGeneralInputComponent(this); + } + + public getOutputComponent(): JSX.Element { + return getTextClassificationOutputComponent(this); + } +} diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/text_classification_output.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/text_classification_output.tsx new file mode 100644 index 0000000000000..69ecf621510af --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/text_classification_output.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiProgress } from '@elastic/eui'; + +import type { + TextClassificationInference, + ZeroShotClassificationInference, + FillMaskInference, + LangIdentInference, +} from '.'; + +export const getTextClassificationOutputComponent = ( + inferrer: + | TextClassificationInference + | ZeroShotClassificationInference + | FillMaskInference + | LangIdentInference +) => ; + +export const TextClassificationOutput: FC<{ + inferrer: + | TextClassificationInference + | ZeroShotClassificationInference + | FillMaskInference + | LangIdentInference; +}> = ({ inferrer }) => { + const result = useObservable(inferrer.inferenceResult$); + if (!result) { + return null; + } + return ( + <> + {result.response.map(({ value, predictionProbability }) => ( + <> + + + + <> + {value} + {predictionProbability} + + + + + ))} + + ); +}; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/zero_shot_classification_inference.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/zero_shot_classification_inference.ts new file mode 100644 index 0000000000000..9a093cc44c170 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/zero_shot_classification_inference.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 { BehaviorSubject } from 'rxjs'; +import { InferenceBase } from '../inference_base'; +import { processResponse } from './common'; +import type { TextClassificationResponse, RawTextClassificationResponse } from './common'; + +import { getZeroShotClassificationInput } from './zero_shot_classification_input'; +import { getTextClassificationOutputComponent } from './text_classification_output'; +import { SUPPORTED_PYTORCH_TASKS } from '../../../../../../../common/constants/trained_models'; + +export class ZeroShotClassificationInference extends InferenceBase { + protected inferenceType = SUPPORTED_PYTORCH_TASKS.ZERO_SHOT_CLASSIFICATION; + + public labelsText$ = new BehaviorSubject(''); + + public async infer() { + try { + this.setRunning(); + const inputText = this.inputText$.getValue(); + const labelsText = this.labelsText$.value; + const inputLabels = labelsText?.split(',').map((l) => l.trim()); + const payload = { + docs: [{ [this.inputField]: inputText }], + inference_config: { + [this.inferenceType]: { + labels: inputLabels, + multi_label: false, + }, + }, + }; + const resp = (await this.trainedModelsApi.inferTrainedModel( + this.model.model_id, + payload, + '30s' + )) as unknown as RawTextClassificationResponse; + + const processedResponse: TextClassificationResponse = processResponse( + resp, + this.model, + inputText + ); + this.inferenceResult$.next(processedResponse); + this.setFinished(); + + return processedResponse; + } catch (error) { + this.setFinishedWithErrors(error); + throw error; + } + } + + public getInputComponent(): JSX.Element { + return getZeroShotClassificationInput(this); + } + + public getOutputComponent(): JSX.Element { + return getTextClassificationOutputComponent(this); + } +} diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/zero_shot_classification_input.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/zero_shot_classification_input.tsx new file mode 100644 index 0000000000000..60f06bb8baf79 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_classification/zero_shot_classification_input.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useEffect, useState } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import { i18n } from '@kbn/i18n'; + +import { EuiSpacer, EuiFieldText, EuiFormRow } from '@elastic/eui'; + +import { TextInput } from '../text_input'; +import { ZeroShotClassificationInference } from './zero_shot_classification_inference'; +import { RUNNING_STATE } from '../inference_base'; + +const ClassNameInput: FC<{ + inferrer: ZeroShotClassificationInference; +}> = ({ inferrer }) => { + const [labelsText, setLabelsText] = useState(''); + + useEffect(() => { + inferrer.labelsText$.next(labelsText); + }, [labelsText]); + + const runningState = useObservable(inferrer.runningState$); + return ( + + { + setLabelsText(e.target.value); + }} + /> + + ); +}; + +export const getZeroShotClassificationInput = ( + inferrer: ZeroShotClassificationInference, + placeholder?: string +) => ( + <> + + + + +); diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_embedding/index.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_embedding/index.ts new file mode 100644 index 0000000000000..cafa529c5e301 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_embedding/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { + TextEmbeddingResponse, + FormattedTextEmbeddingResponse, +} from './text_embedding_inference'; + +export { TextEmbeddingInference } from './text_embedding_inference'; +export { getTextEmbeddingOutputComponent } from './text_embedding_output'; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_embedding/text_embedding_inference.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_embedding/text_embedding_inference.ts new file mode 100644 index 0000000000000..3613f66d3ed93 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_embedding/text_embedding_inference.ts @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import { InferenceBase, InferResponse } from '../inference_base'; +import { getGeneralInputComponent } from '../text_input'; +import { getTextEmbeddingOutputComponent } from './text_embedding_output'; +import { SUPPORTED_PYTORCH_TASKS } from '../../../../../../../common/constants/trained_models'; + +export interface RawTextEmbeddingResponse { + inference_results: [{ predicted_value: number[] }]; +} + +export interface FormattedTextEmbeddingResponse { + predictedValue: number[]; +} + +export type TextEmbeddingResponse = InferResponse< + FormattedTextEmbeddingResponse, + RawTextEmbeddingResponse +>; + +export class TextEmbeddingInference extends InferenceBase { + protected inferenceType = SUPPORTED_PYTORCH_TASKS.TEXT_EMBEDDING; + + public async infer() { + try { + this.setRunning(); + const inputText = this.inputText$.getValue(); + const payload = { + docs: [{ [this.inputField]: inputText }], + }; + const resp = (await this.trainedModelsApi.inferTrainedModel( + this.model.model_id, + payload, + '30s' + )) as unknown as RawTextEmbeddingResponse; + + const processedResponse: TextEmbeddingResponse = processResponse(resp, this.model, inputText); + this.inferenceResult$.next(processedResponse); + this.setFinished(); + + return processedResponse; + } catch (error) { + this.setFinishedWithErrors(error); + throw error; + } + } + + public getInputComponent(): JSX.Element { + return getGeneralInputComponent(this); + } + + public getOutputComponent(): JSX.Element { + return getTextEmbeddingOutputComponent(this); + } +} + +function processResponse( + resp: RawTextEmbeddingResponse, + model: estypes.MlTrainedModelConfig, + inputText: string +) { + const predictedValue = resp.inference_results[0].predicted_value; + return { response: { predictedValue }, rawResponse: resp, inputText }; +} diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_embedding/text_embedding_output.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_embedding/text_embedding_output.tsx new file mode 100644 index 0000000000000..bb60140d39e68 --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_embedding/text_embedding_output.tsx @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiTextArea, EuiCopy, EuiButton } from '@elastic/eui'; + +import type { TextEmbeddingInference } from './text_embedding_inference'; + +export const getTextEmbeddingOutputComponent = (inferrer: TextEmbeddingInference) => ( + +); + +const TextEmbeddingOutput: FC<{ + inferrer: TextEmbeddingInference; +}> = ({ inferrer }) => { + const result = useObservable(inferrer.inferenceResult$); + if (!result) { + return null; + } + + const value = result.response.predictedValue.toString(); + return ( + <> + + + {(copy) => ( + + + + )} + + + ); +}; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_input.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_input.tsx new file mode 100644 index 0000000000000..a154bf97ef56b --- /dev/null +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/models/text_input.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useState, useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; +import useObservable from 'react-use/lib/useObservable'; +import { EuiTextArea } from '@elastic/eui'; +import { RUNNING_STATE } from './inference_base'; +import type { InferrerType } from '.'; + +export const TextInput: FC<{ + placeholder?: string; + inferrer: InferrerType; +}> = ({ placeholder, inferrer }) => { + const [inputText, setInputText] = useState(''); + + useEffect(() => { + inferrer.inputText$.next(inputText); + }, [inputText]); + + const runningState = useObservable(inferrer.runningState$); + + return ( + { + setInputText(e.target.value); + }} + /> + ); +}; + +export const getGeneralInputComponent = (inferrer: InferrerType, placeholder?: string) => ( + +); diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/selected_model.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/selected_model.tsx index 29dbb855b0084..816166c5cbcbf 100644 --- a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/selected_model.tsx +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/selected_model.tsx @@ -8,10 +8,16 @@ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import React, { FC } from 'react'; -import { NerOutput, NerInference } from './models/ner'; -import type { FormattedNerResp } from './models/ner'; -import { LangIdentOutput, LangIdentInference } from './models/lang_ident'; -import type { FormattedLangIdentResp } from './models/lang_ident'; +import { NerInference } from './models/ner'; + +import { + TextClassificationInference, + FillMaskInference, + ZeroShotClassificationInference, + LangIdentInference, +} from './models/text_classification'; + +import { TextEmbeddingInference } from './models/text_embedding'; import { TRAINED_MODEL_TYPE, @@ -31,26 +37,37 @@ export const SelectedModel: FC = ({ model }) => { return null; } - if ( - model.model_type === TRAINED_MODEL_TYPE.PYTORCH && - Object.keys(model.inference_config)[0] === SUPPORTED_PYTORCH_TASKS.NER - ) { - const inferrer = new NerInference(trainedModels, model); - return ( - } - /> - ); + if (model.model_type === TRAINED_MODEL_TYPE.PYTORCH) { + if (Object.keys(model.inference_config)[0] === SUPPORTED_PYTORCH_TASKS.NER) { + const inferrer = new NerInference(trainedModels, model); + return ; + } + + if (Object.keys(model.inference_config)[0] === SUPPORTED_PYTORCH_TASKS.TEXT_CLASSIFICATION) { + const inferrer = new TextClassificationInference(trainedModels, model); + return ; + } + + if ( + Object.keys(model.inference_config)[0] === SUPPORTED_PYTORCH_TASKS.ZERO_SHOT_CLASSIFICATION + ) { + const inferrer = new ZeroShotClassificationInference(trainedModels, model); + return ; + } + + if (Object.keys(model.inference_config)[0] === SUPPORTED_PYTORCH_TASKS.TEXT_EMBEDDING) { + const inferrer = new TextEmbeddingInference(trainedModels, model); + return ; + } + + if (Object.keys(model.inference_config)[0] === SUPPORTED_PYTORCH_TASKS.FILL_MASK) { + const inferrer = new FillMaskInference(trainedModels, model); + return ; + } } if (model.model_type === TRAINED_MODEL_TYPE.LANG_IDENT) { const inferrer = new LangIdentInference(trainedModels, model); - return ( - } - /> - ); + return ; } return null; diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/utils.ts b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/utils.ts index 99a0f76891c30..3ac6ec77f576a 100644 --- a/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/utils.ts +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/test_models/utils.ts @@ -5,26 +5,37 @@ * 2.0. */ -import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { TRAINED_MODEL_TYPE, + DEPLOYMENT_STATE, SUPPORTED_PYTORCH_TASKS, } from '../../../../../common/constants/trained_models'; import type { SupportedPytorchTasksType } from '../../../../../common/constants/trained_models'; +import type { ModelItem } from '../models_list'; + +import { isPopulatedObject } from '../../../../../common'; const PYTORCH_TYPES = Object.values(SUPPORTED_PYTORCH_TASKS); -export function isTestable(model: estypes.MlTrainedModelConfig) { +export function isTestable(modelItem: ModelItem) { if ( - model.model_type === TRAINED_MODEL_TYPE.PYTORCH && - PYTORCH_TYPES.includes(Object.keys(model.inference_config)[0] as SupportedPytorchTasksType) + modelItem.model_type === TRAINED_MODEL_TYPE.PYTORCH && + PYTORCH_TYPES.includes(Object.keys(modelItem.inference_config)[0] as SupportedPytorchTasksType) ) { return true; } - if (model.model_type === TRAINED_MODEL_TYPE.LANG_IDENT) { + if (modelItem.model_type === TRAINED_MODEL_TYPE.LANG_IDENT) { return true; } return false; } + +export function isTestEnabled(modelItem: ModelItem) { + return ( + isPopulatedObject(modelItem.stats?.deployment_stats) === false || + (isPopulatedObject(modelItem.stats?.deployment_stats) && + modelItem.stats?.deployment_stats?.state === DEPLOYMENT_STATE.STARTED) + ); +} diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.tsx index 9f31f5777f9de..85350629263e4 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.tsx @@ -11,6 +11,7 @@ import { Observable } from 'rxjs'; import { FormattedMessage } from '@kbn/i18n-react'; import { throttle } from 'lodash'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; +import { useEmbeddableExecutionContext } from '../common/use_embeddable_execution_context'; import { useAnomalyChartsInputResolver } from './use_anomaly_charts_input_resolver'; import type { IAnomalyChartsEmbeddable } from './anomaly_charts_embeddable'; import type { @@ -27,6 +28,7 @@ import { ANOMALY_THRESHOLD } from '../../../common'; import { TimeBuckets } from '../../application/util/time_buckets'; import { EXPLORER_ENTITY_FIELD_SELECTION_TRIGGER } from '../../ui_actions/triggers'; import { MlLocatorParams } from '../../../common/types/locator'; +import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '..'; const RESIZE_THROTTLE_TIME_MS = 500; @@ -55,6 +57,13 @@ export const EmbeddableAnomalyChartsContainer: FC { + useEmbeddableExecutionContext( + services[0].executionContext, + embeddableInput, + ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, + id + ); + const [chartWidth, setChartWidth] = useState(0); const [severity, setSeverity] = useState( optionValueToThreshold( diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container.tsx index 06c400481491a..c354057d971bb 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container.tsx @@ -11,6 +11,7 @@ import { Observable } from 'rxjs'; import { CoreStart } from '@kbn/core/public'; import { FormattedMessage } from '@kbn/i18n-react'; +import { useEmbeddableExecutionContext } from '../common/use_embeddable_execution_context'; import { IAnomalySwimlaneEmbeddable } from './anomaly_swimlane_embeddable'; import { useSwimlaneInputResolver } from './swimlane_input_resolver'; import { SwimlaneType } from '../../application/explorer/explorer_constants'; @@ -22,6 +23,7 @@ import { AppStateSelectedCells } from '../../application/explorer/explorer_utils import { MlDependencies } from '../../application/app'; import { SWIM_LANE_SELECTION_TRIGGER } from '../../ui_actions'; import { + ANOMALY_SWIMLANE_EMBEDDABLE_TYPE, AnomalySwimlaneEmbeddableInput, AnomalySwimlaneEmbeddableOutput, AnomalySwimlaneServices, @@ -52,6 +54,13 @@ export const EmbeddableSwimLaneContainer: FC = ( onLoading, onError, }) => { + useEmbeddableExecutionContext( + services[0].executionContext, + embeddableInput, + ANOMALY_SWIMLANE_EMBEDDABLE_TYPE, + id + ); + const [chartWidth, setChartWidth] = useState(0); const [fromPage, setFromPage] = useState(1); diff --git a/x-pack/plugins/ml/public/embeddables/common/use_embeddable_execution_context.ts b/x-pack/plugins/ml/public/embeddables/common/use_embeddable_execution_context.ts new file mode 100644 index 0000000000000..68306c54c8590 --- /dev/null +++ b/x-pack/plugins/ml/public/embeddables/common/use_embeddable_execution_context.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import useObservable from 'react-use/lib/useObservable'; +import { map } from 'rxjs/operators'; +import { KibanaExecutionContext } from '@kbn/core/types'; +import { useMemo } from 'react'; +import { useExecutionContext } from '@kbn/kibana-react-plugin/public'; +import type { Observable } from 'rxjs'; +import type { EmbeddableInput } from '@kbn/embeddable-plugin/common'; +import { ExecutionContextStart } from '@kbn/core/public'; + +/** + * Use execution context for ML embeddables. + * @param executionContext + * @param embeddableInput$ + * @param embeddableType + * @param id + */ +export function useEmbeddableExecutionContext( + executionContext: ExecutionContextStart, + embeddableInput$: Observable, + embeddableType: string, + id: string +) { + const parentExecutionContext = useObservable( + embeddableInput$.pipe(map((v) => v.executionContext)) + ); + + const embeddableExecutionContext: KibanaExecutionContext = useMemo(() => { + const child: KibanaExecutionContext = { + type: 'visualization', + name: embeddableType, + id, + }; + + return { + ...parentExecutionContext, + child, + }; + }, [parentExecutionContext, id]); + + useExecutionContext(executionContext, embeddableExecutionContext); +} diff --git a/x-pack/plugins/ml/public/locator/ml_locator.ts b/x-pack/plugins/ml/public/locator/ml_locator.ts index dcc31591208cb..b1ea2549c3347 100644 --- a/x-pack/plugins/ml/public/locator/ml_locator.ts +++ b/x-pack/plugins/ml/public/locator/ml_locator.ts @@ -77,6 +77,7 @@ export class MlLocatorDefinition implements LocatorDefinition { path = formatTrainedModelsNodesManagementUrl('', params.pageState); break; case ML_PAGES.ANOMALY_DETECTION_CREATE_JOB: + case ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_RECOGNIZER: case ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_ADVANCED: case ML_PAGES.DATA_VISUALIZER: case ML_PAGES.DATA_VISUALIZER_FILE: diff --git a/x-pack/plugins/ml/public/maps/anomaly_layer_wizard_factory.tsx b/x-pack/plugins/ml/public/maps/anomaly_layer_wizard_factory.tsx index b40a08fadd128..6b9ee6c2e6332 100644 --- a/x-pack/plugins/ml/public/maps/anomaly_layer_wizard_factory.tsx +++ b/x-pack/plugins/ml/public/maps/anomaly_layer_wizard_factory.tsx @@ -11,13 +11,13 @@ import type { StartServicesAccessor } from '@kbn/core/public'; import { LocatorPublic } from '@kbn/share-plugin/common'; import { SerializableRecord } from '@kbn/utility-types'; import type { LayerWizard, RenderWizardArguments } from '@kbn/maps-plugin/public'; -import { FIELD_ORIGIN, LAYER_TYPE, STYLE_TYPE } from '@kbn/maps-plugin/common'; +import { LAYER_TYPE } from '@kbn/maps-plugin/common'; import { VectorLayerDescriptor, VectorStylePropertiesDescriptor, } from '@kbn/maps-plugin/common/descriptor_types'; -import { SEVERITY_COLOR_RAMP } from '../../common'; import { ML_APP_LOCATOR, ML_PAGES } from '../../common/constants/locator'; +import { CUSTOM_COLOR_RAMP } from './util'; import { CreateAnomalySourceEditor } from './create_anomaly_source_editor'; import { AnomalySource, AnomalySourceDescriptor } from './anomaly_source'; @@ -26,17 +26,6 @@ import type { MlPluginStart, MlStartDependencies } from '../plugin'; import type { MlApiServices } from '../application/services/ml_api_service'; export const ML_ANOMALY = 'ML_ANOMALIES'; -export const CUSTOM_COLOR_RAMP = { - type: STYLE_TYPE.DYNAMIC, - options: { - customColorRamp: SEVERITY_COLOR_RAMP, - field: { - name: 'record_score', - origin: FIELD_ORIGIN.SOURCE, - }, - useCustomColorRamp: true, - }, -}; export class AnomalyLayerWizardFactory { public readonly type = ML_ANOMALY; diff --git a/x-pack/plugins/ml/public/maps/util.ts b/x-pack/plugins/ml/public/maps/util.ts index c1cb3f9efaead..88e9994d303a9 100644 --- a/x-pack/plugins/ml/public/maps/util.ts +++ b/x-pack/plugins/ml/public/maps/util.ts @@ -7,14 +7,19 @@ import { FeatureCollection, Feature, Geometry } from 'geojson'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { htmlIdGenerator } from '@elastic/eui'; +import { FIELD_ORIGIN, STYLE_TYPE } from '@kbn/maps-plugin/common'; import { fromKueryExpression, luceneStringToDsl, toElasticsearchQuery } from '@kbn/es-query'; import { ESSearchResponse } from '@kbn/core/types/elasticsearch'; import { VectorSourceRequestMeta } from '@kbn/maps-plugin/common'; +import { LAYER_TYPE } from '@kbn/maps-plugin/common'; +import { SEVERITY_COLOR_RAMP } from '../../common'; import { formatHumanReadableDateTimeSeconds } from '../../common/util/date_utils'; import type { MlApiServices } from '../application/services/ml_api_service'; import { MLAnomalyDoc } from '../../common/types/anomalies'; import { SEARCH_QUERY_LANGUAGE } from '../../common/constants/search'; import { getIndexPattern } from '../application/explorer/reducers/explorer_reducer/get_index_pattern'; +import { AnomalySource } from './anomaly_source'; export const ML_ANOMALY_LAYERS = { TYPICAL: 'typical', @@ -22,6 +27,57 @@ export const ML_ANOMALY_LAYERS = { TYPICAL_TO_ACTUAL: 'typical to actual', } as const; +export const CUSTOM_COLOR_RAMP = { + type: STYLE_TYPE.DYNAMIC, + options: { + customColorRamp: SEVERITY_COLOR_RAMP, + field: { + name: 'record_score', + origin: FIELD_ORIGIN.SOURCE, + }, + useCustomColorRamp: true, + }, +}; + +export const ACTUAL_STYLE = { + type: 'VECTOR', + properties: { + fillColor: CUSTOM_COLOR_RAMP, + lineColor: CUSTOM_COLOR_RAMP, + }, + isTimeAware: false, +}; + +export const TYPICAL_STYLE = { + type: 'VECTOR', + properties: { + fillColor: { + type: 'STATIC', + options: { + color: '#98A2B2', + }, + }, + lineColor: { + type: 'STATIC', + options: { + color: '#fff', + }, + }, + lineWidth: { + type: 'STATIC', + options: { + size: 2, + }, + }, + iconSize: { + type: 'STATIC', + options: { + size: 6, + }, + }, + }, +}; + export type MlAnomalyLayersType = typeof ML_ANOMALY_LAYERS[keyof typeof ML_ANOMALY_LAYERS]; // Must reverse coordinates here. Map expects [lon, lat] - anomalies are stored as [lat, lon] for lat_lon jobs @@ -32,6 +88,27 @@ function getCoordinates(latLonString: string): number[] { .reverse(); } +export function getInitialAnomaliesLayers(jobId: string) { + const initialLayers = []; + for (const layer in ML_ANOMALY_LAYERS) { + if (ML_ANOMALY_LAYERS.hasOwnProperty(layer)) { + initialLayers.push({ + id: htmlIdGenerator()(), + type: LAYER_TYPE.GEOJSON_VECTOR, + sourceDescriptor: AnomalySource.createDescriptor({ + jobId, + typicalActual: ML_ANOMALY_LAYERS[layer as keyof typeof ML_ANOMALY_LAYERS], + }), + style: + ML_ANOMALY_LAYERS[layer as keyof typeof ML_ANOMALY_LAYERS] === ML_ANOMALY_LAYERS.TYPICAL + ? TYPICAL_STYLE + : ACTUAL_STYLE, + }); + } + } + return initialLayers; +} + export async function getResultsForJobId( mlResultsService: MlApiServices['results'], jobId: string, diff --git a/x-pack/plugins/ml/readme.md b/x-pack/plugins/ml/readme.md index 7bd1a1a221edd..9b155e5f7696c 100644 --- a/x-pack/plugins/ml/readme.md +++ b/x-pack/plugins/ml/readme.md @@ -104,42 +104,49 @@ Run the following commands from the `x-pack` directory and use separate terminal for test server and test runner. The test server command starts an Elasticsearch and Kibana instance that the tests will be run against. -1. Functional UI tests with `Trial` license (default config): - - node scripts/functional_tests_server.js - node scripts/functional_test_runner.js --include-tag mlqa - - ML functional `Trial` license tests are located in `x-pack/test/functional/apps/ml`. - +Functional tests are broken up into independent groups with their own configuration. +Test server and runner need to be pointed to the configuration to run. The basic +commands are + + node scripts/functional_tests_server.js --config PATH_TO_CONFIG + node scripts/functional_test_runner.js --config PATH_TO_CONFIG + +With PATH_TO_CONFIG and other options as follows. + +1. Functional UI tests with `Trial` license: + + Group | PATH_TO_CONFIG + ----- | -------------- + anomaly detection | `test/functional/apps/ml/anomaly_detection/config.ts` + data frame analytics | `test/functional/apps/ml/anomaly_detection/config.ts` + data visualizer | `test/functional/apps/ml/data_frame_analytics/config.ts` + permissions | `test/functional/apps/ml/permissions/config.ts` + stack management jobs | `test/functional/apps/ml/stack_management_jobs/config.ts` + short tests | `test/functional/apps/ml/short_tests/config.ts` + + The `short tests` group contains tests for page navigation, model management, + feature controls, settings and embeddables. Test files for each group are located + in the directory of their copnfiguration file. + 1. Functional UI tests with `Basic` license: - node scripts/functional_tests_server.js --config test/functional_basic/config.ts - node scripts/functional_test_runner.js --config test/functional_basic/config.ts --include-tag mlqa - - ML functional `Basic` license tests are located in `x-pack/test/functional_basic/apps/ml`. + - PATH_TO_CONFIG: `test/functional_basic/config.ts` + - Add `--include-tag ml` to the test runner command + - Tests are located in `x-pack/test/functional_basic/apps/ml` 1. API integration tests with `Trial` license: - node scripts/functional_tests_server.js - node scripts/functional_test_runner.js --config test/api_integration/config.ts --include-tag mlqa - - ML API integration `Trial` license tests are located in `x-pack/test/api_integration/apis/ml`. - -1. API integration tests with `Basic` license: - - node scripts/functional_tests_server.js --config test/api_integration_basic/config.ts - node scripts/functional_test_runner.js --config test/api_integration_basic/config.ts --include-tag mlqa - - ML API integration `Basic` license tests are located in `x-pack/test/api_integration_basic/apis/ml`. + - PATH_TO_CONFIG: `test/api_integration/config.ts` + - Add `--include-tag ml` to the test runner command + - Tests are located in `x-pack/test/api_integration/apis/ml` 1. Accessibility tests: We maintain a suite of accessibility tests (you may see them referred to elsewhere as `a11y` tests). These tests render each of our pages and ensure that the inputs and other elements contain the attributes necessary to ensure all users are able to make use of ML (for example, users relying on screen readers). - node scripts/functional_tests_server --config test/accessibility/config.ts - node scripts/functional_test_runner.js --config test/accessibility/config.ts --grep=ml - - ML accessibility tests are located in `x-pack/test/accessibility/apps`. + - PATH_TO_CONFIG: `test/accessibility/config.ts` + - Add `--grep=ml` to the test runner command + - Tests are located in `x-pack/test/accessibility/apps` ## Generating docs screenshots @@ -151,7 +158,7 @@ for test server and test runner. The test server command starts an Elasticsearch and Kibana instance that the tests will be run against. node scripts/functional_tests_server.js --config test/screenshot_creation/config.ts - node scripts/functional_test_runner.js --config test/screenshot_creation/config.ts --include-tag mlqa + node scripts/functional_test_runner.js --config test/screenshot_creation/config.ts --include-tag ml The generated screenshots are stored in `x-pack/test/functional/screenshots/session/ml_docs`. ML screenshot generation tests are located in `x-pack/test/screenshot_creation/apps/ml_docs`. diff --git a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts index f322f09356c90..808c77d4d6ea5 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts @@ -496,7 +496,28 @@ export function getMlClient( }, async inferTrainedModelDeployment(...p: Parameters) { await modelIdsCheck(p); - return mlClient.inferTrainedModelDeployment(...p); + // Temporary workaround for the incorrect inferTrainedModelDeployment function in the esclient + if ( + // @ts-expect-error TS complains it's always false + p.length === 0 || + p[0] === undefined + ) { + // Temporary generic error message. This should never be triggered + // but is added for type correctness below + throw new Error('Incorrect arguments supplied'); + } + // @ts-expect-error body doesn't exist in the type + const { model_id: id, body, query: querystring } = p[0]; + + return client.asInternalUser.transport.request( + { + method: 'POST', + path: `/_ml/trained_models/${id}/_infer`, + body, + querystring, + }, + p[1] + ); }, async info(...p: Parameters) { return mlClient.info(...p); diff --git a/x-pack/plugins/ml/server/routes/apidoc.json b/x-pack/plugins/ml/server/routes/apidoc.json index ac09aee7fcbb9..e36e51aded4ba 100644 --- a/x-pack/plugins/ml/server/routes/apidoc.json +++ b/x-pack/plugins/ml/server/routes/apidoc.json @@ -172,7 +172,6 @@ "PutTrainedModel", "DeleteTrainedModel", "InferTrainedModelDeployment", - "IngestPipelineSimulate", "Alerting", "PreviewAlert" diff --git a/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts b/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts index 1b9a865dcfca9..2273bb48c4f32 100644 --- a/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts @@ -31,18 +31,8 @@ export const putTrainedModelQuerySchema = schema.object({ defer_definition_decompression: schema.maybe(schema.boolean()), }); -export const pipelineSchema = schema.object({ - pipeline: schema.object({ - description: schema.maybe(schema.string()), - processors: schema.arrayOf(schema.recordOf(schema.string(), schema.any())), - version: schema.maybe(schema.number()), - on_failure: schema.maybe(schema.arrayOf(schema.recordOf(schema.string(), schema.any()))), - }), - docs: schema.arrayOf(schema.recordOf(schema.string(), schema.any())), - verbose: schema.maybe(schema.boolean()), -}); - export const inferTrainedModelQuery = schema.object({ timeout: schema.maybe(schema.string()) }); export const inferTrainedModelBody = schema.object({ docs: schema.any(), + inference_config: schema.maybe(schema.any()), }); diff --git a/x-pack/plugins/ml/server/routes/trained_models.ts b/x-pack/plugins/ml/server/routes/trained_models.ts index 27a062b45767c..4c8893b3144ea 100644 --- a/x-pack/plugins/ml/server/routes/trained_models.ts +++ b/x-pack/plugins/ml/server/routes/trained_models.ts @@ -5,7 +5,6 @@ * 2.0. */ -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { schema } from '@kbn/config-schema'; import { RouteInitialization } from '../types'; import { wrapError } from '../client/error_wrapper'; @@ -14,7 +13,6 @@ import { modelIdSchema, optionalModelIdSchema, putTrainedModelQuerySchema, - pipelineSchema, inferTrainedModelQuery, inferTrainedModelBody, } from './schemas/inference_schema'; @@ -381,45 +379,13 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) const { modelId } = request.params; const body = await mlClient.inferTrainedModelDeployment({ model_id: modelId, - docs: request.body.docs, - ...(request.query.timeout ? { timeout: request.query.timeout } : {}), - }); - return response.ok({ - body, - }); - } catch (e) { - return response.customError(wrapError(e)); - } - }) - ); - - /** - * @apiGroup TrainedModels - * - * @api {post} /api/ml/trained_models/ingest_pipeline_simulate Ingest pipeline simulate - * @apiName IngestPipelineSimulate - * @apiDescription Simulates an ingest pipeline call using supplied documents - */ - router.post( - { - path: '/api/ml/trained_models/ingest_pipeline_simulate', - validate: { - body: pipelineSchema, - }, - options: { - tags: ['access:ml:canStartStopTrainedModels'], - }, - }, - routeGuard.fullLicenseAPIGuard(async ({ client, request, response }) => { - try { - const { pipeline, docs, verbose } = request.body; - - const body = await client.asCurrentUser.ingest.simulate({ - verbose, body: { - pipeline, - docs: docs as estypes.IngestSimulateDocument[], + docs: request.body.docs, + ...(request.body.inference_config + ? { inference_config: request.body.inference_config } + : {}), }, + ...(request.query.timeout ? { timeout: request.query.timeout } : {}), }); return response.ok({ body, diff --git a/x-pack/plugins/monitoring/common/http_api/elasticsearch/index.ts b/x-pack/plugins/monitoring/common/http_api/elasticsearch/index.ts new file mode 100644 index 0000000000000..2fa6180cc34a5 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/elasticsearch/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './post_elasticsearch_ccr'; +export * from './post_elasticsearch_ccr_shard'; +export * from './post_elasticsearch_index_detail'; +export * from './post_elasticsearch_indices'; +export * from './post_elasticsearch_ml_jobs'; +export * from './post_elasticsearch_node_detail'; +export * from './post_elasticsearch_nodes'; +export * from './post_elasticsearch_overview'; diff --git a/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_ccr.ts b/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_ccr.ts new file mode 100644 index 0000000000000..db74be0955c06 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_ccr.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as rt from 'io-ts'; +import { clusterUuidRT, ccsRT, timeRangeRT } from '../shared'; + +export const postElasticsearchCcrRequestParamsRT = rt.type({ + clusterUuid: clusterUuidRT, +}); + +export const postElasticsearchCcrRequestPayloadRT = rt.intersection([ + rt.partial({ + ccs: ccsRT, + }), + rt.type({ + timeRange: timeRangeRT, + }), +]); + +export type PostElasticsearchCcrRequestPayload = rt.TypeOf< + typeof postElasticsearchCcrRequestPayloadRT +>; + +export const postElasticsearchCcrResponsePayloadRT = rt.type({ + // TODO: add payload entries +}); diff --git a/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_ccr_shard.ts b/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_ccr_shard.ts new file mode 100644 index 0000000000000..64f75658420f7 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_ccr_shard.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as rt from 'io-ts'; +import { clusterUuidRT, ccsRT, timeRangeRT } from '../shared'; + +export const postElasticsearchCcrShardRequestParamsRT = rt.type({ + clusterUuid: clusterUuidRT, + index: rt.string, + shardId: rt.string, +}); + +export const postElasticsearchCcrShardRequestPayloadRT = rt.intersection([ + rt.partial({ + ccs: ccsRT, + }), + rt.type({ + timeRange: timeRangeRT, + }), +]); + +export type PostElasticsearchCcrShardRequestPayload = rt.TypeOf< + typeof postElasticsearchCcrShardRequestPayloadRT +>; + +export const postElasticsearchCcrShardResponsePayloadRT = rt.type({ + // TODO: add payload entries +}); diff --git a/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_index_detail.ts b/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_index_detail.ts new file mode 100644 index 0000000000000..b9bb491c0c387 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_index_detail.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as rt from 'io-ts'; +import { ccsRT, clusterUuidRT, createLiteralValueFromUndefinedRT, timeRangeRT } from '../shared'; + +export const postElasticsearchIndexDetailRequestParamsRT = rt.type({ + clusterUuid: clusterUuidRT, + id: rt.string, +}); + +export const postElasticsearchIndexDetailRequestPayloadRT = rt.intersection([ + rt.partial({ + ccs: ccsRT, + }), + rt.type({ + is_advanced: rt.union([rt.boolean, createLiteralValueFromUndefinedRT(false)]), + timeRange: timeRangeRT, + }), +]); + +export type PostElasticsearchIndexDetailRequestPayload = rt.TypeOf< + typeof postElasticsearchIndexDetailRequestPayloadRT +>; + +export const postElasticsearchIndexDetailResponsePayloadRT = rt.type({ + // TODO: add payload entries +}); diff --git a/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_indices.ts b/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_indices.ts new file mode 100644 index 0000000000000..57a4b953f15dc --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_indices.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 * as rt from 'io-ts'; +import { + booleanFromStringRT, + ccsRT, + clusterUuidRT, + createLiteralValueFromUndefinedRT, + timeRangeRT, +} from '../shared'; + +export const postElasticsearchIndicesRequestParamsRT = rt.type({ + clusterUuid: clusterUuidRT, +}); + +export const postElasticsearchIndicesRequestPayloadRT = rt.intersection([ + rt.partial({ + ccs: ccsRT, + }), + rt.type({ + is_advanced: rt.union([rt.boolean, createLiteralValueFromUndefinedRT(false)]), + timeRange: timeRangeRT, + }), +]); + +export type PostElasticsearchIndicesRequestPayload = rt.TypeOf< + typeof postElasticsearchIndicesRequestPayloadRT +>; + +export const postElasticsearchIndicesRequestQueryRT = rt.type({ + show_system_indices: booleanFromStringRT, +}); + +export const postElasticsearchIndicesResponsePayloadRT = rt.type({ + // TODO: add payload entries +}); diff --git a/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_ml_jobs.ts b/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_ml_jobs.ts new file mode 100644 index 0000000000000..3cbe83fa5d27a --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_ml_jobs.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as rt from 'io-ts'; +import { clusterUuidRT, ccsRT, timeRangeRT } from '../shared'; + +export const postElasticsearchMlJobsRequestParamsRT = rt.type({ + clusterUuid: clusterUuidRT, +}); + +export const postElasticsearchMlJobsRequestPayloadRT = rt.intersection([ + rt.partial({ + ccs: ccsRT, + }), + rt.type({ + timeRange: timeRangeRT, + }), +]); + +export type PostElasticsearchMlJobsRequestPayload = rt.TypeOf< + typeof postElasticsearchMlJobsRequestPayloadRT +>; + +export const postElasticsearchMlJobsResponsePayloadRT = rt.type({ + // TODO: add payload entries +}); diff --git a/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_node_detail.ts b/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_node_detail.ts new file mode 100644 index 0000000000000..b29d9fb7b8daf --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_node_detail.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 * as rt from 'io-ts'; +import { ccsRT, clusterUuidRT, createLiteralValueFromUndefinedRT, timeRangeRT } from '../shared'; + +export const postElasticsearchNodeDetailRequestParamsRT = rt.type({ + clusterUuid: clusterUuidRT, + nodeUuid: rt.string, +}); + +export const postElasticsearchNodeDetailRequestPayloadRT = rt.intersection([ + rt.partial({ + ccs: ccsRT, + showSystemIndices: rt.boolean, // show/hide system indices in shard allocation table + }), + rt.type({ + is_advanced: rt.union([rt.boolean, createLiteralValueFromUndefinedRT(false)]), + timeRange: timeRangeRT, + }), +]); + +export type PostElasticsearchNodeDetailRequestPayload = rt.TypeOf< + typeof postElasticsearchNodeDetailRequestPayloadRT +>; + +export const postElasticsearchNodeDetailResponsePayloadRT = rt.type({ + // TODO: add payload entries +}); diff --git a/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_nodes.ts b/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_nodes.ts new file mode 100644 index 0000000000000..bba6525d9a2c8 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_nodes.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 * as rt from 'io-ts'; +import { clusterUuidRT, ccsRT, timeRangeRT, paginationRT, sortingRT } from '../shared'; + +export const postElasticsearchNodesRequestParamsRT = rt.type({ + clusterUuid: clusterUuidRT, +}); + +export const postElasticsearchNodesRequestPayloadRT = rt.intersection([ + rt.partial({ + ccs: ccsRT, + queryText: rt.string, + sort: sortingRT, + }), + rt.type({ + timeRange: timeRangeRT, + pagination: paginationRT, + }), +]); + +export type PostElasticsearchNodesRequestPayload = rt.TypeOf< + typeof postElasticsearchNodesRequestPayloadRT +>; + +export const postElasticsearchNodesResponsePayloadRT = rt.type({ + // TODO: add payload entries +}); diff --git a/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_overview.ts b/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_overview.ts new file mode 100644 index 0000000000000..da82f7cfedbf0 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/elasticsearch/post_elasticsearch_overview.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as rt from 'io-ts'; +import { clusterUuidRT, ccsRT, timeRangeRT } from '../shared'; + +export const postElasticsearchOverviewRequestParamsRT = rt.type({ + clusterUuid: clusterUuidRT, +}); + +export const postElasticsearchOverviewRequestPayloadRT = rt.intersection([ + rt.partial({ + ccs: ccsRT, + }), + rt.type({ + timeRange: timeRangeRT, + }), +]); + +export type PostElasticsearchOverviewRequestPayload = rt.TypeOf< + typeof postElasticsearchOverviewRequestPayloadRT +>; + +export const postElasticsearchOverviewResponsePayloadRT = rt.type({ + // TODO: add payload entries +}); diff --git a/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/get_elasticsearch_settings_cluster.ts b/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/get_elasticsearch_settings_cluster.ts new file mode 100644 index 0000000000000..e68a2920155a5 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/get_elasticsearch_settings_cluster.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 * as rt from 'io-ts'; + +export const getElasticsearchSettingsClusterResponsePayloadRT = rt.type({ + // TODO: add payload entries +}); diff --git a/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/get_elasticsearch_settings_nodes.ts b/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/get_elasticsearch_settings_nodes.ts new file mode 100644 index 0000000000000..2621683b85d97 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/get_elasticsearch_settings_nodes.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 * as rt from 'io-ts'; + +export const getElasticsearchSettingsNodesResponsePayloadRT = rt.type({ + // TODO: add payload entries +}); diff --git a/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/index.ts b/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/index.ts new file mode 100644 index 0000000000000..3268982b69b9a --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/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_elasticsearch_settings_cluster'; +export * from './get_elasticsearch_settings_nodes'; +export * from './post_elasticsearch_settings_internal_monitoring'; +export * from './put_elasticsearch_settings_collection_enabled'; +export * from './put_elasticsearch_settings_collection_interval'; diff --git a/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/post_elasticsearch_settings_internal_monitoring.ts b/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/post_elasticsearch_settings_internal_monitoring.ts new file mode 100644 index 0000000000000..54b65d4c1c527 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/post_elasticsearch_settings_internal_monitoring.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 * as rt from 'io-ts'; +import { ccsRT } from '../shared'; + +export const postElasticsearchSettingsInternalMonitoringRequestPayloadRT = rt.partial({ + ccs: ccsRT, +}); + +export type PostElasticsearchSettingsInternalMonitoringRequestPayload = rt.TypeOf< + typeof postElasticsearchSettingsInternalMonitoringRequestPayloadRT +>; + +export const postElasticsearchSettingsInternalMonitoringResponsePayloadRT = rt.type({ + body: rt.type({ + legacy_indices: rt.number, + mb_indices: rt.number, + }), +}); diff --git a/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/put_elasticsearch_settings_collection_enabled.ts b/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/put_elasticsearch_settings_collection_enabled.ts new file mode 100644 index 0000000000000..f65fdaddc4548 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/put_elasticsearch_settings_collection_enabled.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 * as rt from 'io-ts'; + +export const putElasticsearchSettingsCollectionEnabledResponsePayloadRT = rt.type({ + // TODO: add payload entries +}); diff --git a/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/put_elasticsearch_settings_collection_interval.ts b/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/put_elasticsearch_settings_collection_interval.ts new file mode 100644 index 0000000000000..da4905c044fe0 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/elasticsearch_settings/put_elasticsearch_settings_collection_interval.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 * as rt from 'io-ts'; + +export const putElasticsearchSettingsCollectionIntervalResponsePayloadRT = rt.type({ + // TODO: add payload entries +}); diff --git a/x-pack/plugins/monitoring/common/http_api/shared/index.ts b/x-pack/plugins/monitoring/common/http_api/shared/index.ts index db7d3dce6c463..4e47c7239e39f 100644 --- a/x-pack/plugins/monitoring/common/http_api/shared/index.ts +++ b/x-pack/plugins/monitoring/common/http_api/shared/index.ts @@ -5,6 +5,10 @@ * 2.0. */ -export * from './cluster'; export * from './ccs'; +export * from './cluster'; +export * from './literal_value'; +export * from './pagination'; +export * from './query_string_boolean'; +export * from './sorting'; export * from './time_range'; diff --git a/x-pack/plugins/monitoring/common/http_api/shared/literal_value.ts b/x-pack/plugins/monitoring/common/http_api/shared/literal_value.ts new file mode 100644 index 0000000000000..55719d6b31b12 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/shared/literal_value.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 * as rt from 'io-ts'; + +export const createLiteralValueFromUndefinedRT = ( + literalValue: LiteralValue +) => + rt.undefined.pipe( + new rt.Type( + 'BooleanFromString', + rt.literal(literalValue).is, + (_value, _context) => rt.success(literalValue), + () => undefined + ) + ); diff --git a/x-pack/plugins/monitoring/common/http_api/shared/pagination.ts b/x-pack/plugins/monitoring/common/http_api/shared/pagination.ts new file mode 100644 index 0000000000000..50f1f77b0361c --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/shared/pagination.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as rt from 'io-ts'; + +export const paginationRT = rt.type({ + index: rt.number, + size: rt.number, +}); diff --git a/x-pack/plugins/monitoring/common/http_api/shared/query_string_boolean.ts b/x-pack/plugins/monitoring/common/http_api/shared/query_string_boolean.ts new file mode 100644 index 0000000000000..b9b6fcae3780c --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/shared/query_string_boolean.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 { chain } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import * as rt from 'io-ts'; + +export const booleanFromStringRT = new rt.Type( + 'BooleanFromString', + rt.boolean.is, + (value, context) => + pipe( + rt.string.validate(value, context), + chain((stringValue) => + stringValue === 'true' + ? rt.success(true) + : stringValue === 'false' + ? rt.success(false) + : rt.failure(value, context) + ) + ), + String +); diff --git a/x-pack/plugins/monitoring/common/http_api/shared/sorting.ts b/x-pack/plugins/monitoring/common/http_api/shared/sorting.ts new file mode 100644 index 0000000000000..c52c46a41376c --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/shared/sorting.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 * as rt from 'io-ts'; + +const sortingDirectionRT = rt.keyof({ + asc: null, + desc: null, +}); + +export const sortingRT = rt.partial({ + field: rt.string, + direction: sortingDirectionRT, +}); diff --git a/x-pack/plugins/monitoring/common/http_api/shared/time_range.ts b/x-pack/plugins/monitoring/common/http_api/shared/time_range.ts index edeb56d1e2ea1..4021e8d52f4cd 100644 --- a/x-pack/plugins/monitoring/common/http_api/shared/time_range.ts +++ b/x-pack/plugins/monitoring/common/http_api/shared/time_range.ts @@ -6,8 +6,29 @@ */ import * as rt from 'io-ts'; +import moment from 'moment'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { chain } from 'fp-ts/lib/Either'; + +export const timestampFromStringRT = new rt.Type( + 'timestampFromStringRT', + (input): input is number => typeof input === 'number', + (input, context) => + pipe( + rt.string.validate(input, context), + chain((stringInput) => { + const momentValue = moment.utc(stringInput); + return momentValue.isValid() + ? rt.success(momentValue.valueOf()) + : rt.failure(stringInput, context); + }) + ), + (output) => new Date(output).toISOString() +); export const timeRangeRT = rt.type({ - min: rt.string, - max: rt.string, + min: timestampFromStringRT, + max: timestampFromStringRT, }); + +export type TimeRange = rt.TypeOf; diff --git a/x-pack/plugins/monitoring/common/types/es.ts b/x-pack/plugins/monitoring/common/types/es.ts index fc19ef0e9aab9..f4c4b385d625d 100644 --- a/x-pack/plugins/monitoring/common/types/es.ts +++ b/x-pack/plugins/monitoring/common/types/es.ts @@ -412,6 +412,7 @@ export interface ElasticsearchIndexRecoveryShard { export interface ElasticsearchMetricbeatNode { name?: string; stats?: ElasticsearchNodeStats; + master: boolean; } export interface ElasticsearchMetricbeatSource { diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_advanced_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_advanced_page.tsx index 734d59f310676..410a909a9c4ab 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_advanced_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_advanced_page.tsx @@ -29,6 +29,7 @@ export const ElasticsearchIndexAdvancedPage: React.FC = ({ clust const { index }: { index: string } = useParams(); const { zoomInfo, onBrush } = useCharts(); const clusterUuid = globalState.cluster_uuid; + const ccs = globalState.ccs; const [data, setData] = useState({} as any); const [alerts, setAlerts] = useState({}); @@ -60,6 +61,7 @@ export const ElasticsearchIndexAdvancedPage: React.FC = ({ clust const response = await services.http?.fetch(url, { method: 'POST', body: JSON.stringify({ + ccs, timeRange: { min: bounds.min.toISOString(), max: bounds.max.toISOString(), @@ -84,7 +86,7 @@ export const ElasticsearchIndexAdvancedPage: React.FC = ({ clust }); setAlerts(alertsResponse); } - }, [clusterUuid, services.data?.query.timefilter.timefilter, services.http, index]); + }, [services.data?.query.timefilter.timefilter, services.http, clusterUuid, index, ccs]); return ( = ({ clusters }) = const { index }: { index: string } = useParams(); const { zoomInfo, onBrush } = useCharts(); const clusterUuid = globalState.cluster_uuid; + const ccs = globalState.ccs; const [data, setData] = useState({} as any); const [indexLabel, setIndexLabel] = useState(labels.index as any); const [nodesByIndicesData, setNodesByIndicesData] = useState([]); @@ -72,6 +73,7 @@ export const ElasticsearchIndexPage: React.FC = ({ clusters }) = const response = await services.http?.fetch<{ shards: unknown[]; nodes: unknown[] }>(url, { method: 'POST', body: JSON.stringify({ + ccs, timeRange: { min: bounds.min.toISOString(), max: bounds.max.toISOString(), @@ -103,7 +105,7 @@ export const ElasticsearchIndexPage: React.FC = ({ clusters }) = }); setAlerts(alertsResponse); } - }, [clusterUuid, services.data?.query.timefilter.timefilter, services.http, index]); + }, [services.data?.query.timefilter.timefilter, services.http, clusterUuid, index, ccs]); return ( }; + cluster_state?: { nodes?: Record }; elasticsearch?: ElasticsearchModifiedSource['elasticsearch']; }; nodesShardCount: { nodes: Record }; @@ -67,10 +70,11 @@ export async function getPaginatedNodes( clusterStats?.cluster_state?.nodes ?? clusterStats?.elasticsearch?.cluster?.stats?.state?.nodes ?? {}; - for (const node of nodes) { - node.isOnline = !isUndefined(clusterStateNodes && clusterStateNodes[node.uuid]); - node.shardCount = nodesShardCount?.nodes[node.uuid]?.shardCount ?? 0; - } + const nodesWithStatus: NodeWithStatus[] = nodes.map((node) => ({ + ...node, + isOnline: !isUndefined(clusterStateNodes && clusterStateNodes[node.uuid]), + shardCount: nodesShardCount?.nodes[node.uuid]?.shardCount ?? 0, + })); // `metricSet` defines a list of metrics that are sortable in the UI // but we don't need to fetch all the data for these metrics to perform @@ -80,13 +84,13 @@ export async function getPaginatedNodes( const filters = [ { terms: { - 'source_node.name': nodes.map((node) => node.name), + 'source_node.name': nodesWithStatus.map((node) => node.name), }, }, ]; const groupBy = { field: `source_node.uuid`, - include: nodes.map((node) => node.uuid), + include: nodesWithStatus.map((node) => node.uuid), size, }; const metricSeriesData = await getMetrics( @@ -94,7 +98,7 @@ export async function getPaginatedNodes( 'elasticsearch', metricSet, filters, - { nodes }, + { nodes: nodesWithStatus }, 4, groupBy ); @@ -106,7 +110,7 @@ export async function getPaginatedNodes( const metricList = metricSeriesData[metricName]; for (const metricItem of metricList[0]) { - const node = nodes.find((n) => n.uuid === metricItem.groupedBy); + const node = nodesWithStatus.find((n) => n.uuid === metricItem.groupedBy); if (!node) { continue; } @@ -124,7 +128,7 @@ export async function getPaginatedNodes( // Manually apply pagination/sorting/filtering concerns // Filtering - const filteredNodes = filter(nodes, queryText, ['name']); // We only support filtering by name right now + const filteredNodes = filter(nodesWithStatus, queryText, ['name']); // We only support filtering by name right now // Sorting const sortedNodes = sortNodes(filteredNodes, sort); diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/sort_nodes.ts b/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/sort_nodes.ts index 33d29f2a05998..1fbd0f0130e49 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/sort_nodes.ts +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/sort_nodes.ts @@ -9,7 +9,10 @@ import { orderBy } from 'lodash'; type Node = Record; -export function sortNodes(nodes: Node[], sort?: { field: string; direction: 'asc' | 'desc' }) { +export function sortNodes( + nodes: T[], + sort?: { field: string; direction: 'asc' | 'desc' } +) { if (!sort || !sort.field) { return nodes; } diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/shards/get_shard_stats.ts b/x-pack/plugins/monitoring/server/lib/elasticsearch/shards/get_shard_stats.ts index 9c2c5cf45235a..df60ce3ffecef 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/shards/get_shard_stats.ts +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/shards/get_shard_stats.ts @@ -6,22 +6,15 @@ */ import { get } from 'lodash'; -// @ts-ignore -import { checkParam } from '../../error_missing_required'; -// @ts-ignore +import { ElasticsearchModifiedSource, ElasticsearchResponse } from '../../../../common/types/es'; +import { Globals } from '../../../static_globals'; +import { LegacyRequest } from '../../../types'; +import { getNewIndexPatterns } from '../../cluster/get_index_patterns'; import { createQuery } from '../../create_query'; -// @ts-ignore import { ElasticsearchMetric } from '../../metrics'; -// @ts-ignore -import { normalizeIndexShards, normalizeNodeShards } from './normalize_shard_objects'; -// @ts-ignore -import { getShardAggs } from './get_shard_stat_aggs'; -// @ts-ignore import { calculateIndicesTotals } from './calculate_shard_stat_indices_totals'; -import { LegacyRequest } from '../../../types'; -import { ElasticsearchResponse, ElasticsearchModifiedSource } from '../../../../common/types/es'; -import { getNewIndexPatterns } from '../../cluster/get_index_patterns'; -import { Globals } from '../../../static_globals'; +import { getShardAggs } from './get_shard_stat_aggs'; +import { normalizeIndexShards, normalizeNodeShards } from './normalize_shard_objects'; export function handleResponse( resp: ElasticsearchResponse, @@ -58,7 +51,17 @@ export function handleResponse( export function getShardStats( req: LegacyRequest, cluster: ElasticsearchModifiedSource, - { includeNodes = false, includeIndices = false, indexName = null, nodeUuid = null } = {} + { + includeNodes = false, + includeIndices = false, + indexName = null, + nodeUuid = null, + }: { + includeNodes?: boolean; + includeIndices?: boolean; + indexName?: string | null; + nodeUuid?: string | null; + } = {} ) { const dataset = 'shard'; // data_stream.dataset const type = 'shards'; // legacy diff --git a/x-pack/plugins/monitoring/server/lib/logs/detect_reason.ts b/x-pack/plugins/monitoring/server/lib/logs/detect_reason.ts index 216d29d841a86..e9b906af677e8 100644 --- a/x-pack/plugins/monitoring/server/lib/logs/detect_reason.ts +++ b/x-pack/plugins/monitoring/server/lib/logs/detect_reason.ts @@ -8,7 +8,7 @@ import { LegacyRequest } from '../../types'; import { createTimeFilter } from '../create_query'; -interface Opts { +export interface FilebeatIndexCheckOpts { start: number; end: number; clusterUuid?: string; @@ -19,7 +19,7 @@ interface Opts { async function doesFilebeatIndexExist( req: LegacyRequest, filebeatIndexPattern: string, - { start, end, clusterUuid, nodeUuid, indexUuid }: Opts + { start, end, clusterUuid, nodeUuid, indexUuid }: FilebeatIndexCheckOpts ) { const metric = { timestampField: '@timestamp' }; const filter = [createTimeFilter({ start, end, metric })]; @@ -142,6 +142,10 @@ async function doesFilebeatIndexExist( }; } -export async function detectReason(req: LegacyRequest, filebeatIndexPattern: string, opts: Opts) { +export async function detectReason( + req: LegacyRequest, + filebeatIndexPattern: string, + opts: FilebeatIndexCheckOpts +) { return await doesFilebeatIndexExist(req, filebeatIndexPattern, opts); } diff --git a/x-pack/plugins/monitoring/server/lib/logs/get_logs.ts b/x-pack/plugins/monitoring/server/lib/logs/get_logs.ts index 9e9f67831ba95..c243414c3649e 100644 --- a/x-pack/plugins/monitoring/server/lib/logs/get_logs.ts +++ b/x-pack/plugins/monitoring/server/lib/logs/get_logs.ts @@ -6,17 +6,11 @@ */ import moment from 'moment'; -// @ts-ignore import { checkParam } from '../error_missing_required'; -// @ts-ignore import { createTimeFilter } from '../create_query'; -// @ts-ignore -import { detectReason } from './detect_reason'; -// @ts-ignore +import { detectReason, FilebeatIndexCheckOpts } from './detect_reason'; import { formatUTCTimestampForTimezone } from '../format_timezone'; -// @ts-ignore import { getTimezone } from '../get_timezone'; -// @ts-ignore import { detectReasonFromException } from './detect_reason_from_exception'; import { LegacyRequest } from '../../types'; import { FilebeatResponse } from '../../../common/types/filebeat'; @@ -36,7 +30,7 @@ async function handleResponse( response: FilebeatResponse, req: LegacyRequest, filebeatIndexPattern: string, - opts: { clusterUuid: string; nodeUuid: string; indexUuid: string; start: number; end: number } + opts: FilebeatIndexCheckOpts ) { const result: { enabled: boolean; logs: Log[]; reason?: any } = { enabled: false, @@ -73,13 +67,7 @@ export async function getLogs( config: MonitoringConfig, req: LegacyRequest, filebeatIndexPattern: string, - { - clusterUuid, - nodeUuid, - indexUuid, - start, - end, - }: { clusterUuid: string; nodeUuid: string; indexUuid: string; start: number; end: number } + { clusterUuid, nodeUuid, indexUuid, start, end }: FilebeatIndexCheckOpts ) { checkParam(filebeatIndexPattern, 'filebeatIndexPattern in logs/getLogs'); diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index 433a3358558da..c81fb90e614cb 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -328,7 +328,7 @@ export class MonitoringPlugin const plugins = (await getCoreServices())[1]; const coreContext = await context.core; const actionContext = await context.actions; - const legacyRequest: LegacyRequest = { + const legacyRequest: LegacyRequest = { ...req, logger: this.log, getLogger: this.getLogger, diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/apm/instance.ts b/x-pack/plugins/monitoring/server/routes/api/v1/apm/instance.ts index 69b226ef3eaed..7410a91293fb9 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/apm/instance.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/apm/instance.ts @@ -19,12 +19,15 @@ import { MonitoringCore } from '../../../../types'; import { metricSet } from './metric_set_instance'; export function apmInstanceRoute(server: MonitoringCore) { + const validateParams = createValidationFunction(postApmInstanceRequestParamsRT); + const validateBody = createValidationFunction(postApmInstanceRequestPayloadRT); + server.route({ method: 'post', path: '/api/monitoring/v1/clusters/{clusterUuid}/apm/{apmUuid}', validate: { - params: createValidationFunction(postApmInstanceRequestParamsRT), - body: createValidationFunction(postApmInstanceRequestPayloadRT), + params: validateParams, + body: validateBody, }, async handler(req) { const apmUuid = req.params.apmUuid; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/apm/instances.ts b/x-pack/plugins/monitoring/server/routes/api/v1/apm/instances.ts index 960a8dc3627b0..6225bd4b553b1 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/apm/instances.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/apm/instances.ts @@ -17,12 +17,15 @@ import { handleError } from '../../../../lib/errors'; import { MonitoringCore } from '../../../../types'; export function apmInstancesRoute(server: MonitoringCore) { + const validateParams = createValidationFunction(postApmInstancesRequestParamsRT); + const validateBody = createValidationFunction(postApmInstancesRequestPayloadRT); + server.route({ method: 'post', path: '/api/monitoring/v1/clusters/{clusterUuid}/apm/instances', validate: { - params: createValidationFunction(postApmInstancesRequestParamsRT), - body: createValidationFunction(postApmInstancesRequestPayloadRT), + params: validateParams, + body: validateBody, }, async handler(req) { const config = server.config; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/apm/overview.ts b/x-pack/plugins/monitoring/server/routes/api/v1/apm/overview.ts index 532b5fa4dc4c8..5e23b0138a72f 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/apm/overview.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/apm/overview.ts @@ -17,12 +17,15 @@ import { metricSet } from './metric_set_overview'; import { getApmClusterStatus } from './_get_apm_cluster_status'; export function apmOverviewRoute(server: MonitoringCore) { + const validateParams = createValidationFunction(postApmOverviewRequestParamsRT); + const validateBody = createValidationFunction(postApmOverviewRequestPayloadRT); + server.route({ method: 'post', path: '/api/monitoring/v1/clusters/{clusterUuid}/apm', validate: { - params: createValidationFunction(postApmOverviewRequestParamsRT), - body: createValidationFunction(postApmOverviewRequestPayloadRT), + params: validateParams, + body: validateBody, }, async handler(req) { const config = server.config; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/beats/beat_detail.ts b/x-pack/plugins/monitoring/server/routes/api/v1/beats/beat_detail.ts index 18f6de905dbfe..26477b3611360 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/beats/beat_detail.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/beats/beat_detail.ts @@ -20,12 +20,15 @@ import { MonitoringCore } from '../../../../types'; import { metricSet } from './metric_set_detail'; export function beatsDetailRoute(server: MonitoringCore) { + const validateParams = createValidationFunction(postBeatDetailRequestParamsRT); + const validateBody = createValidationFunction(postBeatDetailRequestPayloadRT); + server.route({ method: 'post', path: '/api/monitoring/v1/clusters/{clusterUuid}/beats/beat/{beatUuid}', validate: { - params: createValidationFunction(postBeatDetailRequestParamsRT), - body: createValidationFunction(postBeatDetailRequestPayloadRT), + params: validateParams, + body: validateBody, }, async handler(req) { const clusterUuid = req.params.clusterUuid; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/beats/beats.ts b/x-pack/plugins/monitoring/server/routes/api/v1/beats/beats.ts index 8bf73bb115f74..385950d23f836 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/beats/beats.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/beats/beats.ts @@ -18,12 +18,15 @@ import { handleError } from '../../../../lib/errors'; import { MonitoringCore } from '../../../../types'; export function beatsListingRoute(server: MonitoringCore) { + const validateParams = createValidationFunction(postBeatsListingRequestParamsRT); + const validateBody = createValidationFunction(postBeatsListingRequestPayloadRT); + server.route({ method: 'post', path: '/api/monitoring/v1/clusters/{clusterUuid}/beats/beats', validate: { - params: createValidationFunction(postBeatsListingRequestParamsRT), - body: createValidationFunction(postBeatsListingRequestPayloadRT), + params: validateParams, + body: validateBody, }, async handler(req) { const config = server.config; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/beats/overview.ts b/x-pack/plugins/monitoring/server/routes/api/v1/beats/overview.ts index 6497c5568a1a8..f36d6cd0b1aeb 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/beats/overview.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/beats/overview.ts @@ -20,12 +20,15 @@ import { MonitoringCore } from '../../../../types'; import { metricSet } from './metric_set_overview'; export function beatsOverviewRoute(server: MonitoringCore) { + const validateParams = createValidationFunction(postBeatsOverviewRequestParamsRT); + const validateBody = createValidationFunction(postBeatsOverviewRequestPayloadRT); + server.route({ method: 'post', path: '/api/monitoring/v1/clusters/{clusterUuid}/beats', validate: { - params: createValidationFunction(postBeatsOverviewRequestParamsRT), - body: createValidationFunction(postBeatsOverviewRequestPayloadRT), + params: validateParams, + body: validateBody, }, async handler(req) { const config = server.config; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr.ts index 3dd4feb3db805..726ed7b2500d1 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr.ts @@ -5,21 +5,24 @@ * 2.0. */ -import { schema } from '@kbn/config-schema'; -import moment from 'moment'; import { get, groupBy } from 'lodash'; -// @ts-ignore -import { handleError } from '../../../../lib/errors/handle_error'; -// @ts-ignore import { prefixIndexPatternWithCcs } from '../../../../../common/ccs_utils'; import { INDEX_PATTERN_ELASTICSEARCH } from '../../../../../common/constants'; import { - ElasticsearchResponse, + postElasticsearchCcrRequestParamsRT, + postElasticsearchCcrRequestPayloadRT, + postElasticsearchCcrResponsePayloadRT, +} from '../../../../../common/http_api/elasticsearch'; +import { TimeRange } from '../../../../../common/http_api/shared'; +import { ElasticsearchLegacySource, ElasticsearchMetricbeatSource, + ElasticsearchResponse, } from '../../../../../common/types/es'; -import { LegacyRequest } from '../../../../types'; import { MonitoringConfig } from '../../../../config'; +import { createValidationFunction } from '../../../../lib/create_route_validation_function'; +import { handleError } from '../../../../lib/errors/handle_error'; +import { LegacyRequest, MonitoringCore } from '../../../../types'; function getBucketScript(max: string, min: string) { return { @@ -33,9 +36,12 @@ function getBucketScript(max: string, min: string) { }; } -function buildRequest(req: LegacyRequest, config: MonitoringConfig, esIndexPattern: string) { - const min = moment.utc(req.payload.timeRange.min).valueOf(); - const max = moment.utc(req.payload.timeRange.max).valueOf(); +function buildRequest( + req: LegacyRequest, + config: MonitoringConfig, + esIndexPattern: string +) { + const { min, max } = req.payload.timeRange; const maxBucketSize = config.ui.max_bucket_size; const aggs = { ops_synced_max: { @@ -195,25 +201,18 @@ function buildRequest(req: LegacyRequest, config: MonitoringConfig, esIndexPatte }; } -export function ccrRoute(server: { route: (p: any) => void; config: MonitoringConfig }) { +export function ccrRoute(server: MonitoringCore) { + const validateParams = createValidationFunction(postElasticsearchCcrRequestParamsRT); + const validateBody = createValidationFunction(postElasticsearchCcrRequestPayloadRT); + server.route({ - method: 'POST', + method: 'post', path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch/ccr', - config: { - validate: { - params: schema.object({ - clusterUuid: schema.string(), - }), - body: schema.object({ - ccs: schema.maybe(schema.string()), - timeRange: schema.object({ - min: schema.string(), - max: schema.string(), - }), - }), - }, + validate: { + params: validateParams, + body: validateBody, }, - async handler(req: LegacyRequest) { + async handler(req) { const config = server.config; const ccs = req.payload.ccs; const esIndexPattern = prefixIndexPatternWithCcs(config, INDEX_PATTERN_ELASTICSEARCH, ccs); @@ -322,7 +321,7 @@ export function ccrRoute(server: { route: (p: any) => void; config: MonitoringCo return accum; }, []); - return { data }; + return postElasticsearchCcrResponsePayloadRT.encode({ data }); } catch (err) { return handleError(err, req); } diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr_shard.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr_shard.ts index 44a1eb1807595..797b8addd02cf 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr_shard.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr_shard.ts @@ -5,18 +5,19 @@ * 2.0. */ -import moment from 'moment'; -import { schema } from '@kbn/config-schema'; -// @ts-ignore -import { handleError } from '../../../../lib/errors/handle_error'; -// @ts-ignore -import { prefixIndexPatternWithCcs } from '../../../../../common/ccs_utils'; -// @ts-ignore -import { getMetrics } from '../../../../lib/details/get_metrics'; +import { + postElasticsearchCcrShardRequestParamsRT, + postElasticsearchCcrShardRequestPayloadRT, + postElasticsearchCcrShardResponsePayloadRT, +} from '../../../../../common/http_api/elasticsearch'; +import { TimeRange } from '../../../../../common/http_api/shared'; import { ElasticsearchResponse } from '../../../../../common/types/es'; -import { LegacyRequest } from '../../../../types'; import { getNewIndexPatterns } from '../../../../lib/cluster/get_index_patterns'; +import { createValidationFunction } from '../../../../lib/create_route_validation_function'; +import { getMetrics } from '../../../../lib/details/get_metrics'; +import { handleError } from '../../../../lib/errors/handle_error'; import { Globals } from '../../../../static_globals'; +import { LegacyRequest, MonitoringCore } from '../../../../types'; function getFormattedLeaderIndex(leaderIndex: string) { let leader = leaderIndex; @@ -27,10 +28,12 @@ function getFormattedLeaderIndex(leaderIndex: string) { return leader; } -async function getCcrStat(req: LegacyRequest, esIndexPattern: string, filters: unknown[]) { - const min = moment.utc(req.payload.timeRange.min).valueOf(); - const max = moment.utc(req.payload.timeRange.max).valueOf(); - +async function getCcrStat( + req: LegacyRequest, + esIndexPattern: string, + filters: unknown[] +) { + const { min, max } = req.payload.timeRange; const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring'); const params = { @@ -78,27 +81,18 @@ async function getCcrStat(req: LegacyRequest, esIndexPattern: string, filters: u return await callWithRequest(req, 'search', params); } -export function ccrShardRoute(server: { route: (p: any) => void; config: () => {} }) { +export function ccrShardRoute(server: MonitoringCore) { + const validateParams = createValidationFunction(postElasticsearchCcrShardRequestParamsRT); + const validateBody = createValidationFunction(postElasticsearchCcrShardRequestPayloadRT); + server.route({ - method: 'POST', + method: 'post', path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch/ccr/{index}/shard/{shardId}', - config: { - validate: { - params: schema.object({ - clusterUuid: schema.string(), - index: schema.string(), - shardId: schema.string(), - }), - body: schema.object({ - ccs: schema.maybe(schema.string()), - timeRange: schema.object({ - min: schema.string(), - max: schema.string(), - }), - }), - }, + validate: { + params: validateParams, + body: validateBody, }, - async handler(req: LegacyRequest) { + async handler(req) { const index = req.params.index; const shardId = req.params.shardId; const moduleType = 'elasticsearch'; @@ -171,7 +165,7 @@ export function ccrShardRoute(server: { route: (p: any) => void; config: () => { const leaderIndex = mbStat ? mbStat?.leader?.index : legacyStat?.leader_index; - return { + return postElasticsearchCcrShardResponsePayloadRT.encode({ metrics, stat: mbStat ?? legacyStat, formattedLeader: getFormattedLeaderIndex(leaderIndex ?? ''), @@ -179,7 +173,7 @@ export function ccrShardRoute(server: { route: (p: any) => void; config: () => { ccrResponse.hits?.hits[0]?._source['@timestamp'] ?? ccrResponse.hits?.hits[0]?._source.timestamp, oldestStat: oldestMBStat ?? oldestLegacyStat, - }; + }); } catch (err) { return handleError(err, req); } diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/index.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/index.ts similarity index 100% rename from x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/index.js rename to x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/index.ts diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/index_detail.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/index_detail.js deleted file mode 100644 index b4f317c9a435d..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/index_detail.js +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { get } from 'lodash'; -import { schema } from '@kbn/config-schema'; -import { getClusterStats } from '../../../../lib/cluster/get_cluster_stats'; -import { getIndexSummary } from '../../../../lib/elasticsearch/indices'; -import { getMetrics } from '../../../../lib/details/get_metrics'; -import { getShardAllocation, getShardStats } from '../../../../lib/elasticsearch/shards'; -import { handleError } from '../../../../lib/errors/handle_error'; -import { prefixIndexPatternWithCcs } from '../../../../../common/ccs_utils'; -import { metricSet } from './metric_set_index_detail'; -import { getLogs } from '../../../../lib/logs/get_logs'; -import { CCS_REMOTE_PATTERN } from '../../../../../common/constants'; - -const { advanced: metricSetAdvanced, overview: metricSetOverview } = metricSet; - -export function esIndexRoute(server) { - server.route({ - method: 'POST', - path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch/indices/{id}', - config: { - validate: { - params: schema.object({ - clusterUuid: schema.string(), - id: schema.string(), - }), - body: schema.object({ - ccs: schema.maybe(schema.string()), - timeRange: schema.object({ - min: schema.string(), - max: schema.string(), - }), - is_advanced: schema.boolean(), - }), - }, - }, - handler: async (req) => { - try { - const config = server.config; - const clusterUuid = req.params.clusterUuid; - const indexUuid = req.params.id; - const start = req.payload.timeRange.min; - const end = req.payload.timeRange.max; - const filebeatIndexPattern = prefixIndexPatternWithCcs( - config, - config.ui.logs.index, - CCS_REMOTE_PATTERN - ); - const isAdvanced = req.payload.is_advanced; - const metricSet = isAdvanced ? metricSetAdvanced : metricSetOverview; - - const cluster = await getClusterStats(req, clusterUuid); - const showSystemIndices = true; // hardcode to true, because this could be a system index - - const shardStats = await getShardStats(req, cluster, { - includeNodes: true, - includeIndices: true, - indexName: indexUuid, - }); - const indexSummary = await getIndexSummary(req, shardStats, { - clusterUuid, - indexUuid, - start, - end, - }); - const metrics = await getMetrics(req, 'elasticsearch', metricSet, [ - { term: { 'index_stats.index': indexUuid } }, - ]); - - let logs; - let shardAllocation; - if (!isAdvanced) { - // TODO: Why so many fields needed for a single component (shard legend)? - const shardFilter = { - bool: { - should: [ - { term: { 'shard.index': indexUuid } }, - { term: { 'elasticsearch.index.name': indexUuid } }, - ], - }, - }; - const stateUuid = get( - cluster, - 'elasticsearch.cluster.stats.state.state_uuid', - get(cluster, 'cluster_state.state_uuid') - ); - const allocationOptions = { - shardFilter, - stateUuid, - showSystemIndices, - }; - const shards = await getShardAllocation(req, allocationOptions); - - logs = await getLogs(config, req, filebeatIndexPattern, { - clusterUuid, - indexUuid, - start, - end, - }); - - shardAllocation = { - shards, - shardStats: { nodes: shardStats.nodes }, - nodes: shardStats.nodes, // for identifying nodes that shard relocates to - stateUuid, // for debugging/troubleshooting - }; - } - - return { - indexSummary, - metrics, - logs, - ...shardAllocation, - }; - } catch (err) { - throw handleError(err, req); - } - }, - }); -} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/index_detail.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/index_detail.ts new file mode 100644 index 0000000000000..1d31064f71ebb --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/index_detail.ts @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { get } from 'lodash'; +import { prefixIndexPatternWithCcs } from '../../../../../common/ccs_utils'; +import { CCS_REMOTE_PATTERN } from '../../../../../common/constants'; +import { + postElasticsearchIndexDetailRequestParamsRT, + postElasticsearchIndexDetailRequestPayloadRT, + postElasticsearchIndexDetailResponsePayloadRT, +} from '../../../../../common/http_api/elasticsearch'; +import { getClusterStats } from '../../../../lib/cluster/get_cluster_stats'; +import { createValidationFunction } from '../../../../lib/create_route_validation_function'; +import { getMetrics } from '../../../../lib/details/get_metrics'; +import { getIndexSummary } from '../../../../lib/elasticsearch/indices'; +import { getShardAllocation, getShardStats } from '../../../../lib/elasticsearch/shards'; +import { handleError } from '../../../../lib/errors/handle_error'; +import { getLogs } from '../../../../lib/logs/get_logs'; +import { MonitoringCore } from '../../../../types'; +import { metricSets } from './metric_set_index_detail'; + +const { advanced: metricSetAdvanced, overview: metricSetOverview } = metricSets; + +export function esIndexRoute(server: MonitoringCore) { + const validateParams = createValidationFunction(postElasticsearchIndexDetailRequestParamsRT); + const validateBody = createValidationFunction(postElasticsearchIndexDetailRequestPayloadRT); + + server.route({ + method: 'post', + path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch/indices/{id}', + validate: { + params: validateParams, + body: validateBody, + }, + handler: async (req) => { + try { + const config = server.config; + const clusterUuid = req.params.clusterUuid; + const indexUuid = req.params.id; + const start = req.payload.timeRange.min; + const end = req.payload.timeRange.max; + const filebeatIndexPattern = prefixIndexPatternWithCcs( + config, + config.ui.logs.index, + CCS_REMOTE_PATTERN + ); + const isAdvanced = req.payload.is_advanced; + const metricSet = isAdvanced ? metricSetAdvanced : metricSetOverview; + + const cluster = await getClusterStats(req, clusterUuid); + const showSystemIndices = true; // hardcode to true, because this could be a system index + + const shardStats = await getShardStats(req, cluster, { + includeNodes: true, + includeIndices: true, + indexName: indexUuid, + }); + const indexSummary = await getIndexSummary(req, shardStats, { + clusterUuid, + indexUuid, + start, + end, + }); + const metrics = await getMetrics(req, 'elasticsearch', metricSet, [ + { term: { 'index_stats.index': indexUuid } }, + ]); + + let logs; + let shardAllocation; + if (!isAdvanced) { + // TODO: Why so many fields needed for a single component (shard legend)? + const shardFilter = { + bool: { + should: [ + { term: { 'shard.index': indexUuid } }, + { term: { 'elasticsearch.index.name': indexUuid } }, + ], + }, + }; + const stateUuid = get( + cluster, + 'elasticsearch.cluster.stats.state.state_uuid', + get(cluster, 'cluster_state.state_uuid') + ); + const allocationOptions = { + shardFilter, + stateUuid, + showSystemIndices, + }; + const shards = await getShardAllocation(req, allocationOptions); + + logs = await getLogs(config, req, filebeatIndexPattern, { + clusterUuid, + indexUuid, + start, + end, + }); + + shardAllocation = { + shards, + shardStats: { nodes: shardStats.nodes }, + nodes: shardStats.nodes, // for identifying nodes that shard relocates to + stateUuid, // for debugging/troubleshooting + }; + } + + return postElasticsearchIndexDetailResponsePayloadRT.encode({ + indexSummary, + metrics, + logs, + ...shardAllocation, + }); + } catch (err) { + throw handleError(err, req); + } + }, + }); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/indices.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/indices.js deleted file mode 100644 index de41137791108..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/indices.js +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { schema } from '@kbn/config-schema'; -import { getClusterStats } from '../../../../lib/cluster/get_cluster_stats'; -import { getClusterStatus } from '../../../../lib/cluster/get_cluster_status'; -import { getIndices } from '../../../../lib/elasticsearch/indices'; -import { handleError } from '../../../../lib/errors/handle_error'; -import { getIndicesUnassignedShardStats } from '../../../../lib/elasticsearch/shards/get_indices_unassigned_shard_stats'; - -export function esIndicesRoute(server) { - server.route({ - method: 'POST', - path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch/indices', - config: { - validate: { - params: schema.object({ - clusterUuid: schema.string(), - }), - query: schema.object({ - show_system_indices: schema.boolean(), - }), - body: schema.object({ - ccs: schema.maybe(schema.string()), - timeRange: schema.object({ - min: schema.string(), - max: schema.string(), - }), - }), - }, - }, - async handler(req) { - const { clusterUuid } = req.params; - const { show_system_indices: showSystemIndices } = req.query; - const { ccs } = req.payload; - - try { - const clusterStats = await getClusterStats(req, clusterUuid, ccs); - const indicesUnassignedShardStats = await getIndicesUnassignedShardStats(req, clusterStats); - const indices = await getIndices(req, showSystemIndices, indicesUnassignedShardStats); - - return { - clusterStatus: getClusterStatus(clusterStats, indicesUnassignedShardStats), - indices, - }; - } catch (err) { - throw handleError(err, req); - } - }, - }); -} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/indices.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/indices.ts new file mode 100644 index 0000000000000..00956410d8c4d --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/indices.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 { + postElasticsearchIndicesRequestParamsRT, + postElasticsearchIndicesRequestPayloadRT, + postElasticsearchIndicesRequestQueryRT, + postElasticsearchIndicesResponsePayloadRT, +} from '../../../../../common/http_api/elasticsearch'; +import { getClusterStats } from '../../../../lib/cluster/get_cluster_stats'; +import { getClusterStatus } from '../../../../lib/cluster/get_cluster_status'; +import { createValidationFunction } from '../../../../lib/create_route_validation_function'; +import { getIndices } from '../../../../lib/elasticsearch/indices'; +import { getIndicesUnassignedShardStats } from '../../../../lib/elasticsearch/shards/get_indices_unassigned_shard_stats'; +import { handleError } from '../../../../lib/errors/handle_error'; +import { MonitoringCore } from '../../../../types'; + +export function esIndicesRoute(server: MonitoringCore) { + const validateParams = createValidationFunction(postElasticsearchIndicesRequestParamsRT); + const validateQuery = createValidationFunction(postElasticsearchIndicesRequestQueryRT); + const validateBody = createValidationFunction(postElasticsearchIndicesRequestPayloadRT); + + server.route({ + method: 'post', + path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch/indices', + validate: { + params: validateParams, + query: validateQuery, + body: validateBody, + }, + async handler(req) { + const { clusterUuid } = req.params; + const { show_system_indices: showSystemIndices } = req.query; + + try { + const clusterStats = await getClusterStats(req, clusterUuid); + const indicesUnassignedShardStats = await getIndicesUnassignedShardStats(req, clusterStats); + const indices = await getIndices(req, showSystemIndices, indicesUnassignedShardStats); + + return postElasticsearchIndicesResponsePayloadRT.encode({ + clusterStatus: getClusterStatus(clusterStats, indicesUnassignedShardStats), + indices, + }); + } catch (err) { + throw handleError(err, req); + } + }, + }); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_index_detail.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_index_detail.js deleted file mode 100644 index fce09eac4918f..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_index_detail.js +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export const metricSet = { - advanced: [ - { - keys: ['index_mem_fixed_bit_set', 'index_mem_versions'], - name: 'index_3', - }, - { - keys: [ - 'index_mem_query_cache', - 'index_mem_request_cache', - 'index_mem_fielddata', - 'index_mem_writer', - ], - name: 'index_4', - }, - { - keys: ['index_searching_total', 'index_indexing_total'], - name: 'index_total', - }, - { - keys: ['index_searching_time', 'index_indexing_total_time', 'index_indexing_primaries_time'], - name: 'index_time', - }, - { - keys: ['index_throttling_indexing_total_time', 'index_throttling_indexing_primaries_time'], - name: 'index_throttling', - }, - { - keys: ['index_segment_refresh_total_time', 'index_segment_refresh_primaries_time'], - name: 'index_refresh', - }, - { - keys: [ - 'index_store_total_size', - 'index_store_primaries_size', - 'index_segment_merge_total_size', - 'index_segment_merge_primaries_size', - ], - name: 'index_disk', - }, - { - keys: ['index_segment_count_total', 'index_segment_count_primaries'], - name: 'index_segment_count', - }, - { - keys: ['index_index_latency', 'index_query_latency'], - name: 'index_latency', - }, - ], - overview: [ - 'index_search_request_rate', - { - keys: ['index_request_rate_total', 'index_request_rate_primary'], - name: 'index_request_rate', - }, - { - keys: ['index_store_total_size', 'index_store_primaries_size'], - name: 'index_size', - }, - 'index_document_count', - { - keys: ['index_segment_count_total', 'index_segment_count_primaries'], - name: 'index_segment_count', - }, - ], -}; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_index_detail.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_index_detail.ts new file mode 100644 index 0000000000000..36e3cef797967 --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_index_detail.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { MetricDescriptor } from '../../../../lib/details/get_metrics'; + +export const metricSets: { + advanced: MetricDescriptor[]; + overview: MetricDescriptor[]; +} = { + advanced: [ + { + keys: ['index_mem_fixed_bit_set', 'index_mem_versions'], + name: 'index_3', + }, + { + keys: [ + 'index_mem_query_cache', + 'index_mem_request_cache', + 'index_mem_fielddata', + 'index_mem_writer', + ], + name: 'index_4', + }, + { + keys: ['index_searching_total', 'index_indexing_total'], + name: 'index_total', + }, + { + keys: ['index_searching_time', 'index_indexing_total_time', 'index_indexing_primaries_time'], + name: 'index_time', + }, + { + keys: ['index_throttling_indexing_total_time', 'index_throttling_indexing_primaries_time'], + name: 'index_throttling', + }, + { + keys: ['index_segment_refresh_total_time', 'index_segment_refresh_primaries_time'], + name: 'index_refresh', + }, + { + keys: [ + 'index_store_total_size', + 'index_store_primaries_size', + 'index_segment_merge_total_size', + 'index_segment_merge_primaries_size', + ], + name: 'index_disk', + }, + { + keys: ['index_segment_count_total', 'index_segment_count_primaries'], + name: 'index_segment_count', + }, + { + keys: ['index_index_latency', 'index_query_latency'], + name: 'index_latency', + }, + ], + overview: [ + 'index_search_request_rate', + { + keys: ['index_request_rate_total', 'index_request_rate_primary'], + name: 'index_request_rate', + }, + { + keys: ['index_store_total_size', 'index_store_primaries_size'], + name: 'index_size', + }, + 'index_document_count', + { + keys: ['index_segment_count_total', 'index_segment_count_primaries'], + name: 'index_segment_count', + }, + ], +}; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_node_detail.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_node_detail.js deleted file mode 100644 index 5303dc452f850..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_node_detail.js +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export const metricSets = { - advanced: [ - { - keys: ['node_jvm_mem_max_in_bytes', 'node_jvm_mem_used_in_bytes'], - name: 'node_jvm_mem', - }, - { - keys: ['node_jvm_gc_old_count', 'node_jvm_gc_young_count'], - name: 'node_gc', - }, - { - keys: ['node_jvm_gc_old_time', 'node_jvm_gc_young_time'], - name: 'node_gc_time', - }, - { - keys: ['node_index_mem_fixed_bit_set', 'node_index_mem_versions'], - name: 'node_index_3', - }, - { - keys: [ - 'node_index_mem_query_cache', - 'node_index_mem_request_cache', - 'node_index_mem_fielddata', - 'node_index_mem_writer', - ], - name: 'node_index_4', - }, - { - keys: ['node_search_total', 'node_index_total'], - name: 'node_request_total', - }, - { - keys: ['node_index_time', 'node_throttle_index_time'], - name: 'node_index_time', - }, - { - keys: ['node_index_threads_write_queue', 'node_index_threads_write_rejected'], - name: 'node_index_threads', - }, - { - keys: [ - 'node_index_threads_search_queue', - 'node_index_threads_search_rejected', - 'node_index_threads_get_queue', - 'node_index_threads_get_rejected', - ], - name: 'node_read_threads', - }, - { - keys: ['node_cpu_utilization', 'node_cgroup_quota'], - name: 'node_cpu_utilization', - }, - { - keys: ['node_cgroup_usage', 'node_cgroup_throttled'], - name: 'node_cgroup_cpu', - }, - { - keys: ['node_cgroup_periods', 'node_cgroup_throttled_count'], - name: 'node_cgroup_stats', - }, - { - keys: ['node_query_latency', 'node_index_latency'], - name: 'node_latency', - }, - ], - overview: [ - { - keys: ['node_total_cumul_io', 'node_total_read_io', 'node_total_write_io'], - name: 'node_total_io', - }, - { - keys: ['node_query_latency', 'node_index_latency'], - name: 'node_latency', - }, - { - keys: ['node_jvm_mem_max_in_bytes', 'node_jvm_mem_used_in_bytes'], - name: 'node_jvm_mem', - }, - { - keys: [], - name: 'node_cpu_metric', - }, - 'node_load_average', - 'node_segment_count', - ], -}; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_node_detail.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_node_detail.ts new file mode 100644 index 0000000000000..9de51af5ecbaa --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_node_detail.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 { MetricDescriptor } from '../../../../lib/details/get_metrics'; + +export const metricSets: { + advanced: MetricDescriptor[]; + overview: MetricDescriptor[]; +} = { + advanced: [ + { + keys: ['node_jvm_mem_max_in_bytes', 'node_jvm_mem_used_in_bytes'], + name: 'node_jvm_mem', + }, + { + keys: ['node_jvm_gc_old_count', 'node_jvm_gc_young_count'], + name: 'node_gc', + }, + { + keys: ['node_jvm_gc_old_time', 'node_jvm_gc_young_time'], + name: 'node_gc_time', + }, + { + keys: ['node_index_mem_fixed_bit_set', 'node_index_mem_versions'], + name: 'node_index_3', + }, + { + keys: [ + 'node_index_mem_query_cache', + 'node_index_mem_request_cache', + 'node_index_mem_fielddata', + 'node_index_mem_writer', + ], + name: 'node_index_4', + }, + { + keys: ['node_search_total', 'node_index_total'], + name: 'node_request_total', + }, + { + keys: ['node_index_time', 'node_throttle_index_time'], + name: 'node_index_time', + }, + { + keys: ['node_index_threads_write_queue', 'node_index_threads_write_rejected'], + name: 'node_index_threads', + }, + { + keys: [ + 'node_index_threads_search_queue', + 'node_index_threads_search_rejected', + 'node_index_threads_get_queue', + 'node_index_threads_get_rejected', + ], + name: 'node_read_threads', + }, + { + keys: ['node_cpu_utilization', 'node_cgroup_quota'], + name: 'node_cpu_utilization', + }, + { + keys: ['node_cgroup_usage', 'node_cgroup_throttled'], + name: 'node_cgroup_cpu', + }, + { + keys: ['node_cgroup_periods', 'node_cgroup_throttled_count'], + name: 'node_cgroup_stats', + }, + { + keys: ['node_query_latency', 'node_index_latency'], + name: 'node_latency', + }, + ], + overview: [ + { + keys: ['node_total_cumul_io', 'node_total_read_io', 'node_total_write_io'], + name: 'node_total_io', + }, + { + keys: ['node_query_latency', 'node_index_latency'], + name: 'node_latency', + }, + { + keys: ['node_jvm_mem_max_in_bytes', 'node_jvm_mem_used_in_bytes'], + name: 'node_jvm_mem', + }, + { + keys: [], + name: 'node_cpu_metric', + }, + 'node_load_average', + 'node_segment_count', + ], +}; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_overview.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_overview.js deleted file mode 100644 index 317486bce4adf..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_overview.js +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export const metricSet = [ - 'cluster_search_request_rate', - 'cluster_query_latency', - { - keys: ['cluster_index_request_rate_total', 'cluster_index_request_rate_primary'], - name: 'cluster_index_request_rate', - }, - 'cluster_index_latency', -]; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_overview.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_overview.ts new file mode 100644 index 0000000000000..9ea00bf8a503a --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/metric_set_overview.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 { MetricDescriptor } from '../../../../lib/details/get_metrics'; + +export const metricSet: MetricDescriptor[] = [ + 'cluster_search_request_rate', + 'cluster_query_latency', + { + keys: ['cluster_index_request_rate_total', 'cluster_index_request_rate_primary'], + name: 'cluster_index_request_rate', + }, + 'cluster_index_latency', +]; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ml_jobs.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ml_jobs.js deleted file mode 100644 index f2f0698bae18c..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ml_jobs.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { schema } from '@kbn/config-schema'; -import { getClusterStats } from '../../../../lib/cluster/get_cluster_stats'; -import { getClusterStatus } from '../../../../lib/cluster/get_cluster_status'; -import { getMlJobs } from '../../../../lib/elasticsearch/get_ml_jobs'; -import { handleError } from '../../../../lib/errors/handle_error'; -import { getIndicesUnassignedShardStats } from '../../../../lib/elasticsearch/shards/get_indices_unassigned_shard_stats'; - -export function mlJobRoute(server) { - server.route({ - method: 'POST', - path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch/ml_jobs', - config: { - validate: { - params: schema.object({ - clusterUuid: schema.string(), - }), - body: schema.object({ - ccs: schema.maybe(schema.string()), - timeRange: schema.object({ - min: schema.string(), - max: schema.string(), - }), - }), - }, - }, - async handler(req) { - const clusterUuid = req.params.clusterUuid; - - try { - const clusterStats = await getClusterStats(req, clusterUuid); - const indicesUnassignedShardStats = await getIndicesUnassignedShardStats(req, clusterStats); - const rows = await getMlJobs(req); - return { - clusterStatus: getClusterStatus(clusterStats, indicesUnassignedShardStats), - rows, - }; - } catch (err) { - throw handleError(err, req); - } - }, - }); -} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ml_jobs.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ml_jobs.ts new file mode 100644 index 0000000000000..f51ca41ee4e9f --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ml_jobs.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + postElasticsearchMlJobsRequestParamsRT, + postElasticsearchMlJobsRequestPayloadRT, + postElasticsearchMlJobsResponsePayloadRT, +} from '../../../../../common/http_api/elasticsearch'; +import { getClusterStats } from '../../../../lib/cluster/get_cluster_stats'; +import { getClusterStatus } from '../../../../lib/cluster/get_cluster_status'; +import { createValidationFunction } from '../../../../lib/create_route_validation_function'; +import { getMlJobs } from '../../../../lib/elasticsearch/get_ml_jobs'; +import { getIndicesUnassignedShardStats } from '../../../../lib/elasticsearch/shards/get_indices_unassigned_shard_stats'; +import { handleError } from '../../../../lib/errors/handle_error'; +import { MonitoringCore } from '../../../../types'; + +export function mlJobRoute(server: MonitoringCore) { + const validateParams = createValidationFunction(postElasticsearchMlJobsRequestParamsRT); + const validateBody = createValidationFunction(postElasticsearchMlJobsRequestPayloadRT); + + server.route({ + method: 'post', + path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch/ml_jobs', + validate: { + params: validateParams, + body: validateBody, + }, + async handler(req) { + const clusterUuid = req.params.clusterUuid; + + try { + const clusterStats = await getClusterStats(req, clusterUuid); + const indicesUnassignedShardStats = await getIndicesUnassignedShardStats(req, clusterStats); + const rows = await getMlJobs(req); + return postElasticsearchMlJobsResponsePayloadRT.encode({ + clusterStatus: getClusterStatus(clusterStats, indicesUnassignedShardStats), + rows, + }); + } catch (err) { + throw handleError(err, req); + } + }, + }); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/node_detail.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/node_detail.js deleted file mode 100644 index 1504bb076101e..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/node_detail.js +++ /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 { get } from 'lodash'; -import { schema } from '@kbn/config-schema'; -import { getClusterStats } from '../../../../lib/cluster/get_cluster_stats'; -import { getNodeSummary } from '../../../../lib/elasticsearch/nodes'; -import { getShardStats, getShardAllocation } from '../../../../lib/elasticsearch/shards'; -import { getMetrics } from '../../../../lib/details/get_metrics'; -import { handleError } from '../../../../lib/errors/handle_error'; -import { prefixIndexPatternWithCcs } from '../../../../../common/ccs_utils'; -import { metricSets } from './metric_set_node_detail'; -import { getLogs } from '../../../../lib/logs/get_logs'; -import { CCS_REMOTE_PATTERN } from '../../../../../common/constants'; - -const { advanced: metricSetAdvanced, overview: metricSetOverview } = metricSets; - -export function esNodeRoute(server) { - server.route({ - method: 'POST', - path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch/nodes/{nodeUuid}', - config: { - validate: { - params: schema.object({ - clusterUuid: schema.string(), - nodeUuid: schema.string(), - }), - body: schema.object({ - ccs: schema.maybe(schema.string()), - showSystemIndices: schema.boolean({ defaultValue: false }), // show/hide system indices in shard allocation table - timeRange: schema.object({ - min: schema.string(), - max: schema.string(), - }), - is_advanced: schema.boolean(), - }), - }, - }, - async handler(req) { - const config = server.config; - const ccs = req.payload.ccs; - const showSystemIndices = req.payload.showSystemIndices; - const clusterUuid = req.params.clusterUuid; - const nodeUuid = req.params.nodeUuid; - const start = req.payload.timeRange.min; - const end = req.payload.timeRange.max; - const filebeatIndexPattern = prefixIndexPatternWithCcs( - config, - config.ui.logs.index, - CCS_REMOTE_PATTERN - ); - const isAdvanced = req.payload.is_advanced; - - let metricSet; - if (isAdvanced) { - metricSet = metricSetAdvanced; - } else { - metricSet = metricSetOverview; - // set the cgroup option if needed - const showCgroupMetricsElasticsearch = config.ui.container.elasticsearch.enabled; - const metricCpu = metricSet.find((m) => m.name === 'node_cpu_metric'); - if (showCgroupMetricsElasticsearch) { - metricCpu.keys = ['node_cgroup_quota_as_cpu_utilization']; - } else { - metricCpu.keys = ['node_cpu_utilization']; - } - } - - try { - const cluster = await getClusterStats(req, clusterUuid, ccs); - - const clusterState = get( - cluster, - 'cluster_state', - get(cluster, 'elasticsearch.cluster.stats.state') - ); - - const shardStats = await getShardStats(req, cluster, { - includeIndices: true, - includeNodes: true, - nodeUuid, - }); - const nodeSummary = await getNodeSummary(req, clusterState, shardStats, { - clusterUuid, - nodeUuid, - start, - end, - }); - const metrics = await getMetrics(req, 'elasticsearch', metricSet, [ - { term: { 'source_node.uuid': nodeUuid } }, - ]); - let logs; - let shardAllocation; - if (!isAdvanced) { - // TODO: Why so many fields needed for a single component (shard legend)? - const shardFilter = { - bool: { - should: [ - { term: { 'shard.node': nodeUuid } }, - { term: { 'elasticsearch.node.name': nodeUuid } }, - ], - }, - }; - const stateUuid = get( - cluster, - 'cluster_state.state_uuid', - get(cluster, 'elasticsearch.cluster.stats.state.state_uuid') - ); - const allocationOptions = { - shardFilter, - stateUuid, - showSystemIndices, - }; - const shards = await getShardAllocation(req, allocationOptions); - - shardAllocation = { - shards, - shardStats: { indices: shardStats.indices }, - nodes: shardStats.nodes, // for identifying nodes that shard relocates to - stateUuid, // for debugging/troubleshooting - }; - - logs = await getLogs(config, req, filebeatIndexPattern, { - clusterUuid, - nodeUuid, - start, - end, - }); - } - - return { - nodeSummary, - metrics, - logs, - ...shardAllocation, - }; - } catch (err) { - throw handleError(err, req); - } - }, - }); -} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/node_detail.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/node_detail.ts new file mode 100644 index 0000000000000..85ba1d1a2bb8e --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/node_detail.ts @@ -0,0 +1,149 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { get } from 'lodash'; +import { prefixIndexPatternWithCcs } from '../../../../../common/ccs_utils'; +import { CCS_REMOTE_PATTERN } from '../../../../../common/constants'; +import { + postElasticsearchNodeDetailRequestParamsRT, + postElasticsearchNodeDetailRequestPayloadRT, + postElasticsearchNodeDetailResponsePayloadRT, +} from '../../../../../common/http_api/elasticsearch'; +import { getClusterStats } from '../../../../lib/cluster/get_cluster_stats'; +import { createValidationFunction } from '../../../../lib/create_route_validation_function'; +import { + getMetrics, + MetricDescriptor, + NamedMetricDescriptor, +} from '../../../../lib/details/get_metrics'; +import { getNodeSummary } from '../../../../lib/elasticsearch/nodes'; +import { getShardAllocation, getShardStats } from '../../../../lib/elasticsearch/shards'; +import { handleError } from '../../../../lib/errors/handle_error'; +import { getLogs } from '../../../../lib/logs/get_logs'; +import { MonitoringCore } from '../../../../types'; +import { metricSets } from './metric_set_node_detail'; + +const { advanced: metricSetAdvanced, overview: metricSetOverview } = metricSets; + +export function esNodeRoute(server: MonitoringCore) { + const validateParams = createValidationFunction(postElasticsearchNodeDetailRequestParamsRT); + const validateBody = createValidationFunction(postElasticsearchNodeDetailRequestPayloadRT); + + server.route({ + method: 'post', + path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch/nodes/{nodeUuid}', + validate: { + params: validateParams, + body: validateBody, + }, + async handler(req) { + const config = server.config; + const showSystemIndices = req.payload.showSystemIndices ?? false; + const clusterUuid = req.params.clusterUuid; + const nodeUuid = req.params.nodeUuid; + const start = req.payload.timeRange.min; + const end = req.payload.timeRange.max; + const filebeatIndexPattern = prefixIndexPatternWithCcs( + config, + config.ui.logs.index, + CCS_REMOTE_PATTERN + ); + const isAdvanced = req.payload.is_advanced; + + let metricSet: MetricDescriptor[]; + if (isAdvanced) { + metricSet = metricSetAdvanced; + } else { + metricSet = metricSetOverview; + // set the cgroup option if needed + const showCgroupMetricsElasticsearch = config.ui.container.elasticsearch.enabled; + const metricCpu = metricSet.find( + (m): m is NamedMetricDescriptor => typeof m === 'object' && m.name === 'node_cpu_metric' + ); + if (metricCpu) { + if (showCgroupMetricsElasticsearch) { + metricCpu.keys = ['node_cgroup_quota_as_cpu_utilization']; + } else { + metricCpu.keys = ['node_cpu_utilization']; + } + } + } + + try { + const cluster = await getClusterStats(req, clusterUuid); + + const clusterState = get( + cluster, + 'cluster_state', + get(cluster, 'elasticsearch.cluster.stats.state') + ); + + const shardStats = await getShardStats(req, cluster, { + includeIndices: true, + includeNodes: true, + nodeUuid, + }); + const nodeSummary = await getNodeSummary(req, clusterState, shardStats, { + clusterUuid, + nodeUuid, + start, + end, + }); + const metrics = await getMetrics(req, 'elasticsearch', metricSet, [ + { term: { 'source_node.uuid': nodeUuid } }, + ]); + let logs; + let shardAllocation; + if (!isAdvanced) { + // TODO: Why so many fields needed for a single component (shard legend)? + const shardFilter = { + bool: { + should: [ + { term: { 'shard.node': nodeUuid } }, + { term: { 'elasticsearch.node.name': nodeUuid } }, + ], + }, + }; + const stateUuid = get( + cluster, + 'cluster_state.state_uuid', + get(cluster, 'elasticsearch.cluster.stats.state.state_uuid') + ); + const allocationOptions = { + shardFilter, + stateUuid, + showSystemIndices, + }; + const shards = await getShardAllocation(req, allocationOptions); + + shardAllocation = { + shards, + shardStats: { indices: shardStats.indices }, + nodes: shardStats.nodes, // for identifying nodes that shard relocates to + stateUuid, // for debugging/troubleshooting + }; + + logs = await getLogs(config, req, filebeatIndexPattern, { + clusterUuid, + nodeUuid, + start, + end, + }); + } + + return postElasticsearchNodeDetailResponsePayloadRT.encode({ + nodeSummary, + metrics, + logs, + ...shardAllocation, + }); + } catch (err) { + throw handleError(err, req); + } + }, + }); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/nodes.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/nodes.js deleted file mode 100644 index fa0329f957f54..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/nodes.js +++ /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 { schema } from '@kbn/config-schema'; -import { getClusterStats } from '../../../../lib/cluster/get_cluster_stats'; -import { getClusterStatus } from '../../../../lib/cluster/get_cluster_status'; -import { getNodes } from '../../../../lib/elasticsearch/nodes'; -import { getNodesShardCount } from '../../../../lib/elasticsearch/shards/get_nodes_shard_count'; -import { handleError } from '../../../../lib/errors/handle_error'; -import { getPaginatedNodes } from '../../../../lib/elasticsearch/nodes/get_nodes/get_paginated_nodes'; -import { LISTING_METRICS_NAMES } from '../../../../lib/elasticsearch/nodes/get_nodes/nodes_listing_metrics'; -import { getIndicesUnassignedShardStats } from '../../../../lib/elasticsearch/shards/get_indices_unassigned_shard_stats'; - -export function esNodesRoute(server) { - server.route({ - method: 'POST', - path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch/nodes', - config: { - validate: { - params: schema.object({ - clusterUuid: schema.string(), - }), - body: schema.object({ - ccs: schema.maybe(schema.string()), - timeRange: schema.object({ - min: schema.string(), - max: schema.string(), - }), - pagination: schema.object({ - index: schema.number(), - size: schema.number(), - }), - sort: schema.object({ - field: schema.string({ defaultValue: '' }), - direction: schema.string({ defaultValue: '' }), - }), - queryText: schema.string({ defaultValue: '' }), - }), - }, - }, - async handler(req) { - const { ccs, pagination, sort, queryText } = req.payload; - const clusterUuid = req.params.clusterUuid; - - try { - const clusterStats = await getClusterStats(req, clusterUuid); - const nodesShardCount = await getNodesShardCount(req, clusterStats); - const indicesUnassignedShardStats = await getIndicesUnassignedShardStats(req, clusterStats); - const clusterStatus = getClusterStatus(clusterStats, indicesUnassignedShardStats); - - const metricSet = LISTING_METRICS_NAMES; - const { pageOfNodes, totalNodeCount } = await getPaginatedNodes( - req, - { clusterUuid }, - metricSet, - pagination, - sort, - queryText, - { - clusterStats, - nodesShardCount, - }, - ccs - ); - - const nodes = await getNodes(req, pageOfNodes, clusterStats, nodesShardCount); - return { clusterStatus, nodes, totalNodeCount }; - } catch (err) { - throw handleError(err, req); - } - }, - }); -} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/nodes.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/nodes.ts new file mode 100644 index 0000000000000..f291af318bf9b --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/nodes.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + postElasticsearchNodesRequestParamsRT, + postElasticsearchNodesRequestPayloadRT, + postElasticsearchNodesResponsePayloadRT, +} from '../../../../../common/http_api/elasticsearch'; +import { getClusterStats } from '../../../../lib/cluster/get_cluster_stats'; +import { getClusterStatus } from '../../../../lib/cluster/get_cluster_status'; +import { createValidationFunction } from '../../../../lib/create_route_validation_function'; +import { getNodes } from '../../../../lib/elasticsearch/nodes'; +import { getPaginatedNodes } from '../../../../lib/elasticsearch/nodes/get_nodes/get_paginated_nodes'; +import { LISTING_METRICS_NAMES } from '../../../../lib/elasticsearch/nodes/get_nodes/nodes_listing_metrics'; +import { getIndicesUnassignedShardStats } from '../../../../lib/elasticsearch/shards/get_indices_unassigned_shard_stats'; +import { getNodesShardCount } from '../../../../lib/elasticsearch/shards/get_nodes_shard_count'; +import { handleError } from '../../../../lib/errors/handle_error'; +import { MonitoringCore } from '../../../../types'; + +export function esNodesRoute(server: MonitoringCore) { + const validateParams = createValidationFunction(postElasticsearchNodesRequestParamsRT); + const validateBody = createValidationFunction(postElasticsearchNodesRequestPayloadRT); + + server.route({ + method: 'post', + path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch/nodes', + validate: { + params: validateParams, + body: validateBody, + }, + async handler(req) { + const { + pagination, + sort: { field = '', direction = 'asc' } = {}, + queryText = '', + } = req.payload; + const clusterUuid = req.params.clusterUuid; + + try { + const clusterStats = await getClusterStats(req, clusterUuid); + const nodesShardCount = await getNodesShardCount(req, clusterStats); + const indicesUnassignedShardStats = await getIndicesUnassignedShardStats(req, clusterStats); + const clusterStatus = getClusterStatus(clusterStats, indicesUnassignedShardStats); + + const metricSet = LISTING_METRICS_NAMES; + const { pageOfNodes, totalNodeCount } = await getPaginatedNodes( + req, + { clusterUuid }, + metricSet, + pagination, + { + field, + direction, + }, + queryText, + { + clusterStats, + nodesShardCount, + } + ); + + const nodes = await getNodes(req, pageOfNodes, clusterStats, nodesShardCount); + return postElasticsearchNodesResponsePayloadRT.encode({ + clusterStatus, + nodes, + totalNodeCount, + }); + } catch (err) { + throw handleError(err, req); + } + }, + }); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/overview.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/overview.js deleted file mode 100644 index 35066bb33784d..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/overview.js +++ /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 { schema } from '@kbn/config-schema'; -import { getClusterStats } from '../../../../lib/cluster/get_cluster_stats'; -import { getClusterStatus } from '../../../../lib/cluster/get_cluster_status'; -import { getLastRecovery } from '../../../../lib/elasticsearch/get_last_recovery'; -import { getMetrics } from '../../../../lib/details/get_metrics'; -import { handleError } from '../../../../lib/errors/handle_error'; -import { prefixIndexPatternWithCcs } from '../../../../../common/ccs_utils'; -import { metricSet } from './metric_set_overview'; -import { getLogs } from '../../../../lib/logs'; -import { getIndicesUnassignedShardStats } from '../../../../lib/elasticsearch/shards/get_indices_unassigned_shard_stats'; -import { CCS_REMOTE_PATTERN } from '../../../../../common/constants'; - -export function esOverviewRoute(server) { - server.route({ - method: 'POST', - path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch', - config: { - validate: { - params: schema.object({ - clusterUuid: schema.string(), - }), - body: schema.object({ - ccs: schema.maybe(schema.string()), - timeRange: schema.object({ - min: schema.string(), - max: schema.string(), - }), - }), - }, - }, - async handler(req) { - const config = server.config; - const clusterUuid = req.params.clusterUuid; - const filebeatIndexPattern = prefixIndexPatternWithCcs( - config, - config.ui.logs.index, - CCS_REMOTE_PATTERN - ); - - const start = req.payload.timeRange.min; - const end = req.payload.timeRange.max; - - try { - const [clusterStats, metrics, shardActivity, logs] = await Promise.all([ - getClusterStats(req, clusterUuid), - getMetrics(req, 'elasticsearch', metricSet), - getLastRecovery(req, config.ui.max_bucket_size), - // TODO this call is missing some items from the signature of `getLogs`, will need to resolve during TS conversion - getLogs(config, req, filebeatIndexPattern, { clusterUuid, start, end }), - ]); - const indicesUnassignedShardStats = await getIndicesUnassignedShardStats(req, clusterStats); - - const result = { - clusterStatus: getClusterStatus(clusterStats, indicesUnassignedShardStats), - metrics, - logs, - shardActivity, - }; - return result; - } catch (err) { - throw handleError(err, req); - } - }, - }); -} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/overview.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/overview.ts new file mode 100644 index 0000000000000..52410dd09f8df --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/overview.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { prefixIndexPatternWithCcs } from '../../../../../common/ccs_utils'; +import { CCS_REMOTE_PATTERN } from '../../../../../common/constants'; +import { + postElasticsearchOverviewRequestParamsRT, + postElasticsearchOverviewRequestPayloadRT, + postElasticsearchOverviewResponsePayloadRT, +} from '../../../../../common/http_api/elasticsearch'; +import { getClusterStats } from '../../../../lib/cluster/get_cluster_stats'; +import { getClusterStatus } from '../../../../lib/cluster/get_cluster_status'; +import { createValidationFunction } from '../../../../lib/create_route_validation_function'; +import { getMetrics } from '../../../../lib/details/get_metrics'; +import { getLastRecovery } from '../../../../lib/elasticsearch/get_last_recovery'; +import { getIndicesUnassignedShardStats } from '../../../../lib/elasticsearch/shards/get_indices_unassigned_shard_stats'; +import { handleError } from '../../../../lib/errors/handle_error'; +import { getLogs } from '../../../../lib/logs'; +import { MonitoringCore } from '../../../../types'; +import { metricSet } from './metric_set_overview'; + +export function esOverviewRoute(server: MonitoringCore) { + const validateParams = createValidationFunction(postElasticsearchOverviewRequestParamsRT); + const validateBody = createValidationFunction(postElasticsearchOverviewRequestPayloadRT); + + server.route({ + method: 'post', + path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch', + validate: { + params: validateParams, + body: validateBody, + }, + async handler(req) { + const config = server.config; + const clusterUuid = req.params.clusterUuid; + const filebeatIndexPattern = prefixIndexPatternWithCcs( + config, + config.ui.logs.index, + CCS_REMOTE_PATTERN + ); + const { min: start, max: end } = req.payload.timeRange; + + try { + const [clusterStats, metrics, shardActivity, logs] = await Promise.all([ + getClusterStats(req, clusterUuid), + getMetrics(req, 'elasticsearch', metricSet), + getLastRecovery(req, config.ui.max_bucket_size), + // TODO this call is missing some items from the signature of `getLogs`, will need to resolve during TS conversion + getLogs(config, req, filebeatIndexPattern, { clusterUuid, start, end }), + ]); + const indicesUnassignedShardStats = await getIndicesUnassignedShardStats(req, clusterStats); + + const result = { + clusterStatus: getClusterStatus(clusterStats, indicesUnassignedShardStats), + metrics, + logs, + shardActivity, + }; + return postElasticsearchOverviewResponsePayloadRT.encode(result); + } catch (err) { + throw handleError(err, req); + } + }, + }); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/cluster.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/cluster.js deleted file mode 100644 index 6996c4885d25d..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/cluster.js +++ /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 { checkClusterSettings } from '../../../../../lib/elasticsearch_settings'; -import { handleSettingsError } from '../../../../../lib/errors'; - -/* - * Cluster Settings Check Route - */ -export function clusterSettingsCheckRoute(server) { - server.route({ - method: 'GET', - path: '/api/monitoring/v1/elasticsearch_settings/check/cluster', - config: { - validate: {}, - }, - async handler(req) { - try { - const response = await checkClusterSettings(req); // needs to be try/catch to handle privilege error - return response; - } catch (err) { - console.log(err); - throw handleSettingsError(err); - } - }, - }); -} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/cluster.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/cluster.ts new file mode 100644 index 0000000000000..df2fafa2a952c --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/cluster.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 { getElasticsearchSettingsClusterResponsePayloadRT } from '../../../../../../common/http_api/elasticsearch_settings'; +import { checkClusterSettings } from '../../../../../lib/elasticsearch_settings'; +import { handleSettingsError } from '../../../../../lib/errors'; +import { MonitoringCore } from '../../../../../types'; + +/* + * Cluster Settings Check Route + */ +export function clusterSettingsCheckRoute(server: MonitoringCore) { + server.route({ + method: 'get', + path: '/api/monitoring/v1/elasticsearch_settings/check/cluster', + validate: {}, + async handler(req) { + try { + const response = await checkClusterSettings(req); // needs to be try/catch to handle privilege error + return getElasticsearchSettingsClusterResponsePayloadRT.encode(response); + } catch (err) { + server.log.error(err); + throw handleSettingsError(err); + } + }, + }); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/internal_monitoring.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/internal_monitoring.ts index 8bee3f273e107..11e0eec3f08f0 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/internal_monitoring.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/internal_monitoring.ts @@ -5,17 +5,21 @@ * 2.0. */ -import { schema } from '@kbn/config-schema'; -import { RequestHandlerContext } from '@kbn/core/server'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { RequestHandlerContext } from '@kbn/core/server'; +import { prefixIndexPatternWithCcs } from '../../../../../../common/ccs_utils'; import { INDEX_PATTERN_ELASTICSEARCH, INDEX_PATTERN_KIBANA, INDEX_PATTERN_LOGSTASH, } from '../../../../../../common/constants'; -import { prefixIndexPatternWithCcs } from '../../../../../../common/ccs_utils'; +import { + postElasticsearchSettingsInternalMonitoringRequestPayloadRT, + postElasticsearchSettingsInternalMonitoringResponsePayloadRT, +} from '../../../../../../common/http_api/elasticsearch_settings'; +import { createValidationFunction } from '../../../../../lib/create_route_validation_function'; import { handleError } from '../../../../../lib/errors'; -import { RouteDependencies, LegacyServer } from '../../../../../types'; +import { LegacyServer, RouteDependencies } from '../../../../../types'; const queryBody = { size: 0, @@ -69,13 +73,15 @@ const checkLatestMonitoringIsLegacy = async (context: RequestHandlerContext, ind }; export function internalMonitoringCheckRoute(server: LegacyServer, npRoute: RouteDependencies) { + const validateBody = createValidationFunction( + postElasticsearchSettingsInternalMonitoringRequestPayloadRT + ); + npRoute.router.post( { path: '/api/monitoring/v1/elasticsearch_settings/check/internal_monitoring', validate: { - body: schema.object({ - ccs: schema.maybe(schema.string()), - }), + body: validateBody, }, }, async (context, request, response) => { @@ -101,9 +107,11 @@ export function internalMonitoringCheckRoute(server: LegacyServer, npRoute: Rout typeCount.mb_indices += counts.mbIndicesCount; }); - return response.ok({ - body: typeCount, - }); + return response.ok( + postElasticsearchSettingsInternalMonitoringResponsePayloadRT.encode({ + body: typeCount, + }) + ); } catch (err) { throw handleError(err); } diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/nodes.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/nodes.js deleted file mode 100644 index fe675302a982f..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/nodes.js +++ /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 { checkNodesSettings } from '../../../../../lib/elasticsearch_settings'; -import { handleSettingsError } from '../../../../../lib/errors'; - -/* - * Cluster Settings Check Route - */ -export function nodesSettingsCheckRoute(server) { - server.route({ - method: 'GET', - path: '/api/monitoring/v1/elasticsearch_settings/check/nodes', - config: { - validate: {}, - }, - async handler(req) { - try { - const response = await checkNodesSettings(req); // needs to be try/catch to handle privilege error - return response; - } catch (err) { - throw handleSettingsError(err); - } - }, - }); -} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/nodes.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/nodes.ts new file mode 100644 index 0000000000000..90c37c6f910c9 --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/nodes.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 { getElasticsearchSettingsNodesResponsePayloadRT } from '../../../../../../common/http_api/elasticsearch_settings'; +import { checkNodesSettings } from '../../../../../lib/elasticsearch_settings'; +import { handleSettingsError } from '../../../../../lib/errors'; +import { MonitoringCore } from '../../../../../types'; + +/* + * Cluster Settings Check Route + */ +export function nodesSettingsCheckRoute(server: MonitoringCore) { + server.route({ + method: 'get', + path: '/api/monitoring/v1/elasticsearch_settings/check/nodes', + validate: {}, + async handler(req) { + try { + const response = await checkNodesSettings(req); // needs to be try/catch to handle privilege error + return getElasticsearchSettingsNodesResponsePayloadRT.encode(response); + } catch (err) { + throw handleSettingsError(err); + } + }, + }); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/index.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/index.js deleted file mode 100644 index 8eb50a57fb858..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/index.js +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { internalMonitoringCheckRoute } from './check/internal_monitoring'; -export { clusterSettingsCheckRoute } from './check/cluster'; -export { nodesSettingsCheckRoute } from './check/nodes'; -export { setCollectionEnabledRoute } from './set/collection_enabled'; -export { setCollectionIntervalRoute } from './set/collection_interval'; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/index.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/index.ts new file mode 100644 index 0000000000000..61bb1ba804a5a --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/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 { clusterSettingsCheckRoute } from './check/cluster'; +export { internalMonitoringCheckRoute } from './check/internal_monitoring'; +export { nodesSettingsCheckRoute } from './check/nodes'; +export { setCollectionEnabledRoute } from './set/collection_enabled'; +export { setCollectionIntervalRoute } from './set/collection_interval'; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/set/collection_enabled.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/set/collection_enabled.js deleted file mode 100644 index c8bf24156f129..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/set/collection_enabled.js +++ /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 { setCollectionEnabled } from '../../../../../lib/elasticsearch_settings'; -import { handleSettingsError } from '../../../../../lib/errors'; - -/* - * Cluster Settings Check Route - */ -export function setCollectionEnabledRoute(server) { - server.route({ - method: 'PUT', - path: '/api/monitoring/v1/elasticsearch_settings/set/collection_enabled', - config: { - validate: {}, - }, - async handler(req) { - try { - const response = await setCollectionEnabled(req); - return response; - } catch (err) { - throw handleSettingsError(err); - } - }, - }); -} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/set/collection_enabled.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/set/collection_enabled.ts new file mode 100644 index 0000000000000..941818699ede2 --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/set/collection_enabled.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 { putElasticsearchSettingsCollectionEnabledResponsePayloadRT } from '../../../../../../common/http_api/elasticsearch_settings'; +import { setCollectionEnabled } from '../../../../../lib/elasticsearch_settings'; +import { handleSettingsError } from '../../../../../lib/errors'; +import { MonitoringCore } from '../../../../../types'; + +/* + * Cluster Settings Check Route + */ +export function setCollectionEnabledRoute(server: MonitoringCore) { + server.route({ + method: 'put', + path: '/api/monitoring/v1/elasticsearch_settings/set/collection_enabled', + validate: {}, + async handler(req) { + try { + const response = await setCollectionEnabled(req); + return putElasticsearchSettingsCollectionEnabledResponsePayloadRT.encode(response); + } catch (err) { + throw handleSettingsError(err); + } + }, + }); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/set/collection_interval.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/set/collection_interval.js deleted file mode 100644 index 60216650062c0..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/set/collection_interval.js +++ /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 { setCollectionInterval } from '../../../../../lib/elasticsearch_settings'; -import { handleSettingsError } from '../../../../../lib/errors'; - -/* - * Cluster Settings Check Route - */ -export function setCollectionIntervalRoute(server) { - server.route({ - method: 'PUT', - path: '/api/monitoring/v1/elasticsearch_settings/set/collection_interval', - config: { - validate: {}, - }, - async handler(req) { - try { - const response = await setCollectionInterval(req); - return response; - } catch (err) { - throw handleSettingsError(err); - } - }, - }); -} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/set/collection_interval.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/set/collection_interval.ts new file mode 100644 index 0000000000000..eb4798efc36cc --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/set/collection_interval.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 { putElasticsearchSettingsCollectionIntervalResponsePayloadRT } from '../../../../../../common/http_api/elasticsearch_settings'; +import { setCollectionInterval } from '../../../../../lib/elasticsearch_settings'; +import { handleSettingsError } from '../../../../../lib/errors'; +import { MonitoringCore } from '../../../../../types'; + +/* + * Cluster Settings Check Route + */ +export function setCollectionIntervalRoute(server: MonitoringCore) { + server.route({ + method: 'put', + path: '/api/monitoring/v1/elasticsearch_settings/set/collection_interval', + validate: {}, + async handler(req) { + try { + const response = await setCollectionInterval(req); + return putElasticsearchSettingsCollectionIntervalResponsePayloadRT.encode(response); + } catch (err) { + throw handleSettingsError(err); + } + }, + }); +} diff --git a/x-pack/plugins/monitoring/server/static_globals.ts b/x-pack/plugins/monitoring/server/static_globals.ts index e601dd0c55155..429ec8693a3e7 100644 --- a/x-pack/plugins/monitoring/server/static_globals.ts +++ b/x-pack/plugins/monitoring/server/static_globals.ts @@ -6,7 +6,6 @@ */ import { CoreSetup, ElasticsearchClient, Logger, PluginInitializerContext } from '@kbn/core/server'; -import url from 'url'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { MonitoringConfig } from './config'; import { PluginsSetup } from './types'; @@ -32,7 +31,7 @@ export type EndpointTypes = export type ClientParams = estypes.SearchRequest | undefined; interface IAppGlobals { - url: string; + url?: string; isCloud: boolean; config: MonitoringConfig; getLogger: GetLogger; @@ -79,11 +78,8 @@ export class Globals { return body; }); - const { protocol, hostname, port } = coreSetup.http.getServerInfo(); - const pathname = coreSetup.http.basePath.serverBasePath; - Globals._app = { - url: url.format({ protocol, hostname, port, pathname }), + url: coreSetup.http.basePath.publicBaseUrl, isCloud: setupPlugins.cloud?.isCloudEnabled || false, config, getLogger, diff --git a/x-pack/plugins/monitoring/server/types.ts b/x-pack/plugins/monitoring/server/types.ts index bcd40fe38e415..5977484518146 100644 --- a/x-pack/plugins/monitoring/server/types.ts +++ b/x-pack/plugins/monitoring/server/types.ts @@ -80,16 +80,22 @@ export interface RouteDependencies { logger: Logger; } -export type MonitoringRouteConfig = { - method: RouteMethod; -} & RouteConfig & { - handler: (request: LegacyRequest) => any; - }; +type LegacyHandler = (req: LegacyRequest) => Promise; + +export type MonitoringRouteConfig = RouteConfig< + Params, + Query, + Body, + Method +> & { + method: Method; + handler: LegacyHandler; +}; export interface MonitoringCore { config: MonitoringConfig; log: Logger; - route: ( + route: ( options: MonitoringRouteConfig ) => void; } @@ -112,15 +118,12 @@ export interface MonitoringPluginSetup { getKibanaStats: IBulkUploader['getKibanaStats']; } -export interface LegacyRequest { +export interface LegacyRequest { logger: Logger; getLogger: (...scopes: string[]) => Logger; - payload: { - [key: string]: any; - }; - params: { - [key: string]: string; - }; + payload: Body; + params: Params; + query: Query; getKibanaStatsCollector: () => any; getUiSettingsService: () => any; getActionTypeRegistry: () => any; diff --git a/x-pack/plugins/observability/common/ui_settings_keys.ts b/x-pack/plugins/observability/common/ui_settings_keys.ts index 4c1b1dc729fea..287fe541cc7b6 100644 --- a/x-pack/plugins/observability/common/ui_settings_keys.ts +++ b/x-pack/plugins/observability/common/ui_settings_keys.ts @@ -5,6 +5,7 @@ * 2.0. */ +export const enableNewSyntheticsView = 'observability:enableNewSyntheticsView'; export const enableInspectEsQueries = 'observability:enableInspectEsQueries'; export const maxSuggestions = 'observability:maxSuggestions'; export const enableComparisonByDefault = 'observability:enableComparisonByDefault'; diff --git a/x-pack/plugins/observability/public/application/application.test.tsx b/x-pack/plugins/observability/public/application/application.test.tsx index a3160d713d1b7..a03f007c2d751 100644 --- a/x-pack/plugins/observability/public/application/application.test.tsx +++ b/x-pack/plugins/observability/public/application/application.test.tsx @@ -63,7 +63,6 @@ describe('renderApp', () => { unsafe: { alertingExperience: { enabled: true }, cases: { enabled: true }, - overviewNext: { enabled: false }, rules: { enabled: true }, }, }; diff --git a/x-pack/plugins/observability/public/components/app/observability_status/observability_status_progress.tsx b/x-pack/plugins/observability/public/components/app/observability_status/observability_status_progress.tsx index 04e77669e963c..050da44457969 100644 --- a/x-pack/plugins/observability/public/components/app/observability_status/observability_status_progress.tsx +++ b/x-pack/plugins/observability/public/components/app/observability_status/observability_status_progress.tsx @@ -24,9 +24,11 @@ import { useGuidedSetupProgress } from '../../../hooks/use_guided_setup_progress interface ObservabilityStatusProgressProps { onViewDetailsClick: () => void; + onDismissClick?: () => void; } export function ObservabilityStatusProgress({ onViewDetailsClick, + onDismissClick, }: ObservabilityStatusProgressProps) { const { hasDataMap, isAllRequestsComplete } = useHasData(); const trackMetric = useUiTracker({ app: 'observability-overview' }); @@ -52,8 +54,11 @@ export function ObservabilityStatusProgress({ const dismissGuidedSetup = useCallback(() => { dismissGuidedSetupProgress(); + if (onDismissClick) { + onDismissClick(); + } trackMetric({ metric: 'guided_setup_progress_dismiss' }); - }, [dismissGuidedSetupProgress, trackMetric]); + }, [dismissGuidedSetupProgress, trackMetric, onDismissClick]); const showDetails = () => { onViewDetailsClick(); diff --git a/x-pack/plugins/observability/public/components/app/section/alerts/index.tsx b/x-pack/plugins/observability/public/components/app/section/alerts/index.tsx deleted file mode 100644 index 9c65df16d9060..0000000000000 --- a/x-pack/plugins/observability/public/components/app/section/alerts/index.tsx +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - EuiBadge, - EuiFlexGroup, - EuiFlexItem, - EuiLink, - EuiText, - EuiSpacer, - EuiTitle, - EuiButtonEmpty, - EuiLoadingSpinner, - EuiCallOut, - EuiPanel, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import moment from 'moment'; -import React, { useState, useMemo } from 'react'; -import { EuiSelect } from '@elastic/eui'; -import { uniqBy } from 'lodash'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { usePluginContext } from '../../../../hooks/use_plugin_context'; -import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; -import { getObservabilityAlerts } from '../../../../services/get_observability_alerts'; -import { paths } from '../../../../config'; -import { ObservabilityAppServices } from '../../../../application/types'; - -const ALL_TYPES = 'ALL_TYPES'; -const allTypes = { - value: ALL_TYPES, - text: i18n.translate('xpack.observability.overview.alert.allTypes', { - defaultMessage: 'All types', - }), -}; - -export function AlertsSection() { - const { config } = usePluginContext(); - const { http } = useKibana().services; - const [filter, setFilter] = useState(ALL_TYPES); - const manageLink = config.unsafe.alertingExperience.enabled - ? http.basePath.prepend(paths.observability.alerts) - : http.basePath.prepend(paths.management.rules); - - const { data, status } = useFetcher(() => getObservabilityAlerts({ http }), [http]); - - const alerts = useMemo(() => data ?? [], [data]); - - const filterOptions = useMemo(() => { - if (!alerts) { - return []; - } - return uniqBy(alerts, (alert) => alert.consumer).map(({ consumer }) => ({ - value: consumer, - text: consumer, - })); - }, [alerts]); - - const isError = status === FETCH_STATUS.FAILURE; - const isLoading = status !== FETCH_STATUS.SUCCESS && !isError; - - if (isLoading) { - return ( - - - - - - ); - } - - if (isError) { - return ( - - - -

- -

-
-
-
- ); - } - - return ( -
- - - -

- {i18n.translate('xpack.observability.overview.alerts.title', { - defaultMessage: 'Alerts', - })} -

-
-
- - - {i18n.translate('xpack.observability.overview.alert.appLink', { - defaultMessage: 'Show all alerts', - })} - - -
- <> - - - setFilter(e.target.value)} - prepend="Show" - /> - - - {alerts - .filter((alert) => filter === ALL_TYPES || alert.consumer === filter) - .map((alert, index) => { - return ( - - - - - - - {alert.name} - - - - - - {alert.alertTypeId} - - {alert.tags.map((tag, idx) => { - return ( - - {tag} - - ); - })} - - - - - {alert.muteAll && ( - - - {i18n.translate('xpack.observability.overview.alerts.muted', { - defaultMessage: 'Muted', - })} - - - )} - - - Last updated{' '} - {moment.duration(moment().diff(alert.updatedAt)).humanize()} ago - - - - - - - - - ); - })} - -
- ); -} diff --git a/x-pack/plugins/observability/public/components/app/section/apm/index.test.tsx b/x-pack/plugins/observability/public/components/app/section/apm/index.test.tsx index a8f49f7d9cbb4..179e8ef70deb1 100644 --- a/x-pack/plugins/observability/public/components/app/section/apm/index.test.tsx +++ b/x-pack/plugins/observability/public/components/app/section/apm/index.test.tsx @@ -48,7 +48,6 @@ describe('APMSection', () => { unsafe: { alertingExperience: { enabled: true }, cases: { enabled: true }, - overviewNext: { enabled: false }, rules: { enabled: true }, }, }, diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts index 072f6e42d1cd8..ad06e5b628eea 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts @@ -13,7 +13,7 @@ import type { FieldBasedIndexPatternColumn, SeriesType, OperationType, - YConfig, + ExtendedYConfig, } from '@kbn/lens-plugin/public'; import type { PersistableFilter } from '@kbn/lens-plugin/common'; @@ -71,7 +71,7 @@ export interface SeriesConfig { hasOperationType: boolean; palette?: PaletteOutput; yTitle?: string; - yConfig?: YConfig[]; + yConfig?: ExtendedYConfig[]; query?: { query: string; language: 'kuery' }; } diff --git a/x-pack/plugins/observability/public/config/register_alerts_table_configuration.tsx b/x-pack/plugins/observability/public/config/register_alerts_table_configuration.tsx new file mode 100644 index 0000000000000..1999fe82e3a6d --- /dev/null +++ b/x-pack/plugins/observability/public/config/register_alerts_table_configuration.tsx @@ -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 { AlertsTableConfigurationRegistryContract } from '@kbn/triggers-actions-ui-plugin/public'; + +import { observabilityFeatureId } from '../../common'; +import { columns as alertO11yColumns } from '../pages/alerts/containers/alerts_table_t_grid/alerts_table_t_grid'; + +const registerAlertsTableConfiguration = (registry: AlertsTableConfigurationRegistryContract) => { + if (registry.has(observabilityFeatureId)) { + return; + } + registry.register({ + id: observabilityFeatureId, + columns: alertO11yColumns, + }); +}; + +export { registerAlertsTableConfiguration }; diff --git a/x-pack/plugins/observability/public/config/translations.ts b/x-pack/plugins/observability/public/config/translations.ts index db1378d5b5a54..13f6470ecb202 100644 --- a/x-pack/plugins/observability/public/config/translations.ts +++ b/x-pack/plugins/observability/public/config/translations.ts @@ -65,6 +65,12 @@ export const translations = { defaultMessage: 'View rule details', } ), + viewAlertDetailsButtonText: i18n.translate( + 'xpack.observability.alertsTable.viewAlertDetailsButtonText', + { + defaultMessage: 'View alert details', + } + ), }, alertsFlyout: { statusLabel: i18n.translate('xpack.observability.alertsFlyout.statusLabel', { diff --git a/x-pack/plugins/observability/public/hooks/use_fetch_rules.ts b/x-pack/plugins/observability/public/hooks/use_fetch_rules.ts index 00cb58e504bdc..de0127b08213e 100644 --- a/x-pack/plugins/observability/public/hooks/use_fetch_rules.ts +++ b/x-pack/plugins/observability/public/hooks/use_fetch_rules.ts @@ -16,6 +16,7 @@ import { useKibana } from '../utils/kibana_react'; export function useFetchRules({ searchText, ruleLastResponseFilter, + ruleStatusesFilter, typesFilter, setPage, page, @@ -42,7 +43,8 @@ export function useFetchRules({ page, searchText, typesFilter: typesFilter.length > 0 ? typesFilter : OBSERVABILITY_RULE_TYPES, - ruleStatusesFilter: ruleLastResponseFilter, + ruleExecutionStatusesFilter: ruleLastResponseFilter, + ruleStatusesFilter, sort, }); setRulesState((oldState) => ({ @@ -58,6 +60,7 @@ export function useFetchRules({ const isFilterApplied = !( isEmpty(searchText) && isEmpty(ruleLastResponseFilter) && + isEmpty(ruleStatusesFilter) && isEmpty(typesFilter) ); @@ -66,7 +69,16 @@ export function useFetchRules({ setRulesState((oldState) => ({ ...oldState, isLoading: false, error: RULES_LOAD_ERROR })); } setInitialLoad(false); - }, [http, page, setPage, searchText, ruleLastResponseFilter, typesFilter, sort]); + }, [ + http, + page, + setPage, + searchText, + ruleLastResponseFilter, + ruleStatusesFilter, + typesFilter, + sort, + ]); useEffect(() => { fetchRules(); }, [fetchRules]); diff --git a/x-pack/plugins/observability/public/index.ts b/x-pack/plugins/observability/public/index.ts index cfabbaaec4afa..00db5b1873980 100644 --- a/x-pack/plugins/observability/public/index.ts +++ b/x-pack/plugins/observability/public/index.ts @@ -28,6 +28,7 @@ export { enableComparisonByDefault, enableInfrastructureView, enableServiceGroups, + enableNewSyntheticsView, } from '../common/ui_settings_keys'; export { uptimeOverviewLocatorID } from '../common'; @@ -36,7 +37,6 @@ export interface ConfigSchema { alertingExperience: { enabled: boolean }; rules: { enabled: boolean }; cases: { enabled: boolean }; - overviewNext: { enabled: boolean }; }; } diff --git a/x-pack/plugins/observability/public/observability_public_plugins_start.mock.ts b/x-pack/plugins/observability/public/observability_public_plugins_start.mock.ts index 62edefc1b737d..50836859a5415 100644 --- a/x-pack/plugins/observability/public/observability_public_plugins_start.mock.ts +++ b/x-pack/plugins/observability/public/observability_public_plugins_start.mock.ts @@ -36,6 +36,9 @@ const triggersActionsUiStartMock = { createStart() { return { getAddAlertFlyout: jest.fn(), + getRuleStatusDropdown: jest.fn(), + getRuleTagBadge: jest.fn(), + getRuleStatusFilter: jest.fn(), ruleTypeRegistry: { has: jest.fn(), register: jest.fn(), diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/index.ts b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/index.ts new file mode 100644 index 0000000000000..b2b4e144952e0 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { renderRuleStats } from './rule_stats'; +export type { RuleStatsState } from './types'; diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx new file mode 100644 index 0000000000000..6f2edf5d0b1b6 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.test.tsx @@ -0,0 +1,199 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderRuleStats } from './rule_stats'; +import { render, screen } from '@testing-library/react'; + +const RULES_PAGE_LINK = '/app/observability/alerts/rules'; +const STAT_CLASS = 'euiStat'; +const STAT_TITLE_PRIMARY_CLASS = 'euiStat__title--primary'; +const STAT_BUTTON_CLASS = 'euiButtonEmpty'; + +describe('Rule stats', () => { + test('renders all rule stats', async () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 0, + muted: 0, + error: 0, + snoozed: 0, + }, + RULES_PAGE_LINK, + false + ); + expect(stats.length).toEqual(6); + }); + test('disabled stat is not clickable, when there are no disabled rules', async () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 0, + muted: 0, + error: 0, + snoozed: 0, + }, + RULES_PAGE_LINK, + false + ); + const { findByText, container } = render(stats[4]); + const disabledElement = await findByText('Disabled'); + expect(disabledElement).toBeInTheDocument(); + expect(container.getElementsByClassName(STAT_CLASS).length).toBe(1); + expect(container.getElementsByClassName(STAT_TITLE_PRIMARY_CLASS).length).toBe(0); + expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(0); + }); + + test('disabled stat is clickable, when there are disabled rules', async () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 1, + muted: 0, + error: 0, + snoozed: 0, + }, + RULES_PAGE_LINK, + false + ); + const { container } = render(stats[4]); + expect(screen.getByText('Disabled').closest('a')).toHaveAttribute( + 'href', + `${RULES_PAGE_LINK}?_a=(lastResponse:!(),status:!(disabled))` + ); + + expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(1); + }); + + test('disabled stat count is link-colored, when there are disabled rules', async () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 1, + muted: 0, + error: 0, + snoozed: 0, + }, + RULES_PAGE_LINK, + false + ); + const { container } = render(stats[4]); + expect(container.getElementsByClassName(STAT_TITLE_PRIMARY_CLASS).length).toBe(1); + }); + + test('snoozed stat is not clickable, when there are no snoozed rules', async () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 0, + muted: 0, + error: 0, + snoozed: 0, + }, + RULES_PAGE_LINK, + false + ); + const { findByText, container } = render(stats[3]); + const snoozedElement = await findByText('Snoozed'); + expect(snoozedElement).toBeInTheDocument(); + expect(container.getElementsByClassName(STAT_CLASS).length).toBe(1); + expect(container.getElementsByClassName(STAT_TITLE_PRIMARY_CLASS).length).toBe(0); + expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(0); + }); + + test('snoozed stat is clickable, when there are snoozed rules', () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 0, + muted: 1, + error: 0, + snoozed: 1, + }, + RULES_PAGE_LINK, + false + ); + const { container } = render(stats[3]); + expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(1); + expect(screen.getByText('Snoozed').closest('a')).toHaveAttribute( + 'href', + `${RULES_PAGE_LINK}?_a=(lastResponse:!(),status:!(snoozed))` + ); + }); + + test('snoozed stat count is link-colored, when there are snoozed rules', async () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 0, + muted: 1, + error: 0, + snoozed: 1, + }, + RULES_PAGE_LINK, + false + ); + const { container } = render(stats[3]); + expect(container.getElementsByClassName(STAT_TITLE_PRIMARY_CLASS).length).toBe(1); + }); + + test('errors stat is not clickable, when there are no error rules', async () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 0, + muted: 0, + error: 0, + snoozed: 0, + }, + RULES_PAGE_LINK, + false + ); + const { findByText, container } = render(stats[2]); + const errorsElement = await findByText('Errors'); + expect(errorsElement).toBeInTheDocument(); + expect(container.getElementsByClassName(STAT_CLASS).length).toBe(1); + expect(container.getElementsByClassName(STAT_TITLE_PRIMARY_CLASS).length).toBe(0); + expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(0); + }); + + test('errors stat is clickable, when there are error rules', () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 0, + muted: 0, + error: 2, + snoozed: 0, + }, + RULES_PAGE_LINK, + false + ); + const { container } = render(stats[2]); + expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(1); + expect(screen.getByText('Errors').closest('a')).toHaveAttribute( + 'href', + `${RULES_PAGE_LINK}?_a=(lastResponse:!(error),status:!())` + ); + }); + + test('errors stat count is link-colored, when there are error rules', () => { + const stats = renderRuleStats( + { + total: 11, + disabled: 0, + muted: 0, + error: 2, + snoozed: 0, + }, + RULES_PAGE_LINK, + false + ); + const { container } = render(stats[2]); + expect(container.getElementsByClassName(STAT_TITLE_PRIMARY_CLASS).length).toBe(1); + }); +}); diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx new file mode 100644 index 0000000000000..62c520c7b7442 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/rule_stats.tsx @@ -0,0 +1,156 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiButtonEmpty, EuiStat } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { euiStyled } from '@kbn/kibana-react-plugin/common'; + +interface RuleStatsState { + total: number; + disabled: number; + muted: number; + error: number; + snoozed: number; +} +type StatType = 'disabled' | 'snoozed' | 'error'; + +const Divider = euiStyled.div` + border-right: 1px solid ${({ theme }) => theme.eui.euiColorLightShade}; + height: 100%; +`; + +const StyledStat = euiStyled(EuiStat)` + .euiText { + line-height: 1; + } +`; + +const ConditionalWrap = ({ + condition, + wrap, + children, +}: { + condition: boolean; + wrap: (wrappedChildren: React.ReactNode) => JSX.Element; + children: JSX.Element; +}): JSX.Element => (condition ? wrap(children) : children); + +export const renderRuleStats = ( + ruleStats: RuleStatsState, + manageRulesHref: string, + ruleStatsLoading: boolean +) => { + const createRuleStatsLink = (stats: RuleStatsState, statType: StatType) => { + const count = stats[statType]; + let statsLink = `${manageRulesHref}?_a=(lastResponse:!(),status:!())`; + if (count > 0) { + switch (statType) { + case 'error': + statsLink = `${manageRulesHref}?_a=(lastResponse:!(error),status:!())`; + break; + case 'snoozed': + case 'disabled': + statsLink = `${manageRulesHref}?_a=(lastResponse:!(),status:!(${statType}))`; + break; + default: + break; + } + } + return statsLink; + }; + + const disabledStatsComponent = ( + 0} + wrap={(wrappedChildren) => ( + + {wrappedChildren} + + )} + > + 0 ? 'primary' : ''} + titleSize="xs" + isLoading={ruleStatsLoading} + data-test-subj="statDisabled" + /> + + ); + + const snoozedStatsComponent = ( + 0} + wrap={(wrappedChildren) => ( + + {wrappedChildren} + + )} + > + 0 ? 'primary' : ''} + titleSize="xs" + isLoading={ruleStatsLoading} + data-test-subj="statMuted" + /> + + ); + + const errorStatsComponent = ( + 0} + wrap={(wrappedChildren) => ( + + {wrappedChildren} + + )} + > + 0 ? 'primary' : ''} + titleSize="xs" + isLoading={ruleStatsLoading} + data-test-subj="statErrors" + /> + + ); + return [ + , + disabledStatsComponent, + snoozedStatsComponent, + errorStatsComponent, + , + + {i18n.translate('xpack.observability.alerts.manageRulesButtonLabel', { + defaultMessage: 'Manage Rules', + })} + , + ].reverse(); +}; diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/types.ts b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/types.ts new file mode 100644 index 0000000000000..87ff668ebf87f --- /dev/null +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats/types.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. + */ + +export interface RuleStatsState { + total: number; + disabled: number; + muted: number; + error: number; + snoozed: number; +} + +export type StatType = 'disabled' | 'snoozed' | 'error'; diff --git a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx index 10315af5874a5..2fe114771c329 100644 --- a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx @@ -5,15 +5,13 @@ * 2.0. */ -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiStat } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { DataViewBase } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import useAsync from 'react-use/lib/useAsync'; import { ALERT_STATUS, AlertStatus } from '@kbn/rule-data-utils'; - -import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { loadRuleAggregations } from '@kbn/triggers-actions-ui-plugin/public'; import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common/parse_technical_fields'; @@ -38,7 +36,9 @@ import { } from '../state_container'; import './styles.scss'; import { AlertsStatusFilter, AlertsDisclaimer, AlertsSearchBar } from '../../components'; +import { renderRuleStats } from '../../components/rule_stats'; import { ObservabilityAppServices } from '../../../../application/types'; +import { OBSERVABILITY_RULE_TYPES } from '../../../rules/config'; interface RuleStatsState { total: number; @@ -56,11 +56,6 @@ export interface TopAlert { active: boolean; } -const Divider = euiStyled.div` - border-right: 1px solid ${({ theme }) => theme.eui.euiColorLightShade}; - height: 100%; -`; - const regExpEscape = (str: string) => str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); const NO_INDEX_PATTERNS: DataViewBase[] = []; const BASE_ALERT_REGEX = new RegExp(`\\s*${regExpEscape(ALERT_STATUS)}\\s*:\\s*"(.*?|\\*?)"`); @@ -115,6 +110,7 @@ function AlertsPage() { try { const response = await loadRuleAggregations({ http, + typesFilter: OBSERVABILITY_RULE_TYPES, }); const { ruleExecutionStatus, ruleMutedStatus, ruleEnabledStatus, ruleSnoozedStatus } = response; @@ -249,54 +245,7 @@ function AlertsPage() { ), - rightSideItems: [ - , - , - , - , - , - - {i18n.translate('xpack.observability.alerts.manageRulesButtonLabel', { - defaultMessage: 'Manage Rules', - })} - , - ].reverse(), + rightSideItems: renderRuleStats(ruleStats, manageRulesHref, ruleStatsLoading), }} > diff --git a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/alerts_table_t_grid.tsx b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/alerts_table_t_grid.tsx index 32c604ab19731..686ae9a15d8de 100644 --- a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/alerts_table_t_grid.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/alerts_table_t_grid.tsx @@ -234,8 +234,29 @@ function ObservabilityActions({ , ] : []), + + ...[ + { + closeActionsPopover(); + setFlyoutAlert(alert); + }} + > + {translations.alertsTable.viewAlertDetailsButtonText} + , + ], ]; - }, [casePermissions?.crud, handleAddToExistingCaseClick, handleAddToNewCaseClick, linkToRule]); + }, [ + casePermissions?.crud, + handleAddToExistingCaseClick, + handleAddToNewCaseClick, + linkToRule, + alert, + setFlyoutAlert, + closeActionsPopover, + ]); const actionsToolTip = actionsMenuItems.length <= 0 @@ -245,18 +266,6 @@ function ObservabilityActions({ return ( <> - - - setFlyoutAlert(alert)} - data-test-subj="openFlyoutButton" - aria-label={translations.alertsTable.viewDetailsTextLabel} - /> - - (({ actions, message, title return ( {title}} body={message &&

{message}

} actions={{renderActions}} diff --git a/x-pack/plugins/observability/public/pages/overview/index.test.tsx b/x-pack/plugins/observability/public/pages/overview/index.test.tsx deleted file mode 100644 index 45206e4440205..0000000000000 --- a/x-pack/plugins/observability/public/pages/overview/index.test.tsx +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import React from 'react'; -import { shallow } from 'enzyme'; -import * as PluginContext from '../../hooks/use_plugin_context'; -import { PluginContextValue } from '../../context/plugin_context'; -import { OverviewPage } from '.'; -import { OverviewPage as OldOverviewPage } from './old_overview_page'; -import { OverviewPage as NewOverviewPage } from './overview_page'; - -describe('Overview page', () => { - it('should render the old overview page when feature flag is disabled and queryParams are empty', () => { - const pluginContext = { - config: { - unsafe: { - overviewNext: { enabled: false }, - }, - }, - }; - - jest - .spyOn(PluginContext, 'usePluginContext') - .mockReturnValue(pluginContext as PluginContextValue); - - const component = shallow(); - expect(component.find(OldOverviewPage)).toHaveLength(1); - expect(component.find(NewOverviewPage)).toHaveLength(0); - }); - - it('should render the new overview page when feature flag is enabled and queryParams are empty', () => { - const pluginContext = { - config: { - unsafe: { - overviewNext: { enabled: true }, - }, - }, - }; - - jest - .spyOn(PluginContext, 'usePluginContext') - .mockReturnValue(pluginContext as PluginContextValue); - - const component = shallow(); - expect(component.find(OldOverviewPage)).toHaveLength(0); - expect(component.find(NewOverviewPage)).toHaveLength(1); - }); - - it('should render the new overview page when feature flag is enabled and alpha param is in the url', () => { - const pluginContext = { - config: { - unsafe: { - overviewNext: { enabled: true }, - }, - }, - }; - - jest - .spyOn(PluginContext, 'usePluginContext') - .mockReturnValue(pluginContext as PluginContextValue); - - const component = shallow(); - expect(component.find(OldOverviewPage)).toHaveLength(0); - expect(component.find(NewOverviewPage)).toHaveLength(1); - }); - - it('should render the new overview page when feature flag is disabled and alpha param is in the url', () => { - const pluginContext = { - config: { - unsafe: { - overviewNext: { enabled: false }, - }, - }, - }; - - jest - .spyOn(PluginContext, 'usePluginContext') - .mockReturnValue(pluginContext as PluginContextValue); - - const component = shallow(); - expect(component.find(OldOverviewPage)).toHaveLength(0); - expect(component.find(NewOverviewPage)).toHaveLength(1); - }); -}); diff --git a/x-pack/plugins/observability/public/pages/overview/index.tsx b/x-pack/plugins/observability/public/pages/overview/index.tsx index 6b773b303031e..9ceabf7c3111a 100644 --- a/x-pack/plugins/observability/public/pages/overview/index.tsx +++ b/x-pack/plugins/observability/public/pages/overview/index.tsx @@ -4,25 +4,354 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; -import { RouteParams } from '../../routes'; +import { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiHorizontalRule, + EuiSpacer, + EuiText, + EuiTitle, + EuiTourStep, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React, { useMemo, useRef, useCallback, useState, useEffect } from 'react'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; +import { observabilityFeatureId } from '../../../common'; +import { useTrackPageview, useUiTracker } from '../..'; +import { EmptySections } from '../../components/app/empty_sections'; +import { ObservabilityHeaderMenu } from '../../components/app/header'; +import { NewsFeed } from '../../components/app/news_feed'; +import { Resources } from '../../components/app/resources'; +import { DatePicker } from '../../components/shared/date_picker'; +import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; +import { useFetcher } from '../../hooks/use_fetcher'; +import { useHasData } from '../../hooks/use_has_data'; import { usePluginContext } from '../../hooks/use_plugin_context'; -import { OverviewPage as OldOverviewPage } from './old_overview_page'; -import { OverviewPage as NewOverviewPage } from './overview_page'; +import { useAlertIndexNames } from '../../hooks/use_alert_index_names'; +import { RouteParams } from '../../routes'; +import { getNewsFeed } from '../../services/get_news_feed'; +import { getBucketSize } from '../../utils/get_bucket_size'; +import { getNoDataConfig } from '../../utils/no_data_config'; +import { DataSections } from './data_sections'; +import { LoadingObservability } from './loading_observability'; +import { AlertsTableTGrid } from '../alerts/containers/alerts_table_t_grid/alerts_table_t_grid'; +import { SectionContainer } from '../../components/app/section'; +import { ObservabilityAppServices } from '../../application/types'; +import { useGetUserCasesPermissions } from '../../hooks/use_get_user_cases_permissions'; +import { paths } from '../../config'; +import { useDatePickerContext } from '../../hooks/use_date_picker_context'; +import { ObservabilityStatusProgress } from '../../components/app/observability_status/observability_status_progress'; +import { ObservabilityStatus } from '../../components/app/observability_status'; +import { useGuidedSetupProgress } from '../../hooks/use_guided_setup_progress'; -export type { BucketSize } from './old_overview_page'; +export type BucketSize = ReturnType; interface Props { routeParams: RouteParams<'/overview'>; } -export function OverviewPage(props: Props) { - const { config } = usePluginContext(); - const alpha = props.routeParams.query.alpha; +const CAPABILITIES_KEYS = ['logs', 'infrastructure', 'apm', 'uptime']; - if (config.unsafe.overviewNext.enabled || alpha) { - return ; - } else { - return ; +function calculateBucketSize({ start, end }: { start?: number; end?: number }) { + if (start && end) { + return getBucketSize({ start, end, minInterval: '60s' }); } } + +const ALERT_TABLE_STATE_STORAGE_KEY = 'xpack.observability.overview.alert.tableState'; + +export function OverviewPage({ routeParams }: Props) { + const trackMetric = useUiTracker({ app: 'observability-overview' }); + useTrackPageview({ app: 'observability-overview', path: 'overview' }); + useTrackPageview({ app: 'observability-overview', path: 'overview', delay: 15000 }); + useBreadcrumbs([ + { + text: i18n.translate('xpack.observability.breadcrumbs.overviewLinkText', { + defaultMessage: 'Overview', + }), + }, + ]); + const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); + + const indexNames = useAlertIndexNames(); + const { + cases, + docLinks, + http, + application: { capabilities }, + } = useKibana().services; + + const { ObservabilityPageTemplate, config } = usePluginContext(); + const { relativeStart, relativeEnd, absoluteStart, absoluteEnd } = useDatePickerContext(); + + const { data: newsFeed } = useFetcher(() => getNewsFeed({ http }), [http]); + + const { hasAnyData, isAllRequestsComplete } = useHasData(); + const refetch = useRef<() => void>(); + + const [isGuidedSetupTourVisible, setGuidedSetupTourVisible] = useState(false); + const hideGuidedSetupTour = useCallback(() => setGuidedSetupTourVisible(false), []); + const { isGuidedSetupProgressDismissed } = useGuidedSetupProgress(); + + const bucketSize = useMemo( + () => + calculateBucketSize({ + start: absoluteStart, + end: absoluteEnd, + }), + [absoluteStart, absoluteEnd] + ); + + const setRefetch = useCallback((ref) => { + refetch.current = ref; + }, []); + + const handleGuidedSetupClick = useCallback(() => { + if (isGuidedSetupProgressDismissed) { + trackMetric({ metric: 'guided_setup_view_details_after_dismiss' }); + } + hideGuidedSetupTour(); + setIsFlyoutVisible(true); + }, [trackMetric, isGuidedSetupProgressDismissed, hideGuidedSetupTour]); + + const onTimeRangeRefresh = useCallback(() => { + return refetch.current && refetch.current(); + }, []); + + const CasesContext = cases.ui.getCasesContext(); + const userPermissions = useGetUserCasesPermissions(); + + useEffect(() => { + if (hasAnyData !== true) { + return; + } + + CAPABILITIES_KEYS.forEach((feature) => { + if (capabilities[feature].show === false) { + trackMetric({ + metric: `oblt_disabled_feature_${feature === 'infrastructure' ? 'metrics' : feature}`, + }); + } + }); + }, [capabilities, hasAnyData, trackMetric]); + + if (hasAnyData === undefined) { + return ; + } + + const hasData = hasAnyData === true || (isAllRequestsComplete === false ? undefined : false); + + const noDataConfig = getNoDataConfig({ + hasData, + basePath: http.basePath, + docsLink: docLinks.links.observability.guide, + }); + + const alertsLink = config.unsafe.alertingExperience.enabled + ? paths.observability.alerts + : paths.management.rules; + + return ( + + ), + } + : undefined + } + > + {hasData && ( + <> + + setIsFlyoutVisible(true)} + onDismissClick={() => setGuidedSetupTourVisible(true)} + /> + + + + + + + + + + {/* Data sections */} + {hasAnyData && } + + + + + + + + {/* Resources / What's New sections */} + + + {!!newsFeed?.items?.length && } + + + + + + + + + )} + {isFlyoutVisible && ( + setIsFlyoutVisible(false)} + aria-labelledby="statusVisualizationFlyoutTitle" + > + + +

+ +

+
+ + +

+ +

+
+
+ + + +
+ )} +
+ ); +} + +interface PageHeaderProps { + showTour?: boolean; + onTourDismiss: () => void; + handleGuidedSetupClick: () => void; + onTimeRangeRefresh: () => void; +} + +function PageHeader({ + showTour = false, + onTourDismiss, + handleGuidedSetupClick, + onTimeRangeRefresh, +}: PageHeaderProps) { + const { relativeStart, relativeEnd, refreshInterval, refreshPaused } = useDatePickerContext(); + const buttonRef = useRef(); + + return ( + + + +

{overviewPageTitle}

+
+
+ + + + + + + + {showTour ? ( + buttonRef.current} + isStepOpen + title={i18n.translate('xpack.observability.overview.guidedSetupTourTitle', { + defaultMessage: 'Guided setup is always available', + })} + content={ + + + + } + step={1} + stepsTotal={1} + maxWidth={400} + onFinish={onTourDismiss} + footerAction={ + + + + } + /> + ) : null} + +
+ ); +} + +const overviewPageTitle = i18n.translate('xpack.observability.overview.pageTitle', { + defaultMessage: 'Overview', +}); diff --git a/x-pack/plugins/observability/public/pages/overview/old_overview_page.tsx b/x-pack/plugins/observability/public/pages/overview/old_overview_page.tsx deleted file mode 100644 index 0c95362b15dbb..0000000000000 --- a/x-pack/plugins/observability/public/pages/overview/old_overview_page.tsx +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, - EuiHorizontalRule, - EuiButton, - EuiFlyout, - EuiFlyoutHeader, - EuiTitle, - EuiFlyoutBody, - EuiText, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useMemo, useRef, useCallback, useState, useEffect } from 'react'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { Storage } from '@kbn/kibana-utils-plugin/public'; -import { observabilityFeatureId } from '../../../common'; -import { useTrackPageview, useUiTracker } from '../..'; -import { EmptySections } from '../../components/app/empty_sections'; -import { ObservabilityHeaderMenu } from '../../components/app/header'; -import { NewsFeed } from '../../components/app/news_feed'; -import { Resources } from '../../components/app/resources'; -import { DatePicker } from '../../components/shared/date_picker'; -import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; -import { useFetcher } from '../../hooks/use_fetcher'; -import { useHasData } from '../../hooks/use_has_data'; -import { usePluginContext } from '../../hooks/use_plugin_context'; -import { useAlertIndexNames } from '../../hooks/use_alert_index_names'; -import { RouteParams } from '../../routes'; -import { getNewsFeed } from '../../services/get_news_feed'; -import { getBucketSize } from '../../utils/get_bucket_size'; -import { getNoDataConfig } from '../../utils/no_data_config'; -import { DataSections } from './data_sections'; -import { LoadingObservability } from './loading_observability'; -import { AlertsTableTGrid } from '../alerts/containers/alerts_table_t_grid/alerts_table_t_grid'; -import { SectionContainer } from '../../components/app/section'; -import { ObservabilityAppServices } from '../../application/types'; -import { useGetUserCasesPermissions } from '../../hooks/use_get_user_cases_permissions'; -import { paths } from '../../config'; -import { useDatePickerContext } from '../../hooks/use_date_picker_context'; -import { ObservabilityStatusProgress } from '../../components/app/observability_status/observability_status_progress'; -import { ObservabilityStatus } from '../../components/app/observability_status'; -import { useGuidedSetupProgress } from '../../hooks/use_guided_setup_progress'; -interface Props { - routeParams: RouteParams<'/overview'>; -} -export type BucketSize = ReturnType; - -const CAPABILITIES_KEYS = ['logs', 'infrastructure', 'apm', 'uptime']; - -function calculateBucketSize({ start, end }: { start?: number; end?: number }) { - if (start && end) { - return getBucketSize({ start, end, minInterval: '60s' }); - } -} - -const ALERT_TABLE_STATE_STORAGE_KEY = 'xpack.observability.overview.alert.tableState'; - -export function OverviewPage({ routeParams }: Props) { - const trackMetric = useUiTracker({ app: 'observability-overview' }); - useTrackPageview({ app: 'observability-overview', path: 'overview' }); - useTrackPageview({ app: 'observability-overview', path: 'overview', delay: 15000 }); - useBreadcrumbs([ - { - text: i18n.translate('xpack.observability.breadcrumbs.overviewLinkText', { - defaultMessage: 'Overview', - }), - }, - ]); - const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); - - const indexNames = useAlertIndexNames(); - const { - cases, - docLinks, - http, - application: { capabilities }, - } = useKibana().services; - - const { ObservabilityPageTemplate, config } = usePluginContext(); - const { relativeStart, relativeEnd, absoluteStart, absoluteEnd } = useDatePickerContext(); - - const { data: newsFeed } = useFetcher(() => getNewsFeed({ http }), [http]); - - const { hasAnyData, isAllRequestsComplete } = useHasData(); - const refetch = useRef<() => void>(); - - const { isGuidedSetupProgressDismissed } = useGuidedSetupProgress(); - - const bucketSize = useMemo( - () => - calculateBucketSize({ - start: absoluteStart, - end: absoluteEnd, - }), - [absoluteStart, absoluteEnd] - ); - - const setRefetch = useCallback((ref) => { - refetch.current = ref; - }, []); - - const handleGuidedSetupClick = useCallback(() => { - if (isGuidedSetupProgressDismissed) { - trackMetric({ metric: 'guided_setup_view_details_after_dismiss' }); - } - - setIsFlyoutVisible(true); - }, [trackMetric, isGuidedSetupProgressDismissed]); - - const onTimeRangeRefresh = useCallback(() => { - return refetch.current && refetch.current(); - }, []); - - const CasesContext = cases.ui.getCasesContext(); - const userPermissions = useGetUserCasesPermissions(); - - useEffect(() => { - if (hasAnyData !== true) { - return; - } - - CAPABILITIES_KEYS.forEach((feature) => { - if (capabilities[feature].show === false) { - trackMetric({ - metric: `oblt_disabled_feature_${feature === 'infrastructure' ? 'metrics' : feature}`, - }); - } - }); - }, [capabilities, hasAnyData, trackMetric]); - - if (hasAnyData === undefined) { - return ; - } - - const hasData = hasAnyData === true || (isAllRequestsComplete === false ? undefined : false); - - const noDataConfig = getNoDataConfig({ - hasData, - basePath: http.basePath, - docsLink: docLinks.links.observability.guide, - }); - - const alertsLink = config.unsafe.alertingExperience.enabled - ? paths.observability.alerts - : paths.management.rules; - - return ( - - ), - } - : undefined - } - > - {hasData && ( - <> - - setIsFlyoutVisible(true)} /> - - - - - - - - - - {/* Data sections */} - {hasAnyData && } - - - - - - - - {/* Resources / What's New sections */} - - - {!!newsFeed?.items?.length && } - - - - - - - - - )} - {isFlyoutVisible && ( - setIsFlyoutVisible(false)} - aria-labelledby="statusVisualizationFlyoutTitle" - > - - -

- -

-
- - -

- -

-
-
- - - -
- )} -
- ); -} - -interface PageHeaderProps { - handleGuidedSetupClick: () => void; - onTimeRangeRefresh: () => void; -} - -function PageHeader({ handleGuidedSetupClick, onTimeRangeRefresh }: PageHeaderProps) { - const { relativeStart, relativeEnd, refreshInterval, refreshPaused } = useDatePickerContext(); - return ( - - - -

{overviewPageTitle}

-
-
- - - - - - - - -
- ); -} - -const overviewPageTitle = i18n.translate('xpack.observability.overview.pageTitle', { - defaultMessage: 'Overview', -}); diff --git a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx index b975e45ac06df..95d263168f82e 100644 --- a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx +++ b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx @@ -85,7 +85,6 @@ const withCore = makeDecorator({ unsafe: { alertingExperience: { enabled: true }, cases: { enabled: true }, - overviewNext: { enabled: false }, rules: { enabled: true }, }, }, diff --git a/x-pack/plugins/observability/public/pages/overview/overview_page.tsx b/x-pack/plugins/observability/public/pages/overview/overview_page.tsx deleted file mode 100644 index 7f81f71cf6683..0000000000000 --- a/x-pack/plugins/observability/public/pages/overview/overview_page.tsx +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { i18n } from '@kbn/i18n'; -import React, { useState } from 'react'; -import { EuiButton, EuiFlyout, EuiFlyoutBody, EuiFlyoutHeader, EuiTitle } from '@elastic/eui'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { useTrackPageview } from '../..'; -import { DatePicker } from '../../components/shared/date_picker'; -import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; -import { useHasData } from '../../hooks/use_has_data'; -import { usePluginContext } from '../../hooks/use_plugin_context'; -import { useDatePickerContext } from '../../hooks/use_date_picker_context'; -import { RouteParams } from '../../routes'; -import { getNoDataConfig } from '../../utils/no_data_config'; -import { LoadingObservability } from './loading_observability'; -import { ObservabilityStatus } from '../../components/app/observability_status'; -import { ObservabilityAppServices } from '../../application/types'; - -interface Props { - routeParams: RouteParams<'/overview'>; -} - -export function OverviewPage({ routeParams }: Props) { - useTrackPageview({ app: 'observability-overview', path: 'overview' }); - useTrackPageview({ app: 'observability-overview', path: 'overview', delay: 15000 }); - useBreadcrumbs([ - { - text: i18n.translate('xpack.observability.breadcrumbs.overviewLinkText', { - defaultMessage: 'Overview', - }), - }, - ]); - - const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); - - const { docLinks, http } = useKibana().services; - const { ObservabilityPageTemplate } = usePluginContext(); - - const { relativeStart, relativeEnd } = useDatePickerContext(); - - const relativeTime = { start: relativeStart, end: relativeEnd }; - - const { hasAnyData, isAllRequestsComplete } = useHasData(); - - if (hasAnyData === undefined) { - return ; - } - - const hasData = hasAnyData === true || (isAllRequestsComplete === false ? undefined : false); - - const noDataConfig = getNoDataConfig({ - hasData, - basePath: http.basePath, - docsLink: docLinks.links.observability.guide, - }); - - const { refreshInterval = 10000, refreshPaused = true } = routeParams.query; - - return ( - , - ], - } - : undefined - } - > - {hasData && ( - <> - setIsFlyoutVisible(true)}>Show observability status - {isFlyoutVisible && ( - setIsFlyoutVisible(false)} - aria-labelledby="flyout-id" - > - - -

Status

-
-
- - - -
- )} - - )} -
- ); -} - -const overviewPageTitle = i18n.translate('xpack.observability.overview.pageTitle', { - defaultMessage: 'Overview', -}); diff --git a/x-pack/plugins/observability/public/pages/rules/components/execution_status.tsx b/x-pack/plugins/observability/public/pages/rules/components/execution_status.tsx index 81dfe9ebd1606..c0c7413aa9c15 100644 --- a/x-pack/plugins/observability/public/pages/rules/components/execution_status.tsx +++ b/x-pack/plugins/observability/public/pages/rules/components/execution_status.tsx @@ -5,14 +5,22 @@ * 2.0. */ -import React from 'react'; -import { EuiHealth, EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React, { useState } from 'react'; +import { EuiHealth, EuiToolTip, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; import { RuleExecutionStatusErrorReasons } from '@kbn/alerting-plugin/common'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { ManageLicenseModal } from './manage_license_model'; import { getHealthColor, rulesStatusesTranslationsMapping } from '../config'; import { RULE_STATUS_LICENSE_ERROR } from '../translations'; import { ExecutionStatusProps } from '../types'; +import { useKibana } from '../../../utils/kibana_react'; -export function ExecutionStatus({ executionStatus }: ExecutionStatusProps) { +export function ExecutionStatus({ executionStatus, item, licenseType }: ExecutionStatusProps) { + const { http } = useKibana().services; + const [manageLicenseModalOpts, setManageLicenseModalOpts] = useState<{ + licenseType: string; + ruleTypeId: string; + } | null>(null); const healthColor = getHealthColor(executionStatus.status); const tooltipMessage = executionStatus.status === 'error' ? `Error: ${executionStatus?.error?.message}` : null; @@ -38,6 +46,36 @@ export function ExecutionStatus({ executionStatus }: ExecutionStatusProps) { return ( {healthWithTooltip} + {isLicenseError && ( + + + setManageLicenseModalOpts({ + licenseType, + ruleTypeId: item.ruleTypeId, + }) + } + > + + + + )} + {manageLicenseModalOpts && ( + { + window.open(`${http.basePath.get()}/app/management/stack/license_management`, '_blank'); + setManageLicenseModalOpts(null); + }} + onCancel={() => setManageLicenseModalOpts(null)} + /> + )} ); } diff --git a/x-pack/plugins/observability/public/pages/rules/components/last_response_filter.tsx b/x-pack/plugins/observability/public/pages/rules/components/last_response_filter.tsx index d7154c47fecf7..f74c56f7f2792 100644 --- a/x-pack/plugins/observability/public/pages/rules/components/last_response_filter.tsx +++ b/x-pack/plugins/observability/public/pages/rules/components/last_response_filter.tsx @@ -18,12 +18,12 @@ import { } from '@elastic/eui'; import { RuleExecutionStatuses, RuleExecutionStatusValues } from '@kbn/alerting-plugin/common'; import { getHealthColor, rulesStatusesTranslationsMapping } from '../config'; -import { StatusFilterProps } from '../types'; +import { LastResponseFilterProps } from '../types'; -export const LastResponseFilter: React.FunctionComponent = ({ +export const LastResponseFilter: React.FunctionComponent = ({ selectedStatuses, onChange, -}: StatusFilterProps) => { +}: LastResponseFilterProps) => { const [selectedValues, setSelectedValues] = useState(selectedStatuses); const [isPopoverOpen, setIsPopoverOpen] = useState(false); diff --git a/x-pack/plugins/observability/public/pages/rules/components/manage_license_model.tsx b/x-pack/plugins/observability/public/pages/rules/components/manage_license_model.tsx new file mode 100644 index 0000000000000..e273c7d5a3044 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/rules/components/manage_license_model.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiConfirmModal } from '@elastic/eui'; +import { capitalize } from 'lodash'; + +interface Props { + licenseType: string; + ruleTypeId: string; + onConfirm: () => void; + onCancel: () => void; +} + +export function ManageLicenseModal({ licenseType, ruleTypeId, onConfirm, onCancel }: Props) { + const licenseRequired = capitalize(licenseType); + return ( + +

+ +

+
+ ); +} diff --git a/x-pack/plugins/observability/public/pages/rules/components/status.tsx b/x-pack/plugins/observability/public/pages/rules/components/status.tsx deleted file mode 100644 index 612d6f8f30bdd..0000000000000 --- a/x-pack/plugins/observability/public/pages/rules/components/status.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useMemo } from 'react'; -import { EuiBadge } from '@elastic/eui'; -import { noop } from 'lodash/fp'; -import { StatusProps } from '../types'; -import { statusMap } from '../config'; -import { RULES_CHANGE_STATUS } from '../translations'; - -export function Status({ type, disabled, onClick }: StatusProps) { - const props = useMemo( - () => ({ - color: statusMap[type].color, - ...(!disabled ? { onClick } : { onClick: noop }), - ...(!disabled ? { iconType: 'arrowDown', iconSide: 'right' as const } : {}), - ...(!disabled ? { iconOnClick: onClick } : { iconOnClick: noop }), - }), - [disabled, onClick, type] - ); - return ( - - {statusMap[type].label} - - ); -} diff --git a/x-pack/plugins/observability/public/pages/rules/components/status_context.tsx b/x-pack/plugins/observability/public/pages/rules/components/status_context.tsx deleted file mode 100644 index c7bd29d85b17a..0000000000000 --- a/x-pack/plugins/observability/public/pages/rules/components/status_context.tsx +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useState, useCallback, useMemo } from 'react'; -import { - EuiPopover, - EuiContextMenuItem, - EuiContextMenuPanel, - EuiLoadingSpinner, -} from '@elastic/eui'; -import { Status } from './status'; -import { RuleStatus, StatusContextProps } from '../types'; -import { statusMap } from '../config'; - -export function StatusContext({ - item, - disabled = false, - onStatusChanged, - enableRule, - disableRule, - muteRule, - unMuteRule, -}: StatusContextProps) { - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const [isUpdating, setIsUpdating] = useState(false); - const togglePopover = useCallback(() => setIsPopoverOpen(!isPopoverOpen), [isPopoverOpen]); - - let currentStatus: RuleStatus; - if (item.enabled) { - currentStatus = item.muteAll ? RuleStatus.snoozed : RuleStatus.enabled; - } else { - currentStatus = RuleStatus.disabled; - } - const popOverButton = useMemo( - () => , - [disabled, currentStatus, togglePopover] - ); - - const onContextMenuItemClick = useCallback( - async (status: RuleStatus) => { - togglePopover(); - if (currentStatus !== status) { - setIsUpdating(true); - - if (status === RuleStatus.enabled) { - await enableRule({ ...item, enabled: true }); - if (item.muteAll) { - await unMuteRule({ ...item, muteAll: false }); - } - } else if (status === RuleStatus.disabled) { - await disableRule({ ...item, enabled: false }); - } else if (status === RuleStatus.snoozed) { - await muteRule({ ...item, muteAll: true }); - } - setIsUpdating(false); - onStatusChanged(status); - } - }, - [ - item, - togglePopover, - enableRule, - disableRule, - muteRule, - unMuteRule, - currentStatus, - onStatusChanged, - ] - ); - - const panelItems = useMemo( - () => - Object.values(RuleStatus).map((status: RuleStatus) => ( - onContextMenuItemClick(status)} - disabled={status === RuleStatus.snoozed && currentStatus === RuleStatus.disabled} - > - {statusMap[status].label} - - )), - [currentStatus, onContextMenuItemClick] - ); - - return isUpdating ? ( - - ) : ( - setIsPopoverOpen(false)} - anchorPosition="downLeft" - isOpen={isPopoverOpen} - panelPaddingSize="none" - > - - - ); -} diff --git a/x-pack/plugins/observability/public/pages/rules/config.ts b/x-pack/plugins/observability/public/pages/rules/config.ts index 9da846c2c1cc4..8c39acb75976d 100644 --- a/x-pack/plugins/observability/public/pages/rules/config.ts +++ b/x-pack/plugins/observability/public/pages/rules/config.ts @@ -7,7 +7,6 @@ import { RuleExecutionStatuses } from '@kbn/alerting-plugin/common'; import { Rule, RuleTypeIndex, RuleType } from '@kbn/triggers-actions-ui-plugin/public'; -import { Status, RuleStatus } from './types'; import { RULE_STATUS_OK, RULE_STATUS_ACTIVE, @@ -15,26 +14,8 @@ import { RULE_STATUS_PENDING, RULE_STATUS_UNKNOWN, RULE_STATUS_WARNING, - RULE_STATUS_ENABLED, - RULE_STATUS_DISABLED, - RULE_STATUS_SNOOZED_INDEFINITELY, } from './translations'; -export const statusMap: Status = { - [RuleStatus.enabled]: { - color: 'primary', - label: RULE_STATUS_ENABLED, - }, - [RuleStatus.disabled]: { - color: 'default', - label: RULE_STATUS_DISABLED, - }, - [RuleStatus.snoozed]: { - color: 'warning', - label: RULE_STATUS_SNOOZED_INDEFINITELY, - }, -}; - export const DEFAULT_SEARCH_PAGE_SIZE: number = 25; export function getHealthColor(status: RuleExecutionStatuses) { diff --git a/x-pack/plugins/observability/public/pages/rules/index.tsx b/x-pack/plugins/observability/public/pages/rules/index.tsx index b161625379fd8..a409754a51a14 100644 --- a/x-pack/plugins/observability/public/pages/rules/index.tsx +++ b/x-pack/plugins/observability/public/pages/rules/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, useMemo } from 'react'; +import React, { useState, useMemo, useCallback } from 'react'; import { capitalize, sortBy } from 'lodash'; import { EuiButton, @@ -23,14 +23,17 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { deleteRules, RuleTableItem, + RuleStatus, enableRule, disableRule, - muteRule, + snoozeRule, useLoadRuleTypes, - unmuteRule, + unsnoozeRule, } from '@kbn/triggers-actions-ui-plugin/public'; import { RuleExecutionStatus, ALERTS_FEATURE_ID } from '@kbn/alerting-plugin/common'; import { usePluginContext } from '../../hooks/use_plugin_context'; +import { Provider, rulesPageStateContainer, useRulesPageStateContainer } from './state_container'; + import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; import { useKibana } from '../../utils/kibana_react'; import { useFetchRules } from '../../hooks/use_fetch_rules'; @@ -38,7 +41,6 @@ import { RulesTable } from './components/rules_table'; import { Name } from './components/name'; import { LastResponseFilter } from './components/last_response_filter'; import { TypeFilter } from './components/type_filter'; -import { StatusContext } from './components/status_context'; import { ExecutionStatus } from './components/execution_status'; import { LastRun } from './components/last_run'; import { EditRuleFlyout } from './components/edit_rule_flyout'; @@ -72,7 +74,7 @@ import { import { ExperimentalBadge } from '../../components/shared/experimental_badge'; const ENTER_KEY = 13; -export function RulesPage() { +function RulesPage() { const { ObservabilityPageTemplate, kibanaFeatures } = usePluginContext(); const { http, @@ -91,11 +93,13 @@ export function RulesPage() { }); const [inputText, setInputText] = useState(); const [searchText, setSearchText] = useState(); - const [ruleLastResponseFilter, setRuleLastResponseFilter] = useState([]); const [typesFilter, setTypesFilter] = useState([]); + const { lastResponse, setLastResponse } = useRulesPageStateContainer(); + const { status, setStatus } = useRulesPageStateContainer(); const [currentRuleToEdit, setCurrentRuleToEdit] = useState(null); const [rulesToDelete, setRulesToDelete] = useState([]); const [createRuleFlyoutVisibility, setCreateRuleFlyoutVisibility] = useState(false); + const [tagPopoverOpenIndex, setTagPopoverOpenIndex] = useState(-1); const isRuleTypeEditableInContext = (ruleTypeId: string) => ruleTypeRegistry.has(ruleTypeId) ? !ruleTypeRegistry.get(ruleTypeId).requiresAppContext : false; @@ -106,7 +110,8 @@ export function RulesPage() { const { rulesState, setRulesState, reload, noData, initialLoad } = useFetchRules({ searchText, - ruleLastResponseFilter, + ruleLastResponseFilter: lastResponse, + ruleStatusesFilter: status, typesFilter, page, setPage, @@ -171,6 +176,23 @@ export function RulesPage() { 'data-test-subj': 'rulesTableCell-name', render: (name: string, rule: RuleTableItem) => , }, + { + field: 'tags', + name: '', + sortable: false, + width: '50px', + 'data-test-subj': 'rulesTableCell-tagsPopover', + render: (tags: string[], item: RuleTableItem) => { + return tags.length > 0 + ? triggersActionsUi.getRuleTagBadge({ + isOpen: tagPopoverOpenIndex === item.index, + tags, + onClick: () => setTagPopoverOpenIndex(item.index), + onClose: () => setTagPopoverOpenIndex(-1), + }) + : null; + }, + }, { field: 'executionStatus.lastExecutionDate', name: LAST_RUN_COLUMN_TITLE, @@ -185,7 +207,11 @@ export function RulesPage() { width: '120px', 'data-test-subj': 'rulesTableCell-status', render: (_executionStatus: RuleExecutionStatus, item: RuleTableItem) => ( - + ), }, { @@ -193,17 +219,17 @@ export function RulesPage() { name: STATUS_COLUMN_TITLE, sortable: true, render: (_enabled: boolean, item: RuleTableItem) => { - return ( - reload()} - enableRule={async () => await enableRule({ http, id: item.id })} - disableRule={async () => await disableRule({ http, id: item.id })} - muteRule={async () => await muteRule({ http, id: item.id })} - unMuteRule={async () => await unmuteRule({ http, id: item.id })} - /> - ); + return triggersActionsUi.getRuleStatusDropdown({ + rule: item, + enableRule: async () => await enableRule({ http, id: item.id }), + disableRule: async () => await disableRule({ http, id: item.id }), + onRuleChanged: () => reload(), + isEditable: item.isEditable && isRuleTypeEditableInContext(item.ruleTypeId), + snoozeRule: async (snoozeEndTime: string | -1) => { + await snoozeRule({ http, id: item.id, snoozeEndTime }); + }, + unsnoozeRule: async () => await unsnoozeRule({ http, id: item.id }), + }); }, }, { @@ -263,6 +289,20 @@ export function RulesPage() { [] ); + const setRuleStatusFilter = useCallback( + (ids: RuleStatus[]) => { + setStatus(ids); + }, + [setStatus] + ); + + const setExecutionStatusFilter = useCallback( + (ids: string[]) => { + setLastResponse(ids); + }, + [setLastResponse] + ); + const getRulesTable = () => { if (noData && !rulesState.isLoading) { return authorizedToCreateAnyRules ? ( @@ -277,6 +317,7 @@ export function RulesPage() { if (initialLoad) { return ; } + return ( <> @@ -314,10 +355,16 @@ export function RulesPage() { setRuleLastResponseFilter(ids)} + selectedStatuses={lastResponse} + onChange={setExecutionStatusFilter} /> + + {triggersActionsUi.getRuleStatusFilter({ + selectedStatuses: status, + onChange: setRuleStatusFilter, + })} + ); } + +function WrappedRulesPage() { + return ( + + + + ); +} + +export { WrappedRulesPage as RulesPage }; diff --git a/x-pack/plugins/observability/public/pages/rules/state_container/index.tsx b/x-pack/plugins/observability/public/pages/rules/state_container/index.tsx new file mode 100644 index 0000000000000..7820342482035 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/rules/state_container/index.tsx @@ -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 { Provider, rulesPageStateContainer } from './state_container'; +export { useRulesPageStateContainer } from './use_rules_page_state_container'; diff --git a/x-pack/plugins/observability/public/pages/rules/state_container/state_container.tsx b/x-pack/plugins/observability/public/pages/rules/state_container/state_container.tsx new file mode 100644 index 0000000000000..039218add3508 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/rules/state_container/state_container.tsx @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + createStateContainer, + createStateContainerReactHelpers, +} from '@kbn/kibana-utils-plugin/public'; +import { RuleStatus } from '@kbn/triggers-actions-ui-plugin/public'; + +interface RulesPageContainerState { + lastResponse: string[]; + status: RuleStatus[]; +} + +const defaultState: RulesPageContainerState = { + lastResponse: [], + status: [], +}; + +interface RulesPageStateTransitions { + setLastResponse: ( + state: RulesPageContainerState + ) => (lastResponse: string[]) => RulesPageContainerState; + setStatus: (state: RulesPageContainerState) => (status: RuleStatus[]) => RulesPageContainerState; +} + +const transitions: RulesPageStateTransitions = { + setLastResponse: (state) => (lastResponse) => { + const filteredIds = lastResponse; + lastResponse.forEach((id) => { + const isPreviouslyChecked = state.lastResponse.includes(id); + if (!isPreviouslyChecked) { + filteredIds.concat(id); + } else { + filteredIds.filter((val) => { + return val !== id; + }); + } + }); + return { ...state, lastResponse: filteredIds }; + }, + setStatus: (state) => (status) => { + const filteredIds = status; + status.forEach((id) => { + const isPreviouslyChecked = state.status.includes(id); + if (!isPreviouslyChecked) { + filteredIds.concat(id); + } else { + filteredIds.filter((val) => { + return val !== id; + }); + } + }); + return { ...state, status: filteredIds }; + }, +}; + +const rulesPageStateContainer = createStateContainer(defaultState, transitions); + +type RulesPageStateContainer = typeof rulesPageStateContainer; +const { Provider, useContainer } = createStateContainerReactHelpers(); + +export { Provider, rulesPageStateContainer, useContainer, defaultState }; +export type { RulesPageStateContainer, RulesPageContainerState, RulesPageStateTransitions }; diff --git a/x-pack/plugins/observability/public/pages/rules/state_container/use_rules_page_state_container.tsx b/x-pack/plugins/observability/public/pages/rules/state_container/use_rules_page_state_container.tsx new file mode 100644 index 0000000000000..cd20de3f95c29 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/rules/state_container/use_rules_page_state_container.tsx @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect } from 'react'; +import { useHistory } from 'react-router-dom'; + +import { + createKbnUrlStateStorage, + syncState, + IKbnUrlStateStorage, + useContainerSelector, +} from '@kbn/kibana-utils-plugin/public'; + +import { + useContainer, + defaultState, + RulesPageStateContainer, + RulesPageContainerState, +} from './state_container'; + +export function useRulesPageStateContainer() { + const stateContainer = useContainer(); + + useUrlStateSyncEffect(stateContainer); + + const { setLastResponse, setStatus } = stateContainer.transitions; + const { lastResponse, status } = useContainerSelector(stateContainer, (state) => state); + + return { + lastResponse, + status, + setLastResponse, + setStatus, + }; +} + +function useUrlStateSyncEffect(stateContainer: RulesPageStateContainer) { + const history = useHistory(); + + useEffect(() => { + const urlStateStorage = createKbnUrlStateStorage({ + history, + useHash: false, + useHashQuery: false, + }); + const { start, stop } = setupUrlStateSync(stateContainer, urlStateStorage); + + start(); + + syncUrlStateWithInitialContainerState(stateContainer, urlStateStorage); + + return stop; + }, [stateContainer, history]); +} + +function setupUrlStateSync( + stateContainer: RulesPageStateContainer, + stateStorage: IKbnUrlStateStorage +) { + // This handles filling the state when an incomplete URL set is provided + const setWithDefaults = (changedState: Partial | null) => { + stateContainer.set({ ...defaultState, ...changedState }); + }; + return syncState({ + storageKey: '_a', + stateContainer: { + ...stateContainer, + set: setWithDefaults, + }, + stateStorage, + }); +} + +function syncUrlStateWithInitialContainerState( + stateContainer: RulesPageStateContainer, + urlStateStorage: IKbnUrlStateStorage +) { + const urlState = urlStateStorage.get>('_a'); + + if (urlState) { + const newState = { + ...defaultState, + ...urlState, + }; + + stateContainer.set(newState); + } else { + // Reset the state container when no URL state or timefilter range is set to avoid accidentally + // re-using state set on a previous visit to the page in the same session + stateContainer.set(defaultState); + } + + urlStateStorage.set('_a', stateContainer.get()); +} diff --git a/x-pack/plugins/observability/public/pages/rules/translations.ts b/x-pack/plugins/observability/public/pages/rules/translations.ts index 36f8ff62f1a4c..69f0b5beebf46 100644 --- a/x-pack/plugins/observability/public/pages/rules/translations.ts +++ b/x-pack/plugins/observability/public/pages/rules/translations.ts @@ -53,27 +53,6 @@ export const RULE_STATUS_WARNING = i18n.translate( } ); -export const RULE_STATUS_ENABLED = i18n.translate( - 'xpack.observability.rules.rulesTable.ruleStatusEnabled', - { - defaultMessage: 'Enabled', - } -); - -export const RULE_STATUS_DISABLED = i18n.translate( - 'xpack.observability.rules.rulesTable.ruleStatusDisabled', - { - defaultMessage: 'Disabled', - } -); - -export const RULE_STATUS_SNOOZED_INDEFINITELY = i18n.translate( - 'xpack.observability.rules.rulesTable.ruleStatusSnoozedIndefinitely', - { - defaultMessage: 'Snoozed indefinitely', - } -); - export const LAST_RESPONSE_COLUMN_TITLE = i18n.translate( 'xpack.observability.rules.rulesTable.columns.lastResponseTitle', { diff --git a/x-pack/plugins/observability/public/pages/rules/types.ts b/x-pack/plugins/observability/public/pages/rules/types.ts index cbcd97919cddc..866e5de91d9c5 100644 --- a/x-pack/plugins/observability/public/pages/rules/types.ts +++ b/x-pack/plugins/observability/public/pages/rules/types.ts @@ -7,44 +7,17 @@ import { Dispatch, SetStateAction } from 'react'; import { EuiTableSortingType, EuiBasicTableColumn } from '@elastic/eui'; import { RuleExecutionStatus } from '@kbn/alerting-plugin/common'; -import { RuleTableItem, Rule } from '@kbn/triggers-actions-ui-plugin/public'; -export interface StatusProps { - type: RuleStatus; - disabled: boolean; - onClick: () => void; -} - -export enum RuleStatus { - enabled = 'enabled', - disabled = 'disabled', - snoozed = 'snoozed', -} - -export type Status = Record< - RuleStatus, - { - color: string; - label: string; - } ->; +import { RuleTableItem, Rule, RuleStatus } from '@kbn/triggers-actions-ui-plugin/public'; -export interface StatusContextProps { - item: RuleTableItem; - disabled: boolean; - onStatusChanged: (status: RuleStatus) => void; - enableRule: (rule: Rule) => Promise; - disableRule: (rule: Rule) => Promise; - muteRule: (rule: Rule) => Promise; - unMuteRule: (rule: Rule) => Promise; -} - -export interface StatusFilterProps { +export interface LastResponseFilterProps { selectedStatuses: string[]; onChange?: (selectedRuleStatusesIds: string[]) => void; } export interface ExecutionStatusProps { executionStatus: RuleExecutionStatus; + item: RuleTableItem; + licenseType: string; } export interface LastRunProps { @@ -69,6 +42,7 @@ export interface Pagination { export interface FetchRulesProps { searchText: string | undefined; ruleLastResponseFilter: string[]; + ruleStatusesFilter: RuleStatus[]; typesFilter: string[]; page: Pagination; setPage: Dispatch>; diff --git a/x-pack/plugins/observability/public/plugin.ts b/x-pack/plugins/observability/public/plugin.ts index a19c5dfa0c3ce..cb8dcaf2dd7e4 100644 --- a/x-pack/plugins/observability/public/plugin.ts +++ b/x-pack/plugins/observability/public/plugin.ts @@ -32,6 +32,7 @@ import { TriggersAndActionsUIPublicPluginStart, } from '@kbn/triggers-actions-ui-plugin/public'; import { KibanaFeature } from '@kbn/features-plugin/common'; + import { ConfigSchema } from '.'; import { observabilityAppId, observabilityFeatureId, casesPath } from '../common'; import { createLazyObservabilityPageTemplate } from './components/shared'; @@ -144,6 +145,12 @@ export class Plugin const { renderApp } = await import('./application'); // Get start services const [coreStart, pluginsStart, { navigation }] = await coreSetup.getStartServices(); + // Register alerts metadata + const { registerAlertsTableConfiguration } = await import( + './config/register_alerts_table_configuration' + ); + const { alertsTableConfigurationRegistry } = pluginsStart.triggersActionsUi; + registerAlertsTableConfiguration(alertsTableConfigurationRegistry); // The `/api/features` endpoint requires the "Global All" Kibana privilege. Users with a // subset of this privilege are not authorized to access this endpoint and will receive a 404 // error that causes the Alerting view to fail to load. @@ -268,7 +275,6 @@ export class Plugin public start(coreStart: CoreStart, pluginsStart: ObservabilityPublicPluginsStart) { const { application } = coreStart; - const config = this.initializerContext.config.get(); updateGlobalNavigation({ diff --git a/x-pack/plugins/observability/public/services/call_observability_api/index.ts b/x-pack/plugins/observability/public/services/call_observability_api/index.ts index 881ea80a4de47..f76f9f5cd7e07 100644 --- a/x-pack/plugins/observability/public/services/call_observability_api/index.ts +++ b/x-pack/plugins/observability/public/services/call_observability_api/index.ts @@ -5,9 +5,7 @@ * 2.0. */ -// @ts-expect-error -import { formatRequest } from '@kbn/server-route-repository/target_node/format_request'; -import type { formatRequest as formatRequestType } from '@kbn/server-route-repository/target_types/format_request'; +import { formatRequest } from '@kbn/server-route-repository'; import type { HttpSetup } from '@kbn/core/public'; import type { AbstractObservabilityClient, ObservabilityClient } from './types'; @@ -19,9 +17,7 @@ export function createCallObservabilityApi(http: HttpSetup) { const client: AbstractObservabilityClient = (endpoint, options) => { const { params: { path, body, query } = {}, ...rest } = options; - const { method, pathname } = formatRequest(endpoint, path) as ReturnType< - typeof formatRequestType - >; + const { method, pathname } = formatRequest(endpoint, path); return http[method](pathname, { ...rest, diff --git a/x-pack/plugins/observability/public/utils/test_helper.tsx b/x-pack/plugins/observability/public/utils/test_helper.tsx index d5607b5ee8b48..bdbb9dd71164a 100644 --- a/x-pack/plugins/observability/public/utils/test_helper.tsx +++ b/x-pack/plugins/observability/public/utils/test_helper.tsx @@ -26,7 +26,6 @@ const config = { unsafe: { alertingExperience: { enabled: true }, cases: { enabled: true }, - overviewNext: { enabled: false }, rules: { enabled: true }, }, }; diff --git a/x-pack/plugins/observability/server/index.ts b/x-pack/plugins/observability/server/index.ts index 39537a67a8cbf..ab2a566fc6b85 100644 --- a/x-pack/plugins/observability/server/index.ts +++ b/x-pack/plugins/observability/server/index.ts @@ -35,7 +35,6 @@ export const config: PluginConfigDescriptor = { alertingExperience: schema.object({ enabled: schema.boolean({ defaultValue: true }) }), rules: schema.object({ enabled: schema.boolean({ defaultValue: true }) }), cases: schema.object({ enabled: schema.boolean({ defaultValue: true }) }), - overviewNext: schema.object({ enabled: schema.boolean({ defaultValue: false }) }), }), }), }; diff --git a/x-pack/plugins/observability/server/ui_settings.ts b/x-pack/plugins/observability/server/ui_settings.ts index 02c519b10d19c..5b21b07d1cea3 100644 --- a/x-pack/plugins/observability/server/ui_settings.ts +++ b/x-pack/plugins/observability/server/ui_settings.ts @@ -18,6 +18,7 @@ import { apmProgressiveLoading, enableServiceGroups, apmServiceInventoryOptimizedSorting, + enableNewSyntheticsView, } from '../common/ui_settings_keys'; const technicalPreviewLabel = i18n.translate( @@ -31,6 +32,22 @@ const technicalPreviewLabel = i18n.translate( * uiSettings definitions for Observability. */ export const uiSettings: Record> = { + [enableNewSyntheticsView]: { + category: [observabilityFeatureId], + name: i18n.translate('xpack.observability.enableNewSyntheticsViewExperimentName', { + defaultMessage: 'Enable new synthetic monitoring application', + }), + value: false, + description: i18n.translate( + 'xpack.observability.enableNewSyntheticsViewExperimentDescription', + { + defaultMessage: + 'Enable new synthetic monitoring application in observability. Refresh the page to apply the setting.', + } + ), + schema: schema.boolean(), + requiresPageReload: true, + }, [enableInspectEsQueries]: { category: [observabilityFeatureId], name: i18n.translate('xpack.observability.enableInspectEsQueriesExperimentName', { @@ -71,7 +88,7 @@ export const uiSettings: Record | undefined) => + ecsMapping + ? Object.entries(ecsMapping).map((item) => ({ + key: item[0], + value: item[1], + })) + : undefined; + +export const convertECSMappingToObject = ( + ecsMapping: Array<{ + key: string; + result: { + type: string; + value: string; + }; + }> +): Record => + reduce( + ecsMapping, + (acc, value) => { + if (!isEmpty(value?.key) && !isEmpty(value.result?.type) && !isEmpty(value.result?.value)) { + acc[value.key] = { + [value.result.type]: value.result.value, + }; + } + + return acc; + }, + {} as Record + ); diff --git a/x-pack/plugins/osquery/cypress/README.md b/x-pack/plugins/osquery/cypress/README.md index 72d558b6e5980..6d72c6aa82b9d 100644 --- a/x-pack/plugins/osquery/cypress/README.md +++ b/x-pack/plugins/osquery/cypress/README.md @@ -101,13 +101,13 @@ We use es_archiver to manage the data that our Cypress tests need. 3. When you are sure that you have all the data you need run the following command from: `x-pack/plugins/osquery` ```sh -node ../../../scripts/es_archiver save --dir ../../test/osquery_cypress/es_archives --config ../../../test/functional/config.js --es-url http://:@: +node ../../../scripts/es_archiver save --dir ../../test/osquery_cypress/es_archives --config ../../../test/functional/config.base.js --es-url http://:@: ``` Example: ```sh -node ../../../scripts/es_archiver save custom_rules ".kibana",".siem-signal*" --dir ../../test/osquery_cypress/es_archives --config ../../../test/functional/config.js --es-url http://elastic:changeme@localhost:9220 +node ../../../scripts/es_archiver save custom_rules ".kibana",".siem-signal*" --dir ../../test/osquery_cypress/es_archives --config ../../../test/functional/config.base.js --es-url http://elastic:changeme@localhost:9220 ``` Note that the command will create the folder if it does not exist. diff --git a/x-pack/plugins/osquery/cypress/fixtures/saved_objects/saved_query.ndjson b/x-pack/plugins/osquery/cypress/fixtures/saved_objects/saved_query.ndjson index b29c4e28e731d..0e1a2f4b67cac 100644 --- a/x-pack/plugins/osquery/cypress/fixtures/saved_objects/saved_query.ndjson +++ b/x-pack/plugins/osquery/cypress/fixtures/saved_objects/saved_query.ndjson @@ -14,6 +14,7 @@ "id": "Saved-Query-Id", "interval": "3600", "query": "select * from uptime;", + "platform": "linux,darwin", "updated_at": "2021-12-21T08:54:38.648Z", "updated_by": "elastic" }, diff --git a/x-pack/plugins/osquery/cypress/integration/all/alerts.spec.ts b/x-pack/plugins/osquery/cypress/integration/all/alerts.spec.ts index 7813a94e2bfe3..21d3584b9fc46 100644 --- a/x-pack/plugins/osquery/cypress/integration/all/alerts.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/all/alerts.spec.ts @@ -37,6 +37,7 @@ describe('Alert Event Details', () => { it('should be able to run live query', () => { const PACK_NAME = 'testpack'; const RULE_NAME = 'Test-rule'; + const TIMELINE_NAME = 'Untitled timeline'; navigateTo('/app/osquery/packs'); preparePack(PACK_NAME); findAndClickButton('Edit'); @@ -57,6 +58,7 @@ describe('Alert Event Details', () => { cy.getBySel('ruleSwitch').click(); cy.getBySel('ruleSwitch').should('have.attr', 'aria-checked', 'true'); cy.visit('/app/security/alerts'); + cy.wait(500); cy.getBySel('expand-event').first().click(); cy.getBySel('take-action-dropdown-btn').click(); cy.getBySel('osquery-action-item').click(); @@ -64,12 +66,16 @@ describe('Alert Event Details', () => { inputQuery('select * from uptime;'); submitQuery(); checkResults(); - + cy.contains('Save for later').click(); + cy.contains('Save query'); + cy.get('.euiButtonEmpty--flushLeft').contains('Cancel').click(); + cy.getBySel('add-to-timeline').first().click(); + cy.getBySel('globalToastList').contains('Added'); cy.getBySel(RESULTS_TABLE).within(() => { cy.getBySel(RESULTS_TABLE_BUTTON).should('not.exist'); }); - cy.contains('Save for later').click(); - cy.contains('Save query'); - cy.contains(/^Save$/); + cy.contains('Cancel').click(); + cy.contains(TIMELINE_NAME).click(); + cy.getBySel('draggableWrapperKeyboardHandler').contains('action_id: "'); }); }); diff --git a/x-pack/plugins/osquery/cypress/integration/all/delete_all_ecs_mappings.spec.ts b/x-pack/plugins/osquery/cypress/integration/all/delete_all_ecs_mappings.spec.ts deleted file mode 100644 index dab935d919a28..0000000000000 --- a/x-pack/plugins/osquery/cypress/integration/all/delete_all_ecs_mappings.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { navigateTo } from '../../tasks/navigation'; -import { login } from '../../tasks/login'; -import { ArchiverMethod, runKbnArchiverScript } from '../../tasks/archiver'; -import { ROLES } from '../../test'; - -describe('ALL - Delete ECS Mappings', () => { - const SAVED_QUERY_ID = 'Saved-Query-Id'; - - before(() => { - runKbnArchiverScript(ArchiverMethod.LOAD, 'saved_query'); - }); - beforeEach(() => { - login(ROLES.soc_manager); - navigateTo('/app/osquery/saved_queries'); - }); - - after(() => { - runKbnArchiverScript(ArchiverMethod.UNLOAD, 'saved_query'); - }); - - it('to click the edit button and edit pack', () => { - cy.react('CustomItemAction', { - props: { index: 1, item: { attributes: { id: SAVED_QUERY_ID } } }, - }).click(); - cy.contains('Custom key/value pairs.').should('exist'); - cy.contains('Hours of uptime').should('exist'); - cy.react('EuiButtonIcon', { props: { id: 'labels-trash' } }).click(); - cy.react('EuiButton').contains('Update query').click(); - cy.wait(5000); - - cy.react('CustomItemAction', { - props: { index: 1, item: { attributes: { id: SAVED_QUERY_ID } } }, - }).click(); - cy.contains('Custom key/value pairs').should('not.exist'); - cy.contains('Hours of uptime').should('not.exist'); - }); -}); diff --git a/x-pack/plugins/osquery/cypress/integration/all/edit_saved_queries.spec.ts b/x-pack/plugins/osquery/cypress/integration/all/edit_saved_queries.spec.ts new file mode 100644 index 0000000000000..6dde0013a4bc6 --- /dev/null +++ b/x-pack/plugins/osquery/cypress/integration/all/edit_saved_queries.spec.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 { navigateTo } from '../../tasks/navigation'; +import { login } from '../../tasks/login'; +import { ArchiverMethod, runKbnArchiverScript } from '../../tasks/archiver'; +import { ROLES } from '../../test'; + +describe('ALL - Edit saved query', () => { + const SAVED_QUERY_ID = 'Saved-Query-Id'; + + before(() => { + runKbnArchiverScript(ArchiverMethod.LOAD, 'saved_query'); + }); + beforeEach(() => { + login(ROLES.soc_manager); + navigateTo('/app/osquery/saved_queries'); + }); + + after(() => { + runKbnArchiverScript(ArchiverMethod.UNLOAD, 'saved_query'); + }); + + it('by changing ecs mappings and platforms', () => { + cy.react('CustomItemAction', { + props: { index: 1, item: { attributes: { id: SAVED_QUERY_ID } } }, + }).click(); + cy.contains('Custom key/value pairs.').should('exist'); + cy.contains('Hours of uptime').should('exist'); + cy.react('ECSComboboxFieldComponent', { props: { field: { value: 'labels' } } }) + .parents('[data-test-subj="ECSMappingEditorForm"]') + .react('EuiButtonIcon', { props: { iconType: 'trash' } }) + .click(); + + cy.react('PlatformCheckBoxGroupField').within(() => { + cy.react('EuiCheckbox', { + props: { + id: 'linux', + checked: true, + }, + }).should('exist'); + cy.react('EuiCheckbox', { + props: { + id: 'darwin', + checked: true, + }, + }).should('exist'); + + cy.react('EuiCheckbox', { + props: { + id: 'windows', + checked: false, + }, + }).should('exist'); + }); + + cy.get('#windows').check({ force: true }); + + cy.react('EuiButton').contains('Update query').click(); + + cy.wait(5000); + + cy.react('CustomItemAction', { + props: { index: 1, item: { attributes: { id: SAVED_QUERY_ID } } }, + }).click(); + cy.contains('Custom key/value pairs').should('not.exist'); + cy.contains('Hours of uptime').should('not.exist'); + + cy.react('PlatformCheckBoxGroupField').within(() => { + cy.react('EuiCheckbox', { + props: { + id: 'linux', + checked: true, + }, + }).should('exist'); + cy.react('EuiCheckbox', { + props: { + id: 'darwin', + checked: true, + }, + }).should('exist'); + + cy.react('EuiCheckbox', { + props: { + id: 'windows', + checked: true, + }, + }).should('exist'); + }); + }); +}); diff --git a/x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts b/x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts index 6a9d102784a58..7cdc55c014505 100644 --- a/x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts @@ -61,10 +61,8 @@ describe('ALL - Live Query', () => { }).should('exist'); cy.react(RESULTS_TABLE_CELL_WRRAPER, { props: { id: 'osquery.days.number', index: 2 }, - }).within(() => { - cy.get('.euiToolTipAnchor').within(() => { - cy.get('svg').should('exist'); - }); - }); + }) + .react('EuiIconTip', { props: { type: 'indexMapping' } }) + .should('exist'); }); }); diff --git a/x-pack/plugins/osquery/cypress/integration/all/packs.spec.ts b/x-pack/plugins/osquery/cypress/integration/all/packs.spec.ts index 255dabbc433b0..917147fe88457 100644 --- a/x-pack/plugins/osquery/cypress/integration/all/packs.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/all/packs.spec.ts @@ -55,7 +55,7 @@ describe('ALL - Packs', () => { cy.react('List').first().click(); findAndClickButton('Add query'); cy.contains('Attach next query'); - getSavedQueriesDropdown().click().type(`${SAVED_QUERY_ID}{downArrow}{enter}`); + getSavedQueriesDropdown().type(`${SAVED_QUERY_ID}{downArrow}{enter}`); cy.react('EuiFormRow', { props: { label: 'Interval (s)' } }) .click() .clear() @@ -92,7 +92,7 @@ describe('ALL - Packs', () => { findAndClickButton('Add query'); cy.contains('Attach next query'); cy.contains('ID must be unique').should('not.exist'); - getSavedQueriesDropdown().click().type(`${SAVED_QUERY_ID}{downArrow}{enter}`); + getSavedQueriesDropdown().type(`${SAVED_QUERY_ID}{downArrow}{enter}`); cy.contains('ID must be unique').should('exist'); cy.react('EuiFlyoutFooter').react('EuiButtonEmpty').contains('Cancel').click(); }); @@ -170,7 +170,7 @@ describe('ALL - Packs', () => { findAndClickButton('Add query'); - getSavedQueriesDropdown().click().type('Multiple {downArrow} {enter}'); + getSavedQueriesDropdown().type('Multiple {downArrow} {enter}'); cy.contains('Custom key/value pairs'); cy.contains('Days of uptime'); cy.contains('List of keywords used to tag each'); @@ -178,7 +178,7 @@ describe('ALL - Packs', () => { cy.contains('Client network address.'); cy.contains('Total uptime seconds'); - getSavedQueriesDropdown().click().type('NOMAPPING {downArrow} {enter}'); + getSavedQueriesDropdown().type('NOMAPPING {downArrow} {enter}'); cy.contains('Custom key/value pairs').should('not.exist'); cy.contains('Days of uptime').should('not.exist'); cy.contains('List of keywords used to tag each').should('not.exist'); @@ -186,7 +186,7 @@ describe('ALL - Packs', () => { cy.contains('Client network address.').should('not.exist'); cy.contains('Total uptime seconds').should('not.exist'); - getSavedQueriesDropdown().click().type('ONE_MAPPING {downArrow} {enter}'); + getSavedQueriesDropdown().type('ONE_MAPPING {downArrow} {enter}'); cy.contains('Name of the continent'); cy.contains('Seconds of uptime'); diff --git a/x-pack/plugins/osquery/cypress/integration/roles/t1_analyst.spec.ts b/x-pack/plugins/osquery/cypress/integration/roles/t1_analyst.spec.ts index 64d72c92dda04..51270332e0a51 100644 --- a/x-pack/plugins/osquery/cypress/integration/roles/t1_analyst.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/roles/t1_analyst.spec.ts @@ -60,7 +60,7 @@ describe('T1 Analyst - READ + runSavedQueries ', () => { cy.waitForReact(1000); cy.contains('New live query').should('not.be.disabled').click(); selectAllAgents(); - getSavedQueriesDropdown().click().type(`${SAVED_QUERY_ID}{downArrow} {enter}`); + getSavedQueriesDropdown().type(`${SAVED_QUERY_ID}{downArrow} {enter}`); cy.contains('select * from uptime'); submitQuery(); checkResults(); diff --git a/x-pack/plugins/osquery/cypress/integration/roles/t2_analyst.spec.ts b/x-pack/plugins/osquery/cypress/integration/roles/t2_analyst.spec.ts index 619865e50bb6b..28a8f8a7880ac 100644 --- a/x-pack/plugins/osquery/cypress/integration/roles/t2_analyst.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/roles/t2_analyst.spec.ts @@ -104,7 +104,10 @@ describe('T2 Analyst - READ + Write Live/Saved + runSavedQueries ', () => { }).click(); cy.contains('Custom key/value pairs.').should('exist'); cy.contains('Hours of uptime').should('exist'); - cy.react('EuiButtonIcon', { props: { id: 'labels-trash' } }).click(); + cy.react('ECSComboboxFieldComponent', { props: { field: { value: 'labels' } } }) + .parents('[data-test-subj="ECSMappingEditorForm"]') + .react('EuiButtonIcon', { props: { iconType: 'trash' } }) + .click(); cy.react('EuiButton').contains('Update query').click(); cy.wait(5000); diff --git a/x-pack/plugins/osquery/cypress/tasks/live_query.ts b/x-pack/plugins/osquery/cypress/tasks/live_query.ts index b3006a3d1074a..ca232d8507d2a 100644 --- a/x-pack/plugins/osquery/cypress/tasks/live_query.ts +++ b/x-pack/plugins/osquery/cypress/tasks/live_query.ts @@ -8,14 +8,15 @@ import { LIVE_QUERY_EDITOR } from '../screens/live_query'; export const DEFAULT_QUERY = 'select * from processes;'; -export const BIG_QUERY = 'select * from processes, users;'; +export const BIG_QUERY = 'select * from processes, users limit 200;'; export const selectAllAgents = () => { - cy.react('EuiComboBox', { props: { placeholder: 'Select agents or groups' } }).type('All agents'); + cy.react('AgentsTable').find('input').should('not.be.disabled'); + cy.react('AgentsTable EuiComboBox', { + props: { placeholder: 'Select agents or groups' }, + }).click(); cy.react('EuiFilterSelectItem').contains('All agents').should('exist'); - cy.react('EuiComboBox', { props: { placeholder: 'Select agents or groups' } }).type( - '{downArrow}{enter}{esc}' - ); + cy.react('AgentsTable EuiComboBox').type('{downArrow}{enter}{esc}'); cy.contains('1 agent selected.'); }; @@ -24,12 +25,11 @@ export const inputQuery = (query: string) => cy.get(LIVE_QUERY_EDITOR).type(quer export const submitQuery = () => cy.contains('Submit').click(); export const checkResults = () => - cy.getBySel('dataGridRowCell', { timeout: 60000 }).should('have.lengthOf.above', 0); + cy.getBySel('dataGridRowCell', { timeout: 120000 }).should('have.lengthOf.above', 0); -export const typeInECSFieldInput = (text: string) => - cy.getBySel('ECS-field-input').click().type(text); +export const typeInECSFieldInput = (text: string) => cy.getBySel('ECS-field-input').type(text); export const typeInOsqueryFieldInput = (text: string) => - cy.react('OsqueryColumnFieldComponent').first().react('ResultComboBox').click().type(text); + cy.react('OsqueryColumnFieldComponent').first().react('ResultComboBox').type(text); export const findFormFieldByRowsLabelAndType = (label: string, text: string) => { cy.react('EuiFormRow', { props: { label } }).type(text); diff --git a/x-pack/plugins/osquery/kibana.json b/x-pack/plugins/osquery/kibana.json index 619bcbeed14f4..1ea8468529b85 100644 --- a/x-pack/plugins/osquery/kibana.json +++ b/x-pack/plugins/osquery/kibana.json @@ -12,7 +12,6 @@ "requiredPlugins": [ "actions", "data", - "dataEnhanced", "discover", "features", "navigation", diff --git a/x-pack/plugins/osquery/public/action_results/use_action_results.ts b/x-pack/plugins/osquery/public/action_results/use_action_results.ts index e567c7ccdb635..d8dd3186238f7 100644 --- a/x-pack/plugins/osquery/public/action_results/use_action_results.ts +++ b/x-pack/plugins/osquery/public/action_results/use_action_results.ts @@ -9,7 +9,7 @@ import { flatten, reverse, uniqBy } from 'lodash/fp'; import { useQuery } from 'react-query'; import { i18n } from '@kbn/i18n'; -import { firstValueFrom } from 'rxjs'; +import { lastValueFrom } from 'rxjs'; import { createFilter, getInspectResponse, @@ -68,7 +68,7 @@ export const useActionResults = ({ return useQuery( ['actionResults', { actionId }], async () => { - const responseData = await firstValueFrom( + const responseData = await lastValueFrom( data.search.search( { actionId, diff --git a/x-pack/plugins/osquery/public/actions/use_action_details.ts b/x-pack/plugins/osquery/public/actions/use_action_details.ts index 1a6550d8f47dd..32b8d4578cb77 100644 --- a/x-pack/plugins/osquery/public/actions/use_action_details.ts +++ b/x-pack/plugins/osquery/public/actions/use_action_details.ts @@ -8,7 +8,7 @@ import { useQuery } from 'react-query'; import { i18n } from '@kbn/i18n'; -import { firstValueFrom } from 'rxjs'; +import { lastValueFrom } from 'rxjs'; import { createFilter } from '../common/helpers'; import { useKibana } from '../common/lib/kibana'; import { @@ -37,7 +37,7 @@ export const useActionDetails = ({ actionId, filterQuery, skip = false }: UseAct return useQuery( ['actionDetails', { actionId, filterQuery }], async () => { - const responseData = await firstValueFrom( + const responseData = await lastValueFrom( data.search.search( { actionId, diff --git a/x-pack/plugins/osquery/public/actions/use_all_actions.ts b/x-pack/plugins/osquery/public/actions/use_all_actions.ts index d1fe00c81a0b5..34a25fa47b6e9 100644 --- a/x-pack/plugins/osquery/public/actions/use_all_actions.ts +++ b/x-pack/plugins/osquery/public/actions/use_all_actions.ts @@ -8,7 +8,7 @@ import { useQuery } from 'react-query'; import { i18n } from '@kbn/i18n'; -import { firstValueFrom } from 'rxjs'; +import { lastValueFrom } from 'rxjs'; import { createFilter, generateTablePaginationOptions, @@ -60,7 +60,7 @@ export const useAllActions = ({ return useQuery( ['actions', { activePage, direction, limit, sortField }], async () => { - const responseData = await firstValueFrom( + const responseData = await lastValueFrom( data.search.search( { factoryQueryType: OsqueryQueries.actions, diff --git a/x-pack/plugins/osquery/public/agents/agents_table.tsx b/x-pack/plugins/osquery/public/agents/agents_table.tsx index 55e63456fe916..75d073c4d9292 100644 --- a/x-pack/plugins/osquery/public/agents/agents_table.tsx +++ b/x-pack/plugins/osquery/public/agents/agents_table.tsx @@ -6,13 +6,13 @@ */ import { find } from 'lodash/fp'; -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { EuiComboBox, EuiHealth, EuiHighlight, EuiSpacer } from '@elastic/eui'; +import deepEqual from 'fast-deep-equal'; import useDebounce from 'react-use/lib/useDebounce'; import { useAllAgents } from './use_all_agents'; import { useAgentGroups } from './use_agent_groups'; -import { useOsqueryPolicies } from './use_osquery_policies'; import { AgentGrouper } from './agent_grouper'; import { getNumAgentsInGrouping, @@ -61,19 +61,16 @@ const AgentsTableComponent: React.FC = ({ agentSelection, onCh ); // grouping related - const osqueryPolicyData = useOsqueryPolicies(); const { - loading: groupsLoading, - totalCount: totalNumAgents, - groups, + isLoading: groupsLoading, + data: agentGroupsData, isFetched: groupsFetched, - } = useAgentGroups(osqueryPolicyData); - const grouper = useMemo(() => new AgentGrouper(), []); + } = useAgentGroups(); const { isLoading: agentsLoading, data: agents, isFetched: agentsFetched, - } = useAllAgents(osqueryPolicyData, debouncedSearchValue, { + } = useAllAgents(debouncedSearchValue, { perPage, }); @@ -96,7 +93,7 @@ const AgentsTableComponent: React.FC = ({ agentSelection, onCh selectedGroups: SelectedGroups; } = generateAgentSelection(selection); if (newAgentSelection.allAgentsSelected) { - setNumAgentsSelected(totalNumAgents); + setNumAgentsSelected(agentGroupsData?.totalCount ?? 0); } else { const checkAgent = generateAgentCheck(selectedGroups); setNumAgentsSelected( @@ -105,14 +102,14 @@ const AgentsTableComponent: React.FC = ({ agentSelection, onCh // add the number of agents added via policy and platform groups getNumAgentsInGrouping(selectedGroups) - // subtract the number of agents double counted by policy/platform selections - getNumOverlapped(selectedGroups, groups.overlap) + getNumOverlapped(selectedGroups, agentGroupsData?.groups?.overlap ?? {}) ); } onChange(newAgentSelection); setSelectedOptions(selection); }, - [groups, onChange, totalNumAgents] + [agentGroupsData, onChange] ); useEffect(() => { @@ -154,26 +151,18 @@ const AgentsTableComponent: React.FC = ({ agentSelection, onCh }, [agentSelection, onSelection, options, selectedOptions]); useEffect(() => { - if (agentsFetched && groupsFetched) { + if (agentsFetched && groupsFetched && agentGroupsData) { + const grouper = new AgentGrouper(); // update the groups when groups or agents have changed - grouper.setTotalAgents(totalNumAgents); - grouper.updateGroup(AGENT_GROUP_KEY.Platform, groups.platforms); - grouper.updateGroup(AGENT_GROUP_KEY.Policy, groups.policies); + grouper.setTotalAgents(agentGroupsData?.totalCount); + grouper.updateGroup(AGENT_GROUP_KEY.Platform, agentGroupsData?.groups.platforms); + grouper.updateGroup(AGENT_GROUP_KEY.Policy, agentGroupsData?.groups.policies); // @ts-expect-error update types grouper.updateGroup(AGENT_GROUP_KEY.Agent, agents); const newOptions = grouper.generateOptions(); - setOptions(newOptions); + setOptions((prevOptions) => (!deepEqual(prevOptions, newOptions) ? newOptions : prevOptions)); } - }, [ - groups.platforms, - groups.policies, - totalNumAgents, - groupsLoading, - agents, - agentsFetched, - groupsFetched, - grouper, - ]); + }, [groupsLoading, agents, agentsFetched, groupsFetched, agentGroupsData]); const renderOption = useCallback((option, searchVal, contentClassName) => { const { label, value } = option; @@ -202,6 +191,7 @@ const AgentsTableComponent: React.FC = ({ agentSelection, onCh return (
= ({ agentSelection, onCh ); }; +AgentsTableComponent.displayName = 'AgentsTable'; + export const AgentsTable = React.memo(AgentsTableComponent); diff --git a/x-pack/plugins/osquery/public/agents/helpers.ts b/x-pack/plugins/osquery/public/agents/helpers.ts index 1c23eac3ca201..db48b5d417c96 100644 --- a/x-pack/plugins/osquery/public/agents/helpers.ts +++ b/x-pack/plugins/osquery/public/agents/helpers.ts @@ -38,7 +38,17 @@ interface Aggs extends estypes.AggregationsTermsAggregateBase { buckets: AggregationDataPoint[]; } -export const processAggregations = (aggs: Record) => { +export const processAggregations = ( + aggs: Record | undefined +) => { + if (!aggs) { + return { + platforms: [], + overlap: {}, + policies: [], + }; + } + const platforms: Group[] = []; const overlap: Overlap = {}; const platformTerms = aggs.platforms as Aggs; diff --git a/x-pack/plugins/osquery/public/agents/use_agent_groups.ts b/x-pack/plugins/osquery/public/agents/use_agent_groups.ts index 2dba1c471a786..3dc96c1f150c7 100644 --- a/x-pack/plugins/osquery/public/agents/use_agent_groups.ts +++ b/x-pack/plugins/osquery/public/agents/use_agent_groups.ts @@ -4,10 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { useState } from 'react'; import { useQuery } from 'react-query'; import { i18n } from '@kbn/i18n'; -import { firstValueFrom } from 'rxjs'; +import { lastValueFrom } from 'rxjs'; import { useKibana } from '../common/lib/kibana'; import { useAgentPolicies } from './use_agent_policies'; @@ -19,28 +18,27 @@ import { import { processAggregations } from './helpers'; import { generateTablePaginationOptions } from '../common/helpers'; -import { Overlap, Group } from './types'; import { useErrorToast } from '../common/hooks/use_error_toast'; +import { useOsqueryPolicies } from './use_osquery_policies'; -interface UseAgentGroups { - osqueryPolicies: string[]; - osqueryPoliciesLoading: boolean; -} - -export const useAgentGroups = ({ osqueryPolicies, osqueryPoliciesLoading }: UseAgentGroups) => { +export const useAgentGroups = () => { const { data } = useKibana().services; const setErrorToast = useErrorToast(); + const { data: osqueryPolicies, isFetched: isOsqueryPoliciesFetched } = useOsqueryPolicies(); const { agentPoliciesLoading, agentPolicyById } = useAgentPolicies(osqueryPolicies); - const [platforms, setPlatforms] = useState([]); - const [policies, setPolicies] = useState([]); - const [loading, setLoading] = useState(true); - const [overlap, setOverlap] = useState(() => ({})); - const [totalCount, setTotalCount] = useState(0); - const { isFetched } = useQuery( + + return useQuery< + AgentsStrategyResponse, + unknown, + { + totalCount: number; + groups: ReturnType; + } + >( ['agentGroups'], async () => { - const responseData = await firstValueFrom( + const responseData = await lastValueFrom( data.search.search( { filterQuery: { terms: { policy_id: osqueryPolicies } }, @@ -76,32 +74,54 @@ export const useAgentGroups = ({ osqueryPolicies, osqueryPoliciesLoading }: UseA ) ); - if (responseData.rawResponse.aggregations) { - const { - platforms: newPlatforms, - overlap: newOverlap, - policies: newPolicies, - } = processAggregations(responseData.rawResponse.aggregations); - - setPlatforms(newPlatforms); - setOverlap(newOverlap); - setPolicies( - newPolicies.map((p) => { - const name = agentPolicyById[p.id]?.name ?? p.name; - - return { - ...p, - name, - }; - }) - ); - } - - setLoading(false); - setTotalCount(responseData.totalCount); + return responseData; }, { - enabled: !osqueryPoliciesLoading && !agentPoliciesLoading, + select: (response) => { + const { platforms, overlap, policies } = processAggregations( + response.rawResponse.aggregations + ); + + return { + totalCount: response.totalCount, + groups: { + platforms, + overlap, + policies: policies.map((p) => { + const name = agentPolicyById[p.id]?.name ?? p.name; + + return { + ...p, + name, + }; + }), + }, + }; + }, + placeholderData: { + totalCount: 0, + edges: [], + pageInfo: { + activePage: 1, + fakeTotalCount: 100, + showMorePagesIndicator: true, + }, + rawResponse: { + took: 0, + timed_out: false, + _shards: { + failed: 0, + successful: 0, + total: 0, + }, + hits: { + hits: [], + }, + }, + }, + refetchOnWindowFocus: false, + keepPreviousData: true, + enabled: isOsqueryPoliciesFetched && !agentPoliciesLoading, onSuccess: () => setErrorToast(), onError: (error) => setErrorToast(error as Error, { @@ -111,15 +131,4 @@ export const useAgentGroups = ({ osqueryPolicies, osqueryPoliciesLoading }: UseA }), } ); - - return { - isFetched, - loading, - totalCount, - groups: { - platforms, - policies, - overlap, - }, - }; }; diff --git a/x-pack/plugins/osquery/public/agents/use_all_agents.ts b/x-pack/plugins/osquery/public/agents/use_all_agents.ts index 9ffcaae16bc6d..defe625eb9a28 100644 --- a/x-pack/plugins/osquery/public/agents/use_all_agents.ts +++ b/x-pack/plugins/osquery/public/agents/use_all_agents.ts @@ -11,11 +11,7 @@ import { useQuery } from 'react-query'; import { GetAgentsResponse } from '@kbn/fleet-plugin/common'; import { useErrorToast } from '../common/hooks/use_error_toast'; import { useKibana } from '../common/lib/kibana'; - -interface UseAllAgents { - osqueryPolicies: string[]; - osqueryPoliciesLoading: boolean; -} +import { useOsqueryPolicies } from './use_osquery_policies'; interface RequestOptions { perPage?: number; @@ -23,22 +19,24 @@ interface RequestOptions { } // TODO: break out the paginated vs all cases into separate hooks -export const useAllAgents = ( - { osqueryPolicies, osqueryPoliciesLoading }: UseAllAgents, - searchValue = '', - opts: RequestOptions = { perPage: 9000 } -) => { +export const useAllAgents = (searchValue = '', opts: RequestOptions = { perPage: 9000 }) => { const { perPage } = opts; const { http } = useKibana().services; const setErrorToast = useErrorToast(); + const { data: osqueryPolicies, isFetched } = useOsqueryPolicies(); + return useQuery( ['agents', osqueryPolicies, searchValue, perPage], () => { - let kuery = `(${osqueryPolicies.map((p) => `policy_id:${p}`).join(' or ')})`; + let kuery = ''; + + if (osqueryPolicies?.length) { + kuery = `(${osqueryPolicies.map((p) => `policy_id:${p}`).join(' or ')})`; - if (searchValue) { - kuery += ` and (local_metadata.host.hostname:*${searchValue}* or local_metadata.elastic.agent.id:*${searchValue}*)`; + if (searchValue) { + kuery += ` and (local_metadata.host.hostname:*${searchValue}* or local_metadata.elastic.agent.id:*${searchValue}*)`; + } } return http.get(`/internal/osquery/fleet_wrapper/agents`, { @@ -51,7 +49,7 @@ export const useAllAgents = ( { // @ts-expect-error update types select: (data) => data?.agents || [], - enabled: !osqueryPoliciesLoading && osqueryPolicies.length > 0, + enabled: isFetched && !!osqueryPolicies?.length, onSuccess: () => setErrorToast(), onError: (error) => // @ts-expect-error update types diff --git a/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts b/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts index 679aeef1bd23b..9ecf5d14c6b38 100644 --- a/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts +++ b/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts @@ -7,7 +7,6 @@ import { uniq } from 'lodash'; import { useQuery } from 'react-query'; -import { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../common/lib/kibana'; import { useErrorToast } from '../common/hooks/use_error_toast'; @@ -16,7 +15,7 @@ export const useOsqueryPolicies = () => { const { http } = useKibana().services; const setErrorToast = useErrorToast(); - const { isLoading: osqueryPoliciesLoading, data: osqueryPolicies = [] } = useQuery( + return useQuery( ['osqueryPolicies'], () => http.get<{ items: Array<{ policy_id: string }> }>( @@ -33,9 +32,4 @@ export const useOsqueryPolicies = () => { }), } ); - - return useMemo( - () => ({ osqueryPoliciesLoading, osqueryPolicies }), - [osqueryPoliciesLoading, osqueryPolicies] - ); }; diff --git a/x-pack/plugins/osquery/public/live_queries/form/index.tsx b/x-pack/plugins/osquery/public/live_queries/form/index.tsx index e63ad38aaa695..6a72a0b59979f 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/index.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/index.tsx @@ -8,22 +8,21 @@ import { EuiButton, EuiButtonEmpty, - EuiSteps, EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiAccordion, EuiAccordionProps, } from '@elastic/eui'; -import { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useMutation } from 'react-query'; import deepMerge from 'deepmerge'; import styled from 'styled-components'; -import { pickBy, isEmpty } from 'lodash'; +import { pickBy, isEmpty, map } from 'lodash'; +import { convertECSMappingToObject } from '../../../common/schemas/common/utils'; import { UseField, Form, FormData, useForm, useFormData, FIELD_TYPES } from '../../shared_imports'; import { AgentsTableField } from './agents_table_field'; import { LiveQueryQueryField } from './live_query_query_field'; @@ -33,16 +32,13 @@ import { queryFieldValidation } from '../../common/validations'; import { fieldValidators } from '../../shared_imports'; import { SavedQueryFlyout } from '../../saved_queries'; import { useErrorToast } from '../../common/hooks/use_error_toast'; -import { - ECSMappingEditorField, - ECSMappingEditorFieldRef, -} from '../../packs/queries/lazy_ecs_mapping_editor_field'; +import { ECSMappingEditorField } from '../../packs/queries/lazy_ecs_mapping_editor_field'; import { SavedQueriesDropdown } from '../../saved_queries/saved_queries_dropdown'; const FORM_ID = 'liveQueryForm'; const StyledEuiAccordion = styled(EuiAccordion)` - ${({ isDisabled }: { isDisabled: boolean }) => isDisabled && 'display: none;'} + ${({ isDisabled }: { isDisabled?: boolean }) => isDisabled && 'display: none;'} .euiAccordion__button { color: ${({ theme }) => theme.eui.euiColorPrimary}; } @@ -55,27 +51,26 @@ const GhostFormField = () => <>; type FormType = 'simple' | 'steps'; interface LiveQueryFormProps { - defaultValue?: Partial | undefined; + defaultValue?: Partial; onSuccess?: () => void; - agentsField?: boolean; queryField?: boolean; ecsMappingField?: boolean; formType?: FormType; enabled?: boolean; - isExternal?: true; + hideAgentsField?: boolean; + addToTimeline?: (payload: { query: [string, string]; isIcon?: true }) => React.ReactElement; } const LiveQueryFormComponent: React.FC = ({ defaultValue, onSuccess, - agentsField = true, queryField = true, ecsMappingField = true, formType = 'steps', enabled = true, - isExternal, + hideAgentsField = false, + addToTimeline, }) => { - const ecsFieldRef = useRef(); const permissions = useKibana().services.application.capabilities.osquery; const { http } = useKibana().services; const [advancedContentState, setAdvancedContentState] = @@ -136,7 +131,7 @@ const LiveQueryFormComponent: React.FC = ({ ], }, ecs_mapping: { - defaultValue: {}, + defaultValue: [], type: FIELD_TYPES.JSON, validations: [], }, @@ -146,18 +141,9 @@ const LiveQueryFormComponent: React.FC = ({ id: FORM_ID, schema: formSchema, onSubmit: async (formData, isValid) => { - const ecsFieldValue = await ecsFieldRef?.current?.validate(); - if (isValid && (!ecsMappingField || !!ecsFieldValue)) { + if (isValid) { try { - await mutateAsync( - pickBy( - { - ...formData, - ...(isEmpty(ecsFieldValue) ? {} : { ecs_mapping: ecsFieldValue }), - }, - (value) => !isEmpty(value) - ) - ); + await mutateAsync(pickBy(formData, (value) => !isEmpty(value))); // eslint-disable-next-line no-empty } catch (e) {} } @@ -165,8 +151,16 @@ const LiveQueryFormComponent: React.FC = ({ options: { stripEmptyFields: false, }, - serializer: ({ savedQueryId, ...formData }) => - pickBy({ ...formData, saved_query_id: savedQueryId }, (value) => !isEmpty(value)), + // eslint-disable-next-line @typescript-eslint/naming-convention + serializer: ({ savedQueryId, ecs_mapping, ...formData }) => + pickBy( + { + ...formData, + saved_query_id: savedQueryId, + ecs_mapping: convertECSMappingToObject(ecs_mapping), + }, + (value) => !isEmpty(value) + ), defaultValue: deepMerge( { agentSelection: { @@ -177,12 +171,13 @@ const LiveQueryFormComponent: React.FC = ({ }, query: '', savedQueryId: null, + ecs_mapping: [], }, defaultValue ?? {} ), }); - const { setFieldValue, submit, isSubmitting } = form; + const { updateFieldValues, setFieldValue, submit, isSubmitting } = form; const actionId = useMemo(() => data?.actions[0].action_id, [data?.actions]); const agentIds = useMemo(() => data?.actions[0].agents, [data?.actions]); @@ -207,13 +202,12 @@ const LiveQueryFormComponent: React.FC = ({ const queryValueProvided = useMemo(() => !!query?.length, [query]); const queryStatus = useMemo(() => { - if (!agentSelected) return 'disabled'; - if (isError || !form.getFields().query.isValid) return 'danger'; + if (isError || !form.getFields().query?.isValid) return 'danger'; if (isLoading) return 'loading'; if (isSuccess) return 'complete'; return 'incomplete'; - }, [agentSelected, isError, isLoading, isSuccess, form]); + }, [isError, isLoading, isSuccess, form]); const resultsStatus = useMemo( () => (queryStatus === 'complete' ? 'incomplete' : 'disabled'), @@ -223,19 +217,28 @@ const LiveQueryFormComponent: React.FC = ({ const handleSavedQueryChange = useCallback( (savedQuery) => { if (savedQuery) { - setFieldValue('query', savedQuery.query); - setFieldValue('savedQueryId', savedQuery.savedQueryId); + updateFieldValues({ + query: savedQuery.query, + savedQueryId: savedQuery.savedQueryId, + ecs_mapping: savedQuery.ecs_mapping + ? map(savedQuery.ecs_mapping, (value, key) => ({ + key, + result: { + type: Object.keys(value)[0], + value: Object.values(value)[0], + }, + })) + : [], + }); + if (!isEmpty(savedQuery.ecs_mapping)) { - setFieldValue('ecs_mapping', savedQuery.ecs_mapping); setAdvancedContentState('open'); - } else { - setFieldValue('ecs_mapping', {}); } } else { setFieldValue('savedQueryId', null); } }, - [setFieldValue] + [setFieldValue, updateFieldValues] ); const commands = useMemo( @@ -251,10 +254,9 @@ const LiveQueryFormComponent: React.FC = ({ const queryComponentProps = useMemo( () => ({ - disabled: queryStatus === 'disabled', commands, }), - [queryStatus, commands] + [commands] ); const flyoutFormDefaultValue = useMemo( @@ -275,9 +277,8 @@ const LiveQueryFormComponent: React.FC = ({ ); const isSavedQueryDisabled = useMemo( - () => - queryComponentProps.disabled || !permissions.runSavedQueries || !permissions.readSavedQueries, - [permissions.readSavedQueries, permissions.runSavedQueries, queryComponentProps.disabled] + () => !permissions.runSavedQueries || !permissions.readSavedQueries, + [permissions.readSavedQueries, permissions.runSavedQueries] ); const queryFieldStepContent = useMemo( @@ -314,16 +315,9 @@ const LiveQueryFormComponent: React.FC = ({ forceState={advancedContentState} onToggle={handleToggle} buttonContent="Advanced" - isDisabled={queryComponentProps.disabled} > - + ) : ( @@ -372,7 +366,6 @@ const LiveQueryFormComponent: React.FC = ({ ecsMappingField, advancedContentState, handleToggle, - query, ecsFieldProps, formType, agentSelected, @@ -393,81 +386,49 @@ const LiveQueryFormComponent: React.FC = ({ actionId={actionId} endDate={data?.actions[0].expiration} agentIds={agentIds} - isExternal={isExternal} + addToTimeline={addToTimeline} /> ) : null, - [actionId, agentIds, data?.actions, isExternal] - ); - - const formSteps: EuiContainedStepProps[] = useMemo( - () => [ - { - title: i18n.translate('xpack.osquery.liveQueryForm.steps.agentsStepHeading', { - defaultMessage: 'Select agents', - }), - children: , - status: agentSelected ? 'complete' : 'incomplete', - }, - { - title: i18n.translate('xpack.osquery.liveQueryForm.steps.queryStepHeading', { - defaultMessage: 'Enter query', - }), - children: queryFieldStepContent, - status: queryStatus, - }, - { - title: i18n.translate('xpack.osquery.liveQueryForm.steps.resultsStepHeading', { - defaultMessage: 'Check results', - }), - children: resultsStepContent, - status: resultsStatus, - }, - ], - [agentSelected, queryFieldStepContent, queryStatus, resultsStepContent, resultsStatus] - ); - - const simpleForm = useMemo( - () => ( - - - {queryFieldStepContent} - {resultsStepContent} - - ), - [agentsField, queryFieldStepContent, resultsStepContent] + [actionId, agentIds, data?.actions, addToTimeline] ); useEffect(() => { - if (defaultValue?.agentSelection) { - setFieldValue('agentSelection', defaultValue?.agentSelection); - } - - if (defaultValue?.query) { - setFieldValue('query', defaultValue?.query); - } - - // TODO: Set query and ECS mapping from savedQueryId object - if (defaultValue?.savedQueryId) { - setFieldValue('savedQueryId', defaultValue?.savedQueryId); - } - - if (!isEmpty(defaultValue?.ecs_mapping)) { - setFieldValue('ecs_mapping', defaultValue?.ecs_mapping); + if (defaultValue) { + updateFieldValues({ + agentSelection: defaultValue.agentSelection, + query: defaultValue.query, + savedQueryId: defaultValue.savedQueryId, + ecs_mapping: defaultValue.ecs_mapping + ? map(defaultValue.ecs_mapping, (value, key) => ({ + key, + result: { + type: Object.keys(value)[0], + value: Object.values(value)[0], + }, + })) + : undefined, + }); } - }, [defaultValue, setFieldValue]); + }, [defaultValue, updateFieldValues]); return ( <>
- {formType === 'steps' ? : simpleForm} + + + + + {queryFieldStepContent} + {resultsStepContent} + {showSavedQueryFlyout ? ( diff --git a/x-pack/plugins/osquery/public/live_queries/form/schema.ts b/x-pack/plugins/osquery/public/live_queries/form/schema.ts deleted file mode 100644 index 3d0195a112fec..0000000000000 --- a/x-pack/plugins/osquery/public/live_queries/form/schema.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FIELD_TYPES, FormSchema } from '../../shared_imports'; - -export const formSchema: FormSchema = { - agents: { - type: FIELD_TYPES.MULTI_SELECT, - }, - query: { - type: FIELD_TYPES.TEXTAREA, - validations: [], - }, -}; diff --git a/x-pack/plugins/osquery/public/live_queries/index.tsx b/x-pack/plugins/osquery/public/live_queries/index.tsx index 56cac9cdf5ec7..a147f929f7f25 100644 --- a/x-pack/plugins/osquery/public/live_queries/index.tsx +++ b/x-pack/plugins/osquery/public/live_queries/index.tsx @@ -28,7 +28,8 @@ interface LiveQueryProps { ecsMappingField?: boolean; enabled?: boolean; formType?: 'steps' | 'simple'; - isExternal?: true; + hideAgentsField?: boolean; + addToTimeline?: (payload: { query: [string, string]; isIcon?: true }) => React.ReactElement; } const LiveQueryComponent: React.FC = ({ @@ -40,12 +41,12 @@ const LiveQueryComponent: React.FC = ({ savedQueryId, // eslint-disable-next-line @typescript-eslint/naming-convention ecs_mapping, - agentsField, queryField, ecsMappingField, formType, enabled, - isExternal, + hideAgentsField, + addToTimeline, }) => { const { data: hasActionResultsPrivileges, isLoading } = useActionResultsPrivileges(); @@ -108,14 +109,14 @@ const LiveQueryComponent: React.FC = ({ return ( ); }; diff --git a/x-pack/plugins/osquery/public/packs/queries/ecs_mapping_editor_field.tsx b/x-pack/plugins/osquery/public/packs/queries/ecs_mapping_editor_field.tsx index bca3036d19f4c..a9c663051e273 100644 --- a/x-pack/plugins/osquery/public/packs/queries/ecs_mapping_editor_field.tsx +++ b/x-pack/plugins/osquery/public/packs/queries/ecs_mapping_editor_field.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import { produce } from 'immer'; import { castArray, each, @@ -19,16 +18,7 @@ import { trim, get, } from 'lodash'; -import React, { - forwardRef, - useCallback, - useEffect, - useMemo, - useRef, - useState, - useImperativeHandle, - MutableRefObject, -} from 'react'; +import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { EuiFormLabel, EuiButtonIcon, @@ -49,7 +39,6 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; import deepEqual from 'fast-deep-equal'; -import deepmerge from 'deepmerge'; import ECSSchema from '../../common/schemas/ecs/v8.2.0.json'; import osquerySchema from '../../common/schemas/osquery/v5.2.2.json'; @@ -57,17 +46,17 @@ import osquerySchema from '../../common/schemas/osquery/v5.2.2.json'; import { FieldIcon } from '../../common/lib/kibana'; import { FIELD_TYPES, - Form, - FormData, FieldHook, getFieldValidityAndErrorMessage, - useForm, useFormData, Field, getUseField, fieldValidators, ValidationFuncArg, UseMultiFields, + UseArray, + ArrayItem, + FormArrayField, } from '../../shared_imports'; import { OsqueryIcon } from '../../components/osquery_icon'; @@ -179,7 +168,7 @@ const ECSComboboxFieldComponent: React.FC = ({ setSelected(newSelectedOptions); setValue(newSelectedOptions[0]?.label ?? ''); }, - [setSelected, setValue] + [setValue] ); // TODO: Create own component for this. @@ -339,7 +328,7 @@ const OsqueryColumnFieldComponent: React.FC = ({ }) => { const inputRef = useRef(); const { setValue } = resultValue; - const { setValue: setType } = resultType; + const { value: typeValue, setValue: setType } = resultType; const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(resultValue); const describedByIds = useMemo(() => (idAria ? [idAria] : []), [idAria]); const [selectedOptions, setSelected] = useState< @@ -383,12 +372,12 @@ const OsqueryColumnFieldComponent: React.FC = ({ const onTypeChange = useCallback( (newType) => { - if (newType !== resultType.value) { + if (newType !== typeValue) { setType(newType); setValue(newType === 'value' && euiFieldProps.singleSelection === false ? [] : ''); } }, - [resultType.value, setType, setValue, euiFieldProps.singleSelection] + [typeValue, setType, setValue, euiFieldProps.singleSelection] ); const handleCreateOption = useCallback( @@ -416,8 +405,9 @@ const OsqueryColumnFieldComponent: React.FC = ({ const Prepend = useMemo( () => ( = ({ onChange={onTypeChange} /> ), - [onTypeChange, resultType.value] + [euiFieldProps.isDisabled, onTypeChange, typeValue] ); useEffect(() => { @@ -438,8 +428,7 @@ const OsqueryColumnFieldComponent: React.FC = ({ if (!euiFieldProps?.singleSelection && !isArray(resultValue.value)) { setValue(resultValue.value.length ? [resultValue.value] : []); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [euiFieldProps?.singleSelection, setValue]); + }, [euiFieldProps?.singleSelection, resultValue.value, setValue]); useEffect(() => { setSelected(() => { @@ -482,7 +471,7 @@ const OsqueryColumnFieldComponent: React.FC = ({ rowHeight={32} isClearable {...euiFieldProps} - options={(resultType.value === 'field' && euiFieldProps.options) || EMPTY_ARRAY} + options={(typeValue === 'field' && euiFieldProps.options) || EMPTY_ARRAY} /> @@ -490,105 +479,96 @@ const OsqueryColumnFieldComponent: React.FC = ({ ); }; -export const OsqueryColumnField = React.memo( - OsqueryColumnFieldComponent, - (prevProps, nextProps) => - prevProps.resultType.value === nextProps.resultType.value && - prevProps.resultType.isChangingValue === nextProps.resultType.isChangingValue && - prevProps.resultType.errors === nextProps.resultType.errors && - prevProps.resultValue.value === nextProps.resultValue.value && - prevProps.resultValue.isChangingValue === nextProps.resultValue.isChangingValue && - prevProps.resultValue.errors === nextProps.resultValue.errors && - deepEqual(prevProps.euiFieldProps, nextProps.euiFieldProps) -); - -export interface ECSMappingEditorFieldRef { - validate: () => Promise< - | Record< - string, - { - field: string; - } - > - | false - | {} - >; -} +export const OsqueryColumnField = React.memo(OsqueryColumnFieldComponent); export interface ECSMappingEditorFieldProps { - field: FieldHook>; - query: string; - fieldRef: MutableRefObject; - euiFieldProps: EuiComboBoxProps<{}>; + euiFieldProps?: EuiComboBoxProps<{}>; } interface ECSMappingEditorFormProps { isDisabled?: boolean; osquerySchemaOptions: OsquerySchemaOption[]; - defaultValue?: FormData; - onAdd?: (payload: FormData) => void; - onChange?: (payload: FormData) => void; - onDelete?: (key: string) => void; + item: ArrayItem; + isLastItem?: boolean; + onDelete?: FormArrayField['removeItem']; } -const getEcsFieldValidator = - (editForm: boolean) => - (args: ValidationFuncArg) => { - const fieldRequiredError = fieldValidators.emptyField( - i18n.translate('xpack.osquery.pack.queryFlyoutForm.ecsFieldRequiredErrorMessage', { - defaultMessage: 'ECS field is required.', - }) - )(args); - +const ecsFieldValidator = ( + args: ValidationFuncArg & { + customData: { + value: { + editForm: boolean; + }; + }; + } +) => { + const editForm: boolean = args.customData.value?.editForm; + const rootPath = args.path.split('.')[0]; + + const fieldRequiredError = fieldValidators.emptyField( + i18n.translate('xpack.osquery.pack.queryFlyoutForm.ecsFieldRequiredErrorMessage', { + defaultMessage: 'ECS field is required.', + }) + )(args); + + if ( + fieldRequiredError && // @ts-expect-error update types - if (fieldRequiredError && ((!editForm && args.formData['result.value'].length) || editForm)) { - return fieldRequiredError; - } + ((!editForm && args.formData[`${rootPath}.result.value`]?.length) || editForm) + ) { + return fieldRequiredError; + } - return undefined; - }; + return undefined; +}; -const getOsqueryResultFieldValidator = - (osquerySchemaOptions: OsquerySchemaOption[], editForm: boolean) => - ( - args: ValidationFuncArg - ) => { - const fieldRequiredError = fieldValidators.emptyField( - i18n.translate('xpack.osquery.pack.queryFlyoutForm.osqueryResultFieldRequiredErrorMessage', { - defaultMessage: 'Value is required.', - }) - )(args); - - if (fieldRequiredError && ((!editForm && args.formData.key.length) || editForm)) { - return fieldRequiredError; - } +const osqueryResultFieldValidator = async ( + args: ValidationFuncArg & { + customData: { + value: { + editForm: boolean; + osquerySchemaOptions: OsquerySchemaOption[]; + }; + }; + } +) => { + const rootPath = args.path.split('.')[0]; + const { editForm, osquerySchemaOptions } = args.customData.value; + const fieldRequiredError = fieldValidators.emptyField( + i18n.translate('xpack.osquery.pack.queryFlyoutForm.osqueryResultFieldRequiredErrorMessage', { + defaultMessage: 'Value is required.', + }) + )(args); + + // @ts-expect-error update types + if (fieldRequiredError && ((!editForm && args.formData[`${rootPath}.key`]?.length) || editForm)) { + return fieldRequiredError; + } - // @ts-expect-error update types - if (!args.value?.length || args.formData['result.type'] !== 'field') return; - - const osqueryColumnExists = find(osquerySchemaOptions, ['label', args.value]); - - return !osqueryColumnExists - ? { - code: 'ERR_FIELD_FORMAT', - path: args.path, - message: i18n.translate( - 'xpack.osquery.pack.queryFlyoutForm.osqueryResultFieldValueMissingErrorMessage', - { - defaultMessage: 'The current query does not return a {columnName} field', - values: { - columnName: args.value, - }, - } - ), - __isBlocking__: false, - } - : undefined; - }; + // @ts-expect-error update types + if (!args.value?.length || args.formData[`${rootPath}.result.type`] !== 'field') return; + + const osqueryColumnExists = find(osquerySchemaOptions, [ + 'label', + isArray(args.value) ? args.value[0] : args.value, + ]); -const FORM_DEFAULT_VALUE = { - key: '', - value: { field: '' }, + return !osqueryColumnExists + ? { + code: 'ERR_FIELD_FORMAT', + path: args.path, + message: i18n.translate( + 'xpack.osquery.pack.queryFlyoutForm.osqueryResultFieldValueMissingErrorMessage', + { + defaultMessage: 'The current query does not return a {columnName} field', + values: { + columnName: args.value, + }, + } + ), + __isBlocking__: false, + } + : undefined; }; interface ECSMappingEditorFormData { @@ -599,217 +579,141 @@ interface ECSMappingEditorFormData { }; } -interface ECSMappingEditorFormRef { - validate: () => Promise<{ - data: ECSMappingEditorFormData | {}; - isValid: boolean; - }>; -} +export const ECSMappingEditorForm: React.FC = ({ + isDisabled, + osquerySchemaOptions, + item, + isLastItem, + onDelete, +}) => { + const multipleValuesField = useRef(false); -export const ECSMappingEditorForm = forwardRef( - ({ isDisabled, osquerySchemaOptions, defaultValue, onAdd, onChange, onDelete }, ref) => { - const editForm = !!defaultValue; - const multipleValuesField = useRef(false); - const currentFormData = useRef(defaultValue); - const formSchema = useMemo( - () => ({ - key: { - type: FIELD_TYPES.COMBO_BOX, - fieldsToValidateOnChange: ['result.value', 'key'], - validations: [ - { - validator: getEcsFieldValidator(editForm), + const MultiFields = useMemo( + () => ( + ({ - key: data.key ?? '', - result: { - type: data.value - ? Object.keys(data.value)[0] - : OSQUERY_COLUMN_VALUE_TYPE_OPTIONS[0].value, - value: data.value ? Object.values(data.value)[0] : '', - }, - }), - }); - - const { submit, reset, validate, validateFields } = form; - - const [formData] = useFormData({ form }); - - const handleSubmit = useCallback(async () => { - validate(); - validateFields(['result.value', 'key']); - const { data, isValid } = await submit(); - - if (isValid) { - const serializedData = { - key: data.key, - value: { - [data.result.type]: data.result.value, }, - }; - if (onAdd) { - onAdd(serializedData); - } - - if (onChange) { - onChange(serializedData); - } - - reset(); - } - }, [validate, validateFields, submit, onAdd, onChange, reset]); - - const handleDeleteClick = useCallback(() => { - if (defaultValue?.key && onDelete) { - onDelete(defaultValue.key); - } - }, [defaultValue, onDelete]); - - const MultiFields = useMemo( - () => ( - - {(fields) => ( - - )} - - ), - [osquerySchemaOptions, isDisabled] - ); - - const ecsComboBoxEuiFieldProps = useMemo(() => ({ isDisabled }), [isDisabled]); + }, + }} + > + {(fields) => ( + + )} + + ), + [item.path, osquerySchemaOptions, isLastItem, isDisabled] + ); - useImperativeHandle( - ref, - () => ({ - validate: async () => { - if (!editForm && deepEqual(formData, FORM_DEFAULT_VALUE)) { - return { data: {}, isValid: true }; - } + const ecsComboBoxEuiFieldProps = useMemo(() => ({ isDisabled }), [isDisabled]); - validateFields(['result.value', 'key']); - const isValid = await validate(); + const validationData = useMemo(() => ({ editForm: !isLastItem }), [isLastItem]); - return { - data: formData?.key?.length - ? { - [formData.key]: { - [formData.result.type]: formData.result.value, - }, - } - : {}, - isValid, - }; + const config = useMemo( + () => ({ + valueChangeDebounceTime: 300, + fieldsToValidateOnChange: [`${item.path}.key`, `${item.path}.result.value`], + validations: [ + { + validator: ecsFieldValidator, }, - }), - [validateFields, editForm, formData, validate] - ); + ], + }), + [item.path] + ); - useEffect(() => { - if (!deepEqual(formData, currentFormData.current)) { - currentFormData.current = formData; - const ecsOption = find(ECSSchemaOptions, ['label', formData.key]); - multipleValuesField.current = - ecsOption?.value?.normalization === 'array' && formData.result.type === 'value'; - handleSubmit(); - } - }, [handleSubmit, formData, onAdd]); + const handleDeleteClick = useCallback(() => { + if (onDelete) { + onDelete(item.id); + } + }, [item.id, onDelete]); - return ( -
- - - - - - + return ( + <> + + + + + + + + + : + + + + + + + {MultiFields} + {!isDisabled && ( - - : - + + {!isLastItem && ( + + )} + - - - - - {MultiFields} - {!isDisabled && ( - - - {defaultValue && ( - - )} - - - )} - - - - - - ); - } -); + )} + + +
+ + + ); +}; interface OsquerySchemaOption { label: string; @@ -831,177 +735,155 @@ interface OsqueryColumn { } export const ECSMappingEditorField = React.memo( - ({ field, query, fieldRef, euiFieldProps }: ECSMappingEditorFieldProps) => { - const { setValue, value = {} } = field; - const [osquerySchemaOptions, setOsquerySchemaOptions] = useState([]); - const formRefs = useRef>({}); - - useImperativeHandle( - fieldRef, - () => ({ - validate: async () => { - const validations = await Promise.all( - Object.values(formRefs.current).map(async (formRef) => { - const { data, isValid } = await formRef.validate(); - - return [data, isValid]; - }) - ); - - if (find(validations, (result) => result[1] === false)) { - return false; - } - - return deepmerge.all(map(validations, '[0]')); - }, - }), - [] - ); + ({ euiFieldProps }: ECSMappingEditorFieldProps) => { + const lastItemPath = useRef(); + const onAdd = useRef(); + const osquerySchemaOptions = useRef([]); + const [{ query, ...formData }, formDataSerializer, isMounted] = useFormData(); useEffect(() => { - setOsquerySchemaOptions((currentValue) => { - if (!query?.length) { - return currentValue; - } + if (!query?.length) { + return; + } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let ast: Record | undefined; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let ast: Record | undefined; - try { - ast = sqlParser.parse(query)?.value; - } catch (e) { - return currentValue; - } + try { + ast = sqlParser.parse(query)?.value; + } catch (e) { + return; + } - const astOsqueryTables: Record< - string, - { - columns: OsqueryColumn[]; - order: number; - } - > = - ast?.from?.value?.reduce( - ( - acc: { - [x: string]: { - columns: OsqueryColumn[]; - order: number; - }; - }, - table: { - value: { - left?: { value: { value: string }; alias?: { value: string } }; - right?: { value: { value: string }; alias?: { value: string } }; - value?: { value: string }; - alias?: { value: string }; - }; - } - ) => { - each(['value.left', 'value.right', 'value'], (valueKey) => { - if (valueKey) { - const osqueryTable = find(osquerySchema, [ - 'name', - get(table, `${valueKey}.value.value`), - ]); - - if (osqueryTable) { - acc[ - get(table, `${valueKey}.alias.value`) ?? get(table, `${valueKey}.value.value`) - ] = { - columns: osqueryTable.columns, - order: Object.keys(acc).length, - }; - } + const astOsqueryTables: Record< + string, + { + columns: OsqueryColumn[]; + order: number; + } + > = + ast?.from?.value?.reduce( + ( + acc: { + [x: string]: { + columns: OsqueryColumn[]; + order: number; + }; + }, + table: { + value: { + left?: { value: { value: string }; alias?: { value: string } }; + right?: { value: { value: string }; alias?: { value: string } }; + value?: { value: string }; + alias?: { value: string }; + }; + } + ) => { + each(['value.left', 'value.right', 'value'], (valueKey) => { + if (valueKey) { + const osqueryTable = find(osquerySchema, [ + 'name', + get(table, `${valueKey}.value.value`), + ]); + + if (osqueryTable) { + acc[ + get(table, `${valueKey}.alias.value`) ?? get(table, `${valueKey}.value.value`) + ] = { + columns: osqueryTable.columns, + order: Object.keys(acc).length, + }; } - }); + } + }); - return acc; - }, - {} - ) ?? {}; + return acc; + }, + {} + ) ?? {}; - // Table doesn't exist in osquery schema - if (isEmpty(astOsqueryTables)) { - return currentValue; - } + // Table doesn't exist in osquery schema + if (isEmpty(astOsqueryTables)) { + return; + } - const suggestions = - isArray(ast?.selectItems?.value) && - ast?.selectItems?.value - ?.map((selectItem: { type: string; value: string; hasAs: boolean; alias?: string }) => { - if (selectItem.type === 'Identifier') { - /* + const suggestions = + isArray(ast?.selectItems?.value) && + ast?.selectItems?.value + ?.map((selectItem: { type: string; value: string; hasAs: boolean; alias?: string }) => { + if (selectItem.type === 'Identifier') { + /* select * from routes, uptime; */ - if (ast?.selectItems?.value.length === 1 && selectItem.value === '*') { - return reduce( - astOsqueryTables, - (acc, { columns: osqueryColumns, order: tableOrder }, table) => { - acc.push( - ...osqueryColumns.map((osqueryColumn) => ({ - label: osqueryColumn.name, - value: { - name: osqueryColumn.name, - description: osqueryColumn.description, - table, - tableOrder, - suggestion_label: osqueryColumn.name, - }, - })) - ); - - return acc; - }, - [] as OsquerySchemaOption[] - ); - } - - /* - select i.*, p.resident_size, p.user_time, p.system_time, time.minutes as counter from osquery_info i, processes p, time where p.pid = i.pid; - */ - - const [table, column] = selectItem.value.includes('.') - ? selectItem.value?.split('.') - : [Object.keys(astOsqueryTables)[0], selectItem.value]; - - if (column === '*' && astOsqueryTables[table]) { - const { columns: osqueryColumns, order: tableOrder } = astOsqueryTables[table]; - - return osqueryColumns.map((osqueryColumn) => ({ - label: osqueryColumn.name, - value: { - name: osqueryColumn.name, - description: osqueryColumn.description, - table, - tableOrder, - suggestion_label: `${osqueryColumn.name}`, - }, - })); - } - - if (astOsqueryTables[table]) { - const osqueryColumn = find(astOsqueryTables[table].columns, ['name', column]); - - if (osqueryColumn) { - const label = selectItem.hasAs ? selectItem.alias : column; - - return [ - { - label, + if (ast?.selectItems?.value.length === 1 && selectItem.value === '*') { + return reduce( + astOsqueryTables, + (acc, { columns: osqueryColumns, order: tableOrder }, table) => { + acc.push( + ...osqueryColumns.map((osqueryColumn) => ({ + label: osqueryColumn.name, value: { name: osqueryColumn.name, description: osqueryColumn.description, table, - tableOrder: astOsqueryTables[table].order, - suggestion_label: `${label}`, + tableOrder, + suggestion_label: osqueryColumn.name, }, + })) + ); + + return acc; + }, + [] as OsquerySchemaOption[] + ); + } + + /* + select i.*, p.resident_size, p.user_time, p.system_time, time.minutes as counter from osquery_info i, processes p, time where p.pid = i.pid; + */ + + const [table, column] = selectItem.value.includes('.') + ? selectItem.value?.split('.') + : [Object.keys(astOsqueryTables)[0], selectItem.value]; + + if (column === '*' && astOsqueryTables[table]) { + const { columns: osqueryColumns, order: tableOrder } = astOsqueryTables[table]; + + return osqueryColumns.map((osqueryColumn) => ({ + label: osqueryColumn.name, + value: { + name: osqueryColumn.name, + description: osqueryColumn.description, + table, + tableOrder, + suggestion_label: `${osqueryColumn.name}`, + }, + })); + } + + if (astOsqueryTables[table]) { + const osqueryColumn = find(astOsqueryTables[table].columns, ['name', column]); + + if (osqueryColumn) { + const label = selectItem.hasAs ? selectItem.alias : column; + + return [ + { + label, + value: { + name: osqueryColumn.name, + description: osqueryColumn.description, + table, + tableOrder: astOsqueryTables[table].order, + suggestion_label: `${label}`, }, - ]; - } + }, + ]; } } + } - /* + /* SELECT pid, uid, name, ROUND(( (user_time + system_time) / (cpu_time.tsb - cpu_time.itsb) ) * 100, 2) AS percentage @@ -1015,95 +897,57 @@ export const ECSMappingEditorField = React.memo( LIMIT 5; */ - if (selectItem.hasAs && selectItem.alias) { - return [ - { - label: selectItem.alias, - value: { - name: selectItem.alias, - description: '', - table: '', - tableOrder: -1, - suggestion_label: selectItem.alias, - }, + if (selectItem.hasAs && selectItem.alias) { + return [ + { + label: selectItem.alias, + value: { + name: selectItem.alias, + description: '', + table: '', + tableOrder: -1, + suggestion_label: selectItem.alias, }, - ]; - } + }, + ]; + } - return []; - }) - .flat(); + return []; + }) + .flat(); - // Remove column duplicates by keeping the column from the table that appears last in the query - return sortedUniqBy( - orderBy(suggestions, ['value.suggestion_label', 'value.tableOrder'], ['asc', 'desc']), - 'label' - ); - }); + // Remove column duplicates by keeping the column from the table that appears last in the query + osquerySchemaOptions.current = sortedUniqBy( + orderBy(suggestions, ['value.suggestion_label', 'value.tableOrder'], ['asc', 'desc']), + 'label' + ); }, [query]); - useEffect(() => { - Object.keys(formRefs.current).forEach((key) => { - if (!value[key]) { - delete formRefs.current[key]; - } - }); - }, [value]); - - const handleAddRow = useCallback( - (newRow) => { - if (newRow?.key && newRow?.value) { - setValue( - produce((draft) => { - draft[newRow.key] = newRow.value; - - return draft; - }) - ); - } - }, - [setValue] - ); - - const handleUpdateRow = useCallback( - (currentKey: string) => (updatedRow: FormData) => { - if (updatedRow?.key && updatedRow?.value) { - setValue( - produce((draft) => { - if (currentKey !== updatedRow.key) { - delete draft[currentKey]; - } + useLayoutEffect(() => { + if (isMounted) { + if (!lastItemPath.current && onAdd.current) { + onAdd.current(); - draft[updatedRow.key] = updatedRow.value; + return; + } - return draft; - }) - ); + if (euiFieldProps?.isDisabled) { + return; } - }, - [setValue] - ); - const handleDeleteRow = useCallback( - (key) => { - if (key) { - setValue( - produce((draft) => { - if (draft[key]) { - delete draft[key]; - } + const itemKey = get(formData, `${lastItemPath.current}.key`); - return draft; - }) - ); + if (itemKey) { + const serializedFormData = formDataSerializer(); + const itemValue = + serializedFormData.ecs_mapping && serializedFormData.ecs_mapping[`${itemKey}`]?.field; - if (formRefs.current[key]) { - delete formRefs.current[key]; + if (itemValue && onAdd.current) { + onAdd.current(); } } - }, - [setValue] - ); + } + }, [euiFieldProps?.isDisabled, formData, formDataSerializer, isMounted, onAdd]); return ( <> @@ -1145,45 +989,31 @@ export const ECSMappingEditorField = React.memo( - {Object.entries(value).map(([ecsKey, ecsValue]) => ( - { - if (formRef) { - formRefs.current[ecsKey] = formRef; - } - }} - key={ecsKey} - osquerySchemaOptions={osquerySchemaOptions} - // eslint-disable-next-line react-perf/jsx-no-new-object-as-prop - defaultValue={{ - key: ecsKey, - value: ecsValue, - }} - onChange={handleUpdateRow(ecsKey)} - onDelete={handleDeleteRow} - isDisabled={!!euiFieldProps?.isDisabled} - /> - ))} - {!euiFieldProps?.isDisabled && ( - { - if (formRef) { - formRefs.current.new = formRef; - } - }} - osquerySchemaOptions={osquerySchemaOptions} - onAdd={handleAddRow} - /> - )} + + {({ items, addItem, removeItem }) => { + lastItemPath.current = items[items.length - 1]?.path; + onAdd.current = addItem; + + return ( + <> + {items.map((item, index) => ( + + ))} + + ); + }} + ); }, - (prevProps, nextProps) => - prevProps.field.value === nextProps.field.value && - prevProps.query === nextProps.query && - deepEqual(prevProps.euiFieldProps, nextProps.euiFieldProps) + (prevProps, nextProps) => deepEqual(prevProps.euiFieldProps, nextProps.euiFieldProps) ); // eslint-disable-next-line import/no-default-export diff --git a/x-pack/plugins/osquery/public/packs/queries/lazy_ecs_mapping_editor_field.tsx b/x-pack/plugins/osquery/public/packs/queries/lazy_ecs_mapping_editor_field.tsx index 69c3e4bc477c9..e2e4d45b669ee 100644 --- a/x-pack/plugins/osquery/public/packs/queries/lazy_ecs_mapping_editor_field.tsx +++ b/x-pack/plugins/osquery/public/packs/queries/lazy_ecs_mapping_editor_field.tsx @@ -6,14 +6,11 @@ */ import React, { lazy, Suspense } from 'react'; -import type { - ECSMappingEditorFieldProps, - ECSMappingEditorFieldRef, -} from './ecs_mapping_editor_field'; +import type { ECSMappingEditorFieldProps } from './ecs_mapping_editor_field'; const LazyECSMappingEditorField = lazy(() => import('./ecs_mapping_editor_field')); -export type { ECSMappingEditorFieldProps, ECSMappingEditorFieldRef }; +export type { ECSMappingEditorFieldProps }; export const ECSMappingEditorField = (props: ECSMappingEditorFieldProps) => ( diff --git a/x-pack/plugins/osquery/public/packs/queries/query_flyout.tsx b/x-pack/plugins/osquery/public/packs/queries/query_flyout.tsx index 8590968b58fb0..9bb0b521fb132 100644 --- a/x-pack/plugins/osquery/public/packs/queries/query_flyout.tsx +++ b/x-pack/plugins/osquery/public/packs/queries/query_flyout.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { isEmpty } from 'lodash'; +import { map } from 'lodash'; import { EuiFlyout, EuiTitle, @@ -19,17 +19,17 @@ import { EuiButton, EuiText, } from '@elastic/eui'; -import React, { useCallback, useMemo, useState, useRef } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { CodeEditorField } from '../../saved_queries/form/code_editor_field'; -import { Form, getUseField, Field, useFormData } from '../../shared_imports'; +import { Form, getUseField, Field } from '../../shared_imports'; import { PlatformCheckBoxGroupField } from './platform_checkbox_group_field'; import { ALL_OSQUERY_VERSIONS_OPTIONS } from './constants'; import { UsePackQueryFormProps, PackFormData, usePackQueryForm } from './use_pack_query_form'; import { SavedQueriesDropdown } from '../../saved_queries/saved_queries_dropdown'; -import { ECSMappingEditorField, ECSMappingEditorFieldRef } from './lazy_ecs_mapping_editor_field'; +import { ECSMappingEditorField } from './lazy_ecs_mapping_editor_field'; const CommonUseField = getUseField({ component: Field }); @@ -46,70 +46,46 @@ const QueryFlyoutComponent: React.FC = ({ onSave, onClose, }) => { - const ecsFieldRef = useRef(); const [isEditMode] = useState(!!defaultValue); const { form } = usePackQueryForm({ uniqueQueryIds, defaultValue, - handleSubmit: async (payload, isValid) => { - const ecsFieldValue = await ecsFieldRef?.current?.validate(); - const isEcsFieldValueValid = - ecsFieldValue && - Object.values(ecsFieldValue).every((field) => !isEmpty(Object.values(field)[0])); - - return new Promise((resolve) => { - if (isValid && isEcsFieldValueValid) { - onSave({ - ...payload, - ...(isEmpty(ecsFieldValue) ? {} : { ecs_mapping: ecsFieldValue }), - }); + handleSubmit: async (payload, isValid) => + new Promise((resolve) => { + if (isValid) { + onSave(payload); onClose(); } resolve(); - }); - }, + }), }); - const { submit, setFieldValue, reset, isSubmitting, validate } = form; - - const [{ query }] = useFormData({ - form, - watch: ['query'], - }); + const { submit, isSubmitting, updateFieldValues } = form; const handleSetQueryValue = useCallback( (savedQuery) => { - reset(); - if (savedQuery) { - setFieldValue('id', savedQuery.id); - setFieldValue('query', savedQuery.query); - - if (savedQuery.description) { - setFieldValue('description', savedQuery.description); - } - - if (savedQuery.interval) { - setFieldValue('interval', savedQuery.interval); - } - - if (savedQuery.platform) { - setFieldValue('platform', savedQuery.platform); - } - - if (savedQuery.version) { - setFieldValue('version', [savedQuery.version]); - } - - if (savedQuery.ecs_mapping) { - setFieldValue('ecs_mapping', savedQuery.ecs_mapping); - } + updateFieldValues({ + id: savedQuery.id, + query: savedQuery.query, + description: savedQuery.description, + platform: savedQuery.platform, + version: savedQuery.version, + interval: savedQuery.interval, + // @ts-expect-error update types + ecs_mapping: + map(savedQuery.ecs_mapping, (value, key) => ({ + key, + result: { + type: Object.keys(value)[0], + value: Object.values(value)[0], + }, + })) ?? [], + }); } - - validate(); }, - [reset, validate, setFieldValue] + [updateFieldValues] ); /* Avoids accidental closing of the flyout when the user clicks outside of the flyout */ const maskProps = useMemo(() => ({ onClick: () => ({}) }), []); @@ -190,12 +166,7 @@ const QueryFlyoutComponent: React.FC = ({ - + diff --git a/x-pack/plugins/osquery/public/packs/queries/schema.tsx b/x-pack/plugins/osquery/public/packs/queries/schema.tsx index d5b169b2c116c..526de9b73b0ea 100644 --- a/x-pack/plugins/osquery/public/packs/queries/schema.tsx +++ b/x-pack/plugins/osquery/public/packs/queries/schema.tsx @@ -71,7 +71,7 @@ export const createFormSchema = (ids: Set) => ({ validations: [], }, ecs_mapping: { - defaultValue: {}, + defaultValue: [], type: FIELD_TYPES.JSON, validations: [], }, diff --git a/x-pack/plugins/osquery/public/packs/queries/use_pack_query_form.tsx b/x-pack/plugins/osquery/public/packs/queries/use_pack_query_form.tsx index b46230a65267e..2b044a443004d 100644 --- a/x-pack/plugins/osquery/public/packs/queries/use_pack_query_form.tsx +++ b/x-pack/plugins/osquery/public/packs/queries/use_pack_query_form.tsx @@ -5,11 +5,12 @@ * 2.0. */ -import { isArray, isEmpty, xor } from 'lodash'; +import { isArray, isEmpty, xor, map } from 'lodash'; import uuid from 'uuid'; import { produce } from 'immer'; import { useMemo } from 'react'; +import { convertECSMappingToObject } from '../../../common/schemas/common/utils'; import { FormConfig, useForm } from '../../shared_imports'; import { createFormSchema } from './schema'; @@ -37,11 +38,14 @@ export interface PackFormData { platform?: string | undefined; version?: string | undefined; ecs_mapping?: - | Record< - string, - { - field: string; - } + | Array< + Record< + string, + { + field?: string; + value?: string; + } + > > | undefined; } @@ -76,7 +80,7 @@ export const usePackQueryForm = ({ id: '', query: '', interval: 3600, - ecs_mapping: {}, + ecs_mapping: [], }, // @ts-expect-error update types serializer: (payload) => @@ -100,6 +104,9 @@ export const usePackQueryForm = ({ if (isEmpty(draft.ecs_mapping)) { delete draft.ecs_mapping; + } else { + // @ts-expect-error update types + draft.ecs_mapping = convertECSMappingToObject(payload.ecs_mapping); } return draft; @@ -114,7 +121,17 @@ export const usePackQueryForm = ({ interval: payload.interval, platform: payload.platform, version: payload.version ? [payload.version] : [], - ecs_mapping: payload.ecs_mapping ?? {}, + ecs_mapping: !isArray(payload.ecs_mapping) + ? map(payload.ecs_mapping, (value, key) => ({ + key, + result: { + // @ts-expect-error update types + type: Object.keys(value)[0], + // @ts-expect-error update types + value: Object.values(value)[0], + }, + })) + : payload.ecs_mapping, }; }, // @ts-expect-error update types diff --git a/x-pack/plugins/osquery/public/plugin.ts b/x-pack/plugins/osquery/public/plugin.ts index e88a866cc545f..e21defbaa8828 100644 --- a/x-pack/plugins/osquery/public/plugin.ts +++ b/x-pack/plugins/osquery/public/plugin.ts @@ -45,6 +45,7 @@ export class OsqueryPlugin implements Plugin React.ReactElement; } const ResultsTableComponent: React.FC = ({ @@ -54,7 +56,7 @@ const ResultsTableComponent: React.FC = ({ agentIds, startDate, endDate, - isExternal, + addToTimeline, }) => { const [isLive, setIsLive] = useState(true); const { data: hasActionResultsPrivileges } = useActionResultsPrivileges(); @@ -107,11 +109,7 @@ const ResultsTableComponent: React.FC = ({ ]); const [columns, setColumns] = useState([]); - const { - data: allResultsData, - isFetched, - isLoading, - } = useAllResults({ + const { data: allResultsData, isLoading } = useAllResults({ actionId, activePage: pagination.pageIndex, limit: pagination.pageSize, @@ -309,10 +307,30 @@ const ResultsTableComponent: React.FC = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [allResultsData?.columns.length, ecsMappingColumns, getHeaderDisplay]); + const leadingControlColumns: EuiDataGridControlColumn[] = useMemo(() => { + const data = allResultsData?.edges; + if (addToTimeline && data) { + return [ + { + id: 'timeline', + width: 38, + headerCellRender: () => null, + rowCellRender: (actionProps: EuiDataGridCellValueElementProps) => { + const eventId = data[actionProps.rowIndex]._id; + + return addToTimeline({ query: ['_id', eventId], isIcon: true }); + }, + }, + ]; + } + + return []; + }, [addToTimeline, allResultsData?.edges]); + const toolbarVisibility = useMemo( () => ({ showDisplaySelector: false, - showFullScreenSelector: !isExternal, + showFullScreenSelector: !addToTimeline, additionalControls: ( <> = ({ endDate={endDate} startDate={startDate} /> + {addToTimeline && addToTimeline({ query: ['action_id', actionId] })} ), }), - [actionId, endDate, startDate, isExternal] + [actionId, addToTimeline, endDate, startDate] ); useEffect( @@ -338,20 +357,44 @@ const ResultsTableComponent: React.FC = ({ setIsLive(() => { if (!agentIds?.length || expired) return false; - return !!(aggregations.totalResponded !== agentIds?.length); + return !!( + aggregations.totalResponded !== agentIds?.length || + allResultsData?.totalCount !== aggregations?.totalRowCount || + (allResultsData?.totalCount && !allResultsData?.edges.length) + ); }), - [agentIds?.length, aggregations.failed, aggregations.totalResponded, expired] + [ + agentIds?.length, + aggregations.totalResponded, + aggregations?.totalRowCount, + allResultsData?.edges.length, + allResultsData?.totalCount, + expired, + ] ); if (!hasActionResultsPrivileges) { return ( - + + } + color="danger" + iconType="alert" + >

- {'Your user role doesn’t have index read permissions on the '} - logs-{OSQUERY_INTEGRATION_NAME}.result* - { - 'index. Access to this index is required to view osquery results. Administrators can update role permissions in Stack Management > Roles.' - } + read, + logs: logs-{OSQUERY_INTEGRATION_NAME}.result*, + }} + />

); @@ -365,13 +408,12 @@ const ResultsTableComponent: React.FC = ({ <> {isLive && } - {isFetched && !allResultsData?.edges.length && !aggregations?.totalRowCount ? ( + {!allResultsData?.edges.length ? ( <> ) : ( - // @ts-expect-error update types = ({ columnVisibility={columnVisibility} rowCount={allResultsData?.totalCount ?? 0} renderCellValue={renderCellValue} + leadingControlColumns={leadingControlColumns} sorting={tableSorting} pagination={tablePagination} height="500px" diff --git a/x-pack/plugins/osquery/public/results/use_all_results.ts b/x-pack/plugins/osquery/public/results/use_all_results.ts index c35ce881586b1..aa63e584bccef 100644 --- a/x-pack/plugins/osquery/public/results/use_all_results.ts +++ b/x-pack/plugins/osquery/public/results/use_all_results.ts @@ -8,7 +8,7 @@ import { useQuery } from 'react-query'; import { i18n } from '@kbn/i18n'; -import { firstValueFrom } from 'rxjs'; +import { lastValueFrom } from 'rxjs'; import { createFilter, generateTablePaginationOptions, @@ -62,7 +62,7 @@ export const useAllResults = ({ return useQuery( ['allActionResults', { actionId, activePage, limit, sort }], async () => { - const responseData = await firstValueFrom( + const responseData = await lastValueFrom( data.search.search( { actionId, diff --git a/x-pack/plugins/osquery/public/routes/saved_queries/edit/form.tsx b/x-pack/plugins/osquery/public/routes/saved_queries/edit/form.tsx index 432a108c8ece3..75969fd8f5dd1 100644 --- a/x-pack/plugins/osquery/public/routes/saved_queries/edit/form.tsx +++ b/x-pack/plugins/osquery/public/routes/saved_queries/edit/form.tsx @@ -13,12 +13,12 @@ import { EuiFlexItem, EuiSpacer, } from '@elastic/eui'; -import React, { useRef } from 'react'; +import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { useRouterNavigate } from '../../../common/lib/kibana'; import { Form } from '../../../shared_imports'; -import { SavedQueryForm, SavedQueryFormRefObject } from '../../../saved_queries/form'; +import { SavedQueryForm } from '../../../saved_queries/form'; import { useSavedQueryForm } from '../../../saved_queries/form/use_saved_query_form'; interface EditSavedQueryFormProps { @@ -32,19 +32,17 @@ const EditSavedQueryFormComponent: React.FC = ({ handleSubmit, viewMode, }) => { - const savedQueryFormRef = useRef(null); const savedQueryListProps = useRouterNavigate('saved_queries'); const { form } = useSavedQueryForm({ defaultValue, - savedQueryFormRef, handleSubmit, }); const { submit, isSubmitting } = form; return (
- + {!viewMode && ( <> diff --git a/x-pack/plugins/osquery/public/routes/saved_queries/edit/tabs.tsx b/x-pack/plugins/osquery/public/routes/saved_queries/edit/tabs.tsx index 0956dc6528a7f..76e8bfd9dd029 100644 --- a/x-pack/plugins/osquery/public/routes/saved_queries/edit/tabs.tsx +++ b/x-pack/plugins/osquery/public/routes/saved_queries/edit/tabs.tsx @@ -17,7 +17,7 @@ interface ResultTabsProps { agentIds?: string[]; startDate?: string; endDate?: string; - isExternal?: true; + addToTimeline?: (payload: { query: [string, string]; isIcon?: true }) => React.ReactElement; } const ResultTabsComponent: React.FC = ({ @@ -25,7 +25,7 @@ const ResultTabsComponent: React.FC = ({ agentIds, endDate, startDate, - isExternal, + addToTimeline, }) => { const tabs = useMemo( () => [ @@ -40,7 +40,7 @@ const ResultTabsComponent: React.FC = ({ agentIds={agentIds} startDate={startDate} endDate={endDate} - isExternal={isExternal} + addToTimeline={addToTimeline} /> ), @@ -60,7 +60,7 @@ const ResultTabsComponent: React.FC = ({ ), }, ], - [actionId, agentIds, endDate, startDate, isExternal] + [actionId, agentIds, endDate, startDate, addToTimeline] ); return ( diff --git a/x-pack/plugins/osquery/public/routes/saved_queries/new/form.tsx b/x-pack/plugins/osquery/public/routes/saved_queries/new/form.tsx index 80899c476f2a3..414cfaabf7f83 100644 --- a/x-pack/plugins/osquery/public/routes/saved_queries/new/form.tsx +++ b/x-pack/plugins/osquery/public/routes/saved_queries/new/form.tsx @@ -13,12 +13,12 @@ import { EuiFlexItem, EuiSpacer, } from '@elastic/eui'; -import React, { useRef } from 'react'; +import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { useRouterNavigate } from '../../../common/lib/kibana'; import { Form } from '../../../shared_imports'; -import { SavedQueryForm, SavedQueryFormRefObject } from '../../../saved_queries/form'; +import { SavedQueryForm } from '../../../saved_queries/form'; import { useSavedQueryForm } from '../../../saved_queries/form/use_saved_query_form'; interface NewSavedQueryFormProps { @@ -30,19 +30,17 @@ const NewSavedQueryFormComponent: React.FC = ({ defaultValue, handleSubmit, }) => { - const savedQueryFormRef = useRef(null); const savedQueryListProps = useRouterNavigate('saved_queries'); const { form } = useSavedQueryForm({ defaultValue, - savedQueryFormRef, handleSubmit, }); const { submit, isSubmitting, isValid } = form; return ( - + diff --git a/x-pack/plugins/osquery/public/saved_queries/form/index.tsx b/x-pack/plugins/osquery/public/saved_queries/form/index.tsx index cc0aa8ee08ca4..44fae4efd3608 100644 --- a/x-pack/plugins/osquery/public/saved_queries/form/index.tsx +++ b/x-pack/plugins/osquery/public/saved_queries/form/index.tsx @@ -13,25 +13,15 @@ import { EuiText, EuiButtonEmpty, } from '@elastic/eui'; -import React, { - useCallback, - useMemo, - useRef, - forwardRef, - useImperativeHandle, - useState, -} from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { ALL_OSQUERY_VERSIONS_OPTIONS } from '../../packs/queries/constants'; import { PlatformCheckBoxGroupField } from '../../packs/queries/platform_checkbox_group_field'; -import { Field, getUseField, UseField, useFormData } from '../../shared_imports'; +import { Field, getUseField, UseField } from '../../shared_imports'; import { CodeEditorField } from './code_editor_field'; -import { - ECSMappingEditorField, - ECSMappingEditorFieldRef, -} from '../../packs/queries/lazy_ecs_mapping_editor_field'; +import { ECSMappingEditorField } from '../../packs/queries/lazy_ecs_mapping_editor_field'; import { PlaygroundFlyout } from './playground_flyout'; export const CommonUseField = getUseField({ component: Field }); @@ -41,131 +31,112 @@ interface SavedQueryFormProps { hasPlayground?: boolean; isValid?: boolean; } -export interface SavedQueryFormRefObject { - validateEcsMapping: ECSMappingEditorFieldRef['validate']; -} -const SavedQueryFormComponent = forwardRef( - ({ viewMode, hasPlayground, isValid }, ref) => { - const [playgroundVisible, setPlaygroundVisible] = useState(false); - const ecsFieldRef = useRef(); - - const euiFieldProps = useMemo( - () => ({ - isDisabled: !!viewMode, - }), - [viewMode] - ); +const SavedQueryFormComponent: React.FC = ({ + viewMode, + hasPlayground, + isValid, +}) => { + const [playgroundVisible, setPlaygroundVisible] = useState(false); - const [{ query }] = useFormData({ watch: ['query'] }); + const euiFieldProps = useMemo( + () => ({ + isDisabled: !!viewMode, + }), + [viewMode] + ); - const handleHidePlayground = useCallback(() => setPlaygroundVisible(false), []); + const handleHidePlayground = useCallback(() => setPlaygroundVisible(false), []); - const handleTogglePlayground = useCallback( - () => setPlaygroundVisible((prevValue) => !prevValue), - [] - ); + const handleTogglePlayground = useCallback( + () => setPlaygroundVisible((prevValue) => !prevValue), + [] + ); - useImperativeHandle( - ref, - () => ({ - validateEcsMapping: () => { - if (ecsFieldRef.current) { - return ecsFieldRef.current.validate(); - } + const intervalEuiFieldProps = useMemo( + () => ({ + append: 's', + ...euiFieldProps, + }), + [euiFieldProps] + ); - return Promise.resolve(false); - }, + const versionEuiFieldProps = useMemo( + () => ({ + noSuggestions: false, + singleSelection: { asPlainText: true }, + placeholder: i18n.translate('xpack.osquery.pack.queriesTable.osqueryVersionAllLabel', { + defaultMessage: 'ALL', }), - [] - ); + options: ALL_OSQUERY_VERSIONS_OPTIONS, + onCreateOption: undefined, + ...euiFieldProps, + }), + [euiFieldProps] + ); - return ( - <> - - - - - - + return ( + <> + + + + + + + + + + + + {!viewMode && hasPlayground && ( - - + + + Test configuration + - {!viewMode && hasPlayground && ( - - - - Test configuration - - - - )} - - - - -
- -
-
- + )} + + + + +
- - - - - - - + + + - - - - - - - - {playgroundVisible && ( - - )} - - ); - } -); + + + + + + + + + + + + + + + {playgroundVisible && ( + + )} + + ); +}; + +SavedQueryFormComponent.displayName = 'SavedQueryForm'; export const SavedQueryForm = React.memo(SavedQueryFormComponent); diff --git a/x-pack/plugins/osquery/public/saved_queries/form/playground_flyout.tsx b/x-pack/plugins/osquery/public/saved_queries/form/playground_flyout.tsx index 60f1dff400867..b5af2652fd110 100644 --- a/x-pack/plugins/osquery/public/saved_queries/form/playground_flyout.tsx +++ b/x-pack/plugins/osquery/public/saved_queries/form/playground_flyout.tsx @@ -6,7 +6,7 @@ */ import { EuiFlyout, EuiFlyoutHeader, EuiTitle, EuiFlyoutBody } from '@elastic/eui'; -import React from 'react'; +import React, { useMemo } from 'react'; import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -26,11 +26,14 @@ interface PlaygroundFlyoutProps { } const PlaygroundFlyoutComponent: React.FC = ({ enabled, onClose }) => { - // eslint-disable-next-line @typescript-eslint/naming-convention - const [{ query, ecs_mapping, id }] = useFormData({ + const [{ query, ecs_mapping: ecsMapping, id }, formDataSerializer] = useFormData({ watch: ['query', 'ecs_mapping', 'savedQueryId'], }); + /* recalculate the form data when ecs_mapping changes */ + // eslint-disable-next-line react-hooks/exhaustive-deps + const serializedFormData = useMemo(() => formDataSerializer(), [ecsMapping, formDataSerializer]); + return ( @@ -48,7 +51,7 @@ const PlaygroundFlyoutComponent: React.FC = ({ enabled, o enabled={enabled && query !== ''} formType="simple" query={query} - ecs_mapping={ecs_mapping} + ecs_mapping={serializedFormData.ecs_mapping} savedQueryId={id} queryField={false} ecsMappingField={false} diff --git a/x-pack/plugins/osquery/public/saved_queries/form/use_saved_query_form.tsx b/x-pack/plugins/osquery/public/saved_queries/form/use_saved_query_form.tsx index bf4123fd86128..1d0d9f28d097b 100644 --- a/x-pack/plugins/osquery/public/saved_queries/form/use_saved_query_form.tsx +++ b/x-pack/plugins/osquery/public/saved_queries/form/use_saved_query_form.tsx @@ -8,27 +8,22 @@ import { isArray, isEmpty, map } from 'lodash'; import uuid from 'uuid'; import { produce } from 'immer'; -import { RefObject, useMemo } from 'react'; +import { useMemo } from 'react'; +import { convertECSMappingToObject } from '../../../common/schemas/common/utils'; import { useForm } from '../../shared_imports'; import { createFormSchema } from '../../packs/queries/schema'; import { PackFormData } from '../../packs/queries/use_pack_query_form'; import { useSavedQueries } from '../use_saved_queries'; -import { SavedQueryFormRefObject } from '.'; const SAVED_QUERY_FORM_ID = 'savedQueryForm'; interface UseSavedQueryFormProps { defaultValue?: unknown; handleSubmit: (payload: unknown) => Promise; - savedQueryFormRef: RefObject; } -export const useSavedQueryForm = ({ - defaultValue, - handleSubmit, - savedQueryFormRef, -}: UseSavedQueryFormProps) => { +export const useSavedQueryForm = ({ defaultValue, handleSubmit }: UseSavedQueryFormProps) => { const { data } = useSavedQueries({}); const ids: string[] = useMemo( () => map(data?.saved_objects, 'attributes.id') ?? [], @@ -50,14 +45,9 @@ export const useSavedQueryForm = ({ id: SAVED_QUERY_FORM_ID + uuid.v4(), schema: formSchema, onSubmit: async (formData, isValid) => { - const ecsFieldValue = await savedQueryFormRef?.current?.validateEcsMapping(); - - if (isValid && !!ecsFieldValue) { + if (isValid) { try { - await handleSubmit({ - ...formData, - ecs_mapping: ecsFieldValue, - }); + await handleSubmit(formData); // eslint-disable-next-line no-empty } catch (e) {} } @@ -66,13 +56,6 @@ export const useSavedQueryForm = ({ defaultValue, serializer: (payload) => produce(payload, (draft) => { - // @ts-expect-error update types - if (draft.platform?.split(',').length === 3) { - // if all platforms are checked then use undefined - // @ts-expect-error update types - delete draft.platform; - } - if (isArray(draft.version)) { if (!draft.version.length) { // @ts-expect-error update types @@ -82,9 +65,12 @@ export const useSavedQueryForm = ({ } } - if (isEmpty(draft.ecs_mapping)) { + if (isEmpty(payload.ecs_mapping)) { // @ts-expect-error update types delete draft.ecs_mapping; + } else { + // @ts-expect-error update types + draft.ecs_mapping = convertECSMappingToObject(payload.ecs_mapping); } // @ts-expect-error update types @@ -103,7 +89,16 @@ export const useSavedQueryForm = ({ interval: payload.interval ?? 3600, platform: payload.platform, version: payload.version ? [payload.version] : [], - ecs_mapping: payload.ecs_mapping ?? {}, + ecs_mapping: + (!isEmpty(payload.ecs_mapping) && + map(payload.ecs_mapping, (value, key) => ({ + key, + result: { + type: Object.keys(value)[0], + value: Object.values(value)[0], + }, + }))) ?? + [], }; }, }); diff --git a/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx b/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx index 4d352b7fd2516..eec949fbe312b 100644 --- a/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx +++ b/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx @@ -12,7 +12,6 @@ import { SimpleSavedObject } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import styled from 'styled-components'; -import deepEqual from 'fast-deep-equal'; import { useSavedQueries } from './use_saved_queries'; import { useFormData } from '../shared_imports'; @@ -47,10 +46,7 @@ const SavedQueriesDropdownComponent: React.FC = ({ }) => { const [selectedOptions, setSelectedOptions] = useState([]); - // eslint-disable-next-line @typescript-eslint/naming-convention - const [{ query, ecs_mapping, savedQueryId }] = useFormData({ - watch: ['ecs_mapping', 'query', 'savedQueryId'], - }); + const [{ savedQueryId }] = useFormData(); const { data } = useSavedQueries({}); @@ -122,15 +118,11 @@ const SavedQueriesDropdownComponent: React.FC = ({ if ( selectedOptions.length && // @ts-expect-error update types - (selectedOptions[0].value.savedQueryId !== savedQueryId || - // @ts-expect-error update types - selectedOptions[0].value.query !== query || - // @ts-expect-error update types - !deepEqual(selectedOptions[0].value.ecs_mapping, ecs_mapping)) + selectedOptions[0].value.savedQueryId !== savedQueryId ) { setSelectedOptions([]); } - }, [ecs_mapping, query, savedQueryId, selectedOptions]); + }, [savedQueryId, selectedOptions]); return ( void; - isExternal?: true; + isExternal?: boolean; } const additionalZIndexStyle = { style: 'z-index: 6000' }; @@ -38,7 +38,6 @@ const SavedQueryFlyoutComponent: React.FC = ({ onClose, isExternal, }) => { - const savedQueryFormRef = useRef(null); const createSavedQueryMutation = useCreateSavedQuery({ withRedirect: false }); const handleSubmit = useCallback( @@ -48,7 +47,6 @@ const SavedQueryFlyoutComponent: React.FC = ({ const { form } = useSavedQueryForm({ defaultValue, - savedQueryFormRef, handleSubmit, }); const { submit, isSubmitting } = form; @@ -60,7 +58,7 @@ const SavedQueryFlyoutComponent: React.FC = ({ ownFocus onClose={onClose} aria-labelledby="flyoutTitle" - maskProps={isExternal && additionalZIndexStyle} // For an edge case to display above the alerts flyout + maskProps={isExternal ? additionalZIndexStyle : undefined} // For an edge case to display above the alerts flyout > @@ -74,7 +72,7 @@ const SavedQueryFlyoutComponent: React.FC = ({ - + diff --git a/x-pack/plugins/osquery/public/shared_components/osquery_action/index.tsx b/x-pack/plugins/osquery/public/shared_components/osquery_action/index.tsx index 5fbc6caedcd15..01285ed69c5d0 100644 --- a/x-pack/plugins/osquery/public/shared_components/osquery_action/index.tsx +++ b/x-pack/plugins/osquery/public/shared_components/osquery_action/index.tsx @@ -8,6 +8,7 @@ import { EuiErrorBoundary, EuiLoadingContent, EuiEmptyPrompt, EuiCode } from '@elastic/eui'; import React, { useMemo } from 'react'; import { QueryClientProvider } from 'react-query'; +import { CoreStart } from '@kbn/core/public'; import { AGENT_STATUS_ERROR, EMPTY_PROMPT, @@ -22,14 +23,21 @@ import { queryClient } from '../../query_client'; import { OsqueryIcon } from '../../components/osquery_icon'; import { KibanaThemeProvider } from '../../shared_imports'; import { useIsOsqueryAvailable } from './use_is_osquery_available'; +import { StartPlugins } from '../../types'; interface OsqueryActionProps { agentId?: string; formType: 'steps' | 'simple'; - isExternal?: true; + hideAgentsField?: boolean; + addToTimeline?: (payload: { query: [string, string]; isIcon?: true }) => React.ReactElement; } -const OsqueryActionComponent: React.FC = ({ agentId, formType = 'simple' }) => { +const OsqueryActionComponent: React.FC = ({ + agentId, + formType = 'simple', + hideAgentsField, + addToTimeline, +}) => { const permissions = useKibana().services.application.capabilities.osquery; const emptyPrompt = useMemo( @@ -99,18 +107,37 @@ const OsqueryActionComponent: React.FC = ({ agentId, formTyp ); } - return ; + return ( + + ); }; export const OsqueryAction = React.memo(OsqueryActionComponent); -// @ts-expect-error update types -const OsqueryActionWrapperComponent = ({ services, agentId, formType, isExternal }) => ( +type OsqueryActionWrapperProps = { services: CoreStart & StartPlugins } & OsqueryActionProps; + +const OsqueryActionWrapperComponent: React.FC = ({ + services, + agentId, + formType, + hideAgentsField = false, + addToTimeline, +}) => ( - + diff --git a/x-pack/plugins/osquery/public/shared_components/osquery_action/osquery_action.test.tsx b/x-pack/plugins/osquery/public/shared_components/osquery_action/osquery_action.test.tsx index f6128642ba645..4c9214ca3ea14 100644 --- a/x-pack/plugins/osquery/public/shared_components/osquery_action/osquery_action.test.tsx +++ b/x-pack/plugins/osquery/public/shared_components/osquery_action/osquery_action.test.tsx @@ -78,35 +78,27 @@ describe('Osquery Action', () => { spyOsquery(); mockKibana(); - const { getByText } = renderWithContext( - - ); + const { getByText } = renderWithContext(); expect(getByText(EMPTY_PROMPT)).toBeInTheDocument(); }); it('should return empty prompt when no agentId', async () => { spyOsquery(); mockKibana(); - const { getByText } = renderWithContext( - - ); + const { getByText } = renderWithContext(); expect(getByText(EMPTY_PROMPT)).toBeInTheDocument(); }); it('should return permission denied when agentFetched and agentData available', async () => { spyOsquery({ agentData: {} }); mockKibana(); - const { getByText } = renderWithContext( - - ); + const { getByText } = renderWithContext(); expect(getByText(PERMISSION_DENIED)).toBeInTheDocument(); }); it('should return agent status error when permissions are ok and agent status is wrong', async () => { spyOsquery({ agentData: {} }); mockKibana(properPermissions); - const { getByText } = renderWithContext( - - ); + const { getByText } = renderWithContext(); expect(getByText(AGENT_STATUS_ERROR)).toBeInTheDocument(); }); it('should return permission denied if just one permission (runSavedQueries) is available', async () => { @@ -116,9 +108,7 @@ describe('Osquery Action', () => { runSavedQueries: true, }, }); - const { getByText } = renderWithContext( - - ); + const { getByText } = renderWithContext(); expect(getByText(PERMISSION_DENIED)).toBeInTheDocument(); }); it('should return permission denied if just one permission (readSavedQueries) is available', async () => { @@ -128,9 +118,7 @@ describe('Osquery Action', () => { readSavedQueries: true, }, }); - const { getByText } = renderWithContext( - - ); + const { getByText } = renderWithContext(); expect(getByText(PERMISSION_DENIED)).toBeInTheDocument(); }); it('should return permission denied if no writeLiveQueries', async () => { @@ -140,9 +128,7 @@ describe('Osquery Action', () => { writeLiveQueries: true, }, }); - const { getByText } = renderWithContext( - - ); + const { getByText } = renderWithContext(); expect(getByText(AGENT_STATUS_ERROR)).toBeInTheDocument(); }); it('should return not available prompt if osquery is not available', async () => { @@ -152,9 +138,7 @@ describe('Osquery Action', () => { writeLiveQueries: true, }, }); - const { getByText } = renderWithContext( - - ); + const { getByText } = renderWithContext(); expect(getByText(NOT_AVAILABLE)).toBeInTheDocument(); }); it('should not return any errors when all data is ok', async () => { @@ -162,7 +146,7 @@ describe('Osquery Action', () => { mockKibana(properPermissions); const { queryByText } = renderWithContext( - + ); expect(queryByText(EMPTY_PROMPT)).not.toBeInTheDocument(); expect(queryByText(PERMISSION_DENIED)).not.toBeInTheDocument(); diff --git a/x-pack/plugins/osquery/public/shared_imports.ts b/x-pack/plugins/osquery/public/shared_imports.ts index 4e08bcd46d9ee..843cc512826c6 100644 --- a/x-pack/plugins/osquery/public/shared_imports.ts +++ b/x-pack/plugins/osquery/public/shared_imports.ts @@ -23,6 +23,8 @@ export { Form, FormDataProvider, UseArray, + ArrayItem, + FormArrayField, UseField, UseMultiFields, useForm, diff --git a/x-pack/plugins/osquery/tsconfig.json b/x-pack/plugins/osquery/tsconfig.json index 5c70e5ffbb944..4eac1baa43d79 100644 --- a/x-pack/plugins/osquery/tsconfig.json +++ b/x-pack/plugins/osquery/tsconfig.json @@ -24,7 +24,6 @@ // requiredPlugins from ./kibana.json { "path": "../../../src/plugins/data/tsconfig.json" }, { "path": "../../../src/plugins/navigation/tsconfig.json" }, - { "path": "../data_enhanced/tsconfig.json" }, { "path": "../fleet/tsconfig.json" }, // optionalPlugins from ./kibana.json diff --git a/x-pack/plugins/reporting/common/errors/errors.test.ts b/x-pack/plugins/reporting/common/errors/errors.test.ts index 37210373f4d68..d687d8b4adce6 100644 --- a/x-pack/plugins/reporting/common/errors/errors.test.ts +++ b/x-pack/plugins/reporting/common/errors/errors.test.ts @@ -7,7 +7,9 @@ import * as errors from '.'; -describe('ReportingError', () => { +const { ReportingError: _, ...nonAbstractErrors } = errors; + +describe('Reporting error', () => { it('provides error code when stringified', () => { expect(new errors.AuthenticationExpiredError() + '').toBe( `ReportingError(code: authentication_expired_error)` @@ -18,10 +20,14 @@ describe('ReportingError', () => { `ReportingError(code: authentication_expired_error) "some details"` ); }); - it('has the expected code structure', () => { - const { ReportingError: _, ...nonAbstractErrors } = errors; + it('has the expected error code structure', () => { + Object.values(nonAbstractErrors).forEach((Ctor) => { + expect(Ctor.code).toMatch(/^[a-z_]+_error$/); + }); + }); + it('has the same error code values on static "code" and instance "code" properties', () => { Object.values(nonAbstractErrors).forEach((Ctor) => { - expect(new Ctor().code).toMatch(/^[a-z_]+_error$/); + expect(Ctor.code).toBe(new Ctor().code); }); }); }); diff --git a/x-pack/plugins/reporting/common/errors/index.ts b/x-pack/plugins/reporting/common/errors/index.ts index d5032c206a186..d2c5f0181df86 100644 --- a/x-pack/plugins/reporting/common/errors/index.ts +++ b/x-pack/plugins/reporting/common/errors/index.ts @@ -15,7 +15,7 @@ export abstract class ReportingError extends Error { * * @note Convention for codes: lower-case, snake-case and end in `_error`. */ - public abstract code: string; + public abstract get code(): string; constructor(public details?: string) { super(); @@ -38,22 +38,34 @@ export abstract class ReportingError extends Error { * access token expired. */ export class AuthenticationExpiredError extends ReportingError { - code = 'authentication_expired_error'; + static code = 'authentication_expired_error' as const; + public get code(): string { + return AuthenticationExpiredError.code; + } } export class QueueTimeoutError extends ReportingError { - code = 'queue_timeout_error'; + static code = 'queue_timeout_error' as const; + public get code(): string { + return QueueTimeoutError.code; + } } /** * An unknown error has occurred. See details. */ export class UnknownError extends ReportingError { - code = 'unknown_error'; + static code = 'unknown_error' as const; + public get code(): string { + return UnknownError.code; + } } export class PdfWorkerOutOfMemoryError extends ReportingError { - code = 'pdf_worker_out_of_memory_error'; + static code = 'pdf_worker_out_of_memory_error' as const; + public get code(): string { + return PdfWorkerOutOfMemoryError.code; + } details = i18n.translate('xpack.reporting.common.pdfWorkerOutOfMemoryErrorMessage', { defaultMessage: @@ -70,7 +82,10 @@ export class PdfWorkerOutOfMemoryError extends ReportingError { } export class BrowserCouldNotLaunchError extends ReportingError { - code = 'browser_could_not_launch_error'; + static code = 'browser_could_not_launch_error' as const; + public get code(): string { + return BrowserCouldNotLaunchError.code; + } details = i18n.translate('xpack.reporting.common.browserCouldNotLaunchErrorMessage', { defaultMessage: 'Cannot generate screenshots because the browser did not launch.', @@ -86,13 +101,42 @@ export class BrowserCouldNotLaunchError extends ReportingError { } export class BrowserUnexpectedlyClosedError extends ReportingError { - code = 'browser_unexpectedly_closed_error'; + static code = 'browser_unexpectedly_closed_error' as const; + public get code(): string { + return BrowserUnexpectedlyClosedError.code; + } } export class BrowserScreenshotError extends ReportingError { - code = 'browser_screenshot_error'; + static code = 'browser_screenshot_error' as const; + public get code(): string { + return BrowserScreenshotError.code; + } } export class KibanaShuttingDownError extends ReportingError { - code = 'kibana_shutting_down_error'; + static code = 'kibana_shutting_down_error' as const; + public get code(): string { + return KibanaShuttingDownError.code; + } +} + +/** + * Special error case that should only occur on Cloud when trying to generate + * a report on a Kibana instance that is too small to be running Chromium. + */ +export class VisualReportingSoftDisabledError extends ReportingError { + static code = 'visual_reporting_soft_disabled_error' as const; + public get code(): string { + return VisualReportingSoftDisabledError.code; + } + + details = i18n.translate('xpack.reporting.common.cloud.insufficientSystemMemoryError', { + defaultMessage: + 'This report cannot be generated because Kibana does not have sufficient memory.', + }); + + public override get message() { + return this.details; + } } diff --git a/x-pack/plugins/reporting/common/errors/map_to_reporting_error.ts b/x-pack/plugins/reporting/common/errors/map_to_reporting_error.ts index eafd4ed683627..1244737deee2e 100644 --- a/x-pack/plugins/reporting/common/errors/map_to_reporting_error.ts +++ b/x-pack/plugins/reporting/common/errors/map_to_reporting_error.ts @@ -13,6 +13,7 @@ import { BrowserUnexpectedlyClosedError, BrowserScreenshotError, PdfWorkerOutOfMemoryError, + VisualReportingSoftDisabledError, } from '.'; export function mapToReportingError(error: unknown): ReportingError { @@ -28,6 +29,8 @@ export function mapToReportingError(error: unknown): ReportingError { return new BrowserCouldNotLaunchError(); case error instanceof errors.PdfWorkerOutOfMemoryError: return new PdfWorkerOutOfMemoryError(); + case error instanceof errors.InsufficientMemoryAvailableOnCloudError: + return new VisualReportingSoftDisabledError(); } return new UnknownError(); } diff --git a/x-pack/plugins/reporting/common/types/index.ts b/x-pack/plugins/reporting/common/types/index.ts index 8b95fdb54b1e0..c8743fcf467a1 100644 --- a/x-pack/plugins/reporting/common/types/index.ts +++ b/x-pack/plugins/reporting/common/types/index.ts @@ -156,6 +156,7 @@ export interface JobSummary { status: JobStatus; jobtype: ReportSource['jobtype']; title: ReportSource['payload']['title']; + errorCode?: ReportOutput['error_code']; maxSizeReached: TaskRunResult['max_size_reached']; csvContainsFormulas: TaskRunResult['csv_contains_formulas']; } diff --git a/x-pack/plugins/reporting/public/lib/__snapshots__/stream_handler.test.ts.snap b/x-pack/plugins/reporting/public/lib/__snapshots__/stream_handler.test.ts.snap index 50c8672733168..935f3e297b2cb 100644 --- a/x-pack/plugins/reporting/public/lib/__snapshots__/stream_handler.test.ts.snap +++ b/x-pack/plugins/reporting/public/lib/__snapshots__/stream_handler.test.ts.snap @@ -5,6 +5,7 @@ Object { "completed": Array [ Object { "csvContainsFormulas": false, + "errorCode": undefined, "id": "job-source-mock1", "jobtype": undefined, "maxSizeReached": false, @@ -13,6 +14,7 @@ Object { }, Object { "csvContainsFormulas": true, + "errorCode": undefined, "id": "job-source-mock4", "jobtype": undefined, "maxSizeReached": false, @@ -23,6 +25,7 @@ Object { "failed": Array [ Object { "csvContainsFormulas": false, + "errorCode": undefined, "id": "job-source-mock2", "jobtype": undefined, "maxSizeReached": false, @@ -94,9 +97,7 @@ Array [ this is the failed report error @@ -187,42 +188,40 @@ Array [ `; exports[`stream handler showNotifications show success 1`] = ` -Array [ - Object { - "color": "success", - "data-test-subj": "completeReportSuccess", - "text": MountPoint { - "reactNode": -

- -

- +

+ - , - }, - "title": MountPoint { - "reactNode": + , - }, + /> + , }, -] + "title": MountPoint { + "reactNode": , + }, +} `; diff --git a/x-pack/plugins/reporting/public/lib/job.tsx b/x-pack/plugins/reporting/public/lib/job.tsx index e875d00cabab8..59a0446590dd8 100644 --- a/x-pack/plugins/reporting/public/lib/job.tsx +++ b/x-pack/plugins/reporting/public/lib/job.tsx @@ -61,6 +61,8 @@ export class Job { public locatorParams?: BaseParamsV2['locatorParams']; + public error_code?: ReportOutput['error_code']; + constructor(report: ReportApiJSON) { this.id = report.id; this.index = report.index; @@ -90,6 +92,7 @@ export class Job { this.csv_contains_formulas = report.output?.csv_contains_formulas; this.max_size_reached = report.output?.max_size_reached; this.warnings = report.output?.warnings; + this.error_code = report.output?.error_code; this.locatorParams = (report.payload as BaseParamsV2).locatorParams; this.metrics = report.metrics; } diff --git a/x-pack/plugins/reporting/public/lib/stream_handler.test.ts b/x-pack/plugins/reporting/public/lib/stream_handler.test.ts index 895266bdd568b..d3075d4e5a906 100644 --- a/x-pack/plugins/reporting/public/lib/stream_handler.test.ts +++ b/x-pack/plugins/reporting/public/lib/stream_handler.test.ts @@ -5,9 +5,10 @@ * 2.0. */ +import { omit } from 'lodash'; import sinon, { stub } from 'sinon'; import { NotificationsStart } from '@kbn/core/public'; -import { coreMock, themeServiceMock } from '@kbn/core/public/mocks'; +import { coreMock, themeServiceMock, docLinksServiceMock } from '@kbn/core/public/mocks'; import { JobSummary, ReportApiJSON } from '../../common/types'; import { Job } from './job'; import { ReportingAPIClient } from './reporting_api_client'; @@ -48,6 +49,7 @@ const notificationsMock = { } as unknown as NotificationsStart; const theme = themeServiceMock.createStartContract(); +const docLink = docLinksServiceMock.createStartContract(); describe('stream handler', () => { afterEach(() => { @@ -55,13 +57,23 @@ describe('stream handler', () => { }); it('constructs', () => { - const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock, theme); + const sh = new ReportingNotifierStreamHandler( + notificationsMock, + jobQueueClientMock, + theme, + docLink + ); expect(sh).not.toBe(null); }); describe('findChangedStatusJobs', () => { it('finds no changed status jobs from empty', (done) => { - const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock, theme); + const sh = new ReportingNotifierStreamHandler( + notificationsMock, + jobQueueClientMock, + theme, + docLink + ); const findJobs = sh.findChangedStatusJobs([]); findJobs.subscribe((data) => { expect(data).toEqual({ completed: [], failed: [] }); @@ -70,7 +82,12 @@ describe('stream handler', () => { }); it('finds changed status jobs', (done) => { - const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock, theme); + const sh = new ReportingNotifierStreamHandler( + notificationsMock, + jobQueueClientMock, + theme, + docLink + ); const findJobs = sh.findChangedStatusJobs([ 'job-source-mock1', 'job-source-mock2', @@ -87,7 +104,12 @@ describe('stream handler', () => { describe('showNotifications', () => { it('show success', (done) => { - const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock, theme); + const sh = new ReportingNotifierStreamHandler( + notificationsMock, + jobQueueClientMock, + theme, + docLink + ); sh.showNotifications({ completed: [ { @@ -102,13 +124,18 @@ describe('stream handler', () => { expect(mockShowDanger.callCount).toBe(0); expect(mockShowSuccess.callCount).toBe(1); expect(mockShowWarning.callCount).toBe(0); - expect(mockShowSuccess.args[0]).toMatchSnapshot(); + expect(omit(mockShowSuccess.args[0][0], 'toastLifeTimeMs')).toMatchSnapshot(); done(); }); }); it('show max length warning', (done) => { - const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock, theme); + const sh = new ReportingNotifierStreamHandler( + notificationsMock, + jobQueueClientMock, + theme, + docLink + ); sh.showNotifications({ completed: [ { @@ -130,7 +157,12 @@ describe('stream handler', () => { }); it('show csv formulas warning', (done) => { - const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock, theme); + const sh = new ReportingNotifierStreamHandler( + notificationsMock, + jobQueueClientMock, + theme, + docLink + ); sh.showNotifications({ completed: [ { @@ -152,7 +184,12 @@ describe('stream handler', () => { }); it('show failed job toast', (done) => { - const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock, theme); + const sh = new ReportingNotifierStreamHandler( + notificationsMock, + jobQueueClientMock, + theme, + docLink + ); sh.showNotifications({ completed: [], failed: [ @@ -173,7 +210,12 @@ describe('stream handler', () => { }); it('show multiple toast', (done) => { - const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock, theme); + const sh = new ReportingNotifierStreamHandler( + notificationsMock, + jobQueueClientMock, + theme, + docLink + ); sh.showNotifications({ completed: [ { diff --git a/x-pack/plugins/reporting/public/lib/stream_handler.ts b/x-pack/plugins/reporting/public/lib/stream_handler.ts index 2ff8b8bd4ebda..ba2c32de49f64 100644 --- a/x-pack/plugins/reporting/public/lib/stream_handler.ts +++ b/x-pack/plugins/reporting/public/lib/stream_handler.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import * as Rx from 'rxjs'; import { catchError, map } from 'rxjs/operators'; -import { NotificationsSetup, ThemeServiceStart } from '@kbn/core/public'; +import { NotificationsSetup, ThemeServiceStart, DocLinksStart } from '@kbn/core/public'; import { JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY, JOB_STATUSES } from '../../common/constants'; import { JobId, JobSummary, JobSummarySet } from '../../common/types'; import { @@ -34,6 +34,7 @@ function getReportStatus(src: Job): JobSummary { jobtype: src.prettyJobTypeName ?? src.jobtype, maxSizeReached: src.max_size_reached, csvContainsFormulas: src.csv_contains_formulas, + errorCode: src.error_code, }; } @@ -41,7 +42,8 @@ export class ReportingNotifierStreamHandler { constructor( private notifications: NotificationsSetup, private apiClient: ReportingAPIClient, - private theme: ThemeServiceStart + private theme: ThemeServiceStart, + private docLinks: DocLinksStart ) {} /* @@ -97,7 +99,13 @@ export class ReportingNotifierStreamHandler { for (const job of failedJobs) { const errorText = await this.apiClient.getError(job.id); this.notifications.toasts.addDanger( - getFailureToast(errorText, job, this.apiClient.getManagementLink, this.theme) + getFailureToast( + errorText, + job, + this.apiClient.getManagementLink, + this.theme, + this.docLinks + ) ); } return { completed: completedJobs, failed: failedJobs }; diff --git a/x-pack/plugins/reporting/public/management/components/report_info_flyout_content.tsx b/x-pack/plugins/reporting/public/management/components/report_info_flyout_content.tsx index 92b38d99cedd1..780ea53d885a9 100644 --- a/x-pack/plugins/reporting/public/management/components/report_info_flyout_content.tsx +++ b/x-pack/plugins/reporting/public/management/components/report_info_flyout_content.tsx @@ -17,10 +17,14 @@ import { import moment from 'moment'; import { USES_HEADLESS_JOB_TYPES } from '../../../common/constants'; +import { VisualReportingSoftDisabledError } from '../../../common/errors'; import type { Job } from '../../lib/job'; import { useKibana } from '../../shared_imports'; +import { sharedI18nTexts } from '../../shared_i18n_texts'; + +// TODO: Move all of these i18n texts to ./i18n_texts.tsx const NA = i18n.translate('xpack.reporting.listing.infoPanel.notApplicableLabel', { defaultMessage: 'N/A', }); @@ -40,7 +44,7 @@ const createDateFormatter = (format: string, tz: string) => (date: string) => { export const ReportInfoFlyoutContent: FunctionComponent = ({ info }) => { const { - services: { uiSettings }, + services: { uiSettings, docLinks }, } = useKibana(); const timezone = @@ -186,19 +190,31 @@ export const ReportInfoFlyoutContent: FunctionComponent = ({ info }) => { ]; const warnings = info.getWarnings(); - const errored = info.getError(); + const errored = + /* + * We link the user to documentation if they hit this error case. Note: this + * should only occur on cloud. + */ + info.error_code === VisualReportingSoftDisabledError.code + ? sharedI18nTexts.cloud.insufficientMemoryError( + docLinks.links.reporting.cloudMinimumRequirements + ) + : info.getError(); return ( <> {Boolean(errored) && ( - - {errored} - + <> + + {errored} + + + )} {Boolean(warnings) && ( <> diff --git a/x-pack/plugins/reporting/public/management/mount_management_section.tsx b/x-pack/plugins/reporting/public/management/mount_management_section.tsx index 9e709f317b7f0..a3f238052f66d 100644 --- a/x-pack/plugins/reporting/public/management/mount_management_section.tsx +++ b/x-pack/plugins/reporting/public/management/mount_management_section.tsx @@ -34,6 +34,7 @@ export async function mountManagementSection( http: coreSetup.http, application: coreStart.application, uiSettings: coreStart.uiSettings, + docLinks: coreStart.docLinks, }} > diff --git a/x-pack/plugins/reporting/public/notifier/job_failure.tsx b/x-pack/plugins/reporting/public/notifier/job_failure.tsx index e653c974861ab..e7a6191c30834 100644 --- a/x-pack/plugins/reporting/public/notifier/job_failure.tsx +++ b/x-pack/plugins/reporting/public/notifier/job_failure.tsx @@ -6,18 +6,20 @@ */ import { EuiCallOut, EuiSpacer } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; -import { ThemeServiceStart, ToastInput } from '@kbn/core/public'; +import { DocLinksStart, ThemeServiceStart, ToastInput } from '@kbn/core/public'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import type { JobSummary, ManagementLinkFn } from '../../common/types'; +import * as errors from '../../common/errors'; +import { sharedI18nTexts } from '../shared_i18n_texts'; export const getFailureToast = ( errorText: string, job: JobSummary, getManagmenetLink: ManagementLinkFn, - theme: ThemeServiceStart + theme: ThemeServiceStart, + docLinks: DocLinksStart ): ToastInput => { return { title: toMountPoint( @@ -30,16 +32,12 @@ export const getFailureToast = ( ), text: toMountPoint( <> - - {errorText} + + {job.errorCode === errors.VisualReportingSoftDisabledError.code + ? sharedI18nTexts.cloud.insufficientMemoryError( + docLinks.links.reporting.cloudMinimumRequirements + ) + : errorText} diff --git a/x-pack/plugins/reporting/public/notifier/job_success.tsx b/x-pack/plugins/reporting/public/notifier/job_success.tsx index 44389e164472a..f7b71d78de8bd 100644 --- a/x-pack/plugins/reporting/public/notifier/job_success.tsx +++ b/x-pack/plugins/reporting/public/notifier/job_success.tsx @@ -37,5 +37,12 @@ export const getSuccessToast = ( , { theme$: theme.theme$ } ), + /** + * If timeout is an Infinity value, a Not-a-Number (NaN) value, or negative, then timeout will be zero. + * And we cannot use `Number.MAX_SAFE_INTEGER` because EUI's Timer implementation + * subtracts it from the current time to evaluate the remainder. + * @see https://www.w3.org/TR/2011/WD-html5-20110525/timers.html + */ + toastLifeTimeMs: Number.MAX_SAFE_INTEGER - Date.now(), 'data-test-subj': 'completeReportSuccess', }); diff --git a/x-pack/plugins/reporting/public/plugin.ts b/x-pack/plugins/reporting/public/plugin.ts index 252745c0cec1f..df47c6f28a6e3 100644 --- a/x-pack/plugins/reporting/public/plugin.ts +++ b/x-pack/plugins/reporting/public/plugin.ts @@ -254,9 +254,9 @@ export class ReportingPublicPlugin } public start(core: CoreStart) { - const { notifications } = core; + const { notifications, docLinks } = core; const apiClient = this.getApiClient(core.http, core.uiSettings); - const streamHandler = new StreamHandler(notifications, apiClient, core.theme); + const streamHandler = new StreamHandler(notifications, apiClient, core.theme, docLinks); const interval = durationToNumber(this.config.poll.jobsRefresh.interval); Rx.timer(0, interval) .pipe( diff --git a/x-pack/plugins/reporting/public/shared_i18n_texts.tsx b/x-pack/plugins/reporting/public/shared_i18n_texts.tsx new file mode 100644 index 0000000000000..344fe5360fc35 --- /dev/null +++ b/x-pack/plugins/reporting/public/shared_i18n_texts.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiLink } from '@elastic/eui'; + +export const sharedI18nTexts = { + cloud: { + insufficientMemoryError: (helpUrl: string) => ( + + {i18n.translate( + 'xpack.reporting.listing.infoPanel.callout.cloud.insufficientMemoryError.urlLink', + { defaultMessage: 'RAM requirements' } + )} + + ), + }} + /> + ), + }, +}; diff --git a/x-pack/plugins/reporting/public/types.ts b/x-pack/plugins/reporting/public/types.ts index 8a745a237ee57..3be354bada72e 100644 --- a/x-pack/plugins/reporting/public/types.ts +++ b/x-pack/plugins/reporting/public/types.ts @@ -5,10 +5,11 @@ * 2.0. */ -import type { HttpSetup, ApplicationStart, CoreSetup } from '@kbn/core/public'; +import type { CoreSetup, CoreStart } from '@kbn/core/public'; export interface KibanaContext { - http: HttpSetup; - application: ApplicationStart; - uiSettings: CoreSetup['uiSettings']; + http: CoreSetup['http']; + application: CoreStart['application']; + uiSettings: CoreStart['uiSettings']; + docLinks: CoreStart['docLinks']; } diff --git a/x-pack/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap b/x-pack/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap index 0bfc8cb0df966..937fb0217f4bf 100644 --- a/x-pack/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap +++ b/x-pack/plugins/reporting/server/usage/__snapshots__/reporting_usage_collector.test.ts.snap @@ -48,6 +48,9 @@ Object { "unknown_error": Object { "type": "long", }, + "visual_reporting_soft_disabled_error": Object { + "type": "long", + }, }, "metrics": Object { "png_cpu": Object { @@ -149,6 +152,9 @@ Object { "unknown_error": Object { "type": "long", }, + "visual_reporting_soft_disabled_error": Object { + "type": "long", + }, }, "metrics": Object { "png_cpu": Object { @@ -416,6 +422,9 @@ Object { "unknown_error": Object { "type": "long", }, + "visual_reporting_soft_disabled_error": Object { + "type": "long", + }, }, "metrics": Object { "png_cpu": Object { @@ -517,6 +526,9 @@ Object { "unknown_error": Object { "type": "long", }, + "visual_reporting_soft_disabled_error": Object { + "type": "long", + }, }, "metrics": Object { "png_cpu": Object { @@ -803,6 +815,9 @@ Object { "unknown_error": Object { "type": "long", }, + "visual_reporting_soft_disabled_error": Object { + "type": "long", + }, }, "layout": Object { "canvas": Object { @@ -932,6 +947,9 @@ Object { "unknown_error": Object { "type": "long", }, + "visual_reporting_soft_disabled_error": Object { + "type": "long", + }, }, "layout": Object { "canvas": Object { @@ -1534,6 +1552,9 @@ Object { "unknown_error": Object { "type": "long", }, + "visual_reporting_soft_disabled_error": Object { + "type": "long", + }, }, "layout": Object { "canvas": Object { @@ -1663,6 +1684,9 @@ Object { "unknown_error": Object { "type": "long", }, + "visual_reporting_soft_disabled_error": Object { + "type": "long", + }, }, "layout": Object { "canvas": Object { diff --git a/x-pack/plugins/reporting/server/usage/get_export_stats.test.ts b/x-pack/plugins/reporting/server/usage/get_export_stats.test.ts index 069d5eb42d7f8..f99c81ea39e29 100644 --- a/x-pack/plugins/reporting/server/usage/get_export_stats.test.ts +++ b/x-pack/plugins/reporting/server/usage/get_export_stats.test.ts @@ -331,6 +331,7 @@ test('Incorporate error code stats', () => { browser_could_not_launch_error: 2, browser_unexpectedly_closed_error: 8, browser_screenshot_error: 27, + visual_reporting_soft_disabled_error: 1, }, }, printable_pdf_v2: { @@ -349,6 +350,7 @@ test('Incorporate error code stats', () => { browser_could_not_launch_error: 2, browser_unexpectedly_closed_error: 8, browser_screenshot_error: 27, + visual_reporting_soft_disabled_error: 1, }, }, csv_searchsource_immediate: { @@ -378,6 +380,7 @@ test('Incorporate error code stats', () => { "kibana_shutting_down_error": 1, "queue_timeout_error": 1, "unknown_error": 0, + "visual_reporting_soft_disabled_error": 1, } `); expect(result.printable_pdf_v2.error_codes).toMatchInlineSnapshot(` @@ -390,6 +393,7 @@ test('Incorporate error code stats', () => { "pdf_worker_out_of_memory_error": 99, "queue_timeout_error": 1, "unknown_error": 0, + "visual_reporting_soft_disabled_error": 1, } `); diff --git a/x-pack/plugins/reporting/server/usage/schema.test.ts b/x-pack/plugins/reporting/server/usage/schema.test.ts index d188d6eb373dd..f877b6251378e 100644 --- a/x-pack/plugins/reporting/server/usage/schema.test.ts +++ b/x-pack/plugins/reporting/server/usage/schema.test.ts @@ -39,6 +39,7 @@ describe('Reporting telemetry schema', () => { "PNG.error_codes.kibana_shutting_down_error.type": "long", "PNG.error_codes.queue_timeout_error.type": "long", "PNG.error_codes.unknown_error.type": "long", + "PNG.error_codes.visual_reporting_soft_disabled_error.type": "long", "PNG.metrics.png_cpu.50.0.type": "long", "PNG.metrics.png_cpu.75.0.type": "long", "PNG.metrics.png_cpu.95.0.type": "long", @@ -68,6 +69,7 @@ describe('Reporting telemetry schema', () => { "PNGV2.error_codes.kibana_shutting_down_error.type": "long", "PNGV2.error_codes.queue_timeout_error.type": "long", "PNGV2.error_codes.unknown_error.type": "long", + "PNGV2.error_codes.visual_reporting_soft_disabled_error.type": "long", "PNGV2.metrics.png_cpu.50.0.type": "long", "PNGV2.metrics.png_cpu.75.0.type": "long", "PNGV2.metrics.png_cpu.95.0.type": "long", @@ -144,6 +146,7 @@ describe('Reporting telemetry schema', () => { "last7Days.PNG.error_codes.kibana_shutting_down_error.type": "long", "last7Days.PNG.error_codes.queue_timeout_error.type": "long", "last7Days.PNG.error_codes.unknown_error.type": "long", + "last7Days.PNG.error_codes.visual_reporting_soft_disabled_error.type": "long", "last7Days.PNG.metrics.png_cpu.50.0.type": "long", "last7Days.PNG.metrics.png_cpu.75.0.type": "long", "last7Days.PNG.metrics.png_cpu.95.0.type": "long", @@ -173,6 +176,7 @@ describe('Reporting telemetry schema', () => { "last7Days.PNGV2.error_codes.kibana_shutting_down_error.type": "long", "last7Days.PNGV2.error_codes.queue_timeout_error.type": "long", "last7Days.PNGV2.error_codes.unknown_error.type": "long", + "last7Days.PNGV2.error_codes.visual_reporting_soft_disabled_error.type": "long", "last7Days.PNGV2.metrics.png_cpu.50.0.type": "long", "last7Days.PNGV2.metrics.png_cpu.75.0.type": "long", "last7Days.PNGV2.metrics.png_cpu.95.0.type": "long", @@ -255,6 +259,7 @@ describe('Reporting telemetry schema', () => { "last7Days.printable_pdf.error_codes.pdf_worker_out_of_memory_error.type": "long", "last7Days.printable_pdf.error_codes.queue_timeout_error.type": "long", "last7Days.printable_pdf.error_codes.unknown_error.type": "long", + "last7Days.printable_pdf.error_codes.visual_reporting_soft_disabled_error.type": "long", "last7Days.printable_pdf.layout.canvas.type": "long", "last7Days.printable_pdf.layout.preserve_layout.type": "long", "last7Days.printable_pdf.layout.print.type": "long", @@ -292,6 +297,7 @@ describe('Reporting telemetry schema', () => { "last7Days.printable_pdf_v2.error_codes.pdf_worker_out_of_memory_error.type": "long", "last7Days.printable_pdf_v2.error_codes.queue_timeout_error.type": "long", "last7Days.printable_pdf_v2.error_codes.unknown_error.type": "long", + "last7Days.printable_pdf_v2.error_codes.visual_reporting_soft_disabled_error.type": "long", "last7Days.printable_pdf_v2.layout.canvas.type": "long", "last7Days.printable_pdf_v2.layout.preserve_layout.type": "long", "last7Days.printable_pdf_v2.layout.print.type": "long", @@ -461,6 +467,7 @@ describe('Reporting telemetry schema', () => { "printable_pdf.error_codes.pdf_worker_out_of_memory_error.type": "long", "printable_pdf.error_codes.queue_timeout_error.type": "long", "printable_pdf.error_codes.unknown_error.type": "long", + "printable_pdf.error_codes.visual_reporting_soft_disabled_error.type": "long", "printable_pdf.layout.canvas.type": "long", "printable_pdf.layout.preserve_layout.type": "long", "printable_pdf.layout.print.type": "long", @@ -498,6 +505,7 @@ describe('Reporting telemetry schema', () => { "printable_pdf_v2.error_codes.pdf_worker_out_of_memory_error.type": "long", "printable_pdf_v2.error_codes.queue_timeout_error.type": "long", "printable_pdf_v2.error_codes.unknown_error.type": "long", + "printable_pdf_v2.error_codes.visual_reporting_soft_disabled_error.type": "long", "printable_pdf_v2.layout.canvas.type": "long", "printable_pdf_v2.layout.preserve_layout.type": "long", "printable_pdf_v2.layout.print.type": "long", diff --git a/x-pack/plugins/reporting/server/usage/schema.ts b/x-pack/plugins/reporting/server/usage/schema.ts index a4cd8a3e51bfa..f89d89d35503d 100644 --- a/x-pack/plugins/reporting/server/usage/schema.ts +++ b/x-pack/plugins/reporting/server/usage/schema.ts @@ -88,6 +88,7 @@ const errorCodesSchemaPng: MakeSchemaFrom = { browser_could_not_launch_error: { type: 'long' }, browser_unexpectedly_closed_error: { type: 'long' }, browser_screenshot_error: { type: 'long' }, + visual_reporting_soft_disabled_error: { type: 'long' }, }; const errorCodesSchemaPdf: MakeSchemaFrom = { pdf_worker_out_of_memory_error: { type: 'long' }, @@ -98,6 +99,7 @@ const errorCodesSchemaPdf: MakeSchemaFrom = { diff --git a/x-pack/plugins/reporting/server/usage/types.ts b/x-pack/plugins/reporting/server/usage/types.ts index cf0acee312ae4..9d7e08932cc1c 100644 --- a/x-pack/plugins/reporting/server/usage/types.ts +++ b/x-pack/plugins/reporting/server/usage/types.ts @@ -203,6 +203,7 @@ export interface ErrorCodeStats { browser_unexpectedly_closed_error: number | null; browser_screenshot_error: number | null; kibana_shutting_down_error: number | null; + visual_reporting_soft_disabled_error: number | null; } export interface MetricsStats { diff --git a/x-pack/plugins/rule_registry/common/types.ts b/x-pack/plugins/rule_registry/common/types.ts index 4bf5fa8b23fdc..d1c5b6706c391 100644 --- a/x-pack/plugins/rule_registry/common/types.ts +++ b/x-pack/plugins/rule_registry/common/types.ts @@ -75,9 +75,8 @@ interface BucketAggsSchemas { | { [x: string]: SortOrderSchema } | Array<{ [x: string]: SortOrderSchema }>; }; - aggs?: { - [x: string]: BucketAggsSchemas; - }; + aggs?: BucketAggsSchemas; + aggregations?: BucketAggsSchemas; } /** @@ -114,78 +113,83 @@ interface BucketAggsSchemas { * - significant_text * - variable_width_histogram */ -export const BucketAggsSchemas: t.Type = t.recursion('BucketAggsSchemas', () => - t.exact( - t.partial({ - filter: t.exact( - t.partial({ - term: t.record(t.string, t.union([t.string, t.boolean, t.number])), - }) - ), - date_histogram: t.exact( - t.partial({ - field: t.string, - fixed_interval: t.string, - min_doc_count: t.number, - extended_bounds: t.type({ - min: t.string, - max: t.string, - }), - }) - ), - histogram: t.exact( - t.partial({ - field: t.string, - interval: t.number, - min_doc_count: t.number, - extended_bounds: t.exact( - t.type({ - min: t.number, - max: t.number, - }) - ), - hard_bounds: t.exact( - t.type({ - min: t.number, - max: t.number, - }) - ), - missing: t.number, - keyed: t.boolean, - order: t.exact( - t.type({ - _count: t.string, - _key: t.string, - }) - ), - }) - ), - nested: t.type({ - path: t.string, - }), - terms: t.exact( - t.partial({ - field: t.string, - collect_mode: t.string, - exclude: t.union([t.string, t.array(t.string)]), - include: t.union([t.string, t.array(t.string)]), - execution_hint: t.string, - missing: t.union([t.number, t.string]), - min_doc_count: t.number, - size: t.number, - show_term_doc_count_error: t.boolean, - order: t.union([ - sortOrderSchema, - t.record(t.string, sortOrderSchema), - t.array(t.record(t.string, sortOrderSchema)), - ]), - }) - ), - aggs: t.record(t.string, BucketAggsSchemas), - }) - ) +const bucketAggsTempsSchemas: t.Type = t.exact( + t.partial({ + filter: t.exact( + t.partial({ + term: t.record(t.string, t.union([t.string, t.boolean, t.number])), + }) + ), + date_histogram: t.exact( + t.partial({ + field: t.string, + fixed_interval: t.string, + min_doc_count: t.number, + extended_bounds: t.type({ + min: t.string, + max: t.string, + }), + }) + ), + histogram: t.exact( + t.partial({ + field: t.string, + interval: t.number, + min_doc_count: t.number, + extended_bounds: t.exact( + t.type({ + min: t.number, + max: t.number, + }) + ), + hard_bounds: t.exact( + t.type({ + min: t.number, + max: t.number, + }) + ), + missing: t.number, + keyed: t.boolean, + order: t.exact( + t.type({ + _count: t.string, + _key: t.string, + }) + ), + }) + ), + nested: t.type({ + path: t.string, + }), + terms: t.exact( + t.partial({ + field: t.string, + collect_mode: t.string, + exclude: t.union([t.string, t.array(t.string)]), + include: t.union([t.string, t.array(t.string)]), + execution_hint: t.string, + missing: t.union([t.number, t.string]), + min_doc_count: t.number, + size: t.number, + show_term_doc_count_error: t.boolean, + order: t.union([ + sortOrderSchema, + t.record(t.string, sortOrderSchema), + t.array(t.record(t.string, sortOrderSchema)), + ]), + }) + ), + }) ); +export const bucketAggsSchemas = t.intersection([ + bucketAggsTempsSchemas, + t.partial({ + aggs: t.union([t.record(t.string, bucketAggsTempsSchemas), t.undefined]), + aggregations: t.union([t.record(t.string, bucketAggsTempsSchemas), t.undefined]), + }), +]); + /** * Schemas for the metrics Aggregations * @@ -215,57 +219,75 @@ export const BucketAggsSchemas: t.Type = t.recursion('BucketA * - t_test * - value_count */ -export const metricsAggsSchemas = t.partial({ - avg: t.partial({ - field: t.string, - missing: t.union([t.string, t.number, t.boolean]), - }), - cardinality: t.partial({ - field: t.string, - precision_threshold: t.number, - rehash: t.boolean, - missing: t.union([t.string, t.number, t.boolean]), - }), - min: t.partial({ - field: t.string, - missing: t.union([t.string, t.number, t.boolean]), - format: t.string, - }), - max: t.partial({ - field: t.string, - missing: t.union([t.string, t.number, t.boolean]), - format: t.string, - }), - sum: t.partial({ - field: t.string, - missing: t.union([t.string, t.number, t.boolean]), - }), - top_hits: t.partial({ - explain: t.boolean, - docvalue_fields: t.union([t.string, t.array(t.string)]), - stored_fields: t.union([t.string, t.array(t.string)]), - from: t.number, - size: t.number, - sort: sortSchema, - seq_no_primary_term: t.boolean, - version: t.boolean, - track_scores: t.boolean, - highlight: t.any, - _source: t.union([t.boolean, t.string, t.array(t.string)]), - }), - weighted_avg: t.partial({ - format: t.string, - value_type: t.string, - value: t.partial({ - field: t.string, - missing: t.number, - }), - weight: t.partial({ - field: t.string, - missing: t.number, - }), - }), -}); +export const metricsAggsSchemas = t.exact( + t.partial({ + avg: t.exact( + t.partial({ + field: t.string, + missing: t.union([t.string, t.number, t.boolean]), + }) + ), + cardinality: t.exact( + t.partial({ + field: t.string, + precision_threshold: t.number, + rehash: t.boolean, + missing: t.union([t.string, t.number, t.boolean]), + }) + ), + min: t.exact( + t.partial({ + field: t.string, + missing: t.union([t.string, t.number, t.boolean]), + format: t.string, + }) + ), + max: t.exact( + t.partial({ + field: t.string, + missing: t.union([t.string, t.number, t.boolean]), + format: t.string, + }) + ), + sum: t.exact( + t.partial({ + field: t.string, + missing: t.union([t.string, t.number, t.boolean]), + }) + ), + top_hits: t.exact( + t.partial({ + explain: t.boolean, + docvalue_fields: t.union([t.string, t.array(t.string)]), + stored_fields: t.union([t.string, t.array(t.string)]), + from: t.number, + size: t.number, + sort: sortSchema, + seq_no_primary_term: t.boolean, + version: t.boolean, + track_scores: t.boolean, + highlight: t.any, + _source: t.union([t.boolean, t.string, t.array(t.string)]), + }) + ), + weighted_avg: t.exact( + t.partial({ + format: t.string, + value_type: t.string, + value: t.partial({ + field: t.string, + missing: t.number, + }), + weight: t.partial({ + field: t.string, + missing: t.number, + }), + }) + ), + aggs: t.undefined, + aggregations: t.undefined, + }) +); export type PutIndexTemplateRequest = estypes.IndicesPutIndexTemplateRequest & { body?: { composed_of?: string[] }; diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts index b5df5dc830d48..effd2f4f81b89 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts @@ -647,7 +647,6 @@ export class AlertsClient { [ReadOperations.Find, ReadOperations.Get, WriteOperations.Update], AlertingAuthorizationEntity.Alert ); - // As long as the user can read a minimum of one type of rule type produced by the provided feature, // the user should be provided that features' alerts index. // Limiting which alerts that user can read on that index will be done via the findAuthorizationFilter @@ -655,19 +654,16 @@ export class AlertsClient { for (const ruleType of augmentedRuleTypes.authorizedRuleTypes) { authorizedFeatures.add(ruleType.producer); } - const validAuthorizedFeatures = Array.from(authorizedFeatures).filter( (feature): feature is ValidFeatureId => featureIds.includes(feature) && isValidFeatureId(feature) ); - - const toReturn = validAuthorizedFeatures.flatMap((feature) => { - const indices = this.ruleDataService.findIndicesByFeature(feature, Dataset.alerts); - if (feature === 'siem') { - return indices.map((i) => `${i.baseName}-${this.spaceId}`); - } else { - return indices.map((i) => i.baseName); + const toReturn = validAuthorizedFeatures.map((feature) => { + const index = this.ruleDataService.findIndexByFeature(feature, Dataset.alerts); + if (index == null) { + throw new Error(`This feature id ${feature} should be associated to an alert index`); } + return index?.getPrimaryAlias(this.spaceId ?? '*') ?? ''; }); return toReturn; diff --git a/x-pack/plugins/rule_registry/server/routes/find.ts b/x-pack/plugins/rule_registry/server/routes/find.ts index eca0a6c2a0551..675037baa312a 100644 --- a/x-pack/plugins/rule_registry/server/routes/find.ts +++ b/x-pack/plugins/rule_registry/server/routes/find.ts @@ -14,7 +14,7 @@ import { PositiveInteger } from '@kbn/securitysolution-io-ts-types'; import { RacRequestHandlerContext } from '../types'; import { BASE_RAC_ALERTS_API_PATH } from '../../common/constants'; import { buildRouteValidation } from './utils/route_validation'; -import { BucketAggsSchemas } from '../../common/types'; +import { bucketAggsSchemas, metricsAggsSchemas } from '../../common/types'; export const findAlertsByQueryRoute = (router: IRouter) => { router.post( @@ -26,7 +26,11 @@ export const findAlertsByQueryRoute = (router: IRouter t.partial({ index: t.string, query: t.object, - aggs: t.union([t.record(t.string, BucketAggsSchemas), t.undefined]), + aggs: t.union([ + t.record(t.string, bucketAggsSchemas), + t.record(t.string, metricsAggsSchemas), + t.undefined, + ]), size: t.union([PositiveInteger, t.undefined]), track_total_hits: t.union([t.boolean, t.undefined]), _source: t.union([t.array(t.string), t.undefined]), diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts index 8bbc14cab9f82..cfbfafd0092bf 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts @@ -16,7 +16,7 @@ export const ruleDataServiceMock = { initializeService: jest.fn(), initializeIndex: jest.fn(), findIndexByName: jest.fn(), - findIndicesByFeature: jest.fn(), + findIndexByFeature: jest.fn(), }), }; diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts index a940201841e6f..a336f73c87c79 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts @@ -71,7 +71,7 @@ export interface IRuleDataService { * Looks up the index information associated with the given Kibana "feature". * Note: features are used in RBAC. */ - findIndicesByFeature(featureId: ValidFeatureId, dataset?: Dataset): IndexInfo[]; + findIndexByFeature(featureId: ValidFeatureId, dataset: Dataset): IndexInfo | null; } // TODO: This is a leftover. Remove its usage from the "observability" plugin and delete it. @@ -214,8 +214,13 @@ export class RuleDataService implements IRuleDataService { return this.indicesByBaseName.get(baseName) ?? null; } - public findIndicesByFeature(featureId: ValidFeatureId, dataset?: Dataset): IndexInfo[] { + public findIndexByFeature(featureId: ValidFeatureId, dataset: Dataset): IndexInfo | null { const foundIndices = this.indicesByFeatureId.get(featureId) ?? []; - return dataset ? foundIndices.filter((i) => i.indexOptions.dataset === dataset) : foundIndices; + if (dataset && foundIndices.length > 0) { + return foundIndices.filter((i) => i.indexOptions.dataset === dataset)[0]; + } else if (foundIndices.length > 0) { + return foundIndices[0]; + } + return null; } } diff --git a/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.test.ts b/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.test.ts index 79b9a8cd75c58..9bfc4d7a40640 100644 --- a/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.test.ts +++ b/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.test.ts @@ -78,12 +78,10 @@ describe('ruleRegistrySearchStrategyProvider()', () => { const searchStrategySearch = jest.fn().mockImplementation(() => of(response)); beforeEach(() => { - ruleDataService.findIndicesByFeature.mockImplementation(() => { - return [ - { - baseName: 'test', - } as IndexInfo, - ]; + ruleDataService.findIndexByFeature.mockImplementation(() => { + return { + baseName: 'test', + } as IndexInfo; }); data.search.getSearchStrategy.mockImplementation(() => { @@ -108,7 +106,7 @@ describe('ruleRegistrySearchStrategyProvider()', () => { }); afterEach(() => { - ruleDataService.findIndicesByFeature.mockClear(); + ruleDataService.findIndexByFeature.mockClear(); data.search.getSearchStrategy.mockClear(); (data.search.searchAsInternalUser.search as jest.Mock).mockClear(); getAuthzFilterSpy.mockClear(); @@ -156,12 +154,10 @@ describe('ruleRegistrySearchStrategyProvider()', () => { }; }); - ruleDataService.findIndicesByFeature.mockImplementation(() => { - return [ - { - baseName: 'myTestIndex', - } as unknown as IndexInfo, - ]; + ruleDataService.findIndexByFeature.mockImplementation(() => { + return { + baseName: 'myTestIndex', + } as unknown as IndexInfo; }); let searchRequest: RuleRegistrySearchRequest = {} as unknown as RuleRegistrySearchRequest; @@ -199,8 +195,8 @@ describe('ruleRegistrySearchStrategyProvider()', () => { request: {}, }; - ruleDataService.findIndicesByFeature.mockImplementationOnce(() => { - return []; + ruleDataService.findIndexByFeature.mockImplementationOnce(() => { + return null; }); const strategy = ruleRegistrySearchStrategyProvider( diff --git a/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts b/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts index 851ffbdc098da..255af29a9a9d3 100644 --- a/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts +++ b/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts @@ -89,17 +89,16 @@ export const ruleRegistrySearchStrategyProvider = ( ); return accum; } - - return [ - ...accum, - ...ruleDataService - .findIndicesByFeature(featureId, Dataset.alerts) - .map((indexInfo) => { - return featureId === 'siem' - ? `${indexInfo.baseName}-${space?.id ?? ''}*` - : `${indexInfo.baseName}*`; - }), - ]; + const alertIndexInfo = ruleDataService.findIndexByFeature(featureId, Dataset.alerts); + if (alertIndexInfo) { + return [ + ...accum, + featureId === 'siem' + ? `${alertIndexInfo.baseName}-${space?.id ?? ''}*` + : `${alertIndexInfo.baseName}*`, + ]; + } + return accum; }, []); if (indices.length === 0) { diff --git a/x-pack/plugins/runtime_fields/public/types.ts b/x-pack/plugins/runtime_fields/public/types.ts index 3cd8506c01b10..b39e5e2ee5e6d 100644 --- a/x-pack/plugins/runtime_fields/public/types.ts +++ b/x-pack/plugins/runtime_fields/public/types.ts @@ -5,8 +5,6 @@ * 2.0. */ -import { DataPublicPluginStart } from '@kbn/data-plugin/public'; - import { RUNTIME_FIELD_TYPES } from './constants'; import { OpenRuntimeFieldEditorProps } from './load_editor'; @@ -24,9 +22,8 @@ export interface PluginStart {} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface SetupPlugins {} -export interface StartPlugins { - data: DataPublicPluginStart; -} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface StartPlugins {} export type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; diff --git a/x-pack/plugins/saved_objects_tagging/server/index.ts b/x-pack/plugins/saved_objects_tagging/server/index.ts index 5f731c77caac7..b9fcdcdbb7b4e 100644 --- a/x-pack/plugins/saved_objects_tagging/server/index.ts +++ b/x-pack/plugins/saved_objects_tagging/server/index.ts @@ -9,6 +9,13 @@ import { PluginInitializerContext } from '@kbn/core/server'; import { SavedObjectTaggingPlugin } from './plugin'; export { config } from './config'; +export type { + SavedObjectTaggingStart, + CreateTagAssignmentServiceOptions, + CreateTagClientOptions, +} from './types'; +export type { IAssignmentService } from './services'; +export type { ITagsClient } from '../common'; export const plugin = (initializerContext: PluginInitializerContext) => new SavedObjectTaggingPlugin(); diff --git a/x-pack/plugins/saved_objects_tagging/server/mocks.ts b/x-pack/plugins/saved_objects_tagging/server/mocks.ts new file mode 100644 index 0000000000000..6e1b456090dd4 --- /dev/null +++ b/x-pack/plugins/saved_objects_tagging/server/mocks.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SavedObjectTaggingStart } from './types'; +import { tagsClientMock } from './services/tags/tags_client.mock'; +import { assigmentServiceMock } from './services/assignments/assignment_service.mock'; + +const createStartMock = () => { + const start: jest.Mocked = { + createTagClient: jest.fn(), + createInternalAssignmentService: jest.fn(), + }; + + start.createTagClient.mockImplementation(() => tagsClientMock.create()); + start.createInternalAssignmentService.mockImplementation(() => assigmentServiceMock.create()); + + return start; +}; + +export const savedObjectsTaggingMock = { + createStartContract: createStartMock, + createTagClient: tagsClientMock.create, + createAssignmentService: assigmentServiceMock.create, +}; diff --git a/x-pack/plugins/saved_objects_tagging/server/plugin.test.ts b/x-pack/plugins/saved_objects_tagging/server/plugin.test.ts index a62952937bc21..78cd2cd1f1f95 100644 --- a/x-pack/plugins/saved_objects_tagging/server/plugin.test.ts +++ b/x-pack/plugins/saved_objects_tagging/server/plugin.test.ts @@ -41,7 +41,7 @@ describe('SavedObjectTaggingPlugin', () => { it('registers the globalSearch route handler context', async () => { const coreSetup = coreMock.createSetup(); - await plugin.setup(coreSetup, { features: featuresPluginSetup }); + plugin.setup(coreSetup, { features: featuresPluginSetup }); expect(coreSetup.http.registerRouteHandlerContext).toHaveBeenCalledTimes(1); expect(coreSetup.http.registerRouteHandlerContext).toHaveBeenCalledWith( 'tags', @@ -50,7 +50,7 @@ describe('SavedObjectTaggingPlugin', () => { }); it('registers the `savedObjectsTagging` feature', async () => { - await plugin.setup(coreMock.createSetup(), { features: featuresPluginSetup }); + plugin.setup(coreMock.createSetup(), { features: featuresPluginSetup }); expect(featuresPluginSetup.registerKibanaFeature).toHaveBeenCalledTimes(1); expect(featuresPluginSetup.registerKibanaFeature).toHaveBeenCalledWith( savedObjectsTaggingFeature @@ -61,7 +61,7 @@ describe('SavedObjectTaggingPlugin', () => { const tagUsageCollector = Symbol('saved_objects_tagging'); createTagUsageCollectorMock.mockReturnValue(tagUsageCollector); - await plugin.setup(coreMock.createSetup(), { + plugin.setup(coreMock.createSetup(), { features: featuresPluginSetup, usageCollection: usageCollectionSetup, }); @@ -70,4 +70,16 @@ describe('SavedObjectTaggingPlugin', () => { expect(usageCollectionSetup.registerCollector).toHaveBeenCalledWith(tagUsageCollector); }); }); + + describe('#start', () => { + it('returns the expected contract', () => { + plugin.setup(coreMock.createSetup(), { features: featuresPluginSetup }); + const contract = plugin.start(coreMock.createStart(), {}); + + expect(contract).toEqual({ + createTagClient: expect.any(Function), + createInternalAssignmentService: expect.any(Function), + }); + }); + }); }); diff --git a/x-pack/plugins/saved_objects_tagging/server/plugin.ts b/x-pack/plugins/saved_objects_tagging/server/plugin.ts index 05a2bb31514f1..59c40ab4f124b 100644 --- a/x-pack/plugins/saved_objects_tagging/server/plugin.ts +++ b/x-pack/plugins/saved_objects_tagging/server/plugin.ts @@ -8,13 +8,19 @@ import { CoreSetup, CoreStart, Plugin } from '@kbn/core/server'; import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; -import { SecurityPluginSetup } from '@kbn/security-plugin/server'; +import { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/server'; import { savedObjectsTaggingFeature } from './features'; import { tagType } from './saved_objects'; -import type { TagsHandlerContext } from './types'; +import type { + TagsHandlerContext, + SavedObjectTaggingStart, + CreateTagClientOptions, + CreateTagAssignmentServiceOptions, +} from './types'; import { TagsRequestHandlerContext } from './request_handler_context'; import { registerRoutes } from './routes'; import { createTagUsageCollector } from './usage'; +import { TagsClient, AssignmentService } from './services'; interface SetupDeps { features: FeaturesPluginSetup; @@ -22,7 +28,13 @@ interface SetupDeps { security?: SecurityPluginSetup; } -export class SavedObjectTaggingPlugin implements Plugin<{}, {}, SetupDeps, {}> { +interface StartDeps { + security?: SecurityPluginStart; +} + +export class SavedObjectTaggingPlugin + implements Plugin<{}, SavedObjectTaggingStart, SetupDeps, StartDeps> +{ public setup( { savedObjects, http }: CoreSetup, { features, usageCollection, security }: SetupDeps @@ -53,7 +65,19 @@ export class SavedObjectTaggingPlugin implements Plugin<{}, {}, SetupDeps, {}> { return {}; } - public start(core: CoreStart) { - return {}; + public start(core: CoreStart, { security }: StartDeps) { + return { + createTagClient: ({ client }: CreateTagClientOptions) => { + return new TagsClient({ client }); + }, + createInternalAssignmentService: ({ client }: CreateTagAssignmentServiceOptions) => { + return new AssignmentService({ + client, + authorization: security?.authz, + typeRegistry: core.savedObjects.getTypeRegistry(), + internal: true, + }); + }, + }; } } diff --git a/x-pack/plugins/saved_objects_tagging/server/services/assignments/assignment_service.test.ts b/x-pack/plugins/saved_objects_tagging/server/services/assignments/assignment_service.test.ts index 3cc4c00da5b30..61e4fbc2fb651 100644 --- a/x-pack/plugins/saved_objects_tagging/server/services/assignments/assignment_service.test.ts +++ b/x-pack/plugins/saved_objects_tagging/server/services/assignments/assignment_service.test.ts @@ -28,245 +28,350 @@ describe('AssignmentService', () => { authorization = securityMock.createSetup().authz; savedObjectClient = savedObjectsClientMock.create(); typeRegistry = savedObjectsTypeRegistryMock.create(); - - service = new AssignmentService({ - request, - typeRegistry, - authorization, - client: savedObjectClient, - }); }); afterEach(() => { getUpdatableSavedObjectTypesMock.mockReset(); }); - describe('#updateTagAssignments', () => { + describe('scoped service', () => { beforeEach(() => { - getUpdatableSavedObjectTypesMock.mockImplementation(({ types }) => Promise.resolve(types)); - - savedObjectClient.bulkGet.mockResolvedValue({ - saved_objects: [], + service = new AssignmentService({ + request, + typeRegistry, + authorization, + client: savedObjectClient, }); }); - it('throws an error if trying to assign non-taggable types', async () => { - await expect( - service.updateTagAssignments({ - tags: ['tag-1', 'tag-2'], - assign: [ - { type: 'dashboard', id: 'dash-1' }, - { type: 'not-supported', id: 'foo' }, - ], - unassign: [], - }) - ).rejects.toThrowErrorMatchingInlineSnapshot(`"Unsupported type [not-supported]"`); + it('throws when instantiated with `internal: false` if no request is provided', () => { + expect(() => { + new AssignmentService({ + internal: false, + typeRegistry, + authorization, + client: savedObjectClient, + }); + }).toThrowErrorMatchingInlineSnapshot(`"request required for non-internal usages"`); }); - it('throws an error if trying to assign non-assignable types', async () => { - getUpdatableSavedObjectTypesMock.mockResolvedValue(['dashboard']); + describe('#updateTagAssignments', () => { + beforeEach(() => { + getUpdatableSavedObjectTypesMock.mockImplementation(({ types }) => Promise.resolve(types)); + + savedObjectClient.bulkGet.mockResolvedValue({ + saved_objects: [], + }); + }); + + it('throws an error if trying to assign non-taggable types', async () => { + await expect( + service.updateTagAssignments({ + tags: ['tag-1', 'tag-2'], + assign: [ + { type: 'dashboard', id: 'dash-1' }, + { type: 'not-supported', id: 'foo' }, + ], + unassign: [], + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Unsupported type [not-supported]"`); + }); - await expect( - service.updateTagAssignments({ + it('throws an error if trying to assign non-assignable types', async () => { + getUpdatableSavedObjectTypesMock.mockResolvedValue(['dashboard']); + + await expect( + service.updateTagAssignments({ + tags: ['tag-1', 'tag-2'], + assign: [ + { type: 'dashboard', id: 'dash-1' }, + { type: 'map', id: 'map-1' }, + ], + unassign: [], + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Forbidden type [map]"`); + }); + + it('calls `soClient.bulkGet` with the correct parameters', async () => { + await service.updateTagAssignments({ tags: ['tag-1', 'tag-2'], - assign: [ - { type: 'dashboard', id: 'dash-1' }, - { type: 'map', id: 'map-1' }, - ], - unassign: [], - }) - ).rejects.toThrowErrorMatchingInlineSnapshot(`"Forbidden type [map]"`); - }); + assign: [{ type: 'dashboard', id: 'dash-1' }], + unassign: [{ type: 'map', id: 'map-1' }], + }); - it('calls `soClient.bulkGet` with the correct parameters', async () => { - await service.updateTagAssignments({ - tags: ['tag-1', 'tag-2'], - assign: [{ type: 'dashboard', id: 'dash-1' }], - unassign: [{ type: 'map', id: 'map-1' }], + expect(savedObjectClient.bulkGet).toHaveBeenCalledTimes(1); + expect(savedObjectClient.bulkGet).toHaveBeenCalledWith([ + { type: 'dashboard', id: 'dash-1', fields: [] }, + { type: 'map', id: 'map-1', fields: [] }, + ]); }); - expect(savedObjectClient.bulkGet).toHaveBeenCalledTimes(1); - expect(savedObjectClient.bulkGet).toHaveBeenCalledWith([ - { type: 'dashboard', id: 'dash-1', fields: [] }, - { type: 'map', id: 'map-1', fields: [] }, - ]); - }); + it('throws an error if any result from `soClient.bulkGet` has an error', async () => { + savedObjectClient.bulkGet.mockResolvedValue({ + saved_objects: [ + createSavedObject({ type: 'dashboard', id: 'dash-1' }), + createSavedObject({ + type: 'map', + id: 'map-1', + error: { + statusCode: 404, + message: 'not found', + error: 'object was not found', + }, + }), + ], + }); - it('throws an error if any result from `soClient.bulkGet` has an error', async () => { - savedObjectClient.bulkGet.mockResolvedValue({ - saved_objects: [ - createSavedObject({ type: 'dashboard', id: 'dash-1' }), - createSavedObject({ - type: 'map', - id: 'map-1', - error: { - statusCode: 404, - message: 'not found', - error: 'object was not found', - }, - }), - ], + await expect( + service.updateTagAssignments({ + tags: ['tag-1', 'tag-2'], + assign: [{ type: 'dashboard', id: 'dash-1' }], + unassign: [{ type: 'map', id: 'map-1' }], + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"not found"`); }); - await expect( - service.updateTagAssignments({ + it('calls `soClient.bulkUpdate` to update the references', async () => { + savedObjectClient.bulkGet.mockResolvedValue({ + saved_objects: [ + createSavedObject({ + type: 'dashboard', + id: 'dash-1', + references: [], + }), + createSavedObject({ + type: 'map', + id: 'map-1', + references: [createReference('dashboard', 'dash-1'), createReference('tag', 'tag-1')], + }), + ], + }); + + await service.updateTagAssignments({ tags: ['tag-1', 'tag-2'], assign: [{ type: 'dashboard', id: 'dash-1' }], unassign: [{ type: 'map', id: 'map-1' }], - }) - ).rejects.toThrowErrorMatchingInlineSnapshot(`"not found"`); - }); + }); - it('calls `soClient.bulkUpdate` to update the references', async () => { - savedObjectClient.bulkGet.mockResolvedValue({ - saved_objects: [ - createSavedObject({ + expect(savedObjectClient.bulkUpdate).toHaveBeenCalledTimes(1); + expect(savedObjectClient.bulkUpdate).toHaveBeenCalledWith([ + { type: 'dashboard', id: 'dash-1', - references: [], - }), - createSavedObject({ + attributes: {}, + references: [createReference('tag', 'tag-1'), createReference('tag', 'tag-2')], + }, + { type: 'map', id: 'map-1', - references: [createReference('dashboard', 'dash-1'), createReference('tag', 'tag-1')], - }), - ], + attributes: {}, + references: [createReference('dashboard', 'dash-1')], + }, + ]); }); + }); - await service.updateTagAssignments({ - tags: ['tag-1', 'tag-2'], - assign: [{ type: 'dashboard', id: 'dash-1' }], - unassign: [{ type: 'map', id: 'map-1' }], + describe('#findAssignableObjects', () => { + beforeEach(() => { + getUpdatableSavedObjectTypesMock.mockImplementation(({ types }) => Promise.resolve(types)); + typeRegistry.getType.mockImplementation( + (name) => + ({ + management: { + defaultSearchField: `${name}-search-field`, + }, + } as any) + ); + savedObjectClient.find.mockResolvedValue({ + saved_objects: [], + total: 0, + page: 1, + per_page: 20, + }); }); - expect(savedObjectClient.bulkUpdate).toHaveBeenCalledTimes(1); - expect(savedObjectClient.bulkUpdate).toHaveBeenCalledWith([ - { - type: 'dashboard', - id: 'dash-1', - attributes: {}, - references: [createReference('tag', 'tag-1'), createReference('tag', 'tag-2')], - }, - { - type: 'map', - id: 'map-1', - attributes: {}, - references: [createReference('dashboard', 'dash-1')], - }, - ]); - }); - }); + it('calls `soClient.find` with the correct parameters', async () => { + await service.findAssignableObjects({ + types: ['dashboard', 'map'], + search: 'term', + maxResults: 20, + }); - describe('#findAssignableObjects', () => { - beforeEach(() => { - getUpdatableSavedObjectTypesMock.mockImplementation(({ types }) => Promise.resolve(types)); - typeRegistry.getType.mockImplementation( - (name) => - ({ - management: { - defaultSearchField: `${name}-search-field`, - }, - } as any) - ); - savedObjectClient.find.mockResolvedValue({ - saved_objects: [], - total: 0, - page: 1, - per_page: 20, + expect(savedObjectClient.find).toHaveBeenCalledTimes(1); + expect(savedObjectClient.find).toHaveBeenCalledWith({ + page: 1, + perPage: 20, + search: 'term', + type: ['dashboard', 'map'], + searchFields: ['dashboard-search-field', 'map-search-field'], + }); }); - }); - it('calls `soClient.find` with the correct parameters', async () => { - await service.findAssignableObjects({ - types: ['dashboard', 'map'], - search: 'term', - maxResults: 20, + it('filters the non-assignable types', async () => { + getUpdatableSavedObjectTypesMock.mockResolvedValue(['dashboard']); + + await service.findAssignableObjects({ + types: ['dashboard', 'map'], + search: 'term', + maxResults: 20, + }); + + expect(savedObjectClient.find).toHaveBeenCalledTimes(1); + expect(savedObjectClient.find).toHaveBeenCalledWith( + expect.objectContaining({ + type: ['dashboard'], + }) + ); }); - expect(savedObjectClient.find).toHaveBeenCalledTimes(1); - expect(savedObjectClient.find).toHaveBeenCalledWith({ - page: 1, - perPage: 20, - search: 'term', - type: ['dashboard', 'map'], - searchFields: ['dashboard-search-field', 'map-search-field'], + it('converts the results returned from `soClient.find`', async () => { + savedObjectClient.find.mockResolvedValue({ + saved_objects: [ + createSavedObject({ + type: 'dashboard', + id: 'dash-1', + }), + createSavedObject({ + type: 'map', + id: 'dash-2', + }), + ] as any[], + total: 2, + page: 1, + per_page: 20, + }); + + const results = await service.findAssignableObjects({ + types: ['dashboard', 'map'], + search: 'term', + maxResults: 20, + }); + + expect(results.map(({ type, id }) => ({ type, id }))).toEqual([ + { type: 'dashboard', id: 'dash-1' }, + { type: 'map', id: 'dash-2' }, + ]); }); }); - it('filters the non-assignable types', async () => { - getUpdatableSavedObjectTypesMock.mockResolvedValue(['dashboard']); + describe('#getAssignableTypes', () => { + it('calls `getUpdatableSavedObjectTypes` with the correct parameters', async () => { + await service.getAssignableTypes(['type-a', 'type-b']); - await service.findAssignableObjects({ - types: ['dashboard', 'map'], - search: 'term', - maxResults: 20, + expect(getUpdatableSavedObjectTypesMock).toHaveBeenCalledTimes(1); + expect(getUpdatableSavedObjectTypesMock).toHaveBeenCalledWith({ + request, + authorization, + types: ['type-a', 'type-b'], + }); }); + it('calls `getUpdatableSavedObjectTypes` with `taggableTypes` when `types` is not specified ', async () => { + await service.getAssignableTypes(); - expect(savedObjectClient.find).toHaveBeenCalledTimes(1); - expect(savedObjectClient.find).toHaveBeenCalledWith( - expect.objectContaining({ - type: ['dashboard'], - }) - ); + expect(getUpdatableSavedObjectTypesMock).toHaveBeenCalledTimes(1); + expect(getUpdatableSavedObjectTypesMock).toHaveBeenCalledWith({ + request, + authorization, + types: taggableTypes, + }); + }); + it('forward the result of `getUpdatableSavedObjectTypes`', async () => { + getUpdatableSavedObjectTypesMock.mockReturnValue(['updatable-a', 'updatable-b']); + + const assignableTypes = await service.getAssignableTypes(); + + expect(assignableTypes).toEqual(['updatable-a', 'updatable-b']); + }); }); + }); - it('converts the results returned from `soClient.find`', async () => { - savedObjectClient.find.mockResolvedValue({ - saved_objects: [ - createSavedObject({ - type: 'dashboard', - id: 'dash-1', - }), - createSavedObject({ - type: 'map', - id: 'dash-2', - }), - ] as any[], - total: 2, - page: 1, - per_page: 20, + describe('internal service', () => { + beforeEach(() => { + service = new AssignmentService({ + internal: true, + typeRegistry, + authorization, + client: savedObjectClient, }); + }); - const results = await service.findAssignableObjects({ - types: ['dashboard', 'map'], - search: 'term', - maxResults: 20, + describe('#updateTagAssignments', () => { + beforeEach(() => { + getUpdatableSavedObjectTypesMock.mockImplementation(({ types }) => Promise.resolve(types)); + + savedObjectClient.bulkGet.mockResolvedValue({ + saved_objects: [], + }); }); - expect(results.map(({ type, id }) => ({ type, id }))).toEqual([ - { type: 'dashboard', id: 'dash-1' }, - { type: 'map', id: 'dash-2' }, - ]); - }); - }); + it('does not calls `getUpdatableSavedObjectTypes`', async () => { + getUpdatableSavedObjectTypesMock.mockResolvedValue(['dashboard']); - describe('#getAssignableTypes', () => { - it('calls `getUpdatableSavedObjectTypes` with the correct parameters', async () => { - await service.getAssignableTypes(['type-a', 'type-b']); + await service.updateTagAssignments({ + tags: ['tag-1', 'tag-2'], + assign: [ + { type: 'dashboard', id: 'dash-1' }, + { type: 'map', id: 'map-1' }, + ], + unassign: [], + }); - expect(getUpdatableSavedObjectTypesMock).toHaveBeenCalledTimes(1); - expect(getUpdatableSavedObjectTypesMock).toHaveBeenCalledWith({ - request, - authorization, - types: ['type-a', 'type-b'], + expect(getUpdatableSavedObjectTypesMock).not.toHaveBeenCalled(); + expect(savedObjectClient.bulkGet).toHaveBeenCalledTimes(1); + expect(savedObjectClient.bulkGet).toHaveBeenCalledWith([ + { type: 'dashboard', id: 'dash-1', fields: [] }, + { type: 'map', id: 'map-1', fields: [] }, + ]); }); }); - it('calls `getUpdatableSavedObjectTypes` with `taggableTypes` when `types` is not specified ', async () => { - await service.getAssignableTypes(); - expect(getUpdatableSavedObjectTypesMock).toHaveBeenCalledTimes(1); - expect(getUpdatableSavedObjectTypesMock).toHaveBeenCalledWith({ - request, - authorization, - types: taggableTypes, + describe('#findAssignableObjects', () => { + beforeEach(() => { + typeRegistry.getType.mockImplementation( + (name) => + ({ + management: { + defaultSearchField: `${name}-search-field`, + }, + } as any) + ); + savedObjectClient.find.mockResolvedValue({ + saved_objects: [], + total: 0, + page: 1, + per_page: 20, + }); + }); + + it('does not calls `getUpdatableSavedObjectTypes`', async () => { + getUpdatableSavedObjectTypesMock.mockResolvedValue(['dashboard']); + + await service.findAssignableObjects({ + types: ['dashboard', 'map'], + search: 'term', + maxResults: 20, + }); + + expect(getUpdatableSavedObjectTypesMock).not.toHaveBeenCalled(); + expect(savedObjectClient.find).toHaveBeenCalledTimes(1); + expect(savedObjectClient.find).toHaveBeenCalledWith( + expect.objectContaining({ + type: ['dashboard', 'map'], + }) + ); }); }); - it('forward the result of `getUpdatableSavedObjectTypes`', async () => { - getUpdatableSavedObjectTypesMock.mockReturnValue(['updatable-a', 'updatable-b']); - const assignableTypes = await service.getAssignableTypes(); + describe('#getAssignableTypes', () => { + it('does not calls `getUpdatableSavedObjectTypes`', async () => { + await service.getAssignableTypes(['type-a', 'type-b']); - expect(assignableTypes).toEqual(['updatable-a', 'updatable-b']); + expect(getUpdatableSavedObjectTypesMock).not.toHaveBeenCalled(); + }); + it('returns the list of all taggable types', async () => { + const assignableTypes = await service.getAssignableTypes(); + expect(assignableTypes).toEqual(taggableTypes); + }); }); }); }); diff --git a/x-pack/plugins/saved_objects_tagging/server/services/assignments/assignment_service.ts b/x-pack/plugins/saved_objects_tagging/server/services/assignments/assignment_service.ts index d6d14f68357be..09a726db856c2 100644 --- a/x-pack/plugins/saved_objects_tagging/server/services/assignments/assignment_service.ts +++ b/x-pack/plugins/saved_objects_tagging/server/services/assignments/assignment_service.ts @@ -28,10 +28,11 @@ import { AssignmentError } from './errors'; import { toAssignableObject } from './utils'; interface AssignmentServiceOptions { - request: KibanaRequest; + request?: KibanaRequest; client: SavedObjectsClientContract; typeRegistry: ISavedObjectTypeRegistry; authorization?: SecurityPluginSetup['authz']; + internal?: boolean; } export type IAssignmentService = PublicMethodsOf; @@ -40,9 +41,20 @@ export class AssignmentService { private readonly soClient: SavedObjectsClientContract; private readonly typeRegistry: ISavedObjectTypeRegistry; private readonly authorization?: SecurityPluginSetup['authz']; - private readonly request: KibanaRequest; - - constructor({ client, typeRegistry, authorization, request }: AssignmentServiceOptions) { + private readonly request?: KibanaRequest; + private readonly internal: boolean; + + constructor({ + client, + typeRegistry, + authorization, + request, + internal = false, + }: AssignmentServiceOptions) { + if (!internal && !request) { + throw new Error('request required for non-internal usages'); + } + this.internal = internal; this.soClient = client; this.typeRegistry = typeRegistry; this.authorization = authorization; @@ -84,8 +96,11 @@ export class AssignmentService { } public async getAssignableTypes(types?: string[]) { + if (this.internal) { + return types ?? taggableTypes; + } return getUpdatableSavedObjectTypes({ - request: this.request, + request: this.request!, types: types ?? taggableTypes, authorization: this.authorization, }); diff --git a/x-pack/plugins/saved_objects_tagging/server/types.ts b/x-pack/plugins/saved_objects_tagging/server/types.ts index 366f2779b6a81..0bc023214151d 100644 --- a/x-pack/plugins/saved_objects_tagging/server/types.ts +++ b/x-pack/plugins/saved_objects_tagging/server/types.ts @@ -5,7 +5,11 @@ * 2.0. */ -import type { IRouter, CustomRequestHandlerContext } from '@kbn/core/server'; +import type { + IRouter, + CustomRequestHandlerContext, + SavedObjectsClientContract, +} from '@kbn/core/server'; import { ITagsClient } from '../common/types'; import { IAssignmentService } from './services'; @@ -14,6 +18,37 @@ export interface ITagsRequestHandlerContext { assignmentService: IAssignmentService; } +/** @public */ +export interface CreateTagClientOptions { + client: SavedObjectsClientContract; +} + +/** @public */ +export interface CreateTagAssignmentServiceOptions { + client: SavedObjectsClientContract; +} + +/** @public */ +export interface SavedObjectTaggingStart { + /** + * Creates a TagClient bound to the provided SavedObject client. + */ + createTagClient: (options: CreateTagClientOptions) => ITagsClient; + + /** + * Creates an internal AssignmentService bound to the provided SavedObject client. + * + * @remark: As assignment services created via this API will not be performing authz check to ensure + * that the current user is allowed to update the assigned/unassigned objects. + * This API is only meant to be used to perform operations on behalf of the 'internal' Kibana user. + * When trying to assign or unassign tags on behalf of a user, use the `tags` request handler context + * instead. + */ + createInternalAssignmentService: ( + options: CreateTagAssignmentServiceOptions + ) => IAssignmentService; +} + /** * @internal */ diff --git a/x-pack/plugins/screenshotting/common/errors.ts b/x-pack/plugins/screenshotting/common/errors.ts index 50effb03ee1da..5284e808c7993 100644 --- a/x-pack/plugins/screenshotting/common/errors.ts +++ b/x-pack/plugins/screenshotting/common/errors.ts @@ -13,3 +13,5 @@ export class FailedToSpawnBrowserError extends Error {} export class BrowserClosedUnexpectedly extends Error {} export class FailedToCaptureScreenshot extends Error {} + +export class InsufficientMemoryAvailableOnCloudError extends Error {} diff --git a/x-pack/plugins/screenshotting/common/index.ts b/x-pack/plugins/screenshotting/common/index.ts index b6b9034cb8189..7570477a1c1c9 100644 --- a/x-pack/plugins/screenshotting/common/index.ts +++ b/x-pack/plugins/screenshotting/common/index.ts @@ -14,3 +14,5 @@ export { SCREENSHOTTING_EXPRESSION, SCREENSHOTTING_EXPRESSION_INPUT, } from './expression'; + +export const PLUGIN_ID = 'screenshotting'; diff --git a/x-pack/plugins/screenshotting/kibana.json b/x-pack/plugins/screenshotting/kibana.json index f376409687311..37f962539446b 100644 --- a/x-pack/plugins/screenshotting/kibana.json +++ b/x-pack/plugins/screenshotting/kibana.json @@ -7,6 +7,7 @@ "githubTeam": "kibana-reporting-services" }, "description": "Kibana Screenshotting Plugin", + "optionalPlugins": ["cloud"], "requiredPlugins": ["expressions", "screenshotMode"], "configPath": ["xpack", "screenshotting"], "server": true, diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/noto/LICENSE_OFL.txt b/x-pack/plugins/screenshotting/server/assets/fonts/noto/LICENSE_OFL.txt similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/noto/LICENSE_OFL.txt rename to x-pack/plugins/screenshotting/server/assets/fonts/noto/LICENSE_OFL.txt diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/noto/NotoSansCJKtc-Medium.ttf b/x-pack/plugins/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Medium.ttf similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/noto/NotoSansCJKtc-Medium.ttf rename to x-pack/plugins/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Medium.ttf diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/noto/NotoSansCJKtc-Regular.ttf b/x-pack/plugins/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Regular.ttf similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/noto/NotoSansCJKtc-Regular.ttf rename to x-pack/plugins/screenshotting/server/assets/fonts/noto/NotoSansCJKtc-Regular.ttf diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/noto/index.js b/x-pack/plugins/screenshotting/server/assets/fonts/noto/index.js similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/noto/index.js rename to x-pack/plugins/screenshotting/server/assets/fonts/noto/index.js diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/roboto/LICENSE.txt b/x-pack/plugins/screenshotting/server/assets/fonts/roboto/LICENSE.txt similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/roboto/LICENSE.txt rename to x-pack/plugins/screenshotting/server/assets/fonts/roboto/LICENSE.txt diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/roboto/Roboto-Italic.ttf b/x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Italic.ttf similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/roboto/Roboto-Italic.ttf rename to x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Italic.ttf diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/roboto/Roboto-Medium.ttf b/x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Medium.ttf similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/roboto/Roboto-Medium.ttf rename to x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Medium.ttf diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/roboto/Roboto-Regular.ttf b/x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Regular.ttf similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/fonts/roboto/Roboto-Regular.ttf rename to x-pack/plugins/screenshotting/server/assets/fonts/roboto/Roboto-Regular.ttf diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/img/logo-grey.png b/x-pack/plugins/screenshotting/server/assets/img/logo-grey.png similarity index 100% rename from x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/assets/img/logo-grey.png rename to x-pack/plugins/screenshotting/server/assets/img/logo-grey.png diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts index fd09396d6c86d..ec8c51af53486 100644 --- a/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts @@ -17,6 +17,7 @@ import { import { ConfigType } from '../../config'; import { allowRequest } from '../network_policy'; import { stripUnsafeHeaders } from './strip_unsafe_headers'; +import { getFooterTemplate, getHeaderTemplate } from './templates'; export type Context = Record; @@ -155,6 +156,52 @@ export class HeadlessChromiumDriver { return !this.page.isClosed(); } + /** + * Despite having "preserveDrawingBuffer": "true" for WebGL driven canvas elements + * we may still get a blank canvas in PDFs. As a further mitigation + * we convert WebGL backed canvases to images and inline replace the canvas element. + * The visual result is identical. + * + * The drawback is that we are mutating the page and so if anything were to interact + * with it after we ran this function it may lead to issues. Ideally, once Chromium + * fixes how PDFs are generated we can remove this code. See: + * + * https://bugs.chromium.org/p/chromium/issues/detail?id=809065 + * https://bugs.chromium.org/p/chromium/issues/detail?id=137576 + * + * Idea adapted from: https://github.com/puppeteer/puppeteer/issues/1731#issuecomment-864345938 + */ + private async workaroundWebGLDrivenCanvases() { + const canvases = await this.page.$$('canvas'); + for (const canvas of canvases) { + await canvas.evaluate((thisCanvas: Element) => { + if ( + (thisCanvas as HTMLCanvasElement).getContext('webgl') || + (thisCanvas as HTMLCanvasElement).getContext('webgl2') + ) { + const newDiv = document.createElement('div'); + const img = document.createElement('img'); + img.src = (thisCanvas as HTMLCanvasElement).toDataURL('image/png'); + newDiv.appendChild(img); + thisCanvas.parentNode!.replaceChild(newDiv, thisCanvas); + } + }); + } + } + + async printA4Pdf({ title, logo }: { title: string; logo?: string }): Promise { + await this.workaroundWebGLDrivenCanvases(); + return this.page.pdf({ + format: 'a4', + preferCSSPageSize: true, + scale: 1, + landscape: false, + displayHeaderFooter: true, + headerTemplate: await getHeaderTemplate({ title }), + footerTemplate: await getFooterTemplate({ logo }), + }); + } + /* * Call Page.screenshot and return a base64-encoded string of the image */ @@ -359,7 +406,7 @@ export class HeadlessChromiumDriver { // `port` is null in URLs that don't explicitly state it, // however we can derive the port from the protocol (http/https) - // IE: https://feeds-staging.elastic.co/kibana/v8.0.0.json + // IE: https://feeds.elastic.co/kibana/v8.0.0.json const derivedPort = (protocol: string | null, port: string | null, url: string) => { if (port) { return port; diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts index 7d31cdc0c6b8c..bfdc74aa43ba6 100644 --- a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts @@ -114,10 +114,6 @@ export class HeadlessChromiumDriverFactory { const dataDir = getDataPath(); fs.mkdirSync(dataDir, { recursive: true }); this.userDataDir = fs.mkdtempSync(path.join(dataDir, 'chromium-')); - - if (this.config.browser.chromium.disableSandbox) { - logger.warn(`Enabling the Chromium sandbox provides an additional layer of protection.`); - } } private getChromiumArgs() { diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/paths.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/paths.ts index d74313fa5ace1..1a04574155c1e 100644 --- a/x-pack/plugins/screenshotting/server/browsers/chromium/paths.ts +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/paths.ts @@ -67,8 +67,8 @@ export class ChromiumArchivePaths { { platform: 'linux', architecture: 'x64', - archiveFilename: 'chromium-70f5d88-linux_x64.zip', - archiveChecksum: '7b1c9c2fb613444fbdf004a3b75a58df', + archiveFilename: 'chromium-70f5d88-locales-linux_x64.zip', + archiveChecksum: '759bda5e5d32533cb136a85e37c0d102', binaryChecksum: '82e80f9727a88ba3836ce230134bd126', binaryRelativePath: 'headless_shell-linux_x64/headless_shell', location: 'custom', @@ -78,8 +78,8 @@ export class ChromiumArchivePaths { { platform: 'linux', architecture: 'arm64', - archiveFilename: 'chromium-70f5d88-linux_arm64.zip', - archiveChecksum: '4a0217cfe7da86ad1e3d0e9e5895ddb5', + archiveFilename: 'chromium-70f5d88-locales-linux_arm64.zip', + archiveChecksum: '33613b8dc5212c0457210d5a37ea4b43', binaryChecksum: '29e943fbee6d87a217abd6cb6747058e', binaryRelativePath: 'headless_shell-linux_arm64/headless_shell', location: 'custom', diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/templates/footer.handlebars.html b/x-pack/plugins/screenshotting/server/browsers/chromium/templates/footer.handlebars.html new file mode 100644 index 0000000000000..ddd85a50fc6a5 --- /dev/null +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/templates/footer.handlebars.html @@ -0,0 +1,47 @@ + +

+ + + {{#if hasCustomLogo}} +
{{poweredByElasticCopy}}
+ {{/if}} +
+  of  +
+
diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/templates/header.handlebars.html b/x-pack/plugins/screenshotting/server/browsers/chromium/templates/header.handlebars.html new file mode 100644 index 0000000000000..616e5f753b233 --- /dev/null +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/templates/header.handlebars.html @@ -0,0 +1,10 @@ + +{{title}} diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/templates/index.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/templates/index.ts new file mode 100644 index 0000000000000..7034dac76cfca --- /dev/null +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/templates/index.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 { i18n } from '@kbn/i18n'; +import fs from 'fs/promises'; +import path from 'path'; +import Handlebars from 'handlebars'; +import { assetPath } from '../../../constants'; + +async function compileTemplate(pathToTemplate: string): Promise> { + const contentsBuffer = await fs.readFile(pathToTemplate); + return Handlebars.compile(contentsBuffer.toString()); +} + +interface HeaderTemplateInput { + title: string; +} +interface GetHeaderArgs { + title: string; +} + +export async function getHeaderTemplate({ title }: GetHeaderArgs): Promise { + const template = await compileTemplate( + path.resolve(__dirname, './header.handlebars.html') + ); + return template({ title }); +} + +async function getDefaultFooterLogo(): Promise { + const logoBuffer = await fs.readFile(path.resolve(assetPath, 'img', 'logo-grey.png')); + return `data:image/png;base64,${logoBuffer.toString('base64')}`; +} + +interface FooterTemplateInput { + base64FooterLogo: string; + hasCustomLogo: boolean; + poweredByElasticCopy: string; +} + +interface GetFooterArgs { + logo?: string; +} + +export async function getFooterTemplate({ logo }: GetFooterArgs): Promise { + const template = await compileTemplate( + path.resolve(__dirname, './footer.handlebars.html') + ); + const hasCustomLogo = Boolean(logo); + return template({ + base64FooterLogo: hasCustomLogo ? logo! : await getDefaultFooterLogo(), + hasCustomLogo, + poweredByElasticCopy: i18n.translate( + 'xpack.screenshotting.exportTypes.printablePdf.footer.logoDescription', + { + defaultMessage: 'Powered by Elastic', + } + ), + }); +} diff --git a/x-pack/plugins/screenshotting/server/browsers/download/index.test.ts b/x-pack/plugins/screenshotting/server/browsers/download/index.test.ts index 6805996fb1a5a..74a80cf10b58b 100644 --- a/x-pack/plugins/screenshotting/server/browsers/download/index.test.ts +++ b/x-pack/plugins/screenshotting/server/browsers/download/index.test.ts @@ -88,11 +88,11 @@ describe('ensureDownloaded', () => { expect.arrayContaining([ 'chrome-mac.zip', 'chrome-win.zip', - 'chromium-70f5d88-linux_x64.zip', + 'chromium-70f5d88-locales-linux_x64.zip', ]) ); expect(readdirSync(path.resolve(`${paths.archivesPath}/arm64`))).toEqual( - expect.arrayContaining(['chrome-mac.zip', 'chromium-70f5d88-linux_arm64.zip']) + expect.arrayContaining(['chrome-mac.zip', 'chromium-70f5d88-locales-linux_arm64.zip']) ); }); diff --git a/x-pack/plugins/screenshotting/server/cloud/index.ts b/x-pack/plugins/screenshotting/server/cloud/index.ts new file mode 100644 index 0000000000000..9ce60bf13c4bb --- /dev/null +++ b/x-pack/plugins/screenshotting/server/cloud/index.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Logger } from '@kbn/core/server'; +import type { CloudSetup } from '@kbn/cloud-plugin/server'; + +const MIN_CLOUD_MEM_GB: number = 2; +const MIN_CLOUD_MEM_MB: number = MIN_CLOUD_MEM_GB * 1024; + +/** + * If we are on Cloud we need to ensure that we have sufficient memory available, + * if we do not Chromium cannot start. See {@link MIN_CLOUD_MEM_MB}. + * + */ +export function systemHasInsufficientMemory( + cloud: undefined | CloudSetup, + logger: Logger +): boolean { + if (cloud?.isCloudEnabled && typeof cloud?.instanceSizeMb === 'number') { + const instanceSizeMb = cloud.instanceSizeMb; + logger.info(`Memory limit read from cloud (in MB): ${instanceSizeMb}`); + return instanceSizeMb < MIN_CLOUD_MEM_MB; + } + return false; +} diff --git a/x-pack/plugins/screenshotting/server/config/create_config.ts b/x-pack/plugins/screenshotting/server/config/create_config.ts index f12f2205d3a57..1b7076d05e478 100644 --- a/x-pack/plugins/screenshotting/server/config/create_config.ts +++ b/x-pack/plugins/screenshotting/server/config/create_config.ts @@ -24,13 +24,13 @@ export async function createConfig(parentLogger: Logger, config: ConfigType) { // disableSandbox was not set by user, apply default for OS const { os, disableSandbox } = await getDefaultChromiumSandboxDisabled(); - const osName = [os.os, os.dist, os.release].filter(Boolean).map(upperFirst).join(' '); + const osName = [os.os, os.dist, os.release].filter(Boolean).map(upperFirst).join(' ').trim(); logger.debug(`Running on OS: '${osName}'`); if (disableSandbox === true) { logger.warn( - `Chromium sandbox provides an additional layer of protection, but is not supported for ${osName} OS. Automatically setting 'xpack.screenshotting.capture.browser.chromium.disableSandbox: true'.` + `Chromium sandbox provides an additional layer of protection, but is not supported for ${osName} OS. Automatically setting 'xpack.screenshotting.browser.chromium.disableSandbox: true'.` ); } else { logger.info( diff --git a/x-pack/plugins/screenshotting/server/constants.ts b/x-pack/plugins/screenshotting/server/constants.ts new file mode 100644 index 0000000000000..38fde163778ec --- /dev/null +++ b/x-pack/plugins/screenshotting/server/constants.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import path from 'path'; + +export const assetPath = path.resolve(__dirname, 'assets'); diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/index.ts b/x-pack/plugins/screenshotting/server/formats/pdf/index.ts index 7a2453b2a426b..716b2bd46352f 100644 --- a/x-pack/plugins/screenshotting/server/formats/pdf/index.ts +++ b/x-pack/plugins/screenshotting/server/formats/pdf/index.ts @@ -5,13 +5,18 @@ * 2.0. */ -import { groupBy } from 'lodash'; +// FIXME: Once/if we have the ability to get page count directly from Chrome/puppeteer +// we should get rid of this lib. +import * as PDFJS from 'pdfjs-dist/legacy/build/pdf.js'; + import type { Values } from '@kbn/utility-types'; -import type { Logger, PackageInfo } from '@kbn/core/server'; +import { groupBy } from 'lodash'; +import type { PackageInfo } from '@kbn/core/server'; import type { LayoutParams } from '../../../common'; import { LayoutTypes } from '../../../common'; import type { Layout } from '../../layouts'; -import type { CaptureOptions, CaptureResult, CaptureMetrics } from '../../screenshots'; +import type { CaptureMetrics, CaptureOptions, CaptureResult } from '../../screenshots'; +import { EventLogger, Transactions } from '../../screenshots/event_logger'; import { pngsToPdf } from './pdf_maker'; /** @@ -92,35 +97,57 @@ function getTimeRange(results: CaptureResult['results']) { } export async function toPdf( - logger: Logger, + eventLogger: EventLogger, packageInfo: PackageInfo, layout: Layout, { logo, title }: PdfScreenshotOptions, { metrics, results }: CaptureResult ): Promise { - const timeRange = getTimeRange(results); - try { - const { buffer, pages } = await pngsToPdf({ - title: title ? `${title}${timeRange ? ` - ${timeRange}` : ''}` : undefined, - results, - layout, - logo, - packageInfo, - logger, + let buffer: Buffer; + let pages: number; + const shouldConvertPngsToPdf = layout.id !== LayoutTypes.PRINT; + if (shouldConvertPngsToPdf) { + const timeRange = getTimeRange(results); + try { + ({ buffer, pages } = await pngsToPdf({ + title: title ? `${title}${timeRange ? ` - ${timeRange}` : ''}` : undefined, + results, + layout, + logo, + packageInfo, + eventLogger, + })); + + return { + metrics: { + ...(metrics ?? {}), + pages, + }, + data: buffer, + errors: results.flatMap(({ error }) => (error ? [error] : [])), + renderErrors: results.flatMap(({ renderErrors }) => renderErrors ?? []), + }; + } catch (error) { + eventLogger.kbnLogger.error(`Could not generate the PDF buffer!`); + eventLogger.error(error, Transactions.PDF); + throw error; + } + } else { + buffer = results[0].screenshots[0].data; // This buffer is already the PDF + pages = await PDFJS.getDocument({ data: buffer }).promise.then((doc) => { + const numPages = doc.numPages; + doc.destroy(); + return numPages; }); - - return { - metrics: { - ...(metrics ?? {}), - pages, - }, - data: buffer, - errors: results.flatMap(({ error }) => (error ? [error] : [])), - renderErrors: results.flatMap(({ renderErrors }) => renderErrors ?? []), - }; - } catch (error) { - logger.error(`Could not generate the PDF buffer!`); - - throw error; } + + return { + metrics: { + ...(metrics ?? {}), + pages, + }, + data: buffer, + errors: results.flatMap(({ error }) => (error ? [error] : [])), + renderErrors: results.flatMap(({ renderErrors }) => renderErrors ?? []), + }; } diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/constants.ts b/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/constants.ts index 3e44a53a7f3c0..03192aacd887f 100644 --- a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/constants.ts +++ b/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/constants.ts @@ -5,9 +5,8 @@ * 2.0. */ -import path from 'path'; +import { assetPath } from '../../../constants'; -export const assetPath = path.resolve(__dirname, 'assets'); export const tableBorderWidth = 1; export const pageMarginTop = 40; export const pageMarginBottom = 80; @@ -21,3 +20,4 @@ export const subheadingMarginTop = 0; export const subheadingMarginBottom = 5; export const subheadingHeight = subheadingFontSize * 1.5 + subheadingMarginTop + subheadingMarginBottom; +export { assetPath }; diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/index.ts b/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/index.ts index be69ec4c5e141..280b9173c7920 100644 --- a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/index.ts +++ b/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/index.ts @@ -5,17 +5,17 @@ * 2.0. */ -import type { Logger, PackageInfo } from '@kbn/core/server'; -import { PdfMaker } from './pdfmaker'; +import type { PackageInfo } from '@kbn/core/server'; import type { Layout } from '../../../layouts'; -import { getTracker } from './tracker'; import type { CaptureResult } from '../../../screenshots'; +import { Actions, EventLogger, Transactions } from '../../../screenshots/event_logger'; +import { PdfMaker } from './pdfmaker'; interface PngsToPdfArgs { results: CaptureResult['results']; layout: Layout; packageInfo: PackageInfo; - logger: Logger; + eventLogger: EventLogger; logo?: string; title?: string; } @@ -26,37 +26,43 @@ export async function pngsToPdf({ logo, title, packageInfo, - logger, + eventLogger, }: PngsToPdfArgs): Promise<{ buffer: Buffer; pages: number }> { - const pdfMaker = new PdfMaker(layout, logo, packageInfo, logger); - const tracker = getTracker(); - if (title) { - pdfMaker.setTitle(title); - } - results.forEach((result) => { - result.screenshots.forEach((png) => { - tracker.startAddImage(); - pdfMaker.addImage(png.data, { - title: png.title ?? undefined, - description: png.description ?? undefined, - }); - tracker.endAddImage(); - }); - }); + const { kbnLogger } = eventLogger; + const transactionEnd = eventLogger.startTransaction(Transactions.PDF); let buffer: Uint8Array | null = null; + let pdfMaker: PdfMaker | null = null; try { - tracker.startCompile(); + pdfMaker = new PdfMaker(layout, logo, packageInfo, kbnLogger); + if (title) { + pdfMaker.setTitle(title); + } + results.forEach((result) => { + result.screenshots.forEach((png) => { + const spanEnd = eventLogger.logPdfEvent( + 'add image to PDF file', + Actions.ADD_IMAGE, + 'output' + ); + pdfMaker?.addImage(png.data, { + title: png.title ?? undefined, + description: png.description ?? undefined, + }); + spanEnd(); + }); + }); + + const spanEnd = eventLogger.logPdfEvent('compile PDF file', Actions.COMPILE, 'output'); buffer = await pdfMaker.generate(); - tracker.endCompile(); + spanEnd(); const byteLength = buffer?.byteLength ?? 0; - logger.debug(`PDF buffer byte length: ${byteLength}`); - tracker.setByteLength(byteLength); - } catch (err) { - throw err; - } finally { - tracker.end(); + transactionEnd({ labels: { byte_length_pdf: byteLength, pdf_pages: pdfMaker.getPageCount() } }); + } catch (error) { + kbnLogger.error(error); + eventLogger.error(error, Actions.COMPILE); + throw error; } return { buffer: Buffer.from(buffer.buffer), pages: pdfMaker.getPageCount() }; diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/pdfmaker.ts b/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/pdfmaker.ts index afd9e294e9ae0..7dd964594ca53 100644 --- a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/pdfmaker.ts +++ b/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/pdfmaker.ts @@ -83,6 +83,8 @@ export class PdfMaker { const groupCount = this.content.length; // inject a page break for every 2 groups on the page + // TODO: Remove this code since we are now using Chromium to drive this + // layout via native print functionality. if (groupCount > 0 && groupCount % this.layout.groupCount === 0) { contents = [ { diff --git a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/tracker.ts b/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/tracker.ts deleted file mode 100644 index 49576a03d18a3..0000000000000 --- a/x-pack/plugins/screenshotting/server/formats/pdf/pdf_maker/tracker.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import apm from 'elastic-apm-node'; - -interface PdfTracker { - setByteLength: (byteLength: number) => void; - startAddImage: () => void; - endAddImage: () => void; - startCompile: () => void; - endCompile: () => void; - end: () => void; -} - -const TRANSACTION_TYPE = 'reporting'; // TODO: Find out whether we can rename to "screenshotting"; -const SPANTYPE_OUTPUT = 'output'; - -interface ApmSpan { - end: () => void; -} - -export function getTracker(): PdfTracker { - const apmTrans = apm.startTransaction('generate-pdf', TRANSACTION_TYPE); - - let apmAddImage: ApmSpan | null = null; - let apmCompilePdf: ApmSpan | null = null; - - return { - startAddImage() { - apmAddImage = apmTrans?.startSpan('add-pdf-image', SPANTYPE_OUTPUT) || null; - }, - endAddImage() { - apmAddImage?.end(); - }, - startCompile() { - apmCompilePdf = apmTrans?.startSpan('compile-pdf', SPANTYPE_OUTPUT) || null; - }, - endCompile() { - apmCompilePdf?.end(); - }, - setByteLength(byteLength: number) { - apmTrans?.setLabel('byte-length', byteLength, false); - }, - end() { - apmTrans?.end(); - }, - }; -} diff --git a/x-pack/plugins/screenshotting/server/plugin.ts b/x-pack/plugins/screenshotting/server/plugin.ts index 23f688206d394..144b88a2c1c75 100755 --- a/x-pack/plugins/screenshotting/server/plugin.ts +++ b/x-pack/plugins/screenshotting/server/plugin.ts @@ -16,6 +16,7 @@ import type { PluginInitializerContext, } from '@kbn/core/server'; import type { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/server'; +import type { CloudSetup } from '@kbn/cloud-plugin/server'; import { ChromiumArchivePaths, HeadlessChromiumDriverFactory, install } from './browsers'; import { ConfigType, createConfig } from './config'; import { Screenshots } from './screenshots'; @@ -23,6 +24,7 @@ import { getChromiumPackage } from './utils'; interface SetupDeps { screenshotMode: ScreenshotModePluginSetup; + cloud?: CloudSetup; } /** @@ -57,7 +59,7 @@ export class ScreenshottingPlugin implements Plugin { const paths = new ChromiumArchivePaths(); @@ -83,9 +85,14 @@ export class ScreenshottingPlugin implements Plugin { const browserDriverFactory = await this.browserDriverFactory; - const logger = this.logger.get('screenshot'); - - return new Screenshots(browserDriverFactory, logger, this.packageInfo, http, this.config); + return new Screenshots( + browserDriverFactory, + this.logger, + this.packageInfo, + http, + this.config, + cloud + ); })(); // Already handled in `browserDriverFactory` this.screenshots.catch(() => {}); diff --git a/x-pack/plugins/screenshotting/server/screenshots/__snapshots__/index.test.ts.snap b/x-pack/plugins/screenshotting/server/screenshots/__snapshots__/index.test.ts.snap index c0971c9b95763..1b3826ce9980d 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/__snapshots__/index.test.ts.snap +++ b/x-pack/plugins/screenshotting/server/screenshots/__snapshots__/index.test.ts.snap @@ -20,7 +20,7 @@ Array [ }, }, ], - "error": [Error: The "wait for elements" phase encountered an error: Error: An error occurred when trying to read the page for visualization panel info: Mock error!], + "error": [Error: The "wait for elements" phase encountered an error: Error: Mock error!], "renderErrors": undefined, "screenshots": Array [ Object { @@ -63,7 +63,7 @@ Array [ }, }, ], - "error": [Error: The "wait for elements" phase encountered an error: Error: An error occurred when trying to read the page for visualization panel info: Mock error!], + "error": [Error: The "wait for elements" phase encountered an error: Error: Mock error!], "renderErrors": undefined, "screenshots": Array [ Object { diff --git a/x-pack/plugins/screenshotting/server/screenshots/event_logger/index.test.ts b/x-pack/plugins/screenshotting/server/screenshots/event_logger/index.test.ts new file mode 100644 index 0000000000000..3a20c404ff497 --- /dev/null +++ b/x-pack/plugins/screenshotting/server/screenshots/event_logger/index.test.ts @@ -0,0 +1,261 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { Actions, EventLogger, ScreenshottingAction, Transactions } from '.'; +import { ElementPosition } from '../get_element_position_data'; +import { ConfigType } from '../../config'; + +jest.mock('uuid', () => ({ + v4: () => 'NEW_UUID', +})); + +type EventLoggerArgs = [message: string, meta: ScreenshottingAction]; +describe('Event Logger', () => { + let eventLogger: EventLogger; + let config: ConfigType; + let logSpy: jest.SpyInstance; + + beforeEach(() => { + const testDate = moment(new Date('2021-04-12T16:00:00.000Z')); + let delaySeconds = 1; + + jest.spyOn(global.Date, 'now').mockImplementation(() => { + return testDate.add(delaySeconds++, 'seconds').valueOf(); + }); + + const logger = loggingSystemMock.createLogger(); + config = { capture: { zoom: 2 } } as ConfigType; + eventLogger = new EventLogger(logger, config); + + logSpy = jest.spyOn(logger, 'debug') as jest.SpyInstance; + }); + + it('creates logs for the events and includes durations and event payload data', () => { + const screenshottingEnd = eventLogger.startTransaction(Transactions.SCREENSHOTTING); + const openUrlEnd = eventLogger.logScreenshottingEvent( + 'open the url to the Kibana application', + Actions.OPEN_URL, + 'wait' + ); + openUrlEnd(); + const getElementPositionsEnd = eventLogger.logScreenshottingEvent( + 'scan the page to find the boundaries of visualization elements', + Actions.GET_ELEMENT_POSITION_DATA, + 'wait' + ); + getElementPositionsEnd(); + screenshottingEnd({ + labels: { + cpu: 12, + cpu_percentage: 0, + memory: 450789, + memory_mb: 449, + byte_length: 14000, + }, + }); + + const pdfEnd = eventLogger.startTransaction(Transactions.PDF); + const addImageEnd = eventLogger.logPdfEvent( + 'add image to the PDF file', + Actions.ADD_IMAGE, + 'output' + ); + addImageEnd(); + pdfEnd({ labels: { pdf_pages: 1, byte_length_pdf: 6666 } }); + + const logs = logSpy.mock.calls.map(([message, data]) => ({ + message, + duration: data?.event?.duration, + screenshotting: data?.kibana?.screenshotting, + })); + + expect(logs.length).toBe(10); + expect(logs).toMatchInlineSnapshot(` + Array [ + Object { + "duration": undefined, + "message": "starting: screenshot-pipeline", + "screenshotting": Object { + "action": "screenshot-pipeline-start", + "session_id": "NEW_UUID", + }, + }, + Object { + "duration": undefined, + "message": "starting: open the url to the Kibana application", + "screenshotting": Object { + "action": "open-url-start", + "session_id": "NEW_UUID", + }, + }, + Object { + "duration": 3000, + "message": "completed: open the url to the Kibana application", + "screenshotting": Object { + "action": "open-url-complete", + "session_id": "NEW_UUID", + }, + }, + Object { + "duration": undefined, + "message": "starting: scan the page to find the boundaries of visualization elements", + "screenshotting": Object { + "action": "get-element-position-data-start", + "session_id": "NEW_UUID", + }, + }, + Object { + "duration": 5000, + "message": "completed: scan the page to find the boundaries of visualization elements", + "screenshotting": Object { + "action": "get-element-position-data-complete", + "session_id": "NEW_UUID", + }, + }, + Object { + "duration": 20000, + "message": "completed: screenshot-pipeline", + "screenshotting": Object { + "action": "screenshot-pipeline-complete", + "byte_length": 14000, + "cpu": 12, + "cpu_percentage": 0, + "memory": 450789, + "memory_mb": 449, + "session_id": "NEW_UUID", + }, + }, + Object { + "duration": undefined, + "message": "starting: generate-pdf", + "screenshotting": Object { + "action": "generate-pdf-start", + "session_id": "NEW_UUID", + }, + }, + Object { + "duration": undefined, + "message": "starting: add image to the PDF file", + "screenshotting": Object { + "action": "add-pdf-image-start", + "session_id": "NEW_UUID", + }, + }, + Object { + "duration": 9000, + "message": "completed: add image to the PDF file", + "screenshotting": Object { + "action": "add-pdf-image-complete", + "session_id": "NEW_UUID", + }, + }, + Object { + "duration": 27000, + "message": "completed: generate-pdf", + "screenshotting": Object { + "action": "generate-pdf-complete", + "byte_length_pdf": 6666, + "pdf_pages": 1, + "session_id": "NEW_UUID", + }, + }, + ] + `); + }); + + it('logs the number of pixels', () => { + const elementPosition = { + boundingClientRect: { width: 1350, height: 2000 }, + scroll: {}, + } as ElementPosition; + const endScreenshot = eventLogger.logScreenshottingEvent( + 'screenshot capture test', + Actions.GET_SCREENSHOT, + 'read', + eventLogger.getPixelsFromElementPosition(elementPosition) + ); + endScreenshot({ byte_length: 4444 }); + + const logData = logSpy.mock.calls.map(([message, data]) => ({ + message, + duration: data.event?.duration, + screenshotting: data.kibana.screenshotting, + })); + + expect(logData).toMatchInlineSnapshot(` + Array [ + Object { + "duration": undefined, + "message": "starting: screenshot capture test", + "screenshotting": Object { + "action": "get-screenshots-start", + "pixels": 10800000, + "session_id": "NEW_UUID", + }, + }, + Object { + "duration": 2000, + "message": "completed: screenshot capture test", + "screenshotting": Object { + "action": "get-screenshots-complete", + "byte_length": 4444, + "pixels": 10800000, + "session_id": "NEW_UUID", + }, + }, + ] + `); + }); + + it('creates helpful error logs', () => { + eventLogger.startTransaction(Transactions.SCREENSHOTTING); + eventLogger.logScreenshottingEvent('opening the url', Actions.OPEN_URL, 'wait'); + eventLogger.error(new Error('Something erroneous happened'), Transactions.SCREENSHOTTING); + + const logData = logSpy.mock.calls.map(([message, data]) => ({ + message, + error: data.error, + screenshotting: data.kibana.screenshotting, + })); + + expect(logData).toMatchInlineSnapshot(` + Array [ + Object { + "error": undefined, + "message": "starting: screenshot-pipeline", + "screenshotting": Object { + "action": "screenshot-pipeline-start", + "session_id": "NEW_UUID", + }, + }, + Object { + "error": undefined, + "message": "starting: opening the url", + "screenshotting": Object { + "action": "open-url-start", + "session_id": "NEW_UUID", + }, + }, + Object { + "error": Object { + "code": undefined, + "message": "Something erroneous happened", + "stack_trace": undefined, + "type": undefined, + }, + "message": "Error: Something erroneous happened", + "screenshotting": Object { + "action": "screenshot-pipeline-error", + "session_id": "NEW_UUID", + }, + }, + ] + `); + }); +}); diff --git a/x-pack/plugins/screenshotting/server/screenshots/event_logger/index.ts b/x-pack/plugins/screenshotting/server/screenshots/event_logger/index.ts new file mode 100644 index 0000000000000..64027ffbd3cf2 --- /dev/null +++ b/x-pack/plugins/screenshotting/server/screenshots/event_logger/index.ts @@ -0,0 +1,316 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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, LogMeta } from '@kbn/core/server'; +import apm from 'elastic-apm-node'; +import uuid from 'uuid'; +import { CaptureResult } from '..'; +import { PLUGIN_ID } from '../../../common'; +import { ConfigType } from '../../config'; +import { ElementPosition } from '../get_element_position_data'; +import type { Screenshot } from '../types'; + +export enum Actions { + OPEN_URL = 'open-url', + GET_ELEMENT_POSITION_DATA = 'get-element-position-data', + GET_NUMBER_OF_ITEMS = 'get-number-of-items', + GET_RENDER_ERRORS = 'get-render-errors', + GET_TIMERANGE = 'get-timerange', + INJECT_CSS = 'inject-css', + REPOSITION = 'position-elements', + WAIT_RENDER = 'wait-for-render', + WAIT_VISUALIZATIONS = 'wait-for-visualizations', + GET_SCREENSHOT = 'get-screenshots', + PRINT_A4_PDF = 'print-a4-pdf', + ADD_IMAGE = 'add-pdf-image', + COMPILE = 'compile-pdf', +} + +export enum Transactions { + SCREENSHOTTING = 'screenshot-pipeline', + PDF = 'generate-pdf', +} + +export type SpanTypes = 'setup' | 'read' | 'wait' | 'correction' | 'output'; + +export interface ScreenshottingAction extends LogMeta { + event?: { + duration?: number; // number of nanoseconds from begin to end of an event + provider: typeof PLUGIN_ID; + }; + + message: string; + kibana: { + screenshotting: { + action: Actions | Transactions; + session_id: string; + + // chromium stats + cpu?: number; + cpu_percentage?: number; + memory?: number; + memory_mb?: number; + + // screenshotting stats + items_count?: number; + pixels?: number; + byte_length?: number; + element_positions?: number; + render_errors?: number; + + // pdf stats + byte_length_pdf?: number; + pdf_pages?: number; + }; + }; +} + +interface ErrorAction { + message: string; + code?: string; + stack_trace?: string; + type?: string; +} + +type SimpleEvent = Omit; + +type LogAdapter = ( + message: string, + suffix: 'start' | 'complete' | 'error', + event: Partial, + startTime?: Date | undefined +) => void; + +type Labels = Record; +type TransactionEndFn = (args: { labels: Partial }) => void; +type LogEndFn = (metricData?: Partial) => void; + +function fillLogData( + message: string, + event: Partial, + suffix: 'start' | 'complete' | 'error', + sessionId: string, + duration: number | undefined +) { + let newMessage = message; + if (suffix !== 'error') { + newMessage = `${suffix === 'start' ? 'starting' : 'completed'}: ${message}`; + } + + let interpretedAction: string; + if (suffix === 'error') { + interpretedAction = event.action + '-error'; + } else { + interpretedAction = event.action + `-${suffix}`; + } + + const logData: ScreenshottingAction = { + message: newMessage, + kibana: { + screenshotting: { + ...event, + action: interpretedAction as Actions, + session_id: sessionId, + }, + }, + event: { duration, provider: PLUGIN_ID }, + }; + return logData; +} + +function logAdapter(logger: Logger, sessionId: string) { + const log: LogAdapter = (message, suffix, event, startTime) => { + let duration: number | undefined; + if (startTime != null) { + const start = startTime.valueOf(); + duration = new Date(Date.now()).valueOf() - start.valueOf(); + } + + const logData = fillLogData(message, event, suffix, sessionId, duration); + logger.debug(logData.message, logData); + }; + return log; +} + +/** + * A class to use internal state properties to log timing between actions in the screenshotting pipeline + */ +export class EventLogger { + private spans = new Map(); + private transactions: Record = { + 'screenshot-pipeline': null, + 'generate-pdf': null, + }; + + private sessionId: string; // identifier to track all logs from one screenshotting flow + private logEvent: LogAdapter; + private timings: Partial> = {}; + + constructor(private readonly logger: Logger, private readonly config: ConfigType) { + this.sessionId = uuid.v4(); + this.logEvent = logAdapter(logger.get('events'), this.sessionId); + } + + private startTiming(a: Actions | Transactions) { + this.timings[a] = new Date(Date.now()); + } + + /** + * @returns Logger - original logger + */ + public get kbnLogger() { + return this.logger; + } + + /** + * General method for logging the beginning of any of this plugin's pipeline + * + * @returns {ScreenshottingEndFn} + */ + public startTransaction( + action: Transactions.SCREENSHOTTING | Transactions.PDF + ): TransactionEndFn { + this.transactions[action] = apm.startTransaction(action, PLUGIN_ID); + const transaction = this.transactions[action]; + + this.startTiming(action); + this.logEvent(action, 'start', { action }); + + return ({ labels }) => { + Object.entries(labels).forEach(([label]) => { + const labelField = label as keyof SimpleEvent; + const labelValue = labels[labelField]; + transaction?.setLabel(label, labelValue, false); + }); + + transaction?.end(); + + this.logEvent(action, 'complete', { ...labels, action }, this.timings[action]); + }; + } + + /** + * General event logging function + * + * @param {string} message + * @param {Actions} action - action type for kibana.screenshotting.action + * @param {TransactionType} transaction - name of the internal APM transaction in which to associate the span + * @param {SpanTypes} type - identifier of the span type + * @param {metricsPre} type - optional metrics to add to the "start" log of the event + * @returns {LogEndFn} - function to log the end of the span + */ + public log( + message: string, + action: Actions, + type: SpanTypes, + metricsPre: Partial = {}, + transaction: Transactions + ): LogEndFn { + const txn = this.transactions[transaction]; + const span = txn?.startSpan(action, type); + + this.spans.set(action, span); + this.startTiming(action); + this.logEvent(message, 'start', { ...metricsPre, action }); + + return (metricData = {}) => { + span?.end(); + this.logEvent( + message, + 'complete', + { ...metricsPre, ...metricData, action }, + this.timings[action] + ); + }; + } + + /** + * Logging helper for screenshotting events + */ + public logScreenshottingEvent( + message: string, + action: Actions, + type: SpanTypes, + metricsPre: Partial = {} + ) { + return this.log(message, action, type, metricsPre, Transactions.SCREENSHOTTING); + } + + /** + * Logging helper for screenshotting events + */ + public logPdfEvent( + message: string, + action: Actions, + type: SpanTypes, + metricsPre: Partial = {} + ) { + return this.log(message, action, type, metricsPre, Transactions.PDF); + } + + /** + * Helper function to calculate the byte length of a set of captured PNG images + */ + public getByteLengthFromCaptureResults( + results: CaptureResult['results'] + ): Pick { + const totalByteLength = results.reduce( + (totals, { screenshots }) => + totals + + screenshots.reduce( + (byteLength: number, screenshot: Screenshot) => byteLength + screenshot.data.byteLength, + 0 + ), + 0 + ); + return { byte_length: totalByteLength }; + } + + /** + * Helper function to create the "metricPre" data needed to log the start + * of a screenshot capture event. + */ + public getPixelsFromElementPosition( + elementPosition: ElementPosition + ): Pick { + const { width, height } = elementPosition.boundingClientRect; + const zoom = this.config.capture.zoom; + const pixels = width * zoom * (height * zoom); + return { pixels }; + } + + /** + * General error logger + * + * @param {ErrorAction} error: The error object that was caught + * @param {Actions} action: The screenshotting action type + * @returns void + */ + public error(error: ErrorAction | string, action: Actions | Transactions) { + const isError = typeof error === 'object'; + const message = `Error: ${isError ? error.message : error}`; + + const errorData = { + ...fillLogData( + message, + { action }, + 'error', + this.sessionId, + undefined // + ), + error: { + message: isError ? error.message : error, + code: isError ? error.code : undefined, + stack_trace: isError ? error.stack_trace : undefined, + type: isError ? error.type : undefined, + }, + }; + + this.logger.get('events').debug(message, errorData); + apm.captureError(error as Error | string); + } +} diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.test.ts b/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.test.ts index 915b036acf22e..f3a76ca79d85f 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.test.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.test.ts @@ -5,20 +5,24 @@ * 2.0. */ -import type { Logger } from '@kbn/core/server'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; import { createMockBrowserDriver } from '../browsers/mock'; +import { ConfigType } from '../config'; import { createMockLayout } from '../layouts/mock'; +import { EventLogger } from './event_logger'; import { getElementPositionAndAttributes } from './get_element_position_data'; describe('getElementPositionAndAttributes', () => { - const logger = {} as jest.Mocked; let browser: ReturnType; let layout: ReturnType; + let eventLogger: EventLogger; + let config = {} as ConfigType; beforeEach(async () => { browser = createMockBrowserDriver(); layout = createMockLayout(); - + config = { capture: { zoom: 2 } } as ConfigType; + eventLogger = new EventLogger(loggingSystemMock.createLogger(), config); browser.evaluate.mockImplementation(({ fn, args }) => (fn as Function)(...args)); // @see https://github.com/jsdom/jsdom/issues/653 @@ -59,7 +63,7 @@ describe('getElementPositionAndAttributes', () => { /> `; - await expect(getElementPositionAndAttributes(browser, logger, layout)).resolves + await expect(getElementPositionAndAttributes(browser, eventLogger, layout)).resolves .toMatchInlineSnapshot(` Array [ Object { @@ -103,6 +107,6 @@ describe('getElementPositionAndAttributes', () => { }); it('should return null when there are no elements matching', async () => { - await expect(getElementPositionAndAttributes(browser, logger, layout)).resolves.toBeNull(); + await expect(getElementPositionAndAttributes(browser, eventLogger, layout)).resolves.toBeNull(); }); }); diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.ts b/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.ts index e3235c6d23253..5018701ce2411 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.ts @@ -5,11 +5,10 @@ * 2.0. */ -import apm from 'elastic-apm-node'; -import type { Logger } from '@kbn/core/server'; import type { HeadlessChromiumDriver } from '../browsers'; import { Layout } from '../layouts'; import { CONTEXT_ELEMENTATTRIBUTES } from './constants'; +import { Actions, EventLogger } from './event_logger'; export interface AttributesMap { [key: string]: string | null; @@ -36,10 +35,17 @@ export interface ElementsPositionAndAttribute { export const getElementPositionAndAttributes = async ( browser: HeadlessChromiumDriver, - logger: Logger, + eventLogger: EventLogger, layout: Layout ): Promise => { - const span = apm.startSpan('get_element_position_data', 'read'); + const { kbnLogger } = eventLogger; + + const spanEnd = eventLogger.logScreenshottingEvent( + 'get element position data', + Actions.GET_ELEMENT_POSITION_DATA, + 'read' + ); + const { screenshot: screenshotSelector } = layout.selectors; // data-shared-items-container let elementsPositionAndAttributes: ElementsPositionAndAttribute[] | null; try { @@ -77,7 +83,7 @@ export const getElementPositionAndAttributes = async ( args: [screenshotSelector, { title: 'data-title', description: 'data-description' }], }, { context: CONTEXT_ELEMENTATTRIBUTES }, - logger + kbnLogger ); if (!elementsPositionAndAttributes?.length) { @@ -86,10 +92,13 @@ export const getElementPositionAndAttributes = async ( ); } } catch (err) { + kbnLogger.error(err); + eventLogger.error(err, Actions.GET_ELEMENT_POSITION_DATA); elementsPositionAndAttributes = null; + // no throw } - span?.end(); + spanEnd({ element_positions: elementsPositionAndAttributes?.length }); return elementsPositionAndAttributes; }; diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_number_of_items.test.ts b/x-pack/plugins/screenshotting/server/screenshots/get_number_of_items.test.ts index 34b8291eb03da..a7c4f27065bcf 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/get_number_of_items.test.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/get_number_of_items.test.ts @@ -5,22 +5,25 @@ * 2.0. */ -import type { Logger } from '@kbn/core/server'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; import { createMockBrowserDriver } from '../browsers/mock'; +import { ConfigType } from '../config'; import { createMockLayout } from '../layouts/mock'; +import { EventLogger } from './event_logger'; import { getNumberOfItems } from './get_number_of_items'; describe('getNumberOfItems', () => { const timeout = 10; let browser: ReturnType; let layout: ReturnType; - let logger: jest.Mocked; + let eventLogger: EventLogger; + let config = {} as ConfigType; beforeEach(async () => { browser = createMockBrowserDriver(); layout = createMockLayout(); - logger = { debug: jest.fn() } as unknown as jest.Mocked; - + config = { capture: { zoom: 2 } } as ConfigType; + eventLogger = new EventLogger(loggingSystemMock.createLogger(), config); browser.evaluate.mockImplementation(({ fn, args }) => (fn as Function)(...args)); }); @@ -33,7 +36,7 @@ describe('getNumberOfItems', () => {
`; - await expect(getNumberOfItems(browser, logger, timeout, layout)).resolves.toBe(10); + await expect(getNumberOfItems(browser, eventLogger, timeout, layout)).resolves.toBe(10); }); it('should determine the number of items by selector ', async () => { @@ -43,7 +46,7 @@ describe('getNumberOfItems', () => { `; - await expect(getNumberOfItems(browser, logger, timeout, layout)).resolves.toBe(3); + await expect(getNumberOfItems(browser, eventLogger, timeout, layout)).resolves.toBe(3); }); it('should fall back to the selector when the attribute is empty', async () => { @@ -53,6 +56,6 @@ describe('getNumberOfItems', () => { `; - await expect(getNumberOfItems(browser, logger, timeout, layout)).resolves.toBe(2); + await expect(getNumberOfItems(browser, eventLogger, timeout, layout)).resolves.toBe(2); }); }); diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_number_of_items.ts b/x-pack/plugins/screenshotting/server/screenshots/get_number_of_items.ts index 9dab001e4730d..0e4da2fe5cf6a 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/get_number_of_items.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/get_number_of_items.ts @@ -5,24 +5,27 @@ * 2.0. */ -import apm from 'elastic-apm-node'; -import type { Logger } from '@kbn/core/server'; import type { HeadlessChromiumDriver } from '../browsers'; import { Layout } from '../layouts'; import { CONTEXT_GETNUMBEROFITEMS, CONTEXT_READMETADATA } from './constants'; +import { Actions, EventLogger } from './event_logger'; export const getNumberOfItems = async ( browser: HeadlessChromiumDriver, - logger: Logger, + eventLogger: EventLogger, timeout: number, layout: Layout ): Promise => { - const span = apm.startSpan('get_number_of_items', 'read'); + const { kbnLogger } = eventLogger; + const spanEnd = eventLogger.logScreenshottingEvent( + 'get the number of visualization items on the page', + Actions.GET_NUMBER_OF_ITEMS, + 'read' + ); + const { renderComplete: renderCompleteSelector, itemsCountAttribute } = layout.selectors; let itemsCount: number; - logger.debug('waiting for elements or items count attribute; or not found to interrupt'); - try { // the dashboard is using the `itemsCountAttribute` attribute to let us // know how many items to expect since gridster incrementally adds panels @@ -31,7 +34,7 @@ export const getNumberOfItems = async ( `${renderCompleteSelector},[${itemsCountAttribute}]`, { timeout }, { context: CONTEXT_READMETADATA }, - logger + kbnLogger ); // returns the value of the `itemsCountAttribute` if it's there, otherwise @@ -52,16 +55,15 @@ export const getNumberOfItems = async ( args: [renderCompleteSelector, itemsCountAttribute], }, { context: CONTEXT_GETNUMBEROFITEMS }, - logger + kbnLogger ); } catch (error) { - logger.error(error); - throw new Error( - `An error occurred when trying to read the page for visualization panel info: ${error.message}` - ); + kbnLogger.error(error); + eventLogger.error(error, Actions.GET_NUMBER_OF_ITEMS); + throw error; } - span?.end(); + spanEnd({ items_count: itemsCount }); return itemsCount; }; diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_pdf.ts b/x-pack/plugins/screenshotting/server/screenshots/get_pdf.ts new file mode 100644 index 0000000000000..026d62ada876c --- /dev/null +++ b/x-pack/plugins/screenshotting/server/screenshots/get_pdf.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 { Actions, EventLogger } from './event_logger'; +import type { HeadlessChromiumDriver } from '../browsers'; +import type { Screenshot } from './types'; + +export async function getPdf( + browser: HeadlessChromiumDriver, + logger: EventLogger, + title: string, + logo?: string +): Promise { + logger.kbnLogger.info('printing PDF'); + + const spanEnd = logger.logPdfEvent('printing A4 PDF', Actions.PRINT_A4_PDF, 'output'); + + const result = [ + { + data: await browser.printA4Pdf({ title, logo }), + title: null, + description: null, + }, + ]; + + spanEnd(); + + return result; +} diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_render_errors.test.ts b/x-pack/plugins/screenshotting/server/screenshots/get_render_errors.test.ts index a475e3c614c15..ece25b37725c8 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/get_render_errors.test.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/get_render_errors.test.ts @@ -5,21 +5,24 @@ * 2.0. */ -import type { Logger } from '@kbn/core/server'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; import { createMockBrowserDriver } from '../browsers/mock'; +import { ConfigType } from '../config'; import { createMockLayout } from '../layouts/mock'; +import { EventLogger } from './event_logger'; import { getRenderErrors } from './get_render_errors'; describe('getRenderErrors', () => { let browser: ReturnType; let layout: ReturnType; - let logger: jest.Mocked; + let eventLogger: EventLogger; + let config = {} as ConfigType; beforeEach(async () => { browser = createMockBrowserDriver(); layout = createMockLayout(); - logger = { debug: jest.fn(), warn: jest.fn() } as unknown as jest.Mocked; - + config = { capture: { zoom: 2 } } as ConfigType; + eventLogger = new EventLogger(loggingSystemMock.createLogger(), config); browser.evaluate.mockImplementation(({ fn, args }) => (fn as Function)(...args)); }); @@ -35,7 +38,7 @@ describe('getRenderErrors', () => {
`; - await expect(getRenderErrors(browser, logger, layout)).resolves.toEqual([ + await expect(getRenderErrors(browser, eventLogger, layout)).resolves.toEqual([ 'a test error', 'a test error', 'a test error', @@ -48,6 +51,6 @@ describe('getRenderErrors', () => { `; - await expect(getRenderErrors(browser, logger, layout)).resolves.toEqual(undefined); + await expect(getRenderErrors(browser, eventLogger, layout)).resolves.toEqual(undefined); }); }); diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_render_errors.ts b/x-pack/plugins/screenshotting/server/screenshots/get_render_errors.ts index e8beb18911210..44b92ceddbc8d 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/get_render_errors.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/get_render_errors.ts @@ -5,45 +5,59 @@ * 2.0. */ -import apm from 'elastic-apm-node'; -import type { Logger } from '@kbn/core/server'; import type { HeadlessChromiumDriver } from '../browsers'; import type { Layout } from '../layouts'; import { CONTEXT_GETRENDERERRORS } from './constants'; +import { Actions, EventLogger } from './event_logger'; export const getRenderErrors = async ( browser: HeadlessChromiumDriver, - logger: Logger, + eventLogger: EventLogger, layout: Layout ): Promise => { - const span = apm.startSpan('get_render_errors', 'read'); - logger.debug('reading render errors'); - const errorsFound: undefined | string[] = await browser.evaluate( - { - fn: (errorSelector, errorAttribute) => { - const visualizations: Element[] = Array.from(document.querySelectorAll(errorSelector)); - const errors: string[] = []; - - visualizations.forEach((visualization) => { - const errorMessage = visualization.getAttribute(errorAttribute); - if (errorMessage) { - errors.push(errorMessage); - } - }); - - return errors.length ? errors : undefined; - }, - args: [layout.selectors.renderError, layout.selectors.renderErrorAttribute], - }, - { context: CONTEXT_GETRENDERERRORS }, - logger + const { kbnLogger } = eventLogger; + + const spanEnd = eventLogger.logScreenshottingEvent( + 'look for render errors', + Actions.GET_RENDER_ERRORS, + 'read' ); - span?.end(); - if (errorsFound?.length) { - logger.warn( - `Found ${errorsFound.length} error messages. See report object for more information.` + let errorsFound: undefined | string[]; + try { + errorsFound = await browser.evaluate( + { + fn: (errorSelector, errorAttribute) => { + const visualizations: Element[] = Array.from(document.querySelectorAll(errorSelector)); + const errors: string[] = []; + + visualizations.forEach((visualization) => { + const errorMessage = visualization.getAttribute(errorAttribute); + if (errorMessage) { + errors.push(errorMessage); + } + }); + + return errors.length ? errors : undefined; + }, + args: [layout.selectors.renderError, layout.selectors.renderErrorAttribute], + }, + { context: CONTEXT_GETRENDERERRORS }, + kbnLogger ); + + const renderErrors = errorsFound?.length; + if (renderErrors) { + kbnLogger.warn( + `Found ${renderErrors} error messages. See report object for more information.` + ); + } + + spanEnd({ render_errors: renderErrors }); + } catch (error) { + kbnLogger.error(error); + eventLogger.error(error, Actions.GET_RENDER_ERRORS); + throw error; } return errorsFound; diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.test.ts b/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.test.ts index 1f104b9bf2d80..c2342280aea20 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.test.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.test.ts @@ -5,8 +5,10 @@ * 2.0. */ -import type { Logger } from '@kbn/core/server'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; import { createMockBrowserDriver } from '../browsers/mock'; +import { ConfigType } from '../config'; +import { EventLogger } from './event_logger'; import { getScreenshots } from './get_screenshots'; describe('getScreenshots', () => { @@ -27,12 +29,13 @@ describe('getScreenshots', () => { }, ]; let browser: ReturnType; - let logger: jest.Mocked; + let eventLogger: EventLogger; + let config = {} as ConfigType; beforeEach(async () => { browser = createMockBrowserDriver(); - logger = { info: jest.fn() } as unknown as jest.Mocked; - + config = { capture: { zoom: 2 } } as ConfigType; + eventLogger = new EventLogger(loggingSystemMock.createLogger(), config); browser.evaluate.mockImplementation(({ fn, args }) => (fn as Function)(...args)); }); @@ -41,7 +44,7 @@ describe('getScreenshots', () => { }); it('should return screenshots', async () => { - await expect(getScreenshots(browser, logger, elementsPositionAndAttributes)).resolves + await expect(getScreenshots(browser, eventLogger, elementsPositionAndAttributes)).resolves .toMatchInlineSnapshot(` Array [ Object { @@ -87,7 +90,7 @@ describe('getScreenshots', () => { }); it('should forward elements positions', async () => { - await getScreenshots(browser, logger, elementsPositionAndAttributes); + await getScreenshots(browser, eventLogger, elementsPositionAndAttributes); expect(browser.screenshot).toHaveBeenCalledTimes(2); expect(browser.screenshot).toHaveBeenNthCalledWith( @@ -104,7 +107,7 @@ describe('getScreenshots', () => { browser.screenshot.mockResolvedValue(Buffer.from('')); await expect( - getScreenshots(browser, logger, elementsPositionAndAttributes) + getScreenshots(browser, eventLogger, elementsPositionAndAttributes) ).rejects.toBeInstanceOf(Error); }); }); diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.ts b/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.ts index 53829b098ee8c..67cfbd111e750 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.ts @@ -5,57 +5,52 @@ * 2.0. */ -import apm from 'elastic-apm-node'; -import type { Logger } from '@kbn/core/server'; import type { HeadlessChromiumDriver } from '../browsers'; +import { Actions, EventLogger } from './event_logger'; import type { ElementsPositionAndAttribute } from './get_element_position_data'; - -export interface Screenshot { - /** - * Screenshot PNG image data. - */ - data: Buffer; - - /** - * Screenshot title. - */ - title: string | null; - - /** - * Screenshot description. - */ - description: string | null; -} +import type { Screenshot } from './types'; export const getScreenshots = async ( browser: HeadlessChromiumDriver, - logger: Logger, + eventLogger: EventLogger, elementsPositionAndAttributes: ElementsPositionAndAttribute[] ): Promise => { - logger.info(`taking screenshots`); + const { kbnLogger } = eventLogger; + kbnLogger.info(`taking screenshots`); const screenshots: Screenshot[] = []; - for (let i = 0; i < elementsPositionAndAttributes.length; i++) { - const span = apm.startSpan('get_screenshots', 'read'); - const item = elementsPositionAndAttributes[i]; - - const data = await browser.screenshot(item.position); - - if (!data?.byteLength) { - throw new Error(`Failure in getScreenshots! Screenshot data is void`); + try { + for (let i = 0; i < elementsPositionAndAttributes.length; i++) { + const item = elementsPositionAndAttributes[i]; + const endScreenshot = eventLogger.logScreenshottingEvent( + 'screenshot capture', + Actions.GET_SCREENSHOT, + 'read', + eventLogger.getPixelsFromElementPosition(item.position) + ); + + const data = await browser.screenshot(item.position); + + if (!data?.byteLength) { + throw new Error(`Failure in getScreenshots! Screenshot data is void`); + } + + screenshots.push({ + data, + title: item.attributes.title, + description: item.attributes.description, + }); + + endScreenshot({ byte_length: data.byteLength }); } - - screenshots.push({ - data, - title: item.attributes.title, - description: item.attributes.description, - }); - - span?.end(); + } catch (error) { + kbnLogger.error(error); + eventLogger.error(error, Actions.GET_SCREENSHOT); + throw error; } - logger.info(`screenshots taken: ${screenshots.length}`); + kbnLogger.info(`screenshots taken: ${screenshots.length}`); return screenshots; }; diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_time_range.test.ts b/x-pack/plugins/screenshotting/server/screenshots/get_time_range.test.ts index 8484412f5fd94..a7a7b9295068e 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/get_time_range.test.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/get_time_range.test.ts @@ -5,21 +5,24 @@ * 2.0. */ -import type { Logger } from '@kbn/core/server'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; import { createMockBrowserDriver } from '../browsers/mock'; +import { ConfigType } from '../config'; import { createMockLayout } from '../layouts/mock'; +import { EventLogger } from './event_logger'; import { getTimeRange } from './get_time_range'; describe('getTimeRange', () => { let browser: ReturnType; let layout: ReturnType; - let logger: jest.Mocked; + let eventLogger: EventLogger; + let config = {} as ConfigType; beforeEach(async () => { browser = createMockBrowserDriver(); layout = createMockLayout(); - logger = { debug: jest.fn(), info: jest.fn() } as unknown as jest.Mocked; - + config = { capture: { zoom: 2 } } as ConfigType; + eventLogger = new EventLogger(loggingSystemMock.createLogger(), config); browser.evaluate.mockImplementation(({ fn, args }) => (fn as Function)(...args)); }); @@ -28,7 +31,7 @@ describe('getTimeRange', () => { }); it('should return null when there is no duration element', async () => { - await expect(getTimeRange(browser, logger, layout)).resolves.toBeNull(); + await expect(getTimeRange(browser, eventLogger, layout)).resolves.toBeNull(); }); it('should return null when duration attrbute is empty', async () => { @@ -36,7 +39,7 @@ describe('getTimeRange', () => {
`; - await expect(getTimeRange(browser, logger, layout)).resolves.toBeNull(); + await expect(getTimeRange(browser, eventLogger, layout)).resolves.toBeNull(); }); it('should return duration', async () => { @@ -44,6 +47,6 @@ describe('getTimeRange', () => {
`; - await expect(getTimeRange(browser, logger, layout)).resolves.toBe('10'); + await expect(getTimeRange(browser, eventLogger, layout)).resolves.toBe('10'); }); }); diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_time_range.ts b/x-pack/plugins/screenshotting/server/screenshots/get_time_range.ts index 41d902436d36b..f9272fd27ac95 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/get_time_range.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/get_time_range.ts @@ -5,19 +5,21 @@ * 2.0. */ -import apm from 'elastic-apm-node'; -import type { Logger } from '@kbn/core/server'; import type { HeadlessChromiumDriver } from '../browsers'; import { Layout } from '../layouts'; import { CONTEXT_GETTIMERANGE } from './constants'; +import { Actions, EventLogger } from './event_logger'; export const getTimeRange = async ( browser: HeadlessChromiumDriver, - logger: Logger, + eventLogger: EventLogger, layout: Layout ): Promise => { - const span = apm.startSpan('get_time_range', 'read'); - logger.debug('getting timeRange'); + const spanEnd = eventLogger.logScreenshottingEvent( + 'looking for time range', + Actions.GET_TIMERANGE, + 'read' + ); const timeRange = await browser.evaluate( { @@ -38,16 +40,14 @@ export const getTimeRange = async ( args: [layout.selectors.timefilterDurationAttribute], }, { context: CONTEXT_GETTIMERANGE }, - logger + eventLogger.kbnLogger ); if (timeRange) { - logger.info(`timeRange: ${timeRange}`); - } else { - logger.debug('no timeRange'); + eventLogger.kbnLogger.info(`timeRange: ${timeRange}`); } - span?.end(); + spanEnd(); return timeRange; }; diff --git a/x-pack/plugins/screenshotting/server/screenshots/index.test.ts b/x-pack/plugins/screenshotting/server/screenshots/index.test.ts index 3833cfcc45932..1cc328509ab72 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/index.test.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/index.test.ts @@ -14,10 +14,12 @@ import { SCREENSHOTTING_EXPRESSION, SCREENSHOTTING_EXPRESSION_INPUT, } from '../../common'; +import type { CloudSetup } from '@kbn/cloud-plugin/server'; import type { HeadlessChromiumDriverFactory } from '../browsers'; import { createMockBrowserDriver, createMockBrowserDriverFactory } from '../browsers/mock'; import type { ConfigType } from '../config'; import type { PngScreenshotOptions } from '../formats'; +import * as errors from '../../common/errors'; import * as Layouts from '../layouts/create_layout'; import { createMockLayout } from '../layouts/mock'; import { CONTEXT_ELEMENTATTRIBUTES } from './constants'; @@ -34,9 +36,15 @@ describe('Screenshot Observable Pipeline', () => { let packageInfo: Readonly; let options: ScreenshotOptions; let screenshots: Screenshots; + let cloud: CloudSetup; let config: ConfigType; beforeEach(async () => { + cloud = { + isCloudEnabled: false, + instanceSizeMb: 1024, // 1GB + apm: {}, + } as CloudSetup; driver = createMockBrowserDriver(); driverFactory = createMockBrowserDriverFactory(driver); http = httpServiceMock.createSetupContract(); @@ -71,7 +79,7 @@ describe('Screenshot Observable Pipeline', () => { browser: {} as ConfigType['browser'], }; - screenshots = new Screenshots(driverFactory, logger, packageInfo, http, config); + screenshots = new Screenshots(driverFactory, logger, packageInfo, http, config, cloud); jest.spyOn(Layouts, 'createLayout').mockReturnValue(layout); @@ -185,4 +193,41 @@ describe('Screenshot Observable Pipeline', () => { expect(result.results).toMatchSnapshot(); }); }); + + describe('cloud', () => { + beforeEach(() => { + cloud.isCloudEnabled = true; + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('throws an error when OS memory is under 1GB on cloud', async () => { + await expect( + lastValueFrom( + screenshots.getScreenshots({ + ...options, + expression: 'kibana', + input: 'something', + } as PngScreenshotOptions) + ) + ).rejects.toEqual(new errors.InsufficientMemoryAvailableOnCloudError()); + + expect(driver.open).toHaveBeenCalledTimes(0); + }); + + it('generates a report when OS memory is 2GB on cloud', async () => { + cloud.instanceSizeMb = 2048; + await lastValueFrom( + screenshots.getScreenshots({ + ...options, + expression: 'kibana', + input: 'something', + } as PngScreenshotOptions) + ); + + expect(driver.open).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/x-pack/plugins/screenshotting/server/screenshots/index.ts b/x-pack/plugins/screenshotting/server/screenshots/index.ts index 3fa0f1d222c3d..33404bb5fadc2 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/index.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/index.ts @@ -8,11 +8,9 @@ import type { HttpServiceSetup, KibanaRequest, Logger, PackageInfo } from '@kbn/core/server'; import type { ExpressionAstExpression } from '@kbn/expressions-plugin/common'; import type { Optional } from '@kbn/utility-types'; -import type { Transaction } from 'elastic-apm-node'; -import apm from 'elastic-apm-node'; import ipaddr from 'ipaddr.js'; import { defaultsDeep, sum } from 'lodash'; -import { from, Observable, of } from 'rxjs'; +import { from, Observable, of, throwError } from 'rxjs'; import { catchError, concatMap, @@ -24,12 +22,15 @@ import { tap, toArray, } from 'rxjs/operators'; +import type { CloudSetup } from '@kbn/cloud-plugin/server'; import { LayoutParams, SCREENSHOTTING_APP_ID, SCREENSHOTTING_EXPRESSION, SCREENSHOTTING_EXPRESSION_INPUT, + errors, } from '../../common'; +import { systemHasInsufficientMemory } from '../cloud'; import type { HeadlessChromiumDriverFactory, PerformanceMetrics } from '../browsers'; import type { ConfigType } from '../config'; import { durationToNumber } from '../config'; @@ -43,6 +44,7 @@ import { } from '../formats'; import type { Layout } from '../layouts'; import { createLayout } from '../layouts'; +import { EventLogger, Transactions } from './event_logger'; import type { ScreenshotObservableOptions, ScreenshotObservableResult } from './observable'; import { ScreenshotObservableHandler, UrlOrUrlWithContext } from './observable'; import { Semaphore } from './semaphore'; @@ -101,26 +103,24 @@ export class Screenshots { private readonly logger: Logger, private readonly packageInfo: PackageInfo, private readonly http: HttpServiceSetup, - private readonly config: ConfigType + private readonly config: ConfigType, + private readonly cloud?: CloudSetup ) { this.semaphore = new Semaphore(config.poolSize); } - private createLayout(transaction: Transaction | null, options: CaptureOptions): Layout { - const apmCreateLayout = transaction?.startSpan('create-layout', 'setup'); + private createLayout(options: CaptureOptions): Layout { const layout = createLayout(options.layout ?? {}); this.logger.debug(`Layout: width=${layout.width} height=${layout.height}`); - apmCreateLayout?.end(); return layout; } private captureScreenshots( + eventLogger: EventLogger, layout: Layout, - transaction: Transaction | null, options: ScreenshotObservableOptions ): Observable { - const apmCreatePage = transaction?.startSpan('create-page', 'wait'); const { browserTimezone } = options; return this.browserDriverFactory @@ -135,24 +135,22 @@ export class Screenshots { .pipe( this.semaphore.acquire(), mergeMap(({ driver, unexpectedExit$, close }) => { - apmCreatePage?.end(); - unexpectedExit$.subscribe({ error: () => transaction?.end() }); - const screen = new ScreenshotObservableHandler( driver, this.config, - this.logger, + eventLogger, layout, options ); return from(options.urls).pipe( concatMap((url, index) => - screen.setupPage(index, url, transaction).pipe( + screen.setupPage(index, url).pipe( catchError((error) => { screen.checkPageIsOpen(); // this fails the job if the browser has closed this.logger.error(error); + eventLogger.error(error, Transactions.SCREENSHOTTING); return of({ ...DEFAULT_SETUP_RESULT, error }); // allow failover screenshot capture }), takeUntil(unexpectedExit$), @@ -162,16 +160,8 @@ export class Screenshots { take(options.urls.length), toArray(), mergeMap((results) => - // At this point we no longer need the page, close it. - close().pipe( - tap(({ metrics }) => { - if (metrics) { - transaction?.setLabel('cpu', metrics.cpu, false); - transaction?.setLabel('memory', metrics.memory, false); - } - }), - map(({ metrics }) => ({ metrics, results })) - ) + // At this point we no longer need the page, close it and send out the results + close().pipe(map(({ metrics }) => ({ metrics, results }))) ) ); }), @@ -228,19 +218,39 @@ export class Screenshots { ); } + systemHasInsufficientMemory(): boolean { + return systemHasInsufficientMemory(this.cloud, this.logger.get('cloud')); + } + getScreenshots(options: PngScreenshotOptions): Observable; getScreenshots(options: PdfScreenshotOptions): Observable; getScreenshots(options: ScreenshotOptions): Observable; getScreenshots(options: ScreenshotOptions): Observable { - const transaction = apm.startTransaction('screenshot-pipeline', 'screenshotting'); - const layout = this.createLayout(transaction, options); + if (this.systemHasInsufficientMemory()) { + return throwError(() => new errors.InsufficientMemoryAvailableOnCloudError()); + } + + const eventLogger = new EventLogger(this.logger, this.config); + const transactionEnd = eventLogger.startTransaction(Transactions.SCREENSHOTTING); + + const layout = this.createLayout(options); const captureOptions = this.getCaptureOptions(options); - return this.captureScreenshots(layout, transaction, captureOptions).pipe( + return this.captureScreenshots(eventLogger, layout, captureOptions).pipe( + tap(({ results, metrics }) => { + transactionEnd({ + labels: { + cpu: metrics?.cpu, + memory: metrics?.memory, + memory_mb: metrics?.memoryInMegabytes, + ...eventLogger.getByteLengthFromCaptureResults(results), + }, + }); + }), mergeMap((result) => { switch (options.format) { case 'pdf': - return toPdf(this.logger, this.packageInfo, layout, options, result); + return toPdf(eventLogger, this.packageInfo, layout, options, result); default: return toPng(result); } diff --git a/x-pack/plugins/screenshotting/server/screenshots/inject_css.ts b/x-pack/plugins/screenshotting/server/screenshots/inject_css.ts index a77cfa8c9e8e6..41426e893ce58 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/inject_css.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/inject_css.ts @@ -7,26 +7,31 @@ import fs from 'fs'; import { promisify } from 'util'; -import apm from 'elastic-apm-node'; -import type { Logger } from '@kbn/core/server'; import type { HeadlessChromiumDriver } from '../browsers'; import { Layout } from '../layouts'; import { CONTEXT_INJECTCSS } from './constants'; +import { Actions, EventLogger } from './event_logger'; const fsp = { readFile: promisify(fs.readFile) }; export const injectCustomCss = async ( browser: HeadlessChromiumDriver, - logger: Logger, + eventLogger: EventLogger, layout: Layout ): Promise => { - const span = apm.startSpan('inject_css', 'correction'); - logger.debug('injecting custom css'); - const filePath = layout.getCssOverridesPath(); if (!filePath) { return; } + + const { kbnLogger } = eventLogger; + + const spanEnd = eventLogger.logScreenshottingEvent( + 'inject CSS into the page', + Actions.INJECT_CSS, + 'correction' + ); + const buffer = await fsp.readFile(filePath); try { await browser.evaluate( @@ -40,14 +45,15 @@ export const injectCustomCss = async ( args: [buffer.toString()], }, { context: CONTEXT_INJECTCSS }, - logger + kbnLogger ); } catch (err) { - logger.error(err); + kbnLogger.error(err); + eventLogger.error(err, Actions.INJECT_CSS); throw new Error( `An error occurred when trying to update Kibana CSS for reporting. ${err.message}` ); } - span?.end(); + spanEnd(); }; diff --git a/x-pack/plugins/screenshotting/server/screenshots/observable.test.ts b/x-pack/plugins/screenshotting/server/screenshots/observable.test.ts index d3acc96411dc6..b282cd32bbd80 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/observable.test.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/observable.test.ts @@ -5,36 +5,33 @@ * 2.0. */ +import { loggingSystemMock } from '@kbn/core/server/mocks'; import { interval, of, throwError } from 'rxjs'; import { map } from 'rxjs/operators'; -import type { Logger } from '@kbn/core/server'; import { createMockBrowserDriver } from '../browsers/mock'; import type { ConfigType } from '../config'; import { createMockLayout } from '../layouts/mock'; +import { EventLogger } from './event_logger'; import { ScreenshotObservableHandler, ScreenshotObservableOptions } from './observable'; describe('ScreenshotObservableHandler', () => { let browser: ReturnType; let config: ConfigType; let layout: ReturnType; - let logger: jest.Mocked; + let eventLogger: EventLogger; let options: ScreenshotObservableOptions; beforeEach(async () => { browser = createMockBrowserDriver(); config = { capture: { - timeouts: { - openUrl: 30000, - waitForElements: 30000, - renderComplete: 30000, - }, + timeouts: { openUrl: 30000, waitForElements: 30000, renderComplete: 30000 }, loadDelay: 5000, zoom: 13, }, } as ConfigType; layout = createMockLayout(); - logger = { error: jest.fn() } as unknown as jest.Mocked; + eventLogger = new EventLogger(loggingSystemMock.createLogger(), config); options = { headers: { testHeader: 'testHeadValue' }, urls: [], @@ -46,7 +43,7 @@ describe('ScreenshotObservableHandler', () => { describe('waitUntil', () => { let screenshots: ScreenshotObservableHandler; beforeEach(() => { - screenshots = new ScreenshotObservableHandler(browser, config, logger, layout, options); + screenshots = new ScreenshotObservableHandler(browser, config, eventLogger, layout, options); }); it('catches TimeoutError and references the timeout config in a custom message', async () => { @@ -79,7 +76,7 @@ describe('ScreenshotObservableHandler', () => { describe('checkPageIsOpen', () => { let screenshots: ScreenshotObservableHandler; beforeEach(() => { - screenshots = new ScreenshotObservableHandler(browser, config, logger, layout, options); + screenshots = new ScreenshotObservableHandler(browser, config, eventLogger, layout, options); }); it('throws a decorated Error when page is not open', async () => { diff --git a/x-pack/plugins/screenshotting/server/screenshots/observable.ts b/x-pack/plugins/screenshotting/server/screenshots/observable.ts index b19f3f254b2a2..d06014c82ecc7 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/observable.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/observable.ts @@ -5,26 +5,28 @@ * 2.0. */ -import type { Transaction } from 'elastic-apm-node'; +import type { Headers } from '@kbn/core/server'; import { defer, forkJoin, Observable, throwError } from 'rxjs'; import { catchError, mergeMap, switchMapTo, timeoutWith } from 'rxjs/operators'; -import type { Headers, Logger } from '@kbn/core/server'; -import { errors } from '../../common'; +import { errors, LayoutTypes } from '../../common'; import type { Context, HeadlessChromiumDriver } from '../browsers'; import { DEFAULT_VIEWPORT, getChromiumDisconnectedError } from '../browsers'; -import { durationToNumber as toNumber, ConfigType } from '../config'; +import { ConfigType, durationToNumber as toNumber } from '../config'; import type { Layout } from '../layouts'; +import { Actions, EventLogger } from './event_logger'; import type { ElementsPositionAndAttribute } from './get_element_position_data'; import { getElementPositionAndAttributes } from './get_element_position_data'; import { getNumberOfItems } from './get_number_of_items'; import { getRenderErrors } from './get_render_errors'; -import type { Screenshot } from './get_screenshots'; +import type { Screenshot } from './types'; import { getScreenshots } from './get_screenshots'; +import { getPdf } from './get_pdf'; import { getTimeRange } from './get_time_range'; import { injectCustomCss } from './inject_css'; import { openUrl } from './open_url'; import { waitForRenderComplete } from './wait_for_render'; import { waitForVisualizations } from './wait_for_visualizations'; +import type { PdfScreenshotOptions } from '../formats'; type CaptureTimeouts = ConfigType['capture']['timeouts']; export interface PhaseTimeouts extends CaptureTimeouts { @@ -90,7 +92,9 @@ interface PageSetupResults { renderErrors?: string[]; } -const getDefaultElementPosition = (dimensions: { height?: number; width?: number } | null) => { +const getDefaultElementPosition = ( + dimensions: { height?: number; width?: number } | null +): ElementsPositionAndAttribute[] => { const height = dimensions?.height || DEFAULT_VIEWPORT.height; const width = dimensions?.width || DEFAULT_VIEWPORT.width; @@ -118,7 +122,7 @@ export class ScreenshotObservableHandler { constructor( private readonly driver: HeadlessChromiumDriver, private readonly config: ConfigType, - private readonly logger: Logger, + private readonly eventLogger: EventLogger, private readonly layout: Layout, private options: ScreenshotObservableOptions ) {} @@ -154,7 +158,7 @@ export class ScreenshotObservableHandler { return openUrl( this.driver, - this.logger, + this.eventLogger, toNumber(this.config.capture.timeouts.openUrl), index, url, @@ -168,52 +172,90 @@ export class ScreenshotObservableHandler { const driver = this.driver; const waitTimeout = toNumber(this.config.capture.timeouts.waitForElements); - return defer(() => getNumberOfItems(driver, this.logger, waitTimeout, this.layout)).pipe( + return defer(() => getNumberOfItems(driver, this.eventLogger, waitTimeout, this.layout)).pipe( mergeMap(async (itemsCount) => { // set the viewport to the dimensions from the job, to allow elements to flow into the expected layout const viewport = this.layout.getViewport(itemsCount) || getDefaultViewPort(); // Set the viewport allowing time for the browser to handle reflow and redraw // before checking for readiness of visualizations. - await driver.setViewport(viewport, this.logger); - await waitForVisualizations(driver, this.logger, waitTimeout, itemsCount, this.layout); + await driver.setViewport(viewport, this.eventLogger.kbnLogger); + await waitForVisualizations(driver, this.eventLogger, waitTimeout, itemsCount, this.layout); }), this.waitUntil(waitTimeout, 'wait for elements') ); } - private completeRender(apmTrans: Transaction | null) { + private completeRender() { const driver = this.driver; const layout = this.layout; - const logger = this.logger; + const eventLogger = this.eventLogger; return defer(async () => { // Waiting till _after_ elements have rendered before injecting our CSS // allows for them to be displayed properly in many cases - await injectCustomCss(driver, logger, layout); + await injectCustomCss(driver, eventLogger, layout); - const apmPositionElements = apmTrans?.startSpan('position-elements', 'correction'); - // position panel elements for print layout - await layout.positionElements?.(driver, logger); - apmPositionElements?.end(); + const spanEnd = this.eventLogger.logScreenshottingEvent( + 'get positions of visualization elements', + Actions.GET_ELEMENT_POSITION_DATA, + 'read' + ); + try { + // position panel elements for print layout + await layout.positionElements?.(driver, eventLogger.kbnLogger); + spanEnd(); + } catch (error) { + eventLogger.error(error, Actions.GET_ELEMENT_POSITION_DATA); + throw error; + } - await waitForRenderComplete(driver, logger, toNumber(this.config.capture.loadDelay), layout); + await waitForRenderComplete( + driver, + eventLogger, + toNumber(this.config.capture.loadDelay), + layout + ); }).pipe( mergeMap(() => forkJoin({ - timeRange: getTimeRange(driver, logger, layout), - elementsPositionAndAttributes: getElementPositionAndAttributes(driver, logger, layout), - renderErrors: getRenderErrors(driver, logger, layout), + timeRange: getTimeRange(driver, eventLogger, layout), + elementsPositionAndAttributes: getElementPositionAndAttributes( + driver, + eventLogger, + layout + ), + renderErrors: getRenderErrors(driver, eventLogger, layout), }) ), this.waitUntil(toNumber(this.config.capture.timeouts.renderComplete), 'render complete') ); } - public setupPage(index: number, url: UrlOrUrlWithContext, apmTrans: Transaction | null) { + public setupPage(index: number, url: UrlOrUrlWithContext) { return this.openUrl(index, url).pipe( switchMapTo(this.waitForElements()), - switchMapTo(this.completeRender(apmTrans)) + switchMapTo(this.completeRender()) + ); + } + + /** + * Given a title and time range value look like: + * + * "[Logs] Web Traffic - Apr 14, 2022 @ 120742.318 to Apr 21, 2022 @ 120742.318" + * + * Otherwise closest thing to that or a blank string. + */ + private getTitle(timeRange: null | string): string { + return `${(this.options as PdfScreenshotOptions).title ?? ''} ${ + timeRange ? `- ${timeRange}` : '' + }`.trim(); + } + + private shouldCapturePdf(): boolean { + return ( + this.layout.id === LayoutTypes.PRINT && + (this.options as PdfScreenshotOptions).format === 'pdf' ); } @@ -227,7 +269,14 @@ export class ScreenshotObservableHandler { getDefaultElementPosition(this.layout.getViewport(1)); let screenshots: Screenshot[] = []; try { - screenshots = await getScreenshots(this.driver, this.logger, elements); + screenshots = this.shouldCapturePdf() + ? await getPdf( + this.driver, + this.eventLogger, + this.getTitle(data.timeRange), + (this.options as PdfScreenshotOptions).logo + ) + : await getScreenshots(this.driver, this.eventLogger, elements); } catch (e) { throw new errors.FailedToCaptureScreenshot(e.message); } diff --git a/x-pack/plugins/screenshotting/server/screenshots/open_url.ts b/x-pack/plugins/screenshotting/server/screenshots/open_url.ts index c557374ff9876..bdf8c678eb1d2 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/open_url.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/open_url.ts @@ -5,33 +5,39 @@ * 2.0. */ -import apm from 'elastic-apm-node'; -import type { Headers, Logger } from '@kbn/core/server'; -import type { HeadlessChromiumDriver } from '../browsers'; -import type { Context } from '../browsers'; +import type { Headers } from '@kbn/core/server'; +import type { Context, HeadlessChromiumDriver } from '../browsers'; import { DEFAULT_PAGELOAD_SELECTOR } from './constants'; +import { Actions, EventLogger } from './event_logger'; export const openUrl = async ( browser: HeadlessChromiumDriver, - logger: Logger, + eventLogger: EventLogger, timeout: number, index: number, url: string, context: Context, headers: Headers ): Promise => { + const { kbnLogger } = eventLogger; + const spanEnd = eventLogger.logScreenshottingEvent('open url', Actions.OPEN_URL, 'wait'); + // If we're moving to another page in the app, we'll want to wait for the app to tell us // it's loaded the next page. const page = index + 1; const waitForSelector = page > 1 ? `[data-shared-page="${page}"]` : DEFAULT_PAGELOAD_SELECTOR; - const span = apm.startSpan('open_url', 'wait'); try { - await browser.open(url, { context, headers, waitForSelector, timeout }, logger); + await browser.open(url, { context, headers, waitForSelector, timeout }, kbnLogger); } catch (err) { - logger.error(err); - throw new Error(`An error occurred when trying to open the Kibana URL: ${err.message}`); + kbnLogger.error(err); + + const newError = new Error( + `An error occurred when trying to open the Kibana URL: ${err.message}` + ); + eventLogger.error(newError, Actions.OPEN_URL); + throw newError; } - span?.end(); + spanEnd(); }; diff --git a/x-pack/plugins/screenshotting/server/screenshots/semaphore.test.ts b/x-pack/plugins/screenshotting/server/screenshots/semaphore.test.ts deleted file mode 100644 index 6d6dd21347974..0000000000000 --- a/x-pack/plugins/screenshotting/server/screenshots/semaphore.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TestScheduler } from 'rxjs/testing'; -import { Semaphore } from './semaphore'; - -describe('Semaphore', () => { - let testScheduler: TestScheduler; - - beforeEach(() => { - testScheduler = new TestScheduler((actual, expected) => { - return expect(actual).toStrictEqual(expected); - }); - }); - - describe('acquire', () => { - it('should limit the number of concurrently subscribed observables', () => { - testScheduler.run(({ cold, expectObservable }) => { - const semaphore = new Semaphore(2); - const observable1 = cold('500ms a|').pipe(semaphore.acquire()); - const observable2 = cold('500ms b|').pipe(semaphore.acquire()); - const observable3 = cold('500ms c|').pipe(semaphore.acquire()); - - expectObservable(observable1).toBe('500ms a|'); - expectObservable(observable2).toBe('500ms b|'); - expectObservable(observable3).toBe('1001ms c|'); - }); - }); - - it('should release semaphore on unsubscription', () => { - testScheduler.run(({ cold, expectObservable }) => { - const semaphore = new Semaphore(2); - const observable1 = cold('500ms a|').pipe(semaphore.acquire()); - const observable2 = cold('500ms b|').pipe(semaphore.acquire()); - const observable3 = cold('500ms c|').pipe(semaphore.acquire()); - - expectObservable(observable1).toBe('500ms a|'); - expectObservable(observable2, '^ 100ms !').toBe(''); - expectObservable(observable3).toBe('601ms c|'); - }); - }); - - it('should release semaphore on error', () => { - testScheduler.run(({ cold, expectObservable }) => { - const semaphore = new Semaphore(2); - const observable1 = cold('500ms a|').pipe(semaphore.acquire()); - const observable2 = cold('100ms #').pipe(semaphore.acquire()); - const observable3 = cold('500ms c|').pipe(semaphore.acquire()); - - expectObservable(observable1).toBe('500ms a|'); - expectObservable(observable2).toBe('100ms #'); - expectObservable(observable3).toBe('600ms c|'); - }); - }); - - it('should remove from the queue on unsubscription', () => { - testScheduler.run(({ cold, expectObservable }) => { - const semaphore = new Semaphore(1); - const observable1 = cold('500ms a|').pipe(semaphore.acquire()); - const observable2 = cold('500ms b').pipe(semaphore.acquire()); - const observable3 = cold('500ms c|').pipe(semaphore.acquire()); - - expectObservable(observable1).toBe('500ms a|'); - expectObservable(observable2, '^ 100ms !').toBe(''); - expectObservable(observable3).toBe('1001ms c|'); - }); - }); - }); -}); diff --git a/x-pack/plugins/screenshotting/server/screenshots/semaphore/index.test.ts b/x-pack/plugins/screenshotting/server/screenshots/semaphore/index.test.ts new file mode 100644 index 0000000000000..0cc40a83723a9 --- /dev/null +++ b/x-pack/plugins/screenshotting/server/screenshots/semaphore/index.test.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TestScheduler } from 'rxjs/testing'; +import { Semaphore } from '.'; + +describe('Semaphore', () => { + let testScheduler: TestScheduler; + + beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => { + return expect(actual).toStrictEqual(expected); + }); + }); + + describe('acquire', () => { + it('should limit the number of concurrently subscribed observables', () => { + testScheduler.run(({ cold, expectObservable }) => { + const semaphore = new Semaphore(2); + const observable1 = cold('500ms a|').pipe(semaphore.acquire()); + const observable2 = cold('500ms b|').pipe(semaphore.acquire()); + const observable3 = cold('500ms c|').pipe(semaphore.acquire()); + + expectObservable(observable1).toBe('500ms a|'); + expectObservable(observable2).toBe('500ms b|'); + expectObservable(observable3).toBe('1001ms c|'); + }); + }); + + it('should release semaphore on unsubscription', () => { + testScheduler.run(({ cold, expectObservable }) => { + const semaphore = new Semaphore(2); + const observable1 = cold('500ms a|').pipe(semaphore.acquire()); + const observable2 = cold('500ms b|').pipe(semaphore.acquire()); + const observable3 = cold('500ms c|').pipe(semaphore.acquire()); + + expectObservable(observable1).toBe('500ms a|'); + expectObservable(observable2, '^ 100ms !').toBe(''); + expectObservable(observable3).toBe('601ms c|'); + }); + }); + + it('should release semaphore on error', () => { + testScheduler.run(({ cold, expectObservable }) => { + const semaphore = new Semaphore(2); + const observable1 = cold('500ms a|').pipe(semaphore.acquire()); + const observable2 = cold('100ms #').pipe(semaphore.acquire()); + const observable3 = cold('500ms c|').pipe(semaphore.acquire()); + + expectObservable(observable1).toBe('500ms a|'); + expectObservable(observable2).toBe('100ms #'); + expectObservable(observable3).toBe('600ms c|'); + }); + }); + + it('should remove from the queue on unsubscription', () => { + testScheduler.run(({ cold, expectObservable }) => { + const semaphore = new Semaphore(1); + const observable1 = cold('500ms a|').pipe(semaphore.acquire()); + const observable2 = cold('500ms b').pipe(semaphore.acquire()); + const observable3 = cold('500ms c|').pipe(semaphore.acquire()); + + expectObservable(observable1).toBe('500ms a|'); + expectObservable(observable2, '^ 100ms !').toBe(''); + expectObservable(observable3).toBe('1001ms c|'); + }); + }); + }); +}); diff --git a/x-pack/plugins/screenshotting/server/screenshots/semaphore.ts b/x-pack/plugins/screenshotting/server/screenshots/semaphore/index.ts similarity index 100% rename from x-pack/plugins/screenshotting/server/screenshots/semaphore.ts rename to x-pack/plugins/screenshotting/server/screenshots/semaphore/index.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/types.ts b/x-pack/plugins/screenshotting/server/screenshots/types.ts new file mode 100644 index 0000000000000..d4a408313fc43 --- /dev/null +++ b/x-pack/plugins/screenshotting/server/screenshots/types.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface Screenshot { + /** + * Screenshot PNG image data. + */ + data: Buffer; + + /** + * Screenshot title. + */ + title: string | null; + + /** + * Screenshot description. + */ + description: string | null; +} diff --git a/x-pack/plugins/screenshotting/server/screenshots/wait_for_render.ts b/x-pack/plugins/screenshotting/server/screenshots/wait_for_render.ts index cee23616faeac..8cf8174be152f 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/wait_for_render.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/wait_for_render.ts @@ -5,21 +5,22 @@ * 2.0. */ -import apm from 'elastic-apm-node'; -import type { Logger } from '@kbn/core/server'; import type { HeadlessChromiumDriver } from '../browsers'; import { Layout } from '../layouts'; import { CONTEXT_WAITFORRENDER } from './constants'; +import { Actions, EventLogger } from './event_logger'; export const waitForRenderComplete = async ( browser: HeadlessChromiumDriver, - logger: Logger, + eventLogger: EventLogger, loadDelay: number, layout: Layout ) => { - const span = apm.startSpan('wait_for_render', 'wait'); - - logger.debug('waiting for rendering to complete'); + const spanEnd = eventLogger.logScreenshottingEvent( + 'wait for render complete', + Actions.WAIT_RENDER, + 'wait' + ); return await browser .evaluate( @@ -66,11 +67,9 @@ export const waitForRenderComplete = async ( args: [layout.selectors.renderComplete, loadDelay], }, { context: CONTEXT_WAITFORRENDER }, - logger + eventLogger.kbnLogger ) .then(() => { - logger.debug('rendering is complete'); - - span?.end(); + spanEnd(); }); }; diff --git a/x-pack/plugins/screenshotting/server/screenshots/wait_for_visualizations.ts b/x-pack/plugins/screenshotting/server/screenshots/wait_for_visualizations.ts index a7485545cdef0..cf49fbe7dc798 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/wait_for_visualizations.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/wait_for_visualizations.ts @@ -5,11 +5,10 @@ * 2.0. */ -import apm from 'elastic-apm-node'; -import type { Logger } from '@kbn/core/server'; import type { HeadlessChromiumDriver } from '../browsers'; import { Layout } from '../layouts'; import { CONTEXT_WAITFORELEMENTSTOBEINDOM } from './constants'; +import { Actions, EventLogger } from './event_logger'; interface CompletedItemsCountParameters { context: string; @@ -37,15 +36,21 @@ const getCompletedItemsCount = ({ */ export const waitForVisualizations = async ( browser: HeadlessChromiumDriver, - logger: Logger, + eventLogger: EventLogger, timeout: number, toEqual: number, layout: Layout ): Promise => { - const span = apm.startSpan('wait_for_visualizations', 'wait'); + const { kbnLogger } = eventLogger; + const spanEnd = eventLogger.logScreenshottingEvent( + 'waiting for each visualization to complete rendering', + Actions.WAIT_VISUALIZATIONS, + 'wait' + ); + const { renderComplete: renderCompleteSelector } = layout.selectors; - logger.debug(`waiting for ${toEqual} rendered elements to be in the DOM`); + kbnLogger.debug(`waiting for ${toEqual} rendered elements to be in the DOM`); try { await browser.waitFor({ @@ -54,13 +59,15 @@ export const waitForVisualizations = async ( timeout, }); - logger.debug(`found ${toEqual} rendered elements in the DOM`); + kbnLogger.debug(`found ${toEqual} rendered elements in the DOM`); } catch (err) { - logger.error(err); - throw new Error( + kbnLogger.error(err); + const newError = new Error( `An error occurred when trying to wait for ${toEqual} visualizations to finish rendering. ${err.message}` ); + eventLogger.error(newError, Actions.WAIT_VISUALIZATIONS); + throw newError; } - span?.end(); + spanEnd(); }; diff --git a/x-pack/plugins/screenshotting/tsconfig.json b/x-pack/plugins/screenshotting/tsconfig.json index d385f72809ce2..af98e0fcc3244 100644 --- a/x-pack/plugins/screenshotting/tsconfig.json +++ b/x-pack/plugins/screenshotting/tsconfig.json @@ -16,5 +16,6 @@ { "path": "../../../src/core/tsconfig.json" }, { "path": "../../../src/plugins/expressions/tsconfig.json" }, { "path": "../../../src/plugins/screenshot_mode/tsconfig.json" }, + { "path": "../cloud/tsconfig.json" }, ] } diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx index ae64ad4655ba5..72612904f1c08 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx @@ -11,7 +11,7 @@ import React from 'react'; import type { Capabilities } from '@kbn/core/public'; import { coreMock, scopedHistoryMock } from '@kbn/core/public/mocks'; -import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { KibanaFeature } from '@kbn/features-plugin/public'; import type { Space } from '@kbn/spaces-plugin/public'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; @@ -139,7 +139,7 @@ function getProps({ const rolesAPIClient = rolesAPIClientMock.create(); rolesAPIClient.getRole.mockResolvedValue(role); - const dataViews = dataPluginMock.createStartContract().dataViews; + const dataViews = dataViewPluginMocks.createStartContract(); // `undefined` titles can technically happen via import/export or other manual manipulation dataViews.getTitles = jest.fn().mockResolvedValue(['foo*', 'bar*', undefined]); @@ -352,7 +352,7 @@ describe('', () => { }); it('can render if index patterns are not available', async () => { - const dataViews = dataPluginMock.createStartContract().dataViews; + const dataViews = dataViewPluginMocks.createStartContract(); dataViews.getTitles = jest.fn().mockRejectedValue({ response: { status: 403 } }); const wrapper = mountWithIntl( diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx index 1cda335ace204..cc9803063b860 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx @@ -31,7 +31,7 @@ import type { NotificationsStart, ScopedHistory, } from '@kbn/core/public'; -import type { DataViewsContract } from '@kbn/data-plugin/public'; +import type { DataViewsContract } from '@kbn/data-views-plugin/public'; import type { KibanaFeature } from '@kbn/features-plugin/common'; import type { FeaturesPluginStart } from '@kbn/features-plugin/public'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx index 20c2d3f4c4bc8..7e1c5eb545a28 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx @@ -136,7 +136,6 @@ export class SpacesPopoverList extends Component { key={`spcMenuList`} data-search-term={searchTerm} className="spcMenu__spacesList" - hasFocus={this.state.allowSpacesListFocus} initialFocusedItemIndex={this.state.allowSpacesListFocus ? 0 : undefined} items={items} /> diff --git a/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts b/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts index 3e871c4979804..c801a1f8f4e1b 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts +++ b/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts @@ -85,7 +85,7 @@ describe('SecurityNavControlService', () => { >
diff --git a/x-pack/plugins/security_solution/public/common/components/sessions_viewer/cell_renderer.tsx b/x-pack/plugins/security_solution/public/common/components/sessions_viewer/cell_renderer.tsx deleted file mode 100644 index 088935b32ce34..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/sessions_viewer/cell_renderer.tsx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { CellValueElementProps } from '../../../timelines/components/timeline/cell_rendering'; -import { DefaultCellRenderer } from '../../../timelines/components/timeline/cell_rendering/default_cell_renderer'; -import { getEmptyValue } from '../empty_value'; -import { MAPPED_PROCESS_END_COLUMN } from './default_headers'; - -const hasEcsDataEndEventAction = (ecsData: CellValueElementProps['ecsData']) => { - return ecsData?.event?.action?.includes('end'); -}; - -export const CellRenderer: React.FC = (props: CellValueElementProps) => { - // We only want to render process.end for event.actions of type 'end' - if (props.columnId === MAPPED_PROCESS_END_COLUMN && !hasEcsDataEndEventAction(props.ecsData)) { - return <>{getEmptyValue()}; - } - - return ; -}; diff --git a/x-pack/plugins/security_solution/public/common/components/sessions_viewer/default_headers.ts b/x-pack/plugins/security_solution/public/common/components/sessions_viewer/default_headers.ts index d73ab1b690f61..4c045e358e1d6 100644 --- a/x-pack/plugins/security_solution/public/common/components/sessions_viewer/default_headers.ts +++ b/x-pack/plugins/security_solution/public/common/components/sessions_viewer/default_headers.ts @@ -10,50 +10,52 @@ import { defaultColumnHeaderType } from '../../../timelines/components/timeline/ import { DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants'; import { SubsetTimelineModel } from '../../../timelines/store/timeline/model'; import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; - -// Using @timestamp as an way of getting the end time of the process. (Currently endpoint doesn't populate process.end) -// @timestamp of an event.action with value of "end" is what we consider that to be the end time of the process -// Current action are: 'start', 'exec', 'end', so we might have up to three events per process. -export const MAPPED_PROCESS_END_COLUMN = '@timestamp'; +import { + COLUMN_SESSION_START, + COLUMN_EXECUTABLE, + COLUMN_ENTRY_USER, + COLUMN_INTERACTIVE, + COLUMN_HOST_NAME, + COLUMN_ENTRY_TYPE, + COLUMN_ENTRY_IP, +} from './translations'; export const sessionsHeaders: ColumnHeaderOptions[] = [ { columnHeaderType: defaultColumnHeaderType, - id: 'process.start', + id: 'process.entry_leader.start', initialWidth: DEFAULT_DATE_COLUMN_MIN_WIDTH, + display: COLUMN_SESSION_START, }, { columnHeaderType: defaultColumnHeaderType, - id: MAPPED_PROCESS_END_COLUMN, - display: 'process.end', + id: 'process.entry_leader.executable', + display: COLUMN_EXECUTABLE, }, { columnHeaderType: defaultColumnHeaderType, - id: 'process.executable', + id: 'process.entry_leader.user.name', + display: COLUMN_ENTRY_USER, }, { columnHeaderType: defaultColumnHeaderType, - id: 'user.name', - }, - { - columnHeaderType: defaultColumnHeaderType, - id: 'process.interactive', - }, - { - columnHeaderType: defaultColumnHeaderType, - id: 'process.pid', + id: 'process.entry_leader.interactive', + display: COLUMN_INTERACTIVE, }, { columnHeaderType: defaultColumnHeaderType, id: 'host.hostname', + display: COLUMN_HOST_NAME, }, { columnHeaderType: defaultColumnHeaderType, id: 'process.entry_leader.entry_meta.type', + display: COLUMN_ENTRY_TYPE, }, { - columnHeaderType: defaultColumnHeaderType, id: 'process.entry_leader.entry_meta.source.ip', + columnHeaderType: defaultColumnHeaderType, + display: COLUMN_ENTRY_IP, }, ]; @@ -62,4 +64,11 @@ export const sessionsDefaultModel: SubsetTimelineModel = { columns: sessionsHeaders, defaultColumns: sessionsHeaders, excludedRowRendererIds: Object.values(RowRendererId), + sort: [ + { + columnId: 'process.entry_leader.start', + columnType: 'date', + sortDirection: 'desc', + }, + ], }; diff --git a/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.test.tsx index 043a2aa378427..5280f298ba99e 100644 --- a/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.test.tsx @@ -109,10 +109,11 @@ describe('SessionsView', () => { expect(wrapper.getByTestId(`${TEST_PREFIX}:startDate`)).toHaveTextContent(startDate); expect(wrapper.getByTestId(`${TEST_PREFIX}:endDate`)).toHaveTextContent(endDate); expect(wrapper.getByTestId(`${TEST_PREFIX}:timelineId`)).toHaveTextContent( - 'hosts-page-sessions' + 'hosts-page-sessions-v2' ); }); }); + it('passes in the right filters to TGrid', async () => { render( diff --git a/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.tsx index 6834553a5eee8..4d89b969e5c17 100644 --- a/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.tsx @@ -12,7 +12,7 @@ import { ESBoolQuery } from '../../../../common/typed_json'; import { StatefulEventsViewer } from '../events_viewer'; import { sessionsDefaultModel } from './default_headers'; import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers'; -import { CellRenderer } from './cell_renderer'; +import { DefaultCellRenderer } from '../../../timelines/components/timeline/cell_rendering/default_cell_renderer'; import * as i18n from './translations'; import { SourcererScopeName } from '../../store/sourcerer/model'; import { getDefaultControlColumn } from '../../../timelines/components/timeline/body/control_columns'; @@ -24,15 +24,8 @@ export const defaultSessionsFilter: Required> = { bool: { filter: [ { - bool: { - should: [ - { - match: { - 'process.entry_leader.same_as_process': true, - }, - }, - ], - minimum_should_match: 1, + exists: { + field: 'process.entry_leader.entity_id', // to exclude any records which have no entry_leader.entity_id }, }, ], @@ -41,10 +34,10 @@ export const defaultSessionsFilter: Required> = { meta: { alias: null, disabled: false, - key: 'process.entry_leader.same_as_process', + key: 'process.entry_leader.entity_id', negate: false, params: {}, - type: 'boolean', + type: 'string', }, }; @@ -95,7 +88,7 @@ const SessionsViewComponent: React.FC = ({ entityType={entityType} id={timelineId} leadingControlColumns={leadingControlColumns} - renderCellValue={CellRenderer} + renderCellValue={DefaultCellRenderer} rowRenderers={defaultRowRenderers} scopeId={SourcererScopeName.default} start={startDate} diff --git a/x-pack/plugins/security_solution/public/common/components/sessions_viewer/translations.ts b/x-pack/plugins/security_solution/public/common/components/sessions_viewer/translations.ts index 606ae2b46fc6a..ea35892f3a2f9 100644 --- a/x-pack/plugins/security_solution/public/common/components/sessions_viewer/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/sessions_viewer/translations.ts @@ -20,3 +20,52 @@ export const SINGLE_COUNT_OF_SESSIONS = i18n.translate( defaultMessage: 'session', } ); + +export const COLUMN_SESSION_START = i18n.translate( + 'xpack.securitySolution.sessionsView.columnSessionStart', + { + defaultMessage: 'Started', + } +); + +export const COLUMN_EXECUTABLE = i18n.translate( + 'xpack.securitySolution.sessionsView.columnExecutable', + { + defaultMessage: 'Executable', + } +); + +export const COLUMN_ENTRY_USER = i18n.translate( + 'xpack.securitySolution.sessionsView.columnEntryUser', + { + defaultMessage: 'User', + } +); + +export const COLUMN_INTERACTIVE = i18n.translate( + 'xpack.securitySolution.sessionsView.columnInteractive', + { + defaultMessage: 'Interactive', + } +); + +export const COLUMN_HOST_NAME = i18n.translate( + 'xpack.securitySolution.sessionsView.columnHostName', + { + defaultMessage: 'Hostname', + } +); + +export const COLUMN_ENTRY_TYPE = i18n.translate( + 'xpack.securitySolution.sessionsView.columnEntryType', + { + defaultMessage: 'Type', + } +); + +export const COLUMN_ENTRY_IP = i18n.translate( + 'xpack.securitySolution.sessionsView.columnEntrySourceIp', + { + defaultMessage: 'Source IP', + } +); diff --git a/x-pack/plugins/security_solution/public/common/components/top_risk_score_contributors/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/top_risk_score_contributors/index.test.tsx new file mode 100644 index 0000000000000..4cc6812772b81 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/top_risk_score_contributors/index.test.tsx @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render } from '@testing-library/react'; +import React from 'react'; +import { TopRiskScoreContributors } from '.'; +import { TestProviders } from '../../mock'; +import { RuleRisk } from '../../../../common/search_strategy'; + +jest.mock('../../containers/query_toggle'); +jest.mock('../../../risk_score/containers'); + +const testProps = { + riskScore: [], + setQuery: jest.fn(), + deleteQuery: jest.fn(), + hostName: 'test-host-name', + from: '2020-07-07T08:20:18.966Z', + to: '2020-07-08T08:20:18.966Z', + loading: false, + toggleStatus: true, + queryId: 'test-query-id', +}; + +describe('Top Risk Score Contributors', () => { + it('renders', () => { + const { queryByTestId } = render( + + + + ); + + expect(queryByTestId('topRiskScoreContributors')).toBeInTheDocument(); + }); + + it('renders sorted items', () => { + const ruleRisk: RuleRisk[] = [ + { + rule_name: 'third', + rule_risk: 10, + rule_id: '3', + }, + { + rule_name: 'first', + rule_risk: 99, + rule_id: '1', + }, + { + rule_name: 'second', + rule_risk: 55, + rule_id: '2', + }, + ]; + + const { queryAllByRole } = render( + + + + ); + + expect(queryAllByRole('row')[1]).toHaveTextContent('first'); + expect(queryAllByRole('row')[2]).toHaveTextContent('second'); + expect(queryAllByRole('row')[3]).toHaveTextContent('third'); + }); + + describe('toggleStatus', () => { + test('toggleStatus=true, render components', () => { + const { queryByTestId } = render( + + + + ); + expect(queryByTestId('topRiskScoreContributors-table')).toBeTruthy(); + }); + + test('toggleStatus=false, do not render components', () => { + const { queryByTestId } = render( + + + + ); + expect(queryByTestId('topRiskScoreContributors-table')).toBeFalsy(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/top_risk_score_contributors/index.tsx b/x-pack/plugins/security_solution/public/common/components/top_risk_score_contributors/index.tsx new file mode 100644 index 0000000000000..7ee2ae5e21413 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/top_risk_score_contributors/index.tsx @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiInMemoryTable, + EuiTableFieldDataColumnType, +} from '@elastic/eui'; + +import { HeaderSection } from '../header_section'; +import { InspectButton, InspectButtonContainer } from '../inspect'; +import * as i18n from './translations'; + +import { RuleRisk } from '../../../../common/search_strategy'; + +import { RuleLink } from '../../../detections/pages/detection_engine/rules/all/use_columns'; + +export interface TopRiskScoreContributorsProps { + loading: boolean; + rules?: RuleRisk[]; + queryId: string; + toggleStatus: boolean; + toggleQuery?: (status: boolean) => void; +} +interface TableItem { + rank: number; + name: string; + id: string; +} + +const columns: Array> = [ + { + name: i18n.RANK_TITLE, + field: 'rank', + width: '45px', + align: 'right', + }, + { + name: i18n.RULE_NAME_TITLE, + field: 'name', + sortable: true, + truncateText: true, + render: (value: TableItem['name'], { id }: TableItem) => + id ? : value, + }, +]; + +const PAGE_SIZE = 5; + +const TopRiskScoreContributorsComponent: React.FC = ({ + rules = [], + loading, + queryId, + toggleStatus, + toggleQuery, +}) => { + const items = useMemo(() => { + return rules + ?.sort((a, b) => b.rule_risk - a.rule_risk) + .map(({ rule_name: name, rule_id: id }, i) => ({ rank: i + 1, name, id })); + }, [rules]); + + const tablePagination = useMemo( + () => ({ + showPerPageOptions: false, + pageSize: PAGE_SIZE, + totalItemCount: items.length, + }), + [items.length] + ); + + return ( + + + + + + + {toggleStatus && ( + + + + )} + + + {toggleStatus && ( + + + + + + )} + + + ); +}; + +export const TopRiskScoreContributors = React.memo(TopRiskScoreContributorsComponent); +TopRiskScoreContributors.displayName = 'TopRiskScoreContributors'; diff --git a/x-pack/plugins/security_solution/public/hosts/components/top_host_score_contributors/translations.ts b/x-pack/plugins/security_solution/public/common/components/top_risk_score_contributors/translations.ts similarity index 100% rename from x-pack/plugins/security_solution/public/hosts/components/top_host_score_contributors/translations.ts rename to x-pack/plugins/security_solution/public/common/components/top_risk_score_contributors/translations.ts diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/constants.ts b/x-pack/plugins/security_solution/public/common/components/url_state/constants.ts index 3b319b810a66e..dcedec945575d 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/constants.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/constants.ts @@ -37,4 +37,6 @@ export type UrlStateType = | 'network' | 'overview' | 'rules' - | 'timeline'; + | 'timeline' + | 'threat_hunting' + | 'dashboards'; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/index.test.tsx index b06fde164af76..162f321b33f6c 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/index.test.tsx @@ -95,6 +95,7 @@ describe('VisualizationActions', () => { addError: jest.fn(), addSuccess: jest.fn(), addWarning: jest.fn(), + remove: jest.fn(), }, }, http: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.test.ts b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.test.ts index 1bd188d01cb51..ce03d159df5a9 100644 --- a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.test.ts @@ -47,6 +47,7 @@ jest.mock('../../../lib/kibana', () => ({ addError: jest.fn(), addSuccess: jest.fn(), addWarning: jest.fn(), + remove: jest.fn(), }), })); diff --git a/x-pack/plugins/security_solution/public/common/containers/query_client/query_client_provider.tsx b/x-pack/plugins/security_solution/public/common/containers/query_client/query_client_provider.tsx index 9d2f9ca9f48fa..2f67dc2734fed 100644 --- a/x-pack/plugins/security_solution/public/common/containers/query_client/query_client_provider.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/query_client/query_client_provider.tsx @@ -8,14 +8,38 @@ import React, { memo, PropsWithChildren, useMemo } from 'react'; import { QueryClient, QueryClientProvider } from 'react-query'; +type QueryClientOptionsProp = ConstructorParameters[0]; + +/** + * A security solution specific react-query query client that sets defaults + */ +export class SecuritySolutionQueryClient extends QueryClient { + constructor(options: QueryClientOptionsProp = {}) { + const optionsWithDefaults: QueryClientOptionsProp = { + ...options, + defaultOptions: { + ...(options.defaultOptions ?? {}), + queries: { + refetchIntervalInBackground: false, + refetchOnWindowFocus: false, + refetchOnMount: true, + keepPreviousData: true, + ...(options?.defaultOptions?.queries ?? {}), + }, + }, + }; + super(optionsWithDefaults); + } +} + export type ReactQueryClientProviderProps = PropsWithChildren<{ - queryClient?: QueryClient; + queryClient?: SecuritySolutionQueryClient; }>; export const ReactQueryClientProvider = memo( ({ queryClient, children }) => { const client = useMemo(() => { - return queryClient || new QueryClient(); + return queryClient || new SecuritySolutionQueryClient(); }, [queryClient]); return {children}; } diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.test.tsx b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.test.tsx index 1d02d608c0e92..a949478593466 100644 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.test.tsx @@ -60,6 +60,7 @@ jest.mock('../../lib/kibana', () => ({ addError: jest.fn(), addSuccess: jest.fn(), addWarning: mockAddWarning, + remove: jest.fn(), }), useKibana: () => ({ services: { diff --git a/x-pack/plugins/security_solution/public/common/hooks/eql/use_eql_preview.ts b/x-pack/plugins/security_solution/public/common/hooks/eql/use_eql_preview.ts index 4addd9048a565..20a9ccbefa900 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/eql/use_eql_preview.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/eql/use_eql_preview.ts @@ -17,9 +17,9 @@ import { isPartialResponse, EqlSearchStrategyRequest, EqlSearchStrategyResponse, + EQL_SEARCH_STRATEGY, } from '@kbn/data-plugin/common'; import { AbortError } from '@kbn/kibana-utils-plugin/common'; -import { EQL_SEARCH_STRATEGY } from '@kbn/data-enhanced-plugin/public'; import * as i18n from '../translations'; import { useKibana } from '../../lib/kibana'; import { formatInspect, getEqlAggsData } from './helpers'; diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.mock.ts b/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.mock.ts index c0bb52b20c534..ae3783e82cdbf 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.mock.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.mock.ts @@ -11,6 +11,7 @@ const createAppToastsMock = (): jest.Mocked => ({ addError: jest.fn(), addSuccess: jest.fn(), addWarning: jest.fn(), + remove: jest.fn(), api: { get$: jest.fn(), add: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.test.ts b/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.test.ts index 9bea6282e3ad6..359d29be7cd08 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.test.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.test.ts @@ -30,15 +30,18 @@ describe('useAppToasts', () => { let addErrorMock: jest.Mock; let addSuccessMock: jest.Mock; let addWarningMock: jest.Mock; + let removeMock: jest.Mock; beforeEach(() => { addErrorMock = jest.fn(); addSuccessMock = jest.fn(); addWarningMock = jest.fn(); + removeMock = jest.fn(); (useToasts as jest.Mock).mockImplementation(() => ({ addError: addErrorMock, addSuccess: addSuccessMock, addWarning: addWarningMock, + remove: removeMock, })); }); diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.ts b/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.ts index 4b3b28a54c642..d9c3713f3a4ba 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.ts @@ -19,7 +19,7 @@ import { IEsError, isEsError } from '@kbn/data-plugin/public'; import { ErrorToastOptions, ToastsStart, Toast } from '@kbn/core/public'; import { useToasts } from '../lib/kibana'; -export type UseAppToasts = Pick & { +export type UseAppToasts = Pick & { api: ToastsStart; addError: (error: unknown, options: ErrorToastOptions) => Toast; }; @@ -36,6 +36,7 @@ export const useAppToasts = (): UseAppToasts => { const addError = useRef(toasts.addError.bind(toasts)).current; const addSuccess = useRef(toasts.addSuccess.bind(toasts)).current; const addWarning = useRef(toasts.addWarning.bind(toasts)).current; + const remove = useRef(toasts.remove.bind(toasts)).current; const _addError = useCallback( (error: unknown, options: ErrorToastOptions) => { @@ -46,8 +47,8 @@ export const useAppToasts = (): UseAppToasts => { ); return useMemo( - () => ({ api: toasts, addError: _addError, addSuccess, addWarning }), - [_addError, addSuccess, addWarning, toasts] + () => ({ api: toasts, addError: _addError, addSuccess, addWarning, remove }), + [_addError, addSuccess, addWarning, remove, toasts] ); }; diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_dashboard_button_href.ts b/x-pack/plugins/security_solution/public/common/hooks/use_dashboard_button_href.ts new file mode 100644 index 0000000000000..39e10a88087c7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/use_dashboard_button_href.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 { useState, useEffect } from 'react'; +import { SavedObjectAttributes } from '@kbn/securitysolution-io-ts-alerting-types'; +import { useKibana } from '../lib/kibana'; + +export const dashboardRequestBody = (title: string) => ({ + type: 'dashboard', + search: `"${title}"`, + fields: ['title'], +}); + +export const useDashboardButtonHref = ({ + to, + from, + title, +}: { + to: string; + from: string; + title: string; +}) => { + const { + dashboard, + savedObjects: { client: savedObjectsClient }, + } = useKibana().services; + + const [buttonHref, setButtonHref] = useState(); + + useEffect(() => { + if (dashboard?.locator && savedObjectsClient) { + savedObjectsClient.find(dashboardRequestBody(title)).then( + async (DashboardsSO?: { + savedObjects?: Array<{ + attributes?: SavedObjectAttributes; + id?: string; + }>; + }) => { + if (DashboardsSO?.savedObjects?.length) { + const dashboardUrl = await dashboard?.locator?.getUrl({ + dashboardId: DashboardsSO.savedObjects[0].id, + timeRange: { + to, + from, + }, + }); + setButtonHref(dashboardUrl); + } + } + ); + } + }, [dashboard, from, savedObjectsClient, to, title]); + + return { + buttonHref, + }; +}; diff --git a/x-pack/plugins/security_solution/public/common/images/hosts_page.png b/x-pack/plugins/security_solution/public/common/images/hosts_page.png new file mode 100644 index 0000000000000..e6edd689c3e47 Binary files /dev/null and b/x-pack/plugins/security_solution/public/common/images/hosts_page.png differ diff --git a/x-pack/plugins/security_solution/public/common/images/network_page.png b/x-pack/plugins/security_solution/public/common/images/network_page.png new file mode 100644 index 0000000000000..45ab4f965e107 Binary files /dev/null and b/x-pack/plugins/security_solution/public/common/images/network_page.png differ diff --git a/x-pack/plugins/security_solution/public/common/images/overview_page.png b/x-pack/plugins/security_solution/public/common/images/overview_page.png new file mode 100644 index 0000000000000..90eb8fabb3ac2 Binary files /dev/null and b/x-pack/plugins/security_solution/public/common/images/overview_page.png differ diff --git a/x-pack/plugins/security_solution/public/common/images/users_page.png b/x-pack/plugins/security_solution/public/common/images/users_page.png new file mode 100644 index 0000000000000..6180a50389719 Binary files /dev/null and b/x-pack/plugins/security_solution/public/common/images/users_page.png differ diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts index 0097e31cfee8a..09e7c1fa6e8c8 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts @@ -157,6 +157,10 @@ export const createStartServicesMock = ( theme: { theme$: themeServiceMock.createTheme$(), }, + timelines: { + getLastUpdated: jest.fn(), + getFieldBrowser: jest.fn(), + }, } as unknown as StartServices; }; diff --git a/x-pack/plugins/security_solution/public/common/lib/triggers_actions_ui/register_alerts_table_configuration.tsx b/x-pack/plugins/security_solution/public/common/lib/triggers_actions_ui/register_alerts_table_configuration.tsx new file mode 100644 index 0000000000000..3a1bcee1eed51 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/lib/triggers_actions_ui/register_alerts_table_configuration.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Storage } from '@kbn/kibana-utils-plugin/public'; +import { AlertsTableConfigurationRegistryContract } from '@kbn/triggers-actions-ui-plugin/public'; + +import { APP_ID } from '../../../../common/constants'; +import { getTimelinesInStorageByIds } from '../../../timelines/containers/local_storage'; +import { TimelineId } from '../../../../common/types'; +import { columns } from '../../../detections/configurations/security_solution_detections'; + +const registerAlertsTableConfiguration = ( + registry: AlertsTableConfigurationRegistryContract, + storage: Storage +) => { + if (registry.has(APP_ID)) { + return; + } + const timelineStorage = getTimelinesInStorageByIds(storage, [TimelineId.detectionsPage]); + const alertColumns = timelineStorage?.[TimelineId.detectionsPage]?.columns ?? columns; + registry.register({ + id: APP_ID, + columns: alertColumns, + }); +}; + +export { registerAlertsTableConfiguration }; diff --git a/x-pack/plugins/security_solution/public/common/links/app_links.ts b/x-pack/plugins/security_solution/public/common/links/app_links.ts new file mode 100644 index 0000000000000..4a972bd5deb1f --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/links/app_links.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 { i18n } from '@kbn/i18n'; +import { SecurityPageName, THREAT_HUNTING_PATH } from '../../../common/constants'; +import { THREAT_HUNTING } from '../../app/translations'; +import { FEATURE, LinkItem, UserPermissions } from './types'; +import { links as hostsLinks } from '../../hosts/links'; +import { links as detectionLinks } from '../../detections/links'; +import { links as networkLinks } from '../../network/links'; +import { links as usersLinks } from '../../users/links'; +import { links as timelinesLinks } from '../../timelines/links'; +import { getCasesLinkItems } from '../../cases/links'; +import { links as managementLinks } from '../../management/links'; +import { gettingStartedLinks, dashboardsLandingLinks } from '../../overview/links'; + +export const appLinks: Readonly = Object.freeze([ + gettingStartedLinks, + dashboardsLandingLinks, + detectionLinks, + { + id: SecurityPageName.threatHuntingLanding, + title: THREAT_HUNTING, + path: THREAT_HUNTING_PATH, + globalNavEnabled: false, + features: [FEATURE.general], + globalSearchKeywords: [ + i18n.translate('xpack.securitySolution.appLinks.threatHunting', { + defaultMessage: 'Threat hunting', + }), + ], + links: [hostsLinks, networkLinks, usersLinks], + }, + timelinesLinks, + getCasesLinkItems(), + managementLinks, +]); + +export const getAppLinks = async ({ + enableExperimental, + license, + capabilities, +}: UserPermissions) => { + // OLM team, implement async behavior here + return appLinks; +}; diff --git a/x-pack/plugins/security_solution/public/common/links/index.tsx b/x-pack/plugins/security_solution/public/common/links/index.tsx new file mode 100644 index 0000000000000..6d8e99cd416d2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/links/index.tsx @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './links'; diff --git a/x-pack/plugins/security_solution/public/common/links/links.test.ts b/x-pack/plugins/security_solution/public/common/links/links.test.ts new file mode 100644 index 0000000000000..d8f6711cfc629 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/links/links.test.ts @@ -0,0 +1,421 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + getAncestorLinksInfo, + getDeepLinks, + getInitialDeepLinks, + getLinkInfo, + getNavLinkItems, + needsUrlState, +} from './links'; +import { CASES_FEATURE_ID, SecurityPageName, SERVER_APP_ID } from '../../../common/constants'; +import { Capabilities } from '@kbn/core/types'; +import { AppDeepLink } from '@kbn/core/public'; +import { mockGlobalState } from '../mock'; +import { NavLinkItem } from './types'; +import { LicenseType } from '@kbn/licensing-plugin/common/types'; +import { LicenseService } from '../../../common/license'; + +const mockExperimentalDefaults = mockGlobalState.app.enableExperimental; +const mockCapabilities = { + [CASES_FEATURE_ID]: { read_cases: true, crud_cases: true }, + [SERVER_APP_ID]: { show: true }, +} as unknown as Capabilities; + +const findDeepLink = (id: string, deepLinks: AppDeepLink[]): AppDeepLink | null => + deepLinks.reduce((deepLinkFound: AppDeepLink | null, deepLink) => { + if (deepLinkFound !== null) { + return deepLinkFound; + } + if (deepLink.id === id) { + return deepLink; + } + if (deepLink.deepLinks) { + return findDeepLink(id, deepLink.deepLinks); + } + return null; + }, null); + +const findNavLink = (id: SecurityPageName, navLinks: NavLinkItem[]): NavLinkItem | null => + navLinks.reduce((deepLinkFound: NavLinkItem | null, deepLink) => { + if (deepLinkFound !== null) { + return deepLinkFound; + } + if (deepLink.id === id) { + return deepLink; + } + if (deepLink.links) { + return findNavLink(id, deepLink.links); + } + return null; + }, null); + +// remove filter once new nav is live +const allPages = Object.values(SecurityPageName).filter( + (pageName) => + pageName !== SecurityPageName.explore && + pageName !== SecurityPageName.detections && + pageName !== SecurityPageName.investigate +); +const casesPages = [ + SecurityPageName.case, + SecurityPageName.caseConfigure, + SecurityPageName.caseCreate, +]; +const featureFlagPages = [ + SecurityPageName.detectionAndResponse, + SecurityPageName.hostsAuthentications, + SecurityPageName.hostsRisk, + SecurityPageName.usersRisk, +]; +const premiumPages = [ + SecurityPageName.caseConfigure, + SecurityPageName.hostsAnomalies, + SecurityPageName.networkAnomalies, + SecurityPageName.usersAnomalies, + SecurityPageName.detectionAndResponse, + SecurityPageName.hostsRisk, + SecurityPageName.usersRisk, +]; +const nonCasesPages = allPages.reduce( + (acc: SecurityPageName[], p) => + casesPages.includes(p) || featureFlagPages.includes(p) ? acc : [p, ...acc], + [] +); + +const licenseBasicMock = jest.fn().mockImplementation((arg: LicenseType) => arg === 'basic'); +const licensePremiumMock = jest.fn().mockReturnValue(true); +const mockLicense = { + isAtLeast: licensePremiumMock, +} as unknown as LicenseService; + +describe('security app link helpers', () => { + beforeEach(() => { + mockLicense.isAtLeast = licensePremiumMock; + }); + describe('getInitialDeepLinks', () => { + it('should return all pages in the app', () => { + const links = getInitialDeepLinks(); + allPages.forEach((page) => expect(findDeepLink(page, links)).toBeTruthy()); + }); + }); + describe('getDeepLinks', () => { + it('basicLicense should return only basic links', async () => { + mockLicense.isAtLeast = licenseBasicMock; + + const links = await getDeepLinks({ + enableExperimental: mockExperimentalDefaults, + license: mockLicense, + capabilities: mockCapabilities, + }); + expect(findDeepLink(SecurityPageName.hostsAnomalies, links)).toBeFalsy(); + allPages.forEach((page) => { + if (premiumPages.includes(page)) { + return expect(findDeepLink(page, links)).toBeFalsy(); + } + if (featureFlagPages.includes(page)) { + // ignore feature flag pages + return; + } + expect(findDeepLink(page, links)).toBeTruthy(); + }); + }); + it('platinumLicense should return all links', async () => { + const links = await getDeepLinks({ + enableExperimental: mockExperimentalDefaults, + license: mockLicense, + capabilities: mockCapabilities, + }); + allPages.forEach((page) => { + if (premiumPages.includes(page) && !featureFlagPages.includes(page)) { + return expect(findDeepLink(page, links)).toBeTruthy(); + } + if (featureFlagPages.includes(page)) { + // ignore feature flag pages + return; + } + expect(findDeepLink(page, links)).toBeTruthy(); + }); + }); + it('hideWhenExperimentalKey hides entry when key = true', async () => { + const links = await getDeepLinks({ + enableExperimental: { ...mockExperimentalDefaults, usersEnabled: true }, + license: mockLicense, + capabilities: mockCapabilities, + }); + expect(findDeepLink(SecurityPageName.hostsAuthentications, links)).toBeFalsy(); + }); + it('hideWhenExperimentalKey shows entry when key = false', async () => { + const links = await getDeepLinks({ + enableExperimental: { ...mockExperimentalDefaults, usersEnabled: false }, + license: mockLicense, + capabilities: mockCapabilities, + }); + expect(findDeepLink(SecurityPageName.hostsAuthentications, links)).toBeTruthy(); + }); + it('experimentalKey shows entry when key = false', async () => { + const links = await getDeepLinks({ + enableExperimental: { + ...mockExperimentalDefaults, + riskyHostsEnabled: false, + riskyUsersEnabled: false, + detectionResponseEnabled: false, + }, + license: mockLicense, + capabilities: mockCapabilities, + }); + expect(findDeepLink(SecurityPageName.hostsRisk, links)).toBeFalsy(); + expect(findDeepLink(SecurityPageName.usersRisk, links)).toBeFalsy(); + expect(findDeepLink(SecurityPageName.detectionAndResponse, links)).toBeFalsy(); + }); + it('experimentalKey shows entry when key = true', async () => { + const links = await getDeepLinks({ + enableExperimental: { + ...mockExperimentalDefaults, + riskyHostsEnabled: true, + riskyUsersEnabled: true, + detectionResponseEnabled: true, + }, + license: mockLicense, + capabilities: mockCapabilities, + }); + expect(findDeepLink(SecurityPageName.hostsRisk, links)).toBeTruthy(); + expect(findDeepLink(SecurityPageName.usersRisk, links)).toBeTruthy(); + expect(findDeepLink(SecurityPageName.detectionAndResponse, links)).toBeTruthy(); + }); + + it('Removes siem features when siem capabilities are false', async () => { + const capabilities = { + ...mockCapabilities, + [SERVER_APP_ID]: { show: false }, + } as unknown as Capabilities; + const links = await getDeepLinks({ + enableExperimental: mockExperimentalDefaults, + license: mockLicense, + capabilities, + }); + nonCasesPages.forEach((page) => { + // investigate is active for both Cases and Timelines pages + if (page === SecurityPageName.investigate) { + return expect(findDeepLink(page, links)).toBeTruthy(); + } + return expect(findDeepLink(page, links)).toBeFalsy(); + }); + casesPages.forEach((page) => expect(findDeepLink(page, links)).toBeTruthy()); + }); + it('Removes cases features when cases capabilities are false', async () => { + const capabilities = { + ...mockCapabilities, + [CASES_FEATURE_ID]: { read_cases: false, crud_cases: false }, + } as unknown as Capabilities; + const links = await getDeepLinks({ + enableExperimental: mockExperimentalDefaults, + license: mockLicense, + capabilities, + }); + nonCasesPages.forEach((page) => expect(findDeepLink(page, links)).toBeTruthy()); + casesPages.forEach((page) => expect(findDeepLink(page, links)).toBeFalsy()); + }); + }); + + describe('getNavLinkItems', () => { + it('basicLicense should return only basic links', () => { + mockLicense.isAtLeast = licenseBasicMock; + const links = getNavLinkItems({ + enableExperimental: mockExperimentalDefaults, + license: mockLicense, + capabilities: mockCapabilities, + }); + expect(findNavLink(SecurityPageName.hostsAnomalies, links)).toBeFalsy(); + allPages.forEach((page) => { + if (premiumPages.includes(page)) { + return expect(findNavLink(page, links)).toBeFalsy(); + } + if (featureFlagPages.includes(page)) { + // ignore feature flag pages + return; + } + expect(findNavLink(page, links)).toBeTruthy(); + }); + }); + it('platinumLicense should return all links', () => { + const links = getNavLinkItems({ + enableExperimental: mockExperimentalDefaults, + license: mockLicense, + capabilities: mockCapabilities, + }); + allPages.forEach((page) => { + if (premiumPages.includes(page) && !featureFlagPages.includes(page)) { + return expect(findNavLink(page, links)).toBeTruthy(); + } + if (featureFlagPages.includes(page)) { + // ignore feature flag pages + return; + } + expect(findNavLink(page, links)).toBeTruthy(); + }); + }); + it('hideWhenExperimentalKey hides entry when key = true', () => { + const links = getNavLinkItems({ + enableExperimental: { ...mockExperimentalDefaults, usersEnabled: true }, + license: mockLicense, + capabilities: mockCapabilities, + }); + expect(findNavLink(SecurityPageName.hostsAuthentications, links)).toBeFalsy(); + }); + it('hideWhenExperimentalKey shows entry when key = false', () => { + const links = getNavLinkItems({ + enableExperimental: { ...mockExperimentalDefaults, usersEnabled: false }, + license: mockLicense, + capabilities: mockCapabilities, + }); + expect(findNavLink(SecurityPageName.hostsAuthentications, links)).toBeTruthy(); + }); + it('experimentalKey shows entry when key = false', () => { + const links = getNavLinkItems({ + enableExperimental: { + ...mockExperimentalDefaults, + riskyHostsEnabled: false, + riskyUsersEnabled: false, + detectionResponseEnabled: false, + }, + license: mockLicense, + capabilities: mockCapabilities, + }); + expect(findNavLink(SecurityPageName.hostsRisk, links)).toBeFalsy(); + expect(findNavLink(SecurityPageName.usersRisk, links)).toBeFalsy(); + expect(findNavLink(SecurityPageName.detectionAndResponse, links)).toBeFalsy(); + }); + it('experimentalKey shows entry when key = true', () => { + const links = getNavLinkItems({ + enableExperimental: { + ...mockExperimentalDefaults, + riskyHostsEnabled: true, + riskyUsersEnabled: true, + detectionResponseEnabled: true, + }, + license: mockLicense, + capabilities: mockCapabilities, + }); + expect(findNavLink(SecurityPageName.hostsRisk, links)).toBeTruthy(); + expect(findNavLink(SecurityPageName.usersRisk, links)).toBeTruthy(); + expect(findNavLink(SecurityPageName.detectionAndResponse, links)).toBeTruthy(); + }); + + it('Removes siem features when siem capabilities are false', () => { + const capabilities = { + ...mockCapabilities, + [SERVER_APP_ID]: { show: false }, + } as unknown as Capabilities; + const links = getNavLinkItems({ + enableExperimental: mockExperimentalDefaults, + license: mockLicense, + capabilities, + }); + nonCasesPages.forEach((page) => { + // investigate is active for both Cases and Timelines pages + if (page === SecurityPageName.investigate) { + return expect(findNavLink(page, links)).toBeTruthy(); + } + return expect(findNavLink(page, links)).toBeFalsy(); + }); + casesPages.forEach((page) => expect(findNavLink(page, links)).toBeTruthy()); + }); + it('Removes cases features when cases capabilities are false', () => { + const capabilities = { + ...mockCapabilities, + [CASES_FEATURE_ID]: { read_cases: false, crud_cases: false }, + } as unknown as Capabilities; + const links = getNavLinkItems({ + enableExperimental: mockExperimentalDefaults, + license: mockLicense, + capabilities, + }); + nonCasesPages.forEach((page) => expect(findNavLink(page, links)).toBeTruthy()); + casesPages.forEach((page) => expect(findNavLink(page, links)).toBeFalsy()); + }); + }); + + describe('getAncestorLinksInfo', () => { + it('finds flattened links for hosts', () => { + const hierarchy = getAncestorLinksInfo(SecurityPageName.hosts); + expect(hierarchy).toEqual([ + { + features: ['siem.show'], + globalNavEnabled: false, + globalSearchKeywords: ['Threat hunting'], + id: 'threat-hunting', + path: '/threat_hunting', + title: 'Threat Hunting', + }, + { + globalNavEnabled: true, + globalNavOrder: 9002, + globalSearchEnabled: true, + globalSearchKeywords: ['Hosts'], + id: 'hosts', + path: '/hosts', + title: 'Hosts', + }, + ]); + }); + it('finds flattened links for uncommonProcesses', () => { + const hierarchy = getAncestorLinksInfo(SecurityPageName.uncommonProcesses); + expect(hierarchy).toEqual([ + { + features: ['siem.show'], + globalNavEnabled: false, + globalSearchKeywords: ['Threat hunting'], + id: 'threat-hunting', + path: '/threat_hunting', + title: 'Threat Hunting', + }, + { + globalNavEnabled: true, + globalNavOrder: 9002, + globalSearchEnabled: true, + globalSearchKeywords: ['Hosts'], + id: 'hosts', + path: '/hosts', + title: 'Hosts', + }, + { + id: 'uncommon_processes', + path: '/hosts/uncommonProcesses', + title: 'Uncommon Processes', + }, + ]); + }); + }); + + describe('needsUrlState', () => { + it('returns true when url state exists for page', () => { + const needsUrl = needsUrlState(SecurityPageName.hosts); + expect(needsUrl).toEqual(true); + }); + it('returns false when url state does not exist for page', () => { + const needsUrl = needsUrlState(SecurityPageName.landing); + expect(needsUrl).toEqual(false); + }); + }); + + describe('getLinkInfo', () => { + it('gets information for an individual link', () => { + const linkInfo = getLinkInfo(SecurityPageName.hosts); + expect(linkInfo).toEqual({ + globalNavEnabled: true, + globalNavOrder: 9002, + globalSearchEnabled: true, + globalSearchKeywords: ['Hosts'], + id: 'hosts', + path: '/hosts', + title: 'Hosts', + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/links/links.ts b/x-pack/plugins/security_solution/public/common/links/links.ts new file mode 100644 index 0000000000000..290a1f3fbd820 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/links/links.ts @@ -0,0 +1,197 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AppDeepLink, AppNavLinkStatus, Capabilities } from '@kbn/core/public'; +import { get } from 'lodash'; +import { SecurityPageName } from '../../../common/constants'; +import { appLinks, getAppLinks } from './app_links'; +import { + Feature, + LinkInfo, + LinkItem, + NavLinkItem, + NormalizedLink, + NormalizedLinks, + UserPermissions, +} from './types'; + +const createDeepLink = (link: LinkItem, linkProps?: UserPermissions): AppDeepLink => ({ + id: link.id, + path: link.path, + title: link.title, + ...(link.links && link.links.length + ? { + deepLinks: reduceLinks({ + links: link.links, + linkProps, + formatFunction: createDeepLink, + }), + } + : {}), + ...(link.icon != null ? { euiIconType: link.icon } : {}), + ...(link.image != null ? { icon: link.image } : {}), + ...(link.globalSearchKeywords != null ? { keywords: link.globalSearchKeywords } : {}), + ...(link.globalNavEnabled != null + ? { navLinkStatus: link.globalNavEnabled ? AppNavLinkStatus.visible : AppNavLinkStatus.hidden } + : {}), + ...(link.globalNavOrder != null ? { order: link.globalNavOrder } : {}), + ...(link.globalSearchEnabled != null ? { searchable: link.globalSearchEnabled } : {}), +}); + +const createNavLinkItem = (link: LinkItem, linkProps?: UserPermissions): NavLinkItem => ({ + id: link.id, + path: link.path, + title: link.title, + ...(link.description != null ? { description: link.description } : {}), + ...(link.icon != null ? { icon: link.icon } : {}), + ...(link.image != null ? { image: link.image } : {}), + ...(link.links && link.links.length + ? { + links: reduceLinks({ + links: link.links, + linkProps, + formatFunction: createNavLinkItem, + }), + } + : {}), + ...(link.skipUrlState != null ? { skipUrlState: link.skipUrlState } : {}), +}); + +const hasFeaturesCapability = ( + features: Feature[] | undefined, + capabilities: Capabilities +): boolean => { + if (!features) { + return true; + } + return features.some((featureKey) => get(capabilities, featureKey, false)); +}; + +const isLinkAllowed = (link: LinkItem, linkProps?: UserPermissions) => + !( + linkProps != null && + // exclude link when license is basic and link is premium + ((linkProps.license && !linkProps.license.isAtLeast(link.licenseType ?? 'basic')) || + // exclude link when enableExperimental[hideWhenExperimentalKey] is enabled and link has hideWhenExperimentalKey + (link.hideWhenExperimentalKey != null && + linkProps.enableExperimental[link.hideWhenExperimentalKey]) || + // exclude link when enableExperimental[experimentalKey] is disabled and link has experimentalKey + (link.experimentalKey != null && !linkProps.enableExperimental[link.experimentalKey]) || + // exclude link when link is not part of enabled feature capabilities + (linkProps.capabilities != null && + !hasFeaturesCapability(link.features, linkProps.capabilities))) + ); + +export function reduceLinks({ + links, + linkProps, + formatFunction, +}: { + links: Readonly; + linkProps?: UserPermissions; + formatFunction: (link: LinkItem, linkProps?: UserPermissions) => T; +}): T[] { + return links.reduce( + (deepLinks: T[], link: LinkItem) => + isLinkAllowed(link, linkProps) ? [...deepLinks, formatFunction(link, linkProps)] : deepLinks, + [] + ); +} + +export const getInitialDeepLinks = (): AppDeepLink[] => { + return appLinks.map((link) => createDeepLink(link)); +}; + +export const getDeepLinks = async ({ + enableExperimental, + license, + capabilities, +}: UserPermissions): Promise => { + const links = await getAppLinks({ enableExperimental, license, capabilities }); + return reduceLinks({ + links, + linkProps: { enableExperimental, license, capabilities }, + formatFunction: createDeepLink, + }); +}; + +export const getNavLinkItems = ({ + enableExperimental, + license, + capabilities, +}: UserPermissions): NavLinkItem[] => + reduceLinks({ + links: appLinks, + linkProps: { enableExperimental, license, capabilities }, + formatFunction: createNavLinkItem, + }); + +/** + * Recursive function to create the `NormalizedLinks` structure from a `LinkItem` array parameter + */ +const getNormalizedLinks = ( + currentLinks: Readonly, + parentId?: SecurityPageName +): NormalizedLinks => { + const result = currentLinks.reduce>( + (normalized, { links, ...currentLink }) => { + normalized[currentLink.id] = { + ...currentLink, + parentId, + }; + if (links && links.length > 0) { + Object.assign(normalized, getNormalizedLinks(links, currentLink.id)); + } + return normalized; + }, + {} + ); + return result as NormalizedLinks; +}; + +/** + * Normalized indexed version of the global `links` array, referencing the parent by id, instead of having nested links children + */ +const normalizedLinks: Readonly = Object.freeze(getNormalizedLinks(appLinks)); + +/** + * Returns the `NormalizedLink` from a link id parameter. + * The object reference is frozen to make sure it is not mutated by the caller. + */ +const getNormalizedLink = (id: SecurityPageName): Readonly => + Object.freeze(normalizedLinks[id]); + +/** + * Returns the `LinkInfo` from a link id parameter + */ +export const getLinkInfo = (id: SecurityPageName): LinkInfo => { + // discards the parentId and creates the linkInfo copy. + const { parentId, ...linkInfo } = getNormalizedLink(id); + return linkInfo; +}; + +/** + * Returns the `LinkInfo` of all the ancestors to the parameter id link, also included. + */ +export const getAncestorLinksInfo = (id: SecurityPageName): LinkInfo[] => { + const ancestors: LinkInfo[] = []; + let currentId: SecurityPageName | undefined = id; + while (currentId) { + const { parentId, ...linkInfo } = getNormalizedLink(currentId); + ancestors.push(linkInfo); + currentId = parentId; + } + return ancestors.reverse(); +}; + +/** + * Returns `true` if the links needs to carry the application state in the url. + * Defaults to `true` if the `skipUrlState` property of the `LinkItem` is `undefined`. + */ +export const needsUrlState = (id: SecurityPageName): boolean => { + return !getNormalizedLink(id).skipUrlState; +}; diff --git a/x-pack/plugins/security_solution/public/common/links/types.ts b/x-pack/plugins/security_solution/public/common/links/types.ts new file mode 100644 index 0000000000000..eea348b3df737 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/links/types.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 { Capabilities } from '@kbn/core/types'; +import { LicenseType } from '@kbn/licensing-plugin/common/types'; +import { LicenseService } from '../../../common/license'; +import { ExperimentalFeatures } from '../../../common/experimental_features'; +import { CASES_FEATURE_ID, SecurityPageName, SERVER_APP_ID } from '../../../common/constants'; + +export const FEATURE = { + general: `${SERVER_APP_ID}.show`, + casesRead: `${CASES_FEATURE_ID}.read_cases`, + casesCrud: `${CASES_FEATURE_ID}.crud_cases`, +}; + +export type Feature = Readonly; + +export interface UserPermissions { + enableExperimental: ExperimentalFeatures; + license?: LicenseService; + capabilities?: Capabilities; +} + +export interface LinkItem { + description?: string; + disabled?: boolean; // default false + /** + * Displays deep link when feature flag is enabled. + */ + experimentalKey?: keyof ExperimentalFeatures; + features?: Feature[]; + /** + * Hides deep link when feature flag is enabled. + */ + globalNavEnabled?: boolean; // default false + globalNavOrder?: number; + globalSearchEnabled?: boolean; + globalSearchKeywords?: string[]; + hideWhenExperimentalKey?: keyof ExperimentalFeatures; + icon?: string; + id: SecurityPageName; + image?: string; + isBeta?: boolean; + licenseType?: LicenseType; + links?: LinkItem[]; + path: string; + skipUrlState?: boolean; // defaults to false + title: string; +} + +export interface NavLinkItem { + description?: string; + icon?: string; + id: SecurityPageName; + links?: NavLinkItem[]; + image?: string; + path: string; + title: string; + skipUrlState?: boolean; // default to false +} + +export type LinkInfo = Omit; +export type NormalizedLink = LinkInfo & { parentId?: SecurityPageName }; +export type NormalizedLinks = Record; 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 3c82161851dd3..539728291156f 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 @@ -55,6 +55,7 @@ jest.mock('../../../../common/lib/kibana/kibana_react', () => { addWarning: jest.fn(), addError: jest.fn(), addSuccess: jest.fn(), + remove: jest.fn(), }, }, }, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.test.tsx index 0f613aff8d456..8cb29901abdad 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.test.tsx @@ -13,6 +13,10 @@ import React from 'react'; import { Ecs } from '../../../../../common/ecs'; import { mockTimelines } from '../../../../common/mock/mock_timelines_plugin'; import { mockCasesContract } from '@kbn/cases-plugin/public/mocks'; +import { initialUserPrivilegesState as mockInitialUserPrivilegesState } from '../../../../common/components/user_privileges/user_privileges_context'; +import { useUserPrivileges } from '../../../../common/components/user_privileges'; + +jest.mock('../../../../common/components/user_privileges'); const ecsRowData: Ecs = { _id: '1', @@ -71,6 +75,7 @@ const addToNewCaseButton = '[data-test-subj="add-to-new-case-action"]'; const markAsOpenButton = '[data-test-subj="open-alert-status"]'; const markAsAcknowledgedButton = '[data-test-subj="acknowledged-alert-status"]'; const markAsClosedButton = '[data-test-subj="close-alert-status"]'; +const addEndpointEventFilterButton = '[data-test-subj="add-event-filter-menu-item"]'; describe('InvestigateInResolverAction', () => { test('it render AddToCase context menu item if timelineId === TimelineId.detectionsPage', () => { @@ -107,12 +112,7 @@ describe('InvestigateInResolverAction', () => { }); test('it does NOT render AddToCase context menu item when timelineId is not in the allowed list', () => { - // In order to enable alert context menu without a timelineId, event needs to be event.kind === 'event' and agent.type === 'endpoint' - const customProps = { - ...props, - ecsRowData: { ...ecsRowData, agent: { type: ['endpoint'] }, event: { kind: ['event'] } }, - }; - const wrapper = mount(, { + const wrapper = mount(, { wrappingComponent: TestProviders, }); wrapper.find(actionMenuButton).simulate('click'); @@ -131,4 +131,84 @@ describe('InvestigateInResolverAction', () => { expect(wrapper.find(markAsAcknowledgedButton).first().exists()).toEqual(true); expect(wrapper.find(markAsClosedButton).first().exists()).toEqual(true); }); + + describe('AddEndpointEventFilter', () => { + const endpointEventProps = { + ...props, + ecsRowData: { ...ecsRowData, agent: { type: ['endpoint'] }, event: { kind: ['event'] } }, + }; + + describe('when users can access endpoint management', () => { + beforeEach(() => { + (useUserPrivileges as jest.Mock).mockReturnValue({ + ...mockInitialUserPrivilegesState(), + endpointPrivileges: { loading: false, canAccessEndpointManagement: true }, + }); + }); + + test('it disables AddEndpointEventFilter when timeline id is not host events page', () => { + const wrapper = mount( + , + { + wrappingComponent: TestProviders, + } + ); + + wrapper.find(actionMenuButton).simulate('click'); + expect(wrapper.find(addEndpointEventFilterButton).first().exists()).toEqual(true); + expect(wrapper.find(addEndpointEventFilterButton).first().props().disabled).toEqual(true); + }); + + test('it enables AddEndpointEventFilter when timeline id is host events page', () => { + const wrapper = mount( + , + { + wrappingComponent: TestProviders, + } + ); + + wrapper.find(actionMenuButton).simulate('click'); + expect(wrapper.find(addEndpointEventFilterButton).first().exists()).toEqual(true); + expect(wrapper.find(addEndpointEventFilterButton).first().props().disabled).toEqual(false); + }); + + test('it disables AddEndpointEventFilter when timeline id is host events page but is not from endpoint', () => { + const customProps = { + ...props, + ecsRowData: { ...ecsRowData, agent: { type: ['other'] }, event: { kind: ['event'] } }, + }; + const wrapper = mount( + , + { + wrappingComponent: TestProviders, + } + ); + + wrapper.find(actionMenuButton).simulate('click'); + expect(wrapper.find(addEndpointEventFilterButton).first().exists()).toEqual(true); + expect(wrapper.find(addEndpointEventFilterButton).first().props().disabled).toEqual(true); + }); + }); + describe('when users can NOT access endpoint management', () => { + beforeEach(() => { + (useUserPrivileges as jest.Mock).mockReturnValue({ + ...mockInitialUserPrivilegesState(), + endpointPrivileges: { loading: false, canAccessEndpointManagement: false }, + }); + }); + + test('it disables AddEndpointEventFilter when timeline id is host events page but cannot acces endpoint management', () => { + const wrapper = mount( + , + { + wrappingComponent: TestProviders, + } + ); + + wrapper.find(actionMenuButton).simulate('click'); + expect(wrapper.find(addEndpointEventFilterButton).first().exists()).toEqual(true); + expect(wrapper.find(addEndpointEventFilterButton).first().props().disabled).toEqual(true); + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx index a6af9febe8b3e..1427b2b3bf388 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx @@ -88,6 +88,9 @@ const AlertContextMenuComponent: React.FC indexOf(ecsRowData.event?.kind, 'event') !== -1, [ecsRowData]); + const isAgentEndpoint = useMemo(() => ecsRowData.agent?.type?.includes('endpoint'), [ecsRowData]); + + const isEndpointEvent = useMemo(() => isEvent && isAgentEndpoint, [isEvent, isAgentEndpoint]); const onButtonClick = useCallback(() => { setPopover(!isPopoverOpen); @@ -173,7 +176,14 @@ const AlertContextMenuComponent: React.FC diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_event_filter_action.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_event_filter_action.tsx index 1a56c575057f0..4327c5a69a949 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_event_filter_action.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_event_filter_action.tsx @@ -12,9 +12,11 @@ import { ACTION_ADD_EVENT_FILTER } from '../translations'; export const useEventFilterAction = ({ onAddEventFilterClick, disabled = false, + tooltipMessage, }: { onAddEventFilterClick: () => void; disabled?: boolean; + tooltipMessage?: string; }) => { const eventFilterActionItems = useMemo( () => [ @@ -23,11 +25,12 @@ export const useEventFilterAction = ({ data-test-subj="add-event-filter-menu-item" onClick={onAddEventFilterClick} disabled={disabled} + toolTipContent={tooltipMessage} > {ACTION_ADD_EVENT_FILTER} , ], - [onAddEventFilterClick, disabled] + [onAddEventFilterClick, disabled, tooltipMessage] ); return { eventFilterActionItems }; }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/translations.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/translations.ts index bdddd8ab46207..eba1fa8238d05 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/translations.ts @@ -185,6 +185,14 @@ export const ACTION_ADD_EVENT_FILTER = i18n.translate( } ); +export const ACTION_ADD_EVENT_FILTER_DISABLED_TOOLTIP = i18n.translate( + 'xpack.securitySolution.detectionEngine.alerts.actions.addEventFilter.disabled.tooltip', + { + defaultMessage: + 'Endpoint event filters can be created from the Events section of the Hosts page.', + } +); + export const ACTION_ADD_ENDPOINT_EXCEPTION = i18n.translate( 'xpack.securitySolution.detectionEngine.alerts.actions.addEndpointException', { diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/use_host_isolation_action.tsx b/x-pack/plugins/security_solution/public/detections/components/host_isolation/use_host_isolation_action.tsx index 57d81d48fca05..62debf400a387 100644 --- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/use_host_isolation_action.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/use_host_isolation_action.tsx @@ -56,11 +56,15 @@ export const useHostIsolationAction = ({ agentId, }); - const isolationSupported = isIsolationSupported({ - osName: hostOsFamily, - version: agentVersion, - capabilities, - }); + const isolationSupported = useMemo(() => { + return isEndpointAlert + ? isIsolationSupported({ + osName: hostOsFamily, + version: agentVersion, + capabilities, + }) + : false; + }, [agentVersion, capabilities, hostOsFamily, isEndpointAlert]); const isIsolationAllowed = useUserPrivileges().endpointPrivileges.canIsolateHost; diff --git a/x-pack/plugins/security_solution/public/detections/components/osquery/osquery_flyout.tsx b/x-pack/plugins/security_solution/public/detections/components/osquery/osquery_flyout.tsx index 3262fc36abf75..f19601649a2f3 100644 --- a/x-pack/plugins/security_solution/public/detections/components/osquery/osquery_flyout.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/osquery/osquery_flyout.tsx @@ -5,13 +5,20 @@ * 2.0. */ -import React from 'react'; +import React, { useCallback } from 'react'; import styled from 'styled-components'; -import { EuiFlyout, EuiFlyoutFooter, EuiFlyoutBody, EuiFlyoutHeader } from '@elastic/eui'; +import { + EuiFlyout, + EuiFlyoutFooter, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiButtonEmpty, + EuiTitle, +} from '@elastic/eui'; import { useKibana } from '../../../common/lib/kibana'; import { OsqueryEventDetailsFooter } from './osquery_flyout_footer'; -import { OsqueryEventDetailsHeader } from './osquery_flyout_header'; import { ACTION_OSQUERY } from './translations'; +import { DataProvider } from '../../../timelines/components/timeline/data_providers/data_provider'; const OsqueryActionWrapper = styled.div` padding: 8px; @@ -22,11 +29,47 @@ export interface OsqueryFlyoutProps { onClose: () => void; } -export const OsqueryFlyout: React.FC = ({ agentId, onClose }) => { +const TimelineComponent = React.memo((props) => { + return ; +}); +TimelineComponent.displayName = 'TimelineComponent'; + +export const OsqueryFlyoutComponent: React.FC = ({ agentId, onClose }) => { const { - services: { osquery }, + services: { osquery, timelines }, } = useKibana(); + const { getAddToTimelineButton } = timelines.getHoverActions(); + + const handleAddToTimeline = useCallback( + (payload: { query: [string, string]; isIcon?: true }) => { + const { + query: [field, value], + isIcon, + } = payload; + const providerA: DataProvider = { + and: [], + enabled: true, + excluded: false, + id: value, + kqlQuery: '', + name: value, + queryMatch: { + field, + value, + operator: ':', + }, + }; + + return getAddToTimelineButton({ + dataProvider: providerA, + field: value, + ownFocus: false, + ...(isIcon ? { showTooltip: true } : { Component: TimelineComponent }), + }); + }, + [getAddToTimelineButton] + ); // @ts-expect-error const { OsqueryAction } = osquery; return ( @@ -36,16 +79,14 @@ export const OsqueryFlyout: React.FC = ({ agentId, onClose } size="m" onClose={onClose} > - - {ACTION_OSQUERY}
} - handleClick={onClose} - data-test-subj="flyout-header-osquery" - /> + + +

{ACTION_OSQUERY}

+
- + @@ -55,4 +96,4 @@ export const OsqueryFlyout: React.FC = ({ agentId, onClose } ); }; -OsqueryFlyout.displayName = 'OsqueryFlyout'; +export const OsqueryFlyout = React.memo(OsqueryFlyoutComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/osquery/osquery_flyout_header.tsx b/x-pack/plugins/security_solution/public/detections/components/osquery/osquery_flyout_header.tsx deleted file mode 100644 index 7a0f7f15f3e74..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/osquery/osquery_flyout_header.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiButtonEmpty, EuiText, EuiTitle } from '@elastic/eui'; -import { BACK_TO_ALERT_DETAILS } from './translations'; - -interface IProps { - primaryText: React.ReactElement; - handleClick: () => void; -} - -const OsqueryEventDetailsHeaderComponent: React.FC = ({ primaryText, handleClick }) => { - return ( - <> - - -

{BACK_TO_ALERT_DETAILS}

-
-
- {primaryText} - - ); -}; - -export const OsqueryEventDetailsHeader = React.memo(OsqueryEventDetailsHeaderComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx index 2ce403a832906..5c8d2643eb445 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx @@ -46,23 +46,7 @@ interface QueryBarDefineRuleProps { const actionTimelineToHide: ActionTimelineToShow[] = ['duplicate', 'createFrom']; -const StyledEuiFormRow = styled(EuiFormRow)` - .kbnTypeahead__items { - max-height: 45vh !important; - } - .globalQueryBar { - padding: 4px 0px 0px 0px; - .kbnQueryBar { - & > div:first-child { - margin: 0px 0px 0px 4px; - } - &__wrap, - &__textarea { - z-index: 0; - } - } - } -`; +const StyledEuiFormRow = styled(EuiFormRow)``; // TODO need to add disabled in the SearchBar @@ -283,6 +267,7 @@ export const QueryBarDefineRule = ({ savedQuery={savedQuery} onSavedQuery={onSavedQuery} hideSavedQuery={false} + displayStyle="inPage" />
)} diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/__snapshots__/index.test.tsx.snap index 1945562cb5b3d..e87b05a0cc8f8 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/__snapshots__/index.test.tsx.snap @@ -32,7 +32,6 @@ exports[`RuleActionsOverflow snapshots renders correctly against snapshot 1`] = > { start: '2001-01-01T17:00:00.000Z', end: '2001-01-02T17:00:00.000Z', queryText: '', - statusFilters: '', + statusFilters: [], signal: abortCtrl.signal, }); @@ -659,7 +659,7 @@ describe('Detections Rules API', () => { start: 'now-30', end: 'now', queryText: '', - statusFilters: '', + statusFilters: [], signal: abortCtrl.signal, }); expect(response).toEqual(responseMock); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts index cb78a7a116ae4..220926ebc1722 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { camelCase } from 'lodash'; import dateMath from '@kbn/datemath'; import { HttpStart } from '@kbn/core/public'; @@ -18,7 +19,11 @@ import { DETECTION_ENGINE_RULES_PREVIEW, detectionEngineRuleExecutionEventsUrl, } from '../../../../../common/constants'; -import { BulkAction } from '../../../../../common/detection_engine/schemas/common'; +import { + AggregateRuleExecutionEvent, + BulkAction, + RuleExecutionStatus, +} from '../../../../../common/detection_engine/schemas/common'; import { FullResponseSchema, PreviewResponse, @@ -320,11 +325,11 @@ export const exportRules = async ({ * @param start Start daterange either in UTC ISO8601 or as datemath string (e.g. `2021-12-29T02:44:41.653Z` or `now-30`) * @param end End daterange either in UTC ISO8601 or as datemath string (e.g. `2021-12-29T02:44:41.653Z` or `now/w`) * @param queryText search string in querystring format (e.g. `event.duration > 1000 OR kibana.alert.rule.execution.metrics.execution_gap_duration_s > 100`) - * @param statusFilters comma separated string of `statusFilters` (e.g. `succeeded,failed,partial failure`) + * @param statusFilters RuleExecutionStatus[] array of `statusFilters` (e.g. `succeeded,failed,partial failure`) * @param page current page to fetch * @param perPage number of results to fetch per page - * @param sortField field to sort by - * @param sortOrder what order to sort by (e.g. `asc` or `desc`) + * @param sortField keyof AggregateRuleExecutionEvent field to sort by + * @param sortOrder SortOrder what order to sort by (e.g. `asc` or `desc`) * @param signal AbortSignal Optional signal for cancelling the request * * @throws An error if response is not OK @@ -345,11 +350,11 @@ export const fetchRuleExecutionEvents = async ({ start: string; end: string; queryText?: string; - statusFilters?: string; + statusFilters?: RuleExecutionStatus[]; page?: number; perPage?: number; - sortField?: string; - sortOrder?: string; + sortField?: keyof AggregateRuleExecutionEvent; + sortOrder?: SortOrder; signal?: AbortSignal; }): Promise => { const url = detectionEngineRuleExecutionEventsUrl(ruleId); @@ -361,7 +366,7 @@ export const fetchRuleExecutionEvents = async ({ start: startDate?.utc().toISOString(), end: endDate?.utc().toISOString(), query_text: queryText, - status_filters: statusFilters, + status_filters: statusFilters?.sort()?.join(','), page, per_page: perPage, sort_field: sortField, diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/mock.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/mock.ts index 533ab6138cb09..8c1737a4519a7 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/mock.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/mock.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { GetAggregateRuleExecutionEventsResponse } from '../../../../../common/detection_engine/schemas/response'; import { FetchRulesResponse, Rule } from './types'; export const savedRuleMock: Rule = { @@ -126,3 +127,170 @@ export const rulesMock: FetchRulesResponse = { }, ], }; + +export const ruleExecutionEventsMock: GetAggregateRuleExecutionEventsResponse = { + events: [ + { + execution_uuid: 'dc45a63c-4872-4964-a2d0-bddd8b2e634d', + timestamp: '2022-04-28T21:19:08.047Z', + duration_ms: 3, + status: 'failure', + message: 'siem.queryRule:a6e61cf0-c737-11ec-9e32-e14913ffdd2d: execution failed', + num_active_alerts: 0, + num_new_alerts: 0, + num_recovered_alerts: 0, + num_triggered_actions: 0, + num_succeeded_actions: 0, + num_errored_actions: 0, + total_search_duration_ms: 0, + es_search_duration_ms: 0, + schedule_delay_ms: 2169, + timed_out: false, + indexing_duration_ms: 0, + search_duration_ms: 0, + gap_duration_s: 0, + security_status: 'failed', + security_message: 'Rule failed to execute because rule ran after it was disabled.', + }, + { + execution_uuid: '0fde9271-05d0-4bfb-8ff8-815756d28350', + timestamp: '2022-04-28T21:19:04.973Z', + duration_ms: 1446, + status: 'success', + message: + "rule executed: siem.queryRule:a6e61cf0-c737-11ec-9e32-e14913ffdd2d: 'Click here for hot fresh alerts!'", + num_active_alerts: 0, + num_new_alerts: 0, + num_recovered_alerts: 0, + num_triggered_actions: 0, + num_succeeded_actions: 0, + num_errored_actions: 0, + total_search_duration_ms: 0, + es_search_duration_ms: 0, + schedule_delay_ms: 2089, + timed_out: false, + indexing_duration_ms: 0, + search_duration_ms: 2, + gap_duration_s: 0, + security_status: 'succeeded', + security_message: 'succeeded', + }, + { + execution_uuid: '5daaa259-ded8-4a52-853e-1e7652d325d5', + timestamp: '2022-04-28T21:19:01.976Z', + duration_ms: 1395, + status: 'success', + message: + "rule executed: siem.queryRule:a6e61cf0-c737-11ec-9e32-e14913ffdd2d: 'Click here for hot fresh alerts!'", + num_active_alerts: 0, + num_new_alerts: 0, + num_recovered_alerts: 0, + num_triggered_actions: 0, + num_succeeded_actions: 0, + num_errored_actions: 0, + total_search_duration_ms: 0, + es_search_duration_ms: 1, + schedule_delay_ms: 2637, + timed_out: false, + indexing_duration_ms: 0, + search_duration_ms: 3, + gap_duration_s: 0, + security_status: 'succeeded', + security_message: 'succeeded', + }, + { + execution_uuid: 'c7223e1c-4264-4a27-8697-0d720243fafc', + timestamp: '2022-04-28T21:18:58.431Z', + duration_ms: 1815, + status: 'success', + message: + "rule executed: siem.queryRule:a6e61cf0-c737-11ec-9e32-e14913ffdd2d: 'Click here for hot fresh alerts!'", + num_active_alerts: 0, + num_new_alerts: 0, + num_recovered_alerts: 0, + num_triggered_actions: 0, + num_succeeded_actions: 0, + num_errored_actions: 0, + total_search_duration_ms: 0, + es_search_duration_ms: 1, + schedule_delay_ms: -255429, + timed_out: false, + indexing_duration_ms: 0, + search_duration_ms: 3, + gap_duration_s: 0, + security_status: 'succeeded', + security_message: 'succeeded', + }, + { + execution_uuid: '1f6ba0c1-cc36-4f45-b919-7790b8a8d670', + timestamp: '2022-04-28T21:18:13.954Z', + duration_ms: 2055, + status: 'success', + message: + "rule executed: siem.queryRule:a6e61cf0-c737-11ec-9e32-e14913ffdd2d: 'Click here for hot fresh alerts!'", + num_active_alerts: 0, + num_new_alerts: 0, + num_recovered_alerts: 0, + num_triggered_actions: 0, + num_succeeded_actions: 0, + num_errored_actions: 0, + total_search_duration_ms: 0, + es_search_duration_ms: 0, + schedule_delay_ms: 2027, + timed_out: false, + indexing_duration_ms: 0, + search_duration_ms: 0, + gap_duration_s: 0, + security_status: 'partial failure', + security_message: + 'Check privileges failed to execute ResponseError: index_not_found_exception: [index_not_found_exception] Reason: no such index [yup] name: "Click here for hot fresh alerts!" id: "a6e61cf0-c737-11ec-9e32-e14913ffdd2d" rule id: "34946b12-88d1-49ef-82b7-9cad45972030" execution id: "1f6ba0c1-cc36-4f45-b919-7790b8a8d670" space ID: "default"', + }, + { + execution_uuid: 'b0f65d64-b229-432b-9d39-f4385a7f9368', + timestamp: '2022-04-28T21:15:43.086Z', + duration_ms: 1205, + status: 'success', + message: + "rule executed: siem.queryRule:a6e61cf0-c737-11ec-9e32-e14913ffdd2d: 'Click here for hot fresh alerts!'", + num_active_alerts: 0, + num_new_alerts: 0, + num_recovered_alerts: 0, + num_triggered_actions: 0, + num_succeeded_actions: 0, + num_errored_actions: 0, + total_search_duration_ms: 0, + es_search_duration_ms: 672, + schedule_delay_ms: 3086, + timed_out: false, + indexing_duration_ms: 140, + search_duration_ms: 684, + gap_duration_s: 0, + security_status: 'succeeded', + security_message: 'succeeded', + }, + { + execution_uuid: '7bfd25b9-c0d8-44b1-982c-485169466a8e', + timestamp: '2022-04-28T21:10:40.135Z', + duration_ms: 6321, + status: 'success', + message: + "rule executed: siem.queryRule:a6e61cf0-c737-11ec-9e32-e14913ffdd2d: 'Click here for hot fresh alerts!'", + num_active_alerts: 0, + num_new_alerts: 0, + num_recovered_alerts: 0, + num_triggered_actions: 0, + num_succeeded_actions: 0, + num_errored_actions: 0, + total_search_duration_ms: 0, + es_search_duration_ms: 930, + schedule_delay_ms: 1222, + timed_out: false, + indexing_duration_ms: 2103, + search_duration_ms: 946, + gap_duration_s: 0, + security_status: 'succeeded', + security_message: 'succeeded', + }, + ], + total: 7, +}; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.test.tsx index 6596cefef4f08..dfeaca617ed24 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.test.tsx @@ -18,6 +18,7 @@ jest.mock('../../../../common/lib/kibana', () => ({ addError: jest.fn(), addSuccess: jest.fn(), addWarning: jest.fn(), + remove: jest.fn(), }), })); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_execution_events.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_execution_events.test.tsx index 2a4efb7f69491..5fba7dd7a2ed2 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_execution_events.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_execution_events.test.tsx @@ -51,7 +51,7 @@ describe('useRuleExecutionEvents', () => { start: 'now-30', end: 'now', queryText: '', - statusFilters: '', + statusFilters: [], }), { wrapper: createReactQueryWrapper(), @@ -92,7 +92,7 @@ describe('useRuleExecutionEvents', () => { duration_ms: 3866, es_search_duration_ms: 1236, execution_uuid: '88d15095-7937-462c-8f21-9763e1387cad', - gap_duration_ms: 0, + gap_duration_s: 0, indexing_duration_ms: 95, message: "rule executed: siem.queryRule:fb1fc150-a292-11ec-a2cf-c1b28b0392b0: 'Lots of Execution Events'", diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_execution_events.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_execution_events.tsx index f2e72858cf392..e18d1f6c2ce5c 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_execution_events.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_execution_events.tsx @@ -5,22 +5,27 @@ * 2.0. */ +import { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { useQuery } from 'react-query'; +import { + AggregateRuleExecutionEvent, + RuleExecutionStatus, +} from '../../../../../common/detection_engine/schemas/common'; import { GetAggregateRuleExecutionEventsResponse } from '../../../../../common/detection_engine/schemas/response'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import { fetchRuleExecutionEvents } from './api'; import * as i18n from './translations'; -interface UseRuleExecutionEventsArgs { +export interface UseRuleExecutionEventsArgs { ruleId: string; start: string; end: string; queryText?: string; - statusFilters?: string; + statusFilters?: RuleExecutionStatus[]; page?: number; perPage?: number; - sortField?: string; - sortOrder?: string; + sortField?: keyof AggregateRuleExecutionEvent; + sortOrder?: SortOrder; } export const useRuleExecutionEvents = ({ @@ -66,6 +71,7 @@ export const useRuleExecutionEvents = ({ }); }, { + keepPreviousData: true, onError: (e) => { addError(e, { title: i18n.RULE_EXECUTION_EVENTS_FETCH_FAILURE }); }, diff --git a/x-pack/plugins/security_solution/public/detections/links.ts b/x-pack/plugins/security_solution/public/detections/links.ts new file mode 100644 index 0000000000000..1cfac62d80e6e --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/links.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 { i18n } from '@kbn/i18n'; +import { ALERTS_PATH, SecurityPageName } from '../../common/constants'; +import { ALERTS } from '../app/translations'; +import { LinkItem, FEATURE } from '../common/links/types'; + +export const links: LinkItem = { + id: SecurityPageName.alerts, + title: ALERTS, + path: ALERTS_PATH, + features: [FEATURE.general], + globalNavEnabled: true, + globalSearchKeywords: [ + i18n.translate('xpack.securitySolution.appLinks.alerts', { + defaultMessage: 'Alerts', + }), + ], + globalSearchEnabled: true, + globalNavOrder: 9001, +}; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx index a783ac7e1e260..1f066751c2b92 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx @@ -98,6 +98,7 @@ jest.mock('../../../common/lib/kibana', () => { addError: jest.fn(), addSuccess: jest.fn(), addWarning: jest.fn(), + remove: jest.fn(), }), }; }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/__mocks__/rule_details_context.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/__mocks__/rule_details_context.tsx new file mode 100644 index 0000000000000..257dc8ec512a8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/__mocks__/rule_details_context.tsx @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RuleDetailsContextType } from '../rule_details_context'; +import React from 'react'; + +export const useRuleDetailsContextMock = { + create: (): jest.Mocked => ({ + executionLogs: { + state: { + superDatePicker: { + recentlyUsedRanges: [], + refreshInterval: 1000, + isPaused: true, + start: 'now-24h', + end: 'now', + }, + queryText: '', + statusFilters: [], + showMetricColumns: true, + pagination: { + pageIndex: 1, + pageSize: 5, + }, + sort: { + sortField: 'timestamp', + sortDirection: 'desc', + }, + }, + actions: { + setEnd: jest.fn(), + setIsPaused: jest.fn(), + setPageIndex: jest.fn(), + setPageSize: jest.fn(), + setQueryText: jest.fn(), + setRecentlyUsedRanges: jest.fn(), + setRefreshInterval: jest.fn(), + setShowMetricColumns: jest.fn(), + setSortDirection: jest.fn(), + setSortField: jest.fn(), + setStart: jest.fn(), + setStatusFilters: jest.fn(), + }, + }, + }), +}; + +export const useRuleDetailsContext = jest + .fn, []>() + .mockImplementation(useRuleDetailsContextMock.create); + +export const useRuleDetailsContextOptional = jest + .fn, []>() + .mockImplementation(useRuleDetailsContextMock.create); + +export const RulesTableContextProvider = jest + .fn() + .mockImplementation(({ children }: { children: React.ReactNode }) => <>{children}); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/__snapshots__/execution_log_table.test.tsx.snap b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/__snapshots__/execution_log_table.test.tsx.snap deleted file mode 100644 index f93cce70172dc..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/__snapshots__/execution_log_table.test.tsx.snap +++ /dev/null @@ -1,140 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ExecutionLogTable snapshots renders correctly against snapshot 1`] = ` - - - - - - - - - - - - - - - Showing 0 rule executions - - - - - - - - - - - , - "render": [Function], - "sortable": false, - "truncateText": false, - "width": "10%", - }, - Object { - "field": "timestamp", - "name": , - "render": [Function], - "sortable": true, - "truncateText": false, - "width": "15%", - }, - Object { - "field": "duration_ms", - "name": , - "render": [Function], - "sortable": true, - "truncateText": false, - "width": "10%", - }, - Object { - "field": "security_message", - "name": , - "render": [Function], - "sortable": false, - "truncateText": false, - "width": "35%", - }, - ] - } - items={Array []} - noItemsMessage={ - - } - onChange={[Function]} - pagination={ - Object { - "pageIndex": 0, - "pageSize": 5, - "pageSizeOptions": Array [ - 5, - 10, - 25, - 50, - ], - "totalItemCount": 0, - } - } - responsive={true} - sorting={ - Object { - "sort": Object { - "direction": "desc", - "field": "timestamp", - }, - } - } - tableLayout="fixed" - /> - -`; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/__snapshots__/rule_duration_format.test.tsx.snap b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/__snapshots__/rule_duration_format.test.tsx.snap deleted file mode 100644 index ddb59cf52a890..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/__snapshots__/rule_duration_format.test.tsx.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`RuleDurationFormat snapshots renders correctly against snapshot 1`] = ` - - 00:00:00:000 - -`; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/execution_log_columns.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/execution_log_columns.tsx index c48f5d6971d23..98569b701d93c 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/execution_log_columns.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/execution_log_columns.tsx @@ -6,9 +6,9 @@ */ import { EuiBasicTableColumn, EuiHealth, EuiLink, EuiText } from '@elastic/eui'; -import { capitalize } from 'lodash'; -import { FormattedMessage } from '@kbn/i18n-react'; import { DocLinksStart } from '@kbn/core/public'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { capitalize } from 'lodash'; import React from 'react'; import { AggregateRuleExecutionEvent, @@ -19,9 +19,9 @@ import { FormattedDate } from '../../../../../../common/components/formatted_dat import { getStatusColor } from '../../../../../components/rules/rule_execution_status/utils'; import { PopoverTooltip } from '../../all/popover_tooltip'; import { TableHeaderTooltipCell } from '../../all/table_header_tooltip_cell'; +import { RuleDurationFormat } from './rule_duration_format'; import * as i18n from './translations'; -import { RuleDurationFormat } from './rule_duration_format'; export const EXECUTION_LOG_COLUMNS: Array> = [ { @@ -32,7 +32,7 @@ export const EXECUTION_LOG_COLUMNS: Array ), field: 'security_status', - render: (value: RuleExecutionStatus, data) => + render: (value: RuleExecutionStatus) => value ? ( {capitalize(value)} ) : ( @@ -89,7 +89,7 @@ export const GET_EXECUTION_LOG_METRICS_COLUMNS = ( docLinks: DocLinksStart ): Array> => [ { - field: 'gap_duration_ms', + field: 'gap_duration_s', name: ( ), render: (value: number) => ( - <>{value ? : getEmptyValue()} + <>{value ? : getEmptyValue()} ), sortable: true, truncateText: false, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/execution_log_search_bar.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/execution_log_search_bar.tsx index b8c2d82ab324e..74804b7c6b557 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/execution_log_search_bar.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/execution_log_search_bar.tsx @@ -56,20 +56,23 @@ const statusFilters = statuses.map((status) => ({ interface ExecutionLogTableSearchProps { onlyShowFilters: true; onSearch: (queryText: string) => void; - onStatusFilterChange: (statusFilters: string[]) => void; + onStatusFilterChange: (statusFilters: RuleExecutionStatus[]) => void; + defaultSelectedStatusFilters?: RuleExecutionStatus[]; } /** * SearchBar + StatusFilters component to be used with the Rule Execution Log table - * NOTE: This component is currently not shown in the UI as custom search queries + * NOTE: The SearchBar component is currently not shown in the UI as custom search queries * are not yet fully supported by the Rule Execution Log aggregation API since * certain queries could result in missing data or inclusion of wrong events. * Please see this comment for history/details: https://github.com/elastic/kibana/pull/127339/files#r825240516 */ export const ExecutionLogSearchBar = React.memo( - ({ onlyShowFilters, onSearch, onStatusFilterChange }) => { + ({ onlyShowFilters, onSearch, onStatusFilterChange, defaultSelectedStatusFilters = [] }) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const [selectedFilters, setSelectedFilters] = useState([]); + const [selectedFilters, setSelectedFilters] = useState( + defaultSelectedStatusFilters + ); const onSearchCallback = useCallback( (queryText: string) => { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/execution_log_table.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/execution_log_table.test.tsx index 81619d01934ba..608853f004eb9 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/execution_log_table.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/execution_log_table.test.tsx @@ -5,83 +5,21 @@ * 2.0. */ +import { ruleExecutionEventsMock } from '../../../../../containers/detection_engine/rules/mock'; +import { render, screen } from '@testing-library/react'; +import { TestProviders } from '../../../../../../common/mock'; +import { useRuleDetailsContextMock } from '../__mocks__/rule_details_context'; import React from 'react'; -import { shallow } from 'enzyme'; import { noop } from 'lodash/fp'; +import { useRuleExecutionEvents } from '../../../../../containers/detection_engine/rules'; import { useSourcererDataView } from '../../../../../../common/containers/sourcerer'; +import { useRuleDetailsContext } from '../rule_details_context'; import { ExecutionLogTable } from './execution_log_table'; -jest.mock('../../../../../containers/detection_engine/rules', () => { - const original = jest.requireActual('../../../../../containers/detection_engine/rules'); - return { - ...original, - useRuleExecutionEvents: jest.fn().mockReturnValue({ - loading: true, - setQuery: () => undefined, - data: null, - response: '', - request: '', - refetch: null, - }), - }; -}); - jest.mock('../../../../../../common/containers/sourcerer'); - -jest.mock('../../../../../../common/hooks/use_app_toasts', () => { - const original = jest.requireActual('../../../../../../common/hooks/use_app_toasts'); - - return { - ...original, - useAppToasts: () => ({ - addSuccess: jest.fn(), - addError: jest.fn(), - }), - }; -}); - -jest.mock('react-redux', () => { - const original = jest.requireActual('react-redux'); - return { - ...original, - useDispatch: () => jest.fn(), - useSelector: () => jest.fn(), - }; -}); - -jest.mock('../../../../../../common/lib/kibana', () => { - const original = jest.requireActual('../../../../../../common/lib/kibana'); - - return { - ...original, - useUiSetting$: jest.fn().mockReturnValue([]), - useKibana: () => ({ - services: { - data: { - query: { - filterManager: jest.fn().mockReturnValue({}), - }, - }, - docLinks: { - links: { - siem: { - troubleshootGaps: 'link', - }, - }, - }, - storage: { - get: jest.fn(), - set: jest.fn(), - }, - timelines: { - getLastUpdated: jest.fn(), - getFieldBrowser: jest.fn(), - }, - }, - }), - }; -}); +jest.mock('../../../../../containers/detection_engine/rules'); +jest.mock('../rule_details_context'); const mockUseSourcererDataView = useSourcererDataView as jest.Mock; mockUseSourcererDataView.mockReturnValue({ @@ -92,13 +30,20 @@ mockUseSourcererDataView.mockReturnValue({ loading: false, }); -// TODO: Replace snapshot test with base test cases +const mockUseRuleExecutionEvents = useRuleExecutionEvents as jest.Mock; +mockUseRuleExecutionEvents.mockReturnValue({ + data: ruleExecutionEventsMock, + isLoading: false, + isFetching: false, +}); describe('ExecutionLogTable', () => { - describe('snapshots', () => { - test('renders correctly against snapshot', () => { - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); + test('Shows total events returned', () => { + const ruleDetailsContext = useRuleDetailsContextMock.create(); + (useRuleDetailsContext as jest.Mock).mockReturnValue(ruleDetailsContext); + render(, { + wrapper: TestProviders, }); + expect(screen.getByTestId('executionsShowing')).toHaveTextContent('Showing 7 rule executions'); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/execution_log_table.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/execution_log_table.tsx index 31fc446fa2bb7..b471493a9ecd2 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/execution_log_table.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/execution_log_table.tsx @@ -5,10 +5,10 @@ * 2.0. */ -import { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { DurationRange } from '@elastic/eui/src/components/date_picker/types'; +import { useDispatch } from 'react-redux'; import styled from 'styled-components'; -import React, { useCallback, useMemo, useState } from 'react'; +import moment from 'moment'; +import React, { useCallback, useMemo, useRef } from 'react'; import { EuiTextColor, EuiFlexGroup, @@ -20,11 +20,17 @@ import { EuiSpacer, EuiSwitch, EuiBasicTable, + EuiButton, } from '@elastic/eui'; -import { buildFilter, FILTERS } from '@kbn/es-query'; +import { buildFilter, Filter, FILTERS, Query } from '@kbn/es-query'; import { MAX_EXECUTION_EVENTS_DISPLAYED } from '@kbn/securitysolution-rules'; +import { mountReactNode } from '@kbn/core/public/utils'; +import { RuleDetailTabs } from '..'; import { RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY } from '../../../../../../../common/constants'; -import { AggregateRuleExecutionEvent } from '../../../../../../../common/detection_engine/schemas/common'; +import { + AggregateRuleExecutionEvent, + RuleExecutionStatus, +} from '../../../../../../../common/detection_engine/schemas/common'; import { UtilityBar, @@ -34,9 +40,23 @@ import { } from '../../../../../../common/components/utility_bar'; import { useSourcererDataView } from '../../../../../../common/containers/sourcerer'; import { useAppToasts } from '../../../../../../common/hooks/use_app_toasts'; +import { useDeepEqualSelector } from '../../../../../../common/hooks/use_selector'; import { useKibana } from '../../../../../../common/lib/kibana'; +import { inputsSelectors } from '../../../../../../common/store'; +import { + setAbsoluteRangeDatePicker, + setFilterQuery, + setRelativeRangeDatePicker, +} from '../../../../../../common/store/inputs/actions'; +import { + AbsoluteTimeRange, + isAbsoluteTimeRange, + isRelativeTimeRange, + RelativeTimeRange, +} from '../../../../../../common/store/inputs/model'; import { SourcererScopeName } from '../../../../../../common/store/sourcerer/model'; import { useRuleExecutionEvents } from '../../../../../containers/detection_engine/rules'; +import { useRuleDetailsContext } from '../rule_details_context'; import * as i18n from './translations'; import { EXECUTION_LOG_COLUMNS, GET_EXECUTION_LOG_METRICS_COLUMNS } from './execution_log_columns'; import { ExecutionLogSearchBar } from './execution_log_search_bar'; @@ -56,6 +76,12 @@ interface ExecutionLogTableProps { selectAlertsTab: () => void; } +interface CachedGlobalQueryState { + filters: Filter[]; + query: Query; + timerange: AbsoluteTimeRange | RelativeTimeRange; +} + const ExecutionLogTableComponent: React.FC = ({ ruleId, selectAlertsTab, @@ -68,28 +94,84 @@ const ExecutionLogTableComponent: React.FC = ({ storage, timelines, } = useKibana().services; - // Datepicker state - const [recentlyUsedRanges, setRecentlyUsedRanges] = useState([]); - const [refreshInterval, setRefreshInterval] = useState(1000); - const [isPaused, setIsPaused] = useState(true); - const [start, setStart] = useState('now-24h'); - const [end, setEnd] = useState('now'); - // Searchbar/Filter/Settings state - const [queryText, setQueryText] = useState(''); - const [statusFilters, setStatusFilters] = useState(undefined); - const [showMetricColumns, setShowMetricColumns] = useState( - storage.get(RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY) ?? false - ); + const { + [RuleDetailTabs.executionLogs]: { + state: { + superDatePicker: { recentlyUsedRanges, refreshInterval, isPaused, start, end }, + queryText, + statusFilters, + showMetricColumns, + pagination: { pageIndex, pageSize }, + sort: { sortField, sortDirection }, + }, + actions: { + setEnd, + setIsPaused, + setPageIndex, + setPageSize, + setQueryText, + setRecentlyUsedRanges, + setRefreshInterval, + setShowMetricColumns, + setSortDirection, + setSortField, + setStart, + setStatusFilters, + }, + }, + } = useRuleDetailsContext(); - // Pagination state - const [pageIndex, setPageIndex] = useState(1); - const [pageSize, setPageSize] = useState(5); - const [sortField, setSortField] = useState('timestamp'); - const [sortDirection, setSortDirection] = useState('desc'); // Index for `add filter` action and toasts for errors const { indexPattern } = useSourcererDataView(SourcererScopeName.detections); - const { addError, addSuccess } = useAppToasts(); + const { addError, addSuccess, remove } = useAppToasts(); + + // QueryString, Filters, and TimeRange state + const dispatch = useDispatch(); + const getGlobalFiltersQuerySelector = useMemo( + () => inputsSelectors.globalFiltersQuerySelector(), + [] + ); + const getGlobalQuerySelector = useMemo(() => inputsSelectors.globalQuerySelector(), []); + const timerange = useDeepEqualSelector(inputsSelectors.globalTimeRangeSelector); + const query = useDeepEqualSelector(getGlobalQuerySelector); + const filters = useDeepEqualSelector(getGlobalFiltersQuerySelector); + const cachedGlobalQueryState = useRef({ filters, query, timerange }); + const successToastId = useRef(''); + + const resetGlobalQueryState = useCallback(() => { + if (isAbsoluteTimeRange(cachedGlobalQueryState.current.timerange)) { + dispatch( + setAbsoluteRangeDatePicker({ + id: 'global', + from: cachedGlobalQueryState.current.timerange.from, + to: cachedGlobalQueryState.current.timerange.to, + }) + ); + } else if (isRelativeTimeRange(cachedGlobalQueryState.current.timerange)) { + dispatch( + setRelativeRangeDatePicker({ + id: 'global', + from: cachedGlobalQueryState.current.timerange.from, + fromStr: cachedGlobalQueryState.current.timerange.fromStr, + to: cachedGlobalQueryState.current.timerange.to, + toStr: cachedGlobalQueryState.current.timerange.toStr, + }) + ); + } + + dispatch( + setFilterQuery({ + id: 'global', + query: cachedGlobalQueryState.current.query.query, + language: cachedGlobalQueryState.current.query.language, + }) + ); + // Using filterManager directly as dispatch(setSearchBarFilter()) was not replacing filters + filterManager.removeAll(); + filterManager.addFilters(cachedGlobalQueryState.current.filters); + remove(successToastId.current); + }, [dispatch, filterManager, remove]); // Table data state const { @@ -118,15 +200,18 @@ const ExecutionLogTableComponent: React.FC = ({ }, [indexPattern]); // Callbacks - const onTableChangeCallback = useCallback(({ page = {}, sort = {} }) => { - const { index, size } = page; - const { field, direction } = sort; + const onTableChangeCallback = useCallback( + ({ page = {}, sort = {} }) => { + const { index, size } = page; + const { field, direction } = sort; - setPageIndex(index + 1); - setPageSize(size); - setSortField(field); - setSortDirection(direction); - }, []); + setPageIndex(index + 1); + setPageSize(size); + setSortField(field); + setSortDirection(direction); + }, + [setPageIndex, setPageSize, setSortDirection, setSortField] + ); const onTimeChangeCallback = useCallback( (props: OnTimeChangeProps) => { @@ -141,14 +226,17 @@ const ExecutionLogTableComponent: React.FC = ({ recentlyUsedRange.length > 10 ? recentlyUsedRange.slice(0, 9) : recentlyUsedRange ); }, - [recentlyUsedRanges] + [recentlyUsedRanges, setEnd, setRecentlyUsedRanges, setStart] ); - const onRefreshChangeCallback = useCallback((props: OnRefreshChangeProps) => { - setIsPaused(props.isPaused); - // Only support auto-refresh >= 1minute -- no current ability to limit within component - setRefreshInterval(props.refreshInterval > 60000 ? props.refreshInterval : 60000); - }, []); + const onRefreshChangeCallback = useCallback( + (props: OnRefreshChangeProps) => { + setIsPaused(props.isPaused); + // Only support auto-refresh >= 1minute -- no current ability to limit within component + setRefreshInterval(props.refreshInterval > 60000 ? props.refreshInterval : 60000); + }, + [setIsPaused, setRefreshInterval] + ); const onRefreshCallback = useCallback( (props: OnRefreshProps) => { @@ -157,19 +245,26 @@ const ExecutionLogTableComponent: React.FC = ({ [refetch] ); - const onSearchCallback = useCallback((updatedQueryText: string) => { - setQueryText(updatedQueryText); - }, []); + const onSearchCallback = useCallback( + (updatedQueryText: string) => { + setQueryText(updatedQueryText); + }, + [setQueryText] + ); - const onStatusFilterChangeCallback = useCallback((updatedStatusFilters: string[]) => { - setStatusFilters( - updatedStatusFilters.length ? updatedStatusFilters.sort().join(',') : undefined - ); - }, []); + const onStatusFilterChangeCallback = useCallback( + (updatedStatusFilters: RuleExecutionStatus[]) => { + setStatusFilters(updatedStatusFilters); + }, + [setStatusFilters] + ); const onFilterByExecutionIdCallback = useCallback( (executionId: string, executionStart: string) => { if (uuidDataViewField != null) { + // Update cached global query state with current state as a rollback point + cachedGlobalQueryState.current = { filters, query, timerange }; + // Create filter & daterange constraints const filter = buildFilter( indexPattern, uuidDataViewField, @@ -179,20 +274,55 @@ const ExecutionLogTableComponent: React.FC = ({ executionId, null ); + dispatch( + setAbsoluteRangeDatePicker({ + id: 'global', + from: moment(executionStart).subtract(1, 'days').toISOString(), + to: moment(executionStart).add(1, 'days').toISOString(), + }) + ); filterManager.removeAll(); filterManager.addFilters(filter); + dispatch(setFilterQuery({ id: 'global', query: '', language: 'kuery' })); selectAlertsTab(); - addSuccess({ - title: i18n.ACTIONS_SEARCH_FILTERS_HAVE_BEEN_UPDATED_TITLE, - text: i18n.ACTIONS_SEARCH_FILTERS_HAVE_BEEN_UPDATED_DESCRIPTION, - }); + successToastId.current = addSuccess( + { + title: i18n.ACTIONS_SEARCH_FILTERS_HAVE_BEEN_UPDATED_TITLE, + text: mountReactNode( + <> +

{i18n.ACTIONS_SEARCH_FILTERS_HAVE_BEEN_UPDATED_DESCRIPTION}

+ + + + {i18n.ACTIONS_SEARCH_FILTERS_HAVE_BEEN_UPDATED_RESTORE_BUTTON} + + + + + ), + }, + // Essentially keep toast around till user dismisses via 'x' + { toastLifeTimeMs: 10 * 60 * 1000 } + ).id; } else { addError(i18n.ACTIONS_FIELD_NOT_FOUND_ERROR, { title: i18n.ACTIONS_FIELD_NOT_FOUND_ERROR_TITLE, }); } }, - [addError, addSuccess, filterManager, indexPattern, selectAlertsTab, uuidDataViewField] + [ + addError, + addSuccess, + dispatch, + filterManager, + filters, + indexPattern, + query, + resetGlobalQueryState, + selectAlertsTab, + timerange, + uuidDataViewField, + ] ); const onShowMetricColumnsCallback = useCallback( @@ -200,7 +330,7 @@ const ExecutionLogTableComponent: React.FC = ({ storage.set(RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY, showMetrics); setShowMetricColumns(showMetrics); }, - [storage] + [setShowMetricColumns, storage] ); // Memoized state @@ -223,8 +353,6 @@ const ExecutionLogTableComponent: React.FC = ({ }; }, [sortDirection, sortField]); - // TODO: Re-add actions once alert count is displayed in table and UX is finalized - // @ts-expect-error unused constant const actions = useMemo( () => [ { @@ -258,9 +386,9 @@ const ExecutionLogTableComponent: React.FC = ({ const executionLogColumns = useMemo( () => showMetricColumns - ? [...EXECUTION_LOG_COLUMNS, ...GET_EXECUTION_LOG_METRICS_COLUMNS(docLinks)] - : [...EXECUTION_LOG_COLUMNS], - [docLinks, showMetricColumns] + ? [...EXECUTION_LOG_COLUMNS, ...GET_EXECUTION_LOG_METRICS_COLUMNS(docLinks), ...actions] + : [...EXECUTION_LOG_COLUMNS, ...actions], + [actions, docLinks, showMetricColumns] ); return ( @@ -271,6 +399,7 @@ const ExecutionLogTableComponent: React.FC = ({ onSearch={onSearchCallback} onStatusFilterChange={onStatusFilterChangeCallback} onlyShowFilters={true} + defaultSelectedStatusFilters={statusFilters} />
@@ -315,7 +444,7 @@ const ExecutionLogTableComponent: React.FC = ({ - + {timelines.getLastUpdated({ showUpdating: isLoading || isFetching, updatedAt: dataUpdatedAt, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/rule_duration_format.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/rule_duration_format.test.tsx index 44f765f934ae8..30e7d6e8f0a2e 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/rule_duration_format.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/rule_duration_format.test.tsx @@ -5,17 +5,82 @@ * 2.0. */ -import { shallow } from 'enzyme'; +import { render } from '@testing-library/react'; import React from 'react'; -import { RuleDurationFormat } from './rule_duration_format'; - -// TODO: Replace snapshot test with base test cases +import { getFormattedDuration, RuleDurationFormat } from './rule_duration_format'; describe('RuleDurationFormat', () => { - describe('snapshots', () => { - test('renders correctly against snapshot', () => { - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); + describe('getFormattedDuration', () => { + test('if input value is 0, formatted response is also 0', () => { + const formattedDuration = getFormattedDuration(0); + expect(formattedDuration).toEqual('00:00:00:000'); + }); + + test('if input value only contains ms, formatted response also only contains ms (SSS)', () => { + const formattedDuration = getFormattedDuration(999); + expect(formattedDuration).toEqual('00:00:00:999'); + }); + + test('for milliseconds (SSS) to seconds (ss) overflow', () => { + const formattedDuration = getFormattedDuration(1000); + expect(formattedDuration).toEqual('00:00:01:000'); + }); + + test('for seconds (ss) to minutes (mm) overflow', () => { + const formattedDuration = getFormattedDuration(60000 + 1); + expect(formattedDuration).toEqual('00:01:00:001'); + }); + + test('for minutes (mm) to hours (hh) overflow', () => { + const formattedDuration = getFormattedDuration(60000 * 60 + 1); + expect(formattedDuration).toEqual('01:00:00:001'); + }); + + test('for hours (hh) to days (ddd) overflow', () => { + const formattedDuration = getFormattedDuration(60000 * 60 * 24 + 1); + expect(formattedDuration).toEqual('001:00:00:00:001'); + }); + + test('for overflow with all units up to hours (hh)', () => { + const formattedDuration = getFormattedDuration(25033167); + expect(formattedDuration).toEqual('06:57:13:167'); + }); + + test('for overflow with all units up to hours (ddd)', () => { + const formattedDuration = getFormattedDuration(2503316723); + expect(formattedDuration).toEqual('028:23:21:56:723'); + }); + + test('for overflow greater than a year', () => { + const formattedDuration = getFormattedDuration((60000 * 60 * 24 + 1) * 365); + expect(formattedDuration).toEqual('> 1 Year'); + }); + + test('for max overflow', () => { + const formattedDuration = getFormattedDuration(Number.MAX_SAFE_INTEGER); + expect(formattedDuration).toEqual('> 1 Year'); + }); + }); + + describe('RuleDurationFormatComponent', () => { + test('renders correctly with duration and no additional props', () => { + const { getByTestId } = render(); + expect(getByTestId('rule-duration-format-value')).toHaveTextContent('00:00:01:000'); + }); + + test('renders correctly with duration and isSeconds=true', () => { + const { getByTestId } = render(); + expect(getByTestId('rule-duration-format-value')).toHaveTextContent('00:00:01:000'); + }); + + test('renders correctly with allowZero=true', () => { + const { getByTestId } = render(); + expect(getByTestId('rule-duration-format-value')).toHaveTextContent('N/A'); + }); + + test('renders correctly with max overflow', () => { + const { getByTestId } = render(); + expect(getByTestId('rule-duration-format-value')).toHaveTextContent('> 1 Year'); }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/rule_duration_format.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/rule_duration_format.tsx index cdbc19ce16c3b..c9f8e1d2734d8 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/rule_duration_format.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/rule_duration_format.tsx @@ -5,48 +5,59 @@ * 2.0. */ -import numeral from '@elastic/numeral'; import moment from 'moment'; import React, { useMemo } from 'react'; +import * as i18n from './translations'; + interface Props { duration: number; - isMillis?: boolean; + isSeconds?: boolean; allowZero?: boolean; } -export function getFormattedDuration(value: number) { +export const getFormattedDuration = (value: number) => { if (!value) { return '00:00:00:000'; } const duration = moment.duration(value); - const hours = Math.floor(duration.asHours()).toString().padStart(2, '0'); - const minutes = Math.floor(duration.asMinutes()).toString().padStart(2, '0'); + const days = Math.floor(duration.asDays()).toString().padStart(3, '0'); + const hours = Math.floor(duration.asHours() % 24) + .toString() + .padStart(2, '0'); + const minutes = Math.floor(duration.asMinutes() % 60) + .toString() + .padStart(2, '0'); const seconds = duration.seconds().toString().padStart(2, '0'); const ms = duration.milliseconds().toString().padStart(3, '0'); - return `${hours}:${minutes}:${seconds}:${ms}`; -} -export function getFormattedMilliseconds(value: number) { - const formatted = numeral(value).format('0,0'); - return `${formatted} ms`; -} + if (Math.floor(duration.asDays()) > 0) { + if (Math.floor(duration.asDays()) >= 365) { + return i18n.GREATER_THAN_YEAR; + } else { + return `${days}:${hours}:${minutes}:${seconds}:${ms}`; + } + } else { + return `${hours}:${minutes}:${seconds}:${ms}`; + } +}; /** - * Formats duration as (hh:mm:ss:SSS) - * @param props duration default as nanos, set isMillis:true to pass in ms + * Formats duration as (hh:mm:ss:SSS) by default, overflowing to include days + * as (ddd:hh:mm:ss:SSS) if necessary, and then finally to `> 1 Year` + * @param props duration as millis, set isSeconds:true to pass in seconds * @constructor */ const RuleDurationFormatComponent = (props: Props) => { - const { duration, isMillis = false, allowZero = true } = props; + const { duration, isSeconds = false, allowZero = true } = props; const formattedDuration = useMemo(() => { // Durations can be buggy and return negative if (allowZero && duration >= 0) { - return getFormattedDuration(isMillis ? duration * 1000 : duration); + return getFormattedDuration(isSeconds ? duration * 1000 : duration); } - return 'N/A'; - }, [allowZero, duration, isMillis]); + return i18n.DURATION_NOT_AVAILABLE; + }, [allowZero, duration, isSeconds]); return {formattedDuration}; }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/translations.ts index d5d8d56664907..b161ae3662e0e 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/execution_log_table/translations.ts @@ -181,6 +181,13 @@ export const ACTIONS_SEARCH_FILTERS_HAVE_BEEN_UPDATED_DESCRIPTION = i18n.transla } ); +export const ACTIONS_SEARCH_FILTERS_HAVE_BEEN_UPDATED_RESTORE_BUTTON = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.actionSearchFiltersUpdatedRestoreButtonTitle', + { + defaultMessage: 'Restore previous filters', + } +); + export const ACTIONS_FIELD_NOT_FOUND_ERROR_TITLE = i18n.translate( 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.actionFieldNotFoundErrorTitle', { @@ -194,3 +201,17 @@ export const ACTIONS_FIELD_NOT_FOUND_ERROR = i18n.translate( defaultMessage: "Cannot find field 'kibana.alert.rule.execution.uuid' in alerts index.", } ); + +export const DURATION_NOT_AVAILABLE = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.durationNotAvailableDescription', + { + defaultMessage: 'N/A', + } +); + +export const GREATER_THAN_YEAR = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.durationGreaterThanYearDescription', + { + defaultMessage: '> 1 Year', + } +); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx index 8550d03eca2aa..cc8872b901f44 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx @@ -108,6 +108,7 @@ import { import * as detectionI18n from '../../translations'; import * as ruleI18n from '../translations'; import { ExecutionLogTable } from './execution_log_table/execution_log_table'; +import { RuleDetailsContextProvider } from './rule_details_context'; import * as i18n from './translations'; import { NeedAdminForUpdateRulesCallOut } from '../../../../components/callouts/need_admin_for_update_callout'; import { MissingPrivilegesCallOut } from '../../../../components/callouts/missing_privileges_callout'; @@ -131,7 +132,14 @@ const StyledFullHeightContainer = styled.div` flex: 1 1 auto; `; -enum RuleDetailTabs { +/** + * Sets min-height on tab container to minimize page hop when switching to tabs with less content + */ +const StyledMinHeightTabContainer = styled.div` + min-height: 800px; +`; + +export enum RuleDetailTabs { alerts = 'alerts', executionLogs = 'executionLogs', exceptions = 'exceptions', @@ -639,181 +647,186 @@ const RuleDetailsPageComponent: React.FC = ({ indexPattern={indexPattern} /> - - - - - - - {ruleStatusI18n.STATUS} - {':'} - - {ruleStatusInfo} - - - } - title={title} - badgeOptions={badgeOptions} - > - - - - - - {i18n.ENABLE_RULE} + + + + + + + {ruleStatusI18n.STATUS} + {':'} + + {ruleStatusInfo} - - - - - - {editRule} - - - - - - - - {ruleError} - {getLegacyUrlConflictCallout} - - - - - - - - - - - {defineRuleData != null && ( - + } + title={title} + badgeOptions={badgeOptions} + > + + + + + - )} - + {i18n.ENABLE_RULE} + +
- - - - {scheduleRuleData != null && ( - + + {editRule} + + - )} - + +
- - - - {tabs} - - - {ruleDetailTab === RuleDetailTabs.alerts && hasIndexRead && ( - <> - - - + {ruleError} + {getLegacyUrlConflictCallout} + + + + - - {updatedAt && - timelinesUi.getLastUpdated({ - updatedAt: updatedAt || Date.now(), - showUpdating, - })} + + + + + + {defineRuleData != null && ( + + )} + + + + + + {scheduleRuleData != null && ( + + )} + + + - - - - - - {ruleId != null && ( - + {tabs} + + + + {ruleDetailTab === RuleDetailTabs.alerts && hasIndexRead && ( + <> + + + + + + {updatedAt && + timelinesUi.getLastUpdated({ + updatedAt: updatedAt || Date.now(), + showUpdating, + })} + + + + + + + + {ruleId != null && ( + + )} + + )} + {ruleDetailTab === RuleDetailTabs.exceptions && ( + )} - - )} - {ruleDetailTab === RuleDetailTabs.exceptions && ( - - )} - {ruleDetailTab === RuleDetailTabs.executionLogs && ( - - )} - + {ruleDetailTab === RuleDetailTabs.executionLogs && ( + + )} + + + diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/rule_details_context.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/rule_details_context.tsx new file mode 100644 index 0000000000000..13b17d0493d43 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/rule_details_context.tsx @@ -0,0 +1,216 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { DurationRange } from '@elastic/eui/src/components/date_picker/types'; +import React, { createContext, useContext, useMemo, useState } from 'react'; +import { RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY } from '../../../../../../common/constants'; +import { + AggregateRuleExecutionEvent, + RuleExecutionStatus, +} from '../../../../../../common/detection_engine/schemas/common'; +import { invariant } from '../../../../../../common/utils/invariant'; +import { useKibana } from '../../../../../common/lib/kibana'; +import { RuleDetailTabs } from '.'; + +export interface ExecutionLogTableState { + /** + * State of the SuperDatePicker component + */ + superDatePicker: { + /** + * DateRanges to display as recently used + */ + recentlyUsedRanges: DurationRange[]; + /** + * Interval to auto-refresh at + */ + refreshInterval: number; + /** + * State of auto-refresh + */ + isPaused: boolean; + /** + * Start datetime + */ + start: string; + /** + * End datetime + */ + end: string; + }; + /** + * SearchBar query + */ + queryText: string; + /** + * Selected Filters by Execution Status(es) + */ + statusFilters: RuleExecutionStatus[]; + /** + * Whether or not to show additional metric columnbs + */ + showMetricColumns: boolean; + /** + * Currently selected page and number of rows per page + */ + pagination: { + pageIndex: number; + pageSize: number; + }; + sort: { + sortField: keyof AggregateRuleExecutionEvent; + sortDirection: SortOrder; + }; +} + +// @ts-expect-error unused constant +const DEFAULT_STATE: ExecutionLogTableState = { + superDatePicker: { + recentlyUsedRanges: [], + refreshInterval: 1000, + isPaused: true, + start: 'now-24hr', + end: 'now', + }, + queryText: '', + statusFilters: [], + showMetricColumns: false, + pagination: { + pageIndex: 1, + pageSize: 5, + }, + sort: { + sortField: 'timestamp', + sortDirection: 'desc', + }, +}; + +export interface ExecutionLogTableActions { + setRecentlyUsedRanges: React.Dispatch>; + setRefreshInterval: React.Dispatch>; + setIsPaused: React.Dispatch>; + setStart: React.Dispatch>; + setEnd: React.Dispatch>; + setQueryText: React.Dispatch>; + setStatusFilters: React.Dispatch>; + setShowMetricColumns: React.Dispatch>; + setPageIndex: React.Dispatch>; + setPageSize: React.Dispatch>; + setSortField: React.Dispatch>; + setSortDirection: React.Dispatch>; +} + +export interface RuleDetailsContextType { + // TODO: Add section for RuleDetailTabs.exceptions and store query/pagination/etc. + // TODO: Let's discuss how to integration with ExceptionsViewerComponent state mgmt + [RuleDetailTabs.executionLogs]: { + state: ExecutionLogTableState; + actions: ExecutionLogTableActions; + }; +} + +const RuleDetailsContext = createContext(null); + +interface RuleDetailsContextProviderProps { + children: React.ReactNode; +} + +export const RuleDetailsContextProvider = ({ children }: RuleDetailsContextProviderProps) => { + const { storage } = useKibana().services; + + // Execution Log Table tab + // // SuperDatePicker State + const [recentlyUsedRanges, setRecentlyUsedRanges] = useState([]); + const [refreshInterval, setRefreshInterval] = useState(1000); + const [isPaused, setIsPaused] = useState(true); + const [start, setStart] = useState('now-24h'); + const [end, setEnd] = useState('now'); + // Searchbar/Filter/Settings state + const [queryText, setQueryText] = useState(''); + const [statusFilters, setStatusFilters] = useState([]); + const [showMetricColumns, setShowMetricColumns] = useState( + storage.get(RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY) ?? false + ); + // Pagination state + const [pageIndex, setPageIndex] = useState(1); + const [pageSize, setPageSize] = useState(5); + const [sortField, setSortField] = useState('timestamp'); + const [sortDirection, setSortDirection] = useState('desc'); + // // End Execution Log Table tab + + const providerValue = useMemo( + () => ({ + [RuleDetailTabs.executionLogs]: { + state: { + superDatePicker: { + recentlyUsedRanges, + refreshInterval, + isPaused, + start, + end, + }, + queryText, + statusFilters, + showMetricColumns, + pagination: { + pageIndex, + pageSize, + }, + sort: { + sortField, + sortDirection, + }, + }, + actions: { + setEnd, + setIsPaused, + setPageIndex, + setPageSize, + setQueryText, + setRecentlyUsedRanges, + setRefreshInterval, + setShowMetricColumns, + setSortDirection, + setSortField, + setStart, + setStatusFilters, + }, + }, + }), + [ + end, + isPaused, + pageIndex, + pageSize, + queryText, + recentlyUsedRanges, + refreshInterval, + showMetricColumns, + sortDirection, + sortField, + start, + statusFilters, + ] + ); + + return ( + {children} + ); +}; + +export const useRuleDetailsContext = (): RuleDetailsContextType => { + const ruleDetailsContext = useContext(RuleDetailsContext); + invariant( + ruleDetailsContext, + 'useRuleDetailsContext should be used inside RuleDetailsContextProvider' + ); + return ruleDetailsContext; +}; + +export const useRuleDetailsContextOptional = (): RuleDetailsContextType | null => + useContext(RuleDetailsContext); diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/index.test.tsx deleted file mode 100644 index a96ffb577d90c..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/index.test.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { render } from '@testing-library/react'; -import React from 'react'; -import { HostRiskScoreOverTime } from '.'; -import { TestProviders } from '../../../common/mock'; -import { useHostRiskScore } from '../../../risk_score/containers'; - -jest.mock('../../../risk_score/containers'); -const useHostRiskScoreMock = useHostRiskScore as jest.Mock; - -describe('Host Risk Flyout', () => { - it('renders', () => { - useHostRiskScoreMock.mockReturnValueOnce([false, { data: [], isModuleEnabled: true }]); - - const { queryByTestId } = render( - - - - ); - - expect(queryByTestId('hostRiskScoreOverTime')).toBeInTheDocument(); - }); - - it('renders loader when HostsRiskScore is laoding', () => { - useHostRiskScoreMock.mockReturnValueOnce([true, { data: [], isModuleEnabled: true }]); - - const { queryByTestId } = render( - - - - ); - - expect(queryByTestId('HostRiskScoreOverTime-loading')).toBeInTheDocument(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/index.tsx deleted file mode 100644 index 52a840e857fff..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/index.tsx +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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, useCallback } from 'react'; -import { - Chart, - LineSeries, - ScaleType, - Settings, - Axis, - Position, - AnnotationDomainType, - LineAnnotation, - TooltipValue, -} from '@elastic/charts'; -import { euiThemeVars } from '@kbn/ui-theme'; -import { EuiFlexGroup, EuiFlexItem, EuiLoadingChart, EuiText, EuiPanel } from '@elastic/eui'; -import styled from 'styled-components'; -import { chartDefaultSettings, useTheme } from '../../../common/components/charts/common'; -import { useTimeZone } from '../../../common/lib/kibana'; -import { histogramDateTimeFormatter } from '../../../common/components/utils'; -import { HeaderSection } from '../../../common/components/header_section'; -import { InspectButton, InspectButtonContainer } from '../../../common/components/inspect'; -import * as i18n from './translations'; -import { PreferenceFormattedDate } from '../../../common/components/formatted_date'; -import { useQueryInspector } from '../../../common/components/page/manage_query'; -import { HostsComponentsQueryProps } from '../../pages/navigation/types'; -import { buildHostNamesFilter } from '../../../../common/search_strategy/security_solution/risk_score'; -import { HostRiskScoreQueryId, useHostRiskScore } from '../../../risk_score/containers'; - -export interface HostRiskScoreOverTimeProps - extends Pick { - hostName: string; - from: string; - to: string; -} - -const RISKY_THRESHOLD = 70; -const DEFAULT_CHART_HEIGHT = 250; -const QUERY_ID = HostRiskScoreQueryId.HOST_RISK_SCORE_OVER_TIME; - -const StyledEuiText = styled(EuiText)` - font-size: 9px; - font-weight: ${({ theme }) => theme.eui.euiFontWeightSemiBold}; - margin-right: ${({ theme }) => theme.eui.paddingSizes.xs}; -`; - -const LoadingChart = styled(EuiLoadingChart)` - display: block; - text-align: center; -`; - -const HostRiskScoreOverTimeComponent: React.FC = ({ - hostName, - from, - to, - setQuery, - deleteQuery, -}) => { - const timeZone = useTimeZone(); - - const dataTimeFormatter = useMemo(() => histogramDateTimeFormatter([from, to]), [from, to]); - const scoreFormatter = useCallback((d: number) => Math.round(d).toString(), []); - const headerFormatter = useCallback( - (tooltip: TooltipValue) => , - [] - ); - - const timerange = useMemo( - () => ({ - from, - to, - }), - [from, to] - ); - const theme = useTheme(); - - const [loading, { data, refetch, inspect }] = useHostRiskScore({ - filterQuery: hostName ? buildHostNamesFilter([hostName]) : undefined, - onlyLatest: false, - timerange, - }); - - const graphData = useMemo( - () => - data - ?.map((hostRisk) => ({ - x: hostRisk['@timestamp'], - y: hostRisk.risk_stats.risk_score, - })) - .reverse() ?? [], - [data] - ); - - useQueryInspector({ - queryId: QUERY_ID, - loading, - refetch, - setQuery, - deleteQuery, - inspect, - }); - - return ( - - - - - - - - - - - - - - -
- {loading ? ( - - ) : ( - - - - - - - {i18n.RISKY} - - } - /> - - )} -
-
-
-
-
- ); -}; - -HostRiskScoreOverTimeComponent.displayName = 'HostRiskScoreOverTimeComponent'; -export const HostRiskScoreOverTime = React.memo(HostRiskScoreOverTimeComponent); -HostRiskScoreOverTime.displayName = 'HostRiskScoreOverTime'; diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/translations.ts b/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/translations.ts deleted file mode 100644 index 5e1b4ca7410a8..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/translations.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 { i18n } from '@kbn/i18n'; - -export const HOST_RISK_SCORE_OVER_TIME = i18n.translate( - 'xpack.securitySolution.hosts.hostScoreOverTime.title', - { - defaultMessage: 'Host risk score over time', - } -); - -export const HOST_RISK_THRESHOLD = i18n.translate( - 'xpack.securitySolution.hosts.hostScoreOverTime.riskyThresholdHeader', - { - defaultMessage: 'Risky threshold', - } -); - -export const RISKY = i18n.translate('xpack.securitySolution.hosts.hostScoreOverTime.riskyLabel', { - defaultMessage: 'Risky', -}); - -export const RISK_SCORE = i18n.translate( - 'xpack.securitySolution.hosts.hostScoreOverTime.riskScore', - { - defaultMessage: 'Risk score', - } -); diff --git a/x-pack/plugins/security_solution/public/hosts/components/top_host_score_contributors/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/top_host_score_contributors/index.test.tsx deleted file mode 100644 index 5ff8696ae5be3..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/components/top_host_score_contributors/index.test.tsx +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { render, fireEvent } from '@testing-library/react'; -import React from 'react'; -import { TopHostScoreContributors } from '.'; -import { TestProviders } from '../../../common/mock'; -import { useHostRiskScore } from '../../../risk_score/containers'; -import { useQueryToggle } from '../../../common/containers/query_toggle'; - -jest.mock('../../../common/containers/query_toggle'); -jest.mock('../../../risk_score/containers'); -const useHostRiskScoreMock = useHostRiskScore as jest.Mock; -const testProps = { - setQuery: jest.fn(), - deleteQuery: jest.fn(), - hostName: 'test-host-name', - from: '2020-07-07T08:20:18.966Z', - to: '2020-07-08T08:20:18.966Z', -}; -describe('Host Risk Flyout', () => { - const mockUseQueryToggle = useQueryToggle as jest.Mock; - const mockSetToggle = jest.fn(); - beforeEach(() => { - jest.clearAllMocks(); - mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: mockSetToggle }); - }); - it('renders', () => { - useHostRiskScoreMock.mockReturnValueOnce([ - true, - { - data: [], - isModuleEnabled: true, - }, - ]); - - const { queryByTestId } = render( - - - - ); - - expect(queryByTestId('topHostScoreContributors')).toBeInTheDocument(); - }); - - it('renders sorted items', () => { - useHostRiskScoreMock.mockReturnValueOnce([ - true, - { - data: [ - { - risk_stats: { - rule_risks: [ - { - rule_name: 'third', - rule_risk: '10', - }, - { - rule_name: 'first', - rule_risk: '99', - }, - { - rule_name: 'second', - rule_risk: '55', - }, - ], - }, - }, - ], - isModuleEnabled: true, - }, - ]); - - const { queryAllByRole } = render( - - - - ); - - expect(queryAllByRole('row')[1]).toHaveTextContent('first'); - expect(queryAllByRole('row')[2]).toHaveTextContent('second'); - expect(queryAllByRole('row')[3]).toHaveTextContent('third'); - }); - - describe('toggleQuery', () => { - beforeEach(() => { - useHostRiskScoreMock.mockReturnValue([ - true, - { - data: [], - isModuleEnabled: true, - }, - ]); - }); - - test('toggleQuery updates toggleStatus', () => { - const { getByTestId } = render( - - - - ); - expect(useHostRiskScoreMock.mock.calls[0][0].skip).toEqual(false); - fireEvent.click(getByTestId('query-toggle-header')); - expect(mockSetToggle).toBeCalledWith(false); - expect(useHostRiskScoreMock.mock.calls[1][0].skip).toEqual(true); - }); - - test('toggleStatus=true, do not skip', () => { - render( - - - - ); - expect(useHostRiskScoreMock.mock.calls[0][0].skip).toEqual(false); - }); - - test('toggleStatus=true, render components', () => { - const { queryByTestId } = render( - - - - ); - expect(queryByTestId('topHostScoreContributors-table')).toBeTruthy(); - }); - - test('toggleStatus=false, do not render components', () => { - mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: mockSetToggle }); - const { queryByTestId } = render( - - - - ); - expect(queryByTestId('topHostScoreContributors-table')).toBeFalsy(); - }); - - test('toggleStatus=false, skip', () => { - mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: mockSetToggle }); - render( - - - - ); - expect(useHostRiskScoreMock.mock.calls[0][0].skip).toEqual(true); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/hosts/components/top_host_score_contributors/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/top_host_score_contributors/index.tsx deleted file mode 100644 index ceb4394619fc5..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/components/top_host_score_contributors/index.tsx +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useCallback, useEffect, useMemo, useState } from 'react'; - -import { - EuiFlexGroup, - EuiFlexItem, - EuiPanel, - EuiInMemoryTable, - EuiTableFieldDataColumnType, -} from '@elastic/eui'; - -import { Direction } from '@kbn/timelines-plugin/common'; -import { HeaderSection } from '../../../common/components/header_section'; -import { InspectButton, InspectButtonContainer } from '../../../common/components/inspect'; -import * as i18n from './translations'; - -import { buildHostNamesFilter, RiskScoreFields } from '../../../../common/search_strategy'; - -import { useQueryInspector } from '../../../common/components/page/manage_query'; -import { HostsComponentsQueryProps } from '../../pages/navigation/types'; - -import { RuleLink } from '../../../detections/pages/detection_engine/rules/all/use_columns'; -import { HostRiskScoreQueryId, useHostRiskScore } from '../../../risk_score/containers'; -import { useQueryToggle } from '../../../common/containers/query_toggle'; - -export interface TopHostScoreContributorsProps - extends Pick { - hostName: string; - from: string; - to: string; -} -interface TableItem { - rank: number; - name: string; - id: string; -} - -const columns: Array> = [ - { - name: i18n.RANK_TITLE, - field: 'rank', - width: '45px', - align: 'right', - }, - { - name: i18n.RULE_NAME_TITLE, - field: 'name', - sortable: true, - truncateText: true, - render: (value: TableItem['name'], { id }: TableItem) => - id ? : value, - }, -]; - -const PAGE_SIZE = 5; -const QUERY_ID = HostRiskScoreQueryId.TOP_HOST_SCORE_CONTRIBUTORS; - -const TopHostScoreContributorsComponent: React.FC = ({ - hostName, - from, - to, - setQuery, - deleteQuery, -}) => { - const timerange = useMemo( - () => ({ - from, - to, - }), - [from, to] - ); - - const sort = useMemo(() => ({ field: RiskScoreFields.timestamp, direction: Direction.desc }), []); - - const { toggleStatus, setToggleStatus } = useQueryToggle(QUERY_ID); - const [querySkip, setQuerySkip] = useState(!toggleStatus); - useEffect(() => { - setQuerySkip(!toggleStatus); - }, [toggleStatus]); - - const toggleQuery = useCallback( - (status: boolean) => { - setToggleStatus(status); - // toggle on = skipQuery false - setQuerySkip(!status); - }, - [setQuerySkip, setToggleStatus] - ); - - const [loading, { data, refetch, inspect }] = useHostRiskScore({ - filterQuery: hostName ? buildHostNamesFilter([hostName]) : undefined, - timerange, - onlyLatest: false, - sort, - skip: querySkip, - pagination: { - querySize: 1, - cursorStart: 0, - }, - }); - - const items = useMemo(() => { - const rules = data && data.length > 0 ? data[0].risk_stats.rule_risks : []; - - return rules - .sort((a, b) => b.rule_risk - a.rule_risk) - .map(({ rule_name: name, rule_id: id }, i) => ({ rank: i + 1, name, id })); - }, [data]); - - const tablePagination = useMemo( - () => ({ - showPerPageOptions: false, - pageSize: PAGE_SIZE, - totalItemCount: items.length, - }), - [items.length] - ); - - useQueryInspector({ - queryId: QUERY_ID, - loading, - refetch, - setQuery, - deleteQuery, - inspect, - }); - - return ( - - - - - - - {toggleStatus && ( - - - - )} - - - {toggleStatus && ( - - - - - - )} - - - ); -}; - -export const TopHostScoreContributors = React.memo(TopHostScoreContributorsComponent); -TopHostScoreContributors.displayName = 'TopHostScoreContributors'; diff --git a/x-pack/plugins/security_solution/public/hosts/links.ts b/x-pack/plugins/security_solution/public/hosts/links.ts new file mode 100644 index 0000000000000..35730291d6c74 --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/links.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; +import { HOSTS_PATH, SecurityPageName } from '../../common/constants'; +import { HOSTS } from '../app/translations'; +import { LinkItem } from '../common/links/types'; + +export const links: LinkItem = { + id: SecurityPageName.hosts, + title: HOSTS, + path: HOSTS_PATH, + globalNavEnabled: true, + globalSearchKeywords: [ + i18n.translate('xpack.securitySolution.appLinks.hosts', { + defaultMessage: 'Hosts', + }), + ], + globalSearchEnabled: true, + globalNavOrder: 9002, + links: [ + { + id: SecurityPageName.hostsAuthentications, + title: i18n.translate('xpack.securitySolution.appLinks.hosts.authentications', { + defaultMessage: 'Authentications', + }), + path: `${HOSTS_PATH}/authentications`, + hideWhenExperimentalKey: 'usersEnabled', + }, + { + id: SecurityPageName.uncommonProcesses, + title: i18n.translate('xpack.securitySolution.appLinks.hosts.uncommonProcesses', { + defaultMessage: 'Uncommon Processes', + }), + path: `${HOSTS_PATH}/uncommonProcesses`, + }, + { + id: SecurityPageName.hostsAnomalies, + title: i18n.translate('xpack.securitySolution.appLinks.hosts.anomalies', { + defaultMessage: 'Anomalies', + }), + path: `${HOSTS_PATH}/anomalies`, + licenseType: 'gold', + }, + { + id: SecurityPageName.hostsEvents, + title: i18n.translate('xpack.securitySolution.appLinks.hosts.events', { + defaultMessage: 'Events', + }), + path: `${HOSTS_PATH}/events`, + }, + { + id: SecurityPageName.hostsExternalAlerts, + title: i18n.translate('xpack.securitySolution.appLinks.hosts.externalAlerts', { + defaultMessage: 'External Alerts', + }), + path: `${HOSTS_PATH}/externalAlerts`, + }, + { + id: SecurityPageName.hostsRisk, + title: i18n.translate('xpack.securitySolution.appLinks.hosts.risk', { + defaultMessage: 'Hosts by risk', + }), + path: `${HOSTS_PATH}/hostRisk`, + experimentalKey: 'riskyHostsEnabled', + }, + { + id: SecurityPageName.sessions, + title: i18n.translate('xpack.securitySolution.appLinks.hosts.sessions', { + defaultMessage: 'Sessions', + }), + path: `${HOSTS_PATH}/sessions`, + isBeta: true, + }, + ], +}; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/constants.ts b/x-pack/plugins/security_solution/public/hosts/pages/navigation/constants.ts new file mode 100644 index 0000000000000..f97fb13c17d15 --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/constants.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const RISKY_HOSTS_DASHBOARD_TITLE = 'Current Risk Score for Hosts'; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/host_risk_tab_body.test.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/host_risk_tab_body.test.tsx new file mode 100644 index 0000000000000..bab6809afc6f6 --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/host_risk_tab_body.test.tsx @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { TestProviders } from '../../../common/mock'; +import { useQueryToggle } from '../../../common/containers/query_toggle'; + +import { useHostRiskScore } from '../../../risk_score/containers'; +import { HostRiskTabBody } from './host_risk_tab_body'; +import { HostsType } from '../../store/model'; + +jest.mock('../../../risk_score/containers'); +jest.mock('../../../common/containers/query_toggle'); +jest.mock('../../../common/lib/kibana'); + +describe('Host query tab body', () => { + const mockUseUserRiskScore = useHostRiskScore as jest.Mock; + const mockUseQueryToggle = useQueryToggle as jest.Mock; + const defaultProps = { + hostName: 'testUser', + indexNames: [], + setQuery: jest.fn(), + skip: false, + startDate: '2019-06-25T04:31:59.345Z', + endDate: '2019-06-25T06:31:59.345Z', + type: HostsType.page, + }; + + beforeEach(() => { + jest.clearAllMocks(); + + mockUseUserRiskScore.mockReturnValue([ + false, + { + inspect: { + dsl: [], + response: [], + }, + isInspected: false, + totalCount: 0, + refetch: jest.fn(), + isModuleEnabled: true, + }, + ]); + }); + + it("doesn't skip when both toggleStatus are true", () => { + mockUseQueryToggle.mockReturnValueOnce({ toggleStatus: true, setToggleStatus: jest.fn() }); + mockUseQueryToggle.mockReturnValueOnce({ toggleStatus: true, setToggleStatus: jest.fn() }); + + render( + + + + ); + expect(mockUseUserRiskScore.mock.calls[0][0].skip).toEqual(false); + }); + + it("doesn't skip when at least one toggleStatus is true", () => { + mockUseQueryToggle.mockReturnValueOnce({ toggleStatus: true, setToggleStatus: jest.fn() }); + mockUseQueryToggle.mockReturnValueOnce({ toggleStatus: false, setToggleStatus: jest.fn() }); + + render( + + + + ); + expect(mockUseUserRiskScore.mock.calls[0][0].skip).toEqual(false); + }); + + it('does skip when both toggleStatus are false', () => { + mockUseQueryToggle.mockReturnValueOnce({ toggleStatus: false, setToggleStatus: jest.fn() }); + mockUseQueryToggle.mockReturnValueOnce({ toggleStatus: false, setToggleStatus: jest.fn() }); + + render( + + + + ); + expect(mockUseUserRiskScore.mock.calls[0][0].skip).toEqual(true); + }); +}); diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/host_risk_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/host_risk_tab_body.tsx index cebcc0ee855ea..7dd76071056e1 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/host_risk_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/host_risk_tab_body.tsx @@ -6,45 +6,106 @@ */ import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; -import { HostRiskScoreOverTime } from '../../components/host_score_over_time'; -import { TopHostScoreContributors } from '../../components/top_host_score_contributors'; + import { HostsComponentsQueryProps } from './types'; import * as i18n from '../translations'; -import { useRiskyHostsDashboardButtonHref } from '../../../overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_button_href'; import { HostRiskInformationButtonEmpty } from '../../components/host_risk_information'; +import { HostRiskScoreQueryId, useHostRiskScore } from '../../../risk_score/containers'; +import { buildHostNamesFilter } from '../../../../common/search_strategy'; +import { useQueryInspector } from '../../../common/components/page/manage_query'; +import { RiskScoreOverTime } from '../../../common/components/risk_score_over_time'; +import { TopRiskScoreContributors } from '../../../common/components/top_risk_score_contributors'; +import { useQueryToggle } from '../../../common/containers/query_toggle'; +import { useDashboardButtonHref } from '../../../common/hooks/use_dashboard_button_href'; +import { RISKY_HOSTS_DASHBOARD_TITLE } from './constants'; const StyledEuiFlexGroup = styled(EuiFlexGroup)` margin-top: ${({ theme }) => theme.eui.paddingSizes.l}; `; +const QUERY_ID = HostRiskScoreQueryId.HOST_DETAILS_RISK_SCORE; + const HostRiskTabBodyComponent: React.FC< Pick & { hostName: string; } > = ({ hostName, startDate, endDate, setQuery, deleteQuery }) => { - const { buttonHref } = useRiskyHostsDashboardButtonHref(startDate, endDate); + const { buttonHref } = useDashboardButtonHref({ + from: startDate, + to: endDate, + title: RISKY_HOSTS_DASHBOARD_TITLE, + }); + + const timerange = useMemo( + () => ({ + from: startDate, + to: endDate, + }), + [startDate, endDate] + ); + + const { toggleStatus: overTimeToggleStatus, setToggleStatus: setOverTimeToggleStatus } = + useQueryToggle(`${QUERY_ID} overTime`); + const { toggleStatus: contributorsToggleStatus, setToggleStatus: setContributorsToggleStatus } = + useQueryToggle(`${QUERY_ID} contributors`); + + const [loading, { data, refetch, inspect }] = useHostRiskScore({ + filterQuery: hostName ? buildHostNamesFilter([hostName]) : undefined, + onlyLatest: false, + skip: !overTimeToggleStatus && !contributorsToggleStatus, + timerange, + }); + + useQueryInspector({ + queryId: QUERY_ID, + loading, + refetch, + setQuery, + deleteQuery, + inspect, + }); + + const toggleContributorsQuery = useCallback( + (status: boolean) => { + setContributorsToggleStatus(status); + }, + [setContributorsToggleStatus] + ); + + const toggleOverTimeQuery = useCallback( + (status: boolean) => { + setOverTimeToggleStatus(status); + }, + [setOverTimeToggleStatus] + ); + + const rules = data && data.length > 0 ? data[data.length - 1].risk_stats.rule_risks : []; return ( <> - + - diff --git a/x-pack/plugins/security_solution/public/hosts/pages/translations.ts b/x-pack/plugins/security_solution/public/hosts/pages/translations.ts index 8b92ef035405f..3e6b23a521026 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/translations.ts +++ b/x-pack/plugins/security_solution/public/hosts/pages/translations.ts @@ -60,7 +60,7 @@ export const NAVIGATION_ALERTS_TITLE = i18n.translate( export const NAVIGATION_HOST_RISK_TITLE = i18n.translate( 'xpack.securitySolution.hosts.navigation.hostRisk', { - defaultMessage: 'Hosts by risk', + defaultMessage: 'Host risk', } ); @@ -97,3 +97,10 @@ export const VIEW_DASHBOARD_BUTTON = i18n.translate( defaultMessage: 'View source dashboard', } ); + +export const HOST_RISK_SCORE_OVER_TIME = i18n.translate( + 'xpack.securitySolution.hosts.navigaton.hostScoreOverTimeTitle', + { + defaultMessage: 'Host risk score over time', + } +); diff --git a/x-pack/plugins/security_solution/public/landing_pages/components/landing_links_icons.test.tsx b/x-pack/plugins/security_solution/public/landing_pages/components/landing_links_icons.test.tsx new file mode 100644 index 0000000000000..3553f44cc621f --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/components/landing_links_icons.test.tsx @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { fireEvent, render } from '@testing-library/react'; +import React from 'react'; +import { SecurityPageName } from '../../app/types'; +import { TestProviders } from '../../common/mock'; +import { LandingLinksIcons, NavItem } from './landing_links_icons'; + +const DEFAULT_NAV_ITEM: NavItem = { + id: SecurityPageName.overview, + label: 'TEST LABEL', + description: 'TEST DESCRIPTION', + icon: 'myTestIcon', +}; + +const mockNavigateTo = jest.fn(); +jest.mock('../../common/lib/kibana', () => { + const originalModule = jest.requireActual('../../common/lib/kibana'); + return { + ...originalModule, + useNavigation: () => ({ + navigateTo: mockNavigateTo, + }), + }; +}); + +jest.mock('../../common/components/link_to', () => { + const originalModule = jest.requireActual('../../common/components/link_to'); + return { + ...originalModule, + useFormatUrl: (id: string) => ({ + formatUrl: jest.fn().mockImplementation((path: string) => `/${id}`), + search: '', + }), + }; +}); + +describe('LandingLinksIcons', () => { + it('renders', () => { + const label = 'test label'; + + const { queryByText } = render( + + + + ); + + expect(queryByText(label)).toBeInTheDocument(); + }); + + it('renders navigation link', () => { + const id = SecurityPageName.administration; + const label = 'myTestLable'; + + const { getByText } = render( + + + + ); + + fireEvent.click(getByText(label)); + + expect(mockNavigateTo).toHaveBeenCalledWith({ url: '/administration' }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/landing_pages/components/landing_links_icons.tsx b/x-pack/plugins/security_solution/public/landing_pages/components/landing_links_icons.tsx new file mode 100644 index 0000000000000..82a0d2148f683 --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/components/landing_links_icons.tsx @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { + EuiFlexGrid, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiText, + EuiTitle, + IconType, +} from '@elastic/eui'; +import React from 'react'; +import styled from 'styled-components'; +import { SecurityPageName } from '../../app/types'; +import { + SecuritySolutionLinkAnchor, + withSecuritySolutionLink, +} from '../../common/components/links'; + +interface LandingLinksImagesProps { + items: NavItem[]; +} + +export interface NavItem { + id: SecurityPageName; + label: string; + icon: IconType; + description: string; + path?: string; +} + +const Link = styled.a` + color: inherit; +`; + +const SecuritySolutionLink = withSecuritySolutionLink(Link); + +const Description = styled(EuiFlexItem)` + max-width: 22em; +`; + +const StyledEuiTitle = styled(EuiTitle)` + margin-top: ${({ theme }) => theme.eui.paddingSizes.m}; + margin-bottom: ${({ theme }) => theme.eui.paddingSizes.xs}; +`; + +export const LandingLinksIcons: React.FC = ({ items }) => ( + + {items.map(({ label, description, path, id, icon }) => ( + + + + + + + + + +

{label}

+
+
+
+ + + {description} + + +
+
+ ))} +
+); diff --git a/x-pack/plugins/security_solution/public/landing_pages/components/landing_links_images.test.tsx b/x-pack/plugins/security_solution/public/landing_pages/components/landing_links_images.test.tsx new file mode 100644 index 0000000000000..479de5e13f432 --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/components/landing_links_images.test.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 { render } from '@testing-library/react'; +import React from 'react'; +import { SecurityPageName } from '../../app/types'; +import { TestProviders } from '../../common/mock'; +import { LandingLinksImages, NavItem } from './landing_links_images'; + +const DEFAULT_NAV_ITEM: NavItem = { + id: SecurityPageName.overview, + label: 'TEST LABEL', + description: 'TEST DESCRIPTION', + image: 'TEST_IMAGE.png', +}; + +jest.mock('../../common/lib/kibana/kibana_react', () => { + return { + useKibana: jest.fn().mockReturnValue({ + services: { + application: { + getUrlForApp: jest.fn(), + }, + }, + }), + }; +}); + +describe('LandingLinksImages', () => { + it('renders', () => { + const label = 'test label'; + + const { queryByText } = render( + + + + ); + + expect(queryByText(label)).toBeInTheDocument(); + }); + + it('renders image', () => { + const image = 'test_image.jpeg'; + const label = 'TEST_LABEL'; + + const { getByTestId } = render( + + + + ); + + expect(getByTestId('LandingLinksImage')).toHaveAttribute('src', image); + }); +}); diff --git a/x-pack/plugins/security_solution/public/landing_pages/components/landing_links_images.tsx b/x-pack/plugins/security_solution/public/landing_pages/components/landing_links_images.tsx new file mode 100644 index 0000000000000..b6a16da8cdc82 --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/components/landing_links_images.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { EuiFlexGroup, EuiFlexItem, EuiImage, EuiPanel, EuiText, EuiTitle } from '@elastic/eui'; +import React from 'react'; +import styled from 'styled-components'; +import { SecurityPageName } from '../../app/types'; +import { withSecuritySolutionLink } from '../../common/components/links'; + +interface LandingLinksImagesProps { + items: NavItem[]; +} + +export interface NavItem { + id: SecurityPageName; + label: string; + image: string; + description: string; + path?: string; +} + +const PrimaryEuiTitle = styled(EuiTitle)` + color: ${(props) => props.theme.eui.euiColorPrimary}; +`; + +const LandingLinksDescripton = styled(EuiText)` + padding-top: ${({ theme }) => theme.eui.paddingSizes.xs}; + max-width: 550px; +`; + +const Link = styled.a` + color: inherit; +`; + +const StyledFlexItem = styled(EuiFlexItem)` + align-items: center; +`; + +const SecuritySolutionLink = withSecuritySolutionLink(Link); + +const Content = styled(EuiFlexItem)` + padding-left: ${({ theme }) => theme.eui.paddingSizes.s}; +`; + +export const LandingLinksImages: React.FC = ({ items }) => ( + + {items.map(({ label, description, path, image, id }) => ( + + + {/* Empty onClick is to force hover style on `EuiPanel` */} + {}}> + + + + + + +

{label}

+
+ + {description} + +
+
+
+
+
+ ))} +
+); diff --git a/x-pack/plugins/security_solution/public/landing_pages/icons/blocklist.tsx b/x-pack/plugins/security_solution/public/landing_pages/icons/blocklist.tsx new file mode 100644 index 0000000000000..75d272034d668 --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/icons/blocklist.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { SVGProps } from 'react'; +export const IconBlocklist: React.FC> = ({ ...props }) => ( + + + + + + + +); diff --git a/x-pack/plugins/security_solution/public/landing_pages/icons/endpoint_policies.tsx b/x-pack/plugins/security_solution/public/landing_pages/icons/endpoint_policies.tsx new file mode 100644 index 0000000000000..7563ac17045a2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/icons/endpoint_policies.tsx @@ -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 React, { SVGProps } from 'react'; +export const IconEndpointPolicies: React.FC> = ({ ...props }) => ( + + + +); diff --git a/x-pack/plugins/security_solution/public/landing_pages/icons/endpoints.tsx b/x-pack/plugins/security_solution/public/landing_pages/icons/endpoints.tsx new file mode 100644 index 0000000000000..a8254d3996c72 --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/icons/endpoints.tsx @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { SVGProps } from 'react'; +export const IconEndpoints: React.FC> = ({ ...props }) => ( + + + + + +); diff --git a/x-pack/plugins/security_solution/public/landing_pages/icons/event_filters.tsx b/x-pack/plugins/security_solution/public/landing_pages/icons/event_filters.tsx new file mode 100644 index 0000000000000..78628b7904e8a --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/icons/event_filters.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { SVGProps } from 'react'; +export const IconEventFilters: React.FC> = ({ ...props }) => ( + + + + + + +); diff --git a/x-pack/plugins/security_solution/public/landing_pages/icons/exception_lists.tsx b/x-pack/plugins/security_solution/public/landing_pages/icons/exception_lists.tsx new file mode 100644 index 0000000000000..a4cc7a25a9785 --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/icons/exception_lists.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, { SVGProps } from 'react'; +export const IconExceptionLists: React.FC> = ({ ...props }) => ( + + + + + + +); diff --git a/x-pack/plugins/security_solution/public/landing_pages/icons/host_isolation.tsx b/x-pack/plugins/security_solution/public/landing_pages/icons/host_isolation.tsx new file mode 100644 index 0000000000000..c0df14075c958 --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/icons/host_isolation.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { SVGProps } from 'react'; +export const IconHostIsolation: React.FC> = ({ ...props }) => ( + + + + + + + + + + + + + +); diff --git a/x-pack/plugins/security_solution/public/landing_pages/icons/siem_rules.tsx b/x-pack/plugins/security_solution/public/landing_pages/icons/siem_rules.tsx new file mode 100644 index 0000000000000..fe89c8fb22581 --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/icons/siem_rules.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, { SVGProps } from 'react'; +export const IconSiemRules: React.FC> = ({ ...props }) => ( + + + + + + + + + + + + + + + + + +); diff --git a/x-pack/plugins/security_solution/public/landing_pages/icons/trusted_applications.tsx b/x-pack/plugins/security_solution/public/landing_pages/icons/trusted_applications.tsx new file mode 100644 index 0000000000000..7b9f980b7f231 --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/icons/trusted_applications.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { SVGProps } from 'react'; +export const IconTrustedApplications: React.FC> = ({ ...props }) => ( + + + + + + + + +); diff --git a/x-pack/plugins/security_solution/public/landing_pages/index.ts b/x-pack/plugins/security_solution/public/landing_pages/index.ts new file mode 100644 index 0000000000000..cac375cce6b2e --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/index.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 { SecuritySubPlugin } from '../app/types'; +import { routes } from './routes'; + +export class LandingPages { + public setup() {} + + public start(): SecuritySubPlugin { + return { + routes, + }; + } +} diff --git a/x-pack/plugins/security_solution/public/landing_pages/jest.config.js b/x-pack/plugins/security_solution/public/landing_pages/jest.config.js new file mode 100644 index 0000000000000..41b8d41269263 --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['/x-pack/plugins/security_solution/public/landing_pages'], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/security_solution/public/landing_pages', + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/security_solution/public/landing_pages/**/*.{ts,tsx}', + ], + // See: https://github.com/elastic/kibana/issues/117255, the moduleNameMapper creates mocks to avoid memory leaks from kibana core. + moduleNameMapper: { + 'core/server$': '/x-pack/plugins/security_solution/server/__mocks__/core.mock.ts', + 'task_manager/server$': + '/x-pack/plugins/security_solution/server/__mocks__/task_manager.mock.ts', + 'alerting/server$': '/x-pack/plugins/security_solution/server/__mocks__/alert.mock.ts', + 'actions/server$': '/x-pack/plugins/security_solution/server/__mocks__/action.mock.ts', + }, +}; diff --git a/x-pack/plugins/security_solution/public/landing_pages/pages/dashboards.tsx b/x-pack/plugins/security_solution/public/landing_pages/pages/dashboards.tsx new file mode 100644 index 0000000000000..8c49fda169ad3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/pages/dashboards.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { SecurityPageName } from '../../app/types'; +import { HeaderPage } from '../../common/components/header_page'; +import { SecuritySolutionPageWrapper } from '../../common/components/page_wrapper'; +import { SpyRoute } from '../../common/utils/route/spy_routes'; +import { LandingLinksImages, NavItem } from '../components/landing_links_images'; +import { DASHBOARDS_PAGE_TITLE } from './translations'; +import overviewPageImg from '../../common/images/overview_page.png'; +import { OVERVIEW } from '../../app/translations'; + +const items: NavItem[] = [ + { + id: SecurityPageName.overview, + label: OVERVIEW, + description: i18n.translate('xpack.securitySolution.landing.dashboards.overviewDescription', { + defaultMessage: 'What is going in your secuity environment', + }), + image: overviewPageImg, + }, +]; + +export const DashboardsLandingPage = () => ( + + + + + +); diff --git a/x-pack/plugins/security_solution/public/landing_pages/pages/manage.test.tsx b/x-pack/plugins/security_solution/public/landing_pages/pages/manage.test.tsx new file mode 100644 index 0000000000000..efb1bcf35c39e --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/pages/manage.test.tsx @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render } from '@testing-library/react'; +import React from 'react'; +import { SecurityPageName } from '../../app/types'; +import { TestProviders } from '../../common/mock'; +import { LandingCategories, NavConfigType } from './manage'; + +const RULES_ITEM_LABEL = 'elastic rules!'; +const EXCEPTIONS_ITEM_LABEL = 'exceptional!'; + +const testConfig: NavConfigType = { + categories: [ + { + label: 'first tests category', + itemIds: [SecurityPageName.rules], + }, + { + label: 'second tests category', + itemIds: [SecurityPageName.exceptions], + }, + ], + items: [ + { + id: SecurityPageName.rules, + label: RULES_ITEM_LABEL, + description: '', + icon: 'testIcon1', + }, + { + id: SecurityPageName.exceptions, + label: EXCEPTIONS_ITEM_LABEL, + description: '', + icon: 'testIcon2', + }, + ], +}; + +describe('LandingCategories', () => { + it('renders items', () => { + const { queryByText } = render( + + + + ); + + expect(queryByText(RULES_ITEM_LABEL)).toBeInTheDocument(); + expect(queryByText(EXCEPTIONS_ITEM_LABEL)).toBeInTheDocument(); + }); + + it('renders items in the same order as defined', () => { + const { queryAllByTestId } = render( + + + + ); + + const renderedItems = queryAllByTestId('LandingItem'); + + expect(renderedItems[0]).toHaveTextContent(EXCEPTIONS_ITEM_LABEL); + expect(renderedItems[1]).toHaveTextContent(RULES_ITEM_LABEL); + }); +}); diff --git a/x-pack/plugins/security_solution/public/landing_pages/pages/manage.tsx b/x-pack/plugins/security_solution/public/landing_pages/pages/manage.tsx new file mode 100644 index 0000000000000..da4d25f621305 --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/pages/manage.tsx @@ -0,0 +1,179 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { EuiHorizontalRule, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { compact } from 'lodash/fp'; +import React from 'react'; +import styled from 'styled-components'; +import { + BLOCKLIST, + ENDPOINTS, + EVENT_FILTERS, + EXCEPTIONS, + TRUSTED_APPLICATIONS, +} from '../../app/translations'; +import { SecurityPageName } from '../../app/types'; +import { HeaderPage } from '../../common/components/header_page'; +import { SecuritySolutionPageWrapper } from '../../common/components/page_wrapper'; +import { SpyRoute } from '../../common/utils/route/spy_routes'; +import { LandingLinksIcons, NavItem } from '../components/landing_links_icons'; +import { IconBlocklist } from '../icons/blocklist'; +import { IconEndpoints } from '../icons/endpoints'; +import { IconEndpointPolicies } from '../icons/endpoint_policies'; +import { IconEventFilters } from '../icons/event_filters'; +import { IconExceptionLists } from '../icons/exception_lists'; +import { IconHostIsolation } from '../icons/host_isolation'; +import { IconSiemRules } from '../icons/siem_rules'; +import { IconTrustedApplications } from '../icons/trusted_applications'; +import { MANAGE_PAGE_TITLE } from './translations'; + +// TODO +const FIX_ME_TEMPORARY_DESCRIPTION = 'Description here'; + +export interface NavConfigType { + items: NavItem[]; + categories: Array<{ label: string; itemIds: SecurityPageName[] }>; +} + +const config: NavConfigType = { + categories: [ + { + label: i18n.translate('xpack.securitySolution.landing.threatHunting.siemTitle', { + defaultMessage: 'SIEM', + }), + itemIds: [SecurityPageName.rules, SecurityPageName.exceptions], + }, + { + label: i18n.translate('xpack.securitySolution.landing.threatHunting.endpointsTitle', { + defaultMessage: 'ENDPOINTS', + }), + itemIds: [ + SecurityPageName.endpoints, + SecurityPageName.policies, + SecurityPageName.trustedApps, + SecurityPageName.eventFilters, + SecurityPageName.blocklist, + SecurityPageName.hostIsolationExceptions, + ], + }, + ], + items: [ + { + id: SecurityPageName.rules, + label: i18n.translate('xpack.securitySolution.landing.manage.rulesLabel', { + defaultMessage: 'SIEM rules', + }), + description: FIX_ME_TEMPORARY_DESCRIPTION, + icon: IconSiemRules, + }, + { + id: SecurityPageName.exceptions, + label: EXCEPTIONS, + description: FIX_ME_TEMPORARY_DESCRIPTION, + icon: IconExceptionLists, + }, + { + id: SecurityPageName.endpoints, + label: ENDPOINTS, + description: i18n.translate('xpack.securitySolution.landing.manage.endpointsDescription', { + defaultMessage: 'Hosts running endpoint security', + }), + icon: IconEndpoints, + }, + { + id: SecurityPageName.policies, + label: i18n.translate('xpack.securitySolution.landing.manage.endpointPoliceLabel', { + defaultMessage: 'Endpoint policies', + }), + description: FIX_ME_TEMPORARY_DESCRIPTION, + icon: IconEndpointPolicies, + }, + { + id: SecurityPageName.trustedApps, + label: TRUSTED_APPLICATIONS, + description: i18n.translate( + 'xpack.securitySolution.landing.manage.trustedApplicationsDescription', + { + defaultMessage: + 'Improve performance or alleviate conflicts with other applications running on your hosts', + } + ), + icon: IconTrustedApplications, + }, + { + id: SecurityPageName.eventFilters, + label: EVENT_FILTERS, + description: i18n.translate('xpack.securitySolution.landing.manage.eventFiltersDescription', { + defaultMessage: 'Exclude unwanted applications from running on your hosts', + }), + icon: IconEventFilters, + }, + { + id: SecurityPageName.blocklist, + label: BLOCKLIST, + description: FIX_ME_TEMPORARY_DESCRIPTION, + icon: IconBlocklist, + }, + { + id: SecurityPageName.hostIsolationExceptions, + label: i18n.translate('xpack.securitySolution.landing.manage.hostIsolationLabel', { + defaultMessage: 'Host isolation IP exceptions', + }), + description: i18n.translate( + 'xpack.securitySolution.landing.manage.hostIsolationDescription', + { + defaultMessage: 'Allow isolated hosts to communicate with specific IPs', + } + ), + + icon: IconHostIsolation, + }, + ], +}; + +export const ManageLandingPage = () => ( + + + + + +); + +const StyledEuiHorizontalRule = styled(EuiHorizontalRule)` + margin-top: ${({ theme }) => theme.eui.paddingSizes.m}; + margin-bottom: ${({ theme }) => theme.eui.paddingSizes.l}; +`; + +const getNavItembyId = (navConfig: NavConfigType) => (itemId: string) => + navConfig.items.find(({ id }: NavItem) => id === itemId); + +const navItemsFromIds = (itemIds: SecurityPageName[], navConfig: NavConfigType) => + compact(itemIds.map(getNavItembyId(navConfig))); + +export const LandingCategories = React.memo(({ navConfig }: { navConfig: NavConfigType }) => { + return ( + <> + {navConfig.categories.map(({ label, itemIds }, index) => ( +
+ {index > 0 && ( + <> + + + + )} + +

{label}

+
+ + +
+ ))} + + ); +}); + +LandingCategories.displayName = 'LandingCategories'; diff --git a/x-pack/plugins/security_solution/public/landing_pages/pages/threat_hunting.tsx b/x-pack/plugins/security_solution/public/landing_pages/pages/threat_hunting.tsx new file mode 100644 index 0000000000000..2a0f4e471a75d --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/pages/threat_hunting.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { SecurityPageName } from '../../app/types'; +import { HeaderPage } from '../../common/components/header_page'; +import { SecuritySolutionPageWrapper } from '../../common/components/page_wrapper'; +import { SpyRoute } from '../../common/utils/route/spy_routes'; +import { LandingLinksImages, NavItem } from '../components/landing_links_images'; +import { THREAT_HUNTING_PAGE_TITLE } from './translations'; +import userPageImg from '../../common/images/users_page.png'; +import hostsPageImg from '../../common/images/hosts_page.png'; +import networkPageImg from '../../common/images/network_page.png'; +import { HOSTS, NETWORK, USERS } from '../../app/translations'; + +const items: NavItem[] = [ + { + id: SecurityPageName.hosts, + label: HOSTS, + description: i18n.translate('xpack.securitySolution.landing.threatHunting.hostsDescription', { + defaultMessage: + 'Computer or other device that communicates with other hosts on a network. Hosts on a network include clients and servers -- that send or receive data, services or applications.', + }), + image: hostsPageImg, + }, + { + id: SecurityPageName.network, + label: NETWORK, + description: i18n.translate('xpack.securitySolution.landing.threatHunting.networkDescription', { + defaultMessage: + 'The action or process of interacting with others to exchange information and develop professional or social contacts.', + }), + image: networkPageImg, + }, + { + id: SecurityPageName.users, + label: USERS, + description: i18n.translate('xpack.securitySolution.landing.threatHunting.usersDescription', { + defaultMessage: 'Sudo commands dashboard from the Logs System integration.', + }), + image: userPageImg, + }, +]; + +export const ThreatHuntingLandingPage = () => ( + + + + + +); diff --git a/x-pack/plugins/security_solution/public/landing_pages/pages/translations.ts b/x-pack/plugins/security_solution/public/landing_pages/pages/translations.ts new file mode 100644 index 0000000000000..13a2396201cc5 --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/pages/translations.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 { i18n } from '@kbn/i18n'; + +export const THREAT_HUNTING_PAGE_TITLE = i18n.translate( + 'xpack.securitySolution.landing.threatHunting.pageTitle', + { + defaultMessage: 'Threat hunting', + } +); + +export const DASHBOARDS_PAGE_TITLE = i18n.translate( + 'xpack.securitySolution.landing.dashboards.pageTitle', + { + defaultMessage: 'Dashboards', + } +); + +export const MANAGE_PAGE_TITLE = i18n.translate('xpack.securitySolution.landing.manage.pageTitle', { + defaultMessage: 'Manage', +}); diff --git a/x-pack/plugins/security_solution/public/landing_pages/routes.tsx b/x-pack/plugins/security_solution/public/landing_pages/routes.tsx new file mode 100644 index 0000000000000..3fbe33cc0ec88 --- /dev/null +++ b/x-pack/plugins/security_solution/public/landing_pages/routes.tsx @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { TrackApplicationView } from '@kbn/usage-collection-plugin/public'; + +import { SecurityPageName, SecuritySubPluginRoutes } from '../app/types'; +import { DASHBOARDS_PATH, MANAGE_PATH, THREAT_HUNTING_PATH } from '../../common/constants'; +import { ThreatHuntingLandingPage } from './pages/threat_hunting'; +import { DashboardsLandingPage } from './pages/dashboards'; +import { ManageLandingPage } from './pages/manage'; + +export const ThreatHuntingRoutes = () => ( + + + +); + +export const DashboardRoutes = () => ( + + + +); + +export const ManageRoutes = () => ( + + + +); + +export const routes: SecuritySubPluginRoutes = [ + { + path: THREAT_HUNTING_PATH, + render: ThreatHuntingRoutes, + }, + { + path: DASHBOARDS_PATH, + render: DashboardRoutes, + }, + { + path: MANAGE_PATH, + render: ManageRoutes, + }, +]; diff --git a/x-pack/plugins/security_solution/public/lazy_sub_plugins.tsx b/x-pack/plugins/security_solution/public/lazy_sub_plugins.tsx index bdf70a995aea1..a9562207f9eaa 100644 --- a/x-pack/plugins/security_solution/public/lazy_sub_plugins.tsx +++ b/x-pack/plugins/security_solution/public/lazy_sub_plugins.tsx @@ -22,6 +22,7 @@ import { Rules } from './rules'; import { Timelines } from './timelines'; import { Management } from './management'; +import { LandingPages } from './landing_pages'; /** * The classes used to instantiate the sub plugins. These are grouped into a single object for the sake of bundling them in a single dynamic import. @@ -38,5 +39,6 @@ const subPluginClasses = { Rules, Timelines, Management, + LandingPages, }; export { subPluginClasses }; diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/bad_argument.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/bad_argument.tsx index 8ff4b71668fd4..2f4b4d241f48c 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/bad_argument.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/bad_argument.tsx @@ -5,11 +5,10 @@ * 2.0. */ -import React, { memo, PropsWithChildren } from 'react'; -import { EuiCallOut, EuiText } from '@elastic/eui'; -import { UserCommandInput } from './user_command_input'; +import React, { memo, PropsWithChildren, useEffect } from 'react'; +import { EuiCallOut } from '@elastic/eui'; import { ParsedCommandInput } from '../service/parsed_command_input'; -import { CommandDefinition } from '../types'; +import { CommandDefinition, CommandExecutionComponentProps } from '../types'; import { CommandInputUsage } from './command_usage'; import { useDataTestSubj } from '../hooks/state_selectors/use_data_test_subj'; import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; @@ -19,21 +18,22 @@ export type BadArgumentProps = PropsWithChildren<{ commandDefinition: CommandDefinition; }>; -export const BadArgument = memo( - ({ parsedInput, commandDefinition, children = null }) => { - const getTestId = useTestIdGenerator(useDataTestSubj()); +/** + * Shows a bad argument error. The error message needs to be defined via the Command History Item's + * `state.errorMessage` + */ +export const BadArgument = memo(({ command, setStatus, store }) => { + const getTestId = useTestIdGenerator(useDataTestSubj()); + + useEffect(() => { + setStatus('success'); + }, [setStatus]); - return ( - <> - - - - - {children} - - - - ); - } -); + return ( + + {store.errorMessage} + + + ); +}); BadArgument.displayName = 'BadArgument'; diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/builtin_commands/clear_command.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/builtin_commands/clear_command.tsx new file mode 100644 index 0000000000000..bfa06f55d2665 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/console/components/builtin_commands/clear_command.tsx @@ -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 { memo, useEffect } from 'react'; +import { useConsoleStateDispatch } from '../../hooks/state_selectors/use_console_state_dispatch'; +import { CommandExecutionComponentProps } from '../../types'; + +export const ClearCommand = memo(({ status, setStatus }) => { + const dispatch = useConsoleStateDispatch(); + + useEffect(() => { + if (status === 'pending') { + dispatch({ type: 'clear' }); + } + setStatus('success'); + }, [status, setStatus, dispatch]); + + return null; +}); +ClearCommand.displayName = 'ClearCommand'; diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/builtin_commands/help_command.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/builtin_commands/help_command.tsx new file mode 100644 index 0000000000000..f8c66f31e396d --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/console/components/builtin_commands/help_command.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import React, { memo, useEffect } from 'react'; +import { useWithCustomHelpComponent } from '../../hooks/state_selectors/use_with_custom_help_component'; +import { CommandList } from '../command_list'; +import { useWithCommandList } from '../../hooks/state_selectors/use_with_command_list'; +import type { CommandExecutionComponentProps } from '../../types'; +import { HelpOutput } from '../help_output'; + +export const HelpCommand = memo((props) => { + const commands = useWithCommandList(); + const CustomHelpComponent = useWithCustomHelpComponent(); + + useEffect(() => { + if (!CustomHelpComponent) { + props.setStatus('success'); + } + }, [CustomHelpComponent, props]); + + return CustomHelpComponent ? ( + + ) : ( + + + + ); +}); +HelpCommand.displayName = 'HelpCommand'; diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/builtin_commands/help_command_argument.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/builtin_commands/help_command_argument.tsx new file mode 100644 index 0000000000000..f67c44013d059 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/console/components/builtin_commands/help_command_argument.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useEffect } from 'react'; + +import { i18n } from '@kbn/i18n'; +import { CommandUsage } from '../command_usage'; +import { HelpOutput } from '../help_output'; +import { CommandExecutionComponentProps } from '../../types'; + +/** + * Builtin component that handles the output of command's `--help` argument + */ +export const HelpCommandArgument = memo((props) => { + const CustomCommandHelp = props.command.commandDefinition.HelpComponent; + + useEffect(() => { + if (!CustomCommandHelp) { + props.setStatus('success'); + } + }, [CustomCommandHelp, props]); + + return CustomCommandHelp ? ( + + ) : ( + + + + ); +}); +HelpCommandArgument.displayName = 'HelpCommandArgument'; diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/command_execution_output.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/command_execution_output.tsx index 8bb9769980914..8a6611ffbbb18 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/command_execution_output.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/command_execution_output.tsx @@ -5,103 +5,74 @@ * 2.0. */ -import React, { memo, ReactNode, useCallback, useEffect, useState } from 'react'; -import { EuiButton, EuiLoadingChart } from '@elastic/eui'; +import React, { memo, useCallback, useMemo } from 'react'; +import { EuiLoadingChart } from '@elastic/eui'; import styled from 'styled-components'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { CommandExecutionFailure } from './command_execution_failure'; +import type { CommandExecutionState, CommandHistoryItem } from './console_state/types'; import { UserCommandInput } from './user_command_input'; -import { Command } from '../types'; -import { useCommandService } from '../hooks/state_selectors/use_command_service'; import { useConsoleStateDispatch } from '../hooks/state_selectors/use_console_state_dispatch'; const CommandOutputContainer = styled.div` position: relative; - - .run-in-background { - position: absolute; - right: 0; - top: 1em; - } `; export interface CommandExecutionOutputProps { - command: Command; + item: CommandHistoryItem; } -export const CommandExecutionOutput = memo(({ command }) => { - const commandService = useCommandService(); - const [isRunning, setIsRunning] = useState(true); - const [output, setOutput] = useState(null); - const dispatch = useConsoleStateDispatch(); - - // FIXME:PT implement the `run in the background` functionality - const [showRunInBackground, setShowRunInTheBackground] = useState(false); - const handleRunInBackgroundClick = useCallback(() => { - setShowRunInTheBackground(false); - }, []); - - useEffect(() => { - (async () => { - const timeoutId = setTimeout(() => { - setShowRunInTheBackground(true); - }, 15000); +export const CommandExecutionOutput = memo( + ({ item: { command, state, id } }) => { + const dispatch = useConsoleStateDispatch(); + const RenderComponent = command.commandDefinition.RenderComponent; - try { - const commandOutput = await commandService.executeCommand(command); - setOutput(commandOutput.result); + const isRunning = useMemo(() => { + return state.status === 'pending'; + }, [state.status]); - // FIXME: PT the console should scroll the bottom as well - } catch (error) { - setOutput(); - } + /** Updates the Command's status */ + const setCommandStatus = useCallback( + (status: CommandExecutionState['status']) => { + dispatch({ + type: 'updateCommandStatusState', + payload: { + id, + value: status, + }, + }); + }, + [dispatch, id] + ); - clearTimeout(timeoutId); - setIsRunning(false); - setShowRunInTheBackground(false); - })(); - }, [command, commandService]); + /** Updates the Command's execution store */ + const setCommandStore = useCallback( + (store) => { + dispatch({ + type: 'updateCommandStoreState', + payload: { + id, + value: store, + }, + }); + }, + [dispatch, id] + ); - useEffect(() => { - if (!isRunning) { - dispatch({ type: 'scrollDown' }); - } - }, [isRunning, dispatch]); - - return ( - - {showRunInBackground && ( -
- - - + return ( + +
+ + {isRunning && } +
+
+
- )} -
- - {isRunning && ( - <> - - - )} -
-
{output}
-
- ); -}); + + ); + } +); CommandExecutionOutput.displayName = 'CommandExecutionOutput'; diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/command_usage.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/command_usage.tsx index 9d17d83f0266f..68b2aab558d83 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/command_usage.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/command_usage.tsx @@ -79,22 +79,20 @@ export const CommandUsage = memo(({ commandDef }) => { {hasArgs && ( <> -

- - - {commandDef.mustHaveArgs && commandDef.args && hasArgs && ( - - - - )} - -

+ + + {commandDef.mustHaveArgs && commandDef.args && hasArgs && ( + + + + )} + {commandDef.args && ( { it("should persist a console's command output history on hide/show", async () => { await render(); enterConsoleCommand(renderResult, 'help', { dataTestSubj: 'testRunningConsole' }); - enterConsoleCommand(renderResult, 'help', { dataTestSubj: 'testRunningConsole' }); + enterConsoleCommand(renderResult, 'cmd1', { dataTestSubj: 'testRunningConsole' }); await waitFor(() => { expect(renderResult.queryAllByTestId('testRunningConsole-historyItem')).toHaveLength(2); }); + // Hide the console userEvent.click(renderResult.getByTestId('consolePopupHideButton')); await waitFor(() => { expect( @@ -317,6 +318,7 @@ describe('When using ConsoleManager', () => { ).toBe(true); }); + // Open the console back up and ensure prior items still there await openRunningConsole(); await waitFor(() => { @@ -324,6 +326,46 @@ describe('When using ConsoleManager', () => { }); }); + it('should provide console rendering state between show/hide', async () => { + const expectedStoreValue = JSON.stringify({ foo: 'bar' }, null, 2); + await render(); + enterConsoleCommand(renderResult, 'cmd1', { dataTestSubj: 'testRunningConsole' }); + + // Command should have `pending` status and no store values + expect(renderResult.getByTestId('exec-output-statusState').textContent).toEqual( + 'status: pending' + ); + expect(renderResult.getByTestId('exec-output-storeStateJson').textContent).toEqual('{}'); + + // Wait for component to update the status and store values + await waitFor(() => { + expect(renderResult.getByTestId('exec-output-statusState').textContent).toMatch( + 'status: success' + ); + }); + expect(renderResult.getByTestId('exec-output-storeStateJson').textContent).toEqual( + expectedStoreValue + ); + + // Hide the console + userEvent.click(renderResult.getByTestId('consolePopupHideButton')); + await waitFor(() => { + expect( + renderResult.getByTestId('consolePopupWrapper').classList.contains('is-hidden') + ).toBe(true); + }); + + // Open the console back up and ensure `status` and `store` are the last set of values + await openRunningConsole(); + + expect(renderResult.getByTestId('exec-output-statusState').textContent).toMatch( + 'status: success' + ); + expect(renderResult.getByTestId('exec-output-storeStateJson').textContent).toEqual( + expectedStoreValue + ); + }); + describe('and the terminate confirmation is shown', () => { const clickOnTerminateButton = async () => { userEvent.click(renderResult.getByTestId('consolePopupTerminateButton')); diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/console_manager/mocks.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/console_manager/mocks.tsx index 57ec4246caf41..0b841f4118d1f 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/console_manager/mocks.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/console_manager/mocks.tsx @@ -9,7 +9,7 @@ import React, { memo, useCallback } from 'react'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { ConsoleRegistrationInterface, RegisteredConsoleClient } from './types'; import { useConsoleManager } from './console_manager'; -import { getCommandServiceMock } from '../../mocks'; +import { getCommandListMock } from '../../mocks'; export const getNewConsoleRegistrationMock = ( overrides: Partial = {} @@ -20,7 +20,7 @@ export const getNewConsoleRegistrationMock = ( meta: { about: 'for unit testing ' }, consoleProps: { 'data-test-subj': 'testRunningConsole', - commandService: getCommandServiceMock(), + commands: getCommandListMock(), }, onBeforeTerminate: jest.fn(), ...overrides, diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/console_state/console_state.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/console_state/console_state.tsx index 852b2b1ab58fe..66c874e4e27a8 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/console_state/console_state.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/console_state/console_state.tsx @@ -17,10 +17,10 @@ type ConsoleStateProviderProps = PropsWithChildren<{}> & InitialStateInterface; * A Console wide data store for internal state management between inner components */ export const ConsoleStateProvider = memo( - ({ commandService, scrollToBottom, dataTestSubj, children }) => { + ({ commands, scrollToBottom, HelpComponent, dataTestSubj, children }) => { const [state, dispatch] = useReducer( stateDataReducer, - { commandService, scrollToBottom, dataTestSubj }, + { commands, scrollToBottom, HelpComponent, dataTestSubj }, initiateState ); diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/console_state/state_reducer.ts b/x-pack/plugins/security_solution/public/management/components/console/components/console_state/state_reducer.ts index 94175d9821ae7..68024aa5b7cfc 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/console_state/state_reducer.ts +++ b/x-pack/plugins/security_solution/public/management/components/console/components/console_state/state_reducer.ts @@ -5,26 +5,28 @@ * 2.0. */ -import { ConsoleDataState, ConsoleStoreReducer } from './types'; +import { handleUpdateCommandState } from './state_update_handlers/handle_update_command_state'; +import type { ConsoleDataState, ConsoleStoreReducer } from './types'; import { handleExecuteCommand } from './state_update_handlers/handle_execute_command'; -import { ConsoleBuiltinCommandsService } from '../../service/builtin_command_service'; +import { getBuiltinCommands } from '../../service/builtin_commands'; export type InitialStateInterface = Pick< ConsoleDataState, - 'commandService' | 'scrollToBottom' | 'dataTestSubj' + 'commands' | 'scrollToBottom' | 'dataTestSubj' | 'HelpComponent' >; export const initiateState = ({ - commandService, + commands, scrollToBottom, dataTestSubj, + HelpComponent, }: InitialStateInterface): ConsoleDataState => { return { - commandService, + commands: getBuiltinCommands().concat(commands), scrollToBottom, + HelpComponent, dataTestSubj, commandHistory: [], - builtinCommandService: new ConsoleBuiltinCommandsService(), }; }; @@ -36,6 +38,13 @@ export const stateDataReducer: ConsoleStoreReducer = (state, action) => { case 'executeCommand': return handleExecuteCommand(state, action); + + case 'updateCommandStatusState': + case 'updateCommandStoreState': + return handleUpdateCommandState(state, action); + + case 'clear': + return { ...state, commandHistory: [] }; } return state; diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/console_state/state_update_handlers/handle_execute_command.test.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/console_state/state_update_handlers/handle_execute_command.test.tsx index 06ecc344d5596..d19376395742f 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/console_state/state_update_handlers/handle_execute_command.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/console_state/state_update_handlers/handle_execute_command.test.tsx @@ -15,13 +15,13 @@ import { ConsoleProps } from '../../../types'; describe('When a Console command is entered by the user', () => { let render: (props?: Partial) => ReturnType; let renderResult: ReturnType; - let commandServiceMock: ConsoleTestSetup['commandServiceMock']; + let commands: ConsoleTestSetup['commands']; let enterCommand: ConsoleTestSetup['enterCommand']; beforeEach(() => { const testSetup = getConsoleTestSetup(); - ({ commandServiceMock, enterCommand } = testSetup); + ({ commands, enterCommand } = testSetup); render = (props = {}) => (renderResult = testSetup.renderConsole(props)); }); @@ -34,18 +34,16 @@ describe('When a Console command is entered by the user', () => { await waitFor(() => { expect(renderResult.getAllByTestId('test-commandList-command')).toHaveLength( // `+2` to account for builtin commands - commandServiceMock.getCommandList().length + 2 + commands.length + 2 ); }); }); it('should display custom help output when Command service has `getHelp()` defined', async () => { - commandServiceMock.getHelp = async () => { - return { - result:
{'help output'}
, - }; + const HelpComponent: React.FunctionComponent = () => { + return
{'help output'}
; }; - render(); + render({ HelpComponent }); enterCommand('help'); await waitFor(() => { @@ -73,11 +71,15 @@ describe('When a Console command is entered by the user', () => { }); it('should should custom command `--help` output when Command service defines `getCommandUsage()`', async () => { - commandServiceMock.getCommandUsage = async () => { - return { - result:
{'command help here'}
, + const cmd2 = commands.find((command) => command.name === 'cmd2'); + + if (cmd2) { + cmd2.HelpComponent = () => { + return
{'command help here'}
; }; - }; + cmd2.HelpComponent.displayName = 'HelpComponent'; + } + render(); enterCommand('cmd2 --help'); diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/console_state/state_update_handlers/handle_execute_command.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/console_state/state_update_handlers/handle_execute_command.tsx index 2815ec4605917..c387cf3d90a8f 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/console_state/state_update_handlers/handle_execute_command.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/console_state/state_update_handlers/handle_execute_command.tsx @@ -5,19 +5,19 @@ * 2.0. */ -/* eslint complexity: ["error", 40]*/ -// FIXME:PT remove the complexity - -import React from 'react'; import { i18n } from '@kbn/i18n'; -import { ConsoleDataAction, ConsoleDataState, ConsoleStoreReducer } from '../types'; +import { v4 as uuidV4 } from 'uuid'; +import { HelpCommandArgument } from '../../builtin_commands/help_command_argument'; +import { + CommandHistoryItem, + ConsoleDataAction, + ConsoleDataState, + ConsoleStoreReducer, +} from '../types'; import { parseCommandInput } from '../../../service/parsed_command_input'; -import { HistoryItem } from '../../history_item'; import { UnknownCommand } from '../../unknow_comand'; -import { HelpOutput } from '../../help_output'; import { BadArgument } from '../../bad_argument'; -import { CommandExecutionOutput } from '../../command_execution_output'; -import { CommandDefinition } from '../../../types'; +import { Command, CommandDefinition, CommandExecutionComponentProps } from '../../../types'; const toCliArgumentOption = (argName: string) => `--${argName}`; @@ -41,6 +41,36 @@ const updateStateWithNewCommandHistoryItem = ( }; }; +const UnknownCommandDefinition: CommandDefinition = { + name: 'unknown-command', + about: 'unknown command', + RenderComponent: () => null, +}; + +const createCommandExecutionState = ( + store: CommandExecutionComponentProps['store'] = {} +): CommandHistoryItem['state'] => { + return { + status: 'pending', + store, + }; +}; + +const cloneCommandDefinitionWithNewRenderComponent = ( + command: Command, + RenderComponent: CommandDefinition['RenderComponent'] +): Command => { + return { + ...command, + commandDefinition: { + ...command.commandDefinition, + // We use the original command definition, but replace + // the RenderComponent for this invocation + RenderComponent, + }, + }; +}; + export const handleExecuteCommand: ConsoleStoreReducer< ConsoleDataAction & { type: 'executeCommand' } > = (state, action) => { @@ -50,116 +80,98 @@ export const handleExecuteCommand: ConsoleStoreReducer< return state; } - const { commandService, builtinCommandService } = state; - - // Is it an internal command? - if (builtinCommandService.isBuiltin(parsedInput.name)) { - const commandOutput = builtinCommandService.executeBuiltinCommand(parsedInput, commandService); - - if (commandOutput.clearBuffer) { - return { - ...state, - commandHistory: [], - }; - } - - return updateStateWithNewCommandHistoryItem(state, commandOutput.result); - } - - // ---------------------------------------------------- - // Validate and execute the user defined command - // ---------------------------------------------------- - const commandDefinition = commandService - .getCommandList() - .find((definition) => definition.name === parsedInput.name); + const { commands } = state; + const commandDefinition: CommandDefinition | undefined = commands.find( + (definition) => definition.name === parsedInput.name + ); // Unknown command if (!commandDefinition) { - return updateStateWithNewCommandHistoryItem( - state, - - - - ); + return updateStateWithNewCommandHistoryItem(state, { + id: uuidV4(), + command: { + input: parsedInput.input, + args: parsedInput, + commandDefinition: { + ...UnknownCommandDefinition, + RenderComponent: UnknownCommand, + }, + }, + state: createCommandExecutionState(), + }); } + const command = { + input: parsedInput.input, + args: parsedInput, + commandDefinition, + }; const requiredArgs = getRequiredArguments(commandDefinition.args); // If args were entered, then validate them if (parsedInput.hasArgs()) { // Show command help if (parsedInput.hasArg('help')) { - return updateStateWithNewCommandHistoryItem( - state, - - - {(commandService.getCommandUsage || builtinCommandService.getCommandUsage)( - commandDefinition - )} - - - ); + return updateStateWithNewCommandHistoryItem(state, { + id: uuidV4(), + command: cloneCommandDefinitionWithNewRenderComponent(command, HelpCommandArgument), + state: createCommandExecutionState(), + }); } // Command supports no arguments if (!commandDefinition.args || Object.keys(commandDefinition.args).length === 0) { - return updateStateWithNewCommandHistoryItem( - state, - - - {i18n.translate( - 'xpack.securitySolution.console.commandValidation.noArgumentsSupported', - { - defaultMessage: 'command does not support any arguments', - } - )} - - - ); + return updateStateWithNewCommandHistoryItem(state, { + id: uuidV4(), + command: cloneCommandDefinitionWithNewRenderComponent(command, BadArgument), + state: createCommandExecutionState({ + errorMessage: i18n.translate( + 'xpack.securitySolution.console.commandValidation.noArgumentsSupported', + { + defaultMessage: 'command does not support any arguments', + } + ), + }), + }); } // no unknown arguments allowed? if (parsedInput.unknownArgs && parsedInput.unknownArgs.length) { - return updateStateWithNewCommandHistoryItem( - state, - - - {i18n.translate('xpack.securitySolution.console.commandValidation.unknownArgument', { + return updateStateWithNewCommandHistoryItem(state, { + id: uuidV4(), + command: cloneCommandDefinitionWithNewRenderComponent(command, BadArgument), + state: createCommandExecutionState({ + errorMessage: i18n.translate( + 'xpack.securitySolution.console.commandValidation.unknownArgument', + { defaultMessage: 'unknown argument(s): {unknownArgs}', values: { unknownArgs: parsedInput.unknownArgs.join(', '), }, - })} - - - ); + } + ), + }), + }); } // Missing required Arguments for (const requiredArg of requiredArgs) { if (!parsedInput.args[requiredArg]) { - return updateStateWithNewCommandHistoryItem( - state, - - - {i18n.translate( - 'xpack.securitySolution.console.commandValidation.missingRequiredArg', - { - defaultMessage: 'missing required argument: {argName}', - values: { - argName: toCliArgumentOption(requiredArg), - }, - } - )} - - - ); + return updateStateWithNewCommandHistoryItem(state, { + id: uuidV4(), + command: cloneCommandDefinitionWithNewRenderComponent(command, BadArgument), + state: createCommandExecutionState({ + errorMessage: i18n.translate( + 'xpack.securitySolution.console.commandValidation.missingRequiredArg', + { + defaultMessage: 'missing required argument: {argName}', + values: { + argName: toCliArgumentOption(requiredArg), + }, + } + ), + }), + }); } } @@ -170,17 +182,19 @@ export const handleExecuteCommand: ConsoleStoreReducer< // Unknown argument if (!argDefinition) { - return updateStateWithNewCommandHistoryItem( - state, - - - {i18n.translate('xpack.securitySolution.console.commandValidation.unsupportedArg', { + return updateStateWithNewCommandHistoryItem(state, { + id: uuidV4(), + command: cloneCommandDefinitionWithNewRenderComponent(command, BadArgument), + state: createCommandExecutionState({ + errorMessage: i18n.translate( + 'xpack.securitySolution.console.commandValidation.unsupportedArg', + { defaultMessage: 'unsupported argument: {argName}', values: { argName: toCliArgumentOption(argName) }, - })} - - - ); + } + ), + }), + }); } // does not allow multiple values @@ -189,81 +203,76 @@ export const handleExecuteCommand: ConsoleStoreReducer< Array.isArray(argInput.values) && argInput.values.length > 0 ) { - return updateStateWithNewCommandHistoryItem( - state, - - - {i18n.translate( - 'xpack.securitySolution.console.commandValidation.argSupportedOnlyOnce', - { - defaultMessage: 'argument can only be used once: {argName}', - values: { argName: toCliArgumentOption(argName) }, - } - )} - - - ); + return updateStateWithNewCommandHistoryItem(state, { + id: uuidV4(), + command: cloneCommandDefinitionWithNewRenderComponent(command, BadArgument), + state: createCommandExecutionState({ + errorMessage: i18n.translate( + 'xpack.securitySolution.console.commandValidation.argSupportedOnlyOnce', + { + defaultMessage: 'argument can only be used once: {argName}', + values: { argName: toCliArgumentOption(argName) }, + } + ), + }), + }); } if (argDefinition.validate) { const validationResult = argDefinition.validate(argInput); if (validationResult !== true) { - return updateStateWithNewCommandHistoryItem( - state, - - - {i18n.translate( - 'xpack.securitySolution.console.commandValidation.invalidArgValue', - { - defaultMessage: 'invalid argument value: {argName}. {error}', - values: { argName: toCliArgumentOption(argName), error: validationResult }, - } - )} - - - ); + return updateStateWithNewCommandHistoryItem(state, { + id: uuidV4(), + command: cloneCommandDefinitionWithNewRenderComponent(command, BadArgument), + state: createCommandExecutionState({ + errorMessage: i18n.translate( + 'xpack.securitySolution.console.commandValidation.invalidArgValue', + { + defaultMessage: 'invalid argument value: {argName}. {error}', + values: { argName: toCliArgumentOption(argName), error: validationResult }, + } + ), + }), + }); } } } } else if (requiredArgs.length > 0) { - return updateStateWithNewCommandHistoryItem( - state, - - - {i18n.translate('xpack.securitySolution.console.commandValidation.mustHaveArgs', { + return updateStateWithNewCommandHistoryItem(state, { + id: uuidV4(), + command: cloneCommandDefinitionWithNewRenderComponent(command, BadArgument), + state: createCommandExecutionState({ + errorMessage: i18n.translate( + 'xpack.securitySolution.console.commandValidation.mustHaveArgs', + { defaultMessage: 'missing required arguments: {requiredArgs}', values: { requiredArgs: requiredArgs.map((argName) => toCliArgumentOption(argName)).join(', '), }, - })} - - - ); + } + ), + }), + }); } else if (commandDefinition.mustHaveArgs) { - return updateStateWithNewCommandHistoryItem( - state, - - - {i18n.translate('xpack.securitySolution.console.commandValidation.oneArgIsRequired', { + return updateStateWithNewCommandHistoryItem(state, { + id: uuidV4(), + command: cloneCommandDefinitionWithNewRenderComponent(command, BadArgument), + state: createCommandExecutionState({ + errorMessage: i18n.translate( + 'xpack.securitySolution.console.commandValidation.oneArgIsRequired', + { defaultMessage: 'at least one argument must be used', - })} - - - ); + } + ), + }), + }); } // All is good. Execute the command - return updateStateWithNewCommandHistoryItem( - state, - - - - ); + return updateStateWithNewCommandHistoryItem(state, { + id: uuidV4(), + command, + state: createCommandExecutionState(), + }); }; diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/console_state/state_update_handlers/handle_update_command_state.ts b/x-pack/plugins/security_solution/public/management/components/console/components/console_state/state_update_handlers/handle_update_command_state.ts new file mode 100644 index 0000000000000..8e176019990a3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/console/components/console_state/state_update_handlers/handle_update_command_state.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + CommandExecutionState, + CommandHistoryItem, + ConsoleDataAction, + ConsoleStoreReducer, +} from '../types'; + +type UpdateCommandStateAction = ConsoleDataAction & { + type: 'updateCommandStoreState' | 'updateCommandStatusState'; +}; + +export const handleUpdateCommandState: ConsoleStoreReducer = ( + state, + { type, payload: { id, value } } +) => { + let foundIt = false; + const updatedCommandHistory = state.commandHistory.map((item) => { + if (foundIt || item.id !== id) { + return item; + } + + foundIt = true; + + const updatedCommandState: CommandHistoryItem = { + ...item, + state: { + ...item.state, + }, + }; + + switch (type) { + case 'updateCommandStoreState': + updatedCommandState.state.store = value as CommandExecutionState['store']; + break; + case 'updateCommandStatusState': + // If the status was not changed, then there is nothing to be done here, so + // instead of triggering a state change (and UI re-render), just return the + // original item; + if (updatedCommandState.state.status === value) { + foundIt = false; + return item; + } + + updatedCommandState.state.status = value as CommandExecutionState['status']; + break; + } + + return updatedCommandState; + }); + + if (foundIt) { + return { + ...state, + commandHistory: updatedCommandHistory, + }; + } + + return state; +}; diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/console_state/types.ts b/x-pack/plugins/security_solution/public/management/components/console/components/console_state/types.ts index 72810d31e3248..356033e147c56 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/console_state/types.ts +++ b/x-pack/plugins/security_solution/public/management/components/console/components/console_state/types.ts @@ -5,28 +5,51 @@ * 2.0. */ -import { Dispatch, Reducer } from 'react'; -import { CommandServiceInterface } from '../../types'; -import { HistoryItemComponent } from '../history_item'; -import { BuiltinCommandServiceInterface } from '../../service/types.builtin_command_service'; +import type { Dispatch, Reducer } from 'react'; +import type { Command, CommandDefinition, CommandExecutionComponent } from '../../types'; export interface ConsoleDataState { - /** Command service defined on input to the `Console` component by consumers of the component */ - commandService: CommandServiceInterface; - /** Command service for builtin console commands */ - builtinCommandService: BuiltinCommandServiceInterface; + /** + * Commands available in the console, which includes both the builtin command and the ones + * defined on input to the `Console` component by consumers of the component + */ + commands: CommandDefinition[]; + /** UI function that scrolls the console down to the bottom */ scrollToBottom: () => void; + /** * List of commands entered by the user and being shown in the UI */ - commandHistory: Array>; + commandHistory: CommandHistoryItem[]; + /** Component defined on input to the Console that will handle the `help` command */ + HelpComponent?: CommandExecutionComponent; dataTestSubj?: string; } +export interface CommandHistoryItem { + id: string; + command: Command; + state: CommandExecutionState; +} + +export interface CommandExecutionState { + status: 'pending' | 'success' | 'error'; + store: Record; +} + export type ConsoleDataAction = | { type: 'scrollDown' } - | { type: 'executeCommand'; payload: { input: string } }; + | { type: 'executeCommand'; payload: { input: string } } + | { type: 'clear' } + | { + type: 'updateCommandStoreState'; + payload: { id: string; value: CommandExecutionState['store'] }; + } + | { + type: 'updateCommandStatusState'; + payload: { id: string; value: CommandExecutionState['status'] }; + }; export interface ConsoleStore { state: ConsoleDataState; diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/help_output.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/help_output.tsx index b0a2217e169c4..597d979e00034 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/help_output.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/help_output.tsx @@ -5,55 +5,30 @@ * 2.0. */ -import React, { memo, ReactNode, useEffect, useState } from 'react'; -import { EuiCallOut, EuiCallOutProps, EuiLoadingChart } from '@elastic/eui'; -import { UserCommandInput } from './user_command_input'; -import { CommandExecutionFailure } from './command_execution_failure'; +import React, { memo, PropsWithChildren, ReactNode } from 'react'; +import { EuiCallOut } from '@elastic/eui'; +import { MaybeImmutable } from '../../../../../common/endpoint/types'; +import { Command } from '..'; import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; import { useDataTestSubj } from '../hooks/state_selectors/use_data_test_subj'; -export interface HelpOutputProps extends Pick { - input: string; - children: ReactNode | Promise<{ result: ReactNode }>; -} -export const HelpOutput = memo(({ input, children, ...euiCalloutProps }) => { - const [content, setContent] = useState(); +type HelpOutputProps = PropsWithChildren<{ + command: MaybeImmutable; + title?: ReactNode; +}>; +export const HelpOutput = memo(({ title, children }) => { const getTestId = useTestIdGenerator(useDataTestSubj()); - useEffect(() => { - if (children instanceof Promise) { - (async () => { - try { - const response = await (children as Promise<{ - result: ReactNode; - }>); - setContent(response.result); - } catch (error) { - setContent(); - } - })(); - - return; - } - - setContent(children); - }, [children]); - return ( -
-
- -
- - {content} - -
+ + {children} + ); }); HelpOutput.displayName = 'HelpOutput'; diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/history_output.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/history_output.tsx index 088a6fac57ae4..cd03f9d39a39d 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/history_output.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/history_output.tsx @@ -5,12 +5,14 @@ * 2.0. */ -import React, { memo, useEffect } from 'react'; +import React, { memo, useEffect, useMemo } from 'react'; import { CommonProps, EuiFlexGroup } from '@elastic/eui'; +import { CommandExecutionOutput } from './command_execution_output'; import { useCommandHistory } from '../hooks/state_selectors/use_command_history'; import { useConsoleStateDispatch } from '../hooks/state_selectors/use_console_state_dispatch'; import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; import { useDataTestSubj } from '../hooks/state_selectors/use_data_test_subj'; +import { HistoryItem } from './history_item'; export type OutputHistoryProps = CommonProps; @@ -19,6 +21,16 @@ export const HistoryOutput = memo((commonProps) => { const dispatch = useConsoleStateDispatch(); const getTestId = useTestIdGenerator(useDataTestSubj()); + const historyBody = useMemo(() => { + return historyItems.map((historyItem) => { + return ( + + + + ); + }); + }, [historyItems]); + // Anytime we add a new item to the history // scroll down so that command input remains visible useEffect(() => { @@ -34,7 +46,7 @@ export const HistoryOutput = memo((commonProps) => { alignItems="flexEnd" responsive={false} > - {historyItems} + {historyBody} ); }); diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/unknow_comand.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/unknow_comand.tsx index 5529457cbb05a..8397c7727de81 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/unknow_comand.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/unknow_comand.tsx @@ -5,42 +5,38 @@ * 2.0. */ -import React, { memo } from 'react'; +import React, { memo, useEffect } from 'react'; import { EuiCallOut, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { UserCommandInput } from './user_command_input'; +import { CommandExecutionComponentProps } from '../types'; import { useDataTestSubj } from '../hooks/state_selectors/use_data_test_subj'; import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; -export interface UnknownCommand { - input: string; -} -export const UnknownCommand = memo(({ input }) => { +export const UnknownCommand = memo(({ setStatus }) => { const getTestId = useTestIdGenerator(useDataTestSubj()); + useEffect(() => { + setStatus('success'); + }, [setStatus]); + return ( - <> -
- -
- - - - - - {'help'}, - }} - /> - - - + + + + + + {'help'}, + }} + /> + + ); }); UnknownCommand.displayName = 'UnknownCommand'; diff --git a/x-pack/plugins/security_solution/public/management/components/console/console.tsx b/x-pack/plugins/security_solution/public/management/components/console/console.tsx index 0f3645037df02..874dbc2eabae0 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/console.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/console.tsx @@ -45,7 +45,7 @@ const ConsoleWindow = styled.div` `; export const Console = memo( - ({ prompt, commandService, managedKey, ...commonProps }) => { + ({ prompt, commands, HelpComponent, managedKey, ...commonProps }) => { const consoleWindowRef = useRef(null); const inputFocusRef: CommandInputProps['focusRef'] = useRef(null); const getTestId = useTestIdGenerator(commonProps['data-test-subj']); @@ -72,8 +72,9 @@ export const Console = memo( return ( {/* diff --git a/x-pack/plugins/security_solution/public/management/components/console/hooks/state_selectors/use_builtin_command_service.ts b/x-pack/plugins/security_solution/public/management/components/console/hooks/state_selectors/use_builtin_command_service.ts deleted file mode 100644 index 22167d5066743..0000000000000 --- a/x-pack/plugins/security_solution/public/management/components/console/hooks/state_selectors/use_builtin_command_service.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useConsoleStore } from '../../components/console_state/console_state'; -import { CommandServiceInterface } from '../../types'; - -export const useCommandService = (): CommandServiceInterface => { - return useConsoleStore().state.builtinCommandService; -}; diff --git a/x-pack/plugins/security_solution/public/management/components/console/hooks/state_selectors/use_command_service.ts b/x-pack/plugins/security_solution/public/management/components/console/hooks/state_selectors/use_command_service.ts deleted file mode 100644 index 66ce0c2b5eb43..0000000000000 --- a/x-pack/plugins/security_solution/public/management/components/console/hooks/state_selectors/use_command_service.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useConsoleStore } from '../../components/console_state/console_state'; -import { CommandServiceInterface } from '../../types'; - -export const useCommandService = (): CommandServiceInterface => { - return useConsoleStore().state.commandService; -}; diff --git a/x-pack/plugins/security_solution/public/management/components/console/hooks/state_selectors/use_with_command_list.ts b/x-pack/plugins/security_solution/public/management/components/console/hooks/state_selectors/use_with_command_list.ts new file mode 100644 index 0000000000000..4f63c55a36098 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/console/hooks/state_selectors/use_with_command_list.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 { useConsoleStore } from '../../components/console_state/console_state'; +import type { CommandDefinition } from '../../types'; + +/** + * Returns the Command service that the console was provided on input + */ +export const useWithCommandList = (): CommandDefinition[] => { + return useConsoleStore().state.commands; +}; diff --git a/x-pack/plugins/security_solution/public/management/components/console/hooks/state_selectors/use_with_custom_help_component.ts b/x-pack/plugins/security_solution/public/management/components/console/hooks/state_selectors/use_with_custom_help_component.ts new file mode 100644 index 0000000000000..b90e5166c81d7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/console/hooks/state_selectors/use_with_custom_help_component.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useConsoleStore } from '../../components/console_state/console_state'; +import { ConsoleDataState } from '../../components/console_state/types'; + +export const useWithCustomHelpComponent = (): ConsoleDataState['HelpComponent'] => { + return useConsoleStore().state.HelpComponent; +}; diff --git a/x-pack/plugins/security_solution/public/management/components/console/index.ts b/x-pack/plugins/security_solution/public/management/components/console/index.ts index 4264aa5a8f830..1603d4b15f353 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/index.ts +++ b/x-pack/plugins/security_solution/public/management/components/console/index.ts @@ -7,7 +7,7 @@ export { Console } from './console'; export { ConsoleManager, useConsoleManager } from './components/console_manager'; -export type { CommandServiceInterface, CommandDefinition, Command, ConsoleProps } from './types'; +export type { CommandDefinition, Command, ConsoleProps } from './types'; export type { ConsoleRegistrationInterface, RegisteredConsoleClient, diff --git a/x-pack/plugins/security_solution/public/management/components/console/mocks.tsx b/x-pack/plugins/security_solution/public/management/components/console/mocks.tsx index d89c5f5374d47..ea24a174498dd 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/mocks.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/mocks.tsx @@ -7,20 +7,19 @@ /* eslint-disable import/no-extraneous-dependencies */ -import React from 'react'; +import React, { useEffect } from 'react'; import { EuiCode } from '@elastic/eui'; import userEvent from '@testing-library/user-event'; import { act } from '@testing-library/react'; import { Console } from './console'; -import type { Command, CommandServiceInterface, ConsoleProps } from './types'; +import type { ConsoleProps, CommandDefinition, CommandExecutionComponent } from './types'; import type { AppContextTestRender } from '../../../common/mock/endpoint'; import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; -import { CommandDefinition } from './types'; export interface ConsoleTestSetup { renderConsole(props?: Partial): ReturnType; - commandServiceMock: jest.Mocked; + commands: CommandDefinition[]; enterCommand( cmd: string, @@ -74,25 +73,16 @@ export const getConsoleTestSetup = (): ConsoleTestSetup => { let renderResult: ReturnType; - const commandServiceMock = getCommandServiceMock(); + const commandList = getCommandListMock(); const renderConsole: ConsoleTestSetup['renderConsole'] = ({ prompt = '$$>', - commandService = commandServiceMock, + commands = commandList, 'data-test-subj': dataTestSubj = 'test', ...others } = {}) => { - if (commandService !== commandServiceMock) { - throw new Error('Must use CommandService provided by test setup'); - } - return (renderResult = mockedContext.render( - + )); }; @@ -102,91 +92,107 @@ export const getConsoleTestSetup = (): ConsoleTestSetup => { return { renderConsole, - commandServiceMock, + commands: commandList, enterCommand, }; }; -export const getCommandServiceMock = (): jest.Mocked => { - return { - getCommandList: jest.fn(() => { - const commands: CommandDefinition[] = [ - { - name: 'cmd1', - about: 'a command with no options', - }, - { - name: 'cmd2', - about: 'runs cmd 2', - args: { - file: { - about: 'Includes file in the run', - required: true, - allowMultiples: false, - validate: () => { - return true; - }, - }, - ext: { - about: 'optional argument', - required: false, - allowMultiples: false, - }, - bad: { - about: 'will fail validation', - required: false, - allowMultiples: false, - validate: () => 'This is a bad value', - }, +export const getCommandListMock = (): CommandDefinition[] => { + const RenderComponent: CommandExecutionComponent = ({ + command, + status, + setStatus, + setStore, + store, + }) => { + useEffect(() => { + if (status !== 'success') { + new Promise((r) => setTimeout(r, 500)).then(() => { + setStatus('success'); + setStore({ foo: 'bar' }); + }); + } + }, [setStatus, setStore, status]); + + return ( +
+
{`${command.commandDefinition.name}`}
+
{`command input: ${command.input}`}
+ + {JSON.stringify(command.args, null, 2)} + +
{'Command render state:'}
+
{`status: ${status}`}
+ + {JSON.stringify(store, null, 2)} + +
+ ); + }; + + const commands: CommandDefinition[] = [ + { + name: 'cmd1', + about: 'a command with no options', + RenderComponent: jest.fn(RenderComponent), + }, + { + name: 'cmd2', + about: 'runs cmd 2', + RenderComponent: jest.fn(RenderComponent), + args: { + file: { + about: 'Includes file in the run', + required: true, + allowMultiples: false, + validate: () => { + return true; }, }, - { - name: 'cmd3', - about: 'allows argument to be used multiple times', - args: { - foo: { - about: 'foo stuff', - required: true, - allowMultiples: true, - }, - }, + ext: { + about: 'optional argument', + required: false, + allowMultiples: false, }, - { - name: 'cmd4', - about: 'all options optinal, but at least one is required', - mustHaveArgs: true, - args: { - foo: { - about: 'foo stuff', - required: false, - allowMultiples: true, - }, - bar: { - about: 'bar stuff', - required: false, - allowMultiples: true, - }, - }, + bad: { + about: 'will fail validation', + required: false, + allowMultiples: false, + validate: () => 'This is a bad value', }, - ]; - - return commands; - }), - - executeCommand: jest.fn(async (command: Command) => { - await new Promise((r) => setTimeout(r, 1)); - - return { - result: ( -
-
{`${command.commandDefinition.name}`}
-
{`command input: ${command.input}`}
- - {JSON.stringify(command.args, null, 2)} - -
- ), - }; - }), - }; + }, + }, + { + name: 'cmd3', + about: 'allows argument to be used multiple times', + RenderComponent: jest.fn(RenderComponent), + args: { + foo: { + about: 'foo stuff', + required: true, + allowMultiples: true, + }, + }, + }, + { + name: 'cmd4', + about: 'all options optional, but at least one is required', + RenderComponent: jest.fn(RenderComponent), + mustHaveArgs: true, + args: { + foo: { + about: 'foo stuff', + required: false, + allowMultiples: true, + }, + bar: { + about: 'bar stuff', + required: false, + allowMultiples: true, + }, + }, + }, + ]; + + return commands; }; diff --git a/x-pack/plugins/security_solution/public/management/components/console/service/builtin_command_service.tsx b/x-pack/plugins/security_solution/public/management/components/console/service/builtin_command_service.tsx deleted file mode 100644 index 6cd8af0dc6eff..0000000000000 --- a/x-pack/plugins/security_solution/public/management/components/console/service/builtin_command_service.tsx +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { ReactNode } from 'react'; -import { i18n } from '@kbn/i18n'; -import { HistoryItem, HistoryItemComponent } from '../components/history_item'; -import { HelpOutput } from '../components/help_output'; -import { ParsedCommandInput } from './parsed_command_input'; -import { CommandList } from '../components/command_list'; -import { CommandUsage } from '../components/command_usage'; -import { Command, CommandDefinition, CommandServiceInterface } from '../types'; -import { BuiltinCommandServiceInterface } from './types.builtin_command_service'; - -const builtInCommands = (): CommandDefinition[] => { - return [ - { - name: 'help', - about: i18n.translate('xpack.securitySolution.console.builtInCommands.helpAbout', { - defaultMessage: 'View list of available commands', - }), - }, - { - name: 'clear', - about: i18n.translate('xpack.securitySolution.console.builtInCommands.clearAbout', { - defaultMessage: 'Clear the console buffer', - }), - }, - ]; -}; - -export class ConsoleBuiltinCommandsService implements BuiltinCommandServiceInterface { - constructor(private commandList = builtInCommands()) {} - - getCommandList(): CommandDefinition[] { - return this.commandList; - } - - async executeCommand(command: Command): Promise<{ result: ReactNode }> { - return { - result: null, - }; - } - - executeBuiltinCommand( - parsedInput: ParsedCommandInput, - contextConsoleService: CommandServiceInterface - ): { result: ReturnType | null; clearBuffer?: boolean } { - switch (parsedInput.name) { - case 'help': - return { - result: ( - - - {this.getHelpContent(parsedInput, contextConsoleService)} - - - ), - }; - - case 'clear': - return { - result: null, - clearBuffer: true, - }; - } - - return { result: null }; - } - - async getHelpContent( - parsedInput: ParsedCommandInput, - commandService: CommandServiceInterface - ): Promise<{ result: ReactNode }> { - let helpOutput: ReactNode; - - if (commandService.getHelp) { - helpOutput = (await commandService.getHelp()).result; - } else { - helpOutput = ( - - ); - } - - return { - result: helpOutput, - }; - } - - isBuiltin(name: string): boolean { - return !!this.commandList.find((command) => command.name === name); - } - - async getCommandUsage(command: CommandDefinition): Promise<{ result: ReactNode }> { - return { - result: , - }; - } -} diff --git a/x-pack/plugins/security_solution/public/management/components/console/service/builtin_commands.tsx b/x-pack/plugins/security_solution/public/management/components/console/service/builtin_commands.tsx new file mode 100644 index 0000000000000..5869e3b4472cb --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/console/service/builtin_commands.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { ClearCommand } from '../components/builtin_commands/clear_command'; +import { HelpCommand } from '../components/builtin_commands/help_command'; +import { CommandDefinition } from '../types'; + +export const getBuiltinCommands = (): CommandDefinition[] => { + return [ + { + name: 'help', + about: i18n.translate('xpack.securitySolution.console.builtInCommands.helpAbout', { + defaultMessage: 'View list of available commands', + }), + RenderComponent: HelpCommand, + }, + { + name: 'clear', + about: i18n.translate('xpack.securitySolution.console.builtInCommands.clearAbout', { + defaultMessage: 'Clear the console buffer', + }), + RenderComponent: ClearCommand, + }, + ]; +}; diff --git a/x-pack/plugins/security_solution/public/management/components/console/service/types.builtin_command_service.ts b/x-pack/plugins/security_solution/public/management/components/console/service/types.builtin_command_service.ts deleted file mode 100644 index dbd5347ea99c2..0000000000000 --- a/x-pack/plugins/security_solution/public/management/components/console/service/types.builtin_command_service.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 { ReactNode } from 'react'; -import { CommandDefinition, CommandServiceInterface } from '../types'; -import { ParsedCommandInput } from './parsed_command_input'; -import { HistoryItemComponent } from '../components/history_item'; - -export interface BuiltinCommandServiceInterface extends CommandServiceInterface { - executeBuiltinCommand( - parsedInput: ParsedCommandInput, - contextConsoleService: CommandServiceInterface - ): { result: ReturnType | null; clearBuffer?: boolean }; - - getHelpContent( - parsedInput: ParsedCommandInput, - commandService: CommandServiceInterface - ): Promise<{ result: ReactNode }>; - - isBuiltin(name: string): boolean; - - getCommandUsage(command: CommandDefinition): Promise<{ result: ReactNode }>; -} diff --git a/x-pack/plugins/security_solution/public/management/components/console/types.ts b/x-pack/plugins/security_solution/public/management/components/console/types.ts index 6b15f03988313..fec4b2722cc92 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/types.ts +++ b/x-pack/plugins/security_solution/public/management/components/console/types.ts @@ -5,16 +5,34 @@ * 2.0. */ -import { ReactNode } from 'react'; -import { CommonProps } from '@elastic/eui'; -import { ParsedArgData, ParsedCommandInput } from './service/parsed_command_input'; +import type { ComponentType, ComponentProps } from 'react'; +import type { CommonProps } from '@elastic/eui'; +import type { CommandExecutionState } from './components/console_state/types'; +import type { Immutable } from '../../../../common/endpoint/types'; +import type { ParsedArgData, ParsedCommandInput } from './service/parsed_command_input'; export interface CommandDefinition { name: string; about: string; - validator?: () => Promise; + /** + * The Component that will be used to render the Command + */ + RenderComponent: CommandExecutionComponent; + /** + * If defined, this command's use of `--help` will be displayed using this component instead of + * the console's built in output. + */ + HelpComponent?: CommandExecutionComponent; + /** + * A store for any data needed when the command is executed. + * The entire `CommandDefinition` is passed along to the component + * that will handle it, so this data will be available there + */ + meta?: Record; + /** If all args are optional, but at least one must be defined, set to true */ mustHaveArgs?: boolean; + /** The list of arguments supported by this command */ args?: { [longName: string]: { required: boolean; @@ -28,7 +46,7 @@ export interface CommandDefinition { // Selector: Idea is that the schema can plugin in a rich component for the // user to select something (ex. a file) // FIXME: implement selector - selector?: () => unknown; + selector?: ComponentType; }; }; } @@ -46,26 +64,44 @@ export interface Command { commandDefinition: CommandDefinition; } -export interface CommandServiceInterface { - getCommandList(): CommandDefinition[]; - - executeCommand(command: Command): Promise<{ result: ReactNode }>; - +/** + * The component that will handle the Command execution and display the result. + */ +export type CommandExecutionComponent = ComponentType<{ + command: Command; /** - * If defined, then the `help` builtin command will display this output instead of the default one - * which is generated out of the Command list + * A data store for the command execution to store data in, if needed. + * Because the Console could be closed/opened several times, which will cause this component + * to be `mounted`/`unmounted` several times, this data store will be beneficial for + * persisting data (ex. API response with IDs) that the command can use to determine + * if the command has already been executed or if it's a new instance. */ - getHelp?: () => Promise<{ result: ReactNode }>; - + store: Immutable; + /** Sets the `store` data above */ + setStore: (state: CommandExecutionState['store']) => void; /** - * If defined, then the output of this function will be used to display individual - * command help (`--help`) + * The status of the command execution. + * Note that the console's UI will show the command as "busy" while the status here is + * `pending`. Ensure that once the action processing completes, that this is set to + * either `success` or `error`. */ - getCommandUsage?: (command: CommandDefinition) => Promise<{ result: ReactNode }>; -} + status: CommandExecutionState['status']; + /** Set the status of the command execution */ + setStatus: (status: CommandExecutionState['status']) => void; +}>; + +export type CommandExecutionComponentProps = ComponentProps; export interface ConsoleProps extends CommonProps { - commandService: CommandServiceInterface; + /** + * The list of Commands that will be available in the console for the user to execute + */ + commands: CommandDefinition[]; + /** + * If defined, then the `help` builtin command will display this output instead of the default one + * which is generated out of the Command list. + */ + HelpComponent?: CommandExecutionComponent; prompt?: string; /** * For internal use only! diff --git a/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_with_router_support.tsx b/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_with_router_support.tsx index 85013b82fb2db..a78375d4cbf49 100644 --- a/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_with_router_support.tsx +++ b/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_with_router_support.tsx @@ -153,11 +153,7 @@ export const ContextMenuWithRouterSupport = memo {title ? {title} : null} - + ); } diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_console/endpoint_console.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_console/endpoint_console.tsx deleted file mode 100644 index 28472e123380a..0000000000000 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_console/endpoint_console.tsx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { memo, useMemo } from 'react'; -import { Console } from '../console'; -import { EndpointConsoleCommandService } from './endpoint_console_command_service'; -import type { HostMetadata } from '../../../../common/endpoint/types'; - -export interface EndpointConsoleProps { - endpoint: HostMetadata; -} - -export const EndpointConsole = memo((props) => { - const consoleService = useMemo(() => { - return new EndpointConsoleCommandService(); - }, []); - - return `} commandService={consoleService} />; -}); - -EndpointConsole.displayName = 'EndpointConsole'; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_console/endpoint_console_command_service.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_console/endpoint_console_command_service.tsx deleted file mode 100644 index 5028879bc1a49..0000000000000 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_console/endpoint_console_command_service.tsx +++ /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 React, { ReactNode } from 'react'; -import { CommandServiceInterface, CommandDefinition, Command } from '../console'; - -/** - * Endpoint specific Response Actions (commands) for use with Console. - */ -export class EndpointConsoleCommandService implements CommandServiceInterface { - getCommandList(): CommandDefinition[] { - return []; - } - - async executeCommand(command: Command): Promise<{ result: ReactNode }> { - return { result: <> }; - } -} diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_console/index.ts b/x-pack/plugins/security_solution/public/management/components/endpoint_console/index.ts deleted file mode 100644 index 97f7fb61ae607..0000000000000 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_console/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor 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 { EndpointConsole } from './endpoint_console'; diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_delete_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_delete_artifact.tsx index 994ed4f466d00..f534cc01e62ee 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_delete_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_delete_artifact.tsx @@ -7,14 +7,19 @@ import pMap from 'p-map'; import { HttpFetchError } from '@kbn/core/public'; import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { useMutation, UseMutationResult, UseQueryOptions } from 'react-query'; +import { useMutation, UseMutationOptions, UseMutationResult } from 'react-query'; import { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; const DEFAULT_OPTIONS = Object.freeze({}); export function useBulkDeleteArtifact( exceptionListApiClient: ExceptionsListApiClient, - customOptions: UseQueryOptions = DEFAULT_OPTIONS, + customOptions: UseMutationOptions< + ExceptionListItemSchema[], + HttpFetchError, + Array<{ itemId?: string; id?: string }>, + () => void + > = DEFAULT_OPTIONS, options: { concurrency: number; } = { diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_update_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_update_artifact.tsx index 3389b3563577a..68090e2aabc90 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_update_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_update_artifact.tsx @@ -10,14 +10,19 @@ import { UpdateExceptionListItemSchema, ExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; -import { useMutation, UseMutationResult, UseQueryOptions } from 'react-query'; +import { useMutation, UseMutationOptions, UseMutationResult } from 'react-query'; import { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; const DEFAULT_OPTIONS = Object.freeze({}); export function useBulkUpdateArtifact( exceptionListApiClient: ExceptionsListApiClient, - customOptions: UseQueryOptions = DEFAULT_OPTIONS, + customOptions: UseMutationOptions< + ExceptionListItemSchema[], + HttpFetchError, + UpdateExceptionListItemSchema[], + () => void + > = DEFAULT_OPTIONS, options: { concurrency: number; } = { diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_create_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_create_artifact.tsx index c1aed4be8005b..79dd59d744e7f 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_create_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_create_artifact.tsx @@ -9,14 +9,19 @@ import { ExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; import { HttpFetchError } from '@kbn/core/public'; -import { useMutation, UseMutationResult, UseQueryOptions } from 'react-query'; +import { useMutation, UseMutationOptions, UseMutationResult } from 'react-query'; import { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; const DEFAULT_OPTIONS = Object.freeze({}); export function useCreateArtifact( exceptionListApiClient: ExceptionsListApiClient, - customOptions: UseQueryOptions = DEFAULT_OPTIONS + customOptions: UseMutationOptions< + ExceptionListItemSchema, + HttpFetchError, + CreateExceptionListItemSchema, + () => void + > = DEFAULT_OPTIONS ): UseMutationResult< ExceptionListItemSchema, HttpFetchError, diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_delete_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_delete_artifact.tsx index 16568a0766444..a12b3aaa9ba40 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_delete_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_delete_artifact.tsx @@ -6,14 +6,19 @@ */ import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { HttpFetchError } from '@kbn/core/public'; -import { useMutation, UseMutationResult, UseQueryOptions } from 'react-query'; +import { useMutation, UseMutationOptions, UseMutationResult } from 'react-query'; import { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; const DEFAULT_OPTIONS = Object.freeze({}); export function useDeleteArtifact( exceptionListApiClient: ExceptionsListApiClient, - customOptions: UseQueryOptions = DEFAULT_OPTIONS + customOptions: UseMutationOptions< + ExceptionListItemSchema, + HttpFetchError, + { itemId?: string; id?: string }, + () => void + > = DEFAULT_OPTIONS ): UseMutationResult< ExceptionListItemSchema, HttpFetchError, diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_get_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_get_artifact.tsx index eb80a689e5a0d..36ae203707c28 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_get_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_get_artifact.tsx @@ -9,24 +9,17 @@ import { HttpFetchError } from '@kbn/core/public'; import { QueryObserverResult, useQuery, UseQueryOptions } from 'react-query'; import { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; -const DEFAULT_OPTIONS = Object.freeze({}); - export function useGetArtifact( exceptionListApiClient: ExceptionsListApiClient, itemId?: string, id?: string, - customQueryOptions: UseQueryOptions = DEFAULT_OPTIONS + customQueryOptions?: UseQueryOptions ): QueryObserverResult { return useQuery( ['get', exceptionListApiClient, itemId, id], () => { return exceptionListApiClient.get(itemId, id); }, - { - refetchIntervalInBackground: false, - refetchOnWindowFocus: false, - refetchOnMount: true, - ...customQueryOptions, - } + customQueryOptions ); } diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_list_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_list_artifact.tsx index 68bee6de0113a..64a5b908e2d9e 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_list_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_list_artifact.tsx @@ -31,9 +31,7 @@ export function useListArtifact( excludedPolicies: string[]; }> = DEFAULT_OPTIONS, searchableFields: MaybeImmutable = DEFAULT_EXCEPTION_LIST_ITEM_SEARCHABLE_FIELDS, - customQueryOptions: Partial< - UseQueryOptions - > = DEFAULT_OPTIONS, + customQueryOptions?: Partial>, customQueryIds: string[] = [] ): QueryObserverResult { const { @@ -64,12 +62,6 @@ export function useListArtifact( return result; }, - { - refetchIntervalInBackground: false, - refetchOnWindowFocus: false, - refetchOnMount: true, - keepPreviousData: true, - ...customQueryOptions, - } + customQueryOptions ); } diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_summary_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_summary_artifact.tsx index 62e5372dc39aa..111fdb4565785 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_summary_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_summary_artifact.tsx @@ -21,9 +21,7 @@ export function useSummaryArtifact( policies: string[]; }> = DEFAULT_OPTIONS, searchableFields: MaybeImmutable = DEFAULT_EXCEPTION_LIST_ITEM_SEARCHABLE_FIELDS, - customQueryOptions: Partial< - UseQueryOptions - > = DEFAULT_OPTIONS + customQueryOptions: Partial> ): QueryObserverResult { const { filter = '', policies = [] } = options; @@ -37,12 +35,6 @@ export function useSummaryArtifact( }) ); }, - { - refetchIntervalInBackground: false, - refetchOnWindowFocus: false, - refetchOnMount: true, - keepPreviousData: true, - ...customQueryOptions, - } + customQueryOptions ); } diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_update_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_update_artifact.tsx index 92da096f71e66..e3a7e37eebf56 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_update_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_update_artifact.tsx @@ -9,14 +9,19 @@ import { ExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; import { HttpFetchError } from '@kbn/core/public'; -import { useMutation, UseMutationResult, UseQueryOptions } from 'react-query'; +import { useMutation, UseMutationOptions, UseMutationResult } from 'react-query'; import { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; const DEFAULT_OPTIONS = Object.freeze({}); export function useUpdateArtifact( exceptionListApiClient: ExceptionsListApiClient, - customQueryOptions: UseQueryOptions = DEFAULT_OPTIONS + customQueryOptions: UseMutationOptions< + ExceptionListItemSchema, + HttpFetchError, + UpdateExceptionListItemSchema, + () => void + > = DEFAULT_OPTIONS ): UseMutationResult< ExceptionListItemSchema, HttpFetchError, diff --git a/x-pack/plugins/security_solution/public/management/links.ts b/x-pack/plugins/security_solution/public/management/links.ts new file mode 100644 index 0000000000000..d941d538c80f7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/links.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 { i18n } from '@kbn/i18n'; +import { + BLOCKLIST_PATH, + ENDPOINTS_PATH, + EVENT_FILTERS_PATH, + EXCEPTIONS_PATH, + HOST_ISOLATION_EXCEPTIONS_PATH, + MANAGEMENT_PATH, + POLICIES_PATH, + RULES_PATH, + SecurityPageName, + TRUSTED_APPS_PATH, +} from '../../common/constants'; +import { + BLOCKLIST, + ENDPOINTS, + EVENT_FILTERS, + EXCEPTIONS, + HOST_ISOLATION_EXCEPTIONS, + MANAGE, + POLICIES, + RULES, + TRUSTED_APPLICATIONS, +} from '../app/translations'; +import { FEATURE, LinkItem } from '../common/links/types'; + +export const links: LinkItem = { + id: SecurityPageName.administration, + title: MANAGE, + path: MANAGEMENT_PATH, + skipUrlState: true, + globalNavEnabled: false, + features: [FEATURE.general], + globalSearchKeywords: [ + i18n.translate('xpack.securitySolution.appLinks.manage', { + defaultMessage: 'Manage', + }), + ], + links: [ + { + id: SecurityPageName.rules, + title: RULES, + path: RULES_PATH, + globalNavEnabled: false, + globalSearchKeywords: [ + i18n.translate('xpack.securitySolution.appLinks.rules', { + defaultMessage: 'Rules', + }), + ], + globalSearchEnabled: true, + }, + { + id: SecurityPageName.exceptions, + title: EXCEPTIONS, + path: EXCEPTIONS_PATH, + globalNavEnabled: false, + globalSearchKeywords: [ + i18n.translate('xpack.securitySolution.appLinks.exceptions', { + defaultMessage: 'Exception lists', + }), + ], + globalSearchEnabled: true, + }, + { + id: SecurityPageName.endpoints, + globalNavEnabled: true, + title: ENDPOINTS, + globalNavOrder: 9006, + path: ENDPOINTS_PATH, + skipUrlState: true, + }, + { + id: SecurityPageName.policies, + title: POLICIES, + path: POLICIES_PATH, + skipUrlState: true, + experimentalKey: 'policyListEnabled', + }, + { + id: SecurityPageName.trustedApps, + title: TRUSTED_APPLICATIONS, + path: TRUSTED_APPS_PATH, + skipUrlState: true, + }, + { + id: SecurityPageName.eventFilters, + title: EVENT_FILTERS, + path: EVENT_FILTERS_PATH, + skipUrlState: true, + }, + { + id: SecurityPageName.hostIsolationExceptions, + title: HOST_ISOLATION_EXCEPTIONS, + path: HOST_ISOLATION_EXCEPTIONS_PATH, + skipUrlState: true, + }, + { + id: SecurityPageName.blocklist, + title: BLOCKLIST, + path: BLOCKLIST_PATH, + skipUrlState: true, + }, + ], +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/blocklist/view/blocklist.tsx b/x-pack/plugins/security_solution/public/management/pages/blocklist/view/blocklist.tsx index a48d770460ec9..759cc37ed902b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/blocklist/view/blocklist.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/blocklist/view/blocklist.tsx @@ -71,7 +71,7 @@ const BLOCKLIST_PAGE_LABELS: ArtifactListPageProps['labels'] = { @@ -87,7 +87,7 @@ const BLOCKLIST_PAGE_LABELS: ArtifactListPageProps['labels'] = { }), emptyStateInfo: i18n.translate('xpack.securitySolution.blocklist.emptyStateInfo', { defaultMessage: - 'The blocklist prevents selected applications from running on your hosts by extending the list of processes the Endpoint considers malicious.', + 'The blocklist prevents specified applications from running on your hosts, extending the list of processes that Endpoint Security considers malicious.', }), emptyStatePrimaryButtonLabel: i18n.translate( 'xpack.securitySolution.blocklist.emptyStatePrimaryButtonLabel', diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts index 57cdaca1f2147..d8dc87885a1fa 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts @@ -424,16 +424,16 @@ describe('endpoint list middleware', () => { path: expect.any(String), query: { agent_ids: [ - '6db499e5-4927-4350-abb8-d8318e7d0eec', - 'c082dda9-1847-4997-8eda-f1192d95bec3', - '8aa1cd61-cc25-4783-afb5-0eefc4919c07', - '47fe24c1-7370-419a-9732-3ff38bf41272', - '0d2b2fa7-a9cd-49fc-ad5f-0252c642290e', - 'f480092d-0445-4bf3-9c96-8a3d5cb97824', - '3850e676-0940-4c4b-aaca-571bd1bc66d9', - '46efcc7a-086a-47a3-8f09-c4ecd6d2d917', - 'afa55826-b81b-4440-a2ac-0644d77a3fc6', - '25b49e50-cb5c-43df-824f-67b8cf697d9d', + '0dc3661d-6e67-46b0-af39-6f12b025fcb0', + 'a8e32a61-2685-47f0-83eb-edf157b8e616', + '37e219a8-fe16-4da9-bf34-634c5824b484', + '2484eb13-967e-4491-bf83-dffefdfe607c', + '0bc08ef6-6d6a-4113-92f2-b97811187c63', + 'f4127d87-b567-4a6e-afa6-9a1c7dc95f01', + 'f9ab5b8c-a43e-4e80-99d6-11570845a697', + '406c4b6a-ca57-4bd1-bc66-d9d999df3e70', + '2da1dd51-f7af-4f0e-b64c-e7751c74b0e7', + '89a94ea4-073c-4cb6-90a2-500805837027', ], }, }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx index 889c5005ec193..24da8b3b86a35 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx @@ -8,7 +8,6 @@ import React, { memo, useCallback, useMemo } from 'react'; import { useHistory } from 'react-router-dom'; import { encode, RisonValue } from 'rison-node'; -import styled from 'styled-components'; import type { Query } from '@kbn/es-query'; import { TimeHistory } from '@kbn/data-plugin/public'; import { DataView } from '@kbn/data-views-plugin/public'; @@ -19,12 +18,6 @@ import { useEndpointSelector } from '../hooks'; import * as selectors from '../../store/selectors'; import { clone } from '../../models/index_pattern'; -const AdminQueryBar = styled.div` - .globalQueryBar { - padding: 0; - } -`; - export const AdminSearchBar = memo(() => { const history = useHistory(); const { admin_query: _, ...queryParams } = useEndpointSelector(selectors.uiQueryParams); @@ -57,7 +50,7 @@ export const AdminSearchBar = memo(() => { return (
{searchBarIndexPatterns && searchBarIndexPatterns.length > 0 && ( - +
{ showQueryBar={true} showQueryInput={true} /> - +
)}
); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/dev_console.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/dev_console.tsx index 6761a32c6fb65..46b2a96faa9c9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/dev_console.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/dev_console.tsx @@ -5,7 +5,15 @@ * 2.0. */ -import React, { memo, useCallback, useMemo } from 'react'; +import React, { + memo, + ReactElement, + ReactNode, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; import { EuiButton, EuiCode, @@ -15,12 +23,11 @@ import { EuiSpacer, EuiText, } from '@elastic/eui'; +import { useIsMounted } from '../../../components/hooks/use_is_mounted'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { useUrlParams } from '../../../components/hooks/use_url_params'; import { - Command, CommandDefinition, - CommandServiceInterface, Console, RegisteredConsoleClient, useConsoleManager, @@ -28,69 +35,138 @@ import { const delay = async (ms: number = 4000) => new Promise((r) => setTimeout(r, ms)); -class DevCommandService implements CommandServiceInterface { - getCommandList(): CommandDefinition[] { - return [ - { - name: 'cmd1', - about: 'Runs cmd1', - }, - { - name: 'get-file', - about: 'retrieve a file from the endpoint', - args: { - file: { - required: true, - allowMultiples: false, - about: 'the file path for the file to be retrieved', - }, - }, +const getCommandList = (): CommandDefinition[] => { + return [ + { + name: 'cmd1', + about: 'Runs cmd1', + RenderComponent: ({ command, setStatus, store, setStore }) => { + const isMounted = useIsMounted(); + + const [apiResponse, setApiResponse] = useState(null); + const [uiResponse, setUiResponse] = useState(null); + + // Emulate a real action where: + // 1. an api request is done to create the action + // 2. wait for a response + // 3. account for component mount/unmount and prevent duplicate api calls + + useEffect(() => { + (async () => { + // Emulate an api call + if (!store.apiInflight) { + setStore({ + ...store, + apiInflight: true, + }); + + window.console.warn(`${Math.random()} ------> cmd1: doing async work`); + + await delay(6000); + setApiResponse(`API was called at: ${new Date().toLocaleString()}`); + } + })(); + }, [setStore, store]); + + useEffect(() => { + (async () => { + const doUiResponse = () => { + setUiResponse( + + {`${command.commandDefinition.name}`} + {`command input: ${command.input}`} + {'Arguments provided:'} + {JSON.stringify(command.args, null, 2)} + + ); + }; + + if (store.apiResponse) { + doUiResponse(); + } else { + await delay(); + doUiResponse(); + } + })(); + }, [ + command.args, + command.commandDefinition.name, + command.input, + isMounted, + store.apiResponse, + ]); + + useEffect(() => { + if (apiResponse && uiResponse) { + setStatus('success'); + } + }, [apiResponse, setStatus, uiResponse]); + + useEffect(() => { + if (apiResponse && store.apiResponse !== apiResponse) { + setStore({ + ...store, + apiResponse, + }); + } + }, [apiResponse, setStore, store]); + + if (store.apiResponse) { + return ( +
+ {uiResponse} + {store.apiResponse as ReactNode} +
+ ); + } + + return null; }, - { - name: 'cmd2', - about: 'runs cmd 2', - args: { - file: { - required: true, - allowMultiples: false, - about: 'Includes file in the run', - validate: () => { - return true; - }, - }, - bad: { - required: false, - allowMultiples: false, - about: 'will fail validation', - validate: () => 'This is a bad value', - }, + args: { + one: { + required: false, + allowMultiples: false, + about: 'just one', }, }, - { - name: 'cmd-long-delay', - about: 'runs cmd 2', - }, - ]; - } - - async executeCommand(command: Command): Promise<{ result: React.ReactNode }> { - await delay(); - - if (command.commandDefinition.name === 'cmd-long-delay') { - await delay(20000); - } - - return { - result: ( -
-
{`${command.commandDefinition.name}`}
-
{`command input: ${command.input}`}
- {JSON.stringify(command.args, null, 2)} -
- ), - }; - } -} + }, + // { + // name: 'get-file', + // about: 'retrieve a file from the endpoint', + // args: { + // file: { + // required: true, + // allowMultiples: false, + // about: 'the file path for the file to be retrieved', + // }, + // }, + // }, + // { + // name: 'cmd2', + // about: 'runs cmd 2', + // args: { + // file: { + // required: true, + // allowMultiples: false, + // about: 'Includes file in the run', + // validate: () => { + // return true; + // }, + // }, + // bad: { + // required: false, + // allowMultiples: false, + // about: 'will fail validation', + // validate: () => 'This is a bad value', + // }, + // }, + // }, + // { + // name: 'cmd-long-delay', + // about: 'runs cmd 2', + // }, + ]; +}; const RunningConsole = memo<{ registeredConsole: RegisteredConsoleClient }>( ({ registeredConsole }) => { @@ -132,8 +208,8 @@ RunningConsole.displayName = 'RunningConsole'; // ------------------------------------------------------------ export const ShowDevConsole = memo(() => { const consoleManager = useConsoleManager(); - const commandService = useMemo(() => { - return new DevCommandService(); + const commands = useMemo(() => { + return getCommandList(); }, []); const handleRegisterOnClick = useCallback(() => { @@ -146,12 +222,12 @@ export const ShowDevConsole = memo(() => { }, consoleProps: { prompt: '>>', - commandService, + commands, 'data-test-subj': 'dev', }, }) .show(); - }, [commandService, consoleManager]); + }, [commands, consoleManager]); return ( @@ -173,8 +249,8 @@ export const ShowDevConsole = memo(() => {

{'Un-managed console'}

- - + + ); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index 39b1c9aecaf65..c773e0486620a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -133,8 +133,7 @@ const timepickerRanges = [ jest.mock('../../../../common/lib/kibana'); jest.mock('../../../../common/hooks/use_license'); -// FLAKY: https://github.com/elastic/kibana/issues/115489 -describe.skip('when on the endpoint list page', () => { +describe('when on the endpoint list page', () => { const docGenerator = new EndpointDocGenerator(); const { act, screen, fireEvent, waitFor } = reactTestingLibrary; @@ -296,17 +295,6 @@ describe.skip('when on the endpoint list page', () => { }); describe('when there is no selected host in the url', () => { - it('should not show the flyout', () => { - setEndpointListApiMockImplementation(coreStart.http, { - endpointsResults: [], - }); - - const renderResult = render(); - expect.assertions(1); - return renderResult.findByTestId('endpointDetailsFlyout').catch((e) => { - expect(e).not.toBeNull(); - }); - }); describe('when list data loads', () => { const generatedPolicyStatuses: Array< HostInfo['metadata']['Endpoint']['policy']['applied']['status'] diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/components/policy_endpoint_count.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/policy_endpoint_count.tsx index 08c2f1909339d..b8b2f98173f21 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/components/policy_endpoint_count.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/policy_endpoint_count.tsx @@ -25,7 +25,7 @@ export const PolicyEndpointCount = memo< policyId: string; nonLinkCondition: boolean; } ->(({ policyId, nonLinkCondition, children, ...otherProps }) => { +>(({ policyId, nonLinkCondition, 'data-test-subj': dataTestSubj, children, ...otherProps }) => { const filterByPolicyQuery = `(language:kuery,query:'united.endpoint.Endpoint.policy.applied.id : "${policyId}"')`; const { search } = useLocation(); const { getAppUrl } = useAppUrl(); @@ -59,11 +59,15 @@ export const PolicyEndpointCount = memo< const clickHandler = useNavigateByRouterEventHandler(toRoutePathWithBackOptions); if (nonLinkCondition) { - return {children}; + return ( + + {children} + + ); } return ( // eslint-disable-next-line @elastic/eui/href-or-on-click - + {children} ); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/mocks.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/mocks.tsx index 908ebc22a19cd..daa44f01dbffd 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/mocks.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/mocks.tsx @@ -13,7 +13,7 @@ import { I18nProvider } from '@kbn/i18n-react'; import type { PackageInfo } from '@kbn/fleet-plugin/common/types'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { QueryClient } from 'react-query'; +import { SecuritySolutionQueryClient } from '../../../../../common/containers/query_client/query_client_provider'; import { AppContextTestRender, createAppRootMockRenderer, @@ -85,7 +85,7 @@ export const createFleetContextRendererMock = (): AppContextTestRender => { additionalMiddleware: [mockedContext.middlewareSpy.actionSpyMiddleware], }); - const queryClient = new QueryClient(); + const queryClient = new SecuritySolutionQueryClient(); const Wrapper: RenderOptions['wrapper'] = ({ children }) => { const services = useMemo(() => { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/with_security_context/render_context_providers.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/with_security_context/render_context_providers.tsx index 9b3472192a718..ec222de1713f0 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/with_security_context/render_context_providers.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/with_security_context/render_context_providers.tsx @@ -8,8 +8,10 @@ import React, { memo, PropsWithChildren } from 'react'; import { Provider as ReduxStoreProvider } from 'react-redux'; import { Store } from 'redux'; -import type { QueryClient } from 'react-query'; -import { ReactQueryClientProvider } from '../../../../../../common/containers/query_client/query_client_provider'; +import { + ReactQueryClientProvider, + SecuritySolutionQueryClient, +} from '../../../../../../common/containers/query_client/query_client_provider'; import { SecuritySolutionStartDependenciesContext } from '../../../../../../common/components/user_privileges/endpoint/security_solution_start_dependencies'; import { CurrentLicense } from '../../../../../../common/components/current_license'; import { StartPlugins } from '../../../../../../types'; @@ -17,7 +19,7 @@ import { StartPlugins } from '../../../../../../types'; export type RenderContextProvidersProps = PropsWithChildren<{ store: Store; depsStart: Pick; - queryClient?: QueryClient; + queryClient?: SecuritySolutionQueryClient; }>; export const RenderContextProviders = memo( diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx index 482fe0ba064bd..3ea50af79a9c3 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx @@ -99,15 +99,23 @@ describe('When on the policy list page', () => { expect(policyNameCells).toBeTruthy(); expect(policyNameCells.length).toBe(5); }); - it('should show a avatar for the Created by column', () => { + it('should show an avatar and name for the Created by column', () => { + const expectedAvatarName = policies.items[0].created_by; const createdByCells = renderResult.getAllByTestId('created-by-avatar'); + const firstCreatedByName = renderResult.getAllByTestId('created-by-name')[0]; expect(createdByCells).toBeTruthy(); expect(createdByCells.length).toBe(5); + expect(createdByCells[0].textContent).toEqual(expectedAvatarName.charAt(0)); + expect(firstCreatedByName.textContent).toEqual(expectedAvatarName); }); - it('should show a avatar for the Updated by column', () => { + it('should show an avatar and name for the Updated by column', () => { + const expectedAvatarName = policies.items[0].updated_by; const updatedByCells = renderResult.getAllByTestId('updated-by-avatar'); + const firstUpdatedByName = renderResult.getAllByTestId('updated-by-name')[0]; expect(updatedByCells).toBeTruthy(); expect(updatedByCells.length).toBe(5); + expect(updatedByCells[0].textContent).toEqual(expectedAvatarName.charAt(0)); + expect(firstUpdatedByName.textContent).toEqual(expectedAvatarName); }); it('should show the correct endpoint count', () => { const endpointCount = renderResult.getAllByTestId('policyEndpointCountLink'); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx index 884a8572ecfe2..67398cbe39a44 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx @@ -189,7 +189,9 @@ export const PolicyList = memo(() => { - {name} + + {name} + ); @@ -222,7 +224,9 @@ export const PolicyList = memo(() => { - {name} + + {name} + ); diff --git a/x-pack/plugins/security_solution/public/management/services/policies/hooks.ts b/x-pack/plugins/security_solution/public/management/services/policies/hooks.ts index 6cc6140d336d2..cf053128e5f37 100644 --- a/x-pack/plugins/security_solution/public/management/services/policies/hooks.ts +++ b/x-pack/plugins/security_solution/public/management/services/policies/hooks.ts @@ -40,11 +40,11 @@ export function useGetEndpointSpecificPolicies( }, }); }, - { - refetchIntervalInBackground: false, - refetchOnWindowFocus: false, - onError, - } + onError + ? { + onError, + } + : undefined ); } @@ -56,7 +56,7 @@ export function useGetEndpointSpecificPolicies( */ export function useGetAgentCountForPolicy({ policyIds, - customQueryOptions = {}, + customQueryOptions, }: { policyIds: string[]; customQueryOptions?: UseQueryOptions; @@ -72,11 +72,7 @@ export function useGetAgentCountForPolicy({ }, }); }, - { - refetchIntervalInBackground: false, - refetchOnWindowFocus: false, - ...customQueryOptions, - } + customQueryOptions ); } @@ -84,7 +80,7 @@ export function useGetAgentCountForPolicy({ * This hook returns the endpoint security package which contains endpoint version info */ export function useGetEndpointSecurityPackage({ - customQueryOptions = {}, + customQueryOptions, }: { customQueryOptions?: UseQueryOptions; }): QueryObserverResult { @@ -94,10 +90,6 @@ export function useGetEndpointSecurityPackage({ () => { return sendGetEndpointSecurityPackage(http); }, - { - refetchIntervalInBackground: false, - refetchOnWindowFocus: false, - ...customQueryOptions, - } + customQueryOptions ); } diff --git a/x-pack/plugins/security_solution/public/network/links.ts b/x-pack/plugins/security_solution/public/network/links.ts new file mode 100644 index 0000000000000..ad209a220eebc --- /dev/null +++ b/x-pack/plugins/security_solution/public/network/links.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 { i18n } from '@kbn/i18n'; +import { NETWORK_PATH, SecurityPageName } from '../../common/constants'; +import { NETWORK } from '../app/translations'; +import { LinkItem } from '../common/links/types'; + +export const links: LinkItem = { + id: SecurityPageName.network, + title: NETWORK, + path: NETWORK_PATH, + globalNavEnabled: true, + globalSearchKeywords: [ + i18n.translate('xpack.securitySolution.appLinks.network', { + defaultMessage: 'Network', + }), + ], + globalNavOrder: 9003, + links: [ + { + id: SecurityPageName.networkDns, + title: i18n.translate('xpack.securitySolution.appLinks.network.dns', { + defaultMessage: 'DNS', + }), + path: `${NETWORK_PATH}/dns`, + }, + { + id: SecurityPageName.networkHttp, + title: i18n.translate('xpack.securitySolution.appLinks.network.http', { + defaultMessage: 'HTTP', + }), + path: `${NETWORK_PATH}/http`, + }, + { + id: SecurityPageName.networkTls, + title: i18n.translate('xpack.securitySolution.appLinks.network.tls', { + defaultMessage: 'TLS', + }), + path: `${NETWORK_PATH}/tls`, + }, + { + id: SecurityPageName.networkExternalAlerts, + title: i18n.translate('xpack.securitySolution.appLinks.network.externalAlerts', { + defaultMessage: 'External Alerts', + }), + path: `${NETWORK_PATH}/external-alerts`, + }, + { + id: SecurityPageName.networkAnomalies, + title: i18n.translate('xpack.securitySolution.appLinks.hosts.anomalies', { + defaultMessage: 'Anomalies', + }), + path: `${NETWORK_PATH}/anomalies`, + licenseType: 'gold', + }, + ], +}; diff --git a/x-pack/plugins/security_solution/public/network/pages/details/utils.ts b/x-pack/plugins/security_solution/public/network/pages/details/utils.ts index 98094665cbcd2..044c1d22a6348 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/utils.ts +++ b/x-pack/plugins/security_solution/public/network/pages/details/utils.ts @@ -42,6 +42,7 @@ export const getBreadcrumbs = ( }), }, ]; + if (params.detailName != null) { breadcrumb = [ ...breadcrumb, diff --git a/x-pack/plugins/security_solution/public/network/pages/network.test.tsx b/x-pack/plugins/security_solution/public/network/pages/network.test.tsx index a52c4e1560893..32ba03abe500b 100644 --- a/x-pack/plugins/security_solution/public/network/pages/network.test.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/network.test.tsx @@ -106,6 +106,7 @@ jest.mock('../../common/lib/kibana', () => { addError: jest.fn(), addSuccess: jest.fn(), addWarning: jest.fn(), + remove: jest.fn(), }), }; }); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx index 963b12a35dbf0..f7f3cc9a81f7f 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx @@ -20,7 +20,7 @@ import { LegendItem } from '../../../../common/components/charts/legend_item'; import { useAlertsByStatus } from './use_alerts_by_status'; import { ALERTS, - ALERTS_TITLE, + ALERTS_TEXT, STATUS_ACKNOWLEDGED, STATUS_CLOSED, STATUS_CRITICAL_LABEL, @@ -49,6 +49,11 @@ const StyledLegendFlexItem = styled(EuiFlexItem)` padding-top: 45px; `; +// To Do remove this styled component once togglequery is updated: #131405 +const StyledEuiPanel = styled(EuiPanel)` + height: fit-content; +`; + interface AlertsByStatusProps { signalIndexName: string | null; } @@ -105,12 +110,12 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => { [] ); + const openCount = donutData?.open?.total ?? 0; + const acknowledgedCount = donutData?.acknowledged?.total ?? 0; + const closedCount = donutData?.closed?.total ?? 0; + const totalAlerts = - loading || donutData == null - ? 0 - : (donutData?.open?.total ?? 0) + - (donutData?.acknowledged?.total ?? 0) + - (donutData?.closed?.total ?? 0); + loading || donutData == null ? 0 : openCount + acknowledgedCount + closedCount; const fillColor: FillColor = useCallback((d: ShapeTreeNode) => { return chartConfigs.find((cfg) => cfg.label === d.dataName)?.color ?? emptyDonutColor; @@ -119,7 +124,10 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => { return ( <> - + {loading && ( { )} } inspectMultiple toggleStatus={toggleStatus} @@ -152,10 +160,8 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => { <> - - {loading ? ( - - ) : ( + {totalAlerts !== 0 && ( + <> @@ -163,9 +169,8 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => { <> {ALERTS(totalAlerts)} - )} - - + + )} @@ -174,8 +179,8 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => { fillColor={fillColor} height={donutHeight} label={STATUS_OPEN} - title={} - totalCount={donutData?.open?.total ?? 0} + title={} + totalCount={openCount} /> @@ -184,8 +189,8 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => { fillColor={fillColor} height={donutHeight} label={STATUS_ACKNOWLEDGED} - title={} - totalCount={donutData?.acknowledged?.total ?? 0} + title={} + totalCount={acknowledgedCount} /> @@ -194,8 +199,8 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => { fillColor={fillColor} height={donutHeight} label={STATUS_CLOSED} - title={} - totalCount={donutData?.closed?.total ?? 0} + title={} + totalCount={closedCount} /> @@ -207,7 +212,7 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => { )} - + ); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/cases_by_status.test.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/cases_by_status.test.tsx new file mode 100644 index 0000000000000..18ac775dc5792 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/cases_by_status.test.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render, screen } from '@testing-library/react'; +import React from 'react'; +import { BarChartComponentProps } from '../../../../common/components/charts/barchart'; +import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { TestProviders } from '../../../../common/mock'; +import { CasesByStatus } from './cases_by_status'; +jest.mock('../../../../common/components/link_to'); +jest.mock('../../../../common/containers/query_toggle'); +jest.mock('./use_cases_by_status', () => ({ + useCasesByStatus: jest.fn().mockReturnValue({ + closed: 1, + inProgress: 2, + isLoading: false, + open: 3, + totalCounts: 6, + updatedAt: new Date('2022-04-08T12:00:00.000Z').valueOf(), + }), +})); +jest.mock('../../../../common/lib/kibana', () => { + const actual = jest.requireActual('../../../../common/lib/kibana'); + return { + ...actual, + useNavigation: jest.fn().mockReturnValue({ + getAppUrl: jest.fn(), + navigateTo: jest.fn(), + }), + }; +}); +jest.mock('../../../../common/components/charts/barchart', () => ({ + BarChart: jest.fn((props: BarChartComponentProps) =>
), +})); + +const mockSetToggle = jest.fn(); +(useQueryToggle as jest.Mock).mockReturnValue({ + toggleStatus: true, + setToggleStatus: mockSetToggle, +}); + +describe('CasesByStatus', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('renders title', () => { + render( + + + + ); + expect(screen.getByTestId('header-section-title')).toHaveTextContent('Cases'); + }); + + test('renders toggleQuery', () => { + render( + + + + ); + expect(screen.getByTestId('query-toggle-header')).toBeInTheDocument(); + }); + + test('renders BarChart', () => { + render( + + + + ); + expect(screen.getByTestId('chart-wrapper')).toBeInTheDocument(); + expect(screen.queryByTestId('bar-chart-mask')).not.toBeInTheDocument(); + }); + + test('collapses content', () => { + (useQueryToggle as jest.Mock).mockReturnValueOnce({ + toggleStatus: false, + setToggleStatus: mockSetToggle, + }); + render( + + + + ); + + expect(screen.queryByTestId('chart-wrapper')).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/cases_by_status.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/cases_by_status.tsx new file mode 100644 index 0000000000000..67ed0d43c830c --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/cases_by_status.tsx @@ -0,0 +1,201 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; +import { AxisStyle, Rotation, ScaleType } from '@elastic/charts'; +import styled from 'styled-components'; +import { FormattedNumber } from '@kbn/i18n-react'; +import numeral from '@elastic/numeral'; +import { BarChart } from '../../../../common/components/charts/barchart'; +import { LastUpdatedAt } from '../util'; +import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { HeaderSection } from '../../../../common/components/header_section'; +import { + CASES, + CASES_BY_STATUS_SECTION_TITLE, + STATUS_CLOSED, + STATUS_IN_PROGRESS, + STATUS_OPEN, + VIEW_CASES, +} from '../translations'; +import { LinkButton } from '../../../../common/components/links'; +import { useCasesByStatus } from './use_cases_by_status'; +import { SecurityPageName } from '../../../../../common/constants'; +import { useFormatUrl } from '../../../../common/components/link_to'; +import { appendSearch } from '../../../../common/components/link_to/helpers'; +import { useNavigation } from '../../../../common/lib/kibana'; + +const CASES_BY_STATUS_ID = 'casesByStatus'; + +export const numberFormatter = (value: string | number): string => value.toLocaleString(); + +export const barchartConfigs = { + series: { + xScaleType: ScaleType.Ordinal, + yScaleType: ScaleType.Linear, + stackAccessors: ['g'], + barSeriesStyle: { + rect: { + widthPixel: 22, + opacity: 1, + }, + }, + }, + axis: { + xTickFormatter: numberFormatter, + left: { + style: { + tickLine: { + size: 0, + }, + tickLabel: { + padding: 16, + fontSize: 14, + }, + } as Partial, + }, + bottom: { + style: { + tickLine: { + size: 0, + }, + tickLabel: { + padding: 16, + fontSize: 10.5, + }, + } as Partial, + labelFormat: (d: unknown) => numeral(d).format('0'), + }, + }, + settings: { + rotation: 90 as Rotation, + }, + customHeight: 146, +}; + +const barColors = { + empty: 'rgba(105, 112, 125, 0.1)', + open: '#79aad9', + 'in-progress': '#f1d86f', + closed: '#d3dae6', +}; + +export const emptyChartSettings = [ + { + key: 'open', + value: [{ y: 20, x: STATUS_OPEN, g: STATUS_OPEN }], + color: barColors.empty, + }, + { + key: 'in-progress', + value: [{ y: 20, x: STATUS_IN_PROGRESS, g: STATUS_IN_PROGRESS }], + color: barColors.empty, + }, + { + key: 'closed', + value: [{ y: 20, x: STATUS_CLOSED, g: STATUS_CLOSED }], + color: barColors.empty, + }, +]; + +const StyledEuiFlexItem = styled(EuiFlexItem)` + align-items: center; + width: 70%; +`; + +const Wrapper = styled.div` + width: 100%; +`; + +const StyledEuiPanel = styled(EuiPanel)` + height: 258px; +`; + +const CasesByStatusComponent: React.FC = () => { + const { toggleStatus, setToggleStatus } = useQueryToggle(CASES_BY_STATUS_ID); + const { getAppUrl, navigateTo } = useNavigation(); + const { search } = useFormatUrl(SecurityPageName.case); + const caseUrl = getAppUrl({ deepLinkId: SecurityPageName.case, path: appendSearch(search) }); + + const goToCases = useCallback( + (ev) => { + ev.preventDefault(); + navigateTo({ url: caseUrl }); + }, + [caseUrl, navigateTo] + ); + const { closed, inProgress, isLoading, open, totalCounts, updatedAt } = useCasesByStatus({ + skip: !toggleStatus, + }); + + const chartData = useMemo( + () => [ + { + key: 'open', + value: [{ y: open, x: STATUS_OPEN, g: STATUS_OPEN }], + color: barColors.open, + }, + { + key: 'in-progress', + value: [{ y: inProgress, x: STATUS_IN_PROGRESS, g: STATUS_IN_PROGRESS }], + color: barColors['in-progress'], + }, + { + key: 'closed', + value: [{ y: closed, x: STATUS_CLOSED, g: STATUS_CLOSED }], + color: barColors.closed, + }, + ], + [closed, inProgress, open] + ); + + return ( + + } + showInspectButton={false} + > + + + + {VIEW_CASES} + + + + + {!isLoading && toggleStatus && ( + + + {totalCounts !== 0 && ( + + <> + + + {' '} + {CASES(totalCounts)} + + + )} + + + + + + + + )} + + ); +}; + +export const CasesByStatus = React.memo(CasesByStatusComponent); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/index.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/index.tsx new file mode 100644 index 0000000000000..4e1aceb20f4ed --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/index.tsx @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { CasesByStatus } from './cases_by_status'; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/use_cases_by_status.test.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/use_cases_by_status.test.tsx new file mode 100644 index 0000000000000..37c48487912e0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/use_cases_by_status.test.tsx @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook, act } from '@testing-library/react-hooks'; +import { mockCasesContract } from '@kbn/cases-plugin/public/mocks'; +import { useKibana } from '../../../../common/lib/kibana'; +import { TestProviders } from '../../../../common/mock'; +import { + useCasesByStatus, + UseCasesByStatusProps, + UseCasesByStatusResults, +} from './use_cases_by_status'; + +const dateNow = new Date('2022-04-08T12:00:00.000Z').valueOf(); +const mockDateNow = jest.fn().mockReturnValue(dateNow); +Date.now = jest.fn(() => mockDateNow()) as unknown as DateConstructor['now']; + +jest.mock('../../../../common/containers/use_global_time', () => { + return { + useGlobalTime: jest + .fn() + .mockReturnValue({ from: '2022-04-05T12:00:00.000Z', to: '2022-04-08T12:00:00.000Z' }), + }; +}); +jest.mock('../../../../common/lib/kibana'); + +const mockGetCasesStatus = jest.fn(); +mockGetCasesStatus.mockResolvedValue({ + countOpenCases: 1, + countInProgressCases: 2, + countClosedCases: 3, +}); + +mockGetCasesStatus.mockResolvedValueOnce({ + countOpenCases: 0, + countInProgressCases: 0, + countClosedCases: 0, +}); + +const mockUseKibana = { + services: { + cases: { + ...mockCasesContract(), + api: { + cases: { + getCasesStatus: mockGetCasesStatus, + }, + }, + }, + }, +}; + +(useKibana as jest.Mock).mockReturnValue(mockUseKibana); + +describe('useCasesByStatus', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + test('init', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook< + UseCasesByStatusProps, + UseCasesByStatusResults + >(() => useCasesByStatus({ skip: false }), { + wrapper: TestProviders, + }); + await waitForNextUpdate(); + expect(result.current).toEqual({ + closed: 0, + inProgress: 0, + isLoading: true, + open: 0, + totalCounts: 0, + updatedAt: dateNow, + }); + }); + }); + + test('fetch data', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook< + UseCasesByStatusProps, + UseCasesByStatusResults + >(() => useCasesByStatus({ skip: false }), { + wrapper: TestProviders, + }); + await waitForNextUpdate(); + await waitForNextUpdate(); + expect(result.current).toEqual({ + closed: 3, + inProgress: 2, + isLoading: false, + open: 1, + totalCounts: 6, + updatedAt: dateNow, + }); + }); + }); + + test('skip', async () => { + const abortSpy = jest.spyOn(AbortController.prototype, 'abort'); + await act(async () => { + const localProps = { skip: false }; + + const { rerender, waitForNextUpdate } = renderHook< + UseCasesByStatusProps, + UseCasesByStatusResults + >(() => useCasesByStatus(localProps), { + wrapper: TestProviders, + }); + await waitForNextUpdate(); + await waitForNextUpdate(); + + localProps.skip = true; + act(() => rerender()); + act(() => rerender()); + expect(abortSpy).toHaveBeenCalledTimes(2); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/use_cases_by_status.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/use_cases_by_status.tsx new file mode 100644 index 0000000000000..580f91dea6d7d --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_by_status/use_cases_by_status.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CasesStatus } from '@kbn/cases-plugin/common/ui'; +import { useState, useEffect } from 'react'; +import { APP_ID } from '../../../../../common/constants'; +import { useGlobalTime } from '../../../../common/containers/use_global_time'; +import { useKibana } from '../../../../common/lib/kibana'; + +export interface UseCasesByStatusProps { + skip?: boolean; +} + +export interface UseCasesByStatusResults { + closed: number; + inProgress: number; + isLoading: boolean; + open: number; + totalCounts: number; + updatedAt: number; +} + +export const useCasesByStatus = ({ skip = false }) => { + const { + services: { cases }, + } = useKibana(); + const { to, from } = useGlobalTime(); + + const [updatedAt, setUpdatedAt] = useState(Date.now()); + const [isLoading, setIsLoading] = useState(true); + const [casesCounts, setCasesCounts] = useState(null); + + useEffect(() => { + let isSubscribed = true; + const abortCtrl = new AbortController(); + const fetchCases = async () => { + try { + const casesResponse = await cases.api.cases.getCasesStatus( + { + from, + to, + owner: APP_ID, + }, + abortCtrl.signal + ); + + if (isSubscribed) { + setCasesCounts(casesResponse); + } + } catch (error) { + if (isSubscribed) { + setCasesCounts(null); + } + } + if (isSubscribed) { + setIsLoading(false); + setUpdatedAt(Date.now()); + } + }; + + if (!skip) { + fetchCases(); + } + + if (skip) { + setIsLoading(false); + isSubscribed = false; + abortCtrl.abort(); + } + + return () => { + isSubscribed = false; + abortCtrl.abort(); + }; + }, [cases.api.cases, from, skip, to]); + + return { + closed: casesCounts?.countClosedCases ?? 0, + inProgress: casesCounts?.countInProgressCases ?? 0, + isLoading, + open: casesCounts?.countOpenCases ?? 0, + totalCounts: + (casesCounts?.countClosedCases ?? 0) + + (casesCounts?.countInProgressCases ?? 0) + + (casesCounts?.countOpenCases ?? 0), + updatedAt, + }; +}; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.test.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.test.tsx new file mode 100644 index 0000000000000..4250c20059094 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.test.tsx @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; 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 { TestProviders } from '../../../../common/mock'; +import { parsedCasesItems } from './mock_data'; +import { CasesTable } from './cases_table'; +import type { UseCaseItems } from './use_case_items'; + +const mockGetAppUrl = jest.fn(); +jest.mock('../../../../common/lib/kibana/hooks', () => { + const original = jest.requireActual('../../../../common/lib/kibana/hooks'); + return { + ...original, + useNavigation: () => ({ + getAppUrl: mockGetAppUrl, + }), + }; +}); + +type UseCaseItemsReturn = ReturnType; +const defaultCaseItemsReturn: UseCaseItemsReturn = { + items: [], + isLoading: false, + updatedAt: Date.now(), +}; +const mockUseCaseItems = jest.fn(() => defaultCaseItemsReturn); +const mockUseCaseItemsReturn = (overrides: Partial) => { + mockUseCaseItems.mockReturnValueOnce({ + ...defaultCaseItemsReturn, + ...overrides, + }); +}; + +jest.mock('./use_case_items', () => ({ + useCaseItems: () => mockUseCaseItems(), +})); + +const renderComponent = () => + render( + + + + ); + +describe('CasesTable', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should render empty table', () => { + const { getByText, getByTestId } = renderComponent(); + + expect(getByTestId('recentlyCreatedCasesPanel')).toBeInTheDocument(); + expect(getByText('No cases to display')).toBeInTheDocument(); + expect(getByTestId('allCasesButton')).toBeInTheDocument(); + }); + + it('should render a loading table', () => { + mockUseCaseItemsReturn({ isLoading: true }); + const { getByText, getByTestId } = renderComponent(); + + expect(getByText('Updating...')).toBeInTheDocument(); + expect(getByTestId('allCasesButton')).toBeInTheDocument(); + expect(getByTestId('recentlyCreatedCasesTable')).toHaveClass('euiBasicTable-loading'); + }); + + it('should render the updated at subtitle', () => { + mockUseCaseItemsReturn({ isLoading: false }); + const { getByText } = renderComponent(); + + expect(getByText('Updated now')).toBeInTheDocument(); + }); + + it('should render the table columns', () => { + mockUseCaseItemsReturn({ items: parsedCasesItems }); + const { getAllByRole } = renderComponent(); + + const columnHeaders = getAllByRole('columnheader'); + expect(columnHeaders.at(0)).toHaveTextContent('Name'); + expect(columnHeaders.at(1)).toHaveTextContent('Note'); + expect(columnHeaders.at(2)).toHaveTextContent('Time'); + expect(columnHeaders.at(3)).toHaveTextContent('Created by'); + expect(columnHeaders.at(4)).toHaveTextContent('Status'); + }); + + it('should render the table items', () => { + mockUseCaseItemsReturn({ items: [parsedCasesItems[0]] }); + const { getByTestId } = renderComponent(); + + expect(getByTestId('recentlyCreatedCaseName')).toHaveTextContent('sdcsd'); + expect(getByTestId('recentlyCreatedCaseNote')).toHaveTextContent('klklk'); + expect(getByTestId('recentlyCreatedCaseTime')).toHaveTextContent('April 25, 2022'); + expect(getByTestId('recentlyCreatedCaseCreatedBy')).toHaveTextContent('elastic'); + expect(getByTestId('recentlyCreatedCaseStatus')).toHaveTextContent('Open'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.tsx new file mode 100644 index 0000000000000..80d2495f57ab5 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.tsx @@ -0,0 +1,145 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo } from 'react'; + +import { + EuiBasicTable, + EuiBasicTableColumn, + EuiButton, + EuiEmptyPrompt, + EuiPanel, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import { CaseStatuses } from '@kbn/cases-plugin/common'; + +import { SecurityPageName } from '../../../../app/types'; +import { FormattedDate } from '../../../../common/components/formatted_date'; +import { HeaderSection } from '../../../../common/components/header_section'; +import { HoverVisibilityContainer } from '../../../../common/components/hover_visibility_container'; +import { BUTTON_CLASS as INPECT_BUTTON_CLASS } from '../../../../common/components/inspect'; +import { CaseDetailsLink } from '../../../../common/components/links'; +import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { useNavigation, NavigateTo, GetAppUrl } from '../../../../common/lib/kibana'; +import * as i18n from '../translations'; +import { LastUpdatedAt } from '../util'; +import { StatusBadge } from './status_badge'; +import { CaseItem, useCaseItems } from './use_case_items'; + +type GetTableColumns = (params: { + getAppUrl: GetAppUrl; + navigateTo: NavigateTo; +}) => Array>; + +const DETECTION_RESPONSE_RECENT_CASES_QUERY_ID = 'recentlyCreatedCasesQuery'; + +export const CasesTable = React.memo(() => { + const { getAppUrl, navigateTo } = useNavigation(); + const { toggleStatus, setToggleStatus } = useQueryToggle( + DETECTION_RESPONSE_RECENT_CASES_QUERY_ID + ); + const { items, isLoading, updatedAt } = useCaseItems({ + skip: !toggleStatus, + }); + + const navigateToCases = useCallback(() => { + navigateTo({ deepLinkId: SecurityPageName.case }); + }, [navigateTo]); + + const columns = useMemo( + () => getTableColumns({ getAppUrl, navigateTo }), + [getAppUrl, navigateTo] + ); + + return ( + + + } + showInspectButton={false} + /> + + {toggleStatus && ( + <> + {i18n.NO_CASES_FOUND}} titleSize="xs" /> + } + /> + + + {i18n.VIEW_RECENT_CASES} + + + )} + + + ); +}); + +CasesTable.displayName = 'CasesTable'; + +const getTableColumns: GetTableColumns = () => [ + { + field: 'id', + name: i18n.CASES_TABLE_COLUMN_NAME, + truncateText: true, + textOnly: true, + 'data-test-subj': 'recentlyCreatedCaseName', + + render: (id: string, { name }) => {name}, + }, + { + field: 'note', + name: i18n.CASES_TABLE_COLUMN_NOTE, + truncateText: true, + textOnly: true, + render: (note: string) => ( + + {note} + + ), + }, + { + field: 'createdAt', + name: i18n.CASES_TABLE_COLUMN_TIME, + render: (createdAt: string) => ( + + ), + 'data-test-subj': 'recentlyCreatedCaseTime', + }, + { + field: 'createdBy', + name: i18n.CASES_TABLE_COLUMN_CREATED_BY, + render: (createdBy: string) => ( + + {createdBy} + + ), + }, + { + field: 'status', + name: i18n.CASES_TABLE_COLUMN_STATUS, + render: (status: CaseStatuses) => , + 'data-test-subj': 'recentlyCreatedCaseStatus', + }, +]; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/index.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/index.ts new file mode 100644 index 0000000000000..4151054408345 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { CasesTable } from './cases_table'; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/mock_data.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/mock_data.ts new file mode 100644 index 0000000000000..2547cfa264757 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/mock_data.ts @@ -0,0 +1,160 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CaseStatuses } from '@kbn/cases-plugin/common'; + +export const mockCasesResult = { + cases: [ + { + id: '0ce2a510-c43a-11ec-98b5-3109bd2a1901', + version: 'Wzg2MDIsMV0=', + comments: [], + totalComment: 0, + totalAlerts: 0, + title: 'sdcsd', + tags: [], + description: 'klklk', + settings: { + syncAlerts: true, + }, + owner: 'securitySolution', + closed_at: '2022-04-25T01:50:40.435Z', + closed_by: { + full_name: null, + email: null, + username: 'elastic', + }, + createdAt: '2022-04-25T01:50:14.499Z', + createdBy: { + username: 'elastic', + email: null, + full_name: null, + }, + status: 'open', + updated_at: '2022-04-25T01:50:40.435Z', + updated_by: { + full_name: null, + email: null, + username: 'elastic', + }, + connector: { + id: 'none', + name: 'none', + type: '.none', + fields: null, + }, + external_service: null, + }, + { + id: 'dc12f930-c178-11ec-98b5-3109bd2a1901', + version: 'Wzg5NjksMV0=', + comments: [], + totalComment: 0, + totalAlerts: 0, + title: 'zzzz', + tags: [], + description: 'sssss', + settings: { + syncAlerts: true, + }, + owner: 'securitySolution', + closed_at: '2022-04-25T13:45:18.317Z', + closed_by: { + full_name: null, + email: null, + username: 'elastic', + }, + createdAt: '2022-04-21T13:42:17.414Z', + createdBy: { + username: 'elastic', + email: null, + full_name: null, + }, + status: 'in-progress', + updated_at: '2022-04-25T13:45:18.317Z', + updated_by: { + full_name: null, + email: null, + username: 'elastic', + }, + connector: { + id: 'none', + name: 'none', + type: '.none', + fields: null, + }, + external_service: null, + }, + { + id: 'd11cbac0-c178-11ec-98b5-3109bd2a1901', + version: 'Wzg5ODQsMV0=', + comments: [], + totalComment: 0, + totalAlerts: 0, + title: 'asxa', + tags: [], + description: 'dsdd', + settings: { + syncAlerts: true, + }, + owner: 'securitySolution', + closed_at: '2022-04-25T13:45:22.539Z', + closed_by: { + full_name: null, + email: null, + username: 'elastic', + }, + createdAt: '2022-04-21T13:41:59.025Z', + createdBy: { + username: 'elastic', + email: null, + full_name: null, + }, + status: 'closed', + updated_at: '2022-04-25T13:45:22.539Z', + updated_by: { + full_name: null, + email: null, + username: 'elastic', + }, + connector: { + id: 'none', + name: 'none', + type: '.none', + fields: null, + }, + external_service: null, + }, + ], +}; + +export const parsedCasesItems = [ + { + name: 'sdcsd', + note: 'klklk', + createdBy: 'elastic', + status: CaseStatuses.open, + createdAt: '2022-04-25T01:50:14.499Z', + id: '0ce2a510-c43a-11ec-98b5-3109bd2a1901', + }, + { + name: 'zzzz', + note: 'sssss', + status: CaseStatuses['in-progress'], + createdAt: '2022-04-21T13:42:17.414Z', + createdBy: 'elastic', + id: 'dc12f930-c178-11ec-98b5-3109bd2a1901', + }, + { + name: 'asxa', + note: 'dsdd', + createdBy: 'elastic', + status: CaseStatuses.closed, + createdAt: '2022-04-21T13:41:59.025Z', + id: 'd11cbac0-c178-11ec-98b5-3109bd2a1901', + }, +]; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/status_badge.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/status_badge.tsx new file mode 100644 index 0000000000000..9ae1281d9e6e7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/status_badge.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { CaseStatuses } from '@kbn/cases-plugin/common'; +import { EuiBadge } from '@elastic/eui'; + +import * as i18n from '../translations'; + +interface Props { + status: CaseStatuses; +} + +const statuses = { + [CaseStatuses.open]: { + color: 'primary', + label: i18n.STATUS_OPEN, + }, + [CaseStatuses['in-progress']]: { + color: 'warning', + label: i18n.STATUS_IN_PROGRESS, + }, + [CaseStatuses.closed]: { + color: 'default', + label: i18n.STATUS_CLOSED, + }, +} as const; + +export const StatusBadge: React.FC = ({ status }) => { + return ( + + {statuses[status].label} + + ); +}; + +StatusBadge.displayName = 'StatusBadge'; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/use_case_items.test.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/use_case_items.test.ts new file mode 100644 index 0000000000000..d112aaac7d5ad --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/use_case_items.test.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act, renderHook } from '@testing-library/react-hooks'; + +import { mockCasesResult, parsedCasesItems } from './mock_data'; +import { useCaseItems, UseCaseItemsProps } from './use_case_items'; + +import type { UseCaseItems } from './use_case_items'; + +const dateNow = new Date('2022-04-08T12:00:00.000Z').valueOf(); +const mockDateNow = jest.fn().mockReturnValue(dateNow); +Date.now = jest.fn(() => mockDateNow()) as unknown as DateConstructor['now']; + +const defaultCasesReturn = { + cases: [], +}; + +const mockCasesApi = jest.fn().mockResolvedValue(defaultCasesReturn); +const mockKibana = { + services: { + cases: { + api: { + cases: { + find: (...props: unknown[]) => mockCasesApi(...props), + }, + }, + }, + }, +}; +jest.mock('../../../../common/lib/kibana', () => ({ + useKibana: () => mockKibana, +})); + +const from = '2020-07-07T08:20:18.966Z'; +const to = '2020-07-08T08:20:18.966Z'; + +const mockUseGlobalTime = jest.fn().mockReturnValue({ from, to }); +jest.mock('../../../../common/containers/use_global_time', () => { + return { + useGlobalTime: (...props: unknown[]) => mockUseGlobalTime(...props), + }; +}); + +const renderUseCaseItems = (overrides: Partial = {}) => + renderHook>(() => + useCaseItems({ skip: false, ...overrides }) + ); + +describe('useCaseItems', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockDateNow.mockReturnValue(dateNow); + mockCasesApi.mockResolvedValue(defaultCasesReturn); + }); + + it('should return default values', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderUseCaseItems(); + + await waitForNextUpdate(); + await waitForNextUpdate(); + + expect(result.current).toEqual({ + items: [], + isLoading: false, + updatedAt: dateNow, + }); + }); + + expect(mockCasesApi).toBeCalledWith({ + from: '2020-07-07T08:20:18.966Z', + to: '2020-07-08T08:20:18.966Z', + owner: 'securitySolution', + sortField: 'create_at', + sortOrder: 'desc', + page: 1, + perPage: 4, + }); + }); + + it('should return parsed items', async () => { + mockCasesApi.mockReturnValue(mockCasesResult); + + await act(async () => { + const { result, waitForNextUpdate } = renderUseCaseItems(); + + await waitForNextUpdate(); + await waitForNextUpdate(); + + expect(result.current).toEqual({ + items: parsedCasesItems, + isLoading: false, + updatedAt: dateNow, + }); + }); + }); + + it('should return new updatedAt', async () => { + const newDateNow = new Date('2022-04-08T14:00:00.000Z').valueOf(); + mockDateNow.mockReturnValue(newDateNow); + mockDateNow.mockReturnValueOnce(dateNow); + mockCasesApi.mockReturnValue(mockCasesResult); + + await act(async () => { + const { result, waitForNextUpdate } = renderUseCaseItems(); + + await waitForNextUpdate(); + await waitForNextUpdate(); + + expect(mockDateNow).toHaveBeenCalled(); + expect(result.current).toEqual({ + items: parsedCasesItems, + isLoading: false, + updatedAt: newDateNow, + }); + }); + }); + + it('should skip the query', () => { + const { result } = renderUseCaseItems({ skip: true }); + + expect(result.current).toEqual({ + items: [], + isLoading: false, + updatedAt: dateNow, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/use_case_items.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/use_case_items.ts new file mode 100644 index 0000000000000..1b19327989729 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/use_case_items.ts @@ -0,0 +1,110 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useState, useEffect } from 'react'; + +import { CaseStatuses } from '@kbn/cases-plugin/common'; +import { Cases } from '@kbn/cases-plugin/common/ui'; + +import { APP_ID } from '../../../../../common/constants'; +import { useGlobalTime } from '../../../../common/containers/use_global_time'; +import { useKibana } from '../../../../common/lib/kibana'; +import { addError } from '../../../../common/store/app/actions'; + +import * as i18n from '../translations'; + +export interface CaseItem { + name: string; + note: string; + createdAt: string; + createdBy: string; + status: CaseStatuses; + id: string; +} + +export interface UseCaseItemsProps { + skip: boolean; +} + +export type UseCaseItems = (props: UseCaseItemsProps) => { + items: CaseItem[]; + isLoading: boolean; + updatedAt: number; +}; + +export const useCaseItems: UseCaseItems = ({ skip }) => { + const { + services: { cases }, + } = useKibana(); + const { to, from } = useGlobalTime(); + const [isLoading, setIsLoading] = useState(true); + const [updatedAt, setUpdatedAt] = useState(Date.now()); + const [items, setItems] = useState([]); + + useEffect(() => { + let isSubscribed = true; + const abortCtrl = new AbortController(); + + const fetchCases = async () => { + try { + const casesResponse = await cases.api.cases.find({ + from, + to, + owner: APP_ID, + sortField: 'create_at', + sortOrder: 'desc', + page: 1, + perPage: 4, + }); + + if (isSubscribed) { + setItems(parseCases(casesResponse)); + setUpdatedAt(Date.now()); + } + } catch (error) { + if (isSubscribed) { + addError(error, { title: i18n.ERROR_MESSAGE_CASES }); + } + } + setIsLoading(false); + }; + + if (!skip) { + fetchCases(); + } + + if (skip) { + setIsLoading(false); + isSubscribed = false; + abortCtrl.abort(); + } + + return () => { + isSubscribed = false; + abortCtrl.abort(); + }; + }, [cases.api.cases, from, skip, to]); + + return { items, isLoading, updatedAt }; +}; + +function parseCases(casesResponse: Cases): CaseItem[] { + const allCases = casesResponse.cases || []; + + return allCases.reduce((accumulated, currentCase) => { + accumulated.push({ + id: currentCase.id, + name: currentCase.title, + note: currentCase.description, + createdAt: currentCase.createdAt, + createdBy: currentCase.createdBy.username || '—', + status: currentCase.status, + }); + + return accumulated; + }, []); +} diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.test.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.test.tsx new file mode 100644 index 0000000000000..a89055a72df6f --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.test.tsx @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; 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 { TestProviders } from '../../../../common/mock'; +import { parsedVulnerableHostsAlertsResult } from './mock_data'; +import { UseHostAlertsItems } from './use_host_alerts_items'; +import { HostAlertsTable } from './host_alerts_table'; + +const mockGetAppUrl = jest.fn(); +jest.mock('../../../../common/lib/kibana/hooks', () => { + const original = jest.requireActual('../../../../common/lib/kibana/hooks'); + return { + ...original, + useNavigation: () => ({ + getAppUrl: mockGetAppUrl, + }), + }; +}); + +type UseHostAlertsItemsReturn = ReturnType; +const defaultUseHostAlertsItemsReturn: UseHostAlertsItemsReturn = { + items: [], + isLoading: false, + updatedAt: Date.now(), +}; +const mockUseHostAlertsItems = jest.fn(() => defaultUseHostAlertsItemsReturn); +const mockUseHostAlertsItemsReturn = (overrides: Partial) => { + mockUseHostAlertsItems.mockReturnValueOnce({ ...defaultUseHostAlertsItemsReturn, ...overrides }); +}; + +jest.mock('./use_host_alerts_items', () => ({ + useHostAlertsItems: () => mockUseHostAlertsItems(), +})); + +const renderComponent = () => + render( + + + + ); + +// FLAKY: https://github.com/elastic/kibana/issues/131611 +describe.skip('HostAlertsTable', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should render empty table', () => { + const { getByText, getByTestId } = renderComponent(); + + expect(getByTestId('severityHostAlertsPanel')).toBeInTheDocument(); + expect(getByText('No alerts to display')).toBeInTheDocument(); + expect(getByTestId('severityHostAlertsButton')).toBeInTheDocument(); + }); + + it('should render a loading table', () => { + mockUseHostAlertsItemsReturn({ isLoading: true }); + const { getByText, getByTestId } = renderComponent(); + + expect(getByText('Updating...')).toBeInTheDocument(); + expect(getByTestId('severityHostAlertsButton')).toBeInTheDocument(); + expect(getByTestId('severityHostAlertsTable')).toHaveClass('euiBasicTable-loading'); + }); + + it('should render the updated at subtitle', () => { + mockUseHostAlertsItemsReturn({ isLoading: false }); + const { getByText } = renderComponent(); + + expect(getByText('Updated now')).toBeInTheDocument(); + }); + + it('should render the table columns', () => { + mockUseHostAlertsItemsReturn({ items: parsedVulnerableHostsAlertsResult }); + const { getAllByRole } = renderComponent(); + + const columnHeaders = getAllByRole('columnheader'); + expect(columnHeaders.at(0)).toHaveTextContent('Host name'); + expect(columnHeaders.at(1)).toHaveTextContent('Alerts'); + expect(columnHeaders.at(2)).toHaveTextContent('Critical'); + expect(columnHeaders.at(3)).toHaveTextContent('High'); + expect(columnHeaders.at(4)).toHaveTextContent('Medium'); + expect(columnHeaders.at(5)).toHaveTextContent('Low'); + }); + + it('should render the table items', () => { + mockUseHostAlertsItemsReturn({ items: [parsedVulnerableHostsAlertsResult[0]] }); + const { getByTestId } = renderComponent(); + + expect(getByTestId('hostSeverityAlertsTable-hostName')).toHaveTextContent('Host-342m5gl1g2'); + expect(getByTestId('hostSeverityAlertsTable-totalAlerts')).toHaveTextContent('100'); + expect(getByTestId('hostSeverityAlertsTable-critical')).toHaveTextContent('5'); + expect(getByTestId('hostSeverityAlertsTable-high')).toHaveTextContent('50'); + expect(getByTestId('hostSeverityAlertsTable-medium')).toHaveTextContent('5'); + expect(getByTestId('hostSeverityAlertsTable-low')).toHaveTextContent('40'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx new file mode 100644 index 0000000000000..49ad4352cb586 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.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, { useCallback, useMemo } from 'react'; +import styled from 'styled-components'; + +import { + EuiBasicTable, + EuiBasicTableColumn, + EuiButton, + EuiEmptyPrompt, + EuiHealth, + EuiPanel, + EuiSpacer, +} from '@elastic/eui'; + +import { SecurityPageName } from '../../../../app/types'; +import { FormattedCount } from '../../../../common/components/formatted_number'; +import { HeaderSection } from '../../../../common/components/header_section'; +import { HoverVisibilityContainer } from '../../../../common/components/hover_visibility_container'; +import { BUTTON_CLASS as INPECT_BUTTON_CLASS } from '../../../../common/components/inspect'; +import { HostDetailsLink } from '../../../../common/components/links'; +import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { useNavigation, NavigateTo, GetAppUrl } from '../../../../common/lib/kibana'; +import * as i18n from '../translations'; +import { LastUpdatedAt, SEVERITY_COLOR } from '../util'; +import { HostAlertsItem, useHostAlertsItems } from './use_host_alerts_items'; + +type GetTableColumns = (params: { + getAppUrl: GetAppUrl; + navigateTo: NavigateTo; +}) => Array>; + +interface HostAlertsTableProps { + signalIndexName: string | null; +} + +const DETECTION_RESPONSE_HOST_SEVERITY_QUERY_ID = 'vulnerableHostsBySeverityQuery'; + +// To Do remove this styled component once togglequery is updated: #131405 +const StyledEuiPanel = styled(EuiPanel)` + height: fit-content; +`; + +export const HostAlertsTable = React.memo(({ signalIndexName }: HostAlertsTableProps) => { + const { getAppUrl, navigateTo } = useNavigation(); + const { toggleStatus, setToggleStatus } = useQueryToggle( + DETECTION_RESPONSE_HOST_SEVERITY_QUERY_ID + ); + + const { items, isLoading, updatedAt } = useHostAlertsItems({ + skip: !toggleStatus, + queryId: DETECTION_RESPONSE_HOST_SEVERITY_QUERY_ID, + signalIndexName, + }); + + const navigateToHosts = useCallback(() => { + navigateTo({ deepLinkId: SecurityPageName.hosts }); + }, [navigateTo]); + + const columns = useMemo( + () => getTableColumns({ getAppUrl, navigateTo }), + [getAppUrl, navigateTo] + ); + + return ( + //
+ + + } + titleSize="s" + toggleQuery={setToggleStatus} + toggleStatus={toggleStatus} + /> + {toggleStatus && ( + <> + {i18n.NO_ALERTS_FOUND}} titleSize="xs" /> + } + /> + + + {i18n.VIEW_ALL_HOST_ALERTS} + + + )} + + + //
+ ); +}); + +HostAlertsTable.displayName = 'HostAlertsTable'; + +const getTableColumns: GetTableColumns = ({ getAppUrl, navigateTo }) => [ + { + field: 'hostName', + name: i18n.HOST_ALERTS_HOSTNAME_COLUMN, + truncateText: true, + textOnly: true, + 'data-test-subj': 'hostSeverityAlertsTable-hostName', + render: (hostName: string) => , + }, + { + field: 'totalAlerts', + name: i18n.ALERTS_TEXT, + 'data-test-subj': 'hostSeverityAlertsTable-totalAlerts', + render: (totalAlerts: number) => , + }, + { + field: 'critical', + name: i18n.STATUS_CRITICAL_LABEL, + render: (count: number) => ( + + + + ), + }, + { + field: 'high', + name: i18n.STATUS_HIGH_LABEL, + render: (count: number) => ( + + + + ), + }, + { + field: 'medium', + name: i18n.STATUS_MEDIUM_LABEL, + render: (count: number) => ( + + + + ), + }, + { + field: 'low', + name: i18n.STATUS_LOW_LABEL, + render: (count: number) => ( + + + + ), + }, +]; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/index.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/index.ts new file mode 100644 index 0000000000000..62423931bbc4f --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { HostAlertsTable } from './host_alerts_table'; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/mock_data.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/mock_data.ts new file mode 100644 index 0000000000000..d46d1b5401a9e --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/mock_data.ts @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { buildVulnerableHostAggregationQuery } from './use_host_alerts_items'; + +export const mockVulnerableHostsBySeverityResult = { + aggregations: { + hostsBySeverity: { + buckets: [ + { + key: 'Host-342m5gl1g2', + doc_count: 100, + high: { + doc_count: 50, + }, + critical: { + doc_count: 5, + }, + low: { + doc_count: 40, + }, + medium: { + doc_count: 5, + }, + }, + { + key: 'Host-vns3hyykhu', + doc_count: 104, + high: { + doc_count: 100, + }, + critical: { + doc_count: 4, + }, + low: { + doc_count: 0, + }, + medium: { + doc_count: 0, + }, + }, + { + key: 'Host-awafztonav', + doc_count: 108, + high: { + doc_count: 50, + }, + critical: { + doc_count: 4, + }, + low: { + doc_count: 50, + }, + medium: { + doc_count: 4, + }, + }, + { + key: 'Host-56k7zf5kne', + doc_count: 128, + high: { + doc_count: 6, + }, + critical: { + doc_count: 1, + }, + low: { + doc_count: 59, + }, + medium: { + doc_count: 62, + }, + }, + ], + }, + }, +}; + +export const parsedVulnerableHostsAlertsResult = [ + { + hostName: 'Host-342m5gl1g2', + totalAlerts: 100, + critical: 5, + high: 50, + low: 40, + medium: 5, + }, + { + hostName: 'Host-vns3hyykhu', + totalAlerts: 104, + critical: 4, + high: 100, + low: 0, + medium: 0, + }, + { + hostName: 'Host-awafztonav', + totalAlerts: 108, + critical: 4, + high: 50, + low: 50, + medium: 4, + }, + { + hostName: 'Host-56k7zf5kne', + totalAlerts: 128, + critical: 1, + high: 6, + low: 59, + medium: 62, + }, +]; + +export const mockQuery = () => ({ + query: buildVulnerableHostAggregationQuery({ + from: '2020-07-07T08:20:18.966Z', + to: '2020-07-08T08:20:18.966Z', + }), + indexName: 'signal-alerts', + skip: false, +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/use_host_alerts_items.test.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/use_host_alerts_items.test.ts new file mode 100644 index 0000000000000..42568a390f686 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/use_host_alerts_items.test.ts @@ -0,0 +1,127 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; + +import { + mockQuery, + mockVulnerableHostsBySeverityResult, + parsedVulnerableHostsAlertsResult, +} from './mock_data'; +import { useHostAlertsItems } from './use_host_alerts_items'; + +import type { UseHostAlertsItems, UseHostAlertsItemsProps } from './use_host_alerts_items'; + +const signalIndexName = 'signal-alerts'; + +const dateNow = new Date('2022-04-15T12:00:00.000Z').valueOf(); +const mockDateNow = jest.fn().mockReturnValue(dateNow); +Date.now = jest.fn(() => mockDateNow()) as unknown as DateConstructor['now']; + +const defaultUseQueryAlertsReturn = { + loading: false, + data: null, + setQuery: () => {}, + response: '', + request: '', + refetch: () => {}, +}; + +const mockUseQueryAlerts = jest.fn().mockReturnValue(defaultUseQueryAlertsReturn); +jest.mock('../../../../detections/containers/detection_engine/alerts/use_query', () => { + return { + useQueryAlerts: (...props: unknown[]) => mockUseQueryAlerts(...props), + }; +}); + +const from = '2020-07-07T08:20:18.966Z'; +const to = '2020-07-08T08:20:18.966Z'; + +const mockUseGlobalTime = jest + .fn() + .mockReturnValue({ from, to, setQuery: jest.fn(), deleteQuery: jest.fn() }); +jest.mock('../../../../common/containers/use_global_time', () => { + return { + useGlobalTime: (...props: unknown[]) => mockUseGlobalTime(...props), + }; +}); + +const renderUseHostAlertsItems = (overrides: Partial = {}) => + renderHook>(() => + useHostAlertsItems({ + skip: false, + signalIndexName, + queryId: 'testing', + ...overrides, + }) + ); + +describe('useVulnerableHostsCounters', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockDateNow.mockReturnValue(dateNow); + mockUseQueryAlerts.mockReturnValue(defaultUseQueryAlertsReturn); + }); + + it('should return default values', () => { + const { result } = renderUseHostAlertsItems(); + + expect(result.current).toEqual({ + items: [], + isLoading: false, + updatedAt: dateNow, + }); + + expect(mockUseQueryAlerts).toBeCalledWith(mockQuery()); + }); + + it('should return parsed items', () => { + mockUseQueryAlerts.mockReturnValue({ + ...defaultUseQueryAlertsReturn, + data: mockVulnerableHostsBySeverityResult, + }); + + const { result } = renderUseHostAlertsItems(); + + expect(result.current).toEqual({ + items: parsedVulnerableHostsAlertsResult, + isLoading: false, + updatedAt: dateNow, + }); + }); + + it('should return new updatedAt', () => { + const newDateNow = new Date('2022-04-08T14:00:00.000Z').valueOf(); + mockDateNow.mockReturnValue(newDateNow); // setUpdatedAt call + mockDateNow.mockReturnValueOnce(dateNow); // initialization call + + mockUseQueryAlerts.mockReturnValue({ + ...defaultUseQueryAlertsReturn, + data: mockVulnerableHostsBySeverityResult, + }); + + const { result } = renderUseHostAlertsItems(); + expect(mockDateNow).toHaveBeenCalled(); + expect(result.current).toEqual({ + items: parsedVulnerableHostsAlertsResult, + isLoading: false, + updatedAt: newDateNow, + }); + }); + + it('should skip the query', () => { + const { result } = renderUseHostAlertsItems({ skip: true }); + + expect(mockUseQueryAlerts).toBeCalledWith({ ...mockQuery(), skip: true }); + + expect(result.current).toEqual({ + items: [], + isLoading: false, + updatedAt: dateNow, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/use_host_alerts_items.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/use_host_alerts_items.ts new file mode 100644 index 0000000000000..a01cd709b44f6 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/use_host_alerts_items.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 { useCallback, useEffect, useState } from 'react'; + +import { useQueryInspector } from '../../../../common/components/page/manage_query'; +import { useGlobalTime } from '../../../../common/containers/use_global_time'; +import { GenericBuckets } from '../../../../../common/search_strategy'; +import { useQueryAlerts } from '../../../../detections/containers/detection_engine/alerts/use_query'; + +const HOSTS_BY_SEVERITY_AGG = 'hostsBySeverity'; + +interface TimeRange { + from: string; + to: string; +} + +export interface UseHostAlertsItemsProps { + skip: boolean; + queryId: string; + signalIndexName: string | null; +} +export interface HostAlertsItem { + hostName: string; + totalAlerts: number; + low: number; + medium: number; + high: number; + critical: number; +} + +export type UseHostAlertsItems = (props: UseHostAlertsItemsProps) => { + items: HostAlertsItem[]; + isLoading: boolean; + updatedAt: number; +}; + +export const useHostAlertsItems: UseHostAlertsItems = ({ skip, queryId, signalIndexName }) => { + const [updatedAt, setUpdatedAt] = useState(Date.now()); + const [items, setItems] = useState([]); + + const { to, from, setQuery: setGlobalQuery, deleteQuery } = useGlobalTime(); + + const { + data, + request, + response, + setQuery, + loading, + refetch: refetchQuery, + } = useQueryAlerts<{}, AlertCountersBySeverityAndHostAggregation>({ + query: buildVulnerableHostAggregationQuery({ from, to }), + indexName: signalIndexName, + skip, + }); + + useEffect(() => { + setQuery(buildVulnerableHostAggregationQuery({ from, to })); + }, [setQuery, from, to]); + + useEffect(() => { + if (data == null || !data.aggregations) { + setItems([]); + } else { + setItems(parseHostsData(data.aggregations)); + } + setUpdatedAt(Date.now()); + }, [data]); + + const refetch = useCallback(() => { + if (!skip && refetchQuery) { + refetchQuery(); + } + }, [skip, refetchQuery]); + + useQueryInspector({ + deleteQuery, + inspect: { + dsl: [request], + response: [response], + }, + refetch, + setQuery: setGlobalQuery, + queryId, + loading, + }); + return { items, isLoading: loading, updatedAt }; +}; + +export const buildVulnerableHostAggregationQuery = ({ from, to }: TimeRange) => ({ + query: { + bool: { + filter: [ + { + term: { + 'kibana.alert.workflow_status': 'open', + }, + }, + { + range: { + '@timestamp': { + gte: from, + lte: to, + }, + }, + }, + ], + }, + }, + size: 0, + aggs: { + [HOSTS_BY_SEVERITY_AGG]: { + terms: { + field: 'host.name', + order: [ + { + 'critical.doc_count': 'desc', + }, + { + 'high.doc_count': 'desc', + }, + { + 'medium.doc_count': 'desc', + }, + { + 'low.doc_count': 'desc', + }, + ], + size: 4, + }, + aggs: { + critical: { + filter: { + term: { + 'kibana.alert.severity': 'critical', + }, + }, + }, + high: { + filter: { + term: { + 'kibana.alert.severity': 'high', + }, + }, + }, + medium: { + filter: { + term: { + 'kibana.alert.severity': 'medium', + }, + }, + }, + low: { + filter: { + term: { + 'kibana.alert.severity': 'low', + }, + }, + }, + }, + }, + }, +}); + +interface SeverityContainer { + doc_count: number; +} +interface AlertBySeverityBucketData extends GenericBuckets { + low: SeverityContainer; + medium: SeverityContainer; + high: SeverityContainer; + critical: SeverityContainer; +} + +interface AlertCountersBySeverityAndHostAggregation { + [HOSTS_BY_SEVERITY_AGG]: { + buckets: AlertBySeverityBucketData[]; + }; +} + +function parseHostsData( + rawAggregation: AlertCountersBySeverityAndHostAggregation +): HostAlertsItem[] { + const buckets = rawAggregation?.[HOSTS_BY_SEVERITY_AGG].buckets ?? []; + + return buckets.reduce((accumalatedAlertsByHost, currentHost) => { + return [ + ...accumalatedAlertsByHost, + { + hostName: currentHost.key || '—', + totalAlerts: currentHost.doc_count, + low: currentHost.low.doc_count, + medium: currentHost.medium.doc_count, + high: currentHost.high.doc_count, + critical: currentHost.critical.doc_count, + }, + ]; + }, []); +} diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.test.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.test.tsx index f41e05a2c04dc..dafd1ca965a3f 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.test.tsx @@ -5,13 +5,15 @@ * 2.0. */ +import moment from 'moment'; import React from 'react'; + import { render } from '@testing-library/react'; + +import { SecurityPageName } from '../../../../../common/constants'; import { TestProviders } from '../../../../common/mock'; import { RuleAlertsTable, RuleAlertsTableProps } from './rule_alerts_table'; import { RuleAlertsItem, UseRuleAlertsItems } from './use_rule_alerts_items'; -import moment from 'moment'; -import { SecurityPageName } from '../../../../../common/constants'; const mockGetAppUrl = jest.fn(); jest.mock('../../../../common/lib/kibana/hooks', () => { diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx index a5ddfe25dd985..550e04753e886 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx @@ -30,6 +30,7 @@ import { SecurityPageName } from '../../../../../common/constants'; import { useQueryToggle } from '../../../../common/containers/query_toggle'; import { HoverVisibilityContainer } from '../../../../common/components/hover_visibility_container'; import { BUTTON_CLASS as INPECT_BUTTON_CLASS } from '../../../../common/components/inspect'; +import { FormattedCount } from '../../../../common/components/formatted_number'; export interface RuleAlertsTableProps { signalIndexName: string | null; @@ -78,6 +79,7 @@ export const getTableColumns: GetTableColumns = ({ getAppUrl, navigateTo }) => [ field: 'alert_count', name: i18n.RULE_ALERTS_COLUMN_ALERT_COUNT, 'data-test-subj': 'severityRuleAlertsTable-alertCount', + render: (alertCount: number) => , }, { field: 'severity', diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/use_rule_alerts_items.test.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/use_rule_alerts_items.test.ts index 85d4c8e5b6a93..264c1cf404219 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/use_rule_alerts_items.test.ts +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/use_rule_alerts_items.test.ts @@ -6,7 +6,7 @@ */ import { renderHook } from '@testing-library/react-hooks'; -import { TestProviders } from '../../../../common/mock'; + import { from, mockSeverityRuleAlertsResponse, @@ -50,16 +50,12 @@ jest.mock('../../../../common/containers/use_global_time', () => { // helper function to render the hook const renderUseRuleAlertsItems = (props: Partial = {}) => - renderHook>( - () => - useRuleAlertsItems({ - queryId: 'test', - signalIndexName: 'signal-alerts', - ...props, - }), - { - wrapper: TestProviders, - } + renderHook>(() => + useRuleAlertsItems({ + queryId: 'test', + signalIndexName: 'signal-alerts', + ...props, + }) ); describe('useRuleAlertsItems', () => { @@ -103,7 +99,6 @@ describe('useRuleAlertsItems', () => { const newDateNow = new Date('2022-04-08T14:00:00.000Z').valueOf(); mockDateNow.mockReturnValue(newDateNow); // setUpdatedAt call mockDateNow.mockReturnValueOnce(dateNow); // initialization call - mockUseQueryAlerts.mockReturnValue({ ...defaultUseQueryAlertsReturn, data: mockSeverityRuleAlertsResponse, diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/translations.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/translations.ts index c2c5b412f1a9d..d013ab258631a 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/translations.ts +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/translations.ts @@ -31,26 +31,23 @@ export const STATUS_LOW_LABEL = i18n.translate( defaultMessage: 'Low', } ); -export const STATUS_OPEN = i18n.translate( - 'xpack.securitySolution.detectionResponse.alertsByStatus.donut.title.open', - { - defaultMessage: 'Open', - } -); export const STATUS_ACKNOWLEDGED = i18n.translate( - 'xpack.securitySolution.detectionResponse.alertsByStatus.donut.title.acknowledged', + 'xpack.securitySolution.detectionResponse.alertsByStatus.status.acknowledged', { defaultMessage: 'Acknowledged', } ); +export const STATUS_OPEN = i18n.translate('xpack.securitySolution.detectionResponse.status.open', { + defaultMessage: 'Open', +}); export const STATUS_CLOSED = i18n.translate( - 'xpack.securitySolution.detectionResponse.alertsByStatus.donut.title.closed', + 'xpack.securitySolution.detectionResponse.status.closed', { defaultMessage: 'Closed', } ); export const STATUS_IN_PROGRESS = i18n.translate( - 'xpack.securitySolution.detectionResponse.alertsByStatus.donut.title.inProgress', + 'xpack.securitySolution.detectionResponse.status.inProgress', { defaultMessage: 'In progress', } @@ -60,30 +57,72 @@ export const ALERTS = (totalAlerts: number) => values: { totalAlerts }, defaultMessage: 'total {totalAlerts, plural, =1 {alert} other {alerts}}', }); -export const ALERTS_TITLE = i18n.translate( - 'xpack.securitySolution.detectionResponse.alertsByStatus.title', - { - defaultMessage: 'Alerts', - } -); +export const ALERTS_TEXT = i18n.translate('xpack.securitySolution.detectionResponse.alerts', { + defaultMessage: 'Alerts', +}); export const UPDATING = i18n.translate('xpack.securitySolution.detectionResponse.updating', { defaultMessage: 'Updating...', }); export const UPDATED = i18n.translate('xpack.securitySolution.detectionResponse.updated', { defaultMessage: 'Updated', }); +export const CASES = (totalCases: number) => + i18n.translate('xpack.securitySolution.detectionResponse.casesByStatus.totalCases', { + values: { totalCases }, + defaultMessage: 'total {totalCases, plural, =1 {case} other {cases}}', + }); +export const CASES_BY_STATUS_SECTION_TITLE = i18n.translate( + 'xpack.securitySolution.detectionResponse.casesByStatusSectionTitle', + { + defaultMessage: 'Cases', + } +); + +export const VIEW_CASES = i18n.translate('xpack.securitySolution.detectionResponse.viewCases', { + defaultMessage: 'View cases', +}); + export const RULE_ALERTS_SECTION_TITLE = i18n.translate( 'xpack.securitySolution.detectionResponse.ruleAlertsSectionTitle', { defaultMessage: 'Open alerts by rule', } ); + +export const HOST_ALERTS_SECTION_TITLE = i18n.translate( + 'xpack.securitySolution.detectionResponse.hostAlertsSectionTitle', + { + defaultMessage: 'Vulnerable hosts by severity', + } +); + +export const USER_ALERTS_SECTION_TITLE = i18n.translate( + 'xpack.securitySolution.detectionResponse.userAlertsSectionTitle', + { + defaultMessage: 'Vulnerable users by severity', + } +); + +export const CASES_TABLE_SECTION_TITLE = i18n.translate( + 'xpack.securitySolution.detectionResponse.caseSectionTitle', + { + defaultMessage: 'Recently created cases', + } +); + export const NO_ALERTS_FOUND = i18n.translate( 'xpack.securitySolution.detectionResponse.noRuleAlerts', { defaultMessage: 'No alerts to display', } ); + +export const NO_CASES_FOUND = i18n.translate( + 'xpack.securitySolution.detectionResponse.noRecentCases', + { + defaultMessage: 'No cases to display', + } +); export const RULE_ALERTS_COLUMN_RULE_NAME = i18n.translate( 'xpack.securitySolution.detectionResponse.ruleAlertsColumnRuleName', { @@ -127,3 +166,80 @@ export const OPEN_ALL_ALERTS_BUTTON = i18n.translate( defaultMessage: 'View all open alerts', } ); + +export const VIEW_ALL_USER_ALERTS = i18n.translate( + 'xpack.securitySolution.detectionResponse.viewAllUserAlerts', + { + defaultMessage: 'View all users', + } +); + +export const VIEW_RECENT_CASES = i18n.translate( + 'xpack.securitySolution.detectionResponse.viewRecentCases', + { + defaultMessage: 'View recent cases', + } +); + +export const VIEW_ALL_HOST_ALERTS = i18n.translate( + 'xpack.securitySolution.detectionResponse.viewAllHostAlerts', + { + defaultMessage: 'View all hosts', + } +); + +export const HOST_ALERTS_HOSTNAME_COLUMN = i18n.translate( + 'xpack.securitySolution.detectionResponse.hostAlertsHostName', + { + defaultMessage: 'Host name', + } +); + +export const USER_ALERTS_USERNAME_COLUMN = i18n.translate( + 'xpack.securitySolution.detectionResponse.userAlertsUserName', + { + defaultMessage: 'User name', + } +); + +export const CASES_TABLE_COLUMN_NAME = i18n.translate( + 'xpack.securitySolution.detectionResponse.caseColumnName', + { + defaultMessage: 'Name', + } +); + +export const CASES_TABLE_COLUMN_NOTE = i18n.translate( + 'xpack.securitySolution.detectionResponse.caseColumnNote', + { + defaultMessage: 'Note', + } +); + +export const CASES_TABLE_COLUMN_TIME = i18n.translate( + 'xpack.securitySolution.detectionResponse.caseColumnTime', + { + defaultMessage: 'Time', + } +); + +export const CASES_TABLE_COLUMN_CREATED_BY = i18n.translate( + 'xpack.securitySolution.detectionResponse.caseColumnCreatedBy', + { + defaultMessage: 'Created by', + } +); + +export const CASES_TABLE_COLUMN_STATUS = i18n.translate( + 'xpack.securitySolution.detectionResponse.caseColumnStatus', + { + defaultMessage: 'Status', + } +); + +export const ERROR_MESSAGE_CASES = i18n.translate( + 'xpack.securitySolution.detectionResponse.errorMessage', + { + defaultMessage: 'Error fetching case data', + } +); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/index.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/index.ts new file mode 100644 index 0000000000000..b14cd15e55f18 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { UserAlertsTable } from './user_alerts_table'; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/mock_data.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/mock_data.ts new file mode 100644 index 0000000000000..e10bf1b05f032 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/mock_data.ts @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { buildVulnerableUserAggregationQuery } from './use_user_alerts_items'; + +export const mockVulnerableUsersBySeverityResult = { + aggregations: { + usersBySeverity: { + buckets: [ + { + key: 'crffn20qcs', + doc_count: 4, + high: { + doc_count: 1, + }, + critical: { + doc_count: 4, + }, + low: { + doc_count: 1, + }, + medium: { + doc_count: 1, + }, + }, + { + key: 'd058hziijl', + doc_count: 4, + high: { + doc_count: 11, + }, + critical: { + doc_count: 1, + }, + low: { + doc_count: 1, + }, + medium: { + doc_count: 1, + }, + }, + { + key: 'nenha4bdhv', + doc_count: 4, + high: { + doc_count: 1, + }, + critical: { + doc_count: 1, + }, + low: { + doc_count: 3, + }, + medium: { + doc_count: 3, + }, + }, + { + key: 'u68nq414uw', + doc_count: 2, + high: { + doc_count: 1, + }, + critical: { + doc_count: 0, + }, + low: { + doc_count: 10, + }, + medium: { + doc_count: 0, + }, + }, + ], + }, + }, +}; + +export const parsedVulnerableUserAlertsResult = [ + { + totalAlerts: 4, + critical: 4, + high: 1, + userName: 'crffn20qcs', + low: 1, + medium: 1, + }, + { + totalAlerts: 4, + critical: 1, + high: 11, + userName: 'd058hziijl', + low: 1, + medium: 1, + }, + { + totalAlerts: 4, + critical: 1, + high: 1, + userName: 'nenha4bdhv', + low: 3, + medium: 3, + }, + { + totalAlerts: 2, + critical: 0, + high: 1, + userName: 'u68nq414uw', + low: 10, + medium: 0, + }, +]; + +export const mockQuery = () => ({ + query: buildVulnerableUserAggregationQuery({ + from: '2020-07-07T08:20:18.966Z', + to: '2020-07-08T08:20:18.966Z', + }), + indexName: 'signal-alerts', + skip: false, +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/use_user_alerts_items.test.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/use_user_alerts_items.test.ts new file mode 100644 index 0000000000000..22ade5d3341c7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/use_user_alerts_items.test.ts @@ -0,0 +1,126 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; + +import { + mockQuery, + mockVulnerableUsersBySeverityResult, + parsedVulnerableUserAlertsResult, +} from './mock_data'; +import { useUserAlertsItems } from './use_user_alerts_items'; + +import type { UseUserAlertsItems, UseUserAlertsItemsProps } from './use_user_alerts_items'; + +const signalIndexName = 'signal-alerts'; + +const dateNow = new Date('2022-04-08T12:00:00.000Z').valueOf(); +const mockDateNow = jest.fn().mockReturnValue(dateNow); +Date.now = jest.fn(() => mockDateNow()) as unknown as DateConstructor['now']; + +const defaultUseQueryAlertsReturn = { + loading: false, + data: null, + setQuery: () => {}, + response: '', + request: '', + refetch: () => {}, +}; +const mockUseQueryAlerts = jest.fn().mockReturnValue(defaultUseQueryAlertsReturn); +jest.mock('../../../../detections/containers/detection_engine/alerts/use_query', () => { + return { + useQueryAlerts: (...props: unknown[]) => mockUseQueryAlerts(...props), + }; +}); + +const from = '2020-07-07T08:20:18.966Z'; +const to = '2020-07-08T08:20:18.966Z'; + +const mockUseGlobalTime = jest + .fn() + .mockReturnValue({ from, to, setQuery: jest.fn(), deleteQuery: jest.fn() }); +jest.mock('../../../../common/containers/use_global_time', () => { + return { + useGlobalTime: (...props: unknown[]) => mockUseGlobalTime(...props), + }; +}); + +const renderUseUserAlertsItems = (overrides: Partial = {}) => + renderHook>(() => + useUserAlertsItems({ + skip: false, + signalIndexName, + queryId: 'testing', + ...overrides, + }) + ); + +describe('useUserAlertsItems', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockDateNow.mockReturnValue(dateNow); + mockUseQueryAlerts.mockReturnValue(defaultUseQueryAlertsReturn); + }); + + it('should return default values', () => { + const { result } = renderUseUserAlertsItems(); + + expect(result.current).toEqual({ + items: [], + isLoading: false, + updatedAt: dateNow, + }); + + expect(mockUseQueryAlerts).toBeCalledWith(mockQuery()); + }); + + it('should return parsed items', () => { + mockUseQueryAlerts.mockReturnValue({ + ...defaultUseQueryAlertsReturn, + data: mockVulnerableUsersBySeverityResult, + }); + + const { result } = renderUseUserAlertsItems(); + + expect(result.current).toEqual({ + items: parsedVulnerableUserAlertsResult, + isLoading: false, + updatedAt: dateNow, + }); + }); + + it('should return new updatedAt', () => { + const newDateNow = new Date('2022-04-08T14:00:00.000Z').valueOf(); + mockDateNow.mockReturnValue(newDateNow); // setUpdatedAt call + mockDateNow.mockReturnValueOnce(dateNow); // initialization call + + mockUseQueryAlerts.mockReturnValue({ + ...defaultUseQueryAlertsReturn, + data: mockVulnerableUsersBySeverityResult, + }); + + const { result } = renderUseUserAlertsItems(); + expect(mockDateNow).toHaveBeenCalled(); + expect(result.current).toEqual({ + items: parsedVulnerableUserAlertsResult, + isLoading: false, + updatedAt: newDateNow, + }); + }); + + it('should skip the query', () => { + const { result } = renderUseUserAlertsItems({ skip: true }); + + expect(mockUseQueryAlerts).toBeCalledWith({ ...mockQuery(), skip: true }); + + expect(result.current).toEqual({ + items: [], + isLoading: false, + updatedAt: dateNow, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/use_user_alerts_items.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/use_user_alerts_items.ts new file mode 100644 index 0000000000000..3e2778f82cde4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/use_user_alerts_items.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 { useCallback, useEffect, useState } from 'react'; + +import { useQueryInspector } from '../../../../common/components/page/manage_query'; +import { useGlobalTime } from '../../../../common/containers/use_global_time'; +import { GenericBuckets } from '../../../../../common/search_strategy'; +import { useQueryAlerts } from '../../../../detections/containers/detection_engine/alerts/use_query'; + +const USERS_BY_SEVERITY_AGG = 'usersBySeverity'; + +interface TimeRange { + from: string; + to: string; +} + +export interface UseUserAlertsItemsProps { + skip: boolean; + queryId: string; + signalIndexName: string | null; +} + +export interface UserAlertsItem { + userName: string; + totalAlerts: number; + low: number; + medium: number; + high: number; + critical: number; +} + +export type UseUserAlertsItems = (props: UseUserAlertsItemsProps) => { + items: UserAlertsItem[]; + isLoading: boolean; + updatedAt: number; +}; + +export const useUserAlertsItems: UseUserAlertsItems = ({ skip, queryId, signalIndexName }) => { + const [updatedAt, setUpdatedAt] = useState(Date.now()); + const [items, setItems] = useState([]); + + const { to, from, setQuery: setGlobalQuery, deleteQuery } = useGlobalTime(); + + const { + setQuery, + data, + loading, + request, + response, + refetch: refetchQuery, + } = useQueryAlerts<{}, AlertCountersBySeverityAggregation>({ + query: buildVulnerableUserAggregationQuery({ from, to }), + indexName: signalIndexName, + skip, + }); + + useEffect(() => { + setQuery(buildVulnerableUserAggregationQuery({ from, to })); + }, [setQuery, from, to]); + + useEffect(() => { + if (data == null || !data.aggregations) { + setItems([]); + } else { + setItems(parseUsersData(data.aggregations)); + } + setUpdatedAt(Date.now()); + }, [data]); + + const refetch = useCallback(() => { + if (!skip && refetchQuery) { + refetchQuery(); + } + }, [skip, refetchQuery]); + + useQueryInspector({ + deleteQuery, + inspect: { + dsl: [request], + response: [response], + }, + refetch, + setQuery: setGlobalQuery, + queryId, + loading, + }); + return { items, isLoading: loading, updatedAt }; +}; + +export const buildVulnerableUserAggregationQuery = ({ from, to }: TimeRange) => ({ + query: { + bool: { + filter: [ + { + term: { + 'kibana.alert.workflow_status': 'open', + }, + }, + { + range: { + '@timestamp': { + gte: from, + lte: to, + }, + }, + }, + ], + }, + }, + size: 0, + aggs: { + [USERS_BY_SEVERITY_AGG]: { + terms: { + field: 'user.name', + order: [ + { + 'critical.doc_count': 'desc', + }, + { + 'high.doc_count': 'desc', + }, + { + 'medium.doc_count': 'desc', + }, + { + 'low.doc_count': 'desc', + }, + ], + size: 4, + }, + aggs: { + critical: { + filter: { + term: { + 'kibana.alert.severity': 'critical', + }, + }, + }, + high: { + filter: { + term: { + 'kibana.alert.severity': 'high', + }, + }, + }, + medium: { + filter: { + term: { + 'kibana.alert.severity': 'medium', + }, + }, + }, + low: { + filter: { + term: { + 'kibana.alert.severity': 'low', + }, + }, + }, + }, + }, + }, +}); + +interface SeverityContainer { + doc_count: number; +} +interface AlertBySeverityBucketData extends GenericBuckets { + low: SeverityContainer; + medium: SeverityContainer; + high: SeverityContainer; + critical: SeverityContainer; +} + +interface AlertCountersBySeverityAggregation { + [USERS_BY_SEVERITY_AGG]: { + buckets: AlertBySeverityBucketData[]; + }; +} + +function parseUsersData(rawAggregation: AlertCountersBySeverityAggregation): UserAlertsItem[] { + const buckets = rawAggregation?.[USERS_BY_SEVERITY_AGG].buckets ?? []; + + return buckets.reduce((accumalatedAlertsByUser, currentUser) => { + return [ + ...accumalatedAlertsByUser, + { + userName: currentUser.key || '—', + totalAlerts: currentUser.doc_count, + low: currentUser.low.doc_count, + medium: currentUser.medium.doc_count, + high: currentUser.high.doc_count, + critical: currentUser.critical.doc_count, + }, + ]; + }, []); +} diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.test.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.test.tsx new file mode 100644 index 0000000000000..74c266243dc56 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.test.tsx @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { render } from '@testing-library/react'; + +import { TestProviders } from '../../../../common/mock'; +import { parsedVulnerableUserAlertsResult } from './mock_data'; +import { UseUserAlertsItems } from './use_user_alerts_items'; +import { UserAlertsTable } from './user_alerts_table'; + +const mockGetAppUrl = jest.fn(); +jest.mock('../../../../common/lib/kibana/hooks', () => { + const original = jest.requireActual('../../../../common/lib/kibana/hooks'); + return { + ...original, + useNavigation: () => ({ + getAppUrl: mockGetAppUrl, + }), + }; +}); + +type UseUserAlertsItemsReturn = ReturnType; +const defaultUseUserAlertsItemsReturn: UseUserAlertsItemsReturn = { + items: [], + isLoading: false, + updatedAt: Date.now(), +}; +const mockUseUserAlertsItems = jest.fn(() => defaultUseUserAlertsItemsReturn); +const mockUseUserAlertsItemsReturn = (overrides: Partial) => { + mockUseUserAlertsItems.mockReturnValueOnce({ ...defaultUseUserAlertsItemsReturn, ...overrides }); +}; + +jest.mock('./use_user_alerts_items', () => ({ + useUserAlertsItems: () => mockUseUserAlertsItems(), +})); + +const renderComponent = () => + render( + + + + ); + +describe('UserAlertsTable', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should render empty table', () => { + const { getByText, getByTestId } = renderComponent(); + + expect(getByTestId('severityUserAlertsPanel')).toBeInTheDocument(); + expect(getByText('No alerts to display')).toBeInTheDocument(); + expect(getByTestId('severityUserAlertsButton')).toBeInTheDocument(); + }); + + it('should render a loading table', () => { + mockUseUserAlertsItemsReturn({ isLoading: true }); + const { getByText, getByTestId } = renderComponent(); + + expect(getByText('Updating...')).toBeInTheDocument(); + expect(getByTestId('severityUserAlertsButton')).toBeInTheDocument(); + expect(getByTestId('severityUserAlertsTable')).toHaveClass('euiBasicTable-loading'); + }); + + it('should render the updated at subtitle', () => { + mockUseUserAlertsItemsReturn({ isLoading: false }); + const { getByText } = renderComponent(); + + expect(getByText('Updated now')).toBeInTheDocument(); + }); + + it('should render the table columns', () => { + mockUseUserAlertsItemsReturn({ items: parsedVulnerableUserAlertsResult }); + const { getAllByRole } = renderComponent(); + + const columnHeaders = getAllByRole('columnheader'); + expect(columnHeaders.at(0)).toHaveTextContent('User name'); + expect(columnHeaders.at(1)).toHaveTextContent('Alerts'); + expect(columnHeaders.at(2)).toHaveTextContent('Critical'); + expect(columnHeaders.at(3)).toHaveTextContent('High'); + expect(columnHeaders.at(4)).toHaveTextContent('Medium'); + expect(columnHeaders.at(5)).toHaveTextContent('Low'); + }); + + it('should render the table items', () => { + mockUseUserAlertsItemsReturn({ items: [parsedVulnerableUserAlertsResult[0]] }); + const { getByTestId } = renderComponent(); + + expect(getByTestId('userSeverityAlertsTable-userName')).toHaveTextContent('crffn20qcs'); + expect(getByTestId('userSeverityAlertsTable-totalAlerts')).toHaveTextContent('4'); + expect(getByTestId('userSeverityAlertsTable-critical')).toHaveTextContent('4'); + expect(getByTestId('userSeverityAlertsTable-high')).toHaveTextContent('1'); + expect(getByTestId('userSeverityAlertsTable-medium')).toHaveTextContent('1'); + expect(getByTestId('userSeverityAlertsTable-low')).toHaveTextContent('1'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx new file mode 100644 index 0000000000000..0416bb48e13e1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx @@ -0,0 +1,156 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo } from 'react'; +import styled from 'styled-components'; + +import { + EuiBasicTable, + EuiBasicTableColumn, + EuiButton, + EuiEmptyPrompt, + EuiHealth, + EuiPanel, + EuiSpacer, +} from '@elastic/eui'; + +import { SecurityPageName } from '../../../../app/types'; +import { FormattedCount } from '../../../../common/components/formatted_number'; +import { HeaderSection } from '../../../../common/components/header_section'; +import { HoverVisibilityContainer } from '../../../../common/components/hover_visibility_container'; +import { BUTTON_CLASS as INPECT_BUTTON_CLASS } from '../../../../common/components/inspect'; +import { UserDetailsLink } from '../../../../common/components/links'; +import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { useNavigation, NavigateTo, GetAppUrl } from '../../../../common/lib/kibana'; +import * as i18n from '../translations'; +import { LastUpdatedAt, SEVERITY_COLOR } from '../util'; +import { UserAlertsItem, useUserAlertsItems } from './use_user_alerts_items'; + +export interface UserAlertsTableProps { + signalIndexName: string | null; +} + +type GetTableColumns = (params: { + getAppUrl: GetAppUrl; + navigateTo: NavigateTo; +}) => Array>; + +const DETECTION_RESPONSE_USER_SEVERITY_QUERY_ID = 'vulnerableUsersBySeverityQuery'; + +// To Do remove this styled component once togglequery is updated: #131405 +const StyledEuiPanel = styled(EuiPanel)` + height: fit-content; +`; + +export const UserAlertsTable = React.memo(({ signalIndexName }: UserAlertsTableProps) => { + const { getAppUrl, navigateTo } = useNavigation(); + const { toggleStatus, setToggleStatus } = useQueryToggle( + DETECTION_RESPONSE_USER_SEVERITY_QUERY_ID + ); + const { items, isLoading, updatedAt } = useUserAlertsItems({ + skip: !toggleStatus, + queryId: DETECTION_RESPONSE_USER_SEVERITY_QUERY_ID, + signalIndexName, + }); + + const navigateToAlerts = useCallback(() => { + navigateTo({ deepLinkId: SecurityPageName.users }); + }, [navigateTo]); + + const columns = useMemo( + () => getTableColumns({ getAppUrl, navigateTo }), + [getAppUrl, navigateTo] + ); + + return ( + + + } + /> + + {toggleStatus && ( + <> + {i18n.NO_ALERTS_FOUND}} titleSize="xs" /> + } + /> + + + {i18n.VIEW_ALL_USER_ALERTS} + + + )} + + + ); +}); + +UserAlertsTable.displayName = 'UserAlertsTable'; + +const getTableColumns: GetTableColumns = ({ getAppUrl, navigateTo }) => [ + { + field: 'userName', + name: i18n.USER_ALERTS_USERNAME_COLUMN, + truncateText: true, + textOnly: true, + 'data-test-subj': 'userSeverityAlertsTable-userName', + render: (userName: string) => , + }, + { + field: 'totalAlerts', + name: i18n.ALERTS_TEXT, + 'data-test-subj': 'userSeverityAlertsTable-totalAlerts', + render: (totalAlerts: number) => , + }, + { + field: 'critical', + name: i18n.STATUS_CRITICAL_LABEL, + render: (count: number) => ( + + + + ), + }, + { + field: 'high', + name: i18n.STATUS_HIGH_LABEL, + render: (count: number) => ( + + + + ), + }, + { + field: 'medium', + name: i18n.STATUS_MEDIUM_LABEL, + render: (count: number) => ( + + {count} + + ), + }, + { + field: 'low', + name: i18n.STATUS_LOW_LABEL, + render: (count: number) => ( + + + + ), + }, +]; diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/index.test.tsx index 575ab0057073e..060c6b3396a6b 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/index.test.tsx @@ -20,17 +20,18 @@ import { mockGlobalState, SUB_PLUGINS_REDUCER, } from '../../../common/mock'; -import { useRiskyHostsDashboardButtonHref } from '../../containers/overview_risky_host_links/use_risky_hosts_dashboard_button_href'; + import { useRiskyHostsDashboardLinks } from '../../containers/overview_risky_host_links/use_risky_hosts_dashboard_links'; import { useHostRiskScore } from '../../../risk_score/containers'; +import { useDashboardButtonHref } from '../../../common/hooks/use_dashboard_button_href'; jest.mock('../../../common/lib/kibana'); jest.mock('../../../risk_score/containers'); const useHostRiskScoreMock = useHostRiskScore as jest.Mock; -jest.mock('../../containers/overview_risky_host_links/use_risky_hosts_dashboard_button_href'); -const useRiskyHostsDashboardButtonHrefMock = useRiskyHostsDashboardButtonHref as jest.Mock; +jest.mock('../../../common/hooks/use_dashboard_button_href'); +const useRiskyHostsDashboardButtonHrefMock = useDashboardButtonHref as jest.Mock; useRiskyHostsDashboardButtonHrefMock.mockReturnValue({ buttonHref: '/test' }); jest.mock('../../containers/overview_risky_host_links/use_risky_hosts_dashboard_links'); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_enabled_module.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_enabled_module.test.tsx index 65c22d634cf27..5cda93859fe16 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_enabled_module.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_enabled_module.test.tsx @@ -18,15 +18,16 @@ import { mockGlobalState, SUB_PLUGINS_REDUCER, } from '../../../common/mock'; -import { useRiskyHostsDashboardButtonHref } from '../../containers/overview_risky_host_links/use_risky_hosts_dashboard_button_href'; + import { useRiskyHostsDashboardLinks } from '../../containers/overview_risky_host_links/use_risky_hosts_dashboard_links'; import { mockTheme } from '../overview_cti_links/mock'; import { RiskyHostsEnabledModule } from './risky_hosts_enabled_module'; +import { useDashboardButtonHref } from '../../../common/hooks/use_dashboard_button_href'; jest.mock('../../../common/lib/kibana'); -jest.mock('../../containers/overview_risky_host_links/use_risky_hosts_dashboard_button_href'); -const useRiskyHostsDashboardButtonHrefMock = useRiskyHostsDashboardButtonHref as jest.Mock; +jest.mock('../../../common/hooks/use_dashboard_button_href'); +const useRiskyHostsDashboardButtonHrefMock = useDashboardButtonHref as jest.Mock; useRiskyHostsDashboardButtonHrefMock.mockReturnValue({ buttonHref: '/test' }); jest.mock('../../containers/overview_risky_host_links/use_risky_hosts_dashboard_links'); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_enabled_module.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_enabled_module.tsx index cdab0a1353e1f..91a7a3fc3fe5d 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_enabled_module.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_enabled_module.tsx @@ -8,9 +8,10 @@ import React, { useMemo } from 'react'; import { RiskyHostsPanelView } from './risky_hosts_panel_view'; import { LinkPanelListItem } from '../link_panel'; -import { useRiskyHostsDashboardButtonHref } from '../../containers/overview_risky_host_links/use_risky_hosts_dashboard_button_href'; import { useRiskyHostsDashboardLinks } from '../../containers/overview_risky_host_links/use_risky_hosts_dashboard_links'; import { HostsRiskScore } from '../../../../common/search_strategy'; +import { useDashboardButtonHref } from '../../../common/hooks/use_dashboard_button_href'; +import { RISKY_HOSTS_DASHBOARD_TITLE } from '../../../hosts/pages/navigation/constants'; const getListItemsFromHits = (items: HostsRiskScore[]): LinkPanelListItem[] => { return items.map(({ host, risk_stats: riskStats, risk: copy }) => ({ @@ -27,7 +28,7 @@ const RiskyHostsEnabledModuleComponent: React.FC<{ to: string; }> = ({ hostRiskScore, to, from }) => { const listItems = useMemo(() => getListItemsFromHits(hostRiskScore || []), [hostRiskScore]); - const { buttonHref } = useRiskyHostsDashboardButtonHref(to, from); + const { buttonHref } = useDashboardButtonHref({ to, from, title: RISKY_HOSTS_DASHBOARD_TITLE }); const { listItemsWithLinks } = useRiskyHostsDashboardLinks(to, from, listItems); return ( diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_button_href.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_button_href.ts deleted file mode 100644 index 5bc2087dc63ab..0000000000000 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_button_href.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { useState, useEffect } from 'react'; -import { SavedObjectAttributes } from '@kbn/securitysolution-io-ts-alerting-types'; -import { useKibana } from '../../../common/lib/kibana'; - -const DASHBOARD_REQUEST_BODY_SEARCH = '"Current Risk Score for Hosts"'; -export const DASHBOARD_REQUEST_BODY = { - type: 'dashboard', - search: DASHBOARD_REQUEST_BODY_SEARCH, - fields: ['title'], -}; - -export const useRiskyHostsDashboardButtonHref = (to: string, from: string) => { - const { - dashboard, - savedObjects: { client: savedObjectsClient }, - } = useKibana().services; - - const [buttonHref, setButtonHref] = useState(); - - useEffect(() => { - if (dashboard?.locator && savedObjectsClient) { - savedObjectsClient.find(DASHBOARD_REQUEST_BODY).then( - async (DashboardsSO?: { - savedObjects?: Array<{ - attributes?: SavedObjectAttributes; - id?: string; - }>; - }) => { - if (DashboardsSO?.savedObjects?.length) { - const dashboardUrl = await dashboard?.locator?.getUrl({ - dashboardId: DashboardsSO.savedObjects[0].id, - timeRange: { - to, - from, - }, - }); - setButtonHref(dashboardUrl); - } - } - ); - } - }, [dashboard, from, savedObjectsClient, to]); - - return { - buttonHref, - }; -}; diff --git a/x-pack/plugins/security_solution/public/overview/links.ts b/x-pack/plugins/security_solution/public/overview/links.ts new file mode 100644 index 0000000000000..89f75053b3d6f --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/links.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { + DASHBOARDS_PATH, + DETECTION_RESPONSE_PATH, + LANDING_PATH, + OVERVIEW_PATH, + SecurityPageName, +} from '../../common/constants'; +import { DASHBOARDS, DETECTION_RESPONSE, GETTING_STARTED, OVERVIEW } from '../app/translations'; +import { FEATURE, LinkItem } from '../common/links/types'; + +export const overviewLinks: LinkItem = { + id: SecurityPageName.overview, + title: OVERVIEW, + path: OVERVIEW_PATH, + globalNavEnabled: true, + features: [FEATURE.general], + globalSearchKeywords: [ + i18n.translate('xpack.securitySolution.appLinks.overview', { + defaultMessage: 'Overview', + }), + ], + globalNavOrder: 9000, +}; + +export const gettingStartedLinks: LinkItem = { + id: SecurityPageName.landing, + title: GETTING_STARTED, + path: LANDING_PATH, + globalNavEnabled: false, + features: [FEATURE.general], + globalSearchKeywords: [ + i18n.translate('xpack.securitySolution.appLinks.getStarted', { + defaultMessage: 'Getting started', + }), + ], + skipUrlState: true, +}; + +export const detectionResponseLinks: LinkItem = { + id: SecurityPageName.detectionAndResponse, + title: DETECTION_RESPONSE, + path: DETECTION_RESPONSE_PATH, + globalNavEnabled: false, + experimentalKey: 'detectionResponseEnabled', + features: [FEATURE.general], + globalSearchKeywords: [ + i18n.translate('xpack.securitySolution.appLinks.detectionAndResponse', { + defaultMessage: 'Detection & Response', + }), + ], +}; + +export const dashboardsLandingLinks: LinkItem = { + id: SecurityPageName.dashboardsLanding, + title: DASHBOARDS, + path: DASHBOARDS_PATH, + globalNavEnabled: false, + features: [FEATURE.general], + globalSearchKeywords: [ + i18n.translate('xpack.securitySolution.appLinks.dashboards', { + defaultMessage: 'Dashboards', + }), + ], + links: [overviewLinks, detectionResponseLinks], +}; diff --git a/x-pack/plugins/security_solution/public/overview/pages/detection_response.test.tsx b/x-pack/plugins/security_solution/public/overview/pages/detection_response.test.tsx index 8cd387978e97d..3b88d22eb4da9 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/detection_response.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/detection_response.test.tsx @@ -11,10 +11,29 @@ import { render } from '@testing-library/react'; import { DetectionResponse } from './detection_response'; import { TestProviders } from '../../common/mock'; +jest.mock('../components/detection_response/alerts_by_status', () => ({ + AlertsByStatus: () =>
, +})); + +jest.mock('../components/detection_response/cases_table', () => ({ + CasesTable: () =>
, +})); + +jest.mock('../components/detection_response/host_alerts_table', () => ({ + HostAlertsTable: () =>
, +})); + jest.mock('../components/detection_response/rule_alerts_table', () => ({ RuleAlertsTable: () =>
, })); -// TODO: add all sections mocks + +jest.mock('../components/detection_response/user_alerts_table', () => ({ + UserAlertsTable: () =>
, +})); + +jest.mock('../components/detection_response/cases_by_status', () => ({ + CasesByStatus: () =>
, +})); jest.mock('../../common/components/search_bar', () => ({ SiemSearchBar: () =>
, @@ -30,14 +49,22 @@ jest.mock('../../common/containers/sourcerer', () => ({ useSourcererDataView: () => mockUseSourcererDataView(), })); -const defaultUseUserInfoReturn = { - signalIndexName: '', - canUserREAD: true, +const defaultUseAlertsPrivilegesReturn = { + hasKibanaREAD: true, hasIndexRead: true, }; -const mockUseUserInfo = jest.fn(() => defaultUseUserInfoReturn); -jest.mock('../../detections/components/user_info', () => ({ - useUserInfo: () => mockUseUserInfo(), + +const defaultUseSignalIndexReturn = { + signalIndexName: '', +}; + +const mockUseSignalIndex = jest.fn(() => defaultUseSignalIndexReturn); +jest.mock('../../detections/containers/detection_engine/alerts/use_signal_index', () => ({ + useSignalIndex: () => mockUseSignalIndex(), +})); +const mockUseAlertsPrivileges = jest.fn(() => defaultUseAlertsPrivilegesReturn); +jest.mock('../../detections/containers/detection_engine/alerts/use_alerts_privileges', () => ({ + useAlertsPrivileges: () => mockUseAlertsPrivileges(), })); const defaultUseCasesPermissionsReturn = { read: true }; @@ -54,7 +81,8 @@ describe('DetectionResponse', () => { beforeEach(() => { jest.clearAllMocks(); mockUseSourcererDataView.mockReturnValue(defaultUseSourcererReturn); - mockUseUserInfo.mockReturnValue(defaultUseUserInfoReturn); + mockUseAlertsPrivileges.mockReturnValue(defaultUseAlertsPrivilegesReturn); + mockUseSignalIndex.mockReturnValue(defaultUseSignalIndexReturn); mockUseCasesPermissions.mockReturnValue(defaultUseCasesPermissionsReturn); }); @@ -114,9 +142,9 @@ describe('DetectionResponse', () => { }); it('should not render alerts data sections if user has not index read permission', () => { - mockUseUserInfo.mockReturnValue({ - ...defaultUseUserInfoReturn, + mockUseAlertsPrivileges.mockReturnValue({ hasIndexRead: false, + hasKibanaREAD: true, }); const result = render( @@ -128,15 +156,19 @@ describe('DetectionResponse', () => { ); expect(result.queryByTestId('detectionResponsePage')).toBeInTheDocument(); + expect(result.queryByTestId('mock_CasesTable')).toBeInTheDocument(); + expect(result.queryByTestId('mock_CasesByStatus')).toBeInTheDocument(); + expect(result.queryByTestId('mock_RuleAlertsTable')).not.toBeInTheDocument(); - // TODO: assert other alert sections are not in the document - // TODO: assert cases sections are in the document + expect(result.queryByTestId('mock_HostAlertsTable')).not.toBeInTheDocument(); + expect(result.queryByTestId('mock_UserAlertsTable')).not.toBeInTheDocument(); + expect(result.queryByTestId('mock_AlertsByStatus')).not.toBeInTheDocument(); }); it('should not render alerts data sections if user has not kibana read permission', () => { - mockUseUserInfo.mockReturnValue({ - ...defaultUseUserInfoReturn, - canUserREAD: false, + mockUseAlertsPrivileges.mockReturnValue({ + hasIndexRead: true, + hasKibanaREAD: false, }); const result = render( @@ -148,9 +180,13 @@ describe('DetectionResponse', () => { ); expect(result.queryByTestId('detectionResponsePage')).toBeInTheDocument(); + expect(result.queryByTestId('mock_CasesTable')).toBeInTheDocument(); + expect(result.queryByTestId('mock_CasesByStatus')).toBeInTheDocument(); + expect(result.queryByTestId('mock_RuleAlertsTable')).not.toBeInTheDocument(); - // TODO: assert all alert sections are not in the document - // TODO: assert all cases sections are in the document + expect(result.queryByTestId('mock_HostAlertsTable')).not.toBeInTheDocument(); + expect(result.queryByTestId('mock_UserAlertsTable')).not.toBeInTheDocument(); + expect(result.queryByTestId('mock_AlertsByStatus')).not.toBeInTheDocument(); }); it('should not render cases data sections if user has not cases read permission', () => { @@ -164,16 +200,20 @@ describe('DetectionResponse', () => { ); + expect(result.queryByTestId('mock_CasesTable')).not.toBeInTheDocument(); + expect(result.queryByTestId('mock_CasesByStatus')).not.toBeInTheDocument(); + expect(result.queryByTestId('detectionResponsePage')).toBeInTheDocument(); expect(result.queryByTestId('mock_RuleAlertsTable')).toBeInTheDocument(); - // TODO: assert all alert sections are in the document - // TODO: assert all cases sections are not in the document + expect(result.queryByTestId('mock_HostAlertsTable')).toBeInTheDocument(); + expect(result.queryByTestId('mock_UserAlertsTable')).toBeInTheDocument(); + expect(result.queryByTestId('mock_AlertsByStatus')).toBeInTheDocument(); }); it('should render page permissions message if user has any read permission', () => { mockUseCasesPermissions.mockReturnValue({ read: false }); - mockUseUserInfo.mockReturnValue({ - ...defaultUseUserInfoReturn, + mockUseAlertsPrivileges.mockReturnValue({ + hasKibanaREAD: true, hasIndexRead: false, }); diff --git a/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx b/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx index 2e0030bafa00e..fedcca0aac7b4 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx @@ -11,14 +11,20 @@ import { SecuritySolutionPageWrapper } from '../../common/components/page_wrappe import { SpyRoute } from '../../common/utils/route/spy_routes'; import { SecurityPageName } from '../../app/types'; import { useSourcererDataView } from '../../common/containers/sourcerer'; -import { useUserInfo } from '../../detections/components/user_info'; import { HeaderPage } from '../../common/components/header_page'; import { useKibana, useGetUserCasesPermissions } from '../../common/lib/kibana'; -import { RuleAlertsTable } from '../components/detection_response/rule_alerts_table'; -import { LandingPageComponent } from '../../common/components/landing_page'; -import * as i18n from './translations'; + import { EmptyPage } from '../../common/components/empty_page'; +import { LandingPageComponent } from '../../common/components/landing_page'; +import { useSignalIndex } from '../../detections/containers/detection_engine/alerts/use_signal_index'; +import { useAlertsPrivileges } from '../../detections/containers/detection_engine/alerts/use_alerts_privileges'; import { AlertsByStatus } from '../components/detection_response/alerts_by_status'; +import { HostAlertsTable } from '../components/detection_response/host_alerts_table'; +import { RuleAlertsTable } from '../components/detection_response/rule_alerts_table'; +import { UserAlertsTable } from '../components/detection_response/user_alerts_table'; +import * as i18n from './translations'; +import { CasesTable } from '../components/detection_response/cases_table'; +import { CasesByStatus } from '../components/detection_response/cases_by_status'; const NoPrivilegePage: React.FC = () => { const { docLinks } = useKibana().services; @@ -45,9 +51,10 @@ const NoPrivilegePage: React.FC = () => { const DetectionResponseComponent = () => { const { indicesExist, indexPattern, loading: isSourcererLoading } = useSourcererDataView(); - const { signalIndexName, canUserREAD, hasIndexRead } = useUserInfo(); + const { signalIndexName } = useSignalIndex(); + const { hasKibanaREAD, hasIndexRead } = useAlertsPrivileges(); const canReadCases = useGetUserCasesPermissions()?.read; - const canReadAlerts = canUserREAD && hasIndexRead; + const canReadAlerts = hasKibanaREAD && hasIndexRead; if (!canReadAlerts && !canReadCases) { return ; @@ -73,7 +80,11 @@ const DetectionResponseComponent = () => { )} - {canReadCases && {'[cases chart]'}} + {canReadCases && ( + + + + )} @@ -83,13 +94,21 @@ const DetectionResponseComponent = () => { )} - {canReadCases && {'[cases table]'}} + {canReadCases && ( + + + + )} {canReadAlerts && ( - {'[hosts table]'} - {'[users table]'} + + + + + + )} diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 1dc14ababd781..4b49c04f295a5 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -142,6 +142,12 @@ export class Plugin implements IPlugin { + // required to show the alert table inside cases + const { alertsTableConfigurationRegistry } = plugins.triggersActionsUi; + const { registerAlertsTableConfiguration } = + await this.lazyRegisterAlertsTableConfiguration(); + registerAlertsTableConfiguration(alertsTableConfigurationRegistry, this.storage); + const [coreStart, startPlugins] = await core.getStartServices(); const subPlugins = await this.startSubPlugins(this.storage, coreStart, startPlugins); const { renderApp } = await this.lazyApplicationDependencies(); @@ -216,6 +222,7 @@ export class Plugin implements IPlugin({ timerange, onlyLatest, @@ -111,7 +111,7 @@ export const useUserRiskScore = ({ sort, skip, pagination, - featureEnabled: usersFeatureEnabled, + featureEnabled: riskyUsersFeatureEnabled, defaultIndex, }); }; diff --git a/x-pack/plugins/security_solution/public/risk_score/containers/index.ts b/x-pack/plugins/security_solution/public/risk_score/containers/index.ts index 089c88aa9be37..ffe964b974776 100644 --- a/x-pack/plugins/security_solution/public/risk_score/containers/index.ts +++ b/x-pack/plugins/security_solution/public/risk_score/containers/index.ts @@ -12,12 +12,12 @@ export * from './kpi'; export const enum UserRiskScoreQueryId { USERS_BY_RISK = 'UsersByRisk', + USER_DETAILS_RISK_SCORE = 'UserDetailsRiskScore', } export const enum HostRiskScoreQueryId { DEFAULT = 'HostRiskScore', - HOST_RISK_SCORE_OVER_TIME = 'HostRiskScoreOverTimeQuery', - TOP_HOST_SCORE_CONTRIBUTORS = 'TopHostScoreContributorsQuery', + HOST_DETAILS_RISK_SCORE = 'HostDetailsRiskScore', OVERVIEW_RISKY_HOSTS = 'OverviewRiskyHosts', HOSTS_BY_RISK = 'HostsByRisk', } diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx index 6412567174c73..c62869c0f0746 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx @@ -275,6 +275,7 @@ export const QueryBarTimeline = memo( savedQuery={savedQuery} onSavedQuery={onSavedQuery} dataTestSubj={'timelineQueryInput'} + displayStyle="inPage" /> ); } diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx index 17dd6491f2326..c5cc33c18c1c4 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx @@ -62,24 +62,13 @@ interface Props { const SearchOrFilterContainer = styled.div` ${({ theme }) => `margin-top: ${theme.eui.euiSizeXS};`} - user-select: none; - .globalQueryBar { - padding: 0px; - .kbnQueryBar { - div:first-child { - margin-right: 0px; - } - } - .globalFilterGroup__wrapper.globalFilterGroup__wrapper-isVisible { - height: auto !important; - } - } + user-select: none; // This should not be here, it makes the entire page inaccessible `; SearchOrFilterContainer.displayName = 'SearchOrFilterContainer'; const ModeFlexItem = styled(EuiFlexItem)` - user-select: none; + user-select: none; // Again, why? `; ModeFlexItem.displayName = 'ModeFlexItem'; diff --git a/x-pack/plugins/security_solution/public/timelines/containers/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/containers/index.test.tsx index dd032016088b6..ee4af121dd054 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/index.test.tsx @@ -35,6 +35,7 @@ jest.mock('../../common/lib/kibana', () => ({ addError: jest.fn(), addSuccess: jest.fn(), addWarning: jest.fn(), + remove: jest.fn(), }), useKibana: jest.fn().mockReturnValue({ services: { diff --git a/x-pack/plugins/security_solution/public/timelines/links.ts b/x-pack/plugins/security_solution/public/timelines/links.ts new file mode 100644 index 0000000000000..1bdadb20cfa6d --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/links.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 { i18n } from '@kbn/i18n'; +import { SecurityPageName, TIMELINES_PATH } from '../../common/constants'; +import { TIMELINES } from '../app/translations'; +import { FEATURE, LinkItem } from '../common/links/types'; + +export const links: LinkItem = { + id: SecurityPageName.timelines, + title: TIMELINES, + path: TIMELINES_PATH, + globalNavEnabled: true, + features: [FEATURE.general], + globalSearchKeywords: [ + i18n.translate('xpack.securitySolution.appLinks.timelines', { + defaultMessage: 'Timelines', + }), + ], + globalNavOrder: 9005, + links: [ + { + id: SecurityPageName.timelinesTemplates, + title: i18n.translate('xpack.securitySolution.appLinks.timeline.templates', { + defaultMessage: 'Templates', + }), + path: `${TIMELINES_PATH}/template`, + }, + ], +}; diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 3fa7e964f6ca1..fca11f84a7abb 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -44,6 +44,7 @@ import type { Overview } from './overview'; import type { Rules } from './rules'; import type { Timelines } from './timelines'; import type { Management } from './management'; +import { LandingPages } from './landing_pages'; export interface SetupPlugins { home?: HomePublicPluginSetup; @@ -106,6 +107,7 @@ export interface SubPlugins { overview: Overview; timelines: Timelines; management: Management; + landingPages: LandingPages; } // TODO: find a better way to defined these types @@ -120,4 +122,5 @@ export interface StartedSubPlugins { overview: ReturnType; timelines: ReturnType; management: ReturnType; + landingPages: ReturnType; } diff --git a/x-pack/plugins/security_solution/public/users/components/constants.ts b/x-pack/plugins/security_solution/public/users/components/constants.ts new file mode 100644 index 0000000000000..059373662cfa8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/users/components/constants.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const RISKY_USERS_DOC_LINK = + 'https://www.github.com/elastic/detection-rules/blob/main/docs/experimental-machine-learning/user-risk-score.md'; diff --git a/x-pack/plugins/security_solution/public/users/components/kpi_users/index.tsx b/x-pack/plugins/security_solution/public/users/components/kpi_users/index.tsx index 11d577a037ddb..843ec0b7c871e 100644 --- a/x-pack/plugins/security_solution/public/users/components/kpi_users/index.tsx +++ b/x-pack/plugins/security_solution/public/users/components/kpi_users/index.tsx @@ -6,17 +6,47 @@ */ import React from 'react'; -import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; +import { EuiFlexItem, EuiFlexGroup, EuiSpacer, EuiLink } from '@elastic/eui'; import { UsersKpiProps } from './types'; import { HostsKpiAuthentications } from '../../../hosts/components/kpi_hosts/authentications'; import { TotalUsersKpi } from './total_users'; +import { useUserRiskScore } from '../../../risk_score/containers'; +import { CallOutSwitcher } from '../../../common/components/callouts'; +import * as i18n from './translations'; +import { RISKY_USERS_DOC_LINK } from '../constants'; export const UsersKpiComponent = React.memo( ({ filterQuery, from, indexNames, to, setQuery, skip, narrowDateRange }) => { + const [_, { isModuleEnabled }] = useUserRiskScore({}); + return ( <> + {isModuleEnabled === false && ( + <> + + {i18n.LEARN_MORE}{' '} + + {i18n.USER_RISK_DATA} + + + + ), + }} + /> + + + )} ( } ); -UsersKpiComponent.displayName = 'HostsKpiComponent'; +UsersKpiComponent.displayName = 'UsersKpiComponent'; diff --git a/x-pack/plugins/security_solution/public/users/components/kpi_users/translations.ts b/x-pack/plugins/security_solution/public/users/components/kpi_users/translations.ts new file mode 100644 index 0000000000000..8315b6dc21c19 --- /dev/null +++ b/x-pack/plugins/security_solution/public/users/components/kpi_users/translations.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 { i18n } from '@kbn/i18n'; + +export const ENABLE_USER_RISK_TEXT = i18n.translate( + 'xpack.securitySolution.kpiUser.enableUserRiskText', + { + defaultMessage: 'Enable user risk module to see more data', + } +); + +export const LEARN_MORE = i18n.translate('xpack.securitySolution.kpiUser.learnMore', { + defaultMessage: 'Learn more about', +}); + +export const USER_RISK_DATA = i18n.translate('xpack.securitySolution.kpiUser.userRiskData', { + defaultMessage: 'user risk data', +}); diff --git a/x-pack/plugins/security_solution/public/users/components/user_risk_information/index.test.tsx b/x-pack/plugins/security_solution/public/users/components/user_risk_information/index.test.tsx new file mode 100644 index 0000000000000..764d732fa2898 --- /dev/null +++ b/x-pack/plugins/security_solution/public/users/components/user_risk_information/index.test.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render, fireEvent } from '@testing-library/react'; +import React from 'react'; +import { UserRiskInformationButtonEmpty } from '.'; +import { TestProviders } from '../../../common/mock'; + +describe('User Risk Flyout', () => { + describe('UserRiskInformationButtonEmpty', () => { + it('renders', () => { + const { queryByTestId } = render(); + + expect(queryByTestId('open-risk-information-flyout-trigger')).toBeInTheDocument(); + }); + }); + + it('opens and displays table with 5 rows', () => { + const NUMBER_OF_ROWS = 1 + 5; // 1 header row + 5 severity rows + const { getByTestId, queryByTestId, queryAllByRole } = render( + + + + ); + + fireEvent.click(getByTestId('open-risk-information-flyout-trigger')); + + expect(queryByTestId('risk-information-table')).toBeInTheDocument(); + expect(queryAllByRole('row')).toHaveLength(NUMBER_OF_ROWS); + }); +}); diff --git a/x-pack/plugins/security_solution/public/users/components/user_risk_information/index.tsx b/x-pack/plugins/security_solution/public/users/components/user_risk_information/index.tsx new file mode 100644 index 0000000000000..066e3b01fbdd2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/users/components/user_risk_information/index.tsx @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + useGeneratedHtmlId, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiText, + EuiTitle, + EuiBasicTable, + EuiFlexGroup, + EuiFlexItem, + EuiFlyoutFooter, + EuiButton, + EuiSpacer, + EuiBasicTableColumn, + EuiButtonEmpty, + EuiLink, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React from 'react'; + +import * as i18n from './translations'; +import { useOnOpenCloseHandler } from '../../../helper_hooks'; +import { RiskScore } from '../../../common/components/severity/common'; +import { RiskSeverity } from '../../../../common/search_strategy'; +import { RISKY_USERS_DOC_LINK } from '../constants'; + +const tableColumns: Array> = [ + { + field: 'classification', + name: i18n.INFORMATION_CLASSIFICATION_HEADER, + render: (riskScore?: RiskSeverity) => { + if (riskScore != null) { + return ; + } + }, + }, + { + field: 'range', + name: i18n.INFORMATION_RISK_HEADER, + }, +]; + +interface TableItem { + range?: string; + classification: RiskSeverity; +} + +const tableItems: TableItem[] = [ + { classification: RiskSeverity.critical, range: i18n.CRITICAL_RISK_DESCRIPTION }, + { classification: RiskSeverity.high, range: '70 - 90 ' }, + { classification: RiskSeverity.moderate, range: '40 - 70' }, + { classification: RiskSeverity.low, range: '20 - 40' }, + { classification: RiskSeverity.unknown, range: i18n.UNKNOWN_RISK_DESCRIPTION }, +]; + +export const USER_RISK_INFO_BUTTON_CLASS = 'UserRiskInformation__button'; + +export const UserRiskInformationButtonEmpty = () => { + const [isFlyoutVisible, handleOnOpen, handleOnClose] = useOnOpenCloseHandler(); + + return ( + <> + + {i18n.INFO_BUTTON_TEXT} + + {isFlyoutVisible && } + + ); +}; + +const UserRiskInformationFlyout = ({ handleOnClose }: { handleOnClose: () => void }) => { + const simpleFlyoutTitleId = useGeneratedHtmlId({ + prefix: 'UserRiskInformation', + }); + + return ( + + + +

{i18n.TITLE}

+
+
+ + +

{i18n.INTRODUCTION}

+

{i18n.EXPLANATION_MESSAGE}

+
+ + + + + + + ), + }} + /> +
+ + + + + {i18n.CLOSE_BUTTON_LTEXT} + + + +
+ ); +}; diff --git a/x-pack/plugins/security_solution/public/users/components/user_risk_information/translations.ts b/x-pack/plugins/security_solution/public/users/components/user_risk_information/translations.ts new file mode 100644 index 0000000000000..dbf4ad96e486c --- /dev/null +++ b/x-pack/plugins/security_solution/public/users/components/user_risk_information/translations.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const INFORMATION_ARIA_LABEL = i18n.translate( + 'xpack.securitySolution.users.userRiskInformation.informationAriaLabel', + { + defaultMessage: 'Information', + } +); + +export const INFORMATION_CLASSIFICATION_HEADER = i18n.translate( + 'xpack.securitySolution.users.userRiskInformation.classificationHeader', + { + defaultMessage: 'Classification', + } +); + +export const INFORMATION_RISK_HEADER = i18n.translate( + 'xpack.securitySolution.users.userRiskInformation.riskHeader', + { + defaultMessage: 'User risk score range', + } +); + +export const UNKNOWN_RISK_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.users.userRiskInformation.unknownRiskDescription', + { + defaultMessage: 'Less than 20', + } +); + +export const CRITICAL_RISK_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.users.userRiskInformation.criticalRiskDescription', + { + defaultMessage: '90 and above', + } +); + +export const TITLE = i18n.translate('xpack.securitySolution.users.userRiskInformation.title', { + defaultMessage: 'How is user risk calculated?', +}); + +export const INTRODUCTION = i18n.translate( + 'xpack.securitySolution.users.userRiskInformation.introduction', + { + defaultMessage: + 'The User Risk Score capability surfaces risky users from within your environment.', + } +); + +export const EXPLANATION_MESSAGE = i18n.translate( + 'xpack.securitySolution.users.userRiskInformation.explanation', + { + defaultMessage: + 'This feature utilizes a transform, with a scripted metric aggregation to calculate user risk scores based on detection rule alerts with an "open" status, within a 5 day time window. The transform runs hourly to keep the score updated as new detection rule alerts stream in.', + } +); + +export const CLOSE_BUTTON_LTEXT = i18n.translate( + 'xpack.securitySolution.users.userRiskInformation.closeBtn', + { + defaultMessage: 'Close', + } +); + +export const INFO_BUTTON_TEXT = i18n.translate( + 'xpack.securitySolution.users.userRiskInformation.buttonLabel', + { + defaultMessage: 'How is risk score calculated?', + } +); diff --git a/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/columns.tsx b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/columns.tsx index c3b26aa1e44d3..3ea4d6a14c247 100644 --- a/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/columns.tsx @@ -22,6 +22,7 @@ import * as i18n from './translations'; import { RiskScore } from '../../../common/components/severity/common'; import { RiskSeverity } from '../../../../common/search_strategy'; import { UserDetailsLink } from '../../../common/components/links'; +import { UsersTableType } from '../../store/model'; export const getUserRiskScoreColumns = ({ dispatchSeverityUpdate, @@ -55,7 +56,7 @@ export const getUserRiskScoreColumns = ({ ) : ( - + ) } /> diff --git a/x-pack/plugins/security_solution/public/users/links.ts b/x-pack/plugins/security_solution/public/users/links.ts new file mode 100644 index 0000000000000..bd7bef4af8e82 --- /dev/null +++ b/x-pack/plugins/security_solution/public/users/links.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 { i18n } from '@kbn/i18n'; +import { SecurityPageName, USERS_PATH } from '../../common/constants'; +import { USERS } from '../app/translations'; +import { LinkItem } from '../common/links/types'; + +export const links: LinkItem = { + id: SecurityPageName.users, + title: USERS, + path: USERS_PATH, + globalNavEnabled: true, + experimentalKey: 'usersEnabled', + globalSearchKeywords: [ + i18n.translate('xpack.securitySolution.appLinks.users', { + defaultMessage: 'Users', + }), + ], + globalNavOrder: 9004, + links: [ + { + id: SecurityPageName.usersAuthentications, + title: i18n.translate('xpack.securitySolution.appLinks.users.authentications', { + defaultMessage: 'Authentications', + }), + path: `${USERS_PATH}/authentications`, + }, + { + id: SecurityPageName.usersAnomalies, + title: i18n.translate('xpack.securitySolution.appLinks.users.anomalies', { + defaultMessage: 'Anomalies', + }), + path: `${USERS_PATH}/anomalies`, + licenseType: 'gold', + }, + { + id: SecurityPageName.usersRisk, + title: i18n.translate('xpack.securitySolution.appLinks.users.risk', { + defaultMessage: 'Users by risk', + }), + path: `${USERS_PATH}/userRisk`, + experimentalKey: 'riskyUsersEnabled', + }, + { + id: SecurityPageName.usersEvents, + title: i18n.translate('xpack.securitySolution.appLinks.users.events', { + defaultMessage: 'Events', + }), + path: `${USERS_PATH}/events`, + }, + { + id: SecurityPageName.usersExternalAlerts, + title: i18n.translate('xpack.securitySolution.appLinks.users.externalAlerts', { + defaultMessage: 'External Alerts', + }), + path: `${USERS_PATH}/externalAlerts`, + }, + ], +}; diff --git a/x-pack/plugins/security_solution/public/users/pages/constants.ts b/x-pack/plugins/security_solution/public/users/pages/constants.ts index 44d3b0ba83e1f..e8b9e4a4118a1 100644 --- a/x-pack/plugins/security_solution/public/users/pages/constants.ts +++ b/x-pack/plugins/security_solution/public/users/pages/constants.ts @@ -12,4 +12,4 @@ export const usersDetailsPagePath = `${USERS_PATH}/:detailName`; export const usersTabPath = `${USERS_PATH}/:tabName(${UsersTableType.allUsers}|${UsersTableType.authentications}|${UsersTableType.anomalies}|${UsersTableType.risk}|${UsersTableType.events}|${UsersTableType.alerts})`; -export const usersDetailsTabPath = `${usersDetailsPagePath}/:tabName(${UsersTableType.authentications}|${UsersTableType.anomalies}|${UsersTableType.events}|${UsersTableType.alerts})`; +export const usersDetailsTabPath = `${usersDetailsPagePath}/:tabName(${UsersTableType.authentications}|${UsersTableType.anomalies}|${UsersTableType.events}|${UsersTableType.alerts}|${UsersTableType.risk})`; diff --git a/x-pack/plugins/security_solution/public/users/pages/details/details_tabs.tsx b/x-pack/plugins/security_solution/public/users/pages/details/details_tabs.tsx index d3c3a4607b39c..22b394f41bfaf 100644 --- a/x-pack/plugins/security_solution/public/users/pages/details/details_tabs.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/details/details_tabs.tsx @@ -21,6 +21,7 @@ import { EventsQueryTabBody } from '../../../common/components/events_tab/events import { AlertsView } from '../../../common/components/alerts_viewer'; import { userNameExistsFilter } from './helpers'; import { AuthenticationsQueryTabBody } from '../navigation'; +import { UserRiskTabBody } from '../navigation/user_risk_tab_body'; export const UsersDetailsTabs = React.memo( ({ @@ -107,6 +108,9 @@ export const UsersDetailsTabs = React.memo( {...tabProps} /> + + + ); } diff --git a/x-pack/plugins/security_solution/public/users/pages/details/nav_tabs.tsx b/x-pack/plugins/security_solution/public/users/pages/details/nav_tabs.tsx index ee070f749925e..9f12d8824f817 100644 --- a/x-pack/plugins/security_solution/public/users/pages/details/nav_tabs.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/details/nav_tabs.tsx @@ -43,6 +43,12 @@ export const navTabsUsersDetails = ( href: getTabsOnUsersDetailsUrl(userName, UsersTableType.alerts), disabled: false, }, + [UsersTableType.risk]: { + id: UsersTableType.risk, + name: i18n.NAVIGATION_RISK_TITLE, + href: getTabsOnUsersDetailsUrl(userName, UsersTableType.risk), + disabled: false, + }, }; return hasMlUserPermissions diff --git a/x-pack/plugins/security_solution/public/users/pages/details/utils.ts b/x-pack/plugins/security_solution/public/users/pages/details/utils.ts index 4b85d0f59314f..26ed75997a85d 100644 --- a/x-pack/plugins/security_solution/public/users/pages/details/utils.ts +++ b/x-pack/plugins/security_solution/public/users/pages/details/utils.ts @@ -27,6 +27,7 @@ const TabNameMappedToI18nKey: Record = { [UsersTableType.risk]: i18n.NAVIGATION_RISK_TITLE, [UsersTableType.events]: i18n.NAVIGATION_EVENTS_TITLE, [UsersTableType.alerts]: i18n.NAVIGATION_ALERTS_TITLE, + [UsersTableType.risk]: i18n.NAVIGATION_RISK_TITLE, }; export const getBreadcrumbs = ( diff --git a/x-pack/plugins/security_solution/public/users/pages/nav_tabs.tsx b/x-pack/plugins/security_solution/public/users/pages/nav_tabs.tsx index 3097fdeb604f3..046b8b7088125 100644 --- a/x-pack/plugins/security_solution/public/users/pages/nav_tabs.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/nav_tabs.tsx @@ -38,12 +38,6 @@ export const navTabsUsers = ( href: getTabsOnUsersUrl(UsersTableType.anomalies), disabled: false, }, - [UsersTableType.risk]: { - id: UsersTableType.risk, - name: i18n.NAVIGATION_RISK_TITLE, - href: getTabsOnUsersUrl(UsersTableType.risk), - disabled: false, - }, [UsersTableType.events]: { id: UsersTableType.events, name: i18n.NAVIGATION_EVENTS_TITLE, @@ -56,6 +50,12 @@ export const navTabsUsers = ( href: getTabsOnUsersUrl(UsersTableType.alerts), disabled: false, }, + [UsersTableType.risk]: { + id: UsersTableType.risk, + name: i18n.NAVIGATION_RISK_TITLE, + href: getTabsOnUsersUrl(UsersTableType.risk), + disabled: false, + }, }; if (!hasMlUserPermissions) { diff --git a/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_score_tab_body.test.tsx b/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_score_tab_body.test.tsx index 6b5ec66f864bb..10c85be1b72f7 100644 --- a/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_score_tab_body.test.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_score_tab_body.test.tsx @@ -29,23 +29,22 @@ describe('All users query tab body', () => { endDate: '2019-06-25T06:31:59.345Z', type: UsersType.page, }; + beforeEach(() => { jest.clearAllMocks(); mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() }); + mockUseUserRiskScore.mockReturnValue([ false, { - authentications: [], - id: '123', inspect: { dsl: [], response: [], }, isInspected: false, totalCount: 0, - pageInfo: { activePage: 1, fakeTotalCount: 100, showMorePagesIndicator: false }, - loadPage: jest.fn(), refetch: jest.fn(), + isModuleEnabled: true, }, ]); mockUseUserRiskScoreKpi.mockReturnValue({ @@ -59,6 +58,7 @@ describe('All users query tab body', () => { }, }); }); + it('toggleStatus=true, do not skip', () => { render( @@ -68,6 +68,7 @@ describe('All users query tab body', () => { expect(mockUseUserRiskScore.mock.calls[0][0].skip).toEqual(false); expect(mockUseUserRiskScoreKpi.mock.calls[0][0].skip).toEqual(false); }); + it('toggleStatus=false, skip', () => { mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() }); render( diff --git a/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_tab_body.test.tsx b/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_tab_body.test.tsx new file mode 100644 index 0000000000000..539b6df2d8f0a --- /dev/null +++ b/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_tab_body.test.tsx @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { TestProviders } from '../../../common/mock'; +import { useQueryToggle } from '../../../common/containers/query_toggle'; +import { UsersType } from '../../store/model'; +import { useUserRiskScore } from '../../../risk_score/containers'; +import { UserRiskTabBody } from './user_risk_tab_body'; + +jest.mock('../../../risk_score/containers'); +jest.mock('../../../common/containers/query_toggle'); +jest.mock('../../../common/lib/kibana'); + +describe('User query tab body', () => { + const mockUseUserRiskScore = useUserRiskScore as jest.Mock; + const mockUseQueryToggle = useQueryToggle as jest.Mock; + const defaultProps = { + userName: 'testUser', + indexNames: [], + setQuery: jest.fn(), + skip: false, + startDate: '2019-06-25T04:31:59.345Z', + endDate: '2019-06-25T06:31:59.345Z', + type: UsersType.page, + }; + + beforeEach(() => { + jest.clearAllMocks(); + + mockUseUserRiskScore.mockReturnValue([ + false, + { + inspect: { + dsl: [], + response: [], + }, + isInspected: false, + totalCount: 0, + refetch: jest.fn(), + isModuleEnabled: true, + }, + ]); + }); + + it("doesn't skip when both toggleStatus are true", () => { + mockUseQueryToggle.mockReturnValueOnce({ toggleStatus: true, setToggleStatus: jest.fn() }); + mockUseQueryToggle.mockReturnValueOnce({ toggleStatus: true, setToggleStatus: jest.fn() }); + + render( + + + + ); + expect(mockUseUserRiskScore.mock.calls[0][0].skip).toEqual(false); + }); + + it("doesn't skip when at least one toggleStatus is true", () => { + mockUseQueryToggle.mockReturnValueOnce({ toggleStatus: true, setToggleStatus: jest.fn() }); + mockUseQueryToggle.mockReturnValueOnce({ toggleStatus: false, setToggleStatus: jest.fn() }); + + render( + + + + ); + expect(mockUseUserRiskScore.mock.calls[0][0].skip).toEqual(false); + }); + + it('does skip when at both toggleStatus are false', () => { + mockUseQueryToggle.mockReturnValueOnce({ toggleStatus: false, setToggleStatus: jest.fn() }); + mockUseQueryToggle.mockReturnValueOnce({ toggleStatus: false, setToggleStatus: jest.fn() }); + + render( + + + + ); + expect(mockUseUserRiskScore.mock.calls[0][0].skip).toEqual(true); + }); +}); diff --git a/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_tab_body.tsx b/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_tab_body.tsx new file mode 100644 index 0000000000000..bb1f73765bf59 --- /dev/null +++ b/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_tab_body.tsx @@ -0,0 +1,138 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React, { useCallback, useMemo } from 'react'; +import styled from 'styled-components'; + +import * as i18n from '../translations'; + +import { useQueryInspector } from '../../../common/components/page/manage_query'; +import { RiskScoreOverTime } from '../../../common/components/risk_score_over_time'; +import { TopRiskScoreContributors } from '../../../common/components/top_risk_score_contributors'; +import { useQueryToggle } from '../../../common/containers/query_toggle'; +import { UserRiskScoreQueryId, useUserRiskScore } from '../../../risk_score/containers'; +import { buildUserNamesFilter } from '../../../../common/search_strategy'; +import { UsersComponentsQueryProps } from './types'; +import { UserRiskInformationButtonEmpty } from '../../components/user_risk_information'; +import { useDashboardButtonHref } from '../../../common/hooks/use_dashboard_button_href'; + +const QUERY_ID = UserRiskScoreQueryId.USER_DETAILS_RISK_SCORE; + +const StyledEuiFlexGroup = styled(EuiFlexGroup)` + margin-top: ${({ theme }) => theme.eui.paddingSizes.l}; +`; + +const RISKY_USERS_DASHBOARD_TITLE = 'User Risk Score (Start Here)'; + +const UserRiskTabBodyComponent: React.FC< + Pick & { + userName: string; + } +> = ({ userName, startDate, endDate, setQuery, deleteQuery }) => { + const { buttonHref } = useDashboardButtonHref({ + to: endDate, + from: startDate, + title: RISKY_USERS_DASHBOARD_TITLE, + }); + + const timerange = useMemo( + () => ({ + from: startDate, + to: endDate, + }), + [startDate, endDate] + ); + + const { toggleStatus: overTimeToggleStatus, setToggleStatus: setOverTimeToggleStatus } = + useQueryToggle(`${QUERY_ID} overTime`); + const { toggleStatus: contributorsToggleStatus, setToggleStatus: setContributorsToggleStatus } = + useQueryToggle(`${QUERY_ID} contributors`); + + const [loading, { data, refetch, inspect }] = useUserRiskScore({ + filterQuery: userName ? buildUserNamesFilter([userName]) : undefined, + onlyLatest: false, + skip: !overTimeToggleStatus && !contributorsToggleStatus, + timerange, + }); + + useQueryInspector({ + queryId: QUERY_ID, + loading, + refetch, + setQuery, + deleteQuery, + inspect, + }); + + const toggleContributorsQuery = useCallback( + (status: boolean) => { + setContributorsToggleStatus(status); + }, + [setContributorsToggleStatus] + ); + + const toggleOverTimeQuery = useCallback( + (status: boolean) => { + setOverTimeToggleStatus(status); + }, + [setOverTimeToggleStatus] + ); + + const rules = data && data.length > 0 ? data[data.length - 1].risk_stats.rule_risks : []; + + return ( + <> + + + + + + + + + + + + + + {i18n.VIEW_DASHBOARD_BUTTON} + + + + + + + + ); +}; + +UserRiskTabBodyComponent.displayName = 'UserRiskTabBodyComponent'; + +export const UserRiskTabBody = React.memo(UserRiskTabBodyComponent); + +UserRiskTabBody.displayName = 'UserRiskTabBody'; diff --git a/x-pack/plugins/security_solution/public/users/pages/translations.ts b/x-pack/plugins/security_solution/public/users/pages/translations.ts index 41fec21c5bfb0..c36abbaab86ec 100644 --- a/x-pack/plugins/security_solution/public/users/pages/translations.ts +++ b/x-pack/plugins/security_solution/public/users/pages/translations.ts @@ -35,7 +35,7 @@ export const NAVIGATION_ANOMALIES_TITLE = i18n.translate( export const NAVIGATION_RISK_TITLE = i18n.translate( 'xpack.securitySolution.users.navigation.riskTitle', { - defaultMessage: 'Users by risk', + defaultMessage: 'User risk', } ); @@ -52,3 +52,17 @@ export const NAVIGATION_ALERTS_TITLE = i18n.translate( defaultMessage: 'External alerts', } ); + +export const USER_RISK_SCORE_OVER_TIME = i18n.translate( + 'xpack.securitySolution.users.navigation.userScoreOverTimeTitle', + { + defaultMessage: 'User risk score over time', + } +); + +export const VIEW_DASHBOARD_BUTTON = i18n.translate( + 'xpack.securitySolution.hosts.navigaton.hostRisk.viewDashboardButtonLabel', + { + defaultMessage: 'View source dashboard', + } +); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/details.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/details.test.ts new file mode 100644 index 0000000000000..9c6ef9b3a2c57 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/details.test.ts @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TypeOf } from '@kbn/config-schema'; +import { + ScopedClusterClientMock, + elasticsearchServiceMock, + savedObjectsClientMock, + httpServerMock, +} from '@kbn/core/server/mocks'; +import type { KibanaResponseFactory, SavedObjectsClientContract } from '@kbn/core/server'; +import { createMockEndpointAppContext, createRouteHandlerContext } from '../../mocks'; +import { applyActionsEsSearchMock } from '../../services/actions/mocks'; +import { requestContextMock } from '../../../lib/detection_engine/routes/__mocks__'; +import { getActionDetailsRequestHandler } from './details'; +import { NotFoundError } from '../../errors'; +import { ActionDetailsRequestSchema } from '../../../../common/endpoint/schema/actions'; +import { EndpointActionGenerator } from '../../../../common/endpoint/data_generators/endpoint_action_generator'; + +describe('when calling the Action Details route handler', () => { + let mockScopedEsClient: ScopedClusterClientMock; + let mockSavedObjectClient: jest.Mocked; + let mockResponse: jest.Mocked; + let actionDetailsRouteHandler: ReturnType; + + beforeEach(() => { + mockScopedEsClient = elasticsearchServiceMock.createScopedClusterClient(); + mockSavedObjectClient = savedObjectsClientMock.create(); + mockResponse = httpServerMock.createResponseFactory(); + actionDetailsRouteHandler = getActionDetailsRequestHandler(createMockEndpointAppContext()); + }); + + it('should call service using action id from request', async () => { + applyActionsEsSearchMock(mockScopedEsClient.asInternalUser); + + const mockContext = requestContextMock.convertContext( + createRouteHandlerContext(mockScopedEsClient, mockSavedObjectClient) + ); + const mockRequest = httpServerMock.createKibanaRequest< + TypeOf, + never, + never + >({ + params: { action_id: 'a-b-c' }, + }); + + await actionDetailsRouteHandler(mockContext, mockRequest, mockResponse); + + expect(mockScopedEsClient.asInternalUser.search).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + body: { + query: { + bool: { + filter: expect.arrayContaining([{ term: { action_id: 'a-b-c' } }]), + }, + }, + }, + }), + expect.any(Object) + ); + + expect(mockResponse.ok).toHaveBeenCalled(); + }); + + it('should respond with 404 if action id not found', async () => { + applyActionsEsSearchMock( + mockScopedEsClient.asInternalUser, + new EndpointActionGenerator().toEsSearchResponse([]) + ); + + const mockContext = requestContextMock.convertContext( + createRouteHandlerContext(mockScopedEsClient, mockSavedObjectClient) + ); + const mockRequest = httpServerMock.createKibanaRequest< + TypeOf, + never, + never + >({ + params: { action_id: '123' }, + }); + + await actionDetailsRouteHandler(mockContext, mockRequest, mockResponse); + + expect(mockResponse.notFound).toHaveBeenCalledWith({ + body: expect.any(NotFoundError), + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/details.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/details.ts new file mode 100644 index 0000000000000..a5ba924a42728 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/details.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RequestHandler } from '@kbn/core/server'; +import { TypeOf } from '@kbn/config-schema'; +import { + SecuritySolutionPluginRouter, + SecuritySolutionRequestHandlerContext, +} from '../../../types'; +import { EndpointAppContext } from '../../types'; +import { ACTION_DETAILS_ROUTE } from '../../../../common/endpoint/constants'; +import { ActionDetailsRequestSchema } from '../../../../common/endpoint/schema/actions'; +import { withEndpointAuthz } from '../with_endpoint_authz'; +import { getActionDetailsById } from '../../services'; +import { errorHandler } from '../error_handler'; + +/** + * Registers the route for handling retrieval of Action Details + * @param router + * @param endpointContext + */ +export const registerActionDetailsRoutes = ( + router: SecuritySolutionPluginRouter, + endpointContext: EndpointAppContext +) => { + // Details for a given action id + router.get( + { + path: ACTION_DETAILS_ROUTE, + validate: ActionDetailsRequestSchema, + options: { authRequired: true, tags: ['access:securitySolution'] }, + }, + withEndpointAuthz( + { all: ['canAccessEndpointManagement'] }, + endpointContext.logFactory.get('hostIsolationDetails'), + getActionDetailsRequestHandler(endpointContext) + ) + ); +}; + +export const getActionDetailsRequestHandler = ( + endpointContext: EndpointAppContext +): RequestHandler< + TypeOf, + never, + never, + SecuritySolutionRequestHandlerContext +> => { + return async (context, req, res) => { + try { + return res.ok({ + body: { + data: await getActionDetailsById( + ( + await context.core + ).elasticsearch.client.asInternalUser, + req.params.action_id + ), + }, + }); + } catch (error) { + return errorHandler(endpointContext.logFactory.get('EndpointActionDetails'), res, error); + } + }; +}; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/index.ts index 3245369b56e40..baa9440ae8d0c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/index.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { registerActionDetailsRoutes } from './details'; import { SecuritySolutionPluginRouter } from '../../../types'; import { EndpointAppContext } from '../../types'; import { registerHostIsolationRoutes } from './isolation'; @@ -22,4 +23,5 @@ export function registerActionRoutes( registerHostIsolationRoutes(router, endpointContext); registerActionStatusRoutes(router, endpointContext); registerActionAuditLogRoutes(router, endpointContext); + registerActionDetailsRoutes(router, endpointContext); } diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts index 69954dbd7e7a7..c640f56efb512 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts @@ -190,6 +190,7 @@ export const isolationRequestHandler = function ( body: { ...doc, }, + refresh: 'wait_for', }, { meta: true } ); @@ -221,6 +222,7 @@ export const isolationRequestHandler = function ( timeout: 300, // 5 minutes user_id: doc.user.id, }, + refresh: 'wait_for', }, { meta: true } ); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.ts index bfd17cccb3d0d..61b2b9c56f5b0 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.ts @@ -18,12 +18,13 @@ import { getPendingActionCounts } from '../../services'; import { withEndpointAuthz } from '../with_endpoint_authz'; /** - * Registers routes for checking status of endpoints based on pending actions + * Registers routes for checking status of actions */ export function registerActionStatusRoutes( router: SecuritySolutionPluginRouter, endpointContext: EndpointAppContext ) { + // Summary of action status for a given list of endpoints router.get( { path: ACTION_STATUS_ROUTE, diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/error_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/error_handler.ts new file mode 100644 index 0000000000000..065ab835dbf99 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/routes/error_handler.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 { IKibanaResponse, KibanaResponseFactory, Logger } from '@kbn/core/server'; +import { CustomHttpRequestError } from '../../utils/custom_http_request_error'; +import { NotFoundError } from '../errors'; +import { EndpointHostUnEnrolledError } from '../services/metadata'; + +/** + * Default Endpoint Routes error handler + * @param logger + * @param res + * @param error + */ +export const errorHandler = ( + logger: Logger, + res: KibanaResponseFactory, + error: E +): IKibanaResponse => { + logger.error(error); + + if (error instanceof CustomHttpRequestError) { + return res.customError({ + statusCode: error.statusCode, + body: error, + }); + } + + if (error instanceof NotFoundError) { + return res.notFound({ body: error }); + } + + if (error instanceof EndpointHostUnEnrolledError) { + return res.badRequest({ body: error }); + } + + // Kibana CORE will take care of `500` errors when the handler `throw`'s, including logging the error + throw error; +}; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts index 1b86924101b68..f9aa361e71f32 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts @@ -7,15 +7,14 @@ import { TypeOf } from '@kbn/config-schema'; import { - IKibanaResponse, IScopedClusterClient, - KibanaResponseFactory, Logger, RequestHandler, SavedObjectsClientContract, } from '@kbn/core/server'; import { PackagePolicy } from '@kbn/fleet-plugin/common/types/models'; import { AgentNotFoundError } from '@kbn/fleet-plugin/server'; +import { errorHandler } from '../error_handler'; import { HostInfo, HostMetadata, @@ -33,9 +32,6 @@ import { findAgentIdsByStatus } from './support/agent_status'; import { EndpointAppContextService } from '../../endpoint_app_context_services'; import { fleetAgentStatusToEndpointHostStatus } from '../../utils'; import { queryResponseToHostListResult } from './support/query_strategies'; -import { NotFoundError } from '../../errors'; -import { EndpointHostUnEnrolledError } from '../../services/metadata'; -import { CustomHttpRequestError } from '../../../utils/custom_http_request_error'; import { GetMetadataListRequestQuery } from '../../../../common/endpoint/schema/metadata'; import { ENDPOINT_DEFAULT_PAGE, @@ -56,32 +52,6 @@ export const getLogger = (endpointAppContext: EndpointAppContext): Logger => { return endpointAppContext.logFactory.get('metadata'); }; -const errorHandler = ( - logger: Logger, - res: KibanaResponseFactory, - error: E -): IKibanaResponse => { - logger.error(error); - - if (error instanceof CustomHttpRequestError) { - return res.customError({ - statusCode: error.statusCode, - body: error, - }); - } - - if (error instanceof NotFoundError) { - return res.notFound({ body: error }); - } - - if (error instanceof EndpointHostUnEnrolledError) { - return res.badRequest({ body: error }); - } - - // Kibana CORE will take care of `500` errors when the handler `throw`'s, including logging the error - throw error; -}; - export function getMetadataListRequestHandler( endpointAppContext: EndpointAppContext, logger: Logger diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions.ts deleted file mode 100644 index 080ee6e588b03..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions.ts +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ElasticsearchClient, Logger } from '@kbn/core/server'; -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { TransportResult } from '@elastic/elasticsearch'; -import { AGENT_ACTIONS_INDEX, AGENT_ACTIONS_RESULTS_INDEX } from '@kbn/fleet-plugin/common'; -import { ENDPOINT_ACTION_RESPONSES_INDEX_PATTERN } from '../../../common/endpoint/constants'; -import { SecuritySolutionRequestHandlerContext } from '../../types'; -import { - ActivityLog, - ActivityLogEntry, - EndpointAction, - LogsEndpointAction, - EndpointActionResponse, - EndpointPendingActions, - LogsEndpointActionResponse, -} from '../../../common/endpoint/types'; -import { - catchAndWrapError, - categorizeActionResults, - categorizeResponseResults, - getActionRequestsResult, - getActionResponsesResult, - getTimeSortedData, - getUniqueLogData, -} from '../utils'; -import { EndpointMetadataService } from './metadata'; - -const PENDING_ACTION_RESPONSE_MAX_LAPSED_TIME = 300000; // 300k ms === 5 minutes - -export const getAuditLogResponse = async ({ - elasticAgentId, - page, - pageSize, - startDate, - endDate, - context, - logger, -}: { - elasticAgentId: string; - page: number; - pageSize: number; - startDate: string; - endDate: string; - context: SecuritySolutionRequestHandlerContext; - logger: Logger; -}): Promise => { - const size = Math.floor(pageSize / 2); - const from = page <= 1 ? 0 : page * size - size + 1; - - const data = await getActivityLog({ - context, - from, - size, - startDate, - endDate, - elasticAgentId, - logger, - }); - - return { - page, - pageSize, - startDate, - endDate, - data, - }; -}; - -const getActivityLog = async ({ - context, - size, - from, - startDate, - endDate, - elasticAgentId, - logger, -}: { - context: SecuritySolutionRequestHandlerContext; - elasticAgentId: string; - size: number; - from: number; - startDate: string; - endDate: string; - logger: Logger; -}): Promise => { - let actionsResult: TransportResult, unknown>; - let responsesResult: TransportResult, unknown>; - - try { - // fetch actions with matching agent_id - const { actionIds, actionRequests } = await getActionRequestsResult({ - context, - logger, - elasticAgentId, - startDate, - endDate, - size, - from, - }); - actionsResult = actionRequests; - - // fetch responses with matching unique set of `action_id`s - responsesResult = await getActionResponsesResult({ - actionIds: [...new Set(actionIds)], // de-dupe `action_id`s - context, - logger, - elasticAgentId, - startDate, - endDate, - }); - } catch (error) { - logger.error(error); - throw error; - } - if (actionsResult?.statusCode !== 200) { - logger.error(`Error fetching actions log for agent_id ${elasticAgentId}`); - throw new Error(`Error fetching actions log for agent_id ${elasticAgentId}`); - } - - // label record as `action`, `fleetAction` - const responses = categorizeResponseResults({ - results: responsesResult?.body?.hits?.hits as Array< - estypes.SearchHit - >, - }); - - // label record as `response`, `fleetResponse` - const actions = categorizeActionResults({ - results: actionsResult?.body?.hits?.hits as Array< - estypes.SearchHit - >, - }); - - // filter out the duplicate endpoint actions that also have fleetActions - // include endpoint actions that have no fleet actions - const uniqueLogData = getUniqueLogData([...responses, ...actions]); - - // sort by @timestamp in desc order, newest first - const sortedData = getTimeSortedData(uniqueLogData); - - return sortedData; -}; - -const hasAckInResponse = (response: EndpointActionResponse): boolean => { - return response.action_response?.endpoint?.ack ?? false; -}; - -// return TRUE if for given action_id/agent_id -// there is no doc in .logs-endpoint.action.response-default -const hasNoEndpointResponse = ({ - action, - agentId, - indexedActionIds, -}: { - action: EndpointAction; - agentId: string; - indexedActionIds: string[]; -}): boolean => { - return action.agents.includes(agentId) && !indexedActionIds.includes(action.action_id); -}; - -// return TRUE if for given action_id/agent_id -// there is no doc in .fleet-actions-results -const hasNoFleetResponse = ({ - action, - agentId, - agentResponses, -}: { - action: EndpointAction; - agentId: string; - agentResponses: EndpointActionResponse[]; -}): boolean => { - return ( - action.agents.includes(agentId) && - !agentResponses.map((e) => e.action_id).includes(action.action_id) - ); -}; - -export const getPendingActionCounts = async ( - esClient: ElasticsearchClient, - metadataService: EndpointMetadataService, - /** The Fleet Agent IDs to be checked */ - agentIDs: string[], - isPendingActionResponsesWithAckEnabled: boolean -): Promise => { - // retrieve the unexpired actions for the given hosts - const recentActions = await esClient - .search( - { - index: AGENT_ACTIONS_INDEX, - size: 10000, - from: 0, - body: { - query: { - bool: { - filter: [ - { term: { type: 'INPUT_ACTION' } }, // actions that are directed at agent children - { term: { input_type: 'endpoint' } }, // filter for agent->endpoint actions - { range: { expiration: { gte: 'now' } } }, // that have not expired yet - { terms: { agents: agentIDs } }, // for the requested agent IDs - ], - }, - }, - }, - }, - { ignore: [404] } - ) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - .then((result) => result?.hits?.hits?.map((a) => a._source!) || []) - .catch(catchAndWrapError); - - // retrieve any responses to those action IDs from these agents - const responses = await fetchActionResponses( - esClient, - metadataService, - recentActions.map((a) => a.action_id), - agentIDs - ); - - const pending: EndpointPendingActions[] = []; - for (const agentId of agentIDs) { - const agentResponses = responses[agentId]; - - // get response actionIds for responses with ACKs - const ackResponseActionIdList: string[] = agentResponses - .filter(hasAckInResponse) - .map((response) => response.action_id); - - // actions Ids that are indexed in new response index - const indexedActionIds = await hasEndpointResponseDoc({ - agentId, - actionIds: ackResponseActionIdList, - esClient, - }); - - const pendingActions: EndpointAction[] = recentActions.filter((action) => { - return ackResponseActionIdList.includes(action.action_id) // if has ack - ? hasNoEndpointResponse({ action, agentId, indexedActionIds }) // then find responses in new index - : hasNoFleetResponse({ - // else use the legacy way - action, - agentId, - agentResponses, - }); - }); - - pending.push({ - agent_id: agentId, - pending_actions: pendingActions - .map((a) => a.data.command) - .reduce((acc, cur) => { - if (!isPendingActionResponsesWithAckEnabled) { - acc[cur] = 0; // set pending counts to 0 when FF is disabled - } else { - // else do the usual counting - if (cur in acc) { - acc[cur] += 1; - } else { - acc[cur] = 1; - } - } - - return acc; - }, {} as EndpointPendingActions['pending_actions']), - }); - } - - return pending; -}; - -/** - * Returns a string of action ids for search result - * - * @param esClient - * @param actionIds - * @param agentId - */ -const hasEndpointResponseDoc = async ({ - actionIds, - agentId, - esClient, -}: { - actionIds: string[]; - agentId: string; - esClient: ElasticsearchClient; -}): Promise => { - const response = await esClient - .search( - { - index: ENDPOINT_ACTION_RESPONSES_INDEX_PATTERN, - size: 10000, - body: { - query: { - bool: { - filter: [{ terms: { action_id: actionIds } }, { term: { agent_id: agentId } }], - }, - }, - }, - }, - { ignore: [404] } - ) - .then((result) => result?.hits?.hits?.map((a) => a._source?.EndpointActions.action_id) || []) - .catch(catchAndWrapError); - return response.filter((action): action is string => action !== undefined); -}; - -/** - * Returns back a map of elastic Agent IDs to array of action responses that have a response. - * - * @param esClient - * @param metadataService - * @param actionIds - * @param agentIds - */ -const fetchActionResponses = async ( - esClient: ElasticsearchClient, - metadataService: EndpointMetadataService, - actionIds: string[], - agentIds: string[] -): Promise> => { - const actionResponsesByAgentId: Record = agentIds.reduce( - (acc, agentId) => { - acc[agentId] = []; - return acc; - }, - {} as Record - ); - - const actionResponses = await esClient - .search( - { - index: AGENT_ACTIONS_RESULTS_INDEX, - size: 10000, - from: 0, - body: { - query: { - bool: { - filter: [ - { terms: { action_id: actionIds } }, // get results for these actions - { terms: { agent_id: agentIds } }, // ONLY responses for the agents we are interested in (ignore others) - ], - }, - }, - }, - }, - { ignore: [404] } - ) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - .then((result) => result?.hits?.hits?.map((a) => a._source!) || []) - .catch(catchAndWrapError); - - if (actionResponses.length === 0) { - return actionResponsesByAgentId; - } - - // Get the latest docs from the metadata data-stream for the Elastic Agent IDs in the action responses - // This will be used determine if we should withhold the action id from the returned list in cases where - // the Endpoint might not yet have sent an updated metadata document (which would be representative of - // the state of the endpoint post-action) - const latestEndpointMetadataDocs = await metadataService.findHostMetadataForFleetAgents( - esClient, - agentIds - ); - - // Object of Elastic Agent Ids to event created date - const endpointLastEventCreated: Record = latestEndpointMetadataDocs.reduce( - (acc, endpointMetadata) => { - acc[endpointMetadata.elastic.agent.id] = new Date(endpointMetadata.event.created); - return acc; - }, - {} as Record - ); - - for (const actionResponse of actionResponses) { - const lastEndpointMetadataEventTimestamp = endpointLastEventCreated[actionResponse.agent_id]; - const actionCompletedAtTimestamp = new Date(actionResponse.completed_at); - // If enough time has lapsed in checking for updated Endpoint metadata doc so that we don't keep - // checking it forever. - // It uses the `@timestamp` in order to ensure we are looking at times that were set by the server - const enoughTimeHasLapsed = - Date.now() - new Date(actionResponse['@timestamp']).getTime() > - PENDING_ACTION_RESPONSE_MAX_LAPSED_TIME; - - if ( - !lastEndpointMetadataEventTimestamp || - enoughTimeHasLapsed || - lastEndpointMetadataEventTimestamp > actionCompletedAtTimestamp - ) { - actionResponsesByAgentId[actionResponse.agent_id].push(actionResponse); - } - } - - return actionResponsesByAgentId; -}; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/action_details_by_id.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/action_details_by_id.test.ts new file mode 100644 index 0000000000000..b977009b15315 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/action_details_by_id.test.ts @@ -0,0 +1,166 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ElasticsearchClientMock } from '@kbn/core/server/mocks'; +import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { + EndpointAction, + EndpointActionResponse, + LogsEndpointAction, + LogsEndpointActionResponse, +} from '../../../../common/endpoint/types'; +import { EndpointActionGenerator } from '../../../../common/endpoint/data_generators/endpoint_action_generator'; +import { getActionDetailsById } from '..'; +import { NotFoundError } from '../../errors'; +import { + applyActionsEsSearchMock, + createActionRequestsEsSearchResultsMock, + createActionResponsesEsSearchResultsMock, +} from './mocks'; + +describe('When using `getActionDetailsById()', () => { + let esClient: ElasticsearchClientMock; + let endpointActionGenerator: EndpointActionGenerator; + let actionRequests: estypes.SearchResponse; + let actionResponses: estypes.SearchResponse; + + beforeEach(() => { + esClient = elasticsearchServiceMock.createScopedClusterClient().asInternalUser; + endpointActionGenerator = new EndpointActionGenerator('seed'); + + actionRequests = createActionRequestsEsSearchResultsMock(); + actionResponses = createActionResponsesEsSearchResultsMock(); + + applyActionsEsSearchMock(esClient, actionRequests, actionResponses); + }); + + it('should return expected output', async () => { + await expect(getActionDetailsById(esClient, '123')).resolves.toEqual({ + agents: ['agent-a'], + command: 'isolate', + completedAt: '2022-04-30T16:08:47.449Z', + id: '123', + isCompleted: true, + isExpired: false, + logEntries: [ + { + item: { + data: { + '@timestamp': '2022-04-27T16:08:47.449Z', + action_id: '123', + agents: ['agent-a'], + data: { + command: 'isolate', + comment: '5wb6pu6kh2xix5i', + }, + expiration: '2022-04-29T16:08:47.449Z', + input_type: 'endpoint', + type: 'INPUT_ACTION', + user_id: 'elastic', + }, + id: '44d8b915-c69c-4c48-8c86-b57d0bd631d0', + }, + type: 'fleetAction', + }, + { + item: { + data: { + '@timestamp': '2022-04-30T16:08:47.449Z', + action_data: { + command: 'unisolate', + comment: '', + }, + action_id: '123', + agent_id: 'agent-a', + completed_at: '2022-04-30T16:08:47.449Z', + error: '', + started_at: expect.any(String), + }, + id: expect.any(String), + }, + type: 'fleetResponse', + }, + { + item: { + data: { + '@timestamp': '2022-04-30T16:08:47.449Z', + EndpointActions: { + action_id: '123', + completed_at: '2022-04-30T16:08:47.449Z', + data: { + command: 'unisolate', + comment: '', + }, + started_at: expect.any(String), + }, + agent: { + id: 'agent-a', + }, + }, + id: expect.any(String), + }, + type: 'response', + }, + ], + startedAt: '2022-04-27T16:08:47.449Z', + }); + }); + + it('should use expected filters when querying for Action Request', async () => { + await getActionDetailsById(esClient, '123'); + + expect(esClient.search).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + body: { + query: { + bool: { + filter: [ + { term: { action_id: '123' } }, + { term: { input_type: 'endpoint' } }, + { term: { type: 'INPUT_ACTION' } }, + ], + }, + }, + }, + }), + expect.any(Object) + ); + }); + + it('should throw an error if action id does not exist', async () => { + actionRequests.hits.hits = []; + (actionResponses.hits.total as estypes.SearchTotalHits).value = 0; + actionRequests = endpointActionGenerator.toEsSearchResponse([]); + + await expect(getActionDetailsById(esClient, '123')).rejects.toBeInstanceOf(NotFoundError); + }); + + it('should have `isExpired` of `true` if NOT complete and expiration is in the past', async () => { + (actionRequests.hits.hits[0]._source as EndpointAction).expiration = `2021-04-30T16:08:47.449Z`; + actionResponses.hits.hits.pop(); // remove the endpoint response + + await expect(getActionDetailsById(esClient, '123')).resolves.toEqual( + expect.objectContaining({ + isExpired: true, + isCompleted: false, + }) + ); + }); + + it('should have `isExpired` of `false` if complete and expiration is in the past', async () => { + (actionRequests.hits.hits[0]._source as EndpointAction).expiration = `2021-04-30T16:08:47.449Z`; + + await expect(getActionDetailsById(esClient, '123')).resolves.toEqual( + expect.objectContaining({ + isExpired: false, + isCompleted: true, + }) + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/action_details_by_id.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/action_details_by_id.ts new file mode 100644 index 0000000000000..768c015b53a91 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/action_details_by_id.ts @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient } from '@kbn/core/server'; +import { getActionCompletionInfo, mapToNormalizedActionRequest } from './utils'; +import { + ActionDetails, + ActivityLogAction, + ActivityLogActionResponse, + EndpointAction, + EndpointActionResponse, + EndpointActivityLogAction, + EndpointActivityLogActionResponse, + LogsEndpointAction, + LogsEndpointActionResponse, +} from '../../../../common/endpoint/types'; +import { + ACTION_REQUEST_INDICES, + ACTION_RESPONSE_INDICES, + catchAndWrapError, + categorizeActionResults, + categorizeResponseResults, + getUniqueLogData, +} from '../../utils'; +import { EndpointError } from '../../../../common/endpoint/errors'; +import { NotFoundError } from '../../errors'; +import { ACTIONS_SEARCH_PAGE_SIZE } from './constants'; + +export const getActionDetailsById = async ( + esClient: ElasticsearchClient, + actionId: string +): Promise => { + let actionRequestsLogEntries: Array; + + let normalizedActionRequest: ReturnType | undefined; + let actionResponses: Array; + + try { + // Get both the Action Request(s) and action Response(s) + const [actionRequestEsSearchResults, actionResponsesEsSearchResults] = await Promise.all([ + // Get the action request(s) + esClient + .search( + { + index: ACTION_REQUEST_INDICES, + body: { + query: { + bool: { + filter: [ + { term: { action_id: actionId } }, + { term: { input_type: 'endpoint' } }, + { term: { type: 'INPUT_ACTION' } }, + ], + }, + }, + }, + }, + { + ignore: [404], + } + ) + .catch(catchAndWrapError), + + // Get the Action Response(s) + esClient + .search( + { + index: ACTION_RESPONSE_INDICES, + size: ACTIONS_SEARCH_PAGE_SIZE, + body: { + query: { + bool: { + filter: [{ term: { action_id: actionId } }], + }, + }, + }, + }, + { ignore: [404] } + ) + .catch(catchAndWrapError), + ]); + + actionRequestsLogEntries = getUniqueLogData( + categorizeActionResults({ + results: actionRequestEsSearchResults?.hits?.hits ?? [], + }) + ) as Array; + + // Multiple Action records could have been returned, but we only really + // need one since they both hold similar data + const actionDoc = actionRequestsLogEntries[0]?.item.data; + + if (actionDoc) { + normalizedActionRequest = mapToNormalizedActionRequest(actionDoc); + } + + actionResponses = categorizeResponseResults({ + results: actionResponsesEsSearchResults?.hits?.hits ?? [], + }) as Array; + } catch (error) { + throw new EndpointError(error.message, error); + } + + // If action id was not found, error out + if (!normalizedActionRequest) { + throw new NotFoundError(`Action with id '${actionId}' not found.`); + } + + const { isCompleted, completedAt } = getActionCompletionInfo( + normalizedActionRequest.agents, + actionResponses + ); + + const actionDetails: ActionDetails = { + id: actionId, + agents: normalizedActionRequest.agents, + command: normalizedActionRequest.command, + startedAt: normalizedActionRequest.createdAt, + logEntries: [...actionRequestsLogEntries, ...actionResponses], + isCompleted, + completedAt, + isExpired: !isCompleted && normalizedActionRequest.expiration < new Date().toISOString(), + }; + + return actionDetails; +}; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/actions.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/actions.ts new file mode 100644 index 0000000000000..59060e4e56952 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/actions.ts @@ -0,0 +1,401 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient, Logger } from '@kbn/core/server'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { TransportResult } from '@elastic/elasticsearch'; +import { AGENT_ACTIONS_INDEX, AGENT_ACTIONS_RESULTS_INDEX } from '@kbn/fleet-plugin/common'; +import { ENDPOINT_ACTION_RESPONSES_INDEX_PATTERN } from '../../../../common/endpoint/constants'; +import { SecuritySolutionRequestHandlerContext } from '../../../types'; +import { + ActivityLog, + ActivityLogEntry, + EndpointAction, + LogsEndpointAction, + EndpointActionResponse, + EndpointPendingActions, + LogsEndpointActionResponse, +} from '../../../../common/endpoint/types'; +import { + catchAndWrapError, + categorizeActionResults, + categorizeResponseResults, + getActionRequestsResult, + getActionResponsesResult, + getTimeSortedData, + getUniqueLogData, +} from '../../utils'; +import { EndpointMetadataService } from '../metadata'; +import { ACTIONS_SEARCH_PAGE_SIZE } from './constants'; + +const PENDING_ACTION_RESPONSE_MAX_LAPSED_TIME = 300000; // 300k ms === 5 minutes + +export const getAuditLogResponse = async ({ + elasticAgentId, + page, + pageSize, + startDate, + endDate, + context, + logger, +}: { + elasticAgentId: string; + page: number; + pageSize: number; + startDate: string; + endDate: string; + context: SecuritySolutionRequestHandlerContext; + logger: Logger; +}): Promise => { + const size = Math.floor(pageSize / 2); + const from = page <= 1 ? 0 : page * size - size + 1; + + const data = await getActivityLog({ + context, + from, + size, + startDate, + endDate, + elasticAgentId, + logger, + }); + + return { + page, + pageSize, + startDate, + endDate, + data, + }; +}; + +const getActivityLog = async ({ + context, + size, + from, + startDate, + endDate, + elasticAgentId, + logger, +}: { + context: SecuritySolutionRequestHandlerContext; + elasticAgentId: string; + size: number; + from: number; + startDate: string; + endDate: string; + logger: Logger; +}): Promise => { + let actionsResult: TransportResult, unknown>; + let responsesResult: TransportResult, unknown>; + + try { + // fetch actions with matching agent_id + const { actionIds, actionRequests } = await getActionRequestsResult({ + context, + logger, + elasticAgentId, + startDate, + endDate, + size, + from, + }); + actionsResult = actionRequests; + + // fetch responses with matching unique set of `action_id`s + responsesResult = await getActionResponsesResult({ + actionIds: [...new Set(actionIds)], // de-dupe `action_id`s + context, + logger, + elasticAgentId, + startDate, + endDate, + }); + } catch (error) { + logger.error(error); + throw error; + } + if (actionsResult?.statusCode !== 200) { + logger.error(`Error fetching actions log for agent_id ${elasticAgentId}`); + throw new Error(`Error fetching actions log for agent_id ${elasticAgentId}`); + } + + // label record as `action`, `fleetAction` + const responses = categorizeResponseResults({ + results: responsesResult?.body?.hits?.hits as Array< + estypes.SearchHit + >, + }); + + // label record as `response`, `fleetResponse` + const actions = categorizeActionResults({ + results: actionsResult?.body?.hits?.hits as Array< + estypes.SearchHit + >, + }); + + // filter out the duplicate endpoint actions that also have fleetActions + // include endpoint actions that have no fleet actions + const uniqueLogData = getUniqueLogData([...responses, ...actions]); + + // sort by @timestamp in desc order, newest first + const sortedData = getTimeSortedData(uniqueLogData); + + return sortedData; +}; + +const hasAckInResponse = (response: EndpointActionResponse): boolean => { + return response.action_response?.endpoint?.ack ?? false; +}; + +// return TRUE if for given action_id/agent_id +// there is no doc in .logs-endpoint.action.response-default +const hasNoEndpointResponse = ({ + action, + agentId, + indexedActionIds, +}: { + action: EndpointAction; + agentId: string; + indexedActionIds: string[]; +}): boolean => { + return action.agents.includes(agentId) && !indexedActionIds.includes(action.action_id); +}; + +// return TRUE if for given action_id/agent_id +// there is no doc in .fleet-actions-results +const hasNoFleetResponse = ({ + action, + agentId, + agentResponses, +}: { + action: EndpointAction; + agentId: string; + agentResponses: EndpointActionResponse[]; +}): boolean => { + return ( + action.agents.includes(agentId) && + !agentResponses.map((e) => e.action_id).includes(action.action_id) + ); +}; + +export const getPendingActionCounts = async ( + esClient: ElasticsearchClient, + metadataService: EndpointMetadataService, + /** The Fleet Agent IDs to be checked */ + agentIDs: string[], + isPendingActionResponsesWithAckEnabled: boolean +): Promise => { + // retrieve the unexpired actions for the given hosts + const recentActions = await esClient + .search( + { + index: AGENT_ACTIONS_INDEX, + size: ACTIONS_SEARCH_PAGE_SIZE, + from: 0, + body: { + query: { + bool: { + filter: [ + { term: { type: 'INPUT_ACTION' } }, // actions that are directed at agent children + { term: { input_type: 'endpoint' } }, // filter for agent->endpoint actions + { range: { expiration: { gte: 'now' } } }, // that have not expired yet + { terms: { agents: agentIDs } }, // for the requested agent IDs + ], + }, + }, + }, + }, + { ignore: [404] } + ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + .then((result) => result?.hits?.hits?.map((a) => a._source!) || []) + .catch(catchAndWrapError); + + // retrieve any responses to those action IDs from these agents + const responses = await fetchActionResponses( + esClient, + metadataService, + recentActions.map((a) => a.action_id), + agentIDs + ); + + const pending: EndpointPendingActions[] = []; + for (const agentId of agentIDs) { + const agentResponses = responses[agentId]; + + // get response actionIds for responses with ACKs + const ackResponseActionIdList: string[] = agentResponses + .filter(hasAckInResponse) + .map((response) => response.action_id); + + // actions Ids that are indexed in new response index + const indexedActionIds = await hasEndpointResponseDoc({ + agentId, + actionIds: ackResponseActionIdList, + esClient, + }); + + const pendingActions: EndpointAction[] = recentActions.filter((action) => { + return ackResponseActionIdList.includes(action.action_id) // if has ack + ? hasNoEndpointResponse({ action, agentId, indexedActionIds }) // then find responses in new index + : hasNoFleetResponse({ + // else use the legacy way + action, + agentId, + agentResponses, + }); + }); + + pending.push({ + agent_id: agentId, + pending_actions: pendingActions + .map((a) => a.data.command) + .reduce((acc, cur) => { + if (!isPendingActionResponsesWithAckEnabled) { + acc[cur] = 0; // set pending counts to 0 when FF is disabled + } else { + // else do the usual counting + if (cur in acc) { + acc[cur] += 1; + } else { + acc[cur] = 1; + } + } + + return acc; + }, {} as EndpointPendingActions['pending_actions']), + }); + } + + return pending; +}; + +/** + * Returns a string of action ids for search result + * + * @param esClient + * @param actionIds + * @param agentId + */ +const hasEndpointResponseDoc = async ({ + actionIds, + agentId, + esClient, +}: { + actionIds: string[]; + agentId: string; + esClient: ElasticsearchClient; +}): Promise => { + const response = await esClient + .search( + { + index: ENDPOINT_ACTION_RESPONSES_INDEX_PATTERN, + size: ACTIONS_SEARCH_PAGE_SIZE, + body: { + query: { + bool: { + filter: [{ terms: { action_id: actionIds } }, { term: { agent_id: agentId } }], + }, + }, + }, + }, + { ignore: [404] } + ) + .then((result) => result?.hits?.hits?.map((a) => a._source?.EndpointActions.action_id) || []) + .catch(catchAndWrapError); + return response.filter((action): action is string => action !== undefined); +}; + +/** + * Returns back a map of elastic Agent IDs to array of action responses that have a response. + * + * @param esClient + * @param metadataService + * @param actionIds + * @param agentIds + */ +const fetchActionResponses = async ( + esClient: ElasticsearchClient, + metadataService: EndpointMetadataService, + actionIds: string[], + agentIds: string[] +): Promise> => { + const actionResponsesByAgentId: Record = agentIds.reduce( + (acc, agentId) => { + acc[agentId] = []; + return acc; + }, + {} as Record + ); + + const actionResponses = await esClient + .search( + { + index: AGENT_ACTIONS_RESULTS_INDEX, + size: ACTIONS_SEARCH_PAGE_SIZE, + from: 0, + body: { + query: { + bool: { + filter: [ + { terms: { action_id: actionIds } }, // get results for these actions + { terms: { agent_id: agentIds } }, // ONLY responses for the agents we are interested in (ignore others) + ], + }, + }, + }, + }, + { ignore: [404] } + ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + .then((result) => result?.hits?.hits?.map((a) => a._source!) || []) + .catch(catchAndWrapError); + + if (actionResponses.length === 0) { + return actionResponsesByAgentId; + } + + // Get the latest docs from the metadata data-stream for the Elastic Agent IDs in the action responses + // This will be used determine if we should withhold the action id from the returned list in cases where + // the Endpoint might not yet have sent an updated metadata document (which would be representative of + // the state of the endpoint post-action) + const latestEndpointMetadataDocs = await metadataService.findHostMetadataForFleetAgents( + esClient, + agentIds + ); + + // Object of Elastic Agent Ids to event created date + const endpointLastEventCreated: Record = latestEndpointMetadataDocs.reduce( + (acc, endpointMetadata) => { + acc[endpointMetadata.elastic.agent.id] = new Date(endpointMetadata.event.created); + return acc; + }, + {} as Record + ); + + for (const actionResponse of actionResponses) { + const lastEndpointMetadataEventTimestamp = endpointLastEventCreated[actionResponse.agent_id]; + const actionCompletedAtTimestamp = new Date(actionResponse.completed_at); + // If enough time has lapsed in checking for updated Endpoint metadata doc so that we don't keep + // checking it forever. + // It uses the `@timestamp` in order to ensure we are looking at times that were set by the server + const enoughTimeHasLapsed = + Date.now() - new Date(actionResponse['@timestamp']).getTime() > + PENDING_ACTION_RESPONSE_MAX_LAPSED_TIME; + + if ( + !lastEndpointMetadataEventTimestamp || + enoughTimeHasLapsed || + lastEndpointMetadataEventTimestamp > actionCompletedAtTimestamp + ) { + actionResponsesByAgentId[actionResponse.agent_id].push(actionResponse); + } + } + + return actionResponsesByAgentId; +}; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/constants.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/constants.ts new file mode 100644 index 0000000000000..43907dce85a1b --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/constants.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. + */ + +/** + * The Page Size to be used when searching against the Actions indexes (both requests and responses) + */ +export const ACTIONS_SEARCH_PAGE_SIZE = 10000; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/index.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/index.ts new file mode 100644 index 0000000000000..33d7892891cb8 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/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 './actions'; +export { getActionDetailsById } from './action_details_by_id'; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/mocks.ts new file mode 100644 index 0000000000000..0670b6e3aa433 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/mocks.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { ElasticsearchClientMock } from '@kbn/core/server/mocks'; +import { AGENT_ACTIONS_INDEX, AGENT_ACTIONS_RESULTS_INDEX } from '@kbn/fleet-plugin/common'; +import { EndpointActionGenerator } from '../../../../common/endpoint/data_generators/endpoint_action_generator'; +import { FleetActionGenerator } from '../../../../common/endpoint/data_generators/fleet_action_generator'; +import { + EndpointAction, + EndpointActionResponse, + LogsEndpointAction, + LogsEndpointActionResponse, +} from '../../../../common/endpoint/types'; +import { + ENDPOINT_ACTION_RESPONSES_INDEX_PATTERN, + ENDPOINT_ACTIONS_INDEX, +} from '../../../../common/endpoint/constants'; + +export const createActionRequestsEsSearchResultsMock = (): estypes.SearchResponse< + EndpointAction | LogsEndpointAction +> => { + const endpointActionGenerator = new EndpointActionGenerator('seed'); + const fleetActionGenerator = new FleetActionGenerator('seed'); + + return endpointActionGenerator.toEsSearchResponse([ + fleetActionGenerator.generateActionEsHit({ + action_id: '123', + agents: ['agent-a'], + '@timestamp': '2022-04-27T16:08:47.449Z', + }), + endpointActionGenerator.generateActionEsHit({ + EndpointActions: { action_id: '123' }, + agent: { id: 'agent-a' }, + '@timestamp': '2022-04-27T16:08:47.449Z', + }), + ]); +}; + +export const createActionResponsesEsSearchResultsMock = (): estypes.SearchResponse< + LogsEndpointActionResponse | EndpointActionResponse +> => { + const endpointActionGenerator = new EndpointActionGenerator('seed'); + const fleetActionGenerator = new FleetActionGenerator('seed'); + + return endpointActionGenerator.toEsSearchResponse< + LogsEndpointActionResponse | EndpointActionResponse + >([ + fleetActionGenerator.generateResponseEsHit({ + action_id: '123', + agent_id: 'agent-a', + error: '', + '@timestamp': '2022-04-30T16:08:47.449Z', + }), + endpointActionGenerator.generateResponseEsHit({ + agent: { id: 'agent-a' }, + EndpointActions: { action_id: '123' }, + '@timestamp': '2022-04-30T16:08:47.449Z', + }), + ]); +}; + +/** + * Applies a mock implementation to the `esClient.search()` method that will return action requests or responses + * depending on what indexes the `.search()` was called with. + * @param esClient + * @param actionRequests + * @param actionResponses + */ +export const applyActionsEsSearchMock = ( + esClient: ElasticsearchClientMock, + actionRequests: estypes.SearchResponse< + EndpointAction | LogsEndpointAction + > = createActionRequestsEsSearchResultsMock(), + actionResponses: estypes.SearchResponse< + LogsEndpointActionResponse | EndpointActionResponse + > = createActionResponsesEsSearchResultsMock() +) => { + const priorSearchMockImplementation = esClient.search.getMockImplementation(); + + esClient.search.mockImplementation(async (...args) => { + const params = args[0] ?? {}; + const indexes = Array.isArray(params.index) ? params.index : [params.index]; + + if (indexes.includes(AGENT_ACTIONS_INDEX) || indexes.includes(ENDPOINT_ACTIONS_INDEX)) { + return actionRequests; + } else if ( + indexes.includes(AGENT_ACTIONS_RESULTS_INDEX) || + indexes.includes(ENDPOINT_ACTION_RESPONSES_INDEX_PATTERN) + ) { + return actionResponses; + } + + if (priorSearchMockImplementation) { + return priorSearchMockImplementation(...args); + } + + return new EndpointActionGenerator().toEsSearchResponse([]); + }); +}; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/utils.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/utils.test.ts new file mode 100644 index 0000000000000..3071c8a417c6a --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/utils.test.ts @@ -0,0 +1,235 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EndpointActionGenerator } from '../../../../common/endpoint/data_generators/endpoint_action_generator'; +import { FleetActionGenerator } from '../../../../common/endpoint/data_generators/fleet_action_generator'; +import { + getActionCompletionInfo, + isLogsEndpointAction, + isLogsEndpointActionResponse, + mapToNormalizedActionRequest, +} from './utils'; +import type { + ActivityLogActionResponse, + EndpointActivityLogActionResponse, +} from '../../../../common/endpoint/types'; + +describe('When using Actions service utilities', () => { + let fleetActionGenerator: FleetActionGenerator; + let endpointActionGenerator: EndpointActionGenerator; + + beforeEach(() => { + fleetActionGenerator = new FleetActionGenerator('seed'); + endpointActionGenerator = new EndpointActionGenerator('seed'); + }); + + describe('#isLogsEndpointAction()', () => { + it('should return `true` for a `LogsEndpointAction` (endpoint action request)', () => { + expect(isLogsEndpointAction(endpointActionGenerator.generate())).toBe(true); + }); + + it('should return `false` for an `EndpointAction` (fleet action request)', () => { + expect(isLogsEndpointAction(fleetActionGenerator.generate())).toBe(false); + }); + }); + + describe('#isLogsEndpointActionResponse()', () => { + it('should return `true` for a `LogsEndpointActionResponse` (response sent by endpoint)', () => { + expect(isLogsEndpointActionResponse(endpointActionGenerator.generateResponse())).toBe(true); + }); + + it('should return `false` for a `EndpointActionResponse` (response sent by fleet agent)', () => { + expect(isLogsEndpointActionResponse(fleetActionGenerator.generateResponse())).toBe(false); + }); + }); + + describe('#mapToNormalizedActionRequest()', () => { + it('normalizes an `EndpointAction` (those stored in Fleet index)', () => { + expect( + mapToNormalizedActionRequest( + fleetActionGenerator.generate({ + '@timestamp': '2022-04-27T16:08:47.449Z', + }) + ) + ).toEqual({ + agents: ['6e6796b0-af39-4f12-b025-fcb06db499e5'], + command: 'isolate', + comment: 'isolate', + createdAt: '2022-04-27T16:08:47.449Z', + createdBy: 'elastic', + expiration: '2022-04-29T16:08:47.449Z', + id: '90d62689-f72d-4a05-b5e3-500cad0dc366', + type: 'ACTION_REQUEST', + }); + }); + + it('normalizes a `LogsEndpointAction` (those stored in Endpoint index)', () => { + expect( + mapToNormalizedActionRequest( + endpointActionGenerator.generate({ + '@timestamp': '2022-04-27T16:08:47.449Z', + }) + ) + ).toEqual({ + agents: ['90d62689-f72d-4a05-b5e3-500cad0dc366'], + command: 'isolate', + comment: 'isolate', + createdAt: '2022-04-27T16:08:47.449Z', + createdBy: 'Shanel', + expiration: '2022-05-10T16:08:47.449Z', + id: '1d6e6796-b0af-496f-92b0-25fcb06db499', + type: 'ACTION_REQUEST', + }); + }); + }); + + describe('#getAction CompletionInfo()', () => { + const COMPLETED_AT = '2022-05-05T18:53:18.836Z'; + const NOT_COMPLETED_OUTPUT = Object.freeze({ + isCompleted: false, + completed: undefined, + }); + + it('should show complete `false` if no action ids', () => { + expect(getActionCompletionInfo([], [])).toEqual(NOT_COMPLETED_OUTPUT); + }); + + it('should show complete as `false` if no responses', () => { + expect(getActionCompletionInfo(['123'], [])).toEqual(NOT_COMPLETED_OUTPUT); + }); + + it('should show complete as `false` if no Endpoint response', () => { + expect( + getActionCompletionInfo( + ['123'], + [ + fleetActionGenerator.generateActivityLogActionResponse({ + item: { data: { action_id: '123' } }, + }), + ] + ) + ).toEqual(NOT_COMPLETED_OUTPUT); + }); + + it('should show complete as `true` with completion date if Endpoint Response received', () => { + expect( + getActionCompletionInfo( + ['123'], + [ + endpointActionGenerator.generateActivityLogActionResponse({ + item: { + data: { + '@timestamp': COMPLETED_AT, + agent: { id: '123' }, + EndpointActions: { completed_at: COMPLETED_AT }, + }, + }, + }), + ] + ) + ).toEqual({ isCompleted: true, completedAt: COMPLETED_AT }); + }); + + describe('with multiple agent ids', () => { + let agentIds: string[]; + let action123Responses: Array; + let action456Responses: Array; + let action789Responses: Array; + + beforeEach(() => { + agentIds = ['123', '456', '789']; + action123Responses = [ + fleetActionGenerator.generateActivityLogActionResponse({ + item: { data: { agent_id: '123', error: '' } }, + }), + endpointActionGenerator.generateActivityLogActionResponse({ + item: { + data: { + '@timestamp': '2022-01-05T19:27:23.816Z', + agent: { id: '123' }, + EndpointActions: { completed_at: '2022-01-05T19:27:23.816Z' }, + }, + }, + }), + ]; + + action456Responses = [ + fleetActionGenerator.generateActivityLogActionResponse({ + item: { data: { agent_id: '456', error: '' } }, + }), + endpointActionGenerator.generateActivityLogActionResponse({ + item: { + data: { + '@timestamp': COMPLETED_AT, + agent: { id: '456' }, + EndpointActions: { completed_at: COMPLETED_AT }, + }, + }, + }), + ]; + + action789Responses = [ + fleetActionGenerator.generateActivityLogActionResponse({ + item: { data: { agent_id: '789', error: '' } }, + }), + endpointActionGenerator.generateActivityLogActionResponse({ + item: { + data: { + '@timestamp': '2022-03-05T19:27:23.816Z', + agent: { id: '789' }, + EndpointActions: { completed_at: '2022-03-05T19:27:23.816Z' }, + }, + }, + }), + ]; + }); + + it('should show complete as `false` if no responses', () => { + expect(getActionCompletionInfo(agentIds, [])).toEqual(NOT_COMPLETED_OUTPUT); + }); + + it('should complete as `false` if at least one agent id is has not received a response', () => { + expect( + getActionCompletionInfo(agentIds, [ + ...action123Responses, + + // Action id: 456 === Not complete (only fleet response) + action456Responses[0], + + ...action789Responses, + ]) + ).toEqual(NOT_COMPLETED_OUTPUT); + }); + + it('should show complete as `true` if all agent response were received', () => { + expect( + getActionCompletionInfo(agentIds, [ + ...action123Responses, + ...action456Responses, + ...action789Responses, + ]) + ).toEqual({ isCompleted: true, completedAt: COMPLETED_AT }); + }); + + it('should complete as `true` if one agent only received a fleet response with error on it', () => { + action456Responses[0].item.data.error = 'something is no good'; + action456Responses[0].item.data['@timestamp'] = '2022-05-06T12:50:19.747Z'; + + expect( + getActionCompletionInfo(agentIds, [ + ...action123Responses, + + // Action id: 456 === is complete with only a fleet response that has `error` + action456Responses[0], + + ...action789Responses, + ]) + ).toEqual({ isCompleted: true, completedAt: '2022-05-06T12:50:19.747Z' }); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/utils.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/utils.ts new file mode 100644 index 0000000000000..cf4c2ba6a718d --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/utils.ts @@ -0,0 +1,212 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + ActivityLogActionResponse, + EndpointAction, + EndpointActionResponse, + EndpointActivityLogActionResponse, + LogsEndpointAction, + LogsEndpointActionResponse, +} from '../../../../common/endpoint/types'; + +/** + * Type guard to check if a given Action is in the shape of the Endpoint Action. + * @param item + */ +export const isLogsEndpointAction = ( + item: LogsEndpointAction | EndpointAction +): item is LogsEndpointAction => { + return 'EndpointActions' in item && 'user' in item && 'agent' in item && '@timestamp' in item; +}; + +/** + * Type guard to track if a given action response is in the shape of the Endpoint Action Response (from the endpoint index) + * @param item + */ +export const isLogsEndpointActionResponse = ( + item: EndpointActionResponse | LogsEndpointActionResponse +): item is LogsEndpointActionResponse => { + return 'EndpointActions' in item && 'agent' in item; +}; + +interface NormalizedActionRequest { + id: string; + type: 'ACTION_REQUEST'; + expiration: string; + agents: string[]; + createdBy: string; + createdAt: string; + command: string; + comment?: string; +} + +/** + * Given an Action record - either a fleet action or an endpoint action - this utility + * will return a normalized data structure based on those two types, which + * will avoid us having to either cast or do type guards against the two different + * types of action request. + */ +export const mapToNormalizedActionRequest = ( + actionRequest: EndpointAction | LogsEndpointAction +): NormalizedActionRequest => { + if (isLogsEndpointAction(actionRequest)) { + return { + agents: Array.isArray(actionRequest.agent.id) + ? actionRequest.agent.id + : [actionRequest.agent.id], + command: actionRequest.EndpointActions.data.command, + comment: actionRequest.EndpointActions.data.command, + type: 'ACTION_REQUEST', + id: actionRequest.EndpointActions.action_id, + expiration: actionRequest.EndpointActions.expiration, + createdBy: actionRequest.user.id, + createdAt: actionRequest['@timestamp'], + }; + } + + // Else, it's a Fleet Endpoint Action record + return { + agents: actionRequest.agents, + command: actionRequest.data.command, + comment: actionRequest.data.command, + type: 'ACTION_REQUEST', + id: actionRequest.action_id, + expiration: actionRequest.expiration, + createdBy: actionRequest.user_id, + createdAt: actionRequest['@timestamp'], + }; +}; + +interface ActionCompletionInfo { + isCompleted: boolean; + completedAt: undefined | string; +} + +export const getActionCompletionInfo = ( + /** List of agents that the action was sent to */ + agentIds: string[], + /** List of action Log responses received for the action */ + actionResponses: Array +): ActionCompletionInfo => { + const completedInfo: ActionCompletionInfo = { + isCompleted: Boolean(agentIds.length), + completedAt: undefined, + }; + + const responsesByAgentId = mapActionResponsesByAgentId(actionResponses); + + for (const agentId of agentIds) { + if (!responsesByAgentId[agentId] || !responsesByAgentId[agentId].isCompleted) { + completedInfo.isCompleted = false; + break; + } + } + + // If completed, then get the completed at date + if (completedInfo.isCompleted) { + for (const normalizedAgentResponse of Object.values(responsesByAgentId)) { + if ( + !completedInfo.completedAt || + completedInfo.completedAt < (normalizedAgentResponse.completedAt ?? '') + ) { + completedInfo.completedAt = normalizedAgentResponse.completedAt; + } + } + } + + return completedInfo; +}; + +interface NormalizedAgentActionResponse { + isCompleted: boolean; + completedAt: undefined | string; + fleetResponse: undefined | ActivityLogActionResponse; + endpointResponse: undefined | EndpointActivityLogActionResponse; +} + +type ActionResponseByAgentId = Record; + +/** + * Given a list of Action Responses, it will return a Map where keys are the Agent ID and + * value is a object having information about the action response's associated with that agent id + * @param actionResponses + */ +const mapActionResponsesByAgentId = ( + actionResponses: Array +): ActionResponseByAgentId => { + const response: ActionResponseByAgentId = {}; + + for (const actionResponse of actionResponses) { + if (actionResponse.type === 'fleetResponse' || actionResponse.type === 'response') { + const agentId = getAgentIdFromActionResponse(actionResponse); + let thisAgentActionResponses = response[agentId]; + + if (!thisAgentActionResponses) { + response[agentId] = { + isCompleted: false, + completedAt: undefined, + fleetResponse: undefined, + endpointResponse: undefined, + }; + + thisAgentActionResponses = response[agentId]; + } + + if (actionResponse.type === 'fleetResponse') { + thisAgentActionResponses.fleetResponse = actionResponse; + } else { + thisAgentActionResponses.endpointResponse = actionResponse; + } + + thisAgentActionResponses.isCompleted = + // Action is complete if an Endpoint Action Response was received + Boolean(thisAgentActionResponses.endpointResponse) || + // OR: + // If we did not have an endpoint response and the Fleet response has `error`, then + // action is complete. Elastic Agent was unable to deliver the action request to the + // endpoint, so we are unlikely to ever receive an Endpoint Response. + Boolean(thisAgentActionResponses.fleetResponse?.item.data.error); + + if (thisAgentActionResponses.isCompleted) { + if (thisAgentActionResponses.endpointResponse) { + thisAgentActionResponses.completedAt = + thisAgentActionResponses.endpointResponse?.item.data['@timestamp']; + } else if ( + thisAgentActionResponses.fleetResponse && + thisAgentActionResponses.fleetResponse?.item.data.error + ) { + // Check if perhaps the Fleet action response returned an error, in which case, the Fleet Agent + // failed to deliver the Action to the Endpoint. If that's the case, we are not going to get + // a Response from endpoint, thus mark the Action as completed and use the Fleet Message's + // timestamp for the complete data/time. + thisAgentActionResponses.isCompleted = true; + thisAgentActionResponses.completedAt = + thisAgentActionResponses.fleetResponse?.item.data['@timestamp']; + } + } + } + } + + return response; +}; + +/** + * Given an Action response, this will return the Agent ID for that action response. + * @param actionResponse + */ +const getAgentIdFromActionResponse = ( + actionResponse: ActivityLogActionResponse | EndpointActivityLogActionResponse +): string => { + const responseData = actionResponse.item.data; + + if (isLogsEndpointActionResponse(responseData)) { + return Array.isArray(responseData.agent.id) ? responseData.agent.id[0] : responseData.agent.id; + } + + return responseData.agent_id; +}; diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/audit_log_helpers.ts b/x-pack/plugins/security_solution/server/endpoint/utils/audit_log_helpers.ts index 3a1e25c32b683..dc326f4fa4631 100644 --- a/x-pack/plugins/security_solution/server/endpoint/utils/audit_log_helpers.ts +++ b/x-pack/plugins/security_solution/server/endpoint/utils/audit_log_helpers.ts @@ -30,11 +30,14 @@ import { LogsEndpointActionResponse, ActivityLogEntry, } from '../../../common/endpoint/types'; -import { doesLogsEndpointActionsIndexExist } from '.'; +import { doesLogsEndpointActionsIndexExist } from './yes_no_data_stream'; -const actionsIndices = [AGENT_ACTIONS_INDEX, ENDPOINT_ACTIONS_INDEX]; +export const ACTION_REQUEST_INDICES = [AGENT_ACTIONS_INDEX, ENDPOINT_ACTIONS_INDEX]; // search all responses indices irrelevant of namespace -const responseIndices = [AGENT_ACTIONS_RESULTS_INDEX, ENDPOINT_ACTION_RESPONSES_INDEX_PATTERN]; +export const ACTION_RESPONSE_INDICES = [ + AGENT_ACTIONS_RESULTS_INDEX, + ENDPOINT_ACTION_RESPONSES_INDEX_PATTERN, +]; export const logsEndpointActionsRegex = new RegExp(`(^\.ds-\.logs-endpoint\.actions-default-).+`); // matches index names like .ds-.logs-endpoint.action.responses-name_space---suffix-2022.01.25-000001 export const logsEndpointResponsesRegex = new RegExp( @@ -173,7 +176,7 @@ export const getActionRequestsResult = async ({ }); const actionsSearchQuery: SearchRequest = { - index: hasLogsEndpointActionsIndex ? actionsIndices : AGENT_ACTIONS_INDEX, + index: hasLogsEndpointActionsIndex ? ACTION_REQUEST_INDICES : AGENT_ACTIONS_INDEX, size, from, body: { @@ -238,7 +241,9 @@ export const getActionResponsesResult = async ({ }); const responsesSearchQuery: SearchRequest = { - index: hasLogsEndpointActionResponsesIndex ? responseIndices : AGENT_ACTIONS_RESULTS_INDEX, + index: hasLogsEndpointActionResponsesIndex + ? ACTION_RESPONSE_INDICES + : AGENT_ACTIONS_RESULTS_INDEX, size: 1000, body: { query: { diff --git a/x-pack/plugins/security_solution/server/features.ts b/x-pack/plugins/security_solution/server/features.ts index 340c4c54c5fc6..375f66a36c3ac 100644 --- a/x-pack/plugins/security_solution/server/features.ts +++ b/x-pack/plugins/security_solution/server/features.ts @@ -133,6 +133,9 @@ export const getKibanaPrivilegesFeaturePrivileges = (ruleTypes: string[]): Kiban rule: { all: ruleTypes, }, + alert: { + all: ruleTypes, + }, }, management: { insightsAndAlerting: ['triggersActions'], @@ -156,6 +159,9 @@ export const getKibanaPrivilegesFeaturePrivileges = (ruleTypes: string[]): Kiban rule: { read: ruleTypes, }, + alert: { + all: ruleTypes, + }, }, management: { insightsAndAlerting: ['triggersActions'], 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 8e2f0da4e65c9..9f4670e3c252a 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 @@ -9,7 +9,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { ALERT_WORKFLOW_STATUS } from '@kbn/rule-data-utils'; import { ruleTypeMappings } from '@kbn/securitysolution-rules'; -import { SavedObjectsFindResponse } from '@kbn/core/server'; +import { SavedObjectsFindResponse, SavedObjectsFindResult } from '@kbn/core/server'; import { ActionResult } from '@kbn/actions-plugin/server'; import { @@ -49,6 +49,8 @@ import { } from '../../../../../common/detection_engine/schemas/common'; // eslint-disable-next-line no-restricted-imports import type { LegacyRuleNotificationAlertType } from '../../notifications/legacy_types'; +// eslint-disable-next-line no-restricted-imports +import { LegacyIRuleActionsAttributes } from '../../rule_actions/legacy_types'; import { RuleExecutionSummariesByRuleId } from '../../rule_execution_log'; export const typicalSetStatusSignalByIdsPayload = (): SetSignalsStatusSchemaDecoded => ({ @@ -575,7 +577,7 @@ export const getAggregateExecutionEvents = (): GetAggregateRuleExecutionEventsRe timed_out: false, indexing_duration_ms: 7, search_duration_ms: 551, - gap_duration_ms: 0, + gap_duration_s: 0, security_status: 'succeeded', security_message: 'succeeded', }, @@ -598,7 +600,7 @@ export const getAggregateExecutionEvents = (): GetAggregateRuleExecutionEventsRe timed_out: false, indexing_duration_ms: 0, search_duration_ms: 0, - gap_duration_ms: 0, + gap_duration_s: 0, security_status: 'partial failure', security_message: 'Check privileges failed to execute ResponseError: index_not_found_exception: [index_not_found_exception] Reason: no such index [broken-index] name: "This Rule Makes Alerts, Actions, AND Moar!" id: "f78f3550-a186-11ec-89a1-0bce95157aba" rule id: "b64b4540-d035-4826-a1e7-f505bf4b9653" execution id: "254d8400-9dc7-43c5-ad4b-227273d1a44b" space ID: "default"', @@ -688,14 +690,20 @@ export const getSignalsMigrationStatusRequest = () => /** * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function */ -export const legacyGetNotificationResult = (): LegacyRuleNotificationAlertType => ({ - id: '200dbf2f-b269-4bf9-aa85-11ba32ba73ba', +export const legacyGetNotificationResult = ({ + id = '456', + ruleId = '123', +}: { + id?: string; + ruleId?: string; +} = {}): LegacyRuleNotificationAlertType => ({ + id, name: 'Notification for Rule Test', tags: [], alertTypeId: 'siem.notifications', consumer: 'siem', params: { - ruleAlertId: '85b64e8a-2e40-4096-86af-5ac172c10825', + ruleAlertId: `${ruleId}`, }, schedule: { interval: '5m', @@ -732,10 +740,357 @@ export const legacyGetNotificationResult = (): LegacyRuleNotificationAlertType = /** * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function */ -export const legacyGetFindNotificationsResultWithSingleHit = - (): FindHit => ({ +export const legacyGetHourlyNotificationResult = ( + id = '456', + ruleId = '123' +): LegacyRuleNotificationAlertType => ({ + id, + name: 'Notification for Rule Test', + tags: [], + alertTypeId: 'siem.notifications', + consumer: 'siem', + params: { + ruleAlertId: `${ruleId}`, + }, + schedule: { + interval: '1h', + }, + enabled: true, + actions: [ + { + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + actionTypeId: '.email', + id: '99403909-ca9b-49ba-9d7a-7e5320e68d05', + }, + ], + throttle: null, + notifyWhen: 'onActiveAlert', + apiKey: null, + apiKeyOwner: 'elastic', + createdBy: 'elastic', + updatedBy: 'elastic', + createdAt: new Date('2020-03-21T11:15:13.530Z'), + muteAll: false, + mutedInstanceIds: [], + scheduledTaskId: '62b3a130-6b70-11ea-9ce9-6b9818c4cbd7', + updatedAt: new Date('2020-03-21T12:37:08.730Z'), + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetDailyNotificationResult = ( + id = '456', + ruleId = '123' +): LegacyRuleNotificationAlertType => ({ + id, + name: 'Notification for Rule Test', + tags: [], + alertTypeId: 'siem.notifications', + consumer: 'siem', + params: { + ruleAlertId: `${ruleId}`, + }, + schedule: { + interval: '1d', + }, + enabled: true, + actions: [ + { + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + actionTypeId: '.email', + id: '99403909-ca9b-49ba-9d7a-7e5320e68d05', + }, + ], + throttle: null, + notifyWhen: 'onActiveAlert', + apiKey: null, + apiKeyOwner: 'elastic', + createdBy: 'elastic', + updatedBy: 'elastic', + createdAt: new Date('2020-03-21T11:15:13.530Z'), + muteAll: false, + mutedInstanceIds: [], + scheduledTaskId: '62b3a130-6b70-11ea-9ce9-6b9818c4cbd7', + updatedAt: new Date('2020-03-21T12:37:08.730Z'), + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetWeeklyNotificationResult = ( + id = '456', + ruleId = '123' +): LegacyRuleNotificationAlertType => ({ + id, + name: 'Notification for Rule Test', + tags: [], + alertTypeId: 'siem.notifications', + consumer: 'siem', + params: { + ruleAlertId: `${ruleId}`, + }, + schedule: { + interval: '7d', + }, + enabled: true, + actions: [ + { + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + actionTypeId: '.email', + id: '99403909-ca9b-49ba-9d7a-7e5320e68d05', + }, + ], + throttle: null, + notifyWhen: 'onActiveAlert', + apiKey: null, + apiKeyOwner: 'elastic', + createdBy: 'elastic', + updatedBy: 'elastic', + createdAt: new Date('2020-03-21T11:15:13.530Z'), + muteAll: false, + mutedInstanceIds: [], + scheduledTaskId: '62b3a130-6b70-11ea-9ce9-6b9818c4cbd7', + updatedAt: new Date('2020-03-21T12:37:08.730Z'), + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetFindNotificationsResultWithSingleHit = ( + ruleId = '123' +): FindHit => ({ + page: 1, + perPage: 1, + total: 1, + data: [legacyGetNotificationResult({ ruleId })], +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetSiemNotificationRuleNoActionsSOResult = ( + ruleId = '123' +): SavedObjectsFindResult => ({ + type: 'siem-detection-engine-rule-actions', + id: 'ID_OF_LEGACY_SIDECAR_NO_ACTIONS', + namespaces: ['default'], + attributes: { + actions: [], + ruleThrottle: 'no_actions', + alertThrottle: null, + }, + references: [{ id: ruleId, type: 'alert', name: 'alert_0' }], + migrationVersion: { + 'siem-detection-engine-rule-actions': '7.11.2', + }, + coreMigrationVersion: '7.15.2', + updated_at: '2022-03-31T19:06:40.473Z', + version: 'WzIzNywxXQ==', + score: 0, +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetSiemNotificationRuleEveryRunSOResult = ( + ruleId = '123' +): SavedObjectsFindResult => ({ + type: 'siem-detection-engine-rule-actions', + id: 'ID_OF_LEGACY_SIDECAR_RULE_RUN_ACTIONS', + namespaces: ['default'], + attributes: { + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + action_type_id: '.email', + }, + ], + ruleThrottle: 'rule', + alertThrottle: null, + }, + references: [{ id: ruleId, type: 'alert', name: 'alert_0' }], + migrationVersion: { + 'siem-detection-engine-rule-actions': '7.11.2', + }, + coreMigrationVersion: '7.15.2', + updated_at: '2022-03-31T19:06:40.473Z', + version: 'WzIzNywxXQ==', + score: 0, +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetSiemNotificationRuleHourlyActionsSOResult = ( + ruleId = '123', + connectorId = '456' +): SavedObjectsFindResult => ({ + type: 'siem-detection-engine-rule-actions', + id: 'ID_OF_LEGACY_SIDECAR_HOURLY_ACTIONS', + namespaces: ['default'], + attributes: { + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + action_type_id: '.email', + }, + ], + ruleThrottle: '1h', + alertThrottle: '1h', + }, + references: [ + { id: ruleId, type: 'alert', name: 'alert_0' }, + { id: connectorId, type: 'action', name: 'action_0' }, + ], + migrationVersion: { + 'siem-detection-engine-rule-actions': '7.11.2', + }, + coreMigrationVersion: '7.15.2', + updated_at: '2022-03-31T19:06:40.473Z', + version: 'WzIzNywxXQ==', + score: 0, +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetSiemNotificationRuleDailyActionsSOResult = ( + ruleId = '123', + connectorId = '456' +): SavedObjectsFindResult => ({ + type: 'siem-detection-engine-rule-actions', + id: 'ID_OF_LEGACY_SIDECAR_DAILY_ACTIONS', + namespaces: ['default'], + attributes: { + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + action_type_id: '.email', + }, + ], + ruleThrottle: '1d', + alertThrottle: '1d', + }, + references: [ + { id: ruleId, type: 'alert', name: 'alert_0' }, + { id: connectorId, type: 'action', name: 'action_0' }, + ], + migrationVersion: { + 'siem-detection-engine-rule-actions': '7.11.2', + }, + coreMigrationVersion: '7.15.2', + updated_at: '2022-03-31T19:06:40.473Z', + version: 'WzIzNywxXQ==', + score: 0, +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetSiemNotificationRuleWeeklyActionsSOResult = ( + ruleId = '123', + connectorId = '456' +): SavedObjectsFindResult => ({ + type: 'siem-detection-engine-rule-actions', + id: 'ID_OF_LEGACY_SIDECAR_WEEKLY_ACTIONS', + namespaces: ['default'], + attributes: { + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + action_type_id: '.email', + }, + ], + ruleThrottle: '7d', + alertThrottle: '7d', + }, + references: [ + { id: ruleId, type: 'alert', name: 'alert_0' }, + { id: connectorId, type: 'action', name: 'action_0' }, + ], + migrationVersion: { + 'siem-detection-engine-rule-actions': '7.11.2', + }, + coreMigrationVersion: '7.15.2', + updated_at: '2022-03-31T19:06:40.473Z', + version: 'WzIzNywxXQ==', + score: 0, +}); + +const getLegacyActionSOs = (ruleId = '123', connectorId = '456') => ({ + none: () => legacyGetSiemNotificationRuleNoActionsSOResult(ruleId), + rule: () => legacyGetSiemNotificationRuleEveryRunSOResult(ruleId), + hourly: () => legacyGetSiemNotificationRuleHourlyActionsSOResult(ruleId, connectorId), + daily: () => legacyGetSiemNotificationRuleDailyActionsSOResult(ruleId, connectorId), + weekly: () => legacyGetSiemNotificationRuleWeeklyActionsSOResult(ruleId, connectorId), +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetSiemNotificationRuleActionsSOResultWithSingleHit = ( + actionTypes: Array<'none' | 'rule' | 'daily' | 'hourly' | 'weekly'>, + ruleId = '123', + connectorId = '456' +): SavedObjectsFindResponse => { + const actions = getLegacyActionSOs(ruleId, connectorId); + + return { page: 1, - perPage: 1, + per_page: 1, total: 1, - data: [legacyGetNotificationResult()], - }); + saved_objects: actionTypes.map((type) => actions[type]()), + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts index 327b8afb46b5d..8db750ba220af 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts @@ -21,6 +21,15 @@ import { installPrepackagedTimelines } from '../../../timeline/routes/prepackage // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { elasticsearchClientMock } from '@kbn/core/server/elasticsearch/client/mocks'; import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; +import { legacyMigrate } from '../../rules/utils'; + +jest.mock('../../rules/utils', () => { + const actual = jest.requireActual('../../rules/utils'); + return { + ...actual, + legacyMigrate: jest.fn(), + }; +}); jest.mock('../../rules/get_prepackaged_rules', () => { return { @@ -92,6 +101,8 @@ describe('add_prepackaged_rules_route', () => { errors: [], }); + (legacyMigrate as jest.Mock).mockResolvedValue(getRuleMock(getQueryRuleParams())); + context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse()) ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts index 83a40005d0148..85c324008856c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts @@ -13,10 +13,20 @@ import { getFindResultWithSingleHit, getDeleteRequestById, getEmptySavedObjectsResponse, + getRuleMock, } from '../__mocks__/request_responses'; import { requestContextMock, serverMock, requestMock } from '../__mocks__'; import { deleteRulesRoute } from './delete_rules_route'; import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; +import { legacyMigrate } from '../../rules/utils'; + +jest.mock('../../rules/utils', () => { + const actual = jest.requireActual('../../rules/utils'); + return { + ...actual, + legacyMigrate: jest.fn(), + }; +}); describe('delete_rules', () => { let server: ReturnType; @@ -29,6 +39,8 @@ describe('delete_rules', () => { clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); + (legacyMigrate as jest.Mock).mockResolvedValue(getRuleMock(getQueryRuleParams())); + deleteRulesRoute(server.router); }); @@ -54,6 +66,7 @@ describe('delete_rules', () => { test('returns 404 when deleting a single rule that does not exist with a valid actionClient and alertClient', async () => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); + (legacyMigrate as jest.Mock).mockResolvedValue(null); const response = await server.inject( getDeleteRequest(), requestContextMock.convertContext(context) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_rule_execution_events_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_rule_execution_events_route.test.ts index 37b1f41328674..c59a0e4dfe176 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_rule_execution_events_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_rule_execution_events_route.test.ts @@ -12,8 +12,6 @@ import { } from '../__mocks__/request_responses'; import { getRuleExecutionEventsRoute } from './get_rule_execution_events_route'; -// TODO: Add additional tests for param validation - describe('getRuleExecutionEventsRoute', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts index 6abac6e946380..fa75be49a61c5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts @@ -23,9 +23,18 @@ import { patchRulesBulkRoute } from './patch_rules_bulk_route'; import { getCreateRulesSchemaMock } from '../../../../../common/detection_engine/schemas/request/rule_schemas.mock'; import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { legacyMigrate } from '../../rules/utils'; jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); +jest.mock('../../rules/utils', () => { + const actual = jest.requireActual('../../rules/utils'); + return { + ...actual, + legacyMigrate: jest.fn(), + }; +}); + describe('patch_rules_bulk', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); @@ -40,6 +49,8 @@ describe('patch_rules_bulk', () => { clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); // update succeeds + (legacyMigrate as jest.Mock).mockResolvedValue(getRuleMock(getQueryRuleParams())); + patchRulesBulkRoute(server.router, ml, logger); }); @@ -54,6 +65,7 @@ describe('patch_rules_bulk', () => { test('returns an error in the response when updating a single rule that does not exist', async () => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); + (legacyMigrate as jest.Mock).mockResolvedValue(null); const response = await server.inject( getPatchBulkRequest(), requestContextMock.convertContext(context) @@ -148,6 +160,8 @@ describe('patch_rules_bulk', () => { describe('request validation', () => { test('rejects payloads with no ID', async () => { + (legacyMigrate as jest.Mock).mockResolvedValue(null); + const request = requestMock.create({ method: 'patch', path: DETECTION_ENGINE_RULES_BULK_UPDATE, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts index cbcf5540b4f15..87c2e79922457 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts @@ -21,9 +21,18 @@ import { requestContextMock, serverMock, requestMock } from '../__mocks__'; import { patchRulesRoute } from './patch_rules_route'; import { getPatchRulesSchemaMock } from '../../../../../common/detection_engine/schemas/request/patch_rules_schema.mock'; import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; +import { legacyMigrate } from '../../rules/utils'; jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); +jest.mock('../../rules/utils', () => { + const actual = jest.requireActual('../../rules/utils'); + return { + ...actual, + legacyMigrate: jest.fn(), + }; +}); + describe('patch_rules', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); @@ -41,6 +50,8 @@ describe('patch_rules', () => { getRuleExecutionSummarySucceeded() ); + (legacyMigrate as jest.Mock).mockResolvedValue(getRuleMock(getQueryRuleParams())); + patchRulesRoute(server.router, ml); }); @@ -55,6 +66,7 @@ describe('patch_rules', () => { test('returns 404 when updating a single rule that does not exist', async () => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); + (legacyMigrate as jest.Mock).mockResolvedValue(null); const response = await server.inject( getPatchRequest(), requestContextMock.convertContext(context) @@ -67,6 +79,7 @@ describe('patch_rules', () => { }); test('returns error if requesting a non-rule', async () => { + (legacyMigrate as jest.Mock).mockResolvedValue(null); clients.rulesClient.find.mockResolvedValue(nonRuleFindResult()); const response = await server.inject( getPatchRequest(), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts index 3dc8ab5199c7f..4c50ee58e2d7f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts @@ -14,6 +14,7 @@ import { getBulkActionEditRequest, getFindResultWithSingleHit, getFindResultWithMultiHits, + getRuleMock, } from '../__mocks__/request_responses'; import { requestContextMock, serverMock, requestMock } from '../__mocks__'; import { performBulkActionRoute } from './perform_bulk_action_route'; @@ -23,10 +24,20 @@ import { } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema.mock'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { readRules } from '../../rules/read_rules'; +import { legacyMigrate } from '../../rules/utils'; +import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); jest.mock('../../rules/read_rules', () => ({ readRules: jest.fn() })); +jest.mock('../../rules/utils', () => { + const actual = jest.requireActual('../../rules/utils'); + return { + ...actual, + legacyMigrate: jest.fn(), + }; +}); + describe('perform_bulk_action', () => { const readRulesMock = readRules as jest.Mock; let server: ReturnType; @@ -40,6 +51,8 @@ describe('perform_bulk_action', () => { logger = loggingSystemMock.createLogger(); ({ clients, context } = requestContextMock.createTools()); ml = mlServicesMock.createSetupContract(); + (legacyMigrate as jest.Mock).mockResolvedValue(getRuleMock(getQueryRuleParams())); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); performBulkActionRoute(server.router, ml, logger); }); @@ -220,7 +233,10 @@ describe('perform_bulk_action', () => { readRulesMock.mockImplementationOnce(() => Promise.resolve({ ...mockRule, params: { ...mockRule.params, type: 'machine_learning' } }) ); - + (legacyMigrate as jest.Mock).mockResolvedValue({ + ...mockRule, + params: { ...mockRule.params, type: 'machine_learning' }, + }); const request = requestMock.create({ method: 'patch', path: DETECTION_ENGINE_RULES_BULK_ACTION, @@ -271,7 +287,10 @@ describe('perform_bulk_action', () => { readRulesMock.mockImplementationOnce(() => Promise.resolve({ ...mockRule, params: { ...mockRule.params, index: ['index-*'] } }) ); - + (legacyMigrate as jest.Mock).mockResolvedValue({ + ...mockRule, + params: { ...mockRule.params, index: ['index-*'] }, + }); const request = requestMock.create({ method: 'patch', path: DETECTION_ENGINE_RULES_BULK_ACTION, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts index 88720646fa6cd..e0c9289a562e7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts @@ -21,9 +21,18 @@ import { BulkError } from '../utils'; import { getCreateRulesSchemaMock } from '../../../../../common/detection_engine/schemas/request/rule_schemas.mock'; import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { legacyMigrate } from '../../rules/utils'; jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); +jest.mock('../../rules/utils', () => { + const actual = jest.requireActual('../../rules/utils'); + return { + ...actual, + legacyMigrate: jest.fn(), + }; +}); + describe('update_rules_bulk', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); @@ -40,6 +49,8 @@ describe('update_rules_bulk', () => { clients.appClient.getSignalsIndex.mockReturnValue('.siem-signals-test-index'); + (legacyMigrate as jest.Mock).mockResolvedValue(getRuleMock(getQueryRuleParams())); + updateRulesBulkRoute(server.router, ml, logger); }); @@ -54,6 +65,8 @@ describe('update_rules_bulk', () => { test('returns 200 as a response when updating a single rule that does not exist', async () => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); + (legacyMigrate as jest.Mock).mockResolvedValue(null); + const expected: BulkError[] = [ { error: { message: 'rule_id: "rule-1" not found', status_code: 404 }, @@ -116,6 +129,8 @@ describe('update_rules_bulk', () => { describe('request validation', () => { test('rejects payloads with no ID', async () => { + (legacyMigrate as jest.Mock).mockResolvedValue(null); + const noIdRequest = requestMock.create({ method: 'put', path: DETECTION_ENGINE_RULES_BULK_UPDATE, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts index 39040f4c9c4fc..7f2d5c3bde7e0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts @@ -21,9 +21,18 @@ import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { updateRulesRoute } from './update_rules_route'; import { getUpdateRulesSchemaMock } from '../../../../../common/detection_engine/schemas/request/rule_schemas.mock'; import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; +import { legacyMigrate } from '../../rules/utils'; jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); +jest.mock('../../rules/utils', () => { + const actual = jest.requireActual('../../rules/utils'); + return { + ...actual, + legacyMigrate: jest.fn(), + }; +}); + describe('update_rules', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); @@ -42,6 +51,8 @@ describe('update_rules', () => { ); clients.appClient.getSignalsIndex.mockReturnValue('.siem-signals-test-index'); + (legacyMigrate as jest.Mock).mockResolvedValue(getRuleMock(getQueryRuleParams())); + updateRulesRoute(server.router, ml); }); @@ -56,6 +67,7 @@ describe('update_rules', () => { test('returns 404 when updating a single rule that does not exist', async () => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); + (legacyMigrate as jest.Mock).mockResolvedValue(null); const response = await server.inject( getUpdateRequest(), requestContextMock.convertContext(context) @@ -69,6 +81,7 @@ describe('update_rules', () => { }); test('returns error when updating non-rule', async () => { + (legacyMigrate as jest.Mock).mockResolvedValue(null); clients.rulesClient.find.mockResolvedValue(nonRuleFindResult()); const response = await server.inject( getUpdateRequest(), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/client_for_routes/client_interface.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/client_for_routes/client_interface.ts index 8d0cae91f1987..3f353732abbc5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/client_for_routes/client_interface.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/client_for_routes/client_interface.ts @@ -9,7 +9,7 @@ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { ExecutionLogTableSortColumns, RuleExecutionEvent, - RuleExecutionStatusType, + RuleExecutionStatus, RuleExecutionSummary, } from '../../../../../common/detection_engine/schemas/common'; import { GetAggregateRuleExecutionEventsResponse } from '../../../../../common/detection_engine/schemas/response'; @@ -19,7 +19,7 @@ export interface GetAggregateExecutionEventsArgs { start: string; end: string; queryText: string; - statusFilters: RuleExecutionStatusType[]; + statusFilters: RuleExecutionStatus[]; page: number; perPage: number; sortField: ExecutionLogTableSortColumns; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log/event_log_reader.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log/event_log_reader.ts index 78e8e62702d47..20cc2f0e78c77 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log/event_log_reader.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log/event_log_reader.ts @@ -6,8 +6,8 @@ */ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { MAX_EXECUTION_EVENTS_DISPLAYED } from '@kbn/securitysolution-rules'; import { IEventLogClient } from '@kbn/event-log-plugin/server'; +import { MAX_EXECUTION_EVENTS_DISPLAYED } from '@kbn/securitysolution-rules'; import { RuleExecutionEvent, @@ -18,13 +18,14 @@ import { invariant } from '../../../../../common/utils/invariant'; import { withSecuritySpan } from '../../../../utils/with_security_span'; import { GetAggregateExecutionEventsArgs } from '../client_for_routes/client_interface'; import { - RULE_SAVED_OBJECT_TYPE, RULE_EXECUTION_LOG_PROVIDER, + RULE_SAVED_OBJECT_TYPE, RuleExecutionLogAction, } from './constants'; import { formatExecutionEventResponse, getExecutionEventAggregation, + mapRuleExecutionStatusToPlatformStatus, } from './get_execution_event_aggregation'; import { EXECUTION_UUID_FIELD, @@ -62,10 +63,15 @@ export const createEventLogReader = (eventLog: IEventLogClient): IEventLogReader let totalExecutions: number | undefined; // If 0 or 3 statuses are selected we can search for all statuses and don't need this pre-filter by ID if (statusFilters.length > 0 && statusFilters.length < 3) { + const outcomes = mapRuleExecutionStatusToPlatformStatus(statusFilters); + const outcomeFilter = outcomes.length ? `OR event.outcome:(${outcomes.join(' OR ')})` : ''; const statusResults = await eventLog.aggregateEventsBySavedObjectIds(soType, soIds, { start, end, - filter: `kibana.alert.rule.execution.status:(${statusFilters.join(' OR ')})`, + // Also query for `event.outcome` to catch executions that only contain platform events + filter: `kibana.alert.rule.execution.status:(${statusFilters.join( + ' OR ' + )}) ${outcomeFilter}`, aggs: { totalExecutions: { cardinality: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log/get_execution_event_aggregation/index.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log/get_execution_event_aggregation/index.test.ts index 7cee84cd64594..dcd592d7a70fc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log/get_execution_event_aggregation/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log/get_execution_event_aggregation/index.test.ts @@ -12,6 +12,7 @@ * 2.0. */ +import { RuleExecutionStatus } from '../../../../../../common/detection_engine/schemas/common'; import { MAX_EXECUTION_EVENTS_DISPLAYED } from '@kbn/securitysolution-rules'; import { @@ -20,6 +21,8 @@ import { formatSortForTermsSort, getExecutionEventAggregation, getProviderAndActionFilter, + mapPlatformStatusToRuleExecutionStatus, + mapRuleExecutionStatusToPlatformStatus, } from '.'; describe('getExecutionEventAggregation', () => { @@ -69,7 +72,7 @@ describe('getExecutionEventAggregation', () => { sort: [{ notsortable: { order: 'asc' } }], }); }).toThrowErrorMatchingInlineSnapshot( - `"Invalid sort field \\"notsortable\\" - must be one of [timestamp,duration_ms,indexing_duration_ms,search_duration_ms,gap_duration_ms,schedule_delay_ms,num_triggered_actions]"` + `"Invalid sort field \\"notsortable\\" - must be one of [timestamp,duration_ms,indexing_duration_ms,search_duration_ms,gap_duration_s,schedule_delay_ms,num_triggered_actions]"` ); }); @@ -82,7 +85,7 @@ describe('getExecutionEventAggregation', () => { sort: [{ notsortable: { order: 'asc' } }, { timestamp: { order: 'asc' } }], }); }).toThrowErrorMatchingInlineSnapshot( - `"Invalid sort field \\"notsortable\\" - must be one of [timestamp,duration_ms,indexing_duration_ms,search_duration_ms,gap_duration_ms,schedule_delay_ms,num_triggered_actions]"` + `"Invalid sort field \\"notsortable\\" - must be one of [timestamp,duration_ms,indexing_duration_ms,search_duration_ms,gap_duration_s,schedule_delay_ms,num_triggered_actions]"` ); }); @@ -206,7 +209,7 @@ describe('getExecutionEventAggregation', () => { top_hits: { size: 1, _source: { - includes: ['event.outcome', 'message'], + includes: ['error.message', 'event.outcome', 'message'], }, }, }, @@ -647,7 +650,7 @@ describe('formatExecutionEventResponse', () => { timed_out: false, indexing_duration_ms: 7, search_duration_ms: 480, - gap_duration_ms: 0, + gap_duration_s: 0, security_status: 'succeeded', security_message: 'succeeded', }, @@ -670,7 +673,7 @@ describe('formatExecutionEventResponse', () => { timed_out: false, indexing_duration_ms: 0, search_duration_ms: 9, - gap_duration_ms: 0, + gap_duration_s: 0, security_status: 'succeeded', security_message: 'succeeded', }, @@ -965,7 +968,7 @@ describe('formatExecutionEventResponse', () => { timed_out: true, indexing_duration_ms: 7, search_duration_ms: 480, - gap_duration_ms: 0, + gap_duration_s: 0, security_status: 'succeeded', security_message: 'succeeded', }, @@ -988,7 +991,7 @@ describe('formatExecutionEventResponse', () => { timed_out: false, indexing_duration_ms: 0, search_duration_ms: 9, - gap_duration_ms: 0, + gap_duration_s: 0, security_status: 'succeeded', security_message: 'succeeded', }, @@ -1288,7 +1291,7 @@ describe('formatExecutionEventResponse', () => { timed_out: true, indexing_duration_ms: 7, search_duration_ms: 480, - gap_duration_ms: 0, + gap_duration_s: 0, security_status: 'succeeded', security_message: 'succeeded', }, @@ -1311,7 +1314,7 @@ describe('formatExecutionEventResponse', () => { timed_out: false, indexing_duration_ms: 0, search_duration_ms: 9, - gap_duration_ms: 0, + gap_duration_s: 0, security_status: 'succeeded', security_message: 'succeeded', }, @@ -1319,3 +1322,53 @@ describe('formatExecutionEventResponse', () => { }); }); }); + +describe('mapRuleStatusToPlatformStatus', () => { + test('should correctly translate empty array to empty array', () => { + expect(mapRuleExecutionStatusToPlatformStatus([])).toEqual([]); + }); + + test('should correctly translate RuleExecutionStatus.failed to `failure` platform status', () => { + expect(mapRuleExecutionStatusToPlatformStatus([RuleExecutionStatus.failed])).toEqual([ + 'failure', + ]); + }); + + test('should correctly translate RuleExecutionStatus.succeeded to `success` platform status', () => { + expect(mapRuleExecutionStatusToPlatformStatus([RuleExecutionStatus.succeeded])).toEqual([ + 'success', + ]); + }); + + test('should correctly translate RuleExecutionStatus.["going to run"] to empty array platform status', () => { + expect(mapRuleExecutionStatusToPlatformStatus([RuleExecutionStatus['going to run']])).toEqual( + [] + ); + }); + + test("should correctly translate multiple RuleExecutionStatus's to platform statuses", () => { + expect( + mapRuleExecutionStatusToPlatformStatus([ + RuleExecutionStatus.succeeded, + RuleExecutionStatus.failed, + RuleExecutionStatus['going to run'], + ]).sort() + ).toEqual(['failure', 'success']); + }); +}); + +describe('mapPlatformStatusToRuleExecutionStatus', () => { + test('should correctly translate `invalid` platform status to `undefined`', () => { + expect(mapPlatformStatusToRuleExecutionStatus('')).toEqual(undefined); + }); + + test('should correctly translate `failure` platform status to `RuleExecutionStatus.failed`', () => { + expect(mapPlatformStatusToRuleExecutionStatus('failure')).toEqual(RuleExecutionStatus.failed); + }); + + test('should correctly translate `success` platform status to `RuleExecutionStatus.succeeded`', () => { + expect(mapPlatformStatusToRuleExecutionStatus('success')).toEqual( + RuleExecutionStatus.succeeded + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log/get_execution_event_aggregation/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log/get_execution_event_aggregation/index.ts index 4cf8e0bd5f1dc..dcf2fbfe911bd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log/get_execution_event_aggregation/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log/get_execution_event_aggregation/index.ts @@ -10,7 +10,10 @@ import { BadRequestError } from '@kbn/securitysolution-es-utils'; import { flatMap, get } from 'lodash'; import { MAX_EXECUTION_EVENTS_DISPLAYED } from '@kbn/securitysolution-rules'; import { AggregateEventsBySavedObjectResult } from '@kbn/event-log-plugin/server'; -import { AggregateRuleExecutionEvent } from '../../../../../../common/detection_engine/schemas/common'; +import { + AggregateRuleExecutionEvent, + RuleExecutionStatus, +} from '../../../../../../common/detection_engine/schemas/common'; import { GetAggregateRuleExecutionEventsResponse } from '../../../../../../common/detection_engine/schemas/response'; import { ExecutionEventAggregationOptions, @@ -22,6 +25,7 @@ import { // Base ECS fields const ACTION_FIELD = 'event.action'; const DURATION_FIELD = 'event.duration'; +const ERROR_MESSAGE_FIELD = 'error.message'; const MESSAGE_FIELD = 'message'; const PROVIDER_FIELD = 'event.provider'; const OUTCOME_FIELD = 'event.outcome'; @@ -48,7 +52,7 @@ const SORT_FIELD_TO_AGG_MAPPING: Record = { duration_ms: 'ruleExecution>executionDuration', indexing_duration_ms: 'securityMetrics>indexDuration', search_duration_ms: 'securityMetrics>searchDuration', - gap_duration_ms: 'securityMetrics>gapDuration', + gap_duration_s: 'securityMetrics>gapDuration', schedule_delay_ms: 'ruleExecution>scheduleDelay', num_triggered_actions: 'ruleExecution>numTriggeredActions', // TODO: To be added in https://github.com/elastic/kibana/pull/126210 @@ -166,7 +170,7 @@ export const getExecutionEventAggregation = ({ top_hits: { size: 1, _source: { - includes: [OUTCOME_FIELD, MESSAGE_FIELD], + includes: [ERROR_MESSAGE_FIELD, OUTCOME_FIELD, MESSAGE_FIELD], }, }, }, @@ -293,11 +297,18 @@ export const formatAggExecutionEventFromBucket = ( // security fields indexing_duration_ms: bucket?.securityMetrics?.indexDuration?.value ?? 0, search_duration_ms: bucket?.securityMetrics?.searchDuration?.value ?? 0, - gap_duration_ms: bucket?.securityMetrics?.gapDuration?.value ?? 0, + gap_duration_s: bucket?.securityMetrics?.gapDuration?.value ?? 0, + // If security_status isn't available, use platform status from `event.outcome`, but translate to RuleExecutionStatus security_status: bucket?.securityStatus?.status?.hits?.hits[0]?._source?.kibana?.alert?.rule?.execution - ?.status, - security_message: bucket?.securityStatus?.message?.hits?.hits[0]?._source?.message, + ?.status ?? + mapPlatformStatusToRuleExecutionStatus( + bucket?.ruleExecution?.outcomeAndMessage?.hits?.hits[0]?._source?.event?.outcome + ), + // If security_message isn't available, use `error.message` instead for platform errors since it is more descriptive than `message` + security_message: + bucket?.securityStatus?.message?.hits?.hits[0]?._source?.message ?? + bucket?.ruleExecution?.outcomeAndMessage?.hits?.hits[0]?._source?.error?.message, }; }; @@ -353,3 +364,40 @@ export const formatSortForTermsSort = (sort: estypes.Sort) => { ) ); }; + +/** + * Maps a RuleExecutionStatus[] to string[] of associated platform statuses. Useful for querying specific platform + * events based on security status values + * @param ruleStatuses RuleExecutionStatus[] + */ +export const mapRuleExecutionStatusToPlatformStatus = ( + ruleStatuses: RuleExecutionStatus[] +): string[] => { + return flatMap(ruleStatuses, (rs) => { + switch (rs) { + case RuleExecutionStatus.failed: + return 'failure'; + case RuleExecutionStatus.succeeded: + return 'success'; + default: + return []; + } + }); +}; + +/** + * Maps a platform status string to RuleExecutionStatus + * @param platformStatus string, i.e. `failure` or `success` + */ +export const mapPlatformStatusToRuleExecutionStatus = ( + platformStatus: string +): RuleExecutionStatus | undefined => { + switch (platformStatus) { + case 'failure': + return RuleExecutionStatus.failed; + case 'success': + return RuleExecutionStatus.succeeded; + default: + return undefined; + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts index 96fba6703a537..2a54ce1e976d4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts @@ -7,14 +7,24 @@ import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; import { savedObjectsClientMock } from '@kbn/core/server/mocks'; -import { getFindResultWithSingleHit } from '../routes/__mocks__/request_responses'; +import { getRuleMock, getFindResultWithSingleHit } from '../routes/__mocks__/request_responses'; import { updatePrepackagedRules } from './update_prepacked_rules'; import { patchRules } from './patch_rules'; import { getAddPrepackagedRulesSchemaDecodedMock } from '../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema.mock'; import { ruleExecutionLogMock } from '../rule_execution_log/__mocks__'; +import { legacyMigrate } from './utils'; +import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; jest.mock('./patch_rules'); +jest.mock('./utils', () => { + const actual = jest.requireActual('./utils'); + return { + ...actual, + legacyMigrate: jest.fn(), + }; +}); + describe('updatePrepackagedRules', () => { let rulesClient: ReturnType; let savedObjectsClient: ReturnType; @@ -24,6 +34,8 @@ describe('updatePrepackagedRules', () => { rulesClient = rulesClientMock.create(); savedObjectsClient = savedObjectsClientMock.create(); ruleExecutionLog = ruleExecutionLogMock.forRoutes.create(); + + (legacyMigrate as jest.Mock).mockResolvedValue(getRuleMock(getQueryRuleParams())); }); it('should omit actions and enabled when calling patchRules', async () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/utils.test.ts index 5151a58d0d885..0952da3182e01 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/utils.test.ts @@ -13,6 +13,8 @@ import { transformToAlertThrottle, transformFromAlertThrottle, transformActions, + legacyMigrate, + getUpdatedActionsParams, } from './utils'; import { RuleAction, SanitizedRule } from '@kbn/alerting-plugin/common'; import { RuleParams } from '../schemas/rule_schemas'; @@ -23,6 +25,67 @@ import { import { FullResponseSchema } from '../../../../common/detection_engine/schemas/request'; // eslint-disable-next-line no-restricted-imports import { LegacyRuleActions } from '../rule_actions/legacy_types'; +import { + getEmptyFindResult, + legacyGetSiemNotificationRuleActionsSOResultWithSingleHit, + legacyGetDailyNotificationResult, + legacyGetHourlyNotificationResult, + legacyGetWeeklyNotificationResult, +} from '../routes/__mocks__/request_responses'; +import { requestContextMock } from '../routes/__mocks__'; + +const getRuleLegacyActions = (): SanitizedRule => + ({ + id: '123', + notifyWhen: 'onThrottleInterval', + name: 'Simple Rule Query', + tags: ['__internal_rule_id:ruleId', '__internal_immutable:false'], + alertTypeId: 'siem.queryRule', + consumer: 'siem', + enabled: true, + throttle: '1h', + apiKeyOwner: 'elastic', + createdBy: 'elastic', + updatedBy: 'elastic', + muteAll: false, + mutedInstanceIds: [], + monitoring: { execution: { history: [], calculated_metrics: { success_ratio: 0 } } }, + mapped_params: { risk_score: 1, severity: '60-high' }, + schedule: { interval: '5m' }, + actions: [], + params: { + author: [], + description: 'Simple Rule Query', + ruleId: 'ruleId', + falsePositives: [], + from: 'now-6m', + immutable: false, + outputIndex: '.siem-signals-default', + maxSignals: 100, + riskScore: 1, + riskScoreMapping: [], + severity: 'high', + severityMapping: [], + threat: [], + to: 'now', + references: [], + version: 1, + exceptionsList: [], + type: 'query', + language: 'kuery', + index: ['auditbeat-*'], + query: 'user.name: root or user.name: admin', + }, + snoozeEndTime: null, + updatedAt: '2022-03-31T21:47:25.695Z', + createdAt: '2022-03-31T21:47:16.379Z', + scheduledTaskId: '21bb9b60-b13c-11ec-99d0-asdfasdfasf', + executionStatus: { + status: 'pending', + lastExecutionDate: '2022-03-31T21:47:25.695Z', + lastDuration: 0, + }, + } as unknown as SanitizedRule); describe('utils', () => { describe('#calculateInterval', () => { @@ -588,4 +651,415 @@ describe('utils', () => { ]); }); }); + + describe('#legacyMigrate', () => { + const ruleId = '123'; + const connectorId = '456'; + const { clients } = requestContextMock.createTools(); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + test('it does no cleanup or migration if no legacy reminants found', async () => { + clients.rulesClient.find.mockResolvedValueOnce(getEmptyFindResult()); + clients.savedObjectsClient.find.mockResolvedValueOnce({ + page: 0, + per_page: 0, + total: 0, + saved_objects: [], + }); + + const rule = { + ...getRuleLegacyActions(), + id: ruleId, + actions: [], + throttle: null, + notifyWhen: 'onActiveAlert', + muteAll: true, + } as SanitizedRule; + + const migratedRule = await legacyMigrate({ + rulesClient: clients.rulesClient, + savedObjectsClient: clients.savedObjectsClient, + rule, + }); + + expect(clients.rulesClient.delete).not.toHaveBeenCalled(); + expect(clients.savedObjectsClient.delete).not.toHaveBeenCalled(); + expect(migratedRule).toEqual(rule); + }); + + // Even if a rule is created with no actions pre 7.16, a + // siem-detection-engine-rule-actions SO is still created + test('it migrates a rule with no actions', async () => { + // siem.notifications is not created for a rule with no actions + clients.rulesClient.find.mockResolvedValueOnce(getEmptyFindResult()); + // siem-detection-engine-rule-actions SO is still created + clients.savedObjectsClient.find.mockResolvedValueOnce( + legacyGetSiemNotificationRuleActionsSOResultWithSingleHit(['none'], ruleId, connectorId) + ); + + const migratedRule = await legacyMigrate({ + rulesClient: clients.rulesClient, + savedObjectsClient: clients.savedObjectsClient, + rule: { + ...getRuleLegacyActions(), + id: ruleId, + actions: [], + throttle: null, + notifyWhen: 'onActiveAlert', + muteAll: true, + }, + }); + + expect(clients.rulesClient.delete).not.toHaveBeenCalled(); + expect(clients.savedObjectsClient.delete).toHaveBeenCalledWith( + 'siem-detection-engine-rule-actions', + 'ID_OF_LEGACY_SIDECAR_NO_ACTIONS' + ); + expect(migratedRule?.actions).toEqual([]); + expect(migratedRule?.throttle).toBeNull(); + expect(migratedRule?.muteAll).toBeTruthy(); + expect(migratedRule?.notifyWhen).toEqual('onActiveAlert'); + }); + + test('it migrates a rule with every rule run action', async () => { + // siem.notifications is not created for a rule with actions run every rule run + clients.rulesClient.find.mockResolvedValueOnce(getEmptyFindResult()); + // siem-detection-engine-rule-actions SO is still created + clients.savedObjectsClient.find.mockResolvedValueOnce( + legacyGetSiemNotificationRuleActionsSOResultWithSingleHit(['rule'], ruleId, connectorId) + ); + + const migratedRule = await legacyMigrate({ + rulesClient: clients.rulesClient, + savedObjectsClient: clients.savedObjectsClient, + rule: { + ...getRuleLegacyActions(), + id: ruleId, + actions: [ + { + actionTypeId: '.email', + params: { + subject: 'Test Actions', + to: ['test@test.com'], + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + }, + id: connectorId, + group: 'default', + }, + ], + throttle: null, + notifyWhen: 'onActiveAlert', + muteAll: false, + }, + }); + + expect(clients.rulesClient.delete).not.toHaveBeenCalled(); + expect(clients.savedObjectsClient.delete).toHaveBeenCalledWith( + 'siem-detection-engine-rule-actions', + 'ID_OF_LEGACY_SIDECAR_RULE_RUN_ACTIONS' + ); + expect(migratedRule?.actions).toEqual([ + { + id: connectorId, + actionTypeId: '.email', + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + subject: 'Test Actions', + to: ['test@test.com'], + }, + }, + ]); + expect(migratedRule?.notifyWhen).toEqual('onActiveAlert'); + expect(migratedRule?.throttle).toBeNull(); + expect(migratedRule?.muteAll).toBeFalsy(); + }); + + test('it migrates a rule with daily legacy actions', async () => { + // siem.notifications is not created for a rule with no actions + clients.rulesClient.find.mockResolvedValueOnce({ + page: 1, + perPage: 1, + total: 1, + data: [legacyGetDailyNotificationResult(connectorId, ruleId)], + }); + // siem-detection-engine-rule-actions SO is still created + clients.savedObjectsClient.find.mockResolvedValueOnce( + legacyGetSiemNotificationRuleActionsSOResultWithSingleHit(['daily'], ruleId, connectorId) + ); + + const migratedRule = await legacyMigrate({ + rulesClient: clients.rulesClient, + savedObjectsClient: clients.savedObjectsClient, + rule: { + ...getRuleLegacyActions(), + id: ruleId, + actions: [], + throttle: null, + notifyWhen: 'onActiveAlert', + }, + }); + + expect(clients.rulesClient.delete).toHaveBeenCalledWith({ id: '456' }); + expect(clients.savedObjectsClient.delete).toHaveBeenCalledWith( + 'siem-detection-engine-rule-actions', + 'ID_OF_LEGACY_SIDECAR_DAILY_ACTIONS' + ); + expect(migratedRule?.actions).toEqual([ + { + actionTypeId: '.email', + group: 'default', + id: connectorId, + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + }, + ]); + expect(migratedRule?.throttle).toEqual('1d'); + expect(migratedRule?.notifyWhen).toEqual('onThrottleInterval'); + expect(migratedRule?.muteAll).toBeFalsy(); + }); + + test('it migrates a rule with hourly legacy actions', async () => { + // siem.notifications is not created for a rule with no actions + clients.rulesClient.find.mockResolvedValueOnce({ + page: 1, + perPage: 1, + total: 1, + data: [legacyGetHourlyNotificationResult(connectorId, ruleId)], + }); + // siem-detection-engine-rule-actions SO is still created + clients.savedObjectsClient.find.mockResolvedValueOnce( + legacyGetSiemNotificationRuleActionsSOResultWithSingleHit(['hourly'], ruleId, connectorId) + ); + + const migratedRule = await legacyMigrate({ + rulesClient: clients.rulesClient, + savedObjectsClient: clients.savedObjectsClient, + rule: { + ...getRuleLegacyActions(), + id: ruleId, + actions: [], + throttle: null, + notifyWhen: 'onActiveAlert', + }, + }); + + expect(clients.rulesClient.delete).toHaveBeenCalledWith({ id: '456' }); + expect(clients.savedObjectsClient.delete).toHaveBeenCalledWith( + 'siem-detection-engine-rule-actions', + 'ID_OF_LEGACY_SIDECAR_HOURLY_ACTIONS' + ); + expect(migratedRule?.actions).toEqual([ + { + actionTypeId: '.email', + group: 'default', + id: connectorId, + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + }, + ]); + expect(migratedRule?.throttle).toEqual('1h'); + expect(migratedRule?.notifyWhen).toEqual('onThrottleInterval'); + expect(migratedRule?.muteAll).toBeFalsy(); + }); + + test('it migrates a rule with weekly legacy actions', async () => { + // siem.notifications is not created for a rule with no actions + clients.rulesClient.find.mockResolvedValueOnce({ + page: 1, + perPage: 1, + total: 1, + data: [legacyGetWeeklyNotificationResult(connectorId, ruleId)], + }); + // siem-detection-engine-rule-actions SO is still created + clients.savedObjectsClient.find.mockResolvedValueOnce( + legacyGetSiemNotificationRuleActionsSOResultWithSingleHit(['weekly'], ruleId, connectorId) + ); + + const migratedRule = await legacyMigrate({ + rulesClient: clients.rulesClient, + savedObjectsClient: clients.savedObjectsClient, + rule: { + ...getRuleLegacyActions(), + id: ruleId, + actions: [], + throttle: null, + notifyWhen: 'onActiveAlert', + }, + }); + + expect(clients.rulesClient.delete).toHaveBeenCalledWith({ id: '456' }); + expect(clients.savedObjectsClient.delete).toHaveBeenCalledWith( + 'siem-detection-engine-rule-actions', + 'ID_OF_LEGACY_SIDECAR_WEEKLY_ACTIONS' + ); + expect(migratedRule?.actions).toEqual([ + { + actionTypeId: '.email', + group: 'default', + id: connectorId, + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + }, + ]); + expect(migratedRule?.throttle).toEqual('7d'); + expect(migratedRule?.notifyWhen).toEqual('onThrottleInterval'); + expect(migratedRule?.muteAll).toBeFalsy(); + }); + }); + + describe('#getUpdatedActionsParams', () => { + it('updates one action', () => { + const { id, ...rule } = { + ...getRuleLegacyActions(), + id: '123', + actions: [], + throttle: null, + notifyWhen: 'onActiveAlert', + } as SanitizedRule; + + expect( + getUpdatedActionsParams({ + rule: { + ...rule, + id, + }, + ruleThrottle: '1h', + actions: [ + { + actionRef: 'action_0', + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['a@a.com'], + subject: 'Test Actions', + }, + action_type_id: '.email', + }, + ], + references: [ + { + id: '61ec7a40-b076-11ec-bb3f-1f063f8e06cf', + type: 'alert', + name: 'alert_0', + }, + { + id: '1234', + type: 'action', + name: 'action_0', + }, + ], + }) + ).toEqual({ + ...rule, + actions: [ + { + actionTypeId: '.email', + group: 'default', + id: '1234', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + subject: 'Test Actions', + to: ['a@a.com'], + }, + }, + ], + throttle: '1h', + notifyWhen: 'onThrottleInterval', + }); + }); + + it('updates multiple actions', () => { + const { id, ...rule } = { + ...getRuleLegacyActions(), + id: '123', + actions: [], + throttle: null, + notifyWhen: 'onActiveAlert', + } as SanitizedRule; + + expect( + getUpdatedActionsParams({ + rule: { + ...rule, + id, + }, + ruleThrottle: '1h', + actions: [ + { + actionRef: 'action_0', + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Rule email', + }, + action_type_id: '.email', + }, + { + actionRef: 'action_1', + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + }, + action_type_id: '.slack', + }, + ], + references: [ + { + id: '064e3160-b076-11ec-bb3f-1f063f8e06cf', + type: 'alert', + name: 'alert_0', + }, + { + id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', + type: 'action', + name: 'action_0', + }, + { + id: '207fa0e0-c04e-11ec-8a52-4fb92379525a', + type: 'action', + name: 'action_1', + }, + ], + }) + ).toEqual({ + ...rule, + actions: [ + { + actionTypeId: '.email', + group: 'default', + id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + subject: 'Rule email', + to: ['test@test.com'], + }, + }, + { + actionTypeId: '.slack', + group: 'default', + id: '207fa0e0-c04e-11ec-8a52-4fb92379525a', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + }, + }, + ], + throttle: '1h', + notifyWhen: 'onThrottleInterval', + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/utils.ts index c7858a131cb1f..dd25676a758e4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/utils.ts @@ -28,6 +28,7 @@ import type { } from '@kbn/securitysolution-io-ts-alerting-types'; import type { ListArrayOrUndefined } from '@kbn/securitysolution-io-ts-list-types'; import type { VersionOrUndefined } from '@kbn/securitysolution-io-ts-types'; +import { SavedObjectReference } from '@kbn/core/server'; import { RuleAction, RuleNotifyWhenType, SanitizedRule } from '@kbn/alerting-plugin/common'; import { RulesClient } from '@kbn/alerting-plugin/server'; import { @@ -63,7 +64,11 @@ import { NOTIFICATION_THROTTLE_RULE, } from '../../../../common/constants'; // eslint-disable-next-line no-restricted-imports -import { LegacyRuleActions } from '../rule_actions/legacy_types'; +import { + LegacyIRuleActionsAttributes, + LegacyRuleActions, + LegacyRuleAlertSavedObjectAction, +} from '../rule_actions/legacy_types'; import { FullResponseSchema } from '../../../../common/detection_engine/schemas/request'; import { transformAlertToRuleAction } from '../../../../common/detection_engine/transform_actions'; // eslint-disable-next-line no-restricted-imports @@ -302,6 +307,59 @@ export const maybeMute = async ({ } }; +/** + * Translate legacy action sidecar action to rule action + */ +export const getUpdatedActionsParams = ({ + rule, + ruleThrottle, + actions, + references, +}: { + rule: SanitizedRule; + ruleThrottle: string | null; + actions: LegacyRuleAlertSavedObjectAction[]; + references: SavedObjectReference[]; +}): Omit, 'id'> => { + const { id, ...restOfRule } = rule; + + const actionReference = references.reduce>( + (acc, reference) => { + acc[reference.name] = reference; + return acc; + }, + {} + ); + + if (isEmpty(actionReference)) { + throw new Error( + `An error occurred migrating legacy action for rule with id:${id}. Connector reference id not found.` + ); + } + // If rule has an action on any other interval (other than on every + // rule run), need to move the action info from the sidecar/legacy action + // into the rule itself + return { + ...restOfRule, + actions: actions.reduce((acc, action) => { + const { actionRef, action_type_id: actionTypeId, ...resOfAction } = action; + if (!actionReference[actionRef]) { + return acc; + } + return [ + ...acc, + { + ...resOfAction, + id: actionReference[actionRef].id, + actionTypeId, + }, + ]; + }, []), + throttle: transformToAlertThrottle(ruleThrottle), + notifyWhen: transformToNotifyWhen(ruleThrottle), + }; +}; + /** * Determines if rule needs to be migrated from legacy actions * and returns necessary pieces for the updated rule @@ -332,7 +390,7 @@ export const legacyMigrate = async ({ }, }, }), - savedObjectsClient.find({ + savedObjectsClient.find({ type: legacyRuleActionsSavedObjectType, hasReference: { type: 'alert', @@ -341,29 +399,57 @@ export const legacyMigrate = async ({ }), ]); - if (siemNotification != null && siemNotification.data.length > 0) { - await Promise.all([ - rulesClient.delete({ id: siemNotification.data[0].id }), - legacyRuleActionsSO != null && legacyRuleActionsSO.saved_objects.length > 0 - ? savedObjectsClient.delete( - legacyRuleActionsSavedObjectType, - legacyRuleActionsSO.saved_objects[0].id - ) - : null, - ]); + const siemNotificationsExist = siemNotification != null && siemNotification.data.length > 0; + const legacyRuleNotificationSOsExist = + legacyRuleActionsSO != null && legacyRuleActionsSO.saved_objects.length > 0; + + // Assumption: if no legacy sidecar SO or notification rule types exist + // that reference the rule in question, assume rule actions are not legacy + if (!siemNotificationsExist && !legacyRuleNotificationSOsExist) { + return rule; + } + // If the legacy notification rule type ("siem.notification") exist, + // migration and cleanup are needed + if (siemNotificationsExist) { + await rulesClient.delete({ id: siemNotification.data[0].id }); + } + // If legacy notification sidecar ("siem-detection-engine-rule-actions") + // exist, migration and cleanup are needed + if (legacyRuleNotificationSOsExist) { + // Delete the legacy sidecar SO + await savedObjectsClient.delete( + legacyRuleActionsSavedObjectType, + legacyRuleActionsSO.saved_objects[0].id + ); + + // If "siem-detection-engine-rule-actions" notes that `ruleThrottle` is + // "no_actions" or "rule", rule has no actions or rule is set to run + // action on every rule run. In these cases, sidecar deletion is the only + // cleanup needed and updates to the "throttle" and "notifyWhen". "siem.notification" are + // not created for these action types + if ( + legacyRuleActionsSO.saved_objects[0].attributes.ruleThrottle === 'no_actions' || + legacyRuleActionsSO.saved_objects[0].attributes.ruleThrottle === 'rule' + ) { + return rule; + } + + // Use "legacyRuleActionsSO" instead of "siemNotification" as "siemNotification" is not created + // until a rule is run and added to task manager. That means that if by chance a user has a rule + // with actions which they have yet to enable, the actions would be lost. Instead, + // "legacyRuleActionsSO" is created on rule creation (pre 7.15) and we can rely on it to be there + const migratedRule = getUpdatedActionsParams({ + rule, + ruleThrottle: legacyRuleActionsSO.saved_objects[0].attributes.ruleThrottle, + actions: legacyRuleActionsSO.saved_objects[0].attributes.actions, + references: legacyRuleActionsSO.saved_objects[0].references, + }); - const { id, ...restOfRule } = rule; - const migratedRule = { - ...restOfRule, - actions: siemNotification.data[0].actions, - throttle: siemNotification.data[0].schedule.interval, - notifyWhen: transformToNotifyWhen(siemNotification.data[0].throttle), - }; await rulesClient.update({ id: rule.id, data: migratedRule, }); + return { id: rule.id, ...migratedRule }; } - return rule; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts index d699e71947853..9213d6c5b278c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts @@ -910,6 +910,32 @@ export const sampleDocSearchResultsNoSortIdNoHits = ( }, }); +/** + * + * @param count Total number of hits to create + * @param guids List of _id values for the hits. If this array is smaller than count, the remaining hits will receive a default value. + * @param ips List of source.ip values for the hits. If this array is smaller than count, the remaining hits will receive a default value. + * @param destIps List of destination.ip values for the hits. If this array is smaller than count, the remaining hits will receive a default value. + * @param sortIds List of sort IDs. The same list is inserted into every hit. + * @returns Array of mock hits + */ +export const repeatedHitsWithSortId = ( + count: number, + guids: string[], + ips?: Array, + destIps?: Array, + sortIds?: string[] +): SignalSourceHit[] => { + return Array.from({ length: count }).map((x, index) => ({ + ...sampleDocWithSortId( + guids[index], + sortIds, + ips ? ips[index] : '127.0.0.1', + destIps ? destIps[index] : '127.0.0.1' + ), + })); +}; + export const repeatedSearchResultsWithSortId = ( total: number, pageSize: number, @@ -929,14 +955,7 @@ export const repeatedSearchResultsWithSortId = ( hits: { total, max_score: 100, - hits: Array.from({ length: pageSize }).map((x, index) => ({ - ...sampleDocWithSortId( - guids[index], - sortIds, - ips ? ips[index] : '127.0.0.1', - destIps ? destIps[index] : '127.0.0.1' - ), - })), + hits: repeatedHitsWithSortId(pageSize, guids, ips, destIps, sortIds), }, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts index 66394d4bca81d..e38ee3952cadb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts @@ -15,7 +15,7 @@ import { RuleExecutorServices, } from '@kbn/alerting-plugin/server'; import { GenericBulkCreateResponse } from '../rule_types/factories'; -import { AnomalyResults, Anomaly } from '../../machine_learning'; +import { Anomaly } from '../../machine_learning'; import { BuildRuleMessage } from './rule_messages'; import { BulkCreate, WrapHits } from './types'; import { CompleteRule, MachineLearningRuleParams } from '../schemas/rule_schemas'; @@ -23,7 +23,7 @@ import { buildReasonMessageForMlAlert } from './reason_formatters'; import { BaseFieldsLatest } from '../../../../common/detection_engine/schemas/alerts'; interface BulkCreateMlSignalsParams { - someResult: AnomalyResults; + anomalyHits: Array>; completeRule: CompleteRule; services: RuleExecutorServices; logger: Logger; @@ -65,32 +65,23 @@ export const transformAnomalyFieldsToEcs = (anomaly: Anomaly): EcsAnomaly => { }; const transformAnomalyResultsToEcs = ( - results: AnomalyResults -): estypes.SearchResponse => { - const transformedHits = results.hits.hits.map(({ _source, ...rest }) => ({ + results: Array> +): Array> => { + return results.map(({ _source, ...rest }) => ({ ...rest, _source: transformAnomalyFieldsToEcs( // @ts-expect-error @elastic/elasticsearch _source is optional _source ), })); - - // @ts-expect-error Anomaly is not assignable to EcsAnomaly - return { - ...results, - hits: { - ...results.hits, - hits: transformedHits, - }, - }; }; export const bulkCreateMlSignals = async ( params: BulkCreateMlSignalsParams ): Promise> => { - const anomalyResults = params.someResult; + const anomalyResults = params.anomalyHits; const ecsResults = transformAnomalyResultsToEcs(anomalyResults); - const wrappedDocs = params.wrapHits(ecsResults.hits.hits, buildReasonMessageForMlAlert); + const wrappedDocs = params.wrapHits(ecsResults, buildReasonMessageForMlAlert); return params.bulkCreate(wrappedDocs); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.ts index 2070d487c49d0..22c11b565e909 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.ts @@ -97,21 +97,21 @@ export const mlExecutor = async ({ exceptionItems, }); - const filteredAnomalyResults = await filterEventsAgainstList({ + const [filteredAnomalyHits, _] = await filterEventsAgainstList({ listClient, exceptionsList: exceptionItems, logger, - eventSearchResult: anomalyResults, + events: anomalyResults.hits.hits, buildRuleMessage, }); - const anomalyCount = filteredAnomalyResults.hits.hits.length; + const anomalyCount = filteredAnomalyHits.length; if (anomalyCount) { logger.debug(buildRuleMessage(`Found ${anomalyCount} signals from ML anomalies.`)); } const { success, errors, bulkCreateDuration, createdItemsCount, createdItems } = await bulkCreateMlSignals({ - someResult: filteredAnomalyResults, + anomalyHits: filteredAnomalyHits, completeRule, services, logger, @@ -124,7 +124,7 @@ export const mlExecutor = async ({ // The legacy ES client does not define failures when it can be present on the structure, hence why I have the & { failures: [] } const shardFailures = ( - filteredAnomalyResults._shards as typeof filteredAnomalyResults._shards & { + anomalyResults._shards as typeof anomalyResults._shards & { failures: []; } ).failures ?? []; @@ -134,7 +134,7 @@ export const mlExecutor = async ({ return mergeReturns([ result, createSearchAfterReturnType({ - success: success && filteredAnomalyResults._shards.failed === 0, + success: success && anomalyResults._shards.failed === 0, errors: [...errors, ...searchErrors], createdSignalsCount: createdItemsCount, createdSignals: createdItems, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events.test.ts index df9bb0cd59f83..bed5f96fbc233 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events.test.ts @@ -9,10 +9,10 @@ import { sampleDocWithSortId } from '../__mocks__/es_results'; import { listMock } from '@kbn/lists-plugin/server/mocks'; import { getSearchListItemResponseMock } from '@kbn/lists-plugin/common/schemas/response/search_list_item_schema.mock'; -import { filterEvents } from './filter_events'; +import { partitionEvents } from './filter_events'; import { FieldSet } from './types'; -describe('filterEvents', () => { +describe('partitionEvents', () => { let listClient = listMock.getListClient(); let events = [sampleDocWithSortId('123', undefined, '1.1.1.1')]; @@ -43,11 +43,12 @@ describe('filterEvents', () => { matchedSet: new Set([JSON.stringify(['1.1.1.1'])]), }, ]; - const field = filterEvents({ + const [included, excluded] = partitionEvents({ events, fieldAndSetTuples, }); - expect([...field]).toEqual([]); + expect(included).toEqual([]); + expect(excluded).toEqual(events); }); test('it does not filter out the event if it is "excluded"', () => { @@ -59,11 +60,12 @@ describe('filterEvents', () => { matchedSet: new Set([JSON.stringify(['1.1.1.1'])]), }, ]; - const field = filterEvents({ + const [included, excluded] = partitionEvents({ events, fieldAndSetTuples, }); - expect([...field]).toEqual(events); + expect(included).toEqual(events); + expect(excluded).toEqual([]); }); test('it does NOT filter out the event if the field is not found', () => { @@ -75,11 +77,12 @@ describe('filterEvents', () => { matchedSet: new Set([JSON.stringify(['1.1.1.1'])]), }, ]; - const field = filterEvents({ + const [included, excluded] = partitionEvents({ events, fieldAndSetTuples, }); - expect([...field]).toEqual(events); + expect(included).toEqual(events); + expect(excluded).toEqual([]); }); test('it does NOT filter out the event if it is in both an inclusion and exclusion list', () => { @@ -100,10 +103,11 @@ describe('filterEvents', () => { }, ]; - const field = filterEvents({ + const [included, excluded] = partitionEvents({ events, fieldAndSetTuples, }); - expect([...field]).toEqual(events); + expect(included).toEqual(events); + expect(excluded).toEqual([]); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events.ts index d267153a4813a..aee98b7e0ff5b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events.ts @@ -5,6 +5,7 @@ * 2.0. */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { partition } from 'lodash'; import { FilterEventsOptions } from './types'; /** @@ -12,12 +13,14 @@ import { FilterEventsOptions } from './types'; * If the entry is in both an inclusion and exclusion list it will not be filtered out. * @param events The events to check against * @param fieldAndSetTuples The field and set tuples + * @returns A tuple where the first element is an array of alerts that should be created and second element is + * an array of alerts that matched the exception and should not be created. */ -export const filterEvents = ({ +export const partitionEvents = ({ events, fieldAndSetTuples, -}: FilterEventsOptions): Array> => { - return events.filter((item) => { +}: FilterEventsOptions): [Array>, Array>] => { + return partition(events, (item) => { return fieldAndSetTuples .map((tuple) => { const eventItem = item.fields ? item.fields[tuple.field] : undefined; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events_against_list.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events_against_list.test.ts index 8c33cf2ea1314..22dc5136fcded 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events_against_list.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events_against_list.test.ts @@ -8,7 +8,7 @@ import uuid from 'uuid'; import { filterEventsAgainstList } from './filter_events_against_list'; import { buildRuleMessageMock as buildRuleMessage } from '../rule_messages.mock'; -import { mockLogger, repeatedSearchResultsWithSortId } from '../__mocks__/es_results'; +import { mockLogger, repeatedHitsWithSortId } from '../__mocks__/es_results'; import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; import { listMock } from '@kbn/lists-plugin/server/mocks'; @@ -30,11 +30,11 @@ describe('filterEventsAgainstList', () => { }); it('should respond with eventSearchResult if exceptionList is empty array', async () => { - const res = await filterEventsAgainstList({ + const [included, excluded] = await filterEventsAgainstList({ logger: mockLogger, listClient, exceptionsList: [], - eventSearchResult: repeatedSearchResultsWithSortId(4, 4, someGuids.slice(0, 3), [ + events: repeatedHitsWithSortId(4, someGuids.slice(0, 3), [ '1.1.1.1', '2.2.2.2', '3.3.3.3', @@ -42,15 +42,16 @@ describe('filterEventsAgainstList', () => { ]), buildRuleMessage, }); - expect(res.hits.hits.length).toEqual(4); + expect(included.length).toEqual(4); + expect(excluded.length).toEqual(0); }); it('should respond with eventSearchResult if exceptionList does not contain value list exceptions', async () => { - const res = await filterEventsAgainstList({ + const [included, excluded] = await filterEventsAgainstList({ logger: mockLogger, listClient, exceptionsList: [getExceptionListItemSchemaMock()], - eventSearchResult: repeatedSearchResultsWithSortId(4, 4, someGuids.slice(0, 3), [ + events: repeatedHitsWithSortId(4, someGuids.slice(0, 3), [ '1.1.1.1', '2.2.2.2', '3.3.3.3', @@ -58,7 +59,8 @@ describe('filterEventsAgainstList', () => { ]), buildRuleMessage, }); - expect(res.hits.hits.length).toEqual(4); + expect(included.length).toEqual(4); + expect(excluded.length).toEqual(0); expect((mockLogger.debug as unknown as jest.Mock).mock.calls[0][0]).toContain( 'no exception items of type list found - returning original search result' ); @@ -79,14 +81,15 @@ describe('filterEventsAgainstList', () => { }, ]; - const res = await filterEventsAgainstList({ + const [included, excluded] = await filterEventsAgainstList({ logger: mockLogger, listClient, exceptionsList: [exceptionItem], - eventSearchResult: repeatedSearchResultsWithSortId(4, 4, someGuids.slice(0, 3)), + events: repeatedHitsWithSortId(4, someGuids.slice(0, 3)), buildRuleMessage, }); - expect(res.hits.hits.length).toEqual(4); + expect(included.length).toEqual(4); + expect(excluded.length).toEqual(0); }); it('should respond with less items in the list if some values match', async () => { @@ -110,11 +113,11 @@ describe('filterEventsAgainstList', () => { })) ) ); - const res = await filterEventsAgainstList({ + const [included, excluded] = await filterEventsAgainstList({ logger: mockLogger, listClient, exceptionsList: [exceptionItem], - eventSearchResult: repeatedSearchResultsWithSortId(4, 4, someGuids.slice(0, 3), [ + events: repeatedHitsWithSortId(4, someGuids.slice(0, 3), [ '1.1.1.1', '2.2.2.2', '3.3.3.3', @@ -126,10 +129,11 @@ describe('filterEventsAgainstList', () => { expect((listClient.searchListItemByValues as jest.Mock).mock.calls[0][0].listId).toEqual( 'ci-badguys.txt' ); - expect(res.hits.hits.length).toEqual(2); + expect(included.length).toEqual(2); + expect(excluded.length).toEqual(2); // @ts-expect-error - const ipVals = res.hits.hits.map((item) => item._source.source.ip); + const ipVals = included.map((item) => item._source.source.ip); expect(['3.3.3.3', '7.7.7.7']).toEqual(ipVals); }); @@ -170,11 +174,11 @@ describe('filterEventsAgainstList', () => { { ...getSearchListItemResponseMock(), value: ['6.6.6.6'] }, ]); - const res = await filterEventsAgainstList({ + const [included, excluded] = await filterEventsAgainstList({ logger: mockLogger, listClient, exceptionsList: [exceptionItem, exceptionItemAgain], - eventSearchResult: repeatedSearchResultsWithSortId(9, 9, someGuids.slice(0, 9), [ + events: repeatedHitsWithSortId(9, someGuids.slice(0, 9), [ '1.1.1.1', '2.2.2.2', '3.3.3.3', @@ -188,10 +192,11 @@ describe('filterEventsAgainstList', () => { buildRuleMessage, }); expect(listClient.searchListItemByValues as jest.Mock).toHaveBeenCalledTimes(2); - expect(res.hits.hits.length).toEqual(6); + expect(included.length).toEqual(6); + expect(excluded.length).toEqual(3); // @ts-expect-error - const ipVals = res.hits.hits.map((item) => item._source.source.ip); + const ipVals = included.map((item) => item._source.source.ip); expect(['1.1.1.1', '3.3.3.3', '5.5.5.5', '7.7.7.7', '8.8.8.8', '9.9.9.9']).toEqual(ipVals); }); @@ -231,11 +236,11 @@ describe('filterEventsAgainstList', () => { { ...getSearchListItemResponseMock(), value: ['6.6.6.6'] }, ]); - const res = await filterEventsAgainstList({ + const [included, excluded] = await filterEventsAgainstList({ logger: mockLogger, listClient, exceptionsList: [exceptionItem, exceptionItemAgain], - eventSearchResult: repeatedSearchResultsWithSortId(9, 9, someGuids.slice(0, 9), [ + events: repeatedHitsWithSortId(9, someGuids.slice(0, 9), [ '1.1.1.1', '2.2.2.2', '3.3.3.3', @@ -250,8 +255,9 @@ describe('filterEventsAgainstList', () => { }); expect(listClient.searchListItemByValues as jest.Mock).toHaveBeenCalledTimes(2); // @ts-expect-error - const ipVals = res.hits.hits.map((item) => item._source.source.ip); - expect(res.hits.hits.length).toEqual(7); + const ipVals = included.map((item) => item._source.source.ip); + expect(included.length).toEqual(7); + expect(excluded.length).toEqual(2); expect(['1.1.1.1', '3.3.3.3', '4.4.4.4', '5.5.5.5', '7.7.7.7', '8.8.8.8', '9.9.9.9']).toEqual( ipVals @@ -290,12 +296,11 @@ describe('filterEventsAgainstList', () => { { ...getSearchListItemResponseMock(), value: ['4.4.4.4'] }, ]); - const res = await filterEventsAgainstList({ + const [included, excluded] = await filterEventsAgainstList({ logger: mockLogger, listClient, exceptionsList: [exceptionItem], - eventSearchResult: repeatedSearchResultsWithSortId( - 9, + events: repeatedHitsWithSortId( 9, someGuids.slice(0, 9), [ @@ -324,10 +329,11 @@ describe('filterEventsAgainstList', () => { buildRuleMessage, }); expect(listClient.searchListItemByValues as jest.Mock).toHaveBeenCalledTimes(2); - expect(res.hits.hits.length).toEqual(8); + expect(included.length).toEqual(8); + expect(excluded.length).toEqual(1); // @ts-expect-error - const ipVals = res.hits.hits.map((item) => item._source.source.ip); + const ipVals = included.map((item) => item._source?.source?.ip); expect([ '1.1.1.1', '2.2.2.2', @@ -368,11 +374,11 @@ describe('filterEventsAgainstList', () => { { ...getSearchListItemResponseMock(), value: ['2.2.2.2'] }, ]); - const res = await filterEventsAgainstList({ + const [included, excluded] = await filterEventsAgainstList({ logger: mockLogger, listClient, exceptionsList: [exceptionItem], - eventSearchResult: repeatedSearchResultsWithSortId(9, 9, someGuids.slice(0, 9), [ + events: repeatedHitsWithSortId(9, someGuids.slice(0, 9), [ '1.1.1.1', '2.2.2.2', '3.3.3.3', @@ -386,10 +392,11 @@ describe('filterEventsAgainstList', () => { buildRuleMessage, }); expect(listClient.searchListItemByValues as jest.Mock).toHaveBeenCalledTimes(2); - expect(res.hits.hits.length).toEqual(9); + expect(included.length).toEqual(9); + expect(excluded.length).toEqual(0); // @ts-expect-error - const ipVals = res.hits.hits.map((item) => item._source.source.ip); + const ipVals = included.map((item) => item._source.source.ip); expect([ '1.1.1.1', '2.2.2.2', @@ -435,12 +442,11 @@ describe('filterEventsAgainstList', () => { { ...getSearchListItemResponseMock(), value: ['3.3.3.3', '4.4.4.4'] }, ]); - const res = await filterEventsAgainstList({ + const [included, excluded] = await filterEventsAgainstList({ logger: mockLogger, listClient, exceptionsList: [exceptionItem], - eventSearchResult: repeatedSearchResultsWithSortId( - 3, + events: repeatedHitsWithSortId( 3, someGuids.slice(0, 3), [ @@ -467,16 +473,17 @@ describe('filterEventsAgainstList', () => { ['2.2.2.2', '3.3.3.3'], ['3.3.3.3', '4.4.4.4'], ]); - expect(res.hits.hits.length).toEqual(2); + expect(included.length).toEqual(2); + expect(excluded.length).toEqual(1); // @ts-expect-error - const sourceIpVals = res.hits.hits.map((item) => item._source.source.ip); + const sourceIpVals = included.map((item) => item._source.source.ip); expect([ ['1.1.1.1', '1.1.1.1'], ['1.1.1.1', '2.2.2.2'], ]).toEqual(sourceIpVals); // @ts-expect-error - const destIpVals = res.hits.hits.map((item) => item._source.destination.ip); + const destIpVals = included.map((item) => item._source.destination.ip); expect([ ['1.1.1.1', '2.2.2.2'], ['2.2.2.2', '3.3.3.3'], @@ -497,14 +504,15 @@ describe('filterEventsAgainstList', () => { }, }, ]; - const res = await filterEventsAgainstList({ + const [included, excluded] = await filterEventsAgainstList({ logger: mockLogger, listClient, exceptionsList: [exceptionItem], - eventSearchResult: repeatedSearchResultsWithSortId(4, 4, someGuids.slice(0, 3)), + events: repeatedHitsWithSortId(4, someGuids.slice(0, 3)), buildRuleMessage, }); - expect(res.hits.hits.length).toEqual(0); + expect(included.length).toEqual(0); + expect(excluded.length).toEqual(4); }); it('should respond with less items in the list if some values match', async () => { @@ -528,11 +536,11 @@ describe('filterEventsAgainstList', () => { })) ) ); - const res = await filterEventsAgainstList({ + const [included, excluded] = await filterEventsAgainstList({ logger: mockLogger, listClient, exceptionsList: [exceptionItem], - eventSearchResult: repeatedSearchResultsWithSortId(4, 4, someGuids.slice(0, 3), [ + events: repeatedHitsWithSortId(4, someGuids.slice(0, 3), [ '1.1.1.1', '2.2.2.2', '3.3.3.3', @@ -544,7 +552,8 @@ describe('filterEventsAgainstList', () => { expect((listClient.searchListItemByValues as jest.Mock).mock.calls[0][0].listId).toEqual( 'ci-badguys.txt' ); - expect(res.hits.hits.length).toEqual(2); + expect(included.length).toEqual(2); + expect(excluded.length).toEqual(2); }); it('should respond with the same items in the list given one exception item with two entries of type list and array of values in document', async () => { @@ -582,12 +591,11 @@ describe('filterEventsAgainstList', () => { { ...getSearchListItemResponseMock(), value: ['3.3.3.3', '4.4.4.4'] }, ]); - const res = await filterEventsAgainstList({ + const [included, excluded] = await filterEventsAgainstList({ logger: mockLogger, listClient, exceptionsList: [exceptionItem], - eventSearchResult: repeatedSearchResultsWithSortId( - 3, + events: repeatedHitsWithSortId( 3, someGuids.slice(0, 3), [ @@ -614,16 +622,17 @@ describe('filterEventsAgainstList', () => { ['2.2.2.2', '3.3.3.3'], ['3.3.3.3', '4.4.4.4'], ]); - expect(res.hits.hits.length).toEqual(2); + expect(included.length).toEqual(2); + expect(excluded.length).toEqual(1); // @ts-expect-error - const sourceIpVals = res.hits.hits.map((item) => item._source.source.ip); + const sourceIpVals = included.map((item) => item._source.source.ip); expect([ ['1.1.1.1', '2.2.2.2'], ['2.2.2.2', '3.3.3.3'], ]).toEqual(sourceIpVals); // @ts-expect-error - const destIpVals = res.hits.hits.map((item) => item._source.destination.ip); + const destIpVals = included.map((item) => item._source.destination.ip); expect([ ['2.2.2.2', '3.3.3.3'], ['3.3.3.3', '4.4.4.4'], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events_against_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events_against_list.ts index 49a8ab0781eb0..7b9f6fde51842 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events_against_list.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/filter_events_against_list.ts @@ -5,13 +5,12 @@ * 2.0. */ -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { entriesList, ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { hasLargeValueList } from '@kbn/securitysolution-list-utils'; -import { FilterEventsAgainstListOptions } from './types'; -import { filterEvents } from './filter_events'; +import { FilterEventsAgainstListOptions, FilterEventsAgainstListReturn } from './types'; +import { partitionEvents } from './filter_events'; import { createFieldAndSetTuples } from './create_field_and_set_tuples'; /** @@ -39,9 +38,9 @@ export const filterEventsAgainstList = async ({ listClient, exceptionsList, logger, - eventSearchResult, + events, buildRuleMessage, -}: FilterEventsAgainstListOptions): Promise> => { +}: FilterEventsAgainstListOptions): Promise> => { try { const atLeastOneLargeValueList = exceptionsList.some(({ entries }) => hasLargeValueList(entries) @@ -51,46 +50,41 @@ export const filterEventsAgainstList = async ({ logger.debug( buildRuleMessage('no exception items of type list found - returning original search result') ); - return eventSearchResult; + return [events, []]; } const valueListExceptionItems = exceptionsList.filter((listItem: ExceptionListItemSchema) => { return listItem.entries.every((entry) => entriesList.is(entry)); }); - const res = await valueListExceptionItems.reduce>>>( + // Every event starts out in the 'included' list, and each value list item checks all the + // current 'included' events and moves events that match the exception to the 'excluded' list + return valueListExceptionItems.reduce>>( async ( - filteredAccum: Promise>>, + filteredAccum: Promise>, exceptionItem: ExceptionListItemSchema ) => { - const events = await filteredAccum; + const [includedEvents, excludedEvents] = await filteredAccum; const fieldAndSetTuples = await createFieldAndSetTuples({ - events, + events: includedEvents, exceptionItem, listClient, logger, buildRuleMessage, }); - const filteredEvents = filterEvents({ events, fieldAndSetTuples }); - const diff = eventSearchResult.hits.hits.length - filteredEvents.length; + const [nextIncludedEvents, nextExcludedEvents] = partitionEvents({ + events: includedEvents, + fieldAndSetTuples, + }); logger.debug( - buildRuleMessage(`Exception with id ${exceptionItem.id} filtered out ${diff} events`) + buildRuleMessage( + `Exception with id ${exceptionItem.id} filtered out ${nextExcludedEvents.length} events` + ) ); - return filteredEvents; + return [nextIncludedEvents, [...excludedEvents, ...nextExcludedEvents]]; }, - Promise.resolve>>(eventSearchResult.hits.hits) + Promise.resolve>([events, []]) ); - - return { - took: eventSearchResult.took, - timed_out: eventSearchResult.timed_out, - _shards: eventSearchResult._shards, - hits: { - total: res.length, - max_score: eventSearchResult.hits.max_score, - hits: res, - }, - }; } catch (exc) { throw new Error(`Failed to query large value based lists index. Reason: ${exc.message}`); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/types.ts index 051685c67db4a..f5d438f54bdb3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filters/types.ts @@ -15,10 +15,15 @@ export interface FilterEventsAgainstListOptions { listClient: ListClient; exceptionsList: ExceptionListItemSchema[]; logger: Logger; - eventSearchResult: estypes.SearchResponse; + events: Array>; buildRuleMessage: BuildRuleMessage; } +export type FilterEventsAgainstListReturn = [ + Array>, + Array> +]; + export interface CreateSetToFilterAgainstOptions { events: Array>; field: string; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts index b96b0e6dc7148..4f3a798a327ce 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts @@ -1004,16 +1004,12 @@ describe('searchAfterAndBulkCreate', () => { }); expect(mockEnrichment).toHaveBeenCalledWith( - expect.objectContaining({ - hits: expect.objectContaining({ - hits: expect.arrayContaining([ - expect.objectContaining({ - ...sampleDocWithSortId(), - _id: expect.any(String), - }), - ]), + expect.objectContaining([ + expect.objectContaining({ + ...sampleDocWithSortId(), + _id: expect.any(String), }), - }) + ]) ); expect(success).toEqual(true); expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(4); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts index 69c001898b217..84ef95b856a5f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts @@ -127,11 +127,11 @@ export const searchAfterAndBulkCreate = async ({ // filter out the search results that match with the values found in the list. // the resulting set are signals to be indexed, given they are not duplicates // of signals already present in the signals index. - const filteredEvents = await filterEventsAgainstList({ + const [includedEvents, _] = await filterEventsAgainstList({ listClient, exceptionsList, logger, - eventSearchResult: mergedSearchResults, + events: mergedSearchResults.hits.hits, buildRuleMessage, }); @@ -139,16 +139,11 @@ export const searchAfterAndBulkCreate = async ({ // if there isn't anything after going through the value list filter // skip the call to bulk create and proceed to the next search_after, // if there is a sort id to continue the search_after with. - if (filteredEvents.hits.hits.length !== 0) { + if (includedEvents.length !== 0) { // make sure we are not going to create more signals than maxSignals allows - if (signalsCreatedCount + filteredEvents.hits.hits.length > tuple.maxSignals) { - filteredEvents.hits.hits = filteredEvents.hits.hits.slice( - 0, - tuple.maxSignals - signalsCreatedCount - ); - } - const enrichedEvents = await enrichment(filteredEvents); - const wrappedDocs = wrapHits(enrichedEvents.hits.hits, buildReasonMessage); + const limitedEvents = includedEvents.slice(0, tuple.maxSignals - signalsCreatedCount); + const enrichedEvents = await enrichment(limitedEvents); + const wrappedDocs = wrapHits(enrichedEvents, buildReasonMessage); const { bulkCreateDuration: bulkDuration, @@ -171,9 +166,7 @@ export const searchAfterAndBulkCreate = async ({ signalsCreatedCount += createdCount; logger.debug(buildRuleMessage(`created ${createdCount} signals`)); logger.debug(buildRuleMessage(`signalsCreatedCount: ${signalsCreatedCount}`)); - logger.debug( - buildRuleMessage(`enrichedEvents.hits.hits: ${enrichedEvents.hits.hits.length}`) - ); + logger.debug(buildRuleMessage(`enrichedEvents.hits.hits: ${enrichedEvents.length}`)); sendAlertTelemetryEvents( logger, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.test.ts index 36bb90936620b..d598b84ea99e2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.test.ts @@ -9,100 +9,86 @@ import { selectEvents, enrichEndpointAlertsSignalID } from './send_telemetry_eve describe('sendAlertTelemetry', () => { it('selectEvents', () => { - const filteredEvents = { - took: 0, - timed_out: false, - _shards: { - total: 1, - successful: 1, - failed: 0, - skipped: 0, + const filteredEvents = [ + { + _index: 'x', + _type: 'x', + _id: 'x', + _score: 0, + _source: { + '@timestamp': 'x', + key1: 'hello', + data_stream: { + dataset: 'endpoint.events', + }, + event: { + id: 'foo', + }, + }, }, - hits: { - total: 2, - max_score: 0, - hits: [ - { - _index: 'x', - _type: 'x', - _id: 'x', - _score: 0, - _source: { - '@timestamp': 'x', - key1: 'hello', - data_stream: { - dataset: 'endpoint.events', - }, - event: { - id: 'foo', - }, - }, + { + _index: 'x', + _type: 'x', + _id: 'x', + _score: 0, + _source: { + '@timestamp': 'x', + key2: 'hello', + data_stream: { + dataset: 'endpoint.alerts', + other: 'x', + }, + event: { + id: 'bar', }, - { - _index: 'x', - _type: 'x', - _id: 'x', - _score: 0, - _source: { - '@timestamp': 'x', - key2: 'hello', - data_stream: { - dataset: 'endpoint.alerts', - other: 'x', - }, - event: { - id: 'bar', - }, - }, + }, + }, + { + _index: 'x', + _type: 'x', + _id: 'x', + _score: 0, + _source: { + '@timestamp': 'x', + key3: 'hello', + data_stream: {}, + event: { + id: 'baz', }, - { - _index: 'x', - _type: 'x', - _id: 'x', - _score: 0, - _source: { - '@timestamp': 'x', - key3: 'hello', - data_stream: {}, - event: { - id: 'baz', - }, - }, + }, + }, + { + _index: 'y', + _type: 'y', + _id: 'y', + _score: 0, + _source: { + '@timestamp': 'y', + key3: 'hello', + data_stream: { + dataset: 'endpoint.alerts', + other: 'y', }, - { - _index: 'y', - _type: 'y', - _id: 'y', - _score: 0, - _source: { - '@timestamp': 'y', - key3: 'hello', - data_stream: { - dataset: 'endpoint.alerts', - other: 'y', - }, - event: { - id: 'not-in-map', - }, - }, + event: { + id: 'not-in-map', }, - { - _index: 'z', - _type: 'z', - _id: 'z', - _score: 0, - _source: { - '@timestamp': 'z', - key3: 'no-event-id', - data_stream: { - dataset: 'endpoint.alerts', - other: 'z', - }, - }, + }, + }, + { + _index: 'z', + _type: 'z', + _id: 'z', + _score: 0, + _source: { + '@timestamp': 'z', + key3: 'no-event-id', + data_stream: { + dataset: 'endpoint.alerts', + other: 'z', }, - ], + }, }, - }; + ]; const joinMap = new Map([ ['foo', '1234'], ['bar', 'abcd'], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.ts index 511f148f13d68..e419fdf632137 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.ts @@ -9,7 +9,7 @@ import { Logger } from '@kbn/core/server'; import { ITelemetryEventsSender } from '../../telemetry/sender'; import { TelemetryEvent } from '../../telemetry/types'; import { BuildRuleMessage } from './rule_messages'; -import { SignalSearchResponse, SignalSource } from './types'; +import { SignalSource, SignalSourceHit } from './types'; interface SearchResultSource { _source: SignalSource; @@ -18,9 +18,9 @@ interface SearchResultSource { type CreatedSignalId = string; type AlertId = string; -export function selectEvents(filteredEvents: SignalSearchResponse): TelemetryEvent[] { +export function selectEvents(filteredEvents: SignalSourceHit[]): TelemetryEvent[] { // @ts-expect-error @elastic/elasticsearch _source is optional - const sources: TelemetryEvent[] = filteredEvents.hits.hits.map(function ( + const sources: TelemetryEvent[] = filteredEvents.map(function ( obj: SearchResultSource ): TelemetryEvent { return obj._source; @@ -46,7 +46,7 @@ export function enrichEndpointAlertsSignalID( export function sendAlertTelemetryEvents( logger: Logger, eventsTelemetry: ITelemetryEventsSender | undefined, - filteredEvents: SignalSearchResponse, + filteredEvents: SignalSourceHit[], createdEvents: SignalSource[], buildRuleMessage: BuildRuleMessage ) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_enrichment.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_enrichment.ts index bc31ee660aad8..4c5391d238b31 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_enrichment.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_enrichment.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SignalSearchResponse, SignalsEnrichment } from '../types'; +import { SignalsEnrichment } from '../types'; import { enrichSignalThreatMatches } from './enrich_signal_threat_matches'; import { BuildThreatEnrichmentOptions, GetMatchedThreats } from './types'; import { getThreatList } from './get_threat_list'; @@ -55,6 +55,5 @@ export const buildThreatEnrichment = ({ return threatResponse.hits.hits; }; - return (signals: SignalSearchResponse): Promise => - enrichSignalThreatMatches(signals, getMatchedThreats, threatIndicatorPath); + return (signals) => enrichSignalThreatMatches(signals, getMatchedThreats, threatIndicatorPath); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts index c5d86c9ab460c..2587c76907ccb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts @@ -10,7 +10,7 @@ import { getFilter } from '../get_filter'; import { searchAfterAndBulkCreate } from '../search_after_bulk_create'; import { buildReasonMessageForThreatMatchAlert } from '../reason_formatters'; import { CreateEventSignalOptions } from './types'; -import { SearchAfterAndBulkCreateReturnType, SignalSearchResponse } from '../types'; +import { SearchAfterAndBulkCreateReturnType, SignalSourceHit } from '../types'; import { getAllThreatListHits } from './get_threat_list'; import { enrichSignalThreatMatches, @@ -112,7 +112,7 @@ export const createEventSignal = async ({ ) ); - const threatEnrichment = (signals: SignalSearchResponse): Promise => + const threatEnrichment = (signals: SignalSourceHit[]): Promise => enrichSignalThreatMatches( signals, () => Promise.resolve(threatListHits), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts index 66e44e5796eb6..b6df435c04dda 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts @@ -8,6 +8,7 @@ import { get } from 'lodash'; import { ENRICHMENT_DESTINATION_PATH } from '../../../../../common/constants'; import { ENRICHMENT_TYPES } from '../../../../../common/cti/constants'; +import { SignalSourceHit } from '../types'; import { getThreatListItemMock } from './build_threat_mapping_filter.mock'; import { @@ -16,11 +17,7 @@ import { groupAndMergeSignalMatches, getSignalMatchesFromThreatList, } from './enrich_signal_threat_matches'; -import { - getNamedQueryMock, - getSignalHitMock, - getSignalsResponseMock, -} from './enrich_signal_threat_matches.mock'; +import { getNamedQueryMock, getSignalHitMock } from './enrich_signal_threat_matches.mock'; import { GetMatchedThreats, ThreatListItem, ThreatMatchNamedQuery } from './types'; import { encodeThreatMatchNamedQuery } from './utils'; @@ -507,14 +504,14 @@ describe('enrichSignalThreatMatches', () => { }); it('performs no enrichment if there are no signals', async () => { - const signals = getSignalsResponseMock([]); + const signals: SignalSourceHit[] = []; const enrichedSignals = await enrichSignalThreatMatches( signals, getMatchedThreats, indicatorPath ); - expect(enrichedSignals.hits.hits).toEqual([]); + expect(enrichedSignals).toEqual([]); }); it('preserves existing threat.enrichments objects on signals', async () => { @@ -526,13 +523,13 @@ describe('enrichSignalThreatMatches', () => { }, matched_queries: [matchedQuery], }); - const signals = getSignalsResponseMock([signalHit]); + const signals: SignalSourceHit[] = [signalHit]; const enrichedSignals = await enrichSignalThreatMatches( signals, getMatchedThreats, indicatorPath ); - const [enrichedHit] = enrichedSignals.hits.hits; + const [enrichedHit] = enrichedSignals; const enrichments = get(enrichedHit._source, ENRICHMENT_DESTINATION_PATH); expect(enrichments).toEqual([ @@ -560,13 +557,13 @@ describe('enrichSignalThreatMatches', () => { const signalHit = getSignalHitMock({ matched_queries: [matchedQuery], }); - const signals = getSignalsResponseMock([signalHit]); + const signals: SignalSourceHit[] = [signalHit]; const enrichedSignals = await enrichSignalThreatMatches( signals, getMatchedThreats, indicatorPath ); - const [enrichedHit] = enrichedSignals.hits.hits; + const [enrichedHit] = enrichedSignals; const enrichments = get(enrichedHit._source, ENRICHMENT_DESTINATION_PATH); expect(enrichments).toEqual([ @@ -598,13 +595,13 @@ describe('enrichSignalThreatMatches', () => { }, matched_queries: [matchedQuery], }); - const signals = getSignalsResponseMock([signalHit]); + const signals: SignalSourceHit[] = [signalHit]; const enrichedSignals = await enrichSignalThreatMatches( signals, getMatchedThreats, indicatorPath ); - const [enrichedHit] = enrichedSignals.hits.hits; + const [enrichedHit] = enrichedSignals; const enrichments = get(enrichedHit._source, ENRICHMENT_DESTINATION_PATH); expect(enrichments).toEqual([ @@ -637,7 +634,7 @@ describe('enrichSignalThreatMatches', () => { _source: { '@timestamp': 'mocked', threat: 'whoops' }, matched_queries: [matchedQuery], }); - const signals = getSignalsResponseMock([signalHit]); + const signals: SignalSourceHit[] = [signalHit]; await expect(() => enrichSignalThreatMatches(signals, getMatchedThreats, indicatorPath) ).rejects.toThrowError('Expected threat field to be an object, but found: whoops'); @@ -674,13 +671,13 @@ describe('enrichSignalThreatMatches', () => { }, matched_queries: [matchedQuery], }); - const signals = getSignalsResponseMock([signalHit]); + const signals: SignalSourceHit[] = [signalHit]; const enrichedSignals = await enrichSignalThreatMatches( signals, getMatchedThreats, 'custom_threat.custom_indicator' ); - const [enrichedHit] = enrichedSignals.hits.hits; + const [enrichedHit] = enrichedSignals; const enrichments = get(enrichedHit._source, ENRICHMENT_DESTINATION_PATH); expect(enrichments).toEqual([ @@ -748,16 +745,15 @@ describe('enrichSignalThreatMatches', () => { ), ], }); - const signals = getSignalsResponseMock([signalHit, otherSignalHit]); + const signals: SignalSourceHit[] = [signalHit, otherSignalHit]; const enrichedSignals = await enrichSignalThreatMatches( signals, getMatchedThreats, indicatorPath ); - expect(enrichedSignals.hits.total).toEqual(expect.objectContaining({ value: 1 })); - expect(enrichedSignals.hits.hits).toHaveLength(1); + expect(enrichedSignals).toHaveLength(1); - const [enrichedHit] = enrichedSignals.hits.hits; + const [enrichedHit] = enrichedSignals; const enrichments = get(enrichedHit._source, ENRICHMENT_DESTINATION_PATH); expect(enrichments).toEqual([ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts index c1fb88176fd4c..58a486068013f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts @@ -8,7 +8,7 @@ import { get, isObject } from 'lodash'; import { ENRICHMENT_TYPES, FEED_NAME_PATH } from '../../../../../common/cti/constants'; -import type { SignalSearchResponse, SignalSourceHit } from '../types'; +import type { SignalSourceHit } from '../types'; import type { GetMatchedThreats, ThreatEnrichment, @@ -109,17 +109,16 @@ export const buildEnrichments = ({ }); export const enrichSignalThreatMatches = async ( - signals: SignalSearchResponse, + signals: SignalSourceHit[], getMatchedThreats: GetMatchedThreats, indicatorPath: string, signalMatchesArg?: SignalMatch[] -): Promise => { - const signalHits = signals.hits.hits; - if (signalHits.length === 0) { +): Promise => { + if (signals.length === 0) { return signals; } - const uniqueHits = groupAndMergeSignalMatches(signalHits); + const uniqueHits = groupAndMergeSignalMatches(signals); const signalMatches: SignalMatch[] = signalMatchesArg ? signalMatchesArg : uniqueHits.map((signalHit) => ({ @@ -177,14 +176,5 @@ export const enrichSignalThreatMatches = async ( }; }); - return { - ...signals, - hits: { - ...signals.hits, - hits: enrichedSignals, - total: isObject(signals.hits.total) - ? { ...signals.hits.total, value: enrichedSignals.length } - : enrichedSignals.length, - }, - }; + return enrichedSignals; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts index 235865a8b60a9..5dc19b1b257b8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts @@ -277,7 +277,7 @@ export interface AlertAttributes { export type BulkResponseErrorAggregation = Record; -export type SignalsEnrichment = (signals: SignalSearchResponse) => Promise; +export type SignalsEnrichment = (signals: SignalSourceHit[]) => Promise; export type BulkCreate = ( docs: Array> diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/endpoint_alerts.ts b/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/endpoint_alerts.ts index 5a62513b1ab38..41394144e9c66 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/endpoint_alerts.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/endpoint_alerts.ts @@ -17,9 +17,7 @@ const baseAllowlistFields: AllowlistFields = { command_line: true, hash: true, pid: true, - pe: { - original_file_name: true, - }, + pe: true, uptime: true, Ext: { architecture: true, @@ -27,11 +25,15 @@ const baseAllowlistFields: AllowlistFields = { dll: true, malware_signature: true, memory_region: true, + protection: true, real: { entity_id: true, }, token: { + elevation: true, + elevation_type: true, integrity_level_name: true, + security_attributes: true, }, }, thread: true, @@ -44,10 +46,9 @@ const allowlistBaseEventFields: AllowlistFields = { name: true, path: true, code_signature: true, + hash: true, malware_signature: true, - pe: { - original_file_name: true, - }, + pe: true, }, dns: true, event: true, @@ -61,6 +62,7 @@ const allowlistBaseEventFields: AllowlistFields = { mtime: true, directory: true, hash: true, + pe: true, Ext: { bytes_compressed: true, bytes_compressed_present: true, diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts index 71e8b8e11c215..240634bccdca4 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts @@ -58,6 +58,9 @@ describe('TelemetryEventsSender', () => { path: 'X', test: 'me', another: 'nope', + pe: { + original_file_name: 'malware.exe', + }, Ext: { bytes_compressed: 'data up to 4mb', bytes_compressed_present: 'data up to 4mb', @@ -89,6 +92,9 @@ describe('TelemetryEventsSender', () => { executable: null, // null fields are never allowlisted working_directory: '/some/usr/dir', entity_id: 'some_entity_id', + Ext: { + protection: 'PsProtectedSignerAntimalware-Light', + }, }, Responses: '{ "result": 0 }', // >= 7.15 Target: { @@ -132,6 +138,9 @@ describe('TelemetryEventsSender', () => { size: 3, created: 0, path: 'X', + pe: { + original_file_name: 'malware.exe', + }, Ext: { bytes_compressed: 'data up to 4mb', bytes_compressed_present: 'data up to 4mb', @@ -159,6 +168,9 @@ describe('TelemetryEventsSender', () => { name: 'foo.exe', working_directory: '/some/usr/dir', entity_id: 'some_entity_id', + Ext: { + protection: 'PsProtectedSignerAntimalware-Light', + }, }, Responses: '{ "result": 0 }', Target: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__mocks__/index.ts index bc31f64fcc626..346c8845751ce 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__mocks__/index.ts @@ -13,12 +13,6 @@ import { UsersFields } from '../../../../../../../common/search_strategy/securit export const mockOptions: UsersRequestOptions = { defaultIndex: ['test_indices*'], - docValueFields: [ - { - field: '@timestamp', - format: 'date_time', - }, - ], factoryQueryType: UsersQueries.users, filterQuery: '{"bool":{"must":[],"filter":[{"match_all":{}},{"match_phrase":{"user.name":{"query":"test_user"}}}],"should":[],"must_not":[]}}', @@ -78,10 +72,12 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { _index: 'endgame-00001', _id: 'inT0934BjUd1_U2597Vf', _score: null, - _source: { - user: { - domain: 'ENDPOINT-W-8-03', - }, + fields: { + 'user.name': ['jose52'], + '@timestamp': ['2022-04-13T17:16:34.540Z'], + 'user.id': ['17'], + 'user.email': ['jose52@barrett.com'], + 'user.domain': ['ENDPOINT-W-8-03'], }, sort: [1644837532000], }, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__snapshots__/index.test.ts.snap b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__snapshots__/index.test.ts.snap index b52d0b1fc1cfd..6383808293a9d 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__snapshots__/index.test.ts.snap +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__snapshots__/index.test.ts.snap @@ -3,7 +3,9 @@ exports[`allHosts search strategy parse should parse data correctly 1`] = ` Array [ Object { - "domain": "ENDPOINT-W-8-03", + "domain": Array [ + "ENDPOINT-W-8-03", + ], "lastSeen": "2022-02-14T11:18:52.000Z", "name": "vagrant", }, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__snapshots__/query.all_users.dsl.test.ts.snap b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__snapshots__/query.all_users.dsl.test.ts.snap index 99f85724d2f5c..2b661fe355a13 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__snapshots__/query.all_users.dsl.test.ts.snap +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/__snapshots__/query.all_users.dsl.test.ts.snap @@ -4,6 +4,7 @@ exports[`buildUsersQuery build query from options correctly 1`] = ` Object { "allow_no_indices": true, "body": Object { + "_source": false, "aggregations": Object { "user_count": Object { "cardinality": Object { @@ -14,11 +15,7 @@ Object { "aggs": Object { "domain": Object { "top_hits": Object { - "_source": Object { - "includes": Array [ - "user.domain", - ], - }, + "_source": false, "size": 1, "sort": Array [ Object { @@ -44,10 +41,12 @@ Object { }, }, }, - "docvalue_fields": Array [ + "fields": Array [ + "user.name", + "user.domain", Object { "field": "@timestamp", - "format": "date_time", + "format": "strict_date_optional_time", }, ], "query": Object { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/index.ts index b8babe0b19845..9c62e55cc0bb1 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/index.ts @@ -50,7 +50,7 @@ export const allUsers: SecuritySolutionFactory = { (bucket: AllUsersAggEsItem) => ({ name: bucket.key, lastSeen: getOr(null, `lastSeen.value_as_string`, bucket), - domain: getOr(null, `domain.hits.hits[0]._source.user.domain`, bucket), + domain: getOr(null, `domain.hits.hits[0].fields['user.domain']`, bucket), }), {} ); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/query.all_users.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/query.all_users.dsl.ts index 82df1d2d97246..d7f8f7e50abe1 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/query.all_users.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/all/query.all_users.dsl.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { isEmpty } from 'lodash/fp'; import type { ISearchRequestParams } from '@kbn/data-plugin/common'; import { Direction } from '../../../../../../common/search_strategy'; import { createQueryFilterClauses } from '../../../../../utils/build_query'; @@ -18,7 +17,6 @@ import { assertUnreachable } from '../../../../../../common/utility_types'; export const buildUsersQuery = ({ defaultIndex, - docValueFields, filterQuery, pagination: { querySize }, sort, @@ -43,7 +41,6 @@ export const buildUsersQuery = ({ ignore_unavailable: true, track_total_hits: false, body: { - ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), aggregations: { user_count: { cardinality: { field: 'user.name' } }, user_data: { @@ -60,15 +57,22 @@ export const buildUsersQuery = ({ }, }, ], - _source: { - includes: ['user.domain'], - }, + _source: false, }, }, }, }, }, query: { bool: { filter } }, + _source: false, + fields: [ + 'user.name', + 'user.domain', + { + field: '@timestamp', + format: 'strict_date_optional_time', + }, + ], size: 0, }, }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/__mocks__/index.ts index 487f4537cd50d..028ee4e4ba1b9 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/__mocks__/index.ts @@ -26,408 +26,6 @@ export const mockOptions: UserAuthenticationsRequestOptions = { 'winlogbeat-*', ], stackByField: AuthStackByField.userName, - docValueFields: [ - { - field: '@timestamp', - format: 'date_time', - }, - { - field: 'event.created', - format: 'date_time', - }, - { - field: 'event.end', - format: 'date_time', - }, - { - field: 'event.ingested', - format: 'date_time', - }, - { - field: 'event.start', - format: 'date_time', - }, - { - field: 'file.accessed', - format: 'date_time', - }, - { - field: 'file.created', - format: 'date_time', - }, - { - field: 'file.ctime', - format: 'date_time', - }, - { - field: 'file.mtime', - format: 'date_time', - }, - { - field: 'package.installed', - format: 'date_time', - }, - { - field: 'process.parent.start', - format: 'date_time', - }, - { - field: 'process.start', - format: 'date_time', - }, - { - field: 'system.audit.host.boottime', - format: 'date_time', - }, - { - field: 'system.audit.package.installtime', - format: 'date_time', - }, - { - field: 'system.audit.user.password.last_changed', - format: 'date_time', - }, - { - field: 'tls.client.not_after', - format: 'date_time', - }, - { - field: 'tls.client.not_before', - format: 'date_time', - }, - { - field: 'tls.server.not_after', - format: 'date_time', - }, - { - field: 'tls.server.not_before', - format: 'date_time', - }, - { - field: 'aws.cloudtrail.user_identity.session_context.creation_date', - format: 'date_time', - }, - { - field: 'azure.auditlogs.properties.activity_datetime', - format: 'date_time', - }, - { - field: 'azure.enqueued_time', - format: 'date_time', - }, - { - field: 'azure.signinlogs.properties.created_at', - format: 'date_time', - }, - { - field: 'cef.extensions.agentReceiptTime', - format: 'date_time', - }, - { - field: 'cef.extensions.deviceCustomDate1', - format: 'date_time', - }, - { - field: 'cef.extensions.deviceCustomDate2', - format: 'date_time', - }, - { - field: 'cef.extensions.deviceReceiptTime', - format: 'date_time', - }, - { - field: 'cef.extensions.endTime', - format: 'date_time', - }, - { - field: 'cef.extensions.fileCreateTime', - format: 'date_time', - }, - { - field: 'cef.extensions.fileModificationTime', - format: 'date_time', - }, - { - field: 'cef.extensions.flexDate1', - format: 'date_time', - }, - { - field: 'cef.extensions.managerReceiptTime', - format: 'date_time', - }, - { - field: 'cef.extensions.oldFileCreateTime', - format: 'date_time', - }, - { - field: 'cef.extensions.oldFileModificationTime', - format: 'date_time', - }, - { - field: 'cef.extensions.startTime', - format: 'date_time', - }, - { - field: 'checkpoint.subs_exp', - format: 'date_time', - }, - { - field: 'crowdstrike.event.EndTimestamp', - format: 'date_time', - }, - { - field: 'crowdstrike.event.IncidentEndTime', - format: 'date_time', - }, - { - field: 'crowdstrike.event.IncidentStartTime', - format: 'date_time', - }, - { - field: 'crowdstrike.event.ProcessEndTime', - format: 'date_time', - }, - { - field: 'crowdstrike.event.ProcessStartTime', - format: 'date_time', - }, - { - field: 'crowdstrike.event.StartTimestamp', - format: 'date_time', - }, - { - field: 'crowdstrike.event.Timestamp', - format: 'date_time', - }, - { - field: 'crowdstrike.event.UTCTimestamp', - format: 'date_time', - }, - { - field: 'crowdstrike.metadata.eventCreationTime', - format: 'date_time', - }, - { - field: 'gsuite.admin.email.log_search_filter.end_date', - format: 'date_time', - }, - { - field: 'gsuite.admin.email.log_search_filter.start_date', - format: 'date_time', - }, - { - field: 'gsuite.admin.user.birthdate', - format: 'date_time', - }, - { - field: 'kafka.block_timestamp', - format: 'date_time', - }, - { - field: 'microsoft.defender_atp.lastUpdateTime', - format: 'date_time', - }, - { - field: 'microsoft.defender_atp.resolvedTime', - format: 'date_time', - }, - { - field: 'misp.campaign.first_seen', - format: 'date_time', - }, - { - field: 'misp.campaign.last_seen', - format: 'date_time', - }, - { - field: 'misp.intrusion_set.first_seen', - format: 'date_time', - }, - { - field: 'misp.intrusion_set.last_seen', - format: 'date_time', - }, - { - field: 'misp.observed_data.first_observed', - format: 'date_time', - }, - { - field: 'misp.observed_data.last_observed', - format: 'date_time', - }, - { - field: 'misp.report.published', - format: 'date_time', - }, - { - field: 'misp.threat_indicator.valid_from', - format: 'date_time', - }, - { - field: 'misp.threat_indicator.valid_until', - format: 'date_time', - }, - { - field: 'netflow.collection_time_milliseconds', - format: 'date_time', - }, - { - field: 'netflow.exporter.timestamp', - format: 'date_time', - }, - { - field: 'netflow.flow_end_microseconds', - format: 'date_time', - }, - { - field: 'netflow.flow_end_milliseconds', - format: 'date_time', - }, - { - field: 'netflow.flow_end_nanoseconds', - format: 'date_time', - }, - { - field: 'netflow.flow_end_seconds', - format: 'date_time', - }, - { - field: 'netflow.flow_start_microseconds', - format: 'date_time', - }, - { - field: 'netflow.flow_start_milliseconds', - format: 'date_time', - }, - { - field: 'netflow.flow_start_nanoseconds', - format: 'date_time', - }, - { - field: 'netflow.flow_start_seconds', - format: 'date_time', - }, - { - field: 'netflow.max_export_seconds', - format: 'date_time', - }, - { - field: 'netflow.max_flow_end_microseconds', - format: 'date_time', - }, - { - field: 'netflow.max_flow_end_milliseconds', - format: 'date_time', - }, - { - field: 'netflow.max_flow_end_nanoseconds', - format: 'date_time', - }, - { - field: 'netflow.max_flow_end_seconds', - format: 'date_time', - }, - { - field: 'netflow.min_export_seconds', - format: 'date_time', - }, - { - field: 'netflow.min_flow_start_microseconds', - format: 'date_time', - }, - { - field: 'netflow.min_flow_start_milliseconds', - format: 'date_time', - }, - { - field: 'netflow.min_flow_start_nanoseconds', - format: 'date_time', - }, - { - field: 'netflow.min_flow_start_seconds', - format: 'date_time', - }, - { - field: 'netflow.monitoring_interval_end_milli_seconds', - format: 'date_time', - }, - { - field: 'netflow.monitoring_interval_start_milli_seconds', - format: 'date_time', - }, - { - field: 'netflow.observation_time_microseconds', - format: 'date_time', - }, - { - field: 'netflow.observation_time_milliseconds', - format: 'date_time', - }, - { - field: 'netflow.observation_time_nanoseconds', - format: 'date_time', - }, - { - field: 'netflow.observation_time_seconds', - format: 'date_time', - }, - { - field: 'netflow.system_init_time_milliseconds', - format: 'date_time', - }, - { - field: 'rsa.internal.lc_ctime', - format: 'date_time', - }, - { - field: 'rsa.internal.time', - format: 'date_time', - }, - { - field: 'rsa.time.effective_time', - format: 'date_time', - }, - { - field: 'rsa.time.endtime', - format: 'date_time', - }, - { - field: 'rsa.time.event_queue_time', - format: 'date_time', - }, - { - field: 'rsa.time.event_time', - format: 'date_time', - }, - { - field: 'rsa.time.expire_time', - format: 'date_time', - }, - { - field: 'rsa.time.recorded_time', - format: 'date_time', - }, - { - field: 'rsa.time.stamp', - format: 'date_time', - }, - { - field: 'rsa.time.starttime', - format: 'date_time', - }, - { - field: 'sophos.xg.date', - format: 'date_time', - }, - { - field: 'sophos.xg.eventtime', - format: 'date_time', - }, - { - field: 'sophos.xg.start_time', - format: 'date_time', - }, - ], factoryQueryType: UsersQueries.authentications, filterQuery: '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', pagination: { @@ -481,106 +79,10 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { _index: 'winlogbeat-8.0.0-2020.09.02-000001', _id: 'zqY7WXQBA6bGZw2uLeKI', _score: null, - _source: { - process: { - name: 'services.exe', - pid: 564, - executable: 'C:\\Windows\\System32\\services.exe', - }, - agent: { - build_date: '2020-07-16 09:16:27 +0000 UTC ', - name: 'siem-windows', - commit: '4dcbde39492bdc3843034bba8db811c68cb44b97 ', - id: '05e1bff7-d7a8-416a-8554-aa10288fa07d', - type: 'winlogbeat', - ephemeral_id: '655abd6c-6c33-435d-a2eb-79b2a01e6d61', - version: '8.0.0', - user: { name: 'inside_winlogbeat_user' }, - }, - winlog: { - computer_name: 'siem-windows', - process: { pid: 576, thread: { id: 880 } }, - keywords: ['Audit Success'], - logon: { id: '0x3e7', type: 'Service' }, - channel: 'Security', - event_data: { - LogonGuid: '{00000000-0000-0000-0000-000000000000}', - TargetOutboundDomainName: '-', - VirtualAccount: '%%1843', - LogonType: '5', - IpPort: '-', - TransmittedServices: '-', - SubjectLogonId: '0x3e7', - LmPackageName: '-', - TargetOutboundUserName: '-', - KeyLength: '0', - TargetLogonId: '0x3e7', - RestrictedAdminMode: '-', - SubjectUserName: 'SIEM-WINDOWS$', - TargetLinkedLogonId: '0x0', - ElevatedToken: '%%1842', - SubjectDomainName: 'WORKGROUP', - IpAddress: '-', - ImpersonationLevel: '%%1833', - TargetUserName: 'SYSTEM', - LogonProcessName: 'Advapi ', - TargetDomainName: 'NT AUTHORITY', - SubjectUserSid: 'S-1-5-18', - TargetUserSid: 'S-1-5-18', - AuthenticationPackageName: 'Negotiate', - }, - opcode: 'Info', - version: 2, - record_id: 57818, - task: 'Logon', - event_id: 4624, - provider_guid: '{54849625-5478-4994-a5ba-3e3b0328c30d}', - activity_id: '{d2485217-6bac-0000-8fbb-3f7e2571d601}', - api: 'wineventlog', - provider_name: 'Microsoft-Windows-Security-Auditing', - }, - log: { level: 'information' }, - source: { domain: '-' }, - message: - 'An account was successfully logged on.\n\nSubject:\n\tSecurity ID:\t\tS-1-5-18\n\tAccount Name:\t\tSIEM-WINDOWS$\n\tAccount Domain:\t\tWORKGROUP\n\tLogon ID:\t\t0x3E7\n\nLogon Information:\n\tLogon Type:\t\t5\n\tRestricted Admin Mode:\t-\n\tVirtual Account:\t\tNo\n\tElevated Token:\t\tYes\n\nImpersonation Level:\t\tImpersonation\n\nNew Logon:\n\tSecurity ID:\t\tS-1-5-18\n\tAccount Name:\t\tSYSTEM\n\tAccount Domain:\t\tNT AUTHORITY\n\tLogon ID:\t\t0x3E7\n\tLinked Logon ID:\t\t0x0\n\tNetwork Account Name:\t-\n\tNetwork Account Domain:\t-\n\tLogon GUID:\t\t{00000000-0000-0000-0000-000000000000}\n\nProcess Information:\n\tProcess ID:\t\t0x234\n\tProcess Name:\t\tC:\\Windows\\System32\\services.exe\n\nNetwork Information:\n\tWorkstation Name:\t-\n\tSource Network Address:\t-\n\tSource Port:\t\t-\n\nDetailed Authentication Information:\n\tLogon Process:\t\tAdvapi \n\tAuthentication Package:\tNegotiate\n\tTransited Services:\t-\n\tPackage Name (NTLM only):\t-\n\tKey Length:\t\t0\n\nThis event is generated when a logon session is created. It is generated on the computer that was accessed.\n\nThe subject fields indicate the account on the local system which requested the logon. This is most commonly a service such as the Server service, or a local process such as Winlogon.exe or Services.exe.\n\nThe logon type field indicates the kind of logon that occurred. The most common types are 2 (interactive) and 3 (network).\n\nThe New Logon fields indicate the account for whom the new logon was created, i.e. the account that was logged on.\n\nThe network fields indicate where a remote logon request originated. Workstation name is not always available and may be left blank in some cases.\n\nThe impersonation level field indicates the extent to which a process in the logon session can impersonate.\n\nThe authentication information fields provide detailed information about this specific logon request.\n\t- Logon GUID is a unique identifier that can be used to correlate this event with a KDC event.\n\t- Transited services indicate which intermediate services have participated in this logon request.\n\t- Package name indicates which sub-protocol was used among the NTLM protocols.\n\t- Key length indicates the length of the generated session key. This will be 0 if no session key was requested.', - cloud: { - availability_zone: 'us-central1-c', - instance: { name: 'siem-windows', id: '9156726559029788564' }, - provider: 'gcp', - machine: { type: 'g1-small' }, - project: { id: 'elastic-siem' }, - }, - '@timestamp': '2020-09-04T13:08:02.532Z', - related: { user: ['SYSTEM', 'SIEM-WINDOWS$'] }, - ecs: { version: '1.5.0' }, - host: { - hostname: 'siem-windows', - os: { - build: '17763.1397', - kernel: '10.0.17763.1397 (WinBuild.160101.0800)', - name: 'Windows Server 2019 Datacenter', - family: 'windows', - version: '10.0', - platform: 'windows', - }, - ip: ['fe80::ecf5:decc:3ec3:767e', '10.200.0.15'], - name: 'siem-windows', - id: 'ce1d3c9b-a815-4643-9641-ada0f2c00609', - mac: ['42:01:0a:c8:00:0f'], - architecture: 'x86_64', - }, - event: { - code: 4624, - provider: 'Microsoft-Windows-Security-Auditing', - created: '2020-09-04T13:08:03.638Z', - kind: 'event', - module: 'security', - action: 'logged-in', - category: 'authentication', - type: 'start', - outcome: 'success', - }, - user: { domain: 'NT AUTHORITY', name: 'SYSTEM', id: 'S-1-5-18' }, + fields: { + '@timestamp': ['2020-09-04T13:08:02.532Z'], + 'host.id': ['ce1d3c9b-a815-4643-9641-ada0f2c00609'], + 'host.name': ['siem-windows'], }, sort: [1599224882532], }, @@ -607,76 +109,11 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { _index: '.ds-logs-system.auth-default-000001', _id: '9_sfWXQBc39KFIJbIsDh', _score: null, - _source: { - agent: { - hostname: 'siem-kibana', - name: 'siem-kibana', - id: 'aa3d9dc7-fef1-4c2f-a68d-25785d624e35', - ephemeral_id: 'e503bd85-11c7-4bc9-ae7d-70be1d919fb7', - type: 'filebeat', - version: '7.9.1', - }, - process: { name: 'sshd', pid: 20764 }, - log: { file: { path: '/var/log/auth.log' }, offset: 552463 }, - source: { - geo: { - continent_name: 'Europe', - region_iso_code: 'DE-BE', - city_name: 'Berlin', - country_iso_code: 'DE', - region_name: 'Land Berlin', - location: { lon: 13.3512, lat: 52.5727 }, - }, - as: { number: 6805, organization: { name: 'Telefonica Germany' } }, - port: 57457, - ip: '77.183.42.188', - }, - cloud: { - availability_zone: 'us-east1-b', - instance: { name: 'siem-kibana', id: '5412578377715150143' }, - provider: 'gcp', - machine: { type: 'n1-standard-2' }, - project: { id: 'elastic-beats' }, - }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T11:49:21.000Z', - system: { - auth: { - ssh: { - method: 'publickey', - signature: 'RSA SHA256:vv64JNLzKZWYA9vonnGWuW7zxWhyZrL/BFxyIGbISx8', - event: 'Accepted', - }, - }, - }, - ecs: { version: '1.5.0' }, - data_stream: { namespace: 'default', type: 'logs', dataset: 'system.auth' }, - host: { - hostname: 'siem-kibana', - os: { - kernel: '4.9.0-8-amd64', - codename: 'stretch', - name: 'Debian GNU/Linux', - family: 'debian', - version: '9 (stretch)', - platform: 'debian', - }, - containerized: false, - ip: ['10.142.0.7', 'fe80::4001:aff:fe8e:7'], - name: 'siem-kibana', - id: 'aa7ca589f1b8220002f2fc61c64cfbf1', - mac: ['42:01:0a:8e:00:07'], - architecture: 'x86_64', - }, - event: { - timezone: '+00:00', - action: 'ssh_login', - type: 'authentication_success', - category: 'authentication', - dataset: 'system.auth', - outcome: 'success', - }, - user: { name: 'tsg' }, + fields: { + 'source.ip': ['77.183.42.188'], + 'host.id': ['aa7ca589f1b8220002f2fc61c64cfbf1'], + 'host.name': ['siem-kibana'], + '@timestamp': ['2020-09-04T11:49:21.000Z'], }, sort: [1599220161000], }, @@ -699,67 +136,11 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { _index: '.ds-logs-system.auth-default-000001', _id: 'ZfxZWXQBc39KFIJbLN5U', _score: null, - _source: { - agent: { - hostname: 'siem-kibana', - name: 'siem-kibana', - id: 'aa3d9dc7-fef1-4c2f-a68d-25785d624e35', - ephemeral_id: 'e503bd85-11c7-4bc9-ae7d-70be1d919fb7', - type: 'filebeat', - version: '7.9.1', - }, - process: { name: 'sshd', pid: 22913 }, - log: { file: { path: '/var/log/auth.log' }, offset: 562910 }, - source: { - geo: { - continent_name: 'Asia', - region_iso_code: 'KR-28', - city_name: 'Incheon', - country_iso_code: 'KR', - region_name: 'Incheon', - location: { lon: 126.7288, lat: 37.4562 }, - }, - as: { number: 4766, organization: { name: 'Korea Telecom' } }, - ip: '59.15.3.197', - }, - cloud: { - availability_zone: 'us-east1-b', - instance: { name: 'siem-kibana', id: '5412578377715150143' }, - provider: 'gcp', - machine: { type: 'n1-standard-2' }, - project: { id: 'elastic-beats' }, - }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T13:40:46.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - data_stream: { namespace: 'default', type: 'logs', dataset: 'system.auth' }, - host: { - hostname: 'siem-kibana', - os: { - kernel: '4.9.0-8-amd64', - codename: 'stretch', - name: 'Debian GNU/Linux', - family: 'debian', - version: '9 (stretch)', - platform: 'debian', - }, - containerized: false, - ip: ['10.142.0.7', 'fe80::4001:aff:fe8e:7'], - name: 'siem-kibana', - id: 'aa7ca589f1b8220002f2fc61c64cfbf1', - mac: ['42:01:0a:8e:00:07'], - architecture: 'x86_64', - }, - event: { - timezone: '+00:00', - action: 'ssh_login', - type: 'authentication_failure', - category: 'authentication', - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'admin' }, + fields: { + 'source.ip': ['59.15.3.197'], + 'host.id': ['aa7ca589f1b8220002f2fc61c64cfbf1'], + 'host.name': ['siem-kibana'], + '@timestamp': ['2020-09-04T13:40:46.000Z'], }, sort: [1599226846000], }, @@ -786,47 +167,10 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { _index: 'filebeat-8.0.0-2020.09.02-000001', _id: 'M_xLWXQBc39KFIJbY7Cb', _score: null, - _source: { - agent: { - name: 'bastion00.siem.estc.dev', - id: 'f9a321c1-ec27-49fa-aacf-6a50ef6d836f', - type: 'filebeat', - ephemeral_id: '734ee3da-1a4f-4bc9-b400-e0cf0e5eeebc', - version: '8.0.0', - }, - process: { name: 'sshd', pid: 20671 }, - log: { file: { path: '/var/log/auth.log' }, offset: 1028103 }, - source: { - geo: { - continent_name: 'North America', - region_iso_code: 'US-NY', - city_name: 'New York', - country_iso_code: 'US', - region_name: 'New York', - location: { lon: -74, lat: 40.7157 }, - }, - ip: '64.227.88.245', - }, - fileset: { name: 'auth' }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T13:25:43.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - related: { ip: ['64.227.88.245'], user: ['user'] }, - service: { type: 'system' }, - host: { hostname: 'bastion00', name: 'bastion00.siem.estc.dev' }, - event: { - ingested: '2020-09-04T13:25:47.034172Z', - timezone: '+00:00', - kind: 'event', - module: 'system', - action: 'ssh_login', - type: ['authentication_failure', 'info'], - category: ['authentication'], - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'user' }, + fields: { + 'source.ip': ['64.227.88.245'], + 'host.name': ['bastion00.siem.estc.dev'], + '@timestamp': ['2020-09-04T13:25:43.000Z'], }, sort: [1599225943000], }, @@ -853,47 +197,10 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { _index: 'filebeat-8.0.0-2020.09.02-000001', _id: 'nPxKWXQBc39KFIJb7q4w', _score: null, - _source: { - agent: { - name: 'bastion00.siem.estc.dev', - id: 'f9a321c1-ec27-49fa-aacf-6a50ef6d836f', - ephemeral_id: '734ee3da-1a4f-4bc9-b400-e0cf0e5eeebc', - type: 'filebeat', - version: '8.0.0', - }, - process: { name: 'sshd', pid: 20665 }, - log: { file: { path: '/var/log/auth.log' }, offset: 1027372 }, - source: { - geo: { - continent_name: 'North America', - region_iso_code: 'US-NY', - city_name: 'New York', - country_iso_code: 'US', - region_name: 'New York', - location: { lon: -74, lat: 40.7157 }, - }, - ip: '64.227.88.245', - }, - fileset: { name: 'auth' }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T13:25:07.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - related: { ip: ['64.227.88.245'], user: ['ubuntu'] }, - service: { type: 'system' }, - host: { hostname: 'bastion00', name: 'bastion00.siem.estc.dev' }, - event: { - ingested: '2020-09-04T13:25:16.974606Z', - timezone: '+00:00', - kind: 'event', - module: 'system', - action: 'ssh_login', - type: ['authentication_failure', 'info'], - category: ['authentication'], - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'ubuntu' }, + fields: { + 'source.ip': ['64.227.88.245'], + 'host.name': ['bastion00.siem.estc.dev'], + '@timestamp': ['2020-09-04T13:25:07.000Z'], }, sort: [1599225907000], }, @@ -920,67 +227,11 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { _index: '.ds-logs-system.auth-default-000001', _id: 'mPsfWXQBc39KFIJbI8HI', _score: null, - _source: { - agent: { - hostname: 'siem-kibana', - name: 'siem-kibana', - id: 'aa3d9dc7-fef1-4c2f-a68d-25785d624e35', - type: 'filebeat', - ephemeral_id: 'e503bd85-11c7-4bc9-ae7d-70be1d919fb7', - version: '7.9.1', - }, - process: { name: 'sshd', pid: 21506 }, - log: { file: { path: '/var/log/auth.log' }, offset: 556761 }, - source: { - geo: { - continent_name: 'Asia', - region_iso_code: 'IN-DL', - city_name: 'New Delhi', - country_iso_code: 'IN', - region_name: 'National Capital Territory of Delhi', - location: { lon: 77.2245, lat: 28.6358 }, - }, - as: { number: 10029, organization: { name: 'SHYAM SPECTRA PVT LTD' } }, - ip: '180.151.228.166', - }, - cloud: { - availability_zone: 'us-east1-b', - instance: { name: 'siem-kibana', id: '5412578377715150143' }, - provider: 'gcp', - machine: { type: 'n1-standard-2' }, - project: { id: 'elastic-beats' }, - }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T12:26:36.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - data_stream: { namespace: 'default', type: 'logs', dataset: 'system.auth' }, - host: { - hostname: 'siem-kibana', - os: { - kernel: '4.9.0-8-amd64', - codename: 'stretch', - name: 'Debian GNU/Linux', - family: 'debian', - version: '9 (stretch)', - platform: 'debian', - }, - containerized: false, - ip: ['10.142.0.7', 'fe80::4001:aff:fe8e:7'], - name: 'siem-kibana', - id: 'aa7ca589f1b8220002f2fc61c64cfbf1', - mac: ['42:01:0a:8e:00:07'], - architecture: 'x86_64', - }, - event: { - timezone: '+00:00', - action: 'ssh_login', - type: 'authentication_failure', - category: 'authentication', - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'odoo' }, + fields: { + 'source.ip': ['180.151.228.166'], + 'host.id': ['aa7ca589f1b8220002f2fc61c64cfbf1'], + 'host.name': ['siem-kibana'], + '@timestamp': ['2020-09-04T12:26:36.000Z'], }, sort: [1599222396000], }, @@ -1007,48 +258,10 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { _index: 'filebeat-8.0.0-2020.09.02-000001', _id: 'aaToWHQBA6bGZw2uR-St', _score: null, - _source: { - agent: { - name: 'bastion00.siem.estc.dev', - id: 'f9a321c1-ec27-49fa-aacf-6a50ef6d836f', - type: 'filebeat', - ephemeral_id: '734ee3da-1a4f-4bc9-b400-e0cf0e5eeebc', - version: '8.0.0', - }, - process: { name: 'sshd', pid: 20475 }, - log: { file: { path: '/var/log/auth.log' }, offset: 1019218 }, - source: { - geo: { - continent_name: 'Europe', - region_iso_code: 'SE-AB', - city_name: 'Stockholm', - country_iso_code: 'SE', - region_name: 'Stockholm', - location: { lon: 17.7833, lat: 59.25 }, - }, - as: { number: 8473, organization: { name: 'Bahnhof AB' } }, - ip: '178.174.148.58', - }, - fileset: { name: 'auth' }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T11:37:22.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - related: { ip: ['178.174.148.58'], user: ['pi'] }, - service: { type: 'system' }, - host: { hostname: 'bastion00', name: 'bastion00.siem.estc.dev' }, - event: { - ingested: '2020-09-04T11:37:31.797423Z', - timezone: '+00:00', - kind: 'event', - module: 'system', - action: 'ssh_login', - type: ['authentication_failure', 'info'], - category: ['authentication'], - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'pi' }, + fields: { + 'source.ip': ['178.174.148.58'], + 'host.name': ['bastion00.siem.estc.dev'], + '@timestamp': ['2020-09-04T11:37:22.000Z'], }, sort: [1599219442000], }, @@ -1075,48 +288,10 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { _index: 'filebeat-8.0.0-2020.09.02-000001', _id: 'VaP_V3QBA6bGZw2upUbg', _score: null, - _source: { - agent: { - name: 'bastion00.siem.estc.dev', - id: 'f9a321c1-ec27-49fa-aacf-6a50ef6d836f', - type: 'filebeat', - ephemeral_id: '734ee3da-1a4f-4bc9-b400-e0cf0e5eeebc', - version: '8.0.0', - }, - process: { name: 'sshd', pid: 19849 }, - log: { file: { path: '/var/log/auth.log' }, offset: 981036 }, - source: { - geo: { - continent_name: 'Europe', - country_iso_code: 'HR', - location: { lon: 15.5, lat: 45.1667 }, - }, - as: { - number: 42864, - organization: { name: 'Giganet Internet Szolgaltato Kft' }, - }, - ip: '45.95.168.157', - }, - fileset: { name: 'auth' }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T07:23:22.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - related: { ip: ['45.95.168.157'], user: ['demo'] }, - service: { type: 'system' }, - host: { hostname: 'bastion00', name: 'bastion00.siem.estc.dev' }, - event: { - ingested: '2020-09-04T07:23:26.046346Z', - timezone: '+00:00', - kind: 'event', - module: 'system', - action: 'ssh_login', - type: ['authentication_failure', 'info'], - category: ['authentication'], - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'demo' }, + fields: { + 'source.ip': ['45.95.168.157'], + 'host.name': ['bastion00.siem.estc.dev'], + '@timestamp': ['2020-09-04T07:23:22.000Z'], }, sort: [1599204202000], }, @@ -1143,72 +318,11 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { _index: '.ds-logs-system.auth-default-000001', _id: 'PqYfWXQBA6bGZw2uIhVU', _score: null, - _source: { - agent: { - hostname: 'siem-kibana', - name: 'siem-kibana', - id: 'aa3d9dc7-fef1-4c2f-a68d-25785d624e35', - ephemeral_id: 'e503bd85-11c7-4bc9-ae7d-70be1d919fb7', - type: 'filebeat', - version: '7.9.1', - }, - process: { name: 'sshd', pid: 20396 }, - log: { file: { path: '/var/log/auth.log' }, offset: 550795 }, - source: { - geo: { - continent_name: 'Asia', - region_iso_code: 'CN-BJ', - city_name: 'Beijing', - country_iso_code: 'CN', - region_name: 'Beijing', - location: { lon: 116.3889, lat: 39.9288 }, - }, - as: { - number: 45090, - organization: { - name: 'Shenzhen Tencent Computer Systems Company Limited', - }, - }, - ip: '123.206.30.76', - }, - cloud: { - availability_zone: 'us-east1-b', - instance: { name: 'siem-kibana', id: '5412578377715150143' }, - provider: 'gcp', - machine: { type: 'n1-standard-2' }, - project: { id: 'elastic-beats' }, - }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T11:20:26.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - data_stream: { namespace: 'default', type: 'logs', dataset: 'system.auth' }, - host: { - hostname: 'siem-kibana', - os: { - kernel: '4.9.0-8-amd64', - codename: 'stretch', - name: 'Debian GNU/Linux', - family: 'debian', - version: '9 (stretch)', - platform: 'debian', - }, - containerized: false, - ip: ['10.142.0.7', 'fe80::4001:aff:fe8e:7'], - name: 'siem-kibana', - id: 'aa7ca589f1b8220002f2fc61c64cfbf1', - mac: ['42:01:0a:8e:00:07'], - architecture: 'x86_64', - }, - event: { - timezone: '+00:00', - action: 'ssh_login', - type: 'authentication_failure', - category: 'authentication', - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'git' }, + fields: { + 'source.ip': ['123.206.30.76'], + 'host.id': ['aa7ca589f1b8220002f2fc61c64cfbf1'], + 'host.name': ['siem-kibana'], + '@timestamp': ['2020-09-04T11:20:26.000Z'], }, sort: [1599218426000], }, @@ -1235,48 +349,10 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { _index: 'filebeat-8.0.0-2020.09.02-000001', _id: 'iMABWHQBB-gskclyitP-', _score: null, - _source: { - agent: { - name: 'bastion00.siem.estc.dev', - id: 'f9a321c1-ec27-49fa-aacf-6a50ef6d836f', - type: 'filebeat', - ephemeral_id: '734ee3da-1a4f-4bc9-b400-e0cf0e5eeebc', - version: '8.0.0', - }, - process: { name: 'sshd', pid: 19870 }, - log: { file: { path: '/var/log/auth.log' }, offset: 984133 }, - source: { - geo: { - continent_name: 'Europe', - country_iso_code: 'HR', - location: { lon: 15.5, lat: 45.1667 }, - }, - as: { - number: 42864, - organization: { name: 'Giganet Internet Szolgaltato Kft' }, - }, - ip: '45.95.168.157', - }, - fileset: { name: 'auth' }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T07:25:28.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - related: { ip: ['45.95.168.157'], user: ['webadmin'] }, - service: { type: 'system' }, - host: { hostname: 'bastion00', name: 'bastion00.siem.estc.dev' }, - event: { - ingested: '2020-09-04T07:25:30.236651Z', - timezone: '+00:00', - kind: 'event', - module: 'system', - action: 'ssh_login', - type: ['authentication_failure', 'info'], - category: ['authentication'], - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'webadmin' }, + fields: { + 'source.ip': ['45.95.168.157'], + 'host.name': ['bastion00.siem.estc.dev'], + '@timestamp': ['2020-09-04T07:25:28.000Z'], }, sort: [1599204328000], }, @@ -1331,106 +407,10 @@ export const formattedSearchStrategyResponse = { _index: 'winlogbeat-8.0.0-2020.09.02-000001', _id: 'zqY7WXQBA6bGZw2uLeKI', _score: null, - _source: { - process: { - name: 'services.exe', - pid: 564, - executable: 'C:\\Windows\\System32\\services.exe', - }, - agent: { - build_date: '2020-07-16 09:16:27 +0000 UTC ', - name: 'siem-windows', - commit: '4dcbde39492bdc3843034bba8db811c68cb44b97 ', - id: '05e1bff7-d7a8-416a-8554-aa10288fa07d', - type: 'winlogbeat', - ephemeral_id: '655abd6c-6c33-435d-a2eb-79b2a01e6d61', - version: '8.0.0', - user: { name: 'inside_winlogbeat_user' }, - }, - winlog: { - computer_name: 'siem-windows', - process: { pid: 576, thread: { id: 880 } }, - keywords: ['Audit Success'], - logon: { id: '0x3e7', type: 'Service' }, - channel: 'Security', - event_data: { - LogonGuid: '{00000000-0000-0000-0000-000000000000}', - TargetOutboundDomainName: '-', - VirtualAccount: '%%1843', - LogonType: '5', - IpPort: '-', - TransmittedServices: '-', - SubjectLogonId: '0x3e7', - LmPackageName: '-', - TargetOutboundUserName: '-', - KeyLength: '0', - TargetLogonId: '0x3e7', - RestrictedAdminMode: '-', - SubjectUserName: 'SIEM-WINDOWS$', - TargetLinkedLogonId: '0x0', - ElevatedToken: '%%1842', - SubjectDomainName: 'WORKGROUP', - IpAddress: '-', - ImpersonationLevel: '%%1833', - TargetUserName: 'SYSTEM', - LogonProcessName: 'Advapi ', - TargetDomainName: 'NT AUTHORITY', - SubjectUserSid: 'S-1-5-18', - TargetUserSid: 'S-1-5-18', - AuthenticationPackageName: 'Negotiate', - }, - opcode: 'Info', - version: 2, - record_id: 57818, - task: 'Logon', - event_id: 4624, - provider_guid: '{54849625-5478-4994-a5ba-3e3b0328c30d}', - activity_id: '{d2485217-6bac-0000-8fbb-3f7e2571d601}', - api: 'wineventlog', - provider_name: 'Microsoft-Windows-Security-Auditing', - }, - log: { level: 'information' }, - source: { domain: '-' }, - message: - 'An account was successfully logged on.\n\nSubject:\n\tSecurity ID:\t\tS-1-5-18\n\tAccount Name:\t\tSIEM-WINDOWS$\n\tAccount Domain:\t\tWORKGROUP\n\tLogon ID:\t\t0x3E7\n\nLogon Information:\n\tLogon Type:\t\t5\n\tRestricted Admin Mode:\t-\n\tVirtual Account:\t\tNo\n\tElevated Token:\t\tYes\n\nImpersonation Level:\t\tImpersonation\n\nNew Logon:\n\tSecurity ID:\t\tS-1-5-18\n\tAccount Name:\t\tSYSTEM\n\tAccount Domain:\t\tNT AUTHORITY\n\tLogon ID:\t\t0x3E7\n\tLinked Logon ID:\t\t0x0\n\tNetwork Account Name:\t-\n\tNetwork Account Domain:\t-\n\tLogon GUID:\t\t{00000000-0000-0000-0000-000000000000}\n\nProcess Information:\n\tProcess ID:\t\t0x234\n\tProcess Name:\t\tC:\\Windows\\System32\\services.exe\n\nNetwork Information:\n\tWorkstation Name:\t-\n\tSource Network Address:\t-\n\tSource Port:\t\t-\n\nDetailed Authentication Information:\n\tLogon Process:\t\tAdvapi \n\tAuthentication Package:\tNegotiate\n\tTransited Services:\t-\n\tPackage Name (NTLM only):\t-\n\tKey Length:\t\t0\n\nThis event is generated when a logon session is created. It is generated on the computer that was accessed.\n\nThe subject fields indicate the account on the local system which requested the logon. This is most commonly a service such as the Server service, or a local process such as Winlogon.exe or Services.exe.\n\nThe logon type field indicates the kind of logon that occurred. The most common types are 2 (interactive) and 3 (network).\n\nThe New Logon fields indicate the account for whom the new logon was created, i.e. the account that was logged on.\n\nThe network fields indicate where a remote logon request originated. Workstation name is not always available and may be left blank in some cases.\n\nThe impersonation level field indicates the extent to which a process in the logon session can impersonate.\n\nThe authentication information fields provide detailed information about this specific logon request.\n\t- Logon GUID is a unique identifier that can be used to correlate this event with a KDC event.\n\t- Transited services indicate which intermediate services have participated in this logon request.\n\t- Package name indicates which sub-protocol was used among the NTLM protocols.\n\t- Key length indicates the length of the generated session key. This will be 0 if no session key was requested.', - cloud: { - availability_zone: 'us-central1-c', - instance: { name: 'siem-windows', id: '9156726559029788564' }, - provider: 'gcp', - machine: { type: 'g1-small' }, - project: { id: 'elastic-siem' }, - }, - '@timestamp': '2020-09-04T13:08:02.532Z', - related: { user: ['SYSTEM', 'SIEM-WINDOWS$'] }, - ecs: { version: '1.5.0' }, - host: { - hostname: 'siem-windows', - os: { - build: '17763.1397', - kernel: '10.0.17763.1397 (WinBuild.160101.0800)', - name: 'Windows Server 2019 Datacenter', - family: 'windows', - version: '10.0', - platform: 'windows', - }, - ip: ['fe80::ecf5:decc:3ec3:767e', '10.200.0.15'], - name: 'siem-windows', - id: 'ce1d3c9b-a815-4643-9641-ada0f2c00609', - mac: ['42:01:0a:c8:00:0f'], - architecture: 'x86_64', - }, - event: { - code: 4624, - provider: 'Microsoft-Windows-Security-Auditing', - created: '2020-09-04T13:08:03.638Z', - kind: 'event', - module: 'security', - action: 'logged-in', - category: 'authentication', - type: 'start', - outcome: 'success', - }, - user: { domain: 'NT AUTHORITY', name: 'SYSTEM', id: 'S-1-5-18' }, + fields: { + 'host.id': ['ce1d3c9b-a815-4643-9641-ada0f2c00609'], + 'host.name': ['siem-windows'], + '@timestamp': ['2020-09-04T13:08:02.532Z'], }, sort: [1599224882532], }, @@ -1457,76 +437,11 @@ export const formattedSearchStrategyResponse = { _index: '.ds-logs-system.auth-default-000001', _id: '9_sfWXQBc39KFIJbIsDh', _score: null, - _source: { - agent: { - hostname: 'siem-kibana', - name: 'siem-kibana', - id: 'aa3d9dc7-fef1-4c2f-a68d-25785d624e35', - ephemeral_id: 'e503bd85-11c7-4bc9-ae7d-70be1d919fb7', - type: 'filebeat', - version: '7.9.1', - }, - process: { name: 'sshd', pid: 20764 }, - log: { file: { path: '/var/log/auth.log' }, offset: 552463 }, - source: { - geo: { - continent_name: 'Europe', - region_iso_code: 'DE-BE', - city_name: 'Berlin', - country_iso_code: 'DE', - region_name: 'Land Berlin', - location: { lon: 13.3512, lat: 52.5727 }, - }, - as: { number: 6805, organization: { name: 'Telefonica Germany' } }, - port: 57457, - ip: '77.183.42.188', - }, - cloud: { - availability_zone: 'us-east1-b', - instance: { name: 'siem-kibana', id: '5412578377715150143' }, - provider: 'gcp', - machine: { type: 'n1-standard-2' }, - project: { id: 'elastic-beats' }, - }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T11:49:21.000Z', - system: { - auth: { - ssh: { - method: 'publickey', - signature: 'RSA SHA256:vv64JNLzKZWYA9vonnGWuW7zxWhyZrL/BFxyIGbISx8', - event: 'Accepted', - }, - }, - }, - ecs: { version: '1.5.0' }, - data_stream: { namespace: 'default', type: 'logs', dataset: 'system.auth' }, - host: { - hostname: 'siem-kibana', - os: { - kernel: '4.9.0-8-amd64', - codename: 'stretch', - name: 'Debian GNU/Linux', - family: 'debian', - version: '9 (stretch)', - platform: 'debian', - }, - containerized: false, - ip: ['10.142.0.7', 'fe80::4001:aff:fe8e:7'], - name: 'siem-kibana', - id: 'aa7ca589f1b8220002f2fc61c64cfbf1', - mac: ['42:01:0a:8e:00:07'], - architecture: 'x86_64', - }, - event: { - timezone: '+00:00', - action: 'ssh_login', - type: 'authentication_success', - category: 'authentication', - dataset: 'system.auth', - outcome: 'success', - }, - user: { name: 'tsg' }, + fields: { + 'source.ip': ['77.183.42.188'], + 'host.id': ['aa7ca589f1b8220002f2fc61c64cfbf1'], + 'host.name': ['siem-kibana'], + '@timestamp': ['2020-09-04T11:49:21.000Z'], }, sort: [1599220161000], }, @@ -1549,67 +464,11 @@ export const formattedSearchStrategyResponse = { _index: '.ds-logs-system.auth-default-000001', _id: 'ZfxZWXQBc39KFIJbLN5U', _score: null, - _source: { - agent: { - hostname: 'siem-kibana', - name: 'siem-kibana', - id: 'aa3d9dc7-fef1-4c2f-a68d-25785d624e35', - ephemeral_id: 'e503bd85-11c7-4bc9-ae7d-70be1d919fb7', - type: 'filebeat', - version: '7.9.1', - }, - process: { name: 'sshd', pid: 22913 }, - log: { file: { path: '/var/log/auth.log' }, offset: 562910 }, - source: { - geo: { - continent_name: 'Asia', - region_iso_code: 'KR-28', - city_name: 'Incheon', - country_iso_code: 'KR', - region_name: 'Incheon', - location: { lon: 126.7288, lat: 37.4562 }, - }, - as: { number: 4766, organization: { name: 'Korea Telecom' } }, - ip: '59.15.3.197', - }, - cloud: { - availability_zone: 'us-east1-b', - instance: { name: 'siem-kibana', id: '5412578377715150143' }, - provider: 'gcp', - machine: { type: 'n1-standard-2' }, - project: { id: 'elastic-beats' }, - }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T13:40:46.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - data_stream: { namespace: 'default', type: 'logs', dataset: 'system.auth' }, - host: { - hostname: 'siem-kibana', - os: { - kernel: '4.9.0-8-amd64', - codename: 'stretch', - name: 'Debian GNU/Linux', - family: 'debian', - version: '9 (stretch)', - platform: 'debian', - }, - containerized: false, - ip: ['10.142.0.7', 'fe80::4001:aff:fe8e:7'], - name: 'siem-kibana', - id: 'aa7ca589f1b8220002f2fc61c64cfbf1', - mac: ['42:01:0a:8e:00:07'], - architecture: 'x86_64', - }, - event: { - timezone: '+00:00', - action: 'ssh_login', - type: 'authentication_failure', - category: 'authentication', - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'admin' }, + fields: { + 'source.ip': ['59.15.3.197'], + 'host.id': ['aa7ca589f1b8220002f2fc61c64cfbf1'], + 'host.name': ['siem-kibana'], + '@timestamp': ['2020-09-04T13:40:46.000Z'], }, sort: [1599226846000], }, @@ -1636,47 +495,10 @@ export const formattedSearchStrategyResponse = { _index: 'filebeat-8.0.0-2020.09.02-000001', _id: 'M_xLWXQBc39KFIJbY7Cb', _score: null, - _source: { - agent: { - name: 'bastion00.siem.estc.dev', - id: 'f9a321c1-ec27-49fa-aacf-6a50ef6d836f', - type: 'filebeat', - ephemeral_id: '734ee3da-1a4f-4bc9-b400-e0cf0e5eeebc', - version: '8.0.0', - }, - process: { name: 'sshd', pid: 20671 }, - log: { file: { path: '/var/log/auth.log' }, offset: 1028103 }, - source: { - geo: { - continent_name: 'North America', - region_iso_code: 'US-NY', - city_name: 'New York', - country_iso_code: 'US', - region_name: 'New York', - location: { lon: -74, lat: 40.7157 }, - }, - ip: '64.227.88.245', - }, - fileset: { name: 'auth' }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T13:25:43.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - related: { ip: ['64.227.88.245'], user: ['user'] }, - service: { type: 'system' }, - host: { hostname: 'bastion00', name: 'bastion00.siem.estc.dev' }, - event: { - ingested: '2020-09-04T13:25:47.034172Z', - timezone: '+00:00', - kind: 'event', - module: 'system', - action: 'ssh_login', - type: ['authentication_failure', 'info'], - category: ['authentication'], - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'user' }, + fields: { + 'source.ip': ['64.227.88.245'], + 'host.name': ['bastion00.siem.estc.dev'], + '@timestamp': ['2020-09-04T13:25:43.000Z'], }, sort: [1599225943000], }, @@ -1703,47 +525,10 @@ export const formattedSearchStrategyResponse = { _index: 'filebeat-8.0.0-2020.09.02-000001', _id: 'nPxKWXQBc39KFIJb7q4w', _score: null, - _source: { - agent: { - name: 'bastion00.siem.estc.dev', - id: 'f9a321c1-ec27-49fa-aacf-6a50ef6d836f', - ephemeral_id: '734ee3da-1a4f-4bc9-b400-e0cf0e5eeebc', - type: 'filebeat', - version: '8.0.0', - }, - process: { name: 'sshd', pid: 20665 }, - log: { file: { path: '/var/log/auth.log' }, offset: 1027372 }, - source: { - geo: { - continent_name: 'North America', - region_iso_code: 'US-NY', - city_name: 'New York', - country_iso_code: 'US', - region_name: 'New York', - location: { lon: -74, lat: 40.7157 }, - }, - ip: '64.227.88.245', - }, - fileset: { name: 'auth' }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T13:25:07.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - related: { ip: ['64.227.88.245'], user: ['ubuntu'] }, - service: { type: 'system' }, - host: { hostname: 'bastion00', name: 'bastion00.siem.estc.dev' }, - event: { - ingested: '2020-09-04T13:25:16.974606Z', - timezone: '+00:00', - kind: 'event', - module: 'system', - action: 'ssh_login', - type: ['authentication_failure', 'info'], - category: ['authentication'], - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'ubuntu' }, + fields: { + 'source.ip': ['64.227.88.245'], + 'host.name': ['bastion00.siem.estc.dev'], + '@timestamp': ['2020-09-04T13:25:07.000Z'], }, sort: [1599225907000], }, @@ -1770,67 +555,11 @@ export const formattedSearchStrategyResponse = { _index: '.ds-logs-system.auth-default-000001', _id: 'mPsfWXQBc39KFIJbI8HI', _score: null, - _source: { - agent: { - hostname: 'siem-kibana', - name: 'siem-kibana', - id: 'aa3d9dc7-fef1-4c2f-a68d-25785d624e35', - type: 'filebeat', - ephemeral_id: 'e503bd85-11c7-4bc9-ae7d-70be1d919fb7', - version: '7.9.1', - }, - process: { name: 'sshd', pid: 21506 }, - log: { file: { path: '/var/log/auth.log' }, offset: 556761 }, - source: { - geo: { - continent_name: 'Asia', - region_iso_code: 'IN-DL', - city_name: 'New Delhi', - country_iso_code: 'IN', - region_name: 'National Capital Territory of Delhi', - location: { lon: 77.2245, lat: 28.6358 }, - }, - as: { number: 10029, organization: { name: 'SHYAM SPECTRA PVT LTD' } }, - ip: '180.151.228.166', - }, - cloud: { - availability_zone: 'us-east1-b', - instance: { name: 'siem-kibana', id: '5412578377715150143' }, - provider: 'gcp', - machine: { type: 'n1-standard-2' }, - project: { id: 'elastic-beats' }, - }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T12:26:36.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - data_stream: { namespace: 'default', type: 'logs', dataset: 'system.auth' }, - host: { - hostname: 'siem-kibana', - os: { - kernel: '4.9.0-8-amd64', - codename: 'stretch', - name: 'Debian GNU/Linux', - family: 'debian', - version: '9 (stretch)', - platform: 'debian', - }, - containerized: false, - ip: ['10.142.0.7', 'fe80::4001:aff:fe8e:7'], - name: 'siem-kibana', - id: 'aa7ca589f1b8220002f2fc61c64cfbf1', - mac: ['42:01:0a:8e:00:07'], - architecture: 'x86_64', - }, - event: { - timezone: '+00:00', - action: 'ssh_login', - type: 'authentication_failure', - category: 'authentication', - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'odoo' }, + fields: { + 'source.ip': ['180.151.228.166'], + 'host.id': ['aa7ca589f1b8220002f2fc61c64cfbf1'], + 'host.name': ['siem-kibana'], + '@timestamp': ['2020-09-04T12:26:36.000Z'], }, sort: [1599222396000], }, @@ -1857,48 +586,10 @@ export const formattedSearchStrategyResponse = { _index: 'filebeat-8.0.0-2020.09.02-000001', _id: 'aaToWHQBA6bGZw2uR-St', _score: null, - _source: { - agent: { - name: 'bastion00.siem.estc.dev', - id: 'f9a321c1-ec27-49fa-aacf-6a50ef6d836f', - type: 'filebeat', - ephemeral_id: '734ee3da-1a4f-4bc9-b400-e0cf0e5eeebc', - version: '8.0.0', - }, - process: { name: 'sshd', pid: 20475 }, - log: { file: { path: '/var/log/auth.log' }, offset: 1019218 }, - source: { - geo: { - continent_name: 'Europe', - region_iso_code: 'SE-AB', - city_name: 'Stockholm', - country_iso_code: 'SE', - region_name: 'Stockholm', - location: { lon: 17.7833, lat: 59.25 }, - }, - as: { number: 8473, organization: { name: 'Bahnhof AB' } }, - ip: '178.174.148.58', - }, - fileset: { name: 'auth' }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T11:37:22.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - related: { ip: ['178.174.148.58'], user: ['pi'] }, - service: { type: 'system' }, - host: { hostname: 'bastion00', name: 'bastion00.siem.estc.dev' }, - event: { - ingested: '2020-09-04T11:37:31.797423Z', - timezone: '+00:00', - kind: 'event', - module: 'system', - action: 'ssh_login', - type: ['authentication_failure', 'info'], - category: ['authentication'], - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'pi' }, + fields: { + 'source.ip': ['178.174.148.58'], + 'host.name': ['bastion00.siem.estc.dev'], + '@timestamp': ['2020-09-04T11:37:22.000Z'], }, sort: [1599219442000], }, @@ -1925,48 +616,10 @@ export const formattedSearchStrategyResponse = { _index: 'filebeat-8.0.0-2020.09.02-000001', _id: 'VaP_V3QBA6bGZw2upUbg', _score: null, - _source: { - agent: { - name: 'bastion00.siem.estc.dev', - id: 'f9a321c1-ec27-49fa-aacf-6a50ef6d836f', - type: 'filebeat', - ephemeral_id: '734ee3da-1a4f-4bc9-b400-e0cf0e5eeebc', - version: '8.0.0', - }, - process: { name: 'sshd', pid: 19849 }, - log: { file: { path: '/var/log/auth.log' }, offset: 981036 }, - source: { - geo: { - continent_name: 'Europe', - country_iso_code: 'HR', - location: { lon: 15.5, lat: 45.1667 }, - }, - as: { - number: 42864, - organization: { name: 'Giganet Internet Szolgaltato Kft' }, - }, - ip: '45.95.168.157', - }, - fileset: { name: 'auth' }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T07:23:22.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - related: { ip: ['45.95.168.157'], user: ['demo'] }, - service: { type: 'system' }, - host: { hostname: 'bastion00', name: 'bastion00.siem.estc.dev' }, - event: { - ingested: '2020-09-04T07:23:26.046346Z', - timezone: '+00:00', - kind: 'event', - module: 'system', - action: 'ssh_login', - type: ['authentication_failure', 'info'], - category: ['authentication'], - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'demo' }, + fields: { + 'source.ip': ['45.95.168.157'], + 'host.name': ['bastion00.siem.estc.dev'], + '@timestamp': ['2020-09-04T07:23:22.000Z'], }, sort: [1599204202000], }, @@ -1993,72 +646,11 @@ export const formattedSearchStrategyResponse = { _index: '.ds-logs-system.auth-default-000001', _id: 'PqYfWXQBA6bGZw2uIhVU', _score: null, - _source: { - agent: { - hostname: 'siem-kibana', - name: 'siem-kibana', - id: 'aa3d9dc7-fef1-4c2f-a68d-25785d624e35', - ephemeral_id: 'e503bd85-11c7-4bc9-ae7d-70be1d919fb7', - type: 'filebeat', - version: '7.9.1', - }, - process: { name: 'sshd', pid: 20396 }, - log: { file: { path: '/var/log/auth.log' }, offset: 550795 }, - source: { - geo: { - continent_name: 'Asia', - region_iso_code: 'CN-BJ', - city_name: 'Beijing', - country_iso_code: 'CN', - region_name: 'Beijing', - location: { lon: 116.3889, lat: 39.9288 }, - }, - as: { - number: 45090, - organization: { - name: 'Shenzhen Tencent Computer Systems Company Limited', - }, - }, - ip: '123.206.30.76', - }, - cloud: { - availability_zone: 'us-east1-b', - instance: { name: 'siem-kibana', id: '5412578377715150143' }, - provider: 'gcp', - machine: { type: 'n1-standard-2' }, - project: { id: 'elastic-beats' }, - }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T11:20:26.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - data_stream: { namespace: 'default', type: 'logs', dataset: 'system.auth' }, - host: { - hostname: 'siem-kibana', - os: { - kernel: '4.9.0-8-amd64', - codename: 'stretch', - name: 'Debian GNU/Linux', - family: 'debian', - version: '9 (stretch)', - platform: 'debian', - }, - containerized: false, - ip: ['10.142.0.7', 'fe80::4001:aff:fe8e:7'], - name: 'siem-kibana', - id: 'aa7ca589f1b8220002f2fc61c64cfbf1', - mac: ['42:01:0a:8e:00:07'], - architecture: 'x86_64', - }, - event: { - timezone: '+00:00', - action: 'ssh_login', - type: 'authentication_failure', - category: 'authentication', - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'git' }, + fields: { + 'source.ip': ['123.206.30.76'], + 'host.id': ['aa7ca589f1b8220002f2fc61c64cfbf1'], + 'host.name': ['siem-kibana'], + '@timestamp': ['2020-09-04T11:20:26.000Z'], }, sort: [1599218426000], }, @@ -2085,48 +677,10 @@ export const formattedSearchStrategyResponse = { _index: 'filebeat-8.0.0-2020.09.02-000001', _id: 'iMABWHQBB-gskclyitP-', _score: null, - _source: { - agent: { - name: 'bastion00.siem.estc.dev', - id: 'f9a321c1-ec27-49fa-aacf-6a50ef6d836f', - type: 'filebeat', - ephemeral_id: '734ee3da-1a4f-4bc9-b400-e0cf0e5eeebc', - version: '8.0.0', - }, - process: { name: 'sshd', pid: 19870 }, - log: { file: { path: '/var/log/auth.log' }, offset: 984133 }, - source: { - geo: { - continent_name: 'Europe', - country_iso_code: 'HR', - location: { lon: 15.5, lat: 45.1667 }, - }, - as: { - number: 42864, - organization: { name: 'Giganet Internet Szolgaltato Kft' }, - }, - ip: '45.95.168.157', - }, - fileset: { name: 'auth' }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T07:25:28.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - related: { ip: ['45.95.168.157'], user: ['webadmin'] }, - service: { type: 'system' }, - host: { hostname: 'bastion00', name: 'bastion00.siem.estc.dev' }, - event: { - ingested: '2020-09-04T07:25:30.236651Z', - timezone: '+00:00', - kind: 'event', - module: 'system', - action: 'ssh_login', - type: ['authentication_failure', 'info'], - category: ['authentication'], - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'webadmin' }, + fields: { + 'source.ip': ['45.95.168.157'], + 'host.name': ['bastion00.siem.estc.dev'], + '@timestamp': ['2020-09-04T07:25:28.000Z'], }, sort: [1599204328000], }, @@ -2163,7 +717,6 @@ export const formattedSearchStrategyResponse = { ], ignore_unavailable: true, body: { - docvalue_fields: mockOptions.docValueFields, aggregations: { stack_by_count: { cardinality: { field: 'user.name' } }, stack_by: { @@ -2179,7 +732,7 @@ export const formattedSearchStrategyResponse = { lastFailure: { top_hits: { size: 1, - _source: [], + _source: false, sort: [{ '@timestamp': { order: 'desc' } }], }, }, @@ -2191,7 +744,7 @@ export const formattedSearchStrategyResponse = { lastSuccess: { top_hits: { size: 1, - _source: [], + _source: false, sort: [{ '@timestamp': { order: 'desc' } }], }, }, @@ -2218,6 +771,16 @@ export const formattedSearchStrategyResponse = { }, }, size: 0, + _source: false, + fields: [ + 'source.ip', + 'host.id', + 'host.name', + { + field: '@timestamp', + format: 'strict_date_optional_time', + }, + ], }, track_total_hits: false, }, @@ -2238,7 +801,7 @@ export const formattedSearchStrategyResponse = { host: { id: ['ce1d3c9b-a815-4643-9641-ada0f2c00609'], name: ['siem-windows'] }, }, }, - cursor: { value: '', tiebreaker: null }, + cursor: { value: undefined, tiebreaker: null }, }, { node: { @@ -2252,13 +815,13 @@ export const formattedSearchStrategyResponse = { host: { id: ['aa7ca589f1b8220002f2fc61c64cfbf1'], name: ['siem-kibana'] }, }, }, - cursor: { value: '', tiebreaker: null }, + cursor: { value: undefined, tiebreaker: null }, }, { node: { failures: 23, successes: 0, - _id: 'admin+23', + _id: 'ZfxZWXQBc39KFIJbLN5U', stackedValue: ['admin'], lastFailure: { timestamp: ['2020-09-04T13:40:46.000Z'], @@ -2266,13 +829,13 @@ export const formattedSearchStrategyResponse = { host: { id: ['aa7ca589f1b8220002f2fc61c64cfbf1'], name: ['siem-kibana'] }, }, }, - cursor: { value: '', tiebreaker: null }, + cursor: { value: undefined, tiebreaker: null }, }, { node: { failures: 21, successes: 0, - _id: 'user+21', + _id: 'M_xLWXQBc39KFIJbY7Cb', stackedValue: ['user'], lastFailure: { timestamp: ['2020-09-04T13:25:43.000Z'], @@ -2280,13 +843,13 @@ export const formattedSearchStrategyResponse = { host: { name: ['bastion00.siem.estc.dev'] }, }, }, - cursor: { value: '', tiebreaker: null }, + cursor: { value: undefined, tiebreaker: null }, }, { node: { failures: 18, successes: 0, - _id: 'ubuntu+18', + _id: 'nPxKWXQBc39KFIJb7q4w', stackedValue: ['ubuntu'], lastFailure: { timestamp: ['2020-09-04T13:25:07.000Z'], @@ -2294,13 +857,13 @@ export const formattedSearchStrategyResponse = { host: { name: ['bastion00.siem.estc.dev'] }, }, }, - cursor: { value: '', tiebreaker: null }, + cursor: { value: undefined, tiebreaker: null }, }, { node: { failures: 17, successes: 0, - _id: 'odoo+17', + _id: 'mPsfWXQBc39KFIJbI8HI', stackedValue: ['odoo'], lastFailure: { timestamp: ['2020-09-04T12:26:36.000Z'], @@ -2308,13 +871,13 @@ export const formattedSearchStrategyResponse = { host: { id: ['aa7ca589f1b8220002f2fc61c64cfbf1'], name: ['siem-kibana'] }, }, }, - cursor: { value: '', tiebreaker: null }, + cursor: { value: undefined, tiebreaker: null }, }, { node: { failures: 17, successes: 0, - _id: 'pi+17', + _id: 'aaToWHQBA6bGZw2uR-St', stackedValue: ['pi'], lastFailure: { timestamp: ['2020-09-04T11:37:22.000Z'], @@ -2322,13 +885,13 @@ export const formattedSearchStrategyResponse = { host: { name: ['bastion00.siem.estc.dev'] }, }, }, - cursor: { value: '', tiebreaker: null }, + cursor: { value: undefined, tiebreaker: null }, }, { node: { failures: 14, successes: 0, - _id: 'demo+14', + _id: 'VaP_V3QBA6bGZw2upUbg', stackedValue: ['demo'], lastFailure: { timestamp: ['2020-09-04T07:23:22.000Z'], @@ -2336,13 +899,13 @@ export const formattedSearchStrategyResponse = { host: { name: ['bastion00.siem.estc.dev'] }, }, }, - cursor: { value: '', tiebreaker: null }, + cursor: { value: undefined, tiebreaker: null }, }, { node: { failures: 13, successes: 0, - _id: 'git+13', + _id: 'PqYfWXQBA6bGZw2uIhVU', stackedValue: ['git'], lastFailure: { timestamp: ['2020-09-04T11:20:26.000Z'], @@ -2350,13 +913,13 @@ export const formattedSearchStrategyResponse = { host: { id: ['aa7ca589f1b8220002f2fc61c64cfbf1'], name: ['siem-kibana'] }, }, }, - cursor: { value: '', tiebreaker: null }, + cursor: { value: undefined, tiebreaker: null }, }, { node: { failures: 13, successes: 0, - _id: 'webadmin+13', + _id: 'iMABWHQBB-gskclyitP-', stackedValue: ['webadmin'], lastFailure: { timestamp: ['2020-09-04T07:25:28.000Z'], @@ -2364,7 +927,7 @@ export const formattedSearchStrategyResponse = { host: { name: ['bastion00.siem.estc.dev'] }, }, }, - cursor: { value: '', tiebreaker: null }, + cursor: { value: undefined, tiebreaker: null }, }, ], totalCount: 188, @@ -2385,7 +948,6 @@ export const expectedDsl = { ], ignore_unavailable: true, body: { - docvalue_fields: mockOptions.docValueFields, aggregations: { stack_by_count: { cardinality: { field: 'user.name' } }, stack_by: { @@ -2399,7 +961,7 @@ export const expectedDsl = { filter: { term: { 'event.outcome': 'failure' } }, aggs: { lastFailure: { - top_hits: { size: 1, _source: [], sort: [{ '@timestamp': { order: 'desc' } }] }, + top_hits: { size: 1, _source: false, sort: [{ '@timestamp': { order: 'desc' } }] }, }, }, }, @@ -2407,7 +969,7 @@ export const expectedDsl = { filter: { term: { 'event.outcome': 'success' } }, aggs: { lastSuccess: { - top_hits: { size: 1, _source: [], sort: [{ '@timestamp': { order: 'desc' } }] }, + top_hits: { size: 1, _source: false, sort: [{ '@timestamp': { order: 'desc' } }] }, }, }, }, @@ -2431,6 +993,16 @@ export const expectedDsl = { ], }, }, + _source: false, + fields: [ + 'source.ip', + 'host.id', + 'host.name', + { + field: '@timestamp', + format: 'strict_date_optional_time', + }, + ], size: 0, }, track_total_hits: false, @@ -2441,7 +1013,7 @@ export const mockHit: AuthenticationHit = { _type: 'type-123', _id: 'id-123', _score: 10, - _source: { + fields: { '@timestamp': 'time-1', }, cursor: 'cursor-1', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/dsl/query.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/dsl/query.dsl.ts index e018716d4c216..3c32ae3f85944 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/dsl/query.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/dsl/query.dsl.ts @@ -5,26 +5,10 @@ * 2.0. */ -import { isEmpty } from 'lodash/fp'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { UserAuthenticationsRequestOptions } from '../../../../../../../common/search_strategy/security_solution/users/authentications'; -import { sourceFieldsMap, hostFieldsMap } from '../../../../../../../common/ecs/ecs_fields'; - import { createQueryFilterClauses } from '../../../../../../utils/build_query'; -import { reduceFields } from '../../../../../../utils/build_query/reduce_fields'; - import { authenticationsFields } from '../helpers'; -import { extendMap } from '../../../../../../../common/ecs/ecs_fields/extend_map'; - -export const auditdFieldsMap: Readonly> = { - latest: '@timestamp', - 'lastSuccess.timestamp': 'lastSuccess.@timestamp', - 'lastFailure.timestamp': 'lastFailure.@timestamp', - ...{ ...extendMap('lastSuccess', sourceFieldsMap) }, - ...{ ...extendMap('lastSuccess', hostFieldsMap) }, - ...{ ...extendMap('lastFailure', sourceFieldsMap) }, - ...{ ...extendMap('lastFailure', hostFieldsMap) }, -}; export const buildQuery = ({ filterQuery, @@ -32,13 +16,7 @@ export const buildQuery = ({ timerange: { from, to }, pagination: { querySize }, defaultIndex, - docValueFields, }: UserAuthenticationsRequestOptions) => { - const esFields = reduceFields(authenticationsFields, { - ...hostFieldsMap, - ...sourceFieldsMap, - }) as string[]; - const filter = [ ...createQueryFilterClauses(filterQuery), { term: { 'event.category': 'authentication' } }, @@ -52,13 +30,13 @@ export const buildQuery = ({ }, }, ]; + const queryFields = authenticationsFields.filter((field) => field !== 'timestamp'); const dslQuery = { allow_no_indices: true, index: defaultIndex, ignore_unavailable: true, body: { - ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), aggregations: { stack_by_count: { cardinality: { @@ -85,7 +63,7 @@ export const buildQuery = ({ lastFailure: { top_hits: { size: 1, - _source: esFields, + _source: false, sort: [{ '@timestamp': { order: 'desc' as const } }], }, }, @@ -101,7 +79,7 @@ export const buildQuery = ({ lastSuccess: { top_hits: { size: 1, - _source: esFields, + _source: false, sort: [{ '@timestamp': { order: 'desc' as const } }], }, }, @@ -116,6 +94,14 @@ export const buildQuery = ({ }, }, size: 0, + _source: false, + fields: [ + ...queryFields, + { + field: '@timestamp', + format: 'strict_date_optional_time', + }, + ], }, track_total_hits: false, }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/helpers.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/helpers.test.ts index 1e745ffcbf2ed..a8eea076ae1be 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/helpers.test.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/helpers.test.ts @@ -6,15 +6,12 @@ */ import { AuthenticationsEdges } from '../../../../../../common/search_strategy'; -import { auditdFieldsMap } from './dsl/query.dsl'; - import { formatAuthenticationData } from './helpers'; import { mockHit } from './__mocks__'; describe('#formatAuthenticationsData', () => { test('it formats a authentication with an empty set', () => { - const fields: readonly string[] = ['']; - const data = formatAuthenticationData(fields, mockHit, auditdFieldsMap); + const data = formatAuthenticationData(mockHit); const expected: AuthenticationsEdges = { cursor: { tiebreaker: null, @@ -32,8 +29,7 @@ describe('#formatAuthenticationsData', () => { }); test('it formats a authentications with a source ip correctly', () => { - const fields: readonly string[] = ['lastSuccess.source.ip']; - const data = formatAuthenticationData(fields, mockHit, auditdFieldsMap); + const data = formatAuthenticationData(mockHit); const expected: AuthenticationsEdges = { cursor: { tiebreaker: null, @@ -51,8 +47,7 @@ describe('#formatAuthenticationsData', () => { }); test('it formats a authentications with a host name only', () => { - const fields: readonly string[] = ['lastSuccess.host.name']; - const data = formatAuthenticationData(fields, mockHit, auditdFieldsMap); + const data = formatAuthenticationData(mockHit); const expected: AuthenticationsEdges = { cursor: { tiebreaker: null, @@ -70,8 +65,7 @@ describe('#formatAuthenticationsData', () => { }); test('it formats a authentications with a host id only', () => { - const fields: readonly string[] = ['lastSuccess.host.id']; - const data = formatAuthenticationData(fields, mockHit, auditdFieldsMap); + const data = formatAuthenticationData(mockHit); const expected: AuthenticationsEdges = { cursor: { tiebreaker: null, @@ -89,8 +83,7 @@ describe('#formatAuthenticationsData', () => { }); test('it formats a authentications with a host name and id correctly', () => { - const fields: readonly string[] = ['lastSuccess.host.name', 'lastSuccess.host.id']; - const data = formatAuthenticationData(fields, mockHit, auditdFieldsMap); + const data = formatAuthenticationData(mockHit); const expected: AuthenticationsEdges = { cursor: { tiebreaker: null, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/helpers.ts index 43baa4aadea14..46c0a83a3b572 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/helpers.ts @@ -7,8 +7,8 @@ import { get, getOr, isEmpty } from 'lodash/fp'; import { set } from '@elastic/safer-lodash-set/fp'; -import { mergeFieldsWithHit } from '../../../../../utils/build_query'; import { toObjectArrayOfStrings } from '../../../../../../common/utils/to_array'; +import { sourceFieldsMap, hostFieldsMap } from '../../../../../../common/ecs/ecs_fields'; import { AuthenticationsEdges, AuthenticationHit, @@ -17,78 +17,79 @@ import { StrategyResponseType, } from '../../../../../../common/search_strategy/security_solution'; -export const authenticationsFields = [ - '_id', - 'failures', - 'successes', - 'stackedValue', - 'lastSuccess.timestamp', - 'lastSuccess.source.ip', - 'lastSuccess.host.id', - 'lastSuccess.host.name', - 'lastFailure.timestamp', - 'lastFailure.source.ip', - 'lastFailure.host.id', - 'lastFailure.host.name', -]; +export const authenticationsFields = ['timestamp', 'source.ip', 'host.id', 'host.name']; +export const authenticationsFieldsMap: Readonly> = { + latest: '@timestamp', + lastSuccess: { + timestamp: '@timestamp', + ...sourceFieldsMap, + ...hostFieldsMap, + }, + lastFailure: { + timestamp: '@timestamp', + ...sourceFieldsMap, + ...hostFieldsMap, + }, +}; -export const formatAuthenticationData = ( - fields: readonly string[] = authenticationsFields, - hit: AuthenticationHit, - fieldMap: Readonly> -): AuthenticationsEdges => - fields.reduce( - (flattenedFields, fieldName) => { - if (hit.cursor) { - flattenedFields.cursor.value = hit.cursor; - } - flattenedFields.node = { - ...flattenedFields.node, - ...{ - _id: hit._id, - stackedValue: [hit.stackedValue], - failures: hit.failures, - successes: hit.successes, - }, - }; - const mergedResult = mergeFieldsWithHit(fieldName, flattenedFields, fieldMap, hit); - const fieldPath = `node.${fieldName}`; - const fieldValue = get(fieldPath, mergedResult); +export const formatAuthenticationData = (hit: AuthenticationHit): AuthenticationsEdges => { + let flattenedFields = { + node: { + _id: hit._id, + stackedValue: [hit.stackedValue], + failures: hit.failures, + successes: hit.successes, + }, + cursor: { + value: hit.cursor, + tiebreaker: null, + }, + }; + + const lastSuccessFields = getAuthenticationFields(authenticationsFields, hit, 'lastSuccess'); + if (Object.keys(lastSuccessFields).length > 0) { + flattenedFields = set('node.lastSuccess', lastSuccessFields, flattenedFields); + } + + const lastFailureFields = getAuthenticationFields(authenticationsFields, hit, 'lastFailure'); + if (Object.keys(lastFailureFields).length > 0) { + flattenedFields = set('node.lastFailure', lastFailureFields, flattenedFields); + } + + return flattenedFields; +}; + +const getAuthenticationFields = (fields: string[], hit: AuthenticationHit, parentField: string) => { + return fields.reduce((flattenedFields, fieldName) => { + const fieldPath = `${fieldName}`; + const esField = get(`${parentField}['${fieldName}']`, authenticationsFieldsMap); + + if (!isEmpty(esField)) { + const fieldValue = get(`${parentField}['${esField}']`, hit.fields); if (!isEmpty(fieldValue)) { return set( fieldPath, toObjectArrayOfStrings(fieldValue).map(({ str }) => str), - mergedResult + flattenedFields ); - } else { - return mergedResult; } - }, - { - node: { - failures: 0, - successes: 0, - _id: '', - stackedValue: [''], - }, - cursor: { - value: '', - tiebreaker: null, - }, } - ); + + return flattenedFields; + }, {}); +}; export const getHits = (response: StrategyResponseType) => getOr([], 'aggregations.stack_by.buckets', response.rawResponse).map( (bucket: AuthenticationBucket) => ({ _id: getOr( `${bucket.key}+${bucket.doc_count}`, - 'failures.lastFailure.hits.hits[0].id', + 'failures.lastFailure.hits.hits[0]._id', bucket ), - _source: { - lastSuccess: getOr(null, 'successes.lastSuccess.hits.hits[0]._source', bucket), - lastFailure: getOr(null, 'failures.lastFailure.hits.hits[0]._source', bucket), + fields: { + lastSuccess: getOr(null, 'successes.lastSuccess.hits.hits[0].fields', bucket), + lastFailure: getOr(null, 'failures.lastFailure.hits.hits[0].fields', bucket), }, stackedValue: bucket.key, failures: bucket.failures.doc_count, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/index.tsx b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/index.tsx index 5bb62f685ce4b..f2483b78dc3ef 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/index.tsx +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/authentications/index.tsx @@ -20,9 +20,9 @@ import { UsersQueries } from '../../../../../../common/search_strategy/security_ import { inspectStringifyObject } from '../../../../../utils/build_query'; import { SecuritySolutionFactory } from '../../types'; -import { auditdFieldsMap, buildQuery as buildAuthenticationQuery } from './dsl/query.dsl'; +import { buildQuery as buildAuthenticationQuery } from './dsl/query.dsl'; -import { authenticationsFields, formatAuthenticationData, getHits } from './helpers'; +import { formatAuthenticationData, getHits } from './helpers'; export const authentications: SecuritySolutionFactory = { buildDsl: (options: UserAuthenticationsRequestOptions) => { @@ -42,7 +42,7 @@ export const authentications: SecuritySolutionFactory - formatAuthenticationData(authenticationsFields, hit, auditdFieldsMap) + formatAuthenticationData(hit) ); const edges = authenticationEdges.splice(cursorStart, querySize - cursorStart); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/details/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/details/__mocks__/index.ts index 33760eec4556e..5b54ffaf8dff8 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/details/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/details/__mocks__/index.ts @@ -12,12 +12,6 @@ import { UserDetailsRequestOptions } from '../../../../../../../common/search_st export const mockOptions: UserDetailsRequestOptions = { defaultIndex: ['test_indices*'], - docValueFields: [ - { - field: '@timestamp', - format: 'date_time', - }, - ], factoryQueryType: UsersQueries.details, filterQuery: '{"bool":{"must":[],"filter":[{"match_all":{}},{"match_phrase":{"user.name":{"query":"test_user"}}}],"should":[],"must_not":[]}}', diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index 95aaeffad7f71..cc1656ace3c65 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -32,7 +32,6 @@ { "path": "../actions/tsconfig.json" }, { "path": "../alerting/tsconfig.json" }, { "path": "../cases/tsconfig.json" }, - { "path": "../data_enhanced/tsconfig.json" }, { "path": "../encrypted_saved_objects/tsconfig.json" }, { "path": "../features/tsconfig.json" }, { "path": "../fleet/tsconfig.json" }, diff --git a/x-pack/plugins/session_view/common/mocks/constants/session_view_process.mock.ts b/x-pack/plugins/session_view/common/mocks/constants/session_view_process.mock.ts index 6def7c7bcbd47..76f954d1fe72e 100644 --- a/x-pack/plugins/session_view/common/mocks/constants/session_view_process.mock.ts +++ b/x-pack/plugins/session_view/common/mocks/constants/session_view_process.mock.ts @@ -819,8 +819,8 @@ export const mockAlerts: ProcessEvent[] = [ architecture: 'x86_64', hostname: 'james-fleet-714-2', id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d', - ip: '127.0.0.1,::1,10.132.0.50,fe80::7d39:3147:4d9a:f809', - mac: '42:01:0a:84:00:32', + ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'], + mac: ['42:01:0a:84:00:32'], name: 'james-fleet-714-2', os: { family: 'centos', @@ -1006,8 +1006,8 @@ export const mockAlerts: ProcessEvent[] = [ architecture: 'x86_64', hostname: 'james-fleet-714-2', id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d', - ip: '127.0.0.1,::1,10.132.0.50,fe80::7d39:3147:4d9a:f809', - mac: '42:01:0a:84:00:32', + ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'], + mac: ['42:01:0a:84:00:32'], name: 'james-fleet-714-2', os: { family: 'centos', @@ -1285,8 +1285,8 @@ export const childProcessMock: Process = { architecture: 'x86_64', hostname: 'james-fleet-714-2', id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d', - ip: '127.0.0.1,::1,10.132.0.50,fe80::7d39:3147:4d9a:f809', - mac: '42:01:0a:84:00:32', + ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'], + mac: ['42:01:0a:84:00:32'], name: 'james-fleet-714-2', os: { family: 'centos', @@ -1370,8 +1370,8 @@ export const processMock: Process = { architecture: 'x86_64', hostname: 'james-fleet-714-2', id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d', - ip: '127.0.0.1,::1,10.132.0.50,fe80::7d39:3147:4d9a:f809', - mac: '42:01:0a:84:00:32', + ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'], + mac: ['42:01:0a:84:00:32'], name: 'james-fleet-714-2', os: { family: 'centos', diff --git a/x-pack/plugins/session_view/common/mocks/responses/session_view_process_events.mock.ts b/x-pack/plugins/session_view/common/mocks/responses/session_view_process_events.mock.ts index 47849f859ba9c..78206cc1a6320 100644 --- a/x-pack/plugins/session_view/common/mocks/responses/session_view_process_events.mock.ts +++ b/x-pack/plugins/session_view/common/mocks/responses/session_view_process_events.mock.ts @@ -24,8 +24,8 @@ export const sessionViewProcessEventsMock: ProcessEventResults = { architecture: 'x86_64', hostname: 'james-fleet-714-2', id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d', - ip: '127.0.0.1,::1,10.132.0.50,fe80::7d39:3147:4d9a:f809', - mac: '42:01:0a:84:00:32', + ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'], + mac: ['42:01:0a:84:00:32'], name: 'james-fleet-714-2', os: { Ext: { @@ -427,8 +427,8 @@ export const sessionViewProcessEventsMock: ProcessEventResults = { architecture: 'x86_64', hostname: 'james-fleet-714-2', id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d', - ip: '127.0.0.1,::1,10.132.0.50,fe80::7d39:3147:4d9a:f809', - mac: '42:01:0a:84:00:32', + ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'], + mac: ['42:01:0a:84:00:32'], name: 'james-fleet-714-2', os: { Ext: { @@ -836,8 +836,8 @@ export const sessionViewProcessEventsMock: ProcessEventResults = { architecture: 'x86_64', hostname: 'james-fleet-714-2', id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d', - ip: '127.0.0.1,::1,10.132.0.50,fe80::7d39:3147:4d9a:f809', - mac: '42:01:0a:84:00:32', + ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'], + mac: ['42:01:0a:84:00:32'], name: 'james-fleet-714-2', os: { Ext: { diff --git a/x-pack/plugins/session_view/common/types/process_tree/index.ts b/x-pack/plugins/session_view/common/types/process_tree/index.ts index 11f5aeb2ffac2..f337b6a38c742 100644 --- a/x-pack/plugins/session_view/common/types/process_tree/index.ts +++ b/x-pack/plugins/session_view/common/types/process_tree/index.ts @@ -97,8 +97,8 @@ export interface ProcessEventHost { architecture?: string; hostname?: string; id?: string; - ip?: string; - mac?: string; + ip?: string[]; + mac?: string[]; name?: string; os?: { family?: string; diff --git a/x-pack/plugins/session_view/public/components/back_to_investigated_alert/index.tsx b/x-pack/plugins/session_view/public/components/back_to_investigated_alert/index.tsx index 6252335c29f61..78ce2f4d871ea 100644 --- a/x-pack/plugins/session_view/public/components/back_to_investigated_alert/index.tsx +++ b/x-pack/plugins/session_view/public/components/back_to_investigated_alert/index.tsx @@ -29,7 +29,7 @@ export const BackToInvestigatedAlert = ({ return (
{ - const { euiTheme } = useEuiTheme(); + const { euiTheme, euiVars } = useEuiTheme(); const cached = useMemo(() => { - const { size, colors, font } = euiTheme; + const { size, font } = euiTheme; - const buttonBackgroundColor = colors.primary; + const buttonStyle = { + color: euiLightVars.euiColorEmptyShade, + backgroundColor: euiLightVars.euiColorPrimaryText, + }; const container: CSSObject = { position: 'absolute', @@ -41,18 +44,18 @@ export const useStyles = ({ isDisplayedAbove }: StylesDeps) => { if (isDisplayedAbove) { container.top = 0; - container.background = `linear-gradient(180deg, ${theme.euiColorLightestShade} 0%, transparent 100%)`; + container.background = `linear-gradient(180deg, ${euiVars.euiColorLightestShade} 0%, transparent 100%)`; } else { container.bottom = 0; - container.background = `linear-gradient(360deg, ${theme.euiColorLightestShade} 0%, transparent 100%)`; + container.background = `linear-gradient(360deg, ${euiVars.euiColorLightestShade} 0%, transparent 100%)`; } return { container, jumpBackBadge, - buttonBackgroundColor, + buttonStyle, }; - }, [isDisplayedAbove, euiTheme]); + }, [isDisplayedAbove, euiTheme, euiVars]); return cached; }; diff --git a/x-pack/plugins/session_view/public/components/detail_panel_copy/__snapshots__/index.test.tsx.snap b/x-pack/plugins/session_view/public/components/detail_panel_copy/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000000..ea9f03408af67 --- /dev/null +++ b/x-pack/plugins/session_view/public/components/detail_panel_copy/__snapshots__/index.test.tsx.snap @@ -0,0 +1,88 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DetailPanelCopy component When DetailPanelCopy is mounted renders DetailPanelCopy correctly 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
+
+ + + copy component test + + +
+
+ , + "container":
+
+ + + copy component test + + +
+
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/x-pack/plugins/session_view/public/components/detail_panel_copy/index.test.tsx b/x-pack/plugins/session_view/public/components/detail_panel_copy/index.test.tsx index 4d3c5e16e1bb7..2b1f53c9ab8ab 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_copy/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/detail_panel_copy/index.test.tsx @@ -24,10 +24,13 @@ describe('DetailPanelCopy component', () => { describe('When DetailPanelCopy is mounted', () => { it('renders DetailPanelCopy correctly', async () => { renderResult = mockedContext.render( - {TEST_CHILD} + + {TEST_CHILD} + ); expect(renderResult.queryByText(TEST_TEXT_COPY)).toBeVisible(); + expect(renderResult).toMatchSnapshot(); }); }); }); diff --git a/x-pack/plugins/session_view/public/components/detail_panel_copy/index.tsx b/x-pack/plugins/session_view/public/components/detail_panel_copy/index.tsx index fb25e85e19ca4..72febd9ea25ea 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_copy/index.tsx +++ b/x-pack/plugins/session_view/public/components/detail_panel_copy/index.tsx @@ -8,12 +8,12 @@ import React, { ReactNode } from 'react'; import { EuiButtonIcon, EuiCopy, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { DetailPanelListItem } from '../detail_panel_list_item'; -import { dataOrDash } from '../../utils/data_or_dash'; import { useStyles } from './styles'; interface DetailPanelCopyDeps { children: ReactNode; - textToCopy: string | number | undefined; + textToCopy: string; + tooltipContent: ReactNode; display?: 'inlineBlock' | 'block' | undefined; } @@ -28,13 +28,14 @@ interface DetailPanelListItemProps { export const DetailPanelCopy = ({ children, textToCopy, + tooltipContent, display = 'inlineBlock', }: DetailPanelCopyDeps) => { const styles = useStyles(); const props: DetailPanelListItemProps = { copy: ( - + {(copy) => ( - + <>{children} diff --git a/x-pack/plugins/session_view/public/components/detail_panel_host_tab/helpers.test.ts b/x-pack/plugins/session_view/public/components/detail_panel_host_tab/helpers.test.ts new file mode 100644 index 0000000000000..a2d096d91310e --- /dev/null +++ b/x-pack/plugins/session_view/public/components/detail_panel_host_tab/helpers.test.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { ProcessEventHost } from '../../../common/types/process_tree'; +import { DASH } from '../../constants'; +import { getHostData } from './helpers'; + +const MOCK_HOST_DATA: ProcessEventHost = { + architecture: 'x86_64', + hostname: 'james-fleet-714-2', + id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d', + ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'], + mac: ['42:01:0a:84:00:32'], + name: 'james-fleet-714-2', + os: { + family: 'centos', + full: 'CentOS 7.9.2009', + kernel: '3.10.0-1160.31.1.el7.x86_64 #1 SMP Thu Jun 10 13:32:12 UTC 2021', + name: 'Linux', + platform: 'centos', + version: '7.9.2009', + }, +}; + +describe('detail panel host tab helpers tests', () => { + it('getHostData returns fields with a dash with undefined host', () => { + const result = getHostData(undefined); + expect(result.architecture).toEqual(DASH); + expect(result.hostname).toEqual(DASH); + expect(result.id).toEqual(DASH); + expect(result.ip).toEqual(DASH); + expect(result.mac).toEqual(DASH); + expect(result.name).toEqual(DASH); + expect(result.os.family).toEqual(DASH); + expect(result.os.full).toEqual(DASH); + expect(result.os.kernel).toEqual(DASH); + expect(result.os.name).toEqual(DASH); + expect(result.os.platform).toEqual(DASH); + expect(result.os.version).toEqual(DASH); + }); + + it('getHostData returns dashes for missing fields', () => { + const result = getHostData({ + ...MOCK_HOST_DATA, + ip: ['127.0.0.1', '', '', 'fe80::7d39:3147:4d9a:f809'], + name: undefined, + os: { + ...MOCK_HOST_DATA.os, + full: undefined, + platform: undefined, + }, + }); + expect(result.architecture).toEqual(MOCK_HOST_DATA.architecture); + expect(result.hostname).toEqual(MOCK_HOST_DATA.hostname); + expect(result.id).toEqual(MOCK_HOST_DATA.id); + expect(result.ip).toEqual(['127.0.0.1', DASH, DASH, 'fe80::7d39:3147:4d9a:f809'].join(', ')); + expect(result.mac).toEqual(MOCK_HOST_DATA.mac?.join(', ')); + expect(result.name).toEqual(DASH); + expect(result.os.family).toEqual(MOCK_HOST_DATA.os?.family); + expect(result.os.full).toEqual(DASH); + expect(result.os.kernel).toEqual(MOCK_HOST_DATA.os?.kernel); + expect(result.os.name).toEqual(MOCK_HOST_DATA.os?.name); + expect(result.os.platform).toEqual(DASH); + expect(result.os.version).toEqual(MOCK_HOST_DATA.os?.version); + }); + + it('getHostData returns all data provided', () => { + const result = getHostData(MOCK_HOST_DATA); + expect(result.architecture).toEqual(MOCK_HOST_DATA.architecture); + expect(result.hostname).toEqual(MOCK_HOST_DATA.hostname); + expect(result.id).toEqual(MOCK_HOST_DATA.id); + expect(result.ip).toEqual(MOCK_HOST_DATA.ip?.join(', ')); + expect(result.mac).toEqual(MOCK_HOST_DATA.mac?.join(', ')); + expect(result.name).toEqual(MOCK_HOST_DATA.name); + expect(result.os.family).toEqual(MOCK_HOST_DATA.os?.family); + expect(result.os.full).toEqual(MOCK_HOST_DATA.os?.full); + expect(result.os.kernel).toEqual(MOCK_HOST_DATA.os?.kernel); + expect(result.os.name).toEqual(MOCK_HOST_DATA.os?.name); + expect(result.os.platform).toEqual(MOCK_HOST_DATA.os?.platform); + expect(result.os.version).toEqual(MOCK_HOST_DATA.os?.version); + }); +}); diff --git a/x-pack/plugins/session_view/public/components/detail_panel_host_tab/helpers.ts b/x-pack/plugins/session_view/public/components/detail_panel_host_tab/helpers.ts new file mode 100644 index 0000000000000..72565f5885e37 --- /dev/null +++ b/x-pack/plugins/session_view/public/components/detail_panel_host_tab/helpers.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 { ProcessEventHost } from '../../../common/types/process_tree'; +import { DASH } from '../../constants'; +import { DetailPanelHost } from '../../types'; +import { dataOrDash } from '../../utils/data_or_dash'; + +export const getHostData = (host: ProcessEventHost | undefined): DetailPanelHost => { + const detailPanelHost: DetailPanelHost = { + architecture: DASH, + hostname: DASH, + id: DASH, + ip: DASH, + mac: DASH, + name: DASH, + os: { + family: DASH, + full: DASH, + kernel: DASH, + name: DASH, + platform: DASH, + version: DASH, + }, + }; + + if (!host) { + return detailPanelHost; + } + + detailPanelHost.hostname = dataOrDash(host.hostname).toString(); + detailPanelHost.id = dataOrDash(host.id).toString(); + detailPanelHost.ip = host.ip?.map?.((ip) => dataOrDash(ip)).join(', ') ?? DASH; + detailPanelHost.mac = host.mac?.map?.((mac) => dataOrDash(mac)).join(', ') ?? DASH; + detailPanelHost.name = dataOrDash(host.name).toString(); + detailPanelHost.architecture = dataOrDash(host.architecture).toString(); + detailPanelHost.os.family = dataOrDash(host.os?.family).toString(); + detailPanelHost.os.full = dataOrDash(host.os?.full).toString(); + detailPanelHost.os.kernel = dataOrDash(host.os?.kernel).toString(); + detailPanelHost.os.name = dataOrDash(host.os?.name).toString(); + detailPanelHost.os.platform = dataOrDash(host.os?.platform).toString(); + detailPanelHost.os.version = dataOrDash(host.os?.version).toString(); + + return detailPanelHost; +}; diff --git a/x-pack/plugins/session_view/public/components/detail_panel_host_tab/index.test.tsx b/x-pack/plugins/session_view/public/components/detail_panel_host_tab/index.test.tsx index 17e9102fb8ed9..41a5ada524974 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_host_tab/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/detail_panel_host_tab/index.test.tsx @@ -13,8 +13,8 @@ import { DetailPanelHostTab } from '.'; const TEST_ARCHITECTURE = 'x86_64'; const TEST_HOSTNAME = 'host-james-fleet-714-2'; const TEST_ID = '48c1b3f1ac5da4e0057fc9f60f4d1d5d'; -const TEST_IP = '127.0.0.1,::1,10.132.0.50,fe80::7d39:3147:4d9a:f809'; -const TEST_MAC = '42:01:0a:84:00:32'; +const TEST_IP = ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809']; +const TEST_MAC = ['42:01:0a:84:00:32']; const TEST_NAME = 'name-james-fleet-714-2'; const TEST_OS_FAMILY = 'family-centos'; const TEST_OS_FULL = 'full-CentOS 7.9.2009'; @@ -62,8 +62,8 @@ describe('DetailPanelHostTab component', () => { expect(renderResult.queryByText(TEST_ARCHITECTURE)).toBeVisible(); expect(renderResult.queryByText(TEST_HOSTNAME)).toBeVisible(); expect(renderResult.queryByText(TEST_ID)).toBeVisible(); - expect(renderResult.queryByText(TEST_IP)).toBeVisible(); - expect(renderResult.queryByText(TEST_MAC)).toBeVisible(); + expect(renderResult.queryByText(TEST_IP.join(', '))).toBeVisible(); + expect(renderResult.queryByText(TEST_MAC.join(', '))).toBeVisible(); expect(renderResult.queryByText(TEST_NAME)).toBeVisible(); // expand host os accordion diff --git a/x-pack/plugins/session_view/public/components/detail_panel_host_tab/index.tsx b/x-pack/plugins/session_view/public/components/detail_panel_host_tab/index.tsx index b2869f6c4e24b..2b1c2f97fa738 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_host_tab/index.tsx +++ b/x-pack/plugins/session_view/public/components/detail_panel_host_tab/index.tsx @@ -4,15 +4,15 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { EuiTextColor } from '@elastic/eui'; import { ProcessEventHost } from '../../../common/types/process_tree'; import { DetailPanelAccordion } from '../detail_panel_accordion'; import { DetailPanelCopy } from '../detail_panel_copy'; import { DetailPanelDescriptionList } from '../detail_panel_description_list'; import { DetailPanelListItem } from '../detail_panel_list_item'; -import { dataOrDash } from '../../utils/data_or_dash'; import { useStyles } from '../detail_panel_process_tab/styles'; +import { getHostData } from './helpers'; interface DetailPanelHostTabDeps { processHost?: ProcessEventHost; @@ -23,6 +23,7 @@ interface DetailPanelHostTabDeps { */ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => { const styles = useStyles(); + const hostData = useMemo(() => getHostData(processHost), [processHost]); return ( <> @@ -31,9 +32,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => { { title: hostname, description: ( - + - {dataOrDash(processHost?.hostname)} + {hostData.hostname} ), @@ -41,9 +45,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => { { title: id, description: ( - + - {dataOrDash(processHost?.id)} + {hostData.id} ), @@ -51,9 +58,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => { { title: ip, description: ( - + - {dataOrDash(processHost?.ip)} + {hostData.ip} ), @@ -61,9 +71,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => { { title: mac, description: ( - + - {dataOrDash(processHost?.mac)} + {hostData.mac} ), @@ -71,9 +84,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => { { title: name, description: ( - + - {dataOrDash(processHost?.name)} + {hostData.name} ), @@ -87,9 +103,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => { { title: architecture, description: ( - + - {dataOrDash(processHost?.architecture)} + {hostData.architecture} ), @@ -97,9 +116,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => { { title: os.family, description: ( - + - {dataOrDash(processHost?.os?.family)} + {hostData.os.family} ), @@ -107,9 +129,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => { { title: os.full, description: ( - + - {dataOrDash(processHost?.os?.full)} + {hostData.os.full} ), @@ -117,9 +142,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => { { title: os.kernel, description: ( - + - {dataOrDash(processHost?.os?.kernel)} + {hostData.os.kernel} ), @@ -127,9 +155,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => { { title: os.name, description: ( - + - {dataOrDash(processHost?.os?.name)} + {hostData.os.name} ), @@ -137,9 +168,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => { { title: os.platform, description: ( - + - {dataOrDash(processHost?.os?.platform)} + {hostData.os.platform} ), @@ -147,9 +181,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => { { title: os.version, description: ( - + - {dataOrDash(processHost?.os?.version)} + {hostData.os.version} ), diff --git a/x-pack/plugins/session_view/public/components/detail_panel_process_tab/helpers.test.ts b/x-pack/plugins/session_view/public/components/detail_panel_process_tab/helpers.test.ts index de5339fa2bbbe..a50c6e73e198d 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_process_tab/helpers.test.ts +++ b/x-pack/plugins/session_view/public/components/detail_panel_process_tab/helpers.test.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { DASH } from '../../constants'; import { getProcessExecutableCopyText, formatProcessArgs, getIsInterativeString } from './helpers'; describe('detail panel process tab helpers tests', () => { @@ -36,7 +37,7 @@ describe('detail panel process tab helpers tests', () => { it("formatProcessArgs returns '-' when given empty args array", () => { const result = formatProcessArgs([]); - expect(result).toEqual('-'); + expect(result).toEqual(DASH); }); it('formatProcessArgs returns formatted args string', () => { diff --git a/x-pack/plugins/session_view/public/components/detail_panel_process_tab/helpers.ts b/x-pack/plugins/session_view/public/components/detail_panel_process_tab/helpers.ts index 4584e7fb217dd..4417a5329a752 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_process_tab/helpers.ts +++ b/x-pack/plugins/session_view/public/components/detail_panel_process_tab/helpers.ts @@ -5,7 +5,29 @@ * 2.0. */ -import { Teletype } from '../../../common/types/process_tree'; +import { EventAction, Process, ProcessFields, Teletype } from '../../../common/types/process_tree'; +import { DetailPanelProcess, DetailPanelProcessLeader } from '../../types'; +import { DASH } from '../../constants'; +import { dataOrDash } from '../../utils/data_or_dash'; + +const FILTER_FORKS_EXECS = [EventAction.fork, EventAction.exec]; + +const DEFAULT_PROCESS_DATA: DetailPanelProcessLeader = { + id: DASH, + name: DASH, + start: DASH, + end: DASH, + exitCode: DASH, + userName: DASH, + groupName: DASH, + workingDirectory: DASH, + interactive: DASH, + args: DASH, + pid: DASH, + entryMetaType: DASH, + entryMetaSourceIp: DASH, + executable: [[DASH]], +}; /** * Serialize an array of executable tuples to a copyable text. @@ -32,11 +54,11 @@ export const getProcessExecutableCopyText = (executable: string[][]): string => /** * Format an array of args for display. * - * @param {String[]} args + * @param {String[] | undefined} args * @return {String} formatted string of process args */ -export const formatProcessArgs = (args: string[]): string => - args.length ? `[${args.map((arg) => `'${arg}'`).join(', ')}]` : '-'; +export const formatProcessArgs = (args: string[] | undefined): string => + args && args.length && args.map ? `[${args.map((arg) => `'${arg}'`).join(', ')}]` : DASH; /** * Get isInteractive boolean string from tty. @@ -46,3 +68,86 @@ export const formatProcessArgs = (args: string[]): string => */ export const getIsInterativeString = (tty: Teletype | undefined): string => !!tty ? 'True' : 'False'; + +const getDetailPanelProcessLeader = ( + leader: ProcessFields | undefined +): DetailPanelProcessLeader => ({ + ...leader, + id: leader?.entity_id ?? DEFAULT_PROCESS_DATA.id, + name: leader?.name ?? DEFAULT_PROCESS_DATA.name, + start: leader?.start ?? DEFAULT_PROCESS_DATA.start, + end: leader?.end ?? DEFAULT_PROCESS_DATA.end, + exitCode: leader?.exit_code?.toString() ?? DEFAULT_PROCESS_DATA.exitCode, + interactive: getIsInterativeString(leader?.tty), + userName: leader?.user?.name ?? DEFAULT_PROCESS_DATA.userName, + groupName: leader?.group?.name ?? DEFAULT_PROCESS_DATA.groupName, + workingDirectory: leader?.working_directory ?? DEFAULT_PROCESS_DATA.workingDirectory, + args: formatProcessArgs(leader?.args) ?? DEFAULT_PROCESS_DATA.args, + pid: leader?.pid?.toString() ?? DEFAULT_PROCESS_DATA.pid, + // TODO: get the event action of leader + executable: leader?.executable ? [[leader?.executable]] : DEFAULT_PROCESS_DATA.executable, + entryMetaType: leader?.entry_meta?.type ?? DEFAULT_PROCESS_DATA.entryMetaType, + entryMetaSourceIp: leader?.entry_meta?.source?.ip ?? DEFAULT_PROCESS_DATA.entryMetaSourceIp, +}); + +export const getDetailPanelProcess = (process: Process | null): DetailPanelProcess => { + const processData = { + id: DEFAULT_PROCESS_DATA.id, + start: DEFAULT_PROCESS_DATA.start, + end: DEFAULT_PROCESS_DATA.end, + exitCode: DEFAULT_PROCESS_DATA.exitCode, + interactive: DEFAULT_PROCESS_DATA.interactive, + userName: DEFAULT_PROCESS_DATA.userName, + groupName: DEFAULT_PROCESS_DATA.groupName, + args: DEFAULT_PROCESS_DATA.args, + pid: DEFAULT_PROCESS_DATA.pid, + executable: DEFAULT_PROCESS_DATA.executable, + workingDirectory: DEFAULT_PROCESS_DATA.workingDirectory, + entryLeader: DEFAULT_PROCESS_DATA, + sessionLeader: DEFAULT_PROCESS_DATA, + groupLeader: DEFAULT_PROCESS_DATA, + parent: DEFAULT_PROCESS_DATA, + } as DetailPanelProcess; + if (!process) { + return processData; + } + + const details = process.getDetails(); + + processData.id = `${dataOrDash(process.id)}`; + processData.start = `${dataOrDash(details.process?.start)}`; + processData.end = `${dataOrDash(process.getEndTime())}`; + processData.exitCode = `${dataOrDash(details.process?.exit_code)}`; + processData.interactive = getIsInterativeString(details.process?.tty); + processData.userName = `${dataOrDash(details.process?.user?.name)}`; + processData.groupName = `${dataOrDash(details.process?.group?.name)}`; + processData.pid = `${dataOrDash(details.process?.pid)}`; + processData.workingDirectory = `${dataOrDash(details.process?.working_directory)}`; + if (details.process?.args) { + processData.args = formatProcessArgs(details.process.args); + } + + // we grab the executable from each process lifecycle event to give an indication + // of the processes journey. Processes can sometimes exec multiple times, so it's good + // information to have. + processData.executable = []; + process.events.forEach((event) => { + if ( + event.process?.executable && + event.event?.action && + FILTER_FORKS_EXECS.includes(event.event.action) + ) { + processData.executable.push([event.process.executable, `(${event.event.action})`]); + } + }); + if (!processData.executable.length) { + processData.executable = DEFAULT_PROCESS_DATA.executable; + } + + processData.entryLeader = getDetailPanelProcessLeader(details?.process?.entry_leader); + processData.sessionLeader = getDetailPanelProcessLeader(details?.process?.session_leader); + processData.groupLeader = getDetailPanelProcessLeader(details?.process?.group_leader); + processData.parent = getDetailPanelProcessLeader(details?.process?.parent); + + return processData; +}; diff --git a/x-pack/plugins/session_view/public/components/detail_panel_process_tab/index.test.tsx b/x-pack/plugins/session_view/public/components/detail_panel_process_tab/index.test.tsx index 7daaf997e2af8..488d83ca9880e 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_process_tab/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/detail_panel_process_tab/index.test.tsx @@ -7,60 +7,15 @@ import React from 'react'; import { AppContextTestRender, createAppRootMockRenderer } from '../../test'; -import { DetailPanelProcess, DetailPanelProcessLeader } from '../../types'; +import { sessionViewBasicProcessMock } from '../../../common/mocks/constants/session_view_process.mock'; import { DetailPanelProcessTab } from '.'; -const getLeaderDetail = (leader: string): DetailPanelProcessLeader => ({ - id: `${leader}-id`, - name: `${leader}-name`, - start: new Date('2022-02-24').toISOString(), - entryMetaType: 'sshd', - working_directory: '/home/jack', - tty: { - char_device: { - major: 8, - minor: 1, - }, - }, - args: ['ls'], - userName: `${leader}-jack`, - groupName: `${leader}-jack-group`, - pid: 1234, - entryMetaSourceIp: '10.132.0.50', - executable: '/usr/bin/bash', -}); - -const TEST_PROCESS_DETAIL: DetailPanelProcess = { - id: 'process-id', - start: new Date('2022-02-22').toISOString(), - end: new Date('2022-02-23').toISOString(), - exit_code: 137, - userName: 'process-jack', - groupName: 'process-jack-group', - args: ['vi', 'test.txt'], - executable: [ - ['test-executable-cmd', '(fork)'], - ['test-executable-cmd', '(exec)'], - ['test-executable-cmd', '(end)'], - ], - working_directory: '/home/jack', - tty: { - char_device: { - major: 8, - minor: 1, - }, - }, - pid: 1233, - entryLeader: getLeaderDetail('entryLeader'), - sessionLeader: getLeaderDetail('sessionLeader'), - groupLeader: getLeaderDetail('groupLeader'), - parent: getLeaderDetail('parent'), -}; - describe('DetailPanelProcessTab component', () => { let render: () => ReturnType; let renderResult: ReturnType; let mockedContext: AppContextTestRender; + const processDetail = sessionViewBasicProcessMock.getDetails(); + const MOCK_PROCESS_END = '2021-11-24T15:25:04.210Z'; beforeEach(() => { mockedContext = createAppRootMockRenderer(); @@ -69,21 +24,29 @@ describe('DetailPanelProcessTab component', () => { describe('When DetailPanelProcessTab is mounted', () => { it('renders DetailPanelProcessTab correctly', async () => { renderResult = mockedContext.render( - + MOCK_PROCESS_END, + }} + /> ); // Process detail rendered correctly - expect(renderResult.queryByText(TEST_PROCESS_DETAIL.id)).toBeVisible(); - expect(renderResult.queryByText(TEST_PROCESS_DETAIL.start)).toBeVisible(); - expect(renderResult.queryByText(TEST_PROCESS_DETAIL.end)).toBeVisible(); - expect(renderResult.queryByText(TEST_PROCESS_DETAIL.exit_code!)).toBeVisible(); - expect(renderResult.queryByText(TEST_PROCESS_DETAIL.userName)).toBeVisible(); - expect(renderResult.queryByText(`['vi', 'test.txt']`)).toBeVisible(); - expect(renderResult.queryAllByText('test-executable-cmd')).toHaveLength(3); + expect(renderResult.queryByText(processDetail!.process!.entity_id!)).toBeVisible(); + expect(renderResult.queryByText(processDetail!.process!.start!)).toBeVisible(); + expect(renderResult.queryByText(MOCK_PROCESS_END)).toBeVisible(); + expect(renderResult.queryByText(processDetail!.process!.exit_code!)).toBeVisible(); + expect(renderResult.queryAllByText(processDetail!.process!.user!.name!)).toHaveLength(10); + expect(renderResult.queryAllByText(processDetail!.process!.working_directory!)).toHaveLength( + 5 + ); + expect(renderResult.queryByText(`['bash']`)).toBeVisible(); + expect(renderResult.queryAllByText('/usr/bin/bash')).toHaveLength(5); + expect(renderResult.queryByText('/usr/bin/vi')).toBeVisible(); expect(renderResult.queryByText('(fork)')).toBeVisible(); expect(renderResult.queryByText('(exec)')).toBeVisible(); - expect(renderResult.queryByText('(end)')).toBeVisible(); - expect(renderResult.queryByText(TEST_PROCESS_DETAIL.pid!)).toBeVisible(); + expect(renderResult.queryByText(processDetail!.process!.pid!)).toBeVisible(); // Process tab accordions rendered correctly // TODO: revert back when we have jump to leaders button working diff --git a/x-pack/plugins/session_view/public/components/detail_panel_process_tab/index.tsx b/x-pack/plugins/session_view/public/components/detail_panel_process_tab/index.tsx index a310784d74733..f62c6188203a2 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_process_tab/index.tsx +++ b/x-pack/plugins/session_view/public/components/detail_panel_process_tab/index.tsx @@ -4,20 +4,20 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { ReactNode } from 'react'; +import React, { ReactNode, useCallback, useMemo } from 'react'; import { EuiTextColor } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { DetailPanelProcess } from '../../types'; +import { Process } from '../../../common/types/process_tree'; import { DetailPanelAccordion } from '../detail_panel_accordion'; import { DetailPanelCopy } from '../detail_panel_copy'; import { DetailPanelDescriptionList } from '../detail_panel_description_list'; import { DetailPanelListItem } from '../detail_panel_list_item'; import { dataOrDash } from '../../utils/data_or_dash'; -import { getProcessExecutableCopyText, formatProcessArgs, getIsInterativeString } from './helpers'; +import { getProcessExecutableCopyText, getDetailPanelProcess } from './helpers'; import { useStyles } from './styles'; interface DetailPanelProcessTabDeps { - processDetail: DetailPanelProcess; + selectedProcess: Process | null; } type ListItems = Array<{ @@ -59,11 +59,41 @@ const leaderDescriptionListInfo = [ }, ]; +const PROCESS_FIELD_PREFIX = 'process'; +const LEADER_FIELD_PREFIX = [ + `${PROCESS_FIELD_PREFIX}.entry_leader`, + `${PROCESS_FIELD_PREFIX}.session_leader`, + `${PROCESS_FIELD_PREFIX}.group_leader`, + `${PROCESS_FIELD_PREFIX}.parent`, +]; + /** * Detail panel in the session view. */ -export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDeps) => { +export const DetailPanelProcessTab = ({ selectedProcess }: DetailPanelProcessTabDeps) => { const styles = useStyles(); + + const processDetail = useMemo(() => getDetailPanelProcess(selectedProcess), [selectedProcess]); + const renderExecs = useCallback( + (executable: string[][]) => + executable.map((execTuple, idx) => { + const [exec, eventAction] = execTuple; + return ( +
+ + {exec} + + {eventAction && ( + + {eventAction} + + )} +
+ ); + }), + [styles.descriptionSemibold, styles.ellipsis, styles.executableAction] + ); + const leaderListItems = [ processDetail.entryLeader, processDetail.sessionLeader, @@ -74,39 +104,64 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe id, start, end, - exit_code: exitCode, + exitCode, entryMetaType, - tty, - working_directory: workingDirectory, + interactive, + workingDirectory, args, + executable, pid, userName, groupName, entryMetaSourceIp, } = leader; - const leaderArgs = formatProcessArgs(args); - const isLeaderInteractive = getIsInterativeString(tty); + + const leaderExecutableText = getProcessExecutableCopyText(executable); const listItems: ListItems = [ { title: entity_id, description: ( - + - {dataOrDash(id)} + {id} ), }, { title: args, - description: {leaderArgs}, + description: ( + + {args} + + ), + }, + { + title: executable, + description: ( + + {renderExecs(executable)} + + ), }, { title: interactive, description: ( - + - {isLeaderInteractive} + {interactive} ), @@ -114,9 +169,12 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe { title: working_directory, description: ( - + - {dataOrDash(workingDirectory)} + {workingDirectory} ), @@ -124,27 +182,47 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe { title: pid, description: ( - + - {dataOrDash(pid)} + {pid} ), }, { title: start, - description: {dataOrDash(start)}, + description: ( + + {start} + + ), }, { title: end, - description: {dataOrDash(end)}, + description: ( + + {end} + + ), }, { title: exit_code, description: ( - + - {dataOrDash(exitCode)} + {exitCode} ), @@ -152,13 +230,23 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe { title: user.name, description: ( - {dataOrDash(userName)} + + {userName} + ), }, { title: group.name, description: ( - {dataOrDash(groupName)} + + {groupName} + ), }, ]; @@ -168,9 +256,12 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe { title: entry_meta.type, description: ( - + - {dataOrDash(entryMetaType)} + {entryMetaType} ), @@ -178,7 +269,10 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe { title: entry_meta.source.ip, description: ( - + {dataOrDash(entryMetaSourceIp)} ), @@ -198,17 +292,15 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe start, end, executable, - exit_code: exitCode, + exitCode, pid, - working_directory: workingDirectory, - tty, + workingDirectory, + interactive, userName, groupName, args, } = processDetail; - - const isInteractive = getIsInterativeString(tty); - const processArgs = formatProcessArgs(args); + const executableText = getProcessExecutableCopyText(executable); return ( <> @@ -217,46 +309,48 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe { title: entity_id, description: ( - + - {dataOrDash(id)} + {id} ), }, { title: args, - description: {processArgs}, + description: ( + + {args} + + ), }, { title: executable, description: ( - {executable.map((execTuple, idx) => { - const [exec, eventAction] = execTuple; - return ( -
- - {dataOrDash(exec)} - - - {eventAction} - -
- ); - })} + {renderExecs(executable)}
), }, { title: interactive, description: ( - + - {isInteractive} + {interactive} ), @@ -264,9 +358,12 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe { title: working_directory, description: ( - + - {dataOrDash(workingDirectory)} + {workingDirectory} ), @@ -274,27 +371,47 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe { title: pid, description: ( - + - {dataOrDash(pid)} + {pid} ), }, { title: start, - description: {dataOrDash(start)}, + description: ( + + {start} + + ), }, { title: end, - description: {dataOrDash(end)}, + description: ( + + {end} + + ), }, { title: exit_code, description: ( - + - {dataOrDash(exitCode)} + {exitCode} ), @@ -302,13 +419,23 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe { title: user.name, description: ( - {dataOrDash(userName)} + + {userName} + ), }, { title: group.name, description: ( - {dataOrDash(groupName)} + + {groupName} + ), }, ]} diff --git a/x-pack/plugins/session_view/public/components/process_tree/index.tsx b/x-pack/plugins/session_view/public/components/process_tree/index.tsx index f2b5fef85002c..8a06ec769b7a9 100644 --- a/x-pack/plugins/session_view/public/components/process_tree/index.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree/index.tsx @@ -160,7 +160,7 @@ export const ProcessTree = ({ <>
{sessionLeader && ( diff --git a/x-pack/plugins/session_view/public/components/process_tree/styles.ts b/x-pack/plugins/session_view/public/components/process_tree/styles.ts index 490829cad440b..04f8a22890c28 100644 --- a/x-pack/plugins/session_view/public/components/process_tree/styles.ts +++ b/x-pack/plugins/session_view/public/components/process_tree/styles.ts @@ -6,23 +6,23 @@ */ import { useMemo } from 'react'; -import { transparentize, useEuiTheme } from '@elastic/eui'; +import { transparentize } from '@elastic/eui'; import { CSSObject } from '@emotion/react'; -import { euiLightVars } from '@kbn/ui-theme'; // using this temporarily until the euiTheme hook is updated to include proper hex values +import { useEuiTheme } from '../../hooks'; export const useStyles = () => { - const { euiTheme } = useEuiTheme(); + const { euiTheme, euiVars } = useEuiTheme(); const cached = useMemo(() => { const { colors, font, size } = euiTheme; const defaultSelectionColor = colors.primary; - const scroller: CSSObject = { + const sessionViewProcessTree: CSSObject = { position: 'relative', fontFamily: font.familyCode, overflow: 'auto', height: '100%', - backgroundColor: euiLightVars.euiColorLightestShade, + backgroundColor: euiVars.euiColorLightestShade, paddingTop: size.base, paddingLeft: size.s, }; @@ -43,12 +43,12 @@ export const useStyles = () => { const alertSelected = transparentize(colors.danger, 0.008); return { - scroller, + sessionViewProcessTree, selectionArea, defaultSelected, alertSelected, }; - }, [euiTheme]); + }, [euiTheme, euiVars]); return cached; }; diff --git a/x-pack/plugins/session_view/public/components/process_tree_alert/__snapshots__/index.test.tsx.snap b/x-pack/plugins/session_view/public/components/process_tree_alert/__snapshots__/index.test.tsx.snap index e9f66ea10d66e..b26778c8f8eea 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_alert/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/session_view/public/components/process_tree_alert/__snapshots__/index.test.tsx.snap @@ -6,13 +6,13 @@ Object { "baseElement":
cmd test alert
@@ -53,13 +52,13 @@ Object { , "container":
cmd test alert
diff --git a/x-pack/plugins/session_view/public/components/process_tree_alert/index.tsx b/x-pack/plugins/session_view/public/components/process_tree_alert/index.tsx index 95618123d709f..f01f08f8e3095 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_alert/index.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_alert/index.tsx @@ -69,12 +69,11 @@ export const ProcessTreeAlert = ({ - - + + {dataOrDash(name)} diff --git a/x-pack/plugins/session_view/public/components/process_tree_alert/styles.ts b/x-pack/plugins/session_view/public/components/process_tree_alert/styles.ts index bcd47edf56db8..e8d3b0374c978 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_alert/styles.ts +++ b/x-pack/plugins/session_view/public/components/process_tree_alert/styles.ts @@ -6,8 +6,9 @@ */ import { useMemo } from 'react'; -import { useEuiTheme, transparentize } from '@elastic/eui'; +import { transparentize } from '@elastic/eui'; import { CSSObject } from '@emotion/react'; +import { useEuiTheme } from '../../hooks'; interface StylesDeps { isInvestigated: boolean; @@ -15,7 +16,7 @@ interface StylesDeps { } export const useStyles = ({ isInvestigated, isSelected }: StylesDeps) => { - const { euiTheme } = useEuiTheme(); + const { euiTheme, euiVars } = useEuiTheme(); const cached = useMemo(() => { const { size, colors, font } = euiTheme; @@ -43,9 +44,9 @@ export const useStyles = ({ isInvestigated, isSelected }: StylesDeps) => { const alert: CSSObject = { fontFamily: font.family, display: 'flex', + gap: size.s, alignItems: 'center', - minHeight: '20px', - padding: `${size.xs} ${size.base}`, + padding: `0 ${size.base}`, boxSizing: 'content-box', cursor: 'pointer', '&:not(:last-child)': { @@ -55,37 +56,30 @@ export const useStyles = ({ isInvestigated, isSelected }: StylesDeps) => { '&:hover': { background: hoverBgColor, }, - }; - - const alertRowItem: CSSObject = { - '&:first-of-type': { - marginRight: size.m, - }, - '&:not(:first-of-type)': { + '&& button': { + flexShrink: 0, marginRight: size.s, + '&:hover, &:focus, &:focus-within': { + backgroundColor: transparentize(euiVars.buttonsBackgroundNormalDefaultPrimary, 0.2), + }, }, }; - const alertRuleName: CSSObject = { - ...alertRowItem, - maxWidth: '70%', - }; - const alertStatus: CSSObject = { - ...alertRowItem, textTransform: 'capitalize', - '&, span': { - cursor: 'pointer !important', - }, + }; + + const alertName: CSSObject = { + padding: `${size.xs} 0`, + color: colors.title, }; return { alert, - alertRowItem, - alertRuleName, alertStatus, + alertName, }; - }, [euiTheme, isInvestigated, isSelected]); + }, [euiTheme, isInvestigated, isSelected, euiVars]); return cached; }; diff --git a/x-pack/plugins/session_view/public/components/process_tree_alerts/styles.ts b/x-pack/plugins/session_view/public/components/process_tree_alerts/styles.ts index bd95d87258178..a82a2253f4b2f 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_alerts/styles.ts +++ b/x-pack/plugins/session_view/public/components/process_tree_alerts/styles.ts @@ -16,7 +16,7 @@ export const useStyles = () => { const { size, colors, border } = euiTheme; const container: CSSObject = { - margin: `${size.xs} ${size.base} 0 ${size.xs}`, + margin: `${size.xs} ${size.base} ${size.base} ${size.xs}`, color: colors.text, padding: `${size.s} 0`, borderStyle: 'solid', diff --git a/x-pack/plugins/session_view/public/components/process_tree_load_more_button/index.tsx b/x-pack/plugins/session_view/public/components/process_tree_load_more_button/index.tsx index fd5bc3fa13e86..942d1606564b7 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_load_more_button/index.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_load_more_button/index.tsx @@ -36,7 +36,7 @@ export const ProcessTreeLoadMoreButton = ({ isLoading={isFetching} > {text} - {eventsRemaining !== 0 && ( + {eventsRemaining > 0 && ( void; isExpanded: boolean; }) => { - const { button, buttonArrow, expandedIcon } = useButtonStyles({ isExpanded }); + const { button, buttonArrow } = useButtonStyles(); return ( - + ); }; @@ -40,12 +43,11 @@ export const AlertButton = ({ onToggle: () => void; alertsCount: number; }) => { - const { alertButton, alertsCountNumber, buttonArrow, expandedIcon } = useButtonStyles({ - isExpanded, - }); + const { alertButton, buttonArrow } = useButtonStyles(); return ( )} - {alertsCount > 1 && ( - ({alertsCount > 99 ? '99+' : alertsCount}) - )} - + {alertsCount > 1 && + (alertsCount > MAX_ALERT_COUNT ? ` (${MAX_ALERT_COUNT}+)` : ` (${alertsCount})`)} + ); }; diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/index.test.tsx b/x-pack/plugins/session_view/public/components/process_tree_node/index.test.tsx index a5e0305595187..1eba726607c10 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_node/index.test.tsx @@ -57,7 +57,9 @@ describe('ProcessTreeNode component', () => { it('should have an alternate rendering for a session leader', async () => { renderResult = mockedContext.render(); - expect(renderResult.container.textContent).toEqual(' bash started by vagrant'); + expect(renderResult.container.textContent?.replace(/\s+/g, ' ')).toEqual( + ' bash started by vagrant' + ); }); // commented out until we get new UX for orphans treatment aka disjointed tree @@ -216,7 +218,7 @@ describe('ProcessTreeNode component', () => { expect(renderResult.queryByTestId('processTreeNodeAlertButton')).toBeTruthy(); expect(renderResult.queryByTestId('processTreeNodeAlertButton')?.textContent).toBe( - `Alerts(${sessionViewAlertProcessMock.getAlerts().length})` + `Alerts (${sessionViewAlertProcessMock.getAlerts().length})` ); }); it('renders Alerts button with 99+ when process has more than 99 alerts', async () => { @@ -234,7 +236,7 @@ describe('ProcessTreeNode component', () => { expect(renderResult.queryByTestId('processTreeNodeAlertButton')).toBeTruthy(); expect(renderResult.queryByTestId('processTreeNodeAlertButton')?.textContent).toBe( - 'Alerts(99+)' + 'Alerts (99+)' ); }); it('toggle Alert Details button when Alert button is clicked', async () => { diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx b/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx index d1b0fded615f6..db81734c65937 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx @@ -33,6 +33,8 @@ import { AlertButton, ChildrenProcessesButton } from './buttons'; import { useButtonStyles } from './use_button_styles'; import { KIBANA_DATE_FORMAT } from '../../../common/constants'; import { useStyles } from './styles'; +import { SplitText } from './split_text'; +import { Nbsp } from './nbsp'; export interface ProcessDeps { process: Process; @@ -104,8 +106,8 @@ export function ProcessTreeNode({ [hasAlerts, alerts, investigatedAlertId] ); const isSelected = selectedProcess?.id === process.id; - const styles = useStyles({ depth, hasAlerts, hasInvestigatedAlert, isSelected }); - const buttonStyles = useButtonStyles({}); + const styles = useStyles({ depth, hasAlerts, hasInvestigatedAlert, isSelected, isSessionLeader }); + const buttonStyles = useButtonStyles(); const nodeRef = useVisible({ viewPortEl: scrollerRef.current, @@ -255,28 +257,37 @@ export function ProcessTreeNode({ onClick={onProcessClicked} > {isSessionLeader ? ( - <> - {' '} - {dataOrDash(name || args?.[0])}{' '} - {' '} - {dataOrDash(user?.name)} - + + + + {dataOrDash(name || args?.[0])} + + + + + + + + {dataOrDash(user?.name)} + ) : ( - + <> {showTimestamp && ( {timeStampsNormal} )} - - {' '} - - {dataOrDash(workingDirectory)}  - {dataOrDash(args?.[0])}{' '} - {args?.slice(1).join(' ')} + + + + {dataOrDash(workingDirectory)} + + {dataOrDash(args?.[0])} + + {args?.slice(1).join(' ') || ''} - + )} {showUserEscalation && ( @@ -308,7 +319,7 @@ export function ProcessTreeNode({ diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/nbsp.tsx b/x-pack/plugins/session_view/public/components/process_tree_node/nbsp.tsx new file mode 100644 index 0000000000000..65274cd0b6ade --- /dev/null +++ b/x-pack/plugins/session_view/public/components/process_tree_node/nbsp.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { CSSObject } from '@emotion/react'; + +const css: CSSObject = { + width: '6px', + display: 'inline-block', +}; + +// Renders a non-breaking space with a specific width. +export const Nbsp = () => { + return  ; +}; diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/split_text.test.tsx b/x-pack/plugins/session_view/public/components/process_tree_node/split_text.test.tsx new file mode 100644 index 0000000000000..eaa18b18d81cf --- /dev/null +++ b/x-pack/plugins/session_view/public/components/process_tree_node/split_text.test.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 { render } from '@testing-library/react'; +import { SplitText } from './split_text'; + +describe('SplitText component', () => { + it('should split a text into one span for each character', async () => { + const text = 'hello world'; + + const renderResult = render({text}); + for (const char of text.replace(/\s+/g, '').split('')) { + expect(await renderResult.findAllByText(char)).toBeTruthy(); + } + expect(renderResult.container.textContent?.replace(/\s+/g, ' ')).toEqual(text); + }); + it('should provide an acessible label for screen readers', async () => { + const text = 'hello world'; + + const renderResult = render({text}); + expect(renderResult.queryByRole('document', { name: text })).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/split_text.tsx b/x-pack/plugins/session_view/public/components/process_tree_node/split_text.tsx new file mode 100644 index 0000000000000..d31590c9ab967 --- /dev/null +++ b/x-pack/plugins/session_view/public/components/process_tree_node/split_text.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { CSSObject } from '@emotion/react'; + +type Props = { + children: string; + role?: string; +}; + +const css: CSSObject = { + '&&': { + display: 'inline', + fontSize: 0, + lineHeight: 0, + }, +}; + +// Split a text into multiple spans, each of which a single character. This is +// useful for creating inline "like" text but still having control over the blocks +// exclusive features, such height or line-height. +// It adds a `aria-label` attribute to a parent span, which is used by screen readers to +// read the text as a single block. +export const SplitText = ({ children, role = 'document', ...props }: Props) => ( + + {children.split('').map(function (char, index) { + return ( + + ); + })} + +); diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/styles.ts b/x-pack/plugins/session_view/public/components/process_tree_node/styles.ts index 6503f373240ad..b68df480064b3 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/styles.ts +++ b/x-pack/plugins/session_view/public/components/process_tree_node/styles.ts @@ -6,30 +6,39 @@ */ import { useMemo } from 'react'; -import { useEuiTheme, transparentize } from '@elastic/eui'; +import { transparentize } from '@elastic/eui'; import { CSSObject } from '@emotion/react'; +import { useEuiTheme } from '../../hooks'; interface StylesDeps { depth: number; hasAlerts: boolean; hasInvestigatedAlert: boolean; isSelected: boolean; + isSessionLeader: boolean; } -export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert, isSelected }: StylesDeps) => { - const { euiTheme } = useEuiTheme(); +export const useStyles = ({ + depth, + hasAlerts, + hasInvestigatedAlert, + isSelected, + isSessionLeader, +}: StylesDeps) => { + const { euiTheme, euiVars } = useEuiTheme(); const cached = useMemo(() => { const { colors, border, size, font } = euiTheme; + const ALERT_INDICATOR_WIDTH = '3px'; + const LINE_HEIGHT = '21px'; + const FONT_SIZE = '13px'; const TREE_INDENT = `calc(${size.l} + ${size.xxs})`; const PROCESS_TREE_LEFT_PADDING = size.s; const darkText: CSSObject = { color: colors.text, fontFamily: font.familyCode, - paddingLeft: size.xxs, - paddingRight: size.xs, }; const children: CSSObject = { @@ -40,12 +49,16 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert, isSelected } borderLeft: border.editable, }; + const icon: CSSObject = { + color: euiVars.euiColorDarkShade, + }; + /** * gets border, bg and hover colors for a process */ const getHighlightColors = () => { let bgColor = 'none'; - const hoverColor = transparentize(colors.primary, 0.04); + let hoverColor = transparentize(colors.primary, 0.04); let borderColor = 'transparent'; let searchResColor = transparentize(colors.warning, 0.32); @@ -55,11 +68,16 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert, isSelected } if (isSelected) { searchResColor = colors.warning; - bgColor = `${transparentize(colors.primary, 0.1)}!important`; + bgColor = transparentize(colors.primary, 0.08); + hoverColor = transparentize(colors.primary, 0.12); } if (hasInvestigatedAlert) { - bgColor = `${transparentize(colors.danger, 0.04)}!important`; + bgColor = transparentize(colors.danger, 0.04); + hoverColor = transparentize(colors.danger, 0.12); + if (isSelected) { + bgColor = transparentize(colors.danger, 0.08); + } } return { bgColor, borderColor, hoverColor, searchResColor }; @@ -67,14 +85,19 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert, isSelected } const { bgColor, borderColor, hoverColor, searchResColor } = getHighlightColors(); + const fontSpacingReset: CSSObject = { + fontSize: 0, + lineHeight: 0, + }; + const processNode: CSSObject = { + ...fontSpacingReset, display: 'block', cursor: 'pointer', position: 'relative', - padding: `${size.xs} 0px`, + marginBottom: isSessionLeader ? size.s : '0px', '&:hover:before': { backgroundColor: hoverColor, - transform: `translateY(-${size.xs})`, }, '&:before': { position: 'absolute', @@ -82,10 +105,29 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert, isSelected } pointerEvents: 'none', content: `''`, marginLeft: `calc(-${depth} * ${TREE_INDENT} - ${PROCESS_TREE_LEFT_PADDING})`, - borderLeft: `${size.xs} solid ${borderColor}`, + borderLeft: `${ALERT_INDICATOR_WIDTH} solid ${borderColor}`, backgroundColor: bgColor, width: `calc(100% + ${depth} * ${TREE_INDENT} + ${PROCESS_TREE_LEFT_PADDING})`, - transform: `translateY(-${size.xs})`, + }, + }; + + const textSection: CSSObject = { + marginLeft: size.s, + span: { + fontSize: FONT_SIZE, + lineHeight: LINE_HEIGHT, + verticalAlign: 'middle', + display: 'inline-block', + }, + }; + + const sessionLeader: CSSObject = { + ...fontSpacingReset, + 'span, b': { + fontSize: FONT_SIZE, + lineHeight: LINE_HEIGHT, + display: 'inline-block', + verticalAlign: 'middle', }, }; @@ -99,18 +141,19 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert, isSelected } paddingLeft: size.s, position: 'relative', verticalAlign: 'middle', - color: colors.mediumShade, + color: euiVars.euiTextSubduedColor, wordBreak: 'break-all', - minHeight: `calc(${size.l} - ${size.xxs})`, - lineHeight: `calc(${size.l} - ${size.xxs})`, + padding: `${size.xs} 0px`, + button: { + marginLeft: '6px', + marginRight: size.xxs, + }, }; const workingDir: CSSObject = { color: colors.successText, fontFamily: font.familyCode, - fontWeight: font.weight.medium, - paddingLeft: size.s, - paddingRight: size.xxs, + fontWeight: font.weight.regular, }; const timeStamp: CSSObject = { @@ -121,6 +164,7 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert, isSelected } paddingRight: size.base, paddingLeft: size.xxl, position: 'relative', + lineHeight: LINE_HEIGHT, }; const alertDetails: CSSObject = { @@ -138,8 +182,11 @@ export const useStyles = ({ depth, hasAlerts, hasInvestigatedAlert, isSelected } workingDir, timeStamp, alertDetails, + icon, + textSection, + sessionLeader, }; - }, [depth, euiTheme, hasAlerts, hasInvestigatedAlert, isSelected]); + }, [depth, euiTheme, hasAlerts, hasInvestigatedAlert, isSelected, euiVars, isSessionLeader]); return cached; }; diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/use_button_styles.ts b/x-pack/plugins/session_view/public/components/process_tree_node/use_button_styles.ts index 67883b12e2bba..8cd4e2c5004b4 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/use_button_styles.ts +++ b/x-pack/plugins/session_view/public/components/process_tree_node/use_button_styles.ts @@ -6,36 +6,49 @@ */ import { useMemo } from 'react'; -import { useEuiTheme, transparentize, shade } from '@elastic/eui'; -import { euiLightVars as theme } from '@kbn/ui-theme'; +import { transparentize } from '@elastic/eui'; import { CSSObject } from '@emotion/react'; +import { useEuiTheme } from '../../hooks'; -interface ButtonStylesDeps { - isExpanded?: boolean; -} - -export const useButtonStyles = ({ isExpanded }: ButtonStylesDeps) => { - const { euiTheme } = useEuiTheme(); +export const useButtonStyles = () => { + const { euiTheme, euiVars } = useEuiTheme(); const cached = useMemo(() => { - const { colors, border, size, font } = euiTheme; + const { border, colors, size, font } = euiTheme; const button: CSSObject = { - background: transparentize(theme.euiColorVis6, 0.04), - border: `${border.width.thin} solid ${transparentize(theme.euiColorVis6, 0.48)}`, lineHeight: '18px', height: '20px', fontSize: size.m, fontFamily: font.family, fontWeight: font.weight.medium, borderRadius: border.radius.small, - color: shade(theme.euiColorVis6, 0.25), marginLeft: size.xs, marginRight: size.xs, minWidth: 0, padding: `${size.s} ${size.xxs}`, - span: { - padding: `0px ${size.xxs} !important`, + color: euiVars.euiColorVis6_asText, + background: transparentize(euiVars.euiColorVis6, 0.04), + border: `${border.width.thin} solid ${transparentize(euiVars.euiColorVis6, 0.48)}`, + '&& > span': { + padding: `0px ${size.xxs}`, + svg: { + transition: `transform ${euiTheme.animation.extraFast}`, + }, + }, + '&&:hover, &&:focus': { + background: transparentize(euiVars.euiColorVis6, 0.12), + textDecoration: 'none', + }, + '&.isExpanded > span svg': { + transform: `rotate(180deg)`, + }, + '&.isExpanded': { + color: colors.ghost, + background: euiVars.euiColorVis6, + '&:hover, &:focus': { + background: euiVars.euiColorVis6, + }, }, }; @@ -45,52 +58,49 @@ export const useButtonStyles = ({ isExpanded }: ButtonStylesDeps) => { const alertButton: CSSObject = { ...button, - color: colors.dangerText, - background: transparentize(colors.dangerText, 0.04), - border: `${border.width.thin} solid ${transparentize(colors.dangerText, 0.48)}`, - }; - - const alertsCountNumber: CSSObject = { - paddingLeft: size.xs, + color: euiVars.euiColorDanger, + background: transparentize(euiVars.euiColorDanger, 0.04), + border: `${border.width.thin} solid ${transparentize(euiVars.euiColorDanger, 0.48)}`, + '&&:hover, &&:focus': { + background: transparentize(euiVars.euiColorDanger, 0.12), + textDecoration: 'none', + }, + '&.isExpanded': { + color: colors.ghost, + background: euiVars.euiColorDanger, + '&:hover, &:focus': { + background: `${euiVars.euiColorDanger}`, + }, + }, }; - if (isExpanded) { - button.color = colors.ghost; - button.background = theme.euiColorVis6; - button['&:hover, &:focus'] = { - backgroundColor: `${theme.euiColorVis6} !important`, - }; - - alertButton.color = colors.ghost; - alertButton.background = colors.dangerText; - alertButton['&:hover, &:focus'] = { - backgroundColor: `${colors.dangerText} !important`, - }; - } - const userChangedButton: CSSObject = { ...button, - color: theme.euiColorVis3, - background: transparentize(theme.euiColorVis3, 0.04), - border: `${border.width.thin} solid ${transparentize(theme.euiColorVis3, 0.48)}`, + cursor: 'default', + color: euiVars.euiColorGhost, + background: euiVars.euiColorVis3, + border: `${border.width.thin} solid ${transparentize(euiVars.euiColorVis3, 0.48)}`, + '&&:hover, &&:focus': { + color: euiVars.euiColorGhost, + background: euiVars.euiColorVis3, + textDecoration: 'none', + transform: 'none', + animation: 'none', + }, }; const buttonSize: CSSObject = { padding: `0px ${euiTheme.size.xs}`, }; - const expandedIcon = isExpanded ? 'arrowUp' : 'arrowDown'; - return { buttonArrow, button, alertButton, - alertsCountNumber, userChangedButton, buttonSize, - expandedIcon, }; - }, [euiTheme, isExpanded]); + }, [euiTheme, euiVars]); return cached; }; diff --git a/x-pack/plugins/session_view/public/components/session_view/index.tsx b/x-pack/plugins/session_view/public/components/session_view/index.tsx index 7d11096ff18bd..5fe3e2365cc58 100644 --- a/x-pack/plugins/session_view/public/components/session_view/index.tsx +++ b/x-pack/plugins/session_view/public/components/session_view/index.tsx @@ -200,15 +200,12 @@ export const SessionView = ({ return ( <>
- + - + { - const { euiTheme } = useEuiTheme(); + const { euiTheme, euiVars } = useEuiTheme(); const cached = useMemo(() => { const { border, colors, size } = euiTheme; @@ -45,21 +44,14 @@ export const useStyles = ({ height = 500, isFullScreen }: StylesDeps) => { flexGrow: 0, alignItems: 'stretch', }; - const searchBar: CSSObject = { - position: 'relative', - input: { - backgroundColor: colors.emptyShade, - }, - }; const sessionViewerComponent: CSSObject = { border: border.thin, borderRadius: border.radius.medium, - }; - - const toolBar: CSSObject = { - backgroundColor: `${euiLightVars.euiFormBackgroundDisabledColor} !important`, // important used since euipanel overrides this - padding: `${size.m} ${size.base}`, + '.sessionViewerToolbar': { + backgroundColor: `${euiVars.euiFormBackgroundDisabledColor}`, + padding: `${size.m} ${size.base}`, + }, }; const betaBadge: CSSObject = { @@ -71,12 +63,10 @@ export const useStyles = ({ height = 500, isFullScreen }: StylesDeps) => { detailPanel, nonGrowGroup, resizeHandle, - searchBar, sessionViewerComponent, - toolBar, betaBadge, }; - }, [euiTheme, isFullScreen, height]); + }, [euiTheme, isFullScreen, height, euiVars]); return cached; }; diff --git a/x-pack/plugins/session_view/public/components/session_view_detail_panel/helpers.test.ts b/x-pack/plugins/session_view/public/components/session_view_detail_panel/helpers.test.ts new file mode 100644 index 0000000000000..70b0fe7ec272f --- /dev/null +++ b/x-pack/plugins/session_view/public/components/session_view_detail_panel/helpers.test.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 { getSelectedTabContent } from './helpers'; +import { EuiTabProps } from '../../types'; + +const TABS: EuiTabProps[] = [ + { + id: '1', + name: 'Process', + content: 'process content', + }, + { + id: '2', + name: 'Host', + content: 'host content', + }, + { + id: '3', + name: 'Alert', + content: 'alert content', + }, +]; + +describe('session view detail panel helpers tests', () => { + it('getSelectedTabContent works', () => { + const result = getSelectedTabContent(TABS, '1'); + expect(result).toBe(TABS[0].content); + }); + + it('getSelectedTabContent returns null if tab id not found', () => { + const result = getSelectedTabContent(TABS, 'process'); + expect(result).toBeNull(); + }); +}); diff --git a/x-pack/plugins/session_view/public/components/session_view_detail_panel/helpers.ts b/x-pack/plugins/session_view/public/components/session_view_detail_panel/helpers.ts index e4e6cb0134bbc..755c24f83361a 100644 --- a/x-pack/plugins/session_view/public/components/session_view_detail_panel/helpers.ts +++ b/x-pack/plugins/session_view/public/components/session_view_detail_panel/helpers.ts @@ -4,108 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EventAction, Process, ProcessFields } from '../../../common/types/process_tree'; -import { DetailPanelProcess, EuiTabProps } from '../../types'; - -const FILTER_FORKS_EXECS = [EventAction.fork, EventAction.exec]; - -const DEFAULT_PROCESS_DATA = { - id: '', - name: '', - start: '', - end: '', - userName: '', - groupName: '', - working_directory: '', - args: [], - entryMetaType: '', - entryMetaSourceIp: '', - executable: '', -}; - -const getDetailPanelProcessLeader = (leader: ProcessFields | undefined) => ({ - ...leader, - name: leader?.name ?? DEFAULT_PROCESS_DATA.name, - start: leader?.start ?? DEFAULT_PROCESS_DATA.start, - working_directory: leader?.working_directory ?? DEFAULT_PROCESS_DATA.working_directory, - args: leader?.args ?? DEFAULT_PROCESS_DATA.args, - executable: leader?.executable ?? DEFAULT_PROCESS_DATA.executable, - id: leader?.entity_id ?? DEFAULT_PROCESS_DATA.id, - entryMetaType: leader?.entry_meta?.type ?? DEFAULT_PROCESS_DATA.entryMetaType, - userName: leader?.user?.name ?? DEFAULT_PROCESS_DATA.userName, - groupName: leader?.group?.name ?? DEFAULT_PROCESS_DATA.groupName, - entryMetaSourceIp: leader?.entry_meta?.source?.ip ?? DEFAULT_PROCESS_DATA.entryMetaSourceIp, -}); - -export const getDetailPanelProcess = (process: Process | null) => { - const processData = {} as DetailPanelProcess; - if (!process) { - return { - id: DEFAULT_PROCESS_DATA.id, - start: DEFAULT_PROCESS_DATA.start, - end: DEFAULT_PROCESS_DATA.end, - userName: DEFAULT_PROCESS_DATA.userName, - groupName: DEFAULT_PROCESS_DATA.groupName, - args: DEFAULT_PROCESS_DATA.args, - executable: [], - working_directory: DEFAULT_PROCESS_DATA.working_directory, - entryLeader: DEFAULT_PROCESS_DATA, - sessionLeader: DEFAULT_PROCESS_DATA, - groupLeader: DEFAULT_PROCESS_DATA, - parent: DEFAULT_PROCESS_DATA, - }; - } - - const details = process.getDetails(); - - processData.id = process.id; - processData.start = details.process?.start ?? ''; - processData.args = []; - processData.executable = []; - - if (!processData.userName) { - processData.userName = details.process?.user?.name ?? ''; - } - if (!processData.groupName) { - processData.groupName = details.process?.group?.name ?? ''; - } - if (!processData.pid) { - processData.pid = details.process?.pid; - } - if (!processData.working_directory) { - processData.working_directory = details.process?.working_directory ?? ''; - } - if (!processData.tty) { - processData.tty = details.process?.tty; - } - if (details.process?.args && details.process.args.length > 0) { - processData.args = details.process.args; - } - if (details.process?.exit_code !== undefined) { - processData.exit_code = details.process.exit_code; - } - - // we grab the executable from each process lifecycle event to give an indication - // of the processes journey. Processes can sometimes exec multiple times, so it's good - // information to have. - process.events.forEach((event) => { - if ( - event.process?.executable && - event.event?.action && - FILTER_FORKS_EXECS.includes(event.event.action) - ) { - processData.executable.push([event.process.executable, `(${event.event.action})`]); - } - }); - - processData.end = process.getEndTime(); - processData.entryLeader = getDetailPanelProcessLeader(details?.process?.entry_leader); - processData.sessionLeader = getDetailPanelProcessLeader(details?.process?.session_leader); - processData.groupLeader = getDetailPanelProcessLeader(details?.process?.group_leader); - processData.parent = getDetailPanelProcessLeader(details?.process?.parent); - - return processData; -}; +import { EuiTabProps } from '../../types'; export const getSelectedTabContent = (tabs: EuiTabProps[], selectedTabId: string) => { const selectedTab = tabs.find((tab) => tab.id === selectedTabId); diff --git a/x-pack/plugins/session_view/public/components/session_view_detail_panel/index.tsx b/x-pack/plugins/session_view/public/components/session_view_detail_panel/index.tsx index 130d15ca4df30..a22ad026c4395 100644 --- a/x-pack/plugins/session_view/public/components/session_view_detail_panel/index.tsx +++ b/x-pack/plugins/session_view/public/components/session_view_detail_panel/index.tsx @@ -9,7 +9,7 @@ import { EuiTabs, EuiTab, EuiNotificationBadge } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { EuiTabProps } from '../../types'; import { Process, ProcessEvent } from '../../../common/types/process_tree'; -import { getDetailPanelProcess, getSelectedTabContent } from './helpers'; +import { getSelectedTabContent } from './helpers'; import { DetailPanelProcessTab } from '../detail_panel_process_tab'; import { DetailPanelHostTab } from '../detail_panel_host_tab'; import { useStyles } from './styles'; @@ -35,7 +35,6 @@ export const SessionViewDetailPanel = ({ onShowAlertDetails, }: SessionViewDetailPanelDeps) => { const [selectedTabId, setSelectedTabId] = useState('process'); - const processDetail = useMemo(() => getDetailPanelProcess(selectedProcess), [selectedProcess]); const alertsCount = useMemo(() => { if (!alerts) { @@ -54,7 +53,7 @@ export const SessionViewDetailPanel = ({ name: i18n.translate('xpack.sessionView.detailsPanel.process', { defaultMessage: 'Process', }), - content: , + content: , }, { id: 'host', @@ -85,12 +84,11 @@ export const SessionViewDetailPanel = ({ ]; }, [ alerts, + selectedProcess, alertsCount, - processDetail, - selectedProcess?.events, + onJumpToEvent, onShowAlertDetails, investigatedAlertId, - onJumpToEvent, ]); const onSelectedTabChanged = useCallback((id: string) => { diff --git a/x-pack/plugins/session_view/public/components/session_view_search_bar/index.test.tsx b/x-pack/plugins/session_view/public/components/session_view_search_bar/index.test.tsx index 67347f2138f0a..dd758529530ba 100644 --- a/x-pack/plugins/session_view/public/components/session_view_search_bar/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/session_view_search_bar/index.test.tsx @@ -34,7 +34,7 @@ describe('SessionViewSearchBar component', () => { /> ); - const searchInput = renderResult.getByTestId('sessionView:searchInput').querySelector('input'); + const searchInput = renderResult.getByTestId('sessionView:searchBar').querySelector('input'); expect(searchInput?.value).toEqual('ls'); @@ -73,7 +73,7 @@ describe('SessionViewSearchBar component', () => { userEvent.click(renderResult.getByTestId('pagination-button-next')); expect(searchPagination.querySelector(paginationTextClass)?.textContent).toEqual('2 of 3'); - const searchInput = renderResult.getByTestId('sessionView:searchInput').querySelector('input'); + const searchInput = renderResult.getByTestId('sessionView:searchBar').querySelector('input'); if (searchInput) { userEvent.type(searchInput, ' -la'); diff --git a/x-pack/plugins/session_view/public/components/session_view_search_bar/index.tsx b/x-pack/plugins/session_view/public/components/session_view_search_bar/index.tsx index 9c24f6b94199c..0cac43f39a8ec 100644 --- a/x-pack/plugins/session_view/public/components/session_view_search_bar/index.tsx +++ b/x-pack/plugins/session_view/public/components/session_view_search_bar/index.tsx @@ -65,7 +65,7 @@ export const SessionViewSearchBar = ({ }, [searchResults, onProcessSelected, selectedResult]); return ( -
+
{noResults && {NO_RESULTS}} {showPagination && ( diff --git a/x-pack/plugins/session_view/public/components/session_view_search_bar/styles.ts b/x-pack/plugins/session_view/public/components/session_view_search_bar/styles.ts index ebb1b8f7b340b..6e7c717e2816b 100644 --- a/x-pack/plugins/session_view/public/components/session_view_search_bar/styles.ts +++ b/x-pack/plugins/session_view/public/components/session_view_search_bar/styles.ts @@ -6,15 +6,15 @@ */ import { useMemo } from 'react'; -import { useEuiTheme } from '@elastic/eui'; import { CSSObject } from '@emotion/react'; +import { useEuiTheme } from '../../hooks'; interface StylesDeps { hasSearchResults: boolean; } export const useStyles = ({ hasSearchResults }: StylesDeps) => { - const { euiTheme } = useEuiTheme(); + const { euiTheme, euiVars } = useEuiTheme(); const cached = useMemo(() => { const pagination: CSSObject = { @@ -36,19 +36,20 @@ export const useStyles = ({ hasSearchResults }: StylesDeps) => { right: euiTheme.size.xxl, }; - const searchBarWithResult: CSSObject = { + const searchBar: CSSObject = { position: 'relative', - 'input.euiFieldSearch.euiFieldSearch-isClearable': { + backgroundColor: euiVars.euiFormBackgroundColor, + input: { paddingRight: hasSearchResults ? '200px' : euiTheme.size.xxl, }, }; return { pagination, - searchBarWithResult, + searchBar, noResults, }; - }, [euiTheme, hasSearchResults]); + }, [euiTheme, euiVars, hasSearchResults]); return cached; }; diff --git a/x-pack/plugins/session_view/public/constants.ts b/x-pack/plugins/session_view/public/constants.ts new file mode 100644 index 0000000000000..2f7306bf3e4a8 --- /dev/null +++ b/x-pack/plugins/session_view/public/constants.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const DASH = '-'; diff --git a/x-pack/plugins/session_view/public/hooks/index.ts b/x-pack/plugins/session_view/public/hooks/index.ts new file mode 100644 index 0000000000000..8db231cd2bc64 --- /dev/null +++ b/x-pack/plugins/session_view/public/hooks/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { useEuiTheme } from './use_eui_theme'; diff --git a/x-pack/plugins/session_view/public/hooks/use_eui_theme.ts b/x-pack/plugins/session_view/public/hooks/use_eui_theme.ts new file mode 100644 index 0000000000000..02f7dd479d2ac --- /dev/null +++ b/x-pack/plugins/session_view/public/hooks/use_eui_theme.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { shade, useEuiTheme as useEuiThemeHook } from '@elastic/eui'; +import { euiLightVars, euiDarkVars } from '@kbn/ui-theme'; +import { useMemo } from 'react'; + +type EuiThemeProps = Parameters; +type ExtraEuiVars = { + // eslint-disable-next-line @typescript-eslint/naming-convention + euiColorVis6_asText: string; + buttonsBackgroundNormalDefaultPrimary: string; +}; +type EuiVars = typeof euiLightVars & ExtraEuiVars; +type EuiThemeReturn = ReturnType & { euiVars: EuiVars }; + +// Not all Eui Tokens were fully migrated to @elastic/eui/useEuiTheme yet, so +// this hook overrides the default useEuiTheme hook to provide a custom hook that +// allows the use the euiVars tokens from the euiLightVars and euiDarkVars +export const useEuiTheme = (...props: EuiThemeProps): EuiThemeReturn => { + const euiThemeHook = useEuiThemeHook(...props); + + const euiVars = useMemo(() => { + const themeVars = euiThemeHook.colorMode === 'DARK' ? euiDarkVars : euiLightVars; + + const extraEuiVars: ExtraEuiVars = { + // eslint-disable-next-line @typescript-eslint/naming-convention + euiColorVis6_asText: shade(themeVars.euiColorVis6, 0.335), + buttonsBackgroundNormalDefaultPrimary: '#006DE4', + }; + + return { + ...themeVars, + ...extraEuiVars, + }; + }, [euiThemeHook.colorMode]); + + return { + ...euiThemeHook, + euiVars, + }; +}; diff --git a/x-pack/plugins/session_view/public/types.ts b/x-pack/plugins/session_view/public/types.ts index 8e5b9a7ed83f2..1f90ae05b0791 100644 --- a/x-pack/plugins/session_view/public/types.ts +++ b/x-pack/plugins/session_view/public/types.ts @@ -6,7 +6,6 @@ */ import { ReactNode } from 'react'; import { CoreStart } from '@kbn/core/public'; -import { Teletype } from '../common/types/process_tree'; export type SessionViewServices = CoreStart; @@ -43,14 +42,14 @@ export interface DetailPanelProcess { id: string; start: string; end: string; - exit_code?: number; + exitCode: string; userName: string; groupName: string; - args: string[]; + args: string; executable: string[][]; - working_directory: string; - tty?: Teletype; - pid?: number; + workingDirectory: string; + interactive: string; + pid: string; entryLeader: DetailPanelProcessLeader; sessionLeader: DetailPanelProcessLeader; groupLeader: DetailPanelProcessLeader; @@ -61,17 +60,34 @@ export interface DetailPanelProcessLeader { id: string; name: string; start: string; - end?: string; - exit_code?: number; + end: string; + exitCode: string; userName: string; groupName: string; - working_directory: string; - tty?: Teletype; - args: string[]; - pid?: number; + workingDirectory: string; + interactive: string; + args: string; + pid: string; entryMetaType: string; entryMetaSourceIp: string; - executable: string; + executable: string[][]; +} + +export interface DetailPanelHost { + architecture: string; + hostname: string; + id: string; + ip: string; + mac: string; + name: string; + os: { + family: string; + full: string; + kernel: string; + name: string; + platform: string; + version: string; + }; } export interface SessionViewStart { diff --git a/x-pack/plugins/session_view/public/utils/data_or_dash.test.ts b/x-pack/plugins/session_view/public/utils/data_or_dash.test.ts index 12ef44cf1d708..c4950bb8d20b0 100644 --- a/x-pack/plugins/session_view/public/utils/data_or_dash.test.ts +++ b/x-pack/plugins/session_view/public/utils/data_or_dash.test.ts @@ -5,11 +5,11 @@ * 2.0. */ +import { DASH } from '../constants'; import { dataOrDash } from './data_or_dash'; const TEST_STRING = '123'; const TEST_NUMBER = 123; -const DASH = '-'; describe('dataOrDash(data)', () => { it('works for a valid string', () => { diff --git a/x-pack/plugins/session_view/public/utils/data_or_dash.ts b/x-pack/plugins/session_view/public/utils/data_or_dash.ts index ff6c2fb9bc1ff..15c82b47220d2 100644 --- a/x-pack/plugins/session_view/public/utils/data_or_dash.ts +++ b/x-pack/plugins/session_view/public/utils/data_or_dash.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { DASH } from '../constants'; + /** * Returns a dash ('-') if data is undefined, and empty string, or a NaN. * @@ -15,7 +17,7 @@ */ export const dataOrDash = (data: string | number | undefined): string | number => { if (data === undefined || data === '' || (typeof data === 'number' && isNaN(data))) { - return '-'; + return DASH; } return data; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/resolve_all_conflicts.test.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/resolve_all_conflicts.test.tsx index ad183196039c6..639323fb5467c 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/resolve_all_conflicts.test.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/resolve_all_conflicts.test.tsx @@ -109,7 +109,6 @@ describe('ResolveAllConflicts', () => { panelPaddingSize="none" > { key={`spcMenuList`} data-search-term={searchTerm} className="spcMenu__spacesList" - hasFocus={this.state.allowSpacesListFocus} initialFocusedItemIndex={this.state.allowSpacesListFocus ? 0 : undefined} items={items} /> diff --git a/x-pack/plugins/spaces/public/space_selector/space_selector.tsx b/x-pack/plugins/spaces/public/space_selector/space_selector.tsx index 4d3a03852e49f..f55e72180c5cf 100644 --- a/x-pack/plugins/spaces/public/space_selector/space_selector.tsx +++ b/x-pack/plugins/spaces/public/space_selector/space_selector.tsx @@ -23,7 +23,7 @@ import type { AppMountParameters, CoreStart } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { KibanaPageTemplate, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; -import { KibanaSolutionAvatar } from '@kbn/shared-ux-components'; +import { KibanaSolutionAvatar } from '@kbn/shared-ux-avatar-solution'; import type { Space } from '../../common'; import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../common/constants'; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx b/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx index e753fee71d44b..6747c60bb840c 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/read_only_filter_items.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { injectI18n } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { getDisplayValueFromFilter } from '@kbn/data-plugin/public'; @@ -30,7 +31,12 @@ export const ReadOnlyFilterItems = ({ filters, indexPatterns }: ReadOnlyFilterIt const filterList = filters.map((filter, index) => { const filterValue = getDisplayValueFromFilter(filter, indexPatterns); return ( - + ; +export const MonitorDefaultsCodec = t.interface({ + [DataStream.HTTP]: HTTPFieldsCodec, + [DataStream.TCP]: TCPFieldsCodec, + [DataStream.ICMP]: ICMPSimpleFieldsCodec, + [DataStream.BROWSER]: BrowserFieldsCodec, +}); + +export type MonitorDefaults = t.TypeOf; + export const MonitorManagementListResultCodec = t.type({ monitors: t.array( t.interface({ diff --git a/x-pack/plugins/synthetics/common/translations.ts b/x-pack/plugins/synthetics/common/translations.ts index 9bef65bd9dad6..52f0dbf5d906e 100644 --- a/x-pack/plugins/synthetics/common/translations.ts +++ b/x-pack/plugins/synthetics/common/translations.ts @@ -28,11 +28,23 @@ export const MonitorStatusTranslations = { defaultMessage: 'Monitor {monitorName} with url {monitorUrl} from {observerLocation} {statusMessage} The latest error message is {latestErrorMessage}', values: { - monitorName: '{{state.monitorName}}', - monitorUrl: '{{{state.monitorUrl}}}', - statusMessage: '{{{state.statusMessage}}}', - latestErrorMessage: '{{{state.latestErrorMessage}}}', - observerLocation: '{{state.observerLocation}}', + monitorName: '{{context.monitorName}}', + monitorUrl: '{{{context.monitorUrl}}}', + statusMessage: '{{{context.statusMessage}}}', + latestErrorMessage: '{{{context.latestErrorMessage}}}', + observerLocation: '{{context.observerLocation}}', + }, + } + ), + defaultRecoveryMessage: i18n.translate( + 'xpack.synthetics.alerts.monitorStatus.defaultRecoveryMessage', + { + defaultMessage: + 'Alert for monitor {monitorName} with url {monitorUrl} from {observerLocation} has recovered', + values: { + monitorName: '{{context.monitorName}}', + monitorUrl: '{{{context.monitorUrl}}}', + observerLocation: '{{context.observerLocation}}', }, } ), @@ -46,13 +58,19 @@ export const MonitorStatusTranslations = { export const TlsTranslations = { defaultActionMessage: i18n.translate('xpack.synthetics.alerts.tls.defaultActionMessage', { - defaultMessage: `Detected TLS certificate {commonName} from issuer {issuer} is {status}. Certificate {summary} -`, + defaultMessage: `Detected TLS certificate {commonName} from issuer {issuer} is {status}. Certificate {summary}`, values: { - commonName: '{{state.commonName}}', - issuer: '{{state.issuer}}', - summary: '{{state.summary}}', - status: '{{state.status}}', + commonName: '{{context.commonName}}', + issuer: '{{context.issuer}}', + summary: '{{context.summary}}', + status: '{{context.status}}', + }, + }), + defaultRecoveryMessage: i18n.translate('xpack.synthetics.alerts.tls.defaultRecoveryMessage', { + defaultMessage: `Alert for TLS certificate {commonName} from issuer {issuer} has recovered`, + values: { + commonName: '{{context.commonName}}', + issuer: '{{context.issuer}}', }, }), name: i18n.translate('xpack.synthetics.alerts.tls.clientName', { @@ -103,14 +121,27 @@ export const DurationAnomalyTranslations = { defaultMessage: `Abnormal ({severity} level) response time detected on {monitor} with url {monitorUrl} at {anomalyStartTimestamp}. Anomaly severity score is {severityScore}. Response times as high as {slowestAnomalyResponse} have been detected from location {observerLocation}. Expected response time is {expectedResponseTime}.`, values: { - severity: '{{state.severity}}', - anomalyStartTimestamp: '{{state.anomalyStartTimestamp}}', - monitor: '{{state.monitor}}', - monitorUrl: '{{{state.monitorUrl}}}', - slowestAnomalyResponse: '{{state.slowestAnomalyResponse}}', - expectedResponseTime: '{{state.expectedResponseTime}}', - severityScore: '{{state.severityScore}}', - observerLocation: '{{state.observerLocation}}', + severity: '{{context.severity}}', + anomalyStartTimestamp: '{{context.anomalyStartTimestamp}}', + monitor: '{{context.monitor}}', + monitorUrl: '{{{context.monitorUrl}}}', + slowestAnomalyResponse: '{{context.slowestAnomalyResponse}}', + expectedResponseTime: '{{context.expectedResponseTime}}', + severityScore: '{{context.severityScore}}', + observerLocation: '{{context.observerLocation}}', + }, + } + ), + defaultRecoveryMessage: i18n.translate( + 'xpack.synthetics.alerts.durationAnomaly.defaultRecoveryMessage', + { + defaultMessage: `Alert for abnormal ({severity} level) response time detected on monitor {monitor} with url {monitorUrl} from location {observerLocation} at {anomalyStartTimestamp} has recovered`, + values: { + severity: '{{context.severity}}', + anomalyStartTimestamp: '{{context.anomalyStartTimestamp}}', + monitor: '{{context.monitor}}', + monitorUrl: '{{{context.monitorUrl}}}', + observerLocation: '{{context.observerLocation}}', }, } ), diff --git a/x-pack/plugins/synthetics/e2e/config.ts b/x-pack/plugins/synthetics/e2e/config.ts index e8af8510fe5dc..42e97eb21e90a 100644 --- a/x-pack/plugins/synthetics/e2e/config.ts +++ b/x-pack/plugins/synthetics/e2e/config.ts @@ -19,7 +19,7 @@ async function config({ readConfigFile }: FtrConfigProviderContext) { require.resolve('../../../../test/common/config.js') ); const xpackFunctionalTestsConfig = await readConfigFile( - require.resolve('../../../test/functional/config.js') + require.resolve('../../../test/functional/config.base.js') ); const kibanaConfig = readKibanaConfig(); diff --git a/x-pack/plugins/synthetics/e2e/tasks/es_archiver.ts b/x-pack/plugins/synthetics/e2e/tasks/es_archiver.ts index dac5672bdf649..bbb66b19f5a5e 100644 --- a/x-pack/plugins/synthetics/e2e/tasks/es_archiver.ts +++ b/x-pack/plugins/synthetics/e2e/tasks/es_archiver.ts @@ -16,7 +16,7 @@ const NODE_TLS_REJECT_UNAUTHORIZED = '1'; export const esArchiverLoad = (folder: string) => { const path = Path.join(ES_ARCHIVE_DIR, folder); execSync( - `node ../../../../scripts/es_archiver load "${path}" --config ../../../test/functional/config.js`, + `node ../../../../scripts/es_archiver load "${path}" --config ../../../test/functional/config.base.js`, { env: { ...process.env, NODE_TLS_REJECT_UNAUTHORIZED }, stdio: 'inherit' } ); }; @@ -24,14 +24,14 @@ export const esArchiverLoad = (folder: string) => { export const esArchiverUnload = (folder: string) => { const path = Path.join(ES_ARCHIVE_DIR, folder); execSync( - `node ../../../../scripts/es_archiver unload "${path}" --config ../../../test/functional/config.js`, + `node ../../../../scripts/es_archiver unload "${path}" --config ../../../test/functional/config.base.js`, { env: { ...process.env, NODE_TLS_REJECT_UNAUTHORIZED }, stdio: 'inherit' } ); }; export const esArchiverResetKibana = () => { execSync( - `node ../../../../scripts/es_archiver empty-kibana-index --config ../../../test/functional/config.js`, + `node ../../../../scripts/es_archiver empty-kibana-index --config ../../../test/functional/config.base.js`, { env: { ...process.env, NODE_TLS_REJECT_UNAUTHORIZED }, stdio: 'inherit' } ); }; diff --git a/x-pack/plugins/synthetics/public/apps/plugin.ts b/x-pack/plugins/synthetics/public/apps/plugin.ts deleted file mode 100644 index 7f1a773376688..0000000000000 --- a/x-pack/plugins/synthetics/public/apps/plugin.ts +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - CoreSetup, - CoreStart, - Plugin, - PluginInitializerContext, - AppMountParameters, -} from '@kbn/core/public'; -import { from } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { i18n } from '@kbn/i18n'; -import { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; -import { DiscoverStart } from '@kbn/discover-plugin/public'; -import { DEFAULT_APP_CATEGORIES } from '@kbn/core/public'; - -import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; -import { EmbeddableStart } from '@kbn/embeddable-plugin/public'; -import { - TriggersAndActionsUIPublicPluginSetup, - TriggersAndActionsUIPublicPluginStart, -} from '@kbn/triggers-actions-ui-plugin/public'; -import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; -import { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; - -import { FleetStart } from '@kbn/fleet-plugin/public'; -import { - FetchDataParams, - ObservabilityPublicSetup, - ObservabilityPublicStart, -} from '@kbn/observability-plugin/public'; -import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; -import { Start as InspectorPluginStart } from '@kbn/inspector-plugin/public'; -import { CasesUiStart } from '@kbn/cases-plugin/public'; -import { CloudSetup } from '@kbn/cloud-plugin/public'; -import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; -import { PLUGIN } from '../../common/constants/plugin'; -import { - LazySyntheticsPolicyCreateExtension, - LazySyntheticsPolicyEditExtension, -} from '../components/fleet_package'; -import { LazySyntheticsCustomAssetsExtension } from '../components/fleet_package/lazy_synthetics_custom_assets_extension'; -import { uptimeOverviewNavigatorParams } from './locators/overview'; -import { alertTypeInitializers, legacyAlertTypeInitializers } from '../lib/alert_types'; - -export interface ClientPluginsSetup { - home?: HomePublicPluginSetup; - data: DataPublicPluginSetup; - observability: ObservabilityPublicSetup; - share: SharePluginSetup; - triggersActionsUi: TriggersAndActionsUIPublicPluginSetup; - cloud?: CloudSetup; -} - -export interface ClientPluginsStart { - fleet?: FleetStart; - data: DataPublicPluginStart; - unifiedSearch: UnifiedSearchPublicPluginStart; - discover: DiscoverStart; - inspector: InspectorPluginStart; - embeddable: EmbeddableStart; - observability: ObservabilityPublicStart; - share: SharePluginStart; - triggersActionsUi: TriggersAndActionsUIPublicPluginStart; - cases: CasesUiStart; - dataViews: DataViewsPublicPluginStart; -} - -export interface UptimePluginServices extends Partial { - embeddable: EmbeddableStart; - data: DataPublicPluginStart; - triggersActionsUi: TriggersAndActionsUIPublicPluginStart; - storage: IStorageWrapper; -} - -export type ClientSetup = void; -export type ClientStart = void; - -export class UptimePlugin - implements Plugin -{ - constructor(private readonly initContext: PluginInitializerContext) {} - - public setup(core: CoreSetup, plugins: ClientPluginsSetup): void { - if (plugins.home) { - plugins.home.featureCatalogue.register({ - id: PLUGIN.ID, - title: PLUGIN.TITLE, - description: PLUGIN.DESCRIPTION, - icon: 'uptimeApp', - path: '/app/uptime', - showOnHomePage: false, - category: 'data', - }); - } - const getUptimeDataHelper = async () => { - const [coreStart] = await core.getStartServices(); - const { UptimeDataHelper } = await import('./uptime_overview_fetcher'); - - return UptimeDataHelper(coreStart); - }; - - plugins.share.url.locators.create(uptimeOverviewNavigatorParams); - - plugins.observability.dashboard.register({ - appName: 'synthetics', - hasData: async () => { - const dataHelper = await getUptimeDataHelper(); - const status = await dataHelper.indexStatus(); - return { hasData: status.docCount > 0, indices: status.indices }; - }, - fetchData: async (params: FetchDataParams) => { - const dataHelper = await getUptimeDataHelper(); - return await dataHelper.overviewData(params); - }, - }); - - plugins.observability.navigation.registerSections( - from(core.getStartServices()).pipe( - map(([coreStart]) => { - if (coreStart.application.capabilities.uptime.show) { - return [ - { - label: 'Uptime', - sortKey: 500, - entries: [ - { - label: i18n.translate('xpack.synthetics.overview.heading', { - defaultMessage: 'Monitors', - }), - app: 'uptime', - path: '/', - matchFullPath: true, - ignoreTrailingSlash: true, - }, - { - label: i18n.translate('xpack.synthetics.certificatesPage.heading', { - defaultMessage: 'TLS Certificates', - }), - app: 'uptime', - path: '/certificates', - matchFullPath: true, - }, - ], - }, - ]; - } - - return []; - }) - ) - ); - - const { observabilityRuleTypeRegistry } = plugins.observability; - - core.getStartServices().then(([coreStart, clientPluginsStart]) => { - alertTypeInitializers.forEach((init) => { - const alertInitializer = init({ - core: coreStart, - plugins: clientPluginsStart, - }); - if ( - clientPluginsStart.triggersActionsUi && - !clientPluginsStart.triggersActionsUi.ruleTypeRegistry.has(alertInitializer.id) - ) { - observabilityRuleTypeRegistry.register(alertInitializer); - } - }); - - legacyAlertTypeInitializers.forEach((init) => { - const alertInitializer = init({ - core: coreStart, - plugins: clientPluginsStart, - }); - if ( - clientPluginsStart.triggersActionsUi && - !clientPluginsStart.triggersActionsUi.ruleTypeRegistry.has(alertInitializer.id) - ) { - plugins.triggersActionsUi.ruleTypeRegistry.register(alertInitializer); - } - }); - }); - - core.application.register({ - id: PLUGIN.ID, - euiIconType: 'logoObservability', - order: 8400, - title: PLUGIN.TITLE, - category: DEFAULT_APP_CATEGORIES.observability, - keywords: [ - 'Synthetics', - 'pings', - 'checks', - 'availability', - 'response duration', - 'response time', - 'outside in', - 'reachability', - 'reachable', - 'digital', - 'performance', - 'web performance', - 'web perf', - ], - deepLinks: [ - { id: 'Down monitors', title: 'Down monitors', path: '/?statusFilter=down' }, - { id: 'Certificates', title: 'TLS Certificates', path: '/certificates' }, - { id: 'Settings', title: 'Settings', path: '/settings' }, - ], - mount: async (params: AppMountParameters) => { - const [coreStart, corePlugins] = await core.getStartServices(); - - const { renderApp } = await import('./render_app'); - return renderApp(coreStart, plugins, corePlugins, params, this.initContext.env.mode.dev); - }, - }); - } - - public start(start: CoreStart, plugins: ClientPluginsStart): void { - if (plugins.fleet) { - const { registerExtension } = plugins.fleet; - - registerExtension({ - package: 'synthetics', - view: 'package-policy-create', - Component: LazySyntheticsPolicyCreateExtension, - }); - - registerExtension({ - package: 'synthetics', - view: 'package-policy-edit', - useLatestPackageVersion: true, - Component: LazySyntheticsPolicyEditExtension, - }); - - registerExtension({ - package: 'synthetics', - view: 'package-detail-assets', - Component: LazySyntheticsCustomAssetsExtension, - }); - } - } - - public stop(): void {} -} diff --git a/x-pack/plugins/synthetics/public/apps/render_app.tsx b/x-pack/plugins/synthetics/public/apps/render_app.tsx deleted file mode 100644 index 36ac093f23d0a..0000000000000 --- a/x-pack/plugins/synthetics/public/apps/render_app.tsx +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import ReactDOM from 'react-dom'; -import { i18n as i18nFormatter } from '@kbn/i18n'; -import { AppMountParameters, CoreStart } from '@kbn/core/public'; -import { getIntegratedAppAvailability } from '../lib/adapters/framework/capabilities_adapter'; -import { - DEFAULT_DARK_MODE, - DEFAULT_TIMEPICKER_QUICK_RANGES, - INTEGRATED_SOLUTIONS, -} from '../../common/constants'; -import { UptimeApp, UptimeAppProps } from './uptime_app'; -import { ClientPluginsSetup, ClientPluginsStart } from './plugin'; - -export function renderApp( - core: CoreStart, - plugins: ClientPluginsSetup, - startPlugins: ClientPluginsStart, - appMountParameters: AppMountParameters, - isDev: boolean -) { - const { - application: { capabilities }, - chrome: { setBadge, setHelpExtension }, - docLinks, - http: { basePath }, - i18n, - } = core; - - const { apm, infrastructure, logs } = getIntegratedAppAvailability( - capabilities, - INTEGRATED_SOLUTIONS - ); - - const canSave = (capabilities.uptime.save ?? false) as boolean; - - const props: UptimeAppProps = { - isDev, - plugins, - canSave, - core, - i18n, - startPlugins, - basePath: basePath.get(), - darkMode: core.uiSettings.get(DEFAULT_DARK_MODE), - commonlyUsedRanges: core.uiSettings.get(DEFAULT_TIMEPICKER_QUICK_RANGES), - isApmAvailable: apm, - isInfraAvailable: infrastructure, - isLogsAvailable: logs, - renderGlobalHelpControls: () => - setHelpExtension({ - appName: i18nFormatter.translate('xpack.synthetics.header.appName', { - defaultMessage: 'Uptime', - }), - links: [ - { - linkType: 'documentation', - href: `${docLinks.links.observability.monitorUptime}`, - }, - { - linkType: 'discuss', - href: 'https://discuss.elastic.co/c/uptime', - }, - ], - }), - setBadge, - appMountParameters, - setBreadcrumbs: core.chrome.setBreadcrumbs, - }; - - ReactDOM.render(, appMountParameters.element); - - return () => { - ReactDOM.unmountComponentAtNode(appMountParameters.element); - }; -} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.test.tsx new file mode 100644 index 0000000000000..7040da99c39a3 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.test.tsx @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { SyntheticsDatePicker } from './synthetics_date_picker'; +import { startPlugins } from '../../../utils/testing/__mocks__/synthetics_plugin_start_mock'; +import { createMemoryHistory } from 'history'; +import { render } from '../../../utils/testing'; +import { fireEvent } from '@testing-library/dom'; + +describe('SyntheticsDatePicker component', () => { + jest.setTimeout(10_000); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders properly with mock data', async () => { + const { findByText } = render(); + expect(await findByText('Last 15 minutes')).toBeInTheDocument(); + expect(await findByText('Refresh')).toBeInTheDocument(); + }); + + it('uses shared date range state when there is no url date range state', async () => { + const customHistory = createMemoryHistory({ + initialEntries: ['/?dateRangeStart=now-15m&dateRangeEnd=now'], + }); + + jest.spyOn(customHistory, 'push'); + + const { findByText } = render(, { + history: customHistory, + core: startPlugins, + }); + + expect(await findByText('~ 15 minutes ago')).toBeInTheDocument(); + + expect(await findByText('~ 30 minutes ago')).toBeInTheDocument(); + + expect(customHistory.push).toHaveBeenCalledWith({ + pathname: '/', + search: 'dateRangeEnd=now-15m&dateRangeStart=now-30m', + }); + }); + + it('should use url date range even if shared date range is present', async () => { + const customHistory = createMemoryHistory({ + initialEntries: ['/?g=%22%22&dateRangeStart=now-10m&dateRangeEnd=now'], + }); + + jest.spyOn(customHistory, 'push'); + + const { findByText } = render(, { + history: customHistory, + core: startPlugins, + }); + + expect(await findByText('Last 10 minutes')).toBeInTheDocument(); + + // it should update shared state + + expect(startPlugins.data.query.timefilter.timefilter.setTime).toHaveBeenCalledWith({ + from: 'now-10m', + to: 'now', + }); + }); + + it('should handle on change', async () => { + const customHistory = createMemoryHistory({ + initialEntries: ['/?g=%22%22&dateRangeStart=now-10m&dateRangeEnd=now'], + }); + + jest.spyOn(customHistory, 'push'); + + const { findByText, getByTestId, findByTestId } = render(, { + history: customHistory, + core: startPlugins, + }); + + expect(await findByText('Last 10 minutes')).toBeInTheDocument(); + + fireEvent.click(getByTestId('superDatePickerToggleQuickMenuButton')); + + fireEvent.click(await findByTestId('superDatePickerCommonlyUsed_Today')); + + expect(await findByText('Today')).toBeInTheDocument(); + + // it should update shared state + + expect(startPlugins.data.query.timefilter.timefilter.setTime).toHaveBeenCalledTimes(2); + + expect(startPlugins.data.query.timefilter.timefilter.setTime).toHaveBeenCalledWith({ + from: 'now-10m', + to: 'now', + }); + + expect(startPlugins.data.query.timefilter.timefilter.setTime).toHaveBeenLastCalledWith({ + from: 'now/d', + to: 'now', + }); + }); +}); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.tsx new file mode 100644 index 0000000000000..b9da1c098716d --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useContext, useEffect } from 'react'; +import { EuiSuperDatePicker } from '@elastic/eui'; +import { useUrlParams } from '../../../hooks'; +import { CLIENT_DEFAULTS } from '../../../../../../common/constants'; +import { + SyntheticsSettingsContext, + SyntheticsStartupPluginsContext, + SyntheticsRefreshContext, +} from '../../../contexts'; + +const isSyntheticsDefaultDateRange = (dateRangeStart: string, dateRangeEnd: string) => { + const { DATE_RANGE_START, DATE_RANGE_END } = CLIENT_DEFAULTS; + + return dateRangeStart === DATE_RANGE_START && dateRangeEnd === DATE_RANGE_END; +}; + +export const SyntheticsDatePicker = () => { + const [getUrlParams, updateUrl] = useUrlParams(); + const { commonlyUsedRanges } = useContext(SyntheticsSettingsContext); + const { refreshApp } = useContext(SyntheticsRefreshContext); + + const { data } = useContext(SyntheticsStartupPluginsContext); + + // read time from state and update the url + const sharedTimeState = data?.query.timefilter.timefilter.getTime(); + + const { + autorefreshInterval, + autorefreshIsPaused, + dateRangeStart: start, + dateRangeEnd: end, + } = getUrlParams(); + + useEffect(() => { + const { from, to } = sharedTimeState ?? {}; + // if it's synthetics default range, and we have shared state from kibana, let's use that + if (isSyntheticsDefaultDateRange(start, end) && (from !== start || to !== end)) { + updateUrl({ dateRangeStart: from, dateRangeEnd: to }); + } else if (from !== start || to !== end) { + // if it's coming url. let's update shared state + data?.query.timefilter.timefilter.setTime({ from: start, to: end }); + } + + // only need at start, rest date picker on change fucn will take care off + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const euiCommonlyUsedRanges = commonlyUsedRanges + ? commonlyUsedRanges.map( + ({ from, to, display }: { from: string; to: string; display: string }) => { + return { + start: from, + end: to, + label: display, + }; + } + ) + : CLIENT_DEFAULTS.COMMONLY_USED_DATE_RANGES; + + return ( + { + if (data?.query?.timefilter?.timefilter) { + data?.query.timefilter.timefilter.setTime({ from: startN, to: endN }); + } + + updateUrl({ dateRangeStart: startN, dateRangeEnd: endN }); + refreshApp(); + }} + onRefresh={refreshApp} + onRefreshChange={({ isPaused, refreshInterval }) => { + updateUrl({ + autorefreshInterval: + refreshInterval === undefined ? autorefreshInterval : refreshInterval, + autorefreshIsPaused: isPaused, + }); + }} + /> + ); +}; diff --git a/x-pack/plugins/synthetics/public/components/common/header/action_menu.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/header/action_menu.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/header/action_menu.tsx rename to x-pack/plugins/synthetics/public/apps/synthetics/components/common/header/action_menu.tsx diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/header/action_menu_content.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/header/action_menu_content.test.tsx new file mode 100644 index 0000000000000..0439eff39d88c --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/header/action_menu_content.test.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '../../../utils/testing/rtl_helpers'; +import { ActionMenuContent } from './action_menu_content'; + +describe('ActionMenuContent', () => { + it('renders settings link', () => { + const { getByRole, getByText } = render(); + + const settingsAnchor = getByRole('link', { name: 'Navigate to the Uptime settings page' }); + expect(settingsAnchor.getAttribute('href')).toBe('/settings'); + expect(getByText('Settings')); + }); + + it('renders exploratory view link', () => { + const { getByLabelText, getByText } = render(); + + const analyzeAnchor = getByLabelText( + 'Navigate to the "Explore Data" view to visualize Synthetics/User data' + ); + + expect(analyzeAnchor.getAttribute('href')).toContain('/app/observability/exploratory-view'); + expect(getByText('Explore data')); + }); + + it('renders Add Data link', () => { + const { getByLabelText, getByText } = render(); + + const addDataAnchor = getByLabelText('Navigate to a tutorial about adding Uptime data'); + + // this href value is mocked, so it doesn't correspond to the real link + // that Kibana core services will provide + expect(addDataAnchor.getAttribute('href')).toBe('/home#/tutorial/uptimeMonitors'); + expect(getByText('Add data')); + }); +}); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/header/action_menu_content.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/header/action_menu_content.tsx new file mode 100644 index 0000000000000..aaf41dc46bcaf --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/header/action_menu_content.tsx @@ -0,0 +1,120 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiHeaderLinks, EuiToolTip, EuiHeaderLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useHistory, useRouteMatch } from 'react-router-dom'; +import { createExploratoryViewUrl } from '@kbn/observability-plugin/public'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { useSyntheticsSettingsContext } from '../../../contexts'; +import { useGetUrlParams } from '../../../hooks'; +import { MONITOR_ROUTE, SETTINGS_ROUTE } from '../../../../../../common/constants'; +import { stringifyUrlParams } from '../../../utils/url_params'; +import { InspectorHeaderLink } from './inspector_header_link'; + +const ADD_DATA_LABEL = i18n.translate('xpack.synthetics.addDataButtonLabel', { + defaultMessage: 'Add data', +}); + +const ANALYZE_DATA = i18n.translate('xpack.synthetics.analyzeDataButtonLabel', { + defaultMessage: 'Explore data', +}); + +const ANALYZE_MESSAGE = i18n.translate('xpack.synthetics.analyzeDataButtonLabel.message', { + defaultMessage: + 'Explore Data allows you to select and filter result data in any dimension and look for the cause or impact of performance problems.', +}); + +export function ActionMenuContent(): React.ReactElement { + const kibana = useKibana(); + const { basePath } = useSyntheticsSettingsContext(); + const params = useGetUrlParams(); + const { dateRangeStart, dateRangeEnd } = params; + const history = useHistory(); + + const selectedMonitor = { + monitor: { id: undefined, name: 'test' }, + }; /* useSelector(monitorStatusSelector) TODO: Implement state for monitor status */ + + const detailRouteMatch = useRouteMatch(MONITOR_ROUTE); + const monitorId = selectedMonitor?.monitor?.id; + + const syntheticExploratoryViewLink = createExploratoryViewUrl( + { + reportType: 'kpi-over-time', + allSeries: [ + { + dataType: 'synthetics', + seriesType: 'area', + selectedMetricField: 'monitor.duration.us', + time: { from: dateRangeStart, to: dateRangeEnd }, + breakdown: monitorId ? 'observer.geo.name' : 'monitor.type', + reportDefinitions: { + 'monitor.name': + selectedMonitor?.monitor?.name && detailRouteMatch?.isExact === true + ? [selectedMonitor?.monitor?.name] + : [], + 'url.full': ['ALL_VALUES'], + }, + name: monitorId ? `${monitorId}-response-duration` : 'All monitors response duration', + }, + ], + }, + basePath + ); + + return ( + + {/* TODO: See if it's needed for new Synthetics App */} + + + + + + {ANALYZE_MESSAGE}

}> + + {ANALYZE_DATA} + +
+ + + {ADD_DATA_LABEL} + + +
+ ); +} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/header/inspector_header_link.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/header/inspector_header_link.tsx new file mode 100644 index 0000000000000..b1166d342ff3e --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/header/inspector_header_link.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiHeaderLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { enableInspectEsQueries, useInspectorContext } from '@kbn/observability-plugin/public'; +import { ClientPluginsStart } from '../../../../../plugin'; +import { useSyntheticsSettingsContext } from '../../../contexts'; + +export function InspectorHeaderLink() { + const { + services: { inspector, uiSettings }, + } = useKibana(); + + const { isDev } = useSyntheticsSettingsContext(); + + const { inspectorAdapters } = useInspectorContext(); + + const isInspectorEnabled = uiSettings?.get(enableInspectEsQueries); + + const inspect = () => { + inspector.open(inspectorAdapters); + }; + + if (!isInspectorEnabled && !isDev) { + return null; + } + + return ( + + {i18n.translate('xpack.synthetics.inspectButtonText', { + defaultMessage: 'Inspect', + })} + + ); +} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/pages/not_found.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/pages/not_found.test.tsx new file mode 100644 index 0000000000000..0bade3c639a5e --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/pages/not_found.test.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { NotFoundPage } from './not_found'; +import { render } from '../../../utils/testing'; + +describe('NotFoundPage', () => { + it('render component', async () => { + const { findByText } = render(); + + expect(await findByText('Page not found')).toBeInTheDocument(); + expect(await findByText('Back to home')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/synthetics/public/pages/not_found.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/pages/not_found.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/pages/not_found.tsx rename to x-pack/plugins/synthetics/public/apps/synthetics/components/common/pages/not_found.tsx diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/pages/synthetics_page_template.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/pages/synthetics_page_template.test.tsx new file mode 100644 index 0000000000000..bd58aa12b1c82 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/pages/synthetics_page_template.test.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// app.test.js +import React from 'react'; +import 'jest-styled-components'; +import { render } from '../../../utils/testing/rtl_helpers'; +import { SyntheticsPageTemplateComponent } from './synthetics_page_template'; +import { OVERVIEW_ROUTE } from '../../../../../../common/constants'; +import { useBreakpoints } from '../../../../../hooks/use_breakpoints'; + +jest.mock('../../../../../hooks/use_breakpoints', () => { + const down = jest.fn().mockReturnValue(false); + return { + useBreakpoints: () => ({ down }), + }; +}); + +describe('SyntheticsPageTemplateComponent', () => { + describe('styling', () => { + // In this test we use snapshots because we're asserting on generated + // styles. Writing assertions manually here could make this test really + // convoluted, and it require us to manually update styling strings + // according to `styled-components` generator, which is counter-productive. + // In general, however, we avoid snaphshot tests. + + it('does not apply header centering on bigger resolutions', () => { + const { container } = render(); + expect(container.firstChild).toBeDefined(); + }); + + it('applies the header centering on mobile', () => { + (useBreakpoints().down as jest.Mock).mockReturnValue(true); + const { container } = render(); + expect(container.firstChild).toBeDefined(); + }); + }); +}); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/pages/synthetics_page_template.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/pages/synthetics_page_template.tsx new file mode 100644 index 0000000000000..44b38236fc2a2 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/pages/synthetics_page_template.tsx @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect, useMemo } from 'react'; +import styled from 'styled-components'; +import { EuiPageHeaderProps, EuiPageTemplateProps } from '@elastic/eui'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { useInspectorContext } from '@kbn/observability-plugin/public'; +import { ClientPluginsStart } from '../../../../../plugin'; +import { useNoDataConfig } from '../../../hooks/use_no_data_config'; +import { EmptyStateLoading } from '../../overview/empty_state/empty_state_loading'; +import { EmptyStateError } from '../../overview/empty_state/empty_state_error'; +import { useHasData } from '../../overview/empty_state/use_has_data'; +import { useBreakpoints } from '../../../hooks'; + +interface Props { + path: string; + pageHeader?: EuiPageHeaderProps; +} + +const mobileCenteredHeader = ` + .euiPageHeaderContent > .euiFlexGroup > .euiFlexItem { + align-items: center; + } +`; + +export const SyntheticsPageTemplateComponent: React.FC = ({ + path, + pageHeader, + children, + ...pageTemplateProps +}) => { + const { + services: { observability }, + } = useKibana(); + const { down } = useBreakpoints(); + const isMobile = down('s'); + + const PageTemplateComponent = observability.navigation.PageTemplate; + const StyledPageTemplateComponent = useMemo(() => { + return styled(PageTemplateComponent)<{ isMobile: boolean }>` + .euiPageHeaderContent > .euiFlexGroup { + flex-wrap: wrap; + } + + ${(props) => (props.isMobile ? mobileCenteredHeader : '')} + `; + }, [PageTemplateComponent]); + + const noDataConfig = useNoDataConfig(); + + const { loading, error, data } = useHasData(); + const { inspectorAdapters } = useInspectorContext(); + + useEffect(() => { + inspectorAdapters.requests.reset(); + }, [inspectorAdapters.requests]); + + if (error) { + return ; + } + + const showLoading = loading && !data; + + return ( + <> + + {showLoading && } +
+ {children} +
+
+ + ); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/wrappers/service_allowed_wrapper.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/wrappers/service_allowed_wrapper.tsx new file mode 100644 index 0000000000000..163ed0d37b7c3 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/wrappers/service_allowed_wrapper.tsx @@ -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 React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiButton, EuiEmptyPrompt, EuiLoadingLogo } from '@elastic/eui'; +import { useSyntheticsServiceAllowed } from '../../../hooks/use_service_allowed'; + +export const ServiceAllowedWrapper: React.FC = ({ children }) => { + const { isAllowed, signupUrl, loading } = useSyntheticsServiceAllowed(); + + if (loading) { + return ( + } + title={

{LOADING_MONITOR_MANAGEMENT_LABEL}

} + /> + ); + } + + // checking for explicit false + if (isAllowed === false) { + return ( + {MONITOR_MANAGEMENT_LABEL}} + body={

{PUBLIC_BETA_DESCRIPTION}

} + actions={[ + + {REQUEST_ACCESS_LABEL} + , + ]} + /> + ); + } + + return <>{children}; +}; + +const REQUEST_ACCESS_LABEL = i18n.translate('xpack.synthetics.monitorManagement.requestAccess', { + defaultMessage: 'Request access', +}); + +export const MONITOR_MANAGEMENT_LABEL = i18n.translate('xpack.synthetics.monitorManagement.label', { + defaultMessage: 'Monitor Management', +}); + +const LOADING_MONITOR_MANAGEMENT_LABEL = i18n.translate( + 'xpack.synthetics.monitorManagement.loading.label', + { + defaultMessage: 'Loading Monitor Management', + } +); + +export const PUBLIC_BETA_DESCRIPTION = i18n.translate( + 'xpack.synthetics.monitorManagement.publicBetaDescription', + { + defaultMessage: + "We've got a brand new app on the way. In the meantime, we're excited to give you early access to our globally managed testing infrastructure. This will allow you to upload synthetic monitors using our new point and click script recorder and manage your monitors with a new UI.", + } +); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/monitor_add_edit_page.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/monitor_add_edit_page.tsx new file mode 100644 index 0000000000000..d0a0c04150f8d --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/monitor_add_edit_page.tsx @@ -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 React from 'react'; +import { useTrackPageview } from '@kbn/observability-plugin/public'; +import { useMonitorAddEditBreadcrumbs } from './use_breadcrumbs'; + +export const MonitorAddEditPage: React.FC = () => { + useTrackPageview({ app: 'synthetics', path: 'add-monitor' }); + useTrackPageview({ app: 'synthetics', path: 'add-monitor', delay: 15000 }); + useMonitorAddEditBreadcrumbs(); + + return ( + <> +

Monitor Add or Edit page

+ + ); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/use_breadcrumbs.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/use_breadcrumbs.ts new file mode 100644 index 0000000000000..2fb17340c25fc --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/use_breadcrumbs.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 { i18n } from '@kbn/i18n'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; +import { MONITOR_ADD_ROUTE } from '../../../../../common/constants'; +import { PLUGIN } from '../../../../../common/constants/plugin'; + +export const useMonitorAddEditBreadcrumbs = () => { + const kibana = useKibana(); + const appPath = kibana.services.application?.getUrlForApp(PLUGIN.SYNTHETICS_PLUGIN_ID) ?? ''; + + useBreadcrumbs([ + { + text: ADD_MONITOR_CRUMB, + href: `${appPath}/${MONITOR_ADD_ROUTE}`, + }, + ]); +}; + +export const ADD_MONITOR_CRUMB = i18n.translate( + 'xpack.synthetics.monitorManagement.addMonitorCrumb', + { + defaultMessage: 'Add monitor', + } +); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_management/monitor_management_page.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_management/monitor_management_page.tsx new file mode 100644 index 0000000000000..c4ca665563f0d --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_management/monitor_management_page.tsx @@ -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 React from 'react'; +import { useTrackPageview } from '@kbn/observability-plugin/public'; +import { useMonitorManagementBreadcrumbs } from './use_breadcrumbs'; + +export const MonitorManagementPage: React.FC = () => { + useTrackPageview({ app: 'synthetics', path: 'manage-monitors' }); + useTrackPageview({ app: 'synthetics', path: 'manage-monitors', delay: 15000 }); + useMonitorManagementBreadcrumbs(); + + return ( + <> +

Monitor Management List page (Monitor Management Page)

+ + ); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_management/use_breadcrumbs.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_management/use_breadcrumbs.ts new file mode 100644 index 0000000000000..be94df18ef7d2 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_management/use_breadcrumbs.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 { i18n } from '@kbn/i18n'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; +import { MONITOR_MANAGEMENT_ROUTE } from '../../../../../common/constants'; +import { PLUGIN } from '../../../../../common/constants/plugin'; + +export const useMonitorManagementBreadcrumbs = () => { + const kibana = useKibana(); + const appPath = kibana.services.application?.getUrlForApp(PLUGIN.SYNTHETICS_PLUGIN_ID) ?? ''; + + useBreadcrumbs([ + { + text: MONITOR_MANAGEMENT_CRUMB, + href: `${appPath}/${MONITOR_MANAGEMENT_ROUTE}`, + }, + ]); +}; + +const MONITOR_MANAGEMENT_CRUMB = i18n.translate( + 'xpack.synthetics.monitorManagementPage.monitorManagementCrumb', + { + defaultMessage: 'Monitor Management', + } +); diff --git a/x-pack/plugins/synthetics/public/components/overview/empty_state/empty_state_error.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/overview/empty_state/empty_state_error.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/empty_state/empty_state_error.tsx rename to x-pack/plugins/synthetics/public/apps/synthetics/components/overview/empty_state/empty_state_error.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/empty_state/empty_state_loading.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/overview/empty_state/empty_state_loading.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/empty_state/empty_state_loading.tsx rename to x-pack/plugins/synthetics/public/apps/synthetics/components/overview/empty_state/empty_state_loading.tsx diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/overview/empty_state/use_has_data.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/overview/empty_state/use_has_data.tsx new file mode 100644 index 0000000000000..3561d841c7564 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/overview/empty_state/use_has_data.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 { useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { getIndexStatus, selectIndexState } from '../../../state'; +import { SyntheticsRefreshContext } from '../../../contexts'; +// import { getDynamicSettings } from '../../../state/actions/dynamic_settings'; + +export const useHasData = () => { + const { loading, error, data } = useSelector(selectIndexState); + const { lastRefresh } = useContext(SyntheticsRefreshContext); + + // const { settings } = useSelector(selectDynamicSettings); // TODO: Add state for dynamicSettings + const settings = { heartbeatIndices: 'synthetics-*' }; + + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(getIndexStatus()); + }, [dispatch, lastRefresh]); + + /* useEffect(() => { + dispatch(getDynamicSettings()); + }, [dispatch]);*/ + + return { + data, + error, + loading, + settings, + }; +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/overview/overview_page.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/overview/overview_page.tsx new file mode 100644 index 0000000000000..4a370fc024423 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/overview/overview_page.tsx @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; +import React from 'react'; +import { useTrackPageview } from '@kbn/observability-plugin/public'; +import { useSyntheticsSettingsContext } from '../../contexts'; +import { useOverviewBreadcrumbs } from './use_breadcrumbs'; + +export const OverviewPage: React.FC = () => { + useTrackPageview({ app: 'synthetics', path: 'overview' }); + useTrackPageview({ app: 'synthetics', path: 'overview', delay: 15000 }); + useOverviewBreadcrumbs(); + const { basePath } = useSyntheticsSettingsContext(); + + return ( + + +

This page should show empty state or overview

+
+ + Monitor Management + + + Add Monitor + +
+ ); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/overview/use_breadcrumbs.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/overview/use_breadcrumbs.ts new file mode 100644 index 0000000000000..d33a0fd3c20cc --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/overview/use_breadcrumbs.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 { i18n } from '@kbn/i18n'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; +import { PLUGIN } from '../../../../../common/constants/plugin'; + +export const useOverviewBreadcrumbs = () => { + const kibana = useKibana(); + const appPath = kibana.services.application?.getUrlForApp(PLUGIN.SYNTHETICS_PLUGIN_ID) ?? ''; + + useBreadcrumbs([ + { + text: OVERVIEW_PAGE_CRUMB, + href: `${appPath}`, + }, + ]); +}; + +export const OVERVIEW_PAGE_CRUMB = i18n.translate('xpack.synthetics.overviewPage.overviewCrumb', { + defaultMessage: 'Overview', +}); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/contexts/index.ts b/x-pack/plugins/synthetics/public/apps/synthetics/contexts/index.ts new file mode 100644 index 0000000000000..6a2e0226671fe --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/contexts/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 './synthetics_refresh_context'; +export * from './synthetics_settings_context'; +export * from './synthetics_theme_context'; +export * from './synthetics_startup_plugins_context'; +export * from './synthetics_data_view_context'; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/contexts/synthetics_data_view_context.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/contexts/synthetics_data_view_context.tsx new file mode 100644 index 0000000000000..af657abd77540 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/contexts/synthetics_data_view_context.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { createContext, useContext } from 'react'; +import { useFetcher } from '@kbn/observability-plugin/public'; +import { DataViewsPublicPluginStart, DataView } from '@kbn/data-views-plugin/public'; +import { useHasData } from '../components/overview/empty_state/use_has_data'; + +export const SyntheticsDataViewContext = createContext({} as DataView); + +export const SyntheticsDataViewContextProvider: React.FC<{ + dataViews: DataViewsPublicPluginStart; +}> = ({ children, dataViews }) => { + const { settings, data: indexStatus } = useHasData(); + + const heartbeatIndices = settings?.heartbeatIndices || ''; + + const { data } = useFetcher>(async () => { + if (heartbeatIndices && indexStatus?.indexExists) { + // this only creates an dateView in memory, not as saved object + return dataViews.create({ title: heartbeatIndices }); + } + }, [heartbeatIndices, indexStatus?.indexExists]); + + return ; +}; + +export const useSyntheticsDataView = () => useContext(SyntheticsDataViewContext); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/contexts/synthetics_refresh_context.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/contexts/synthetics_refresh_context.tsx new file mode 100644 index 0000000000000..696054eeb618b --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/contexts/synthetics_refresh_context.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { createContext, useContext, useMemo, useState } from 'react'; + +interface SyntheticsRefreshContext { + lastRefresh: number; + refreshApp: () => void; +} + +const defaultContext: SyntheticsRefreshContext = { + lastRefresh: 0, + refreshApp: () => { + throw new Error('App refresh was not initialized, set it when you invoke the context'); + }, +}; + +export const SyntheticsRefreshContext = createContext(defaultContext); + +export const SyntheticsRefreshContextProvider: React.FC = ({ children }) => { + const [lastRefresh, setLastRefresh] = useState(Date.now()); + + const refreshApp = () => { + const refreshTime = Date.now(); + setLastRefresh(refreshTime); + }; + + const value = useMemo(() => { + return { lastRefresh, refreshApp }; + }, [lastRefresh]); + + return ; +}; + +export const useSyntheticsRefreshContext = () => useContext(SyntheticsRefreshContext); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/contexts/synthetics_settings_context.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/contexts/synthetics_settings_context.tsx new file mode 100644 index 0000000000000..61231cfde6dfe --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/contexts/synthetics_settings_context.tsx @@ -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 { + AppMountParameters, + ChromeBadge, + ChromeBreadcrumb, + CoreStart, + I18nStart, +} from '@kbn/core/public'; +import React, { createContext, useContext, useMemo } from 'react'; +import { ClientPluginsSetup, ClientPluginsStart } from '../../../plugin'; +import { CLIENT_DEFAULTS, CONTEXT_DEFAULTS } from '../../../../common/constants'; +import { useGetUrlParams } from '../hooks'; + +export interface CommonlyUsedDateRange { + from: string; + to: string; + display: string; +} + +export interface SyntheticsAppProps { + basePath: string; + canSave: boolean; + core: CoreStart; + darkMode: boolean; + i18n: I18nStart; + isApmAvailable: boolean; + isInfraAvailable: boolean; + isLogsAvailable: boolean; + plugins: ClientPluginsSetup; + startPlugins: ClientPluginsStart; + setBadge: (badge?: ChromeBadge) => void; + renderGlobalHelpControls(): void; + commonlyUsedRanges: CommonlyUsedDateRange[]; + setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; + appMountParameters: AppMountParameters; + isDev: boolean; +} + +export interface SyntheticsSettingsContextValues { + basePath: string; + dateRangeStart: string; + dateRangeEnd: string; + isApmAvailable: boolean; + isInfraAvailable: boolean; + isLogsAvailable: boolean; + commonlyUsedRanges?: CommonlyUsedDateRange[]; + isDev?: boolean; +} + +const { BASE_PATH } = CONTEXT_DEFAULTS; + +const { DATE_RANGE_START, DATE_RANGE_END } = CLIENT_DEFAULTS; + +/** + * These are default values for the context. These defaults are typically + * overwritten by the Synthetics App upon its invocation. + */ +const defaultContext: SyntheticsSettingsContextValues = { + basePath: BASE_PATH, + dateRangeStart: DATE_RANGE_START, + dateRangeEnd: DATE_RANGE_END, + isApmAvailable: true, + isInfraAvailable: true, + isLogsAvailable: true, + isDev: false, +}; +export const SyntheticsSettingsContext = createContext(defaultContext); + +export const SyntheticsSettingsContextProvider: React.FC = ({ + children, + ...props +}) => { + const { basePath, isApmAvailable, isInfraAvailable, isLogsAvailable, commonlyUsedRanges, isDev } = + props; + + const { dateRangeStart, dateRangeEnd } = useGetUrlParams(); + + const value = useMemo(() => { + return { + isDev, + basePath, + isApmAvailable, + isInfraAvailable, + isLogsAvailable, + commonlyUsedRanges, + dateRangeStart: dateRangeStart ?? DATE_RANGE_START, + dateRangeEnd: dateRangeEnd ?? DATE_RANGE_END, + }; + }, [ + isDev, + basePath, + isApmAvailable, + isInfraAvailable, + isLogsAvailable, + dateRangeStart, + dateRangeEnd, + commonlyUsedRanges, + ]); + + return ; +}; + +export const useSyntheticsSettingsContext = () => useContext(SyntheticsSettingsContext); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/contexts/synthetics_startup_plugins_context.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/contexts/synthetics_startup_plugins_context.tsx new file mode 100644 index 0000000000000..75ee7b3d6ae67 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/contexts/synthetics_startup_plugins_context.tsx @@ -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 React, { createContext, useContext } from 'react'; +import { ClientPluginsStart } from '../../../plugin'; + +export const SyntheticsStartupPluginsContext = createContext>({}); + +export const SyntheticsStartupPluginsContextProvider: React.FC> = ({ + children, + ...props +}) => ; + +export const useSyntheticsStartPlugins = () => useContext(SyntheticsStartupPluginsContext); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/contexts/synthetics_theme_context.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/contexts/synthetics_theme_context.tsx new file mode 100644 index 0000000000000..32844de9d04c2 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/contexts/synthetics_theme_context.tsx @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { euiLightVars, euiDarkVars } from '@kbn/ui-theme'; +import React, { createContext, useMemo } from 'react'; +import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; +import { DARK_THEME, LIGHT_THEME, PartialTheme, Theme } from '@elastic/charts'; + +export interface SyntheticsAppColors { + danger: string; + dangerBehindText: string; + success: string; + gray: string; + range: string; + mean: string; + warning: string; + lightestShade: string; +} + +export interface SyntheticsThemeContextValues { + colors: SyntheticsAppColors; + chartTheme: { + baseTheme?: Theme; + theme?: PartialTheme; + }; +} + +/** + * These are default values for the context. These defaults are typically + * overwritten by the Synthetics App upon its invocation. + */ +const defaultContext: SyntheticsThemeContextValues = { + colors: { + danger: euiLightVars.euiColorDanger, + dangerBehindText: euiDarkVars.euiColorVis9_behindText, + mean: euiLightVars.euiColorPrimary, + range: euiLightVars.euiFocusBackgroundColor, + success: euiLightVars.euiColorSuccess, + warning: euiLightVars.euiColorWarning, + gray: euiLightVars.euiColorLightShade, + lightestShade: euiLightVars.euiColorLightestShade, + }, + chartTheme: { + baseTheme: LIGHT_THEME, + theme: EUI_CHARTS_THEME_LIGHT.theme, + }, +}; + +export const SyntheticsThemeContext = createContext(defaultContext); + +interface ThemeContextProps { + darkMode: boolean; +} + +export const SyntheticsThemeContextProvider: React.FC = ({ + darkMode, + children, +}) => { + let colors: SyntheticsAppColors; + if (darkMode) { + colors = { + danger: euiDarkVars.euiColorVis9, + dangerBehindText: euiDarkVars.euiColorVis9_behindText, + mean: euiDarkVars.euiColorPrimary, + gray: euiDarkVars.euiColorLightShade, + range: euiDarkVars.euiFocusBackgroundColor, + success: euiDarkVars.euiColorSuccess, + warning: euiDarkVars.euiColorWarning, + lightestShade: euiDarkVars.euiColorLightestShade, + }; + } else { + colors = { + danger: euiLightVars.euiColorVis9, + dangerBehindText: euiLightVars.euiColorVis9_behindText, + mean: euiLightVars.euiColorPrimary, + gray: euiLightVars.euiColorLightShade, + range: euiLightVars.euiFocusBackgroundColor, + success: euiLightVars.euiColorSuccess, + warning: euiLightVars.euiColorWarning, + lightestShade: euiLightVars.euiColorLightestShade, + }; + } + const value = useMemo(() => { + return { + colors, + chartTheme: { + baseTheme: darkMode ? DARK_THEME : LIGHT_THEME, + theme: darkMode ? EUI_CHARTS_THEME_DARK.theme : EUI_CHARTS_THEME_LIGHT.theme, + }, + }; + }, [colors, darkMode]); + + return ; +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/index.ts b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/index.ts new file mode 100644 index 0000000000000..15079dc68823b --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './use_url_params'; +export * from './use_breadcrumbs'; +export * from './use_telemetry'; +export * from '../../../hooks/use_breakpoints'; +export * from './use_service_allowed'; +export * from './use_no_data_config'; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_breadcrumbs.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_breadcrumbs.test.tsx new file mode 100644 index 0000000000000..d2639da47e79e --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_breadcrumbs.test.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 { ChromeBreadcrumb } from '@kbn/core/public'; +import { render } from '../utils/testing'; +import React from 'react'; +import { Route } from 'react-router-dom'; +import { OVERVIEW_ROUTE } from '../../../../common/constants'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { + SyntheticsUrlParams, + getSupportedUrlParams, +} from '../utils/url_params/get_supported_url_params'; +import { makeBaseBreadcrumb, useBreadcrumbs } from './use_breadcrumbs'; + +describe('useBreadcrumbs', () => { + it('sets the given breadcrumbs', () => { + const [getBreadcrumbs, core] = mockCore(); + + const expectedCrumbs: ChromeBreadcrumb[] = [ + { text: 'Crumb: ', href: 'http://href.example.net' }, + { text: 'Crumb II: Son of Crumb', href: 'http://href2.example.net' }, + ]; + + const Component = () => { + useBreadcrumbs(expectedCrumbs); + return <>Hello; + }; + + render( + + + + + + ); + + const urlParams: SyntheticsUrlParams = getSupportedUrlParams({}); + expect(JSON.stringify(getBreadcrumbs())).toEqual( + JSON.stringify( + makeBaseBreadcrumb('/app/synthetics', '/app/observability', urlParams).concat( + expectedCrumbs + ) + ) + ); + }); +}); + +const mockCore: () => [() => ChromeBreadcrumb[], any] = () => { + let breadcrumbObj: ChromeBreadcrumb[] = []; + const get = () => { + return breadcrumbObj; + }; + const core = { + application: { + getUrlForApp: (app: string) => + app === 'synthetics' ? '/app/synthetics' : '/app/observability', + navigateToUrl: jest.fn(), + }, + chrome: { + setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => { + breadcrumbObj = newBreadcrumbs; + }, + }, + }; + + return [get, core]; +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_breadcrumbs.ts b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_breadcrumbs.ts new file mode 100644 index 0000000000000..0902481c51a9c --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_breadcrumbs.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ChromeBreadcrumb } from '@kbn/core/public'; +import { i18n } from '@kbn/i18n'; +import { MouseEvent, useEffect } from 'react'; +import { EuiBreadcrumb } from '@elastic/eui'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { SyntheticsUrlParams, stringifyUrlParams } from '../utils/url_params'; +import { useUrlParams } from './use_url_params'; +import { PLUGIN } from '../../../../common/constants/plugin'; + +const EMPTY_QUERY = '?'; + +function handleBreadcrumbClick( + breadcrumbs: ChromeBreadcrumb[], + navigateToHref?: (url: string) => Promise +) { + return breadcrumbs.map((bc) => ({ + ...bc, + ...(bc.href + ? { + onClick: (event: MouseEvent) => { + if (navigateToHref && bc.href) { + event.preventDefault(); + navigateToHref(bc.href); + } + }, + } + : {}), + })); +} + +export const makeBaseBreadcrumb = ( + uptimePath: string, + observabilityPath: string, + params?: SyntheticsUrlParams +): [EuiBreadcrumb, EuiBreadcrumb] => { + if (params) { + const crumbParams: Partial = { ...params }; + + delete crumbParams.statusFilter; + const query = stringifyUrlParams(crumbParams, true); + uptimePath += query === EMPTY_QUERY ? '' : query; + } + + return [ + { + text: i18n.translate('xpack.synthetics.breadcrumbs.observabilityText', { + defaultMessage: 'Observability', + }), + href: observabilityPath, + }, + { + text: i18n.translate('xpack.synthetics.breadcrumbs.overviewBreadcrumbText', { + defaultMessage: 'Synthetics', + }), + href: uptimePath, + }, + ]; +}; + +export const useBreadcrumbs = (extraCrumbs: ChromeBreadcrumb[]) => { + const params = useUrlParams()[0](); + const kibana = useKibana(); + const setBreadcrumbs = kibana.services.chrome?.setBreadcrumbs; + const uptimePath = kibana.services.application?.getUrlForApp(PLUGIN.SYNTHETICS_PLUGIN_ID) ?? ''; + const observabilityPath = + kibana.services.application?.getUrlForApp('observability-overview') ?? ''; + const navigate = kibana.services.application?.navigateToUrl; + + useEffect(() => { + if (setBreadcrumbs) { + setBreadcrumbs( + handleBreadcrumbClick( + makeBaseBreadcrumb(uptimePath, observabilityPath, params).concat(extraCrumbs), + navigate + ) + ); + } + }, [uptimePath, observabilityPath, extraCrumbs, navigate, params, setBreadcrumbs]); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_no_data_config.ts b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_no_data_config.ts new file mode 100644 index 0000000000000..6243d2f5d8c8a --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_no_data_config.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 { i18n } from '@kbn/i18n'; +import { useContext } from 'react'; +import { useSelector } from 'react-redux'; +import { KibanaPageTemplateProps, useKibana } from '@kbn/kibana-react-plugin/public'; +import { SyntheticsSettingsContext } from '../contexts'; +import { ClientPluginsStart } from '../../../plugin'; +import { selectIndexState } from '../state'; + +export function useNoDataConfig(): KibanaPageTemplateProps['noDataConfig'] { + const { basePath } = useContext(SyntheticsSettingsContext); + + const { + services: { docLinks }, + } = useKibana(); + + const { data } = useSelector(selectIndexState); + + // Returns no data config when there is no historical data + if (data && !data.indexExists) { + return { + solution: i18n.translate('xpack.synthetics.noDataConfig.solutionName', { + defaultMessage: 'Observability', + }), + actions: { + beats: { + title: i18n.translate('xpack.synthetics.noDataConfig.beatsCard.title', { + defaultMessage: 'Add monitors with Heartbeat', + }), + description: i18n.translate('xpack.synthetics.noDataConfig.beatsCard.description', { + defaultMessage: + 'Proactively monitor the availability of your sites and services. Receive alerts and resolve issues faster to optimize your users experience.', + }), + href: basePath + `/app/home#/tutorial/uptimeMonitors`, + }, + }, + docsLink: docLinks!.links.observability.guide, + }; + } +} +// TODO: Change no data config for Synthetics diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_service_allowed.ts b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_service_allowed.ts new file mode 100644 index 0000000000000..12a2ed7043973 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_service_allowed.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 { useDispatch } from 'react-redux'; +import { useEffect } from 'react'; +// import { syntheticsServiceAllowedSelector } from '../../../state/selectors'; +// import { getSyntheticsServiceAllowed } from '../../../state/actions'; + +export const useSyntheticsServiceAllowed = () => { + const dispatch = useDispatch(); + + useEffect(() => { + // dispatch(getSyntheticsServiceAllowed.get()); + }, [dispatch]); + + // return useSelector(syntheticsServiceAllowedSelector); + // TODO Implement for Synthetics App + return { isAllowed: true, signupUrl: 'https://example.com', loading: false }; +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_telemetry.ts b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_telemetry.ts new file mode 100644 index 0000000000000..64ecabaff5d5a --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_telemetry.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect } from 'react'; +import { useGetUrlParams } from './use_url_params'; +import { apiService } from '../../../utils/api_service'; +// import { API_URLS } from '../../../common/constants'; + +export enum SyntheticsPage { + Overview = 'Overview', + Monitor = 'Monitor', + MonitorAdd = 'AddMonitor', + MonitorEdit = 'EditMonitor', + MonitorManagement = 'MonitorManagement', + Settings = 'Settings', + Certificates = 'Certificates', + StepDetail = 'StepDetail', + SyntheticCheckStepsPage = 'SyntheticCheckStepsPage', + NotFound = '__not-found__', +} + +export const useSyntheticsTelemetry = (page?: SyntheticsPage) => { + const { dateRangeStart, dateRangeEnd, autorefreshInterval, autorefreshIsPaused } = + useGetUrlParams(); + + useEffect(() => { + if (!apiService.http) throw new Error('Core http services are not defined'); + + /* + TODO: Add/Modify telemetry endpoint for synthetics + const params = { + page, + autorefreshInterval: autorefreshInterval / 1000, // divide by 1000 to keep it in secs + dateStart: dateRangeStart, + dateEnd: dateRangeEnd, + autoRefreshEnabled: !autorefreshIsPaused, + };*/ + setTimeout(() => { + // apiService.post(API_URLS.LOG_PAGE_VIEW, params); + }, 100); + }, [autorefreshInterval, autorefreshIsPaused, dateRangeEnd, dateRangeStart, page]); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_url_params.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_url_params.test.tsx new file mode 100644 index 0000000000000..d1e9ceae998b5 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_url_params.test.tsx @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import DateMath from '@kbn/datemath'; +import userEvent from '@testing-library/user-event'; +import { render } from '../utils/testing'; +import React, { useState, Fragment } from 'react'; +import { useUrlParams, SyntheticsUrlParamsHook } from './use_url_params'; +import { SyntheticsRefreshContext } from '../contexts'; + +interface MockUrlParamsComponentProps { + hook: SyntheticsUrlParamsHook; + updateParams?: { [key: string]: any }; +} + +const UseUrlParamsTestComponent = ({ hook, updateParams }: MockUrlParamsComponentProps) => { + const [params, setParams] = useState({}); + const [getUrlParams, updateUrlParams] = hook(); + const queryParams = getUrlParams(); + return ( + + {Object.keys(params).length > 0 ?
{JSON.stringify(params)}
: null} + + +
+ ); +}; + +describe('useUrlParams', () => { + let dateMathSpy: any; + const MOCK_DATE_VALUE = 20; + + beforeEach(() => { + dateMathSpy = jest.spyOn(DateMath, 'parse'); + dateMathSpy.mockReturnValue(MOCK_DATE_VALUE); + }); + + it('accepts router props, updates URL params, and returns the current params', async () => { + const { findByText, history } = render( + + + + ); + + const pushSpy = jest.spyOn(history, 'push'); + + const setUrlParamsButton = await findByText('Set url params'); + userEvent.click(setUrlParamsButton); + expect(pushSpy).toHaveBeenCalledWith({ + pathname: '/', + search: 'dateRangeEnd=now&dateRangeStart=now-12d', + }); + pushSpy.mockClear(); + }); +}); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_url_params.ts b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_url_params.ts new file mode 100644 index 0000000000000..ec31c711cffc7 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_url_params.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useEffect } from 'react'; +import { stringify } from 'query-string'; +import { useLocation, useHistory } from 'react-router-dom'; +import { useDispatch, useSelector } from 'react-redux'; +import { SyntheticsUrlParams, getSupportedUrlParams } from '../utils/url_params'; + +// TODO: Create the following imports for new Synthetics App +import { selectedFiltersSelector } from '../../../legacy_uptime/state/selectors'; +import { setSelectedFilters } from '../../../legacy_uptime/state/actions/selected_filters'; +import { getFiltersFromMap } from '../../../legacy_uptime/hooks/use_selected_filters'; +import { getParsedParams } from '../../../legacy_uptime/lib/helper/parse_search'; + +export type GetUrlParams = () => SyntheticsUrlParams; +export type UpdateUrlParams = (updatedParams: { + [key: string]: string | number | boolean | undefined; +}) => void; + +export type SyntheticsUrlParamsHook = () => [GetUrlParams, UpdateUrlParams]; + +export const useGetUrlParams: GetUrlParams = () => { + const { search } = useLocation(); + + return getSupportedUrlParams(getParsedParams(search)); +}; + +const getMapFromFilters = (value: any): Map | undefined => { + try { + return new Map(JSON.parse(value)); + } catch { + return undefined; + } +}; + +export const useUrlParams: SyntheticsUrlParamsHook = () => { + const { pathname, search } = useLocation(); + const history = useHistory(); + const dispatch = useDispatch(); + const selectedFilters = useSelector(selectedFiltersSelector); + const { filters } = useGetUrlParams(); + + useEffect(() => { + if (selectedFilters === null) { + const filterMap = getMapFromFilters(filters); + if (filterMap) { + dispatch(setSelectedFilters(getFiltersFromMap(filterMap))); + } + } + }, [dispatch, filters, selectedFilters]); + + const updateUrlParams: UpdateUrlParams = useCallback( + (updatedParams) => { + const currentParams = getParsedParams(search); + const mergedParams = { + ...currentParams, + ...updatedParams, + }; + + const updatedSearch = stringify( + // drop any parameters that have no value + Object.keys(mergedParams).reduce((params, key) => { + const value = mergedParams[key]; + if (value === undefined || value === '') { + return params; + } + return { + ...params, + [key]: value, + }; + }, {}) + ); + + // only update the URL if the search has actually changed + if (search !== updatedSearch) { + history.push({ pathname, search: updatedSearch }); + } + const filterMap = getMapFromFilters(mergedParams.filters); + if (!filterMap) { + dispatch(setSelectedFilters(null)); + } else { + dispatch(setSelectedFilters(getFiltersFromMap(filterMap))); + } + }, + [dispatch, history, pathname, search] + ); + + return [useGetUrlParams, updateUrlParams]; +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/render_app.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/render_app.tsx new file mode 100644 index 0000000000000..76b86c0b82383 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/render_app.tsx @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { i18n as i18nFormatter } from '@kbn/i18n'; +import { AppMountParameters, CoreStart } from '@kbn/core/public'; +import { SyntheticsAppProps } from './contexts'; +import { getIntegratedAppAvailability } from './utils/adapters'; +import { + DEFAULT_DARK_MODE, + DEFAULT_TIMEPICKER_QUICK_RANGES, + INTEGRATED_SOLUTIONS, +} from '../../../common/constants'; +import { SyntheticsApp } from './synthetics_app'; +import { ClientPluginsSetup, ClientPluginsStart } from '../../plugin'; + +export function renderApp( + core: CoreStart, + plugins: ClientPluginsSetup, + startPlugins: ClientPluginsStart, + appMountParameters: AppMountParameters, + isDev: boolean +) { + const { + application: { capabilities }, + chrome: { setBadge, setHelpExtension }, + docLinks, + http: { basePath }, + i18n, + } = core; + + const { apm, infrastructure, logs } = getIntegratedAppAvailability( + capabilities, + INTEGRATED_SOLUTIONS + ); + + const canSave = (capabilities.uptime.save ?? false) as boolean; // TODO: Determine for synthetics + + const props: SyntheticsAppProps = { + isDev, + plugins, + canSave, + core, + i18n, + startPlugins, + basePath: basePath.get(), + darkMode: core.uiSettings.get(DEFAULT_DARK_MODE), + commonlyUsedRanges: core.uiSettings.get(DEFAULT_TIMEPICKER_QUICK_RANGES), + isApmAvailable: apm, + isInfraAvailable: infrastructure, + isLogsAvailable: logs, + renderGlobalHelpControls: () => + setHelpExtension({ + appName: i18nFormatter.translate('xpack.synthetics.header.appName', { + defaultMessage: 'Synthetics', + }), + links: [ + { + linkType: 'documentation', + href: `${docLinks.links.observability.monitorUptime}`, // TODO: Include synthetics link + }, + { + linkType: 'discuss', + href: 'https://discuss.elastic.co/c/uptime', // TODO: Include synthetics link + }, + ], + }), + setBadge, + appMountParameters, + setBreadcrumbs: core.chrome.setBreadcrumbs, + }; + + ReactDOM.render(, appMountParameters.element); + + return () => { + ReactDOM.unmountComponentAtNode(appMountParameters.element); + }; +} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/routes.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/routes.tsx new file mode 100644 index 0000000000000..7f04b3992885b --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/routes.tsx @@ -0,0 +1,184 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useEffect } from 'react'; +import { EuiPageTemplateProps, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { Route, Switch } from 'react-router-dom'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; +import { APP_WRAPPER_CLASS } from '@kbn/core/public'; +import { useInspectorContext } from '@kbn/observability-plugin/public'; +import { MonitorAddEditPage } from './components/monitor_add_edit/monitor_add_edit_page'; +import { OverviewPage } from './components/overview/overview_page'; +import { SyntheticsPageTemplateComponent } from './components/common/pages/synthetics_page_template'; +import { NotFoundPage } from './components/common/pages/not_found'; +import { ServiceAllowedWrapper } from './components/common/wrappers/service_allowed_wrapper'; +import { + MONITOR_ADD_ROUTE, + MONITOR_MANAGEMENT_ROUTE, + OVERVIEW_ROUTE, +} from '../../../common/constants'; +import { MonitorManagementPage } from './components/monitor_management/monitor_management_page'; +import { apiService } from '../../utils/api_service'; +import { SyntheticsPage, useSyntheticsTelemetry } from './hooks/use_telemetry'; + +type RouteProps = { + path: string; + component: React.FC; + dataTestSubj: string; + title: string; + telemetryId: SyntheticsPage; + pageHeader: { + pageTitle: string | JSX.Element; + children?: JSX.Element; + rightSideItems?: JSX.Element[]; + }; +} & EuiPageTemplateProps; + +const baseTitle = i18n.translate('xpack.synthetics.routes.baseTitle', { + defaultMessage: 'Synthetics - Kibana', +}); + +export const MONITOR_MANAGEMENT_LABEL = i18n.translate( + 'xpack.synthetics.monitorManagement.heading', + { + defaultMessage: 'Monitor Management', + } +); + +const getRoutes = (): RouteProps[] => { + return [ + { + title: i18n.translate('xpack.synthetics.overviewRoute.title', { + defaultMessage: 'Synthetics Overview | {baseTitle}', + values: { baseTitle }, + }), + path: OVERVIEW_ROUTE, + component: () => , + dataTestSubj: 'syntheticsOverviewPage', + telemetryId: SyntheticsPage.Overview, + pageHeader: { + pageTitle: ( + + + + + + ), + rightSideItems: [ + /* */ + ], + }, + }, + { + title: i18n.translate('xpack.synthetics.monitorManagementRoute.title', { + defaultMessage: 'Monitor Management | {baseTitle}', + values: { baseTitle }, + }), + path: MONITOR_MANAGEMENT_ROUTE, + component: () => ( + + + + ), + dataTestSubj: 'syntheticsMonitorManagementPage', + telemetryId: SyntheticsPage.MonitorManagement, + pageHeader: { + pageTitle: ( + + + + + + ), + rightSideItems: [ + /* */ + ], + }, + }, + { + title: i18n.translate('xpack.synthetics.addMonitorRoute.title', { + defaultMessage: 'Add Monitor | {baseTitle}', + values: { baseTitle }, + }), + path: MONITOR_ADD_ROUTE, + component: () => ( + + + + ), + dataTestSubj: 'syntheticsMonitorAddPage', + telemetryId: SyntheticsPage.MonitorAdd, + pageHeader: { + pageTitle: ( + + ), + }, + // bottomBar: , + bottomBarProps: { paddingSize: 'm' as const }, + }, + ]; +}; + +const RouteInit: React.FC> = ({ + path, + title, + telemetryId, +}) => { + useSyntheticsTelemetry(telemetryId); + useEffect(() => { + document.title = title; + }, [path, title]); + return null; +}; + +export const PageRouter: FC = () => { + const routes = getRoutes(); + const { addInspectorRequest } = useInspectorContext(); + + apiService.addInspectorRequest = addInspectorRequest; + + return ( + + {routes.map( + ({ + title, + path, + component: RouteComponent, + dataTestSubj, + telemetryId, + pageHeader, + ...pageTemplateProps + }) => ( + +
+ {/* TODO: See if the callout is needed for Synthetics App as well */} + + + + +
+
+ ) + )} + +
+ ); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/index.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/index.ts new file mode 100644 index 0000000000000..153eab7415cd5 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { store, storage } from './store'; + +export type { RooState as AppState } from './root_reducer'; + +export * from './ui'; +export * from './index_status'; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/index_status/actions.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/index_status/actions.ts new file mode 100644 index 0000000000000..0b359a52502fa --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/index_status/actions.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 { IHttpFetchError } from '@kbn/core/public'; +import { createAction } from '@reduxjs/toolkit'; +import { StatesIndexStatus } from '../../../../../common/runtime_types'; + +export const getIndexStatus = createAction('[INDEX STATUS] GET'); +export const getIndexStatusSuccess = createAction('[INDEX STATUS] GET SUCCESS'); +export const getIndexStatusFail = createAction('[INDEX STATUS] GET FAIL'); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/index_status/api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/index_status/api.ts new file mode 100644 index 0000000000000..ba6ded899f9c4 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/index_status/api.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 { API_URLS } from '../../../../../common/constants'; +import { StatesIndexStatus, StatesIndexStatusType } from '../../../../../common/runtime_types'; +import { apiService } from '../../../../utils/api_service'; + +export const fetchIndexStatus = async (): Promise => { + return await apiService.get(API_URLS.INDEX_STATUS, undefined, StatesIndexStatusType); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/index_status/effects.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/index_status/effects.ts new file mode 100644 index 0000000000000..cf0eaf83e5c2c --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/index_status/effects.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 { takeLeading } from 'redux-saga/effects'; +import { getIndexStatus, getIndexStatusSuccess, getIndexStatusFail } from './actions'; +import { fetchEffectFactory } from '../utils/fetch_effect'; +import { fetchIndexStatus } from './api'; + +export function* fetchIndexStatusEffect() { + yield takeLeading( + getIndexStatus, + fetchEffectFactory(fetchIndexStatus, getIndexStatusSuccess, getIndexStatusFail) + ); +} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/index_status/index.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/index_status/index.ts new file mode 100644 index 0000000000000..e140145bb324a --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/index_status/index.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 { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public'; +import { createReducer } from '@reduxjs/toolkit'; +import { StatesIndexStatus } from '../../../../../common/runtime_types'; + +import { getIndexStatus, getIndexStatusSuccess, getIndexStatusFail } from './actions'; + +export interface IndexStatusState { + data: StatesIndexStatus | null; + loading: boolean; + error: IHttpFetchError | null; +} + +const initialState: IndexStatusState = { + data: null, + loading: false, + error: null, +}; + +export const indexStatusReducer = createReducer(initialState, (builder) => { + builder + .addCase(getIndexStatus, (state) => { + state.loading = true; + }) + .addCase(getIndexStatusSuccess, (state, action) => { + state.data = action.payload; + state.loading = false; + }) + .addCase(getIndexStatusFail, (state, action) => { + state.error = action.payload as IHttpFetchError; + state.loading = false; + }); +}); + +export * from './actions'; +export * from './effects'; +export * from './selectors'; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/index_status/selectors.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/index_status/selectors.ts new file mode 100644 index 0000000000000..7d7da80b692a2 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/index_status/selectors.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 { createSelector } from 'reselect'; +import type { RooState } from '../root_reducer'; + +const getState = (appState: RooState) => appState.indexStatus; +export const selectIndexState = createSelector(getState, (state) => state); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/root_effect.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/root_effect.ts new file mode 100644 index 0000000000000..7d1aaa60aa4f3 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/root_effect.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { all, fork } from 'redux-saga/effects'; +import { fetchIndexStatusEffect } from './index_status'; + +export const rootEffect = function* root(): Generator { + yield all([fork(fetchIndexStatusEffect)]); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/root_reducer.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/root_reducer.ts new file mode 100644 index 0000000000000..a57c0af9e6fdb --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/root_reducer.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 { combineReducers } from '@reduxjs/toolkit'; + +import { uiReducer } from './ui'; +import { indexStatusReducer } from './index_status'; + +export const rootReducer = combineReducers({ + ui: uiReducer, + indexStatus: indexStatusReducer, +}); + +export type RooState = ReturnType; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/store.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/store.ts new file mode 100644 index 0000000000000..19320f0102a99 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/store.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 { configureStore } from '@reduxjs/toolkit'; +import createSagaMiddleware from 'redux-saga'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; +import { rootEffect } from './root_effect'; +import { rootReducer } from './root_reducer'; + +const sagaMW = createSagaMiddleware(); + +export const store = configureStore({ + reducer: rootReducer, + middleware: (getDefaultMiddleware) => getDefaultMiddleware({ thunk: false }).concat(sagaMW), + devTools: process.env.NODE_ENV !== 'production', + preloadedState: {}, + enhancers: [], +}); + +sagaMW.run(rootEffect); + +export const storage = new Storage(window.localStorage); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/ui/actions.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/ui/actions.ts new file mode 100644 index 0000000000000..d613b3f3d3509 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/ui/actions.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createAction } from '@reduxjs/toolkit'; + +export interface PopoverState { + id: string; + open: boolean; +} + +export const setAlertFlyoutVisible = createAction('[UI] TOGGLE ALERT FLYOUT'); + +export const setAlertFlyoutType = createAction('[UI] SET ALERT FLYOUT TYPE'); + +export const setBasePath = createAction('[UI] SET BASE PATH'); + +export const setEsKueryString = createAction('[UI] SET ES KUERY STRING'); + +export const setSearchTextAction = createAction('[UI] SET SEARCH'); + +export const toggleIntegrationsPopover = createAction( + '[UI] TOGGLE INTEGRATION POPOVER STATE' +); + +export const setSelectedMonitorId = createAction('[UI] SET MONITOR ID'); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/ui/index.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/ui/index.ts new file mode 100644 index 0000000000000..618e6dd524767 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/ui/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 { createReducer } from '@reduxjs/toolkit'; + +import { + PopoverState, + toggleIntegrationsPopover, + setBasePath, + setEsKueryString, + setAlertFlyoutType, + setAlertFlyoutVisible, + setSearchTextAction, + setSelectedMonitorId, +} from './actions'; + +export interface UiState { + alertFlyoutVisible: boolean; + alertFlyoutType?: string; + basePath: string; + esKuery: string; + searchText: string; + integrationsPopoverOpen: PopoverState | null; + monitorId: string; +} + +const initialState: UiState = { + alertFlyoutVisible: false, + basePath: '', + esKuery: '', + searchText: '', + integrationsPopoverOpen: null, + monitorId: '', +}; + +export const uiReducer = createReducer(initialState, (builder) => { + builder + .addCase(toggleIntegrationsPopover, (state, action) => { + state.integrationsPopoverOpen = action.payload; + }) + .addCase(setAlertFlyoutVisible, (state, action) => { + state.alertFlyoutVisible = action.payload ?? !state.alertFlyoutVisible; + }) + .addCase(setAlertFlyoutType, (state, action) => { + state.alertFlyoutType = action.payload; + }) + .addCase(setBasePath, (state, action) => { + state.basePath = action.payload; + }) + .addCase(setEsKueryString, (state, action) => { + state.esKuery = action.payload; + }) + .addCase(setSearchTextAction, (state, action) => { + state.searchText = action.payload; + }) + .addCase(setSelectedMonitorId, (state, action) => { + state.monitorId = action.payload; + }); +}); + +export * from './actions'; +export * from './selectors'; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/ui/selectors.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/ui/selectors.ts new file mode 100644 index 0000000000000..52d9841cde961 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/ui/selectors.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 { createSelector } from 'reselect'; +import type { RooState } from '../root_reducer'; + +const uiStateSelector = (appState: RooState) => appState.ui; +export const selectBasePath = createSelector(uiStateSelector, ({ basePath }) => basePath); + +export const selectIsIntegrationsPopupOpen = createSelector( + uiStateSelector, + ({ integrationsPopoverOpen }) => integrationsPopoverOpen +); + +export const selectAlertFlyoutVisibility = createSelector( + uiStateSelector, + ({ alertFlyoutVisible }) => alertFlyoutVisible +); + +export const selectAlertFlyoutType = createSelector( + uiStateSelector, + ({ alertFlyoutType }) => alertFlyoutType +); + +export const selectEsKuery = createSelector(uiStateSelector, ({ esKuery }) => esKuery); + +export const selectSearchText = createSelector(uiStateSelector, ({ searchText }) => searchText); + +export const selectMonitorId = createSelector(uiStateSelector, ({ monitorId }) => monitorId); diff --git a/x-pack/plugins/synthetics/public/state/effects/fetch_effect.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/utils/fetch_effect.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/effects/fetch_effect.ts rename to x-pack/plugins/synthetics/public/apps/synthetics/state/utils/fetch_effect.ts diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/synthetics_app.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/synthetics_app.tsx new file mode 100644 index 0000000000000..07fb3604abd42 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/synthetics_app.tsx @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useEffect } from 'react'; +import { Provider as ReduxProvider } from 'react-redux'; +import { Router } from 'react-router-dom'; +import { EuiErrorBoundary } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { APP_WRAPPER_CLASS } from '@kbn/core/public'; +import { + KibanaContextProvider, + KibanaThemeProvider, + RedirectAppLinks, +} from '@kbn/kibana-react-plugin/public'; +import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; +import { InspectorContextProvider } from '@kbn/observability-plugin/public'; +import { SyntheticsAppProps } from './contexts'; + +import { + SyntheticsRefreshContextProvider, + SyntheticsSettingsContextProvider, + SyntheticsThemeContextProvider, + SyntheticsStartupPluginsContextProvider, + SyntheticsDataViewContextProvider, +} from './contexts'; + +import { PageRouter } from './routes'; +import { store, storage, setBasePath } from './state'; +import { kibanaService } from '../../utils/kibana_service'; +import { ActionMenu } from './components/common/header/action_menu'; + +const Application = (props: SyntheticsAppProps) => { + const { + basePath, + canSave, + core, + darkMode, + i18n: i18nCore, + plugins, + renderGlobalHelpControls, + setBadge, + startPlugins, + appMountParameters, + } = props; + + useEffect(() => { + renderGlobalHelpControls(); + setBadge( + !canSave + ? { + text: i18n.translate('xpack.synthetics.badge.readOnly.text', { + defaultMessage: 'Read only', + }), + tooltip: i18n.translate('xpack.synthetics.badge.readOnly.tooltip', { + defaultMessage: 'Unable to save', + }), + iconType: 'glasses', + } + : undefined + ); + }, [canSave, renderGlobalHelpControls, setBadge]); + + kibanaService.core = core; + kibanaService.theme = props.appMountParameters.theme$; + + store.dispatch(setBasePath(basePath)); + + return ( + + + + + + + + + + + + +
+ + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+ ); +}; + +export const SyntheticsApp = (props: SyntheticsAppProps) => ; diff --git a/x-pack/plugins/synthetics/public/lib/adapters/framework/capabilities_adapter.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/adapters/capabilities_adapter.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/adapters/framework/capabilities_adapter.ts rename to x-pack/plugins/synthetics/public/apps/synthetics/utils/adapters/capabilities_adapter.ts diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/adapters/index.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/adapters/index.ts new file mode 100644 index 0000000000000..2d0e4b7b2ba6f --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/adapters/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 './capabilities_adapter'; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/syncthetics_store.mock.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/syncthetics_store.mock.ts new file mode 100644 index 0000000000000..adf2a15e70fa6 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/syncthetics_store.mock.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 { AppState } from '../../../state'; + +/** + * NOTE: This variable name MUST start with 'mock*' in order for + * Jest to accept its use within a jest.mock() + */ +export const mockState: AppState = { + ui: { + alertFlyoutVisible: false, + basePath: 'yyz', + esKuery: '', + integrationsPopoverOpen: null, + searchText: '', + monitorId: '', + }, + indexStatus: { + data: null, + error: null, + loading: false, + }, +}; + +// TODO: Complete mock state diff --git a/x-pack/plugins/synthetics/public/lib/__mocks__/uptime_plugin_start_mock.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_plugin_start_mock.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/__mocks__/uptime_plugin_start_mock.ts rename to x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_plugin_start_mock.ts diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/get_supported_url_params.test.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/get_supported_url_params.test.ts new file mode 100644 index 0000000000000..89419ca61ed43 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/get_supported_url_params.test.ts @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import DateMath from '@kbn/datemath'; +import { getSupportedUrlParams } from '../url_params'; +import { CLIENT_DEFAULTS } from '../../../../../common/constants'; + +describe('getSupportedUrlParams', () => { + let dateMathSpy: any; + const MOCK_DATE_VALUE = 20; + + beforeEach(() => { + dateMathSpy = jest.spyOn(DateMath, 'parse'); + dateMathSpy.mockReturnValue(MOCK_DATE_VALUE); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('returns custom values', () => { + const customValues = { + autorefreshInterval: '23', + autorefreshIsPaused: 'false', + dateRangeStart: 'now-15m', + dateRangeEnd: 'now', + monitorListPageIndex: '23', + monitorListPageSize: '50', + monitorListSortDirection: 'desc', + monitorListSortField: 'monitor.status', + search: 'monitor.status: down', + selectedPingStatus: 'up', + }; + + const expected = { + absoluteDateRangeEnd: 20, + absoluteDateRangeStart: 20, + autorefreshInterval: 23, + autorefreshIsPaused: false, + dateRangeEnd: 'now', + dateRangeStart: 'now-15m', + search: 'monitor.status: down', + }; + + const result = getSupportedUrlParams(customValues); + expect(result).toMatchObject(expected); + }); + + it('returns default values', () => { + const { + AUTOREFRESH_INTERVAL, + AUTOREFRESH_IS_PAUSED, + DATE_RANGE_START, + DATE_RANGE_END, + FILTERS, + SEARCH, + STATUS_FILTER, + } = CLIENT_DEFAULTS; + const result = getSupportedUrlParams({}); + expect(result).toEqual({ + absoluteDateRangeStart: MOCK_DATE_VALUE, + absoluteDateRangeEnd: MOCK_DATE_VALUE, + autorefreshInterval: AUTOREFRESH_INTERVAL, + autorefreshIsPaused: AUTOREFRESH_IS_PAUSED, + dateRangeStart: DATE_RANGE_START, + dateRangeEnd: DATE_RANGE_END, + excludedFilters: '', + filters: FILTERS, + focusConnectorField: false, + pagination: undefined, + search: SEARCH, + statusFilter: STATUS_FILTER, + query: '', + }); + }); + + it('returns the first item for string arrays', () => { + const result = getSupportedUrlParams({ + dateRangeStart: ['now-18d', 'now-11d', 'now-5m'], + }); + + const expected = { + absoluteDateRangeEnd: 20, + absoluteDateRangeStart: 20, + autorefreshInterval: 60000, + }; + + expect(result).toMatchObject(expected); + }); + + it('provides defaults for undefined values', () => { + const result = getSupportedUrlParams({ + dateRangeStart: undefined, + }); + + const expected = { + absoluteDateRangeStart: 20, + }; + + expect(result).toMatchObject(expected); + }); + + it('provides defaults for empty string array values', () => { + const result = getSupportedUrlParams({ + dateRangeStart: [], + }); + + const expected = { + absoluteDateRangeStart: 20, + }; + + expect(result).toMatchObject(expected); + }); +}); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/get_supported_url_params.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/get_supported_url_params.ts new file mode 100644 index 0000000000000..c9badbcf7f728 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/get_supported_url_params.ts @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { parseIsPaused } from '../url_params/parse_is_paused'; +import { parseUrlInt } from '../url_params/parse_url_int'; +import { CLIENT_DEFAULTS } from '../../../../../common/constants'; +import { parseAbsoluteDate } from '../url_params/parse_absolute_date'; + +export interface SyntheticsUrlParams { + absoluteDateRangeStart: number; + absoluteDateRangeEnd: number; + autorefreshInterval: number; + autorefreshIsPaused: boolean; + dateRangeStart: string; + dateRangeEnd: string; + pagination?: string; + filters: string; + excludedFilters: string; + search: string; + statusFilter: string; + focusConnectorField?: boolean; + query?: string; +} + +const { + ABSOLUTE_DATE_RANGE_START, + ABSOLUTE_DATE_RANGE_END, + AUTOREFRESH_INTERVAL, + AUTOREFRESH_IS_PAUSED, + DATE_RANGE_START, + DATE_RANGE_END, + SEARCH, + FILTERS, + STATUS_FILTER, +} = CLIENT_DEFAULTS; + +/** + * Gets the current URL values for the application. If no item is present + * for the URL, a default value is supplied. + * + * @param params A set of key-value pairs where the value is either + * undefined or a string/string array. If a string array is passed, + * only the first item is chosen. Support for lists in the URL will + * require further development. + */ +export const getSupportedUrlParams = (params: { + [key: string]: string | string[] | undefined | null; +}): SyntheticsUrlParams => { + const filteredParams: { [key: string]: string | undefined } = {}; + Object.keys(params).forEach((key) => { + let value: string | undefined; + if (params[key] === undefined) { + value = undefined; + } else if (Array.isArray(params[key])) { + // @ts-ignore this must be an array, and it's ok if the + // 0th element is undefined + value = params[key][0]; + } else { + // @ts-ignore this will not be an array because the preceding + // block tests for that + value = params[key]; + } + filteredParams[key] = value; + }); + + const { + autorefreshInterval, + autorefreshIsPaused, + dateRangeStart, + dateRangeEnd, + filters, + excludedFilters, + search, + statusFilter, + pagination, + focusConnectorField, + query, + } = filteredParams; + + return { + pagination, + absoluteDateRangeStart: parseAbsoluteDate( + dateRangeStart || DATE_RANGE_START, + ABSOLUTE_DATE_RANGE_START + ), + absoluteDateRangeEnd: parseAbsoluteDate( + dateRangeEnd || DATE_RANGE_END, + ABSOLUTE_DATE_RANGE_END, + { roundUp: true } + ), + autorefreshInterval: parseUrlInt(autorefreshInterval, AUTOREFRESH_INTERVAL), + autorefreshIsPaused: parseIsPaused(autorefreshIsPaused, AUTOREFRESH_IS_PAUSED), + dateRangeStart: dateRangeStart || DATE_RANGE_START, + dateRangeEnd: dateRangeEnd || DATE_RANGE_END, + filters: filters || FILTERS, + excludedFilters: excludedFilters || '', + search: search || SEARCH, + statusFilter: statusFilter || STATUS_FILTER, + focusConnectorField: !!focusConnectorField, + query: query || '', + }; +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/helper_with_redux.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/helper_with_redux.tsx new file mode 100644 index 0000000000000..23831b1a4bc25 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/helper_with_redux.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { Store } from 'redux'; +import { createStore as createReduxStore, applyMiddleware } from 'redux'; + +import { Provider as ReduxProvider } from 'react-redux'; +import createSagaMiddleware from 'redux-saga'; + +import { AppState } from '../../state'; +import { rootReducer } from '../../state/root_reducer'; +import { rootEffect } from '../../state/root_effect'; + +const createRealStore = (): Store => { + const sagaMW = createSagaMiddleware(); + const store = createReduxStore(rootReducer, applyMiddleware(sagaMW)); + sagaMW.run(rootEffect); + return store; +}; + +export const MountWithReduxProvider: React.FC<{ state?: AppState; useRealStore?: boolean }> = ({ + children, + state, + useRealStore, +}) => { + const store = useRealStore + ? createRealStore() + : { + dispatch: jest.fn(), + getState: jest.fn().mockReturnValue(state || { selectedFilters: null }), + subscribe: jest.fn(), + replaceReducer: jest.fn(), + [Symbol.observable]: jest.fn(), + }; + + return {children}; +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/index.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/index.ts new file mode 100644 index 0000000000000..cebc9f5030293 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/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 './rtl_helpers'; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/rtl_helpers.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/rtl_helpers.tsx new file mode 100644 index 0000000000000..71d86cc53a76b --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/rtl_helpers.tsx @@ -0,0 +1,363 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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, { ReactElement, ReactNode } from 'react'; +import { of } from 'rxjs'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { + render as reactTestLibRender, + MatcherFunction, + RenderOptions, +} from '@testing-library/react'; +import { Router, Route } from 'react-router-dom'; +import { merge } from 'lodash'; +import { createMemoryHistory, History } from 'history'; +import { CoreStart } from '@kbn/core/public'; +import { I18nProvider } from '@kbn/i18n-react'; +import { EuiPageTemplate } from '@elastic/eui'; +import { coreMock } from '@kbn/core/public/mocks'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { configure } from '@testing-library/dom'; +import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; +import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; +import { KibanaContextProvider, KibanaServices } from '@kbn/kibana-react-plugin/public'; +import { triggersActionsUiMock } from '@kbn/triggers-actions-ui-plugin/public/mocks'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { mockState } from './__mocks__/syncthetics_store.mock'; +import { MountWithReduxProvider } from './helper_with_redux'; +import { AppState } from '../../state'; +import { stringifyUrlParams } from '../url_params'; +import { ClientPluginsStart } from '../../../../plugin'; +import { + SyntheticsRefreshContextProvider, + SyntheticsStartupPluginsContextProvider, +} from '../../contexts'; +import { kibanaService } from '../../../../utils/kibana_service'; + +type DeepPartial = { + [P in keyof T]?: DeepPartial; +}; + +interface KibanaProps { + services?: KibanaServices; +} + +export interface KibanaProviderOptions { + core?: DeepPartial & Partial; + kibanaProps?: KibanaProps; +} + +interface MockKibanaProviderProps extends KibanaProviderOptions { + children: ReactElement | ReactNode; +} + +interface MockRouterProps extends MockKibanaProviderProps { + history?: History; + path?: string; +} + +type Url = + | string + | { + path: string; + queryParams: Record; + }; + +interface RenderRouterOptions extends KibanaProviderOptions { + history?: History; + renderOptions?: Omit; + state?: Partial | DeepPartial; + url?: Url; + path?: string; +} + +function getSetting(key: string): T { + return 'MMM D, YYYY @ HH:mm:ss.SSS' as unknown as T; +} + +function setSetting$(key: string): T { + return of('MMM D, YYYY @ HH:mm:ss.SSS') as unknown as T; +} + +const createMockStore = () => { + let store: Record = {}; + return { + get: jest.fn().mockImplementation((key) => store[key]), + set: jest.fn().mockImplementation((key, value) => (store[key] = value)), + remove: jest.fn().mockImplementation((key: string) => delete store[key]), + clear: jest.fn().mockImplementation(() => (store = {})), + }; +}; + +const mockAppUrls: Record = { + uptime: '/app/uptime', + observability: '/app/observability', + '/home#/tutorial/uptimeMonitors': '/home#/tutorial/uptimeMonitors', +}; + +/* default mock core */ +export const defaultCore = coreMock.createStart(); +export const mockCore: () => Partial = () => { + const core: Partial = { + ...defaultCore, + application: { + ...defaultCore.application, + getUrlForApp: (app: string) => mockAppUrls[app], + navigateToUrl: jest.fn(), + capabilities: { + ...defaultCore.application.capabilities, + uptime: { + 'alerting:save': true, + configureSettings: true, + save: true, + show: true, + }, + actions: { + save: true, + }, + }, + }, + uiSettings: { + ...defaultCore.uiSettings, + get: getSetting, + get$: setSetting$, + }, + usageCollection: { + reportUiCounter: () => {}, + }, + triggersActionsUi: triggersActionsUiMock.createStart(), + storage: createMockStore(), + data: dataPluginMock.createStartContract(), + observability: { + useRulesLink: () => ({ href: 'newRuleLink' }), + navigation: { + // @ts-ignore + PageTemplate: EuiPageTemplate, + }, + ExploratoryViewEmbeddable: () =>
Embeddable exploratory view
, + }, + }; + + return core; +}; + +/* Mock Provider Components */ +export function MockKibanaProvider({ + children, + core, + kibanaProps, +}: MockKibanaProviderProps) { + const coreOptions = merge({}, mockCore(), core); + + kibanaService.core = coreOptions as any; + + return ( + + + + + {children} + + + + + ); +} + +export function MockRouter({ + children, + core, + path, + history = createMemoryHistory(), + kibanaProps, +}: MockRouterProps) { + return ( + + + {children} + + + ); +} +configure({ testIdAttribute: 'data-test-subj' }); + +export const MockRedux = ({ + state, + history = createMemoryHistory(), + children, + path, +}: { + state: Partial; + history?: History; + children: React.ReactNode; + path?: string; + useRealStore?: boolean; +}) => { + const testState: AppState = { + ...mockState, + ...state, + }; + + return ( + + + {children} + + + ); +}; + +export function WrappedHelper({ + children, + core, + kibanaProps, + state, + url, + useRealStore, + path, + history = createMemoryHistory(), +}: RenderRouterOptions & { children: ReactElement; useRealStore?: boolean }) { + const testState: AppState = merge({}, mockState, state); + + return ( + + + {children} + + + ); +} + +/* Custom react testing library render */ +export function render( + ui: ReactElement, + { + history = createMemoryHistory(), + core, + kibanaProps, + renderOptions, + state, + url, + path, + useRealStore, + }: RenderRouterOptions & { useRealStore?: boolean } = {} +) { + if (url) { + history = getHistoryFromUrl(url); + } + + return { + ...reactTestLibRender( + + {ui} + , + renderOptions + ), + history, + }; +} + +const getHistoryFromUrl = (url: Url) => { + if (typeof url === 'string') { + return createMemoryHistory({ + initialEntries: [url], + }); + } + + return createMemoryHistory({ + initialEntries: [url.path + stringifyUrlParams(url.queryParams)], + }); +}; + +const forNearestTag = + (tag: string) => + (getByText: (f: MatcherFunction) => HTMLElement | null) => + (text: string): HTMLElement | null => + getByText((_content: string, node: Element | null) => { + if (!node) return false; + const noOtherButtonHasText = Array.from(node.children).every( + (child) => child && (child.textContent !== text || child.tagName.toLowerCase() !== tag) + ); + return ( + noOtherButtonHasText && node.textContent === text && node.tagName.toLowerCase() === tag + ); + }); + +// This function allows us to query for the nearest button with test +// no matter whether it has nested tags or not (as EuiButton elements do). +export const forNearestButton = forNearestTag('button'); + +export const forNearestAnchor = forNearestTag('a'); + +export const makeSyntheticsPermissionsCore = ( + permissions: Partial<{ + 'alerting:save': boolean; + configureSettings: boolean; + save: boolean; + show: boolean; + }> +) => { + return { + application: { + capabilities: { + uptime: { + 'alerting:save': true, + configureSettings: true, + save: true, + show: true, + ...permissions, + }, + }, + }, + }; +}; + +// This function filters out the queried elements which appear only +// either on mobile or desktop. +// +// It does so by filtering those with the class passed as the `classWrapper`. +// For mobile, we filter classes which tell elements to be hidden on desktop. +// For desktop, we do the opposite. +// +// We have this function because EUI will manipulate the visibility of some +// elements through pure CSS, which we can't assert on tests. Therefore, +// we look for the corresponding class wrapper. +const finderWithClassWrapper = + (classWrapper: string) => + ( + getterFn: (f: MatcherFunction) => HTMLElement | null, + customAttribute?: keyof Element | keyof HTMLElement + ) => + (text: string): HTMLElement | null => + getterFn((_content: string, node: Element | null) => { + if (!node) return false; + // There are actually properties that are not in Element but which + // appear on the `node`, so we must cast the customAttribute as a keyof Element + const content = node[(customAttribute as keyof Element) ?? 'innerHTML']; + if (content === text && wrappedInClass(node, classWrapper)) return true; + return false; + }); + +const wrappedInClass = (element: HTMLElement | Element, classWrapper: string): boolean => { + if (element.className.includes(classWrapper)) return true; + if (element.parentElement) return wrappedInClass(element.parentElement, classWrapper); + return false; +}; + +export const forMobileOnly = finderWithClassWrapper('hideForDesktop'); +export const forDesktopOnly = finderWithClassWrapper('hideForMobile'); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/get_supported_url_params.test.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/get_supported_url_params.test.ts new file mode 100644 index 0000000000000..5e67d8f0e6026 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/get_supported_url_params.test.ts @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import DateMath from '@kbn/datemath'; +import { getSupportedUrlParams } from './get_supported_url_params'; +import { CLIENT_DEFAULTS } from '../../../../../common/constants'; + +describe('getSupportedUrlParams', () => { + let dateMathSpy: any; + const MOCK_DATE_VALUE = 20; + + beforeEach(() => { + dateMathSpy = jest.spyOn(DateMath, 'parse'); + dateMathSpy.mockReturnValue(MOCK_DATE_VALUE); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('returns custom values', () => { + const customValues = { + autorefreshInterval: '23', + autorefreshIsPaused: 'false', + dateRangeStart: 'now-15m', + dateRangeEnd: 'now', + monitorListPageIndex: '23', + monitorListPageSize: '50', + monitorListSortDirection: 'desc', + monitorListSortField: 'monitor.status', + search: 'monitor.status: down', + selectedPingStatus: 'up', + }; + + const expected = { + absoluteDateRangeEnd: 20, + absoluteDateRangeStart: 20, + autorefreshInterval: 23, + autorefreshIsPaused: false, + dateRangeEnd: 'now', + dateRangeStart: 'now-15m', + search: 'monitor.status: down', + }; + + const result = getSupportedUrlParams(customValues); + expect(result).toMatchObject(expected); + }); + + it('returns default values', () => { + const { + AUTOREFRESH_INTERVAL, + AUTOREFRESH_IS_PAUSED, + DATE_RANGE_START, + DATE_RANGE_END, + FILTERS, + SEARCH, + STATUS_FILTER, + } = CLIENT_DEFAULTS; + const result = getSupportedUrlParams({}); + expect(result).toEqual({ + absoluteDateRangeStart: MOCK_DATE_VALUE, + absoluteDateRangeEnd: MOCK_DATE_VALUE, + autorefreshInterval: AUTOREFRESH_INTERVAL, + autorefreshIsPaused: AUTOREFRESH_IS_PAUSED, + dateRangeStart: DATE_RANGE_START, + dateRangeEnd: DATE_RANGE_END, + excludedFilters: '', + filters: FILTERS, + focusConnectorField: false, + pagination: undefined, + search: SEARCH, + statusFilter: STATUS_FILTER, + query: '', + }); + }); + + it('returns the first item for string arrays', () => { + const result = getSupportedUrlParams({ + dateRangeStart: ['now-18d', 'now-11d', 'now-5m'], + }); + + const expected = { + absoluteDateRangeEnd: 20, + absoluteDateRangeStart: 20, + autorefreshInterval: 60000, + }; + + expect(result).toMatchObject(expected); + }); + + it('provides defaults for undefined values', () => { + const result = getSupportedUrlParams({ + dateRangeStart: undefined, + }); + + const expected = { + absoluteDateRangeStart: 20, + }; + + expect(result).toMatchObject(expected); + }); + + it('provides defaults for empty string array values', () => { + const result = getSupportedUrlParams({ + dateRangeStart: [], + }); + + const expected = { + absoluteDateRangeStart: 20, + }; + + expect(result).toMatchObject(expected); + }); +}); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/get_supported_url_params.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/get_supported_url_params.ts new file mode 100644 index 0000000000000..1036c07973b27 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/get_supported_url_params.ts @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { parseIsPaused } from './parse_is_paused'; +import { parseUrlInt } from './parse_url_int'; +import { CLIENT_DEFAULTS } from '../../../../../common/constants'; +import { parseAbsoluteDate } from './parse_absolute_date'; + +// TODO: Change for Synthetics App if needed (Copied from legacy_uptime) +export interface SyntheticsUrlParams { + absoluteDateRangeStart: number; + absoluteDateRangeEnd: number; + autorefreshInterval: number; + autorefreshIsPaused: boolean; + dateRangeStart: string; + dateRangeEnd: string; + pagination?: string; + filters: string; + excludedFilters: string; + search: string; + statusFilter: string; + focusConnectorField?: boolean; + query?: string; +} + +const { + ABSOLUTE_DATE_RANGE_START, + ABSOLUTE_DATE_RANGE_END, + AUTOREFRESH_INTERVAL, + AUTOREFRESH_IS_PAUSED, + DATE_RANGE_START, + DATE_RANGE_END, + SEARCH, + FILTERS, + STATUS_FILTER, +} = CLIENT_DEFAULTS; + +/** + * Gets the current URL values for the application. If no item is present + * for the URL, a default value is supplied. + * + * @param params A set of key-value pairs where the value is either + * undefined or a string/string array. If a string array is passed, + * only the first item is chosen. Support for lists in the URL will + * require further development. + */ +export const getSupportedUrlParams = (params: { + [key: string]: string | string[] | undefined | null; +}): SyntheticsUrlParams => { + const filteredParams: { [key: string]: string | undefined } = {}; + Object.keys(params).forEach((key) => { + let value: string | undefined; + if (params[key] === undefined) { + value = undefined; + } else if (Array.isArray(params[key])) { + // @ts-ignore this must be an array, and it's ok if the + // 0th element is undefined + value = params[key][0]; + } else { + // @ts-ignore this will not be an array because the preceding + // block tests for that + value = params[key]; + } + filteredParams[key] = value; + }); + + const { + autorefreshInterval, + autorefreshIsPaused, + dateRangeStart, + dateRangeEnd, + filters, + excludedFilters, + search, + statusFilter, + pagination, + focusConnectorField, + query, + } = filteredParams; + + return { + pagination, + absoluteDateRangeStart: parseAbsoluteDate( + dateRangeStart || DATE_RANGE_START, + ABSOLUTE_DATE_RANGE_START + ), + absoluteDateRangeEnd: parseAbsoluteDate( + dateRangeEnd || DATE_RANGE_END, + ABSOLUTE_DATE_RANGE_END, + { roundUp: true } + ), + autorefreshInterval: parseUrlInt(autorefreshInterval, AUTOREFRESH_INTERVAL), + autorefreshIsPaused: parseIsPaused(autorefreshIsPaused, AUTOREFRESH_IS_PAUSED), + dateRangeStart: dateRangeStart || DATE_RANGE_START, + dateRangeEnd: dateRangeEnd || DATE_RANGE_END, + filters: filters || FILTERS, + excludedFilters: excludedFilters || '', + search: search || SEARCH, + statusFilter: statusFilter || STATUS_FILTER, + focusConnectorField: !!focusConnectorField, + query: query || '', + }; +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/index.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/index.ts new file mode 100644 index 0000000000000..a3f27112b0e81 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { SyntheticsUrlParams } from './get_supported_url_params'; +export { getSupportedUrlParams } from './get_supported_url_params'; +export * from './stringify_url_params'; diff --git a/x-pack/plugins/synthetics/public/lib/helper/url_params/parse_absolute_date.test.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/parse_absolute_date.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/url_params/parse_absolute_date.test.ts rename to x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/parse_absolute_date.test.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/url_params/parse_absolute_date.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/parse_absolute_date.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/url_params/parse_absolute_date.ts rename to x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/parse_absolute_date.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/url_params/parse_is_paused.test.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/parse_is_paused.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/url_params/parse_is_paused.test.ts rename to x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/parse_is_paused.test.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/url_params/parse_is_paused.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/parse_is_paused.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/url_params/parse_is_paused.ts rename to x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/parse_is_paused.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/url_params/parse_url_int.test.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/parse_url_int.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/url_params/parse_url_int.test.ts rename to x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/parse_url_int.test.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/url_params/parse_url_int.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/parse_url_int.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/url_params/parse_url_int.ts rename to x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/parse_url_int.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/stringify_url_params.test.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/stringify_url_params.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/stringify_url_params.test.ts rename to x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/stringify_url_params.test.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/stringify_url_params.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/stringify_url_params.ts similarity index 86% rename from x-pack/plugins/synthetics/public/lib/helper/stringify_url_params.ts rename to x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/stringify_url_params.ts index edfa8e62d6137..59c71af795cce 100644 --- a/x-pack/plugins/synthetics/public/lib/helper/stringify_url_params.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/url_params/stringify_url_params.ts @@ -6,8 +6,8 @@ */ import { stringify } from 'query-string'; -import { UptimeUrlParams } from './url_params'; -import { CLIENT_DEFAULTS } from '../../../common/constants'; +import { SyntheticsUrlParams } from './get_supported_url_params'; +import { CLIENT_DEFAULTS } from '../../../../../common/constants'; const { AUTOREFRESH_INTERVAL, @@ -17,7 +17,7 @@ const { FOCUS_CONNECTOR_FIELD, } = CLIENT_DEFAULTS; -export const stringifyUrlParams = (params: Partial, ignoreEmpty = false) => { +export const stringifyUrlParams = (params: Partial, ignoreEmpty = false) => { if (ignoreEmpty) { // We don't want to encode this values because they are often set to Date.now(), the relative // values in dateRangeStart are better for a URL. diff --git a/x-pack/plugins/synthetics/public/apps/use_no_data_config.ts b/x-pack/plugins/synthetics/public/apps/use_no_data_config.ts deleted file mode 100644 index 8898e42548748..0000000000000 --- a/x-pack/plugins/synthetics/public/apps/use_no_data_config.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { useContext } from 'react'; -import { useSelector } from 'react-redux'; -import { KibanaPageTemplateProps, useKibana } from '@kbn/kibana-react-plugin/public'; -import { UptimeSettingsContext } from '../contexts'; -import { ClientPluginsStart } from './plugin'; -import { indexStatusSelector } from '../state/selectors'; - -export function useNoDataConfig(): KibanaPageTemplateProps['noDataConfig'] { - const { basePath } = useContext(UptimeSettingsContext); - - const { - services: { docLinks }, - } = useKibana(); - - const { data } = useSelector(indexStatusSelector); - - // Returns no data config when there is no historical data - if (data && !data.indexExists) { - return { - solution: i18n.translate('xpack.synthetics.noDataConfig.solutionName', { - defaultMessage: 'Observability', - }), - actions: { - beats: { - title: i18n.translate('xpack.synthetics.noDataConfig.beatsCard.title', { - defaultMessage: 'Add monitors with Heartbeat', - }), - description: i18n.translate('xpack.synthetics.noDataConfig.beatsCard.description', { - defaultMessage: - 'Proactively monitor the availability of your sites and services. Receive alerts and resolve issues faster to optimize your users experience.', - }), - href: basePath + `/app/home#/tutorial/uptimeMonitors`, - }, - }, - docsLink: docLinks!.links.observability.guide, - }; - } -} diff --git a/x-pack/plugins/synthetics/public/badge.ts b/x-pack/plugins/synthetics/public/badge.ts deleted file mode 100644 index a42eaa58a7943..0000000000000 --- a/x-pack/plugins/synthetics/public/badge.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ChromeBadge } from '@kbn/core/public'; -export type UMBadge = ChromeBadge | undefined; diff --git a/x-pack/plugins/synthetics/public/components/common/header/action_menu_content.tsx b/x-pack/plugins/synthetics/public/components/common/header/action_menu_content.tsx deleted file mode 100644 index 311c76c87a845..0000000000000 --- a/x-pack/plugins/synthetics/public/components/common/header/action_menu_content.tsx +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { EuiHeaderLinks, EuiToolTip, EuiHeaderLink } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { useHistory, useRouteMatch } from 'react-router-dom'; -import { useSelector } from 'react-redux'; -import { createExploratoryViewUrl } from '@kbn/observability-plugin/public'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { useUptimeSettingsContext } from '../../../contexts/uptime_settings_context'; -import { useGetUrlParams } from '../../../hooks'; -import { ToggleAlertFlyoutButton } from '../../overview/alerts/alerts_containers'; -import { MONITOR_ROUTE, SETTINGS_ROUTE } from '../../../../common/constants'; -import { stringifyUrlParams } from '../../../lib/helper/stringify_url_params'; -import { InspectorHeaderLink } from './inspector_header_link'; -import { monitorStatusSelector } from '../../../state/selectors'; -import { ManageMonitorsBtn } from './manage_monitors_btn'; - -const ADD_DATA_LABEL = i18n.translate('xpack.synthetics.addDataButtonLabel', { - defaultMessage: 'Add data', -}); - -const ANALYZE_DATA = i18n.translate('xpack.synthetics.analyzeDataButtonLabel', { - defaultMessage: 'Explore data', -}); - -const ANALYZE_MESSAGE = i18n.translate('xpack.synthetics.analyzeDataButtonLabel.message', { - defaultMessage: - 'Explore Data allows you to select and filter result data in any dimension and look for the cause or impact of performance problems.', -}); - -export function ActionMenuContent(): React.ReactElement { - const kibana = useKibana(); - const { basePath } = useUptimeSettingsContext(); - const params = useGetUrlParams(); - const { dateRangeStart, dateRangeEnd } = params; - const history = useHistory(); - - const selectedMonitor = useSelector(monitorStatusSelector); - - const detailRouteMatch = useRouteMatch(MONITOR_ROUTE); - const monitorId = selectedMonitor?.monitor?.id; - - const syntheticExploratoryViewLink = createExploratoryViewUrl( - { - reportType: 'kpi-over-time', - allSeries: [ - { - dataType: 'synthetics', - seriesType: 'area', - selectedMetricField: 'monitor.duration.us', - time: { from: dateRangeStart, to: dateRangeEnd }, - breakdown: monitorId ? 'observer.geo.name' : 'monitor.type', - reportDefinitions: { - 'monitor.name': - selectedMonitor?.monitor?.name && detailRouteMatch?.isExact === true - ? [selectedMonitor?.monitor?.name] - : [], - 'url.full': ['ALL_VALUES'], - }, - name: monitorId ? `${monitorId}-response-duration` : 'All monitors response duration', - }, - ], - }, - basePath - ); - - return ( - - - - - - - - - - {ANALYZE_MESSAGE}

}> - - {ANALYZE_DATA} - -
- - - {ADD_DATA_LABEL} - - -
- ); -} diff --git a/x-pack/plugins/synthetics/public/components/common/header/inspector_header_link.tsx b/x-pack/plugins/synthetics/public/components/common/header/inspector_header_link.tsx deleted file mode 100644 index aa3e53be500cd..0000000000000 --- a/x-pack/plugins/synthetics/public/components/common/header/inspector_header_link.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiHeaderLink } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { enableInspectEsQueries, useInspectorContext } from '@kbn/observability-plugin/public'; -import { ClientPluginsStart } from '../../../apps/plugin'; -import { useUptimeSettingsContext } from '../../../contexts/uptime_settings_context'; - -export function InspectorHeaderLink() { - const { - services: { inspector, uiSettings }, - } = useKibana(); - - const { isDev } = useUptimeSettingsContext(); - - const { inspectorAdapters } = useInspectorContext(); - - const isInspectorEnabled = uiSettings?.get(enableInspectEsQueries); - - const inspect = () => { - inspector.open(inspectorAdapters); - }; - - if (!isInspectorEnabled && !isDev) { - return null; - } - - return ( - - {i18n.translate('xpack.synthetics.inspectButtonText', { - defaultMessage: 'Inspect', - })} - - ); -} diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/common/default_values.ts b/x-pack/plugins/synthetics/public/components/fleet_package/common/default_values.ts deleted file mode 100644 index 54c6969833cf1..0000000000000 --- a/x-pack/plugins/synthetics/public/components/fleet_package/common/default_values.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 { DEFAULT_NAMESPACE_STRING } from '../../../../common/constants'; -import { CommonFields, ConfigKey, ScheduleUnit, DataStream } from '../types'; - -export const defaultValues: CommonFields = { - [ConfigKey.MONITOR_TYPE]: DataStream.HTTP, - [ConfigKey.LOCATIONS]: [], - [ConfigKey.ENABLED]: true, - [ConfigKey.SCHEDULE]: { - number: '3', - unit: ScheduleUnit.MINUTES, - }, - [ConfigKey.APM_SERVICE_NAME]: '', - [ConfigKey.TAGS]: [], - [ConfigKey.TIMEOUT]: '16', - [ConfigKey.NAME]: '', - [ConfigKey.LOCATIONS]: [], - [ConfigKey.NAMESPACE]: DEFAULT_NAMESPACE_STRING, -}; diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/browser_context.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/contexts/browser_context.tsx deleted file mode 100644 index f7b67488e4bcc..0000000000000 --- a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/browser_context.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, { createContext, useContext, useMemo, useState } from 'react'; -import { BrowserSimpleFields, ConfigKey, DataStream, ScheduleUnit } from '../types'; -import { defaultValues as commonDefaultValues } from '../common/default_values'; - -interface BrowserSimpleFieldsContext { - setFields: React.Dispatch>; - fields: BrowserSimpleFields; - defaultValues: BrowserSimpleFields; -} - -interface BrowserSimpleFieldsContextProvider { - children: React.ReactNode; - defaultValues?: BrowserSimpleFields; -} - -export const initialValues: BrowserSimpleFields = { - ...commonDefaultValues, - [ConfigKey.SCHEDULE]: { - unit: ScheduleUnit.MINUTES, - number: '10', - }, - [ConfigKey.METADATA]: { - script_source: { - is_generated_script: false, - file_name: '', - }, - is_zip_url_tls_enabled: false, - }, - [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, - [ConfigKey.SOURCE_ZIP_URL]: '', - [ConfigKey.SOURCE_ZIP_USERNAME]: '', - [ConfigKey.SOURCE_ZIP_PASSWORD]: '', - [ConfigKey.SOURCE_ZIP_FOLDER]: '', - [ConfigKey.SOURCE_ZIP_PROXY_URL]: '', - [ConfigKey.SOURCE_INLINE]: '', - [ConfigKey.PARAMS]: '', - [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: undefined, - [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: undefined, - [ConfigKey.ZIP_URL_TLS_KEY]: undefined, - [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: undefined, - [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: undefined, - [ConfigKey.ZIP_URL_TLS_VERSION]: undefined, - [ConfigKey.URLS]: undefined, - [ConfigKey.PORT]: undefined, -}; - -const defaultContext: BrowserSimpleFieldsContext = { - setFields: (_fields: React.SetStateAction) => { - throw new Error( - 'setFields was not initialized for Browser Simple Fields, set it when you invoke the context' - ); - }, - fields: initialValues, // mutable - defaultValues: initialValues, // immutable -}; - -export const BrowserSimpleFieldsContext = createContext(defaultContext); - -export const BrowserSimpleFieldsContextProvider = ({ - children, - defaultValues = initialValues, -}: BrowserSimpleFieldsContextProvider) => { - const [fields, setFields] = useState(defaultValues); - - const value = useMemo(() => { - return { fields, setFields, defaultValues }; - }, [fields, defaultValues]); - - return ; -}; - -export const useBrowserSimpleFieldsContext = () => useContext(BrowserSimpleFieldsContext); diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/http_context_advanced.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/contexts/http_context_advanced.tsx deleted file mode 100644 index 4d64bd3cf1ad4..0000000000000 --- a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/http_context_advanced.tsx +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { createContext, useContext, useMemo, useState } from 'react'; -import { HTTPAdvancedFields, ConfigKey, Mode, ResponseBodyIndexPolicy, HTTPMethod } from '../types'; - -interface HTTPAdvancedFieldsContext { - setFields: React.Dispatch>; - fields: HTTPAdvancedFields; - defaultValues: HTTPAdvancedFields; -} - -interface HTTPAdvancedFieldsContextProvider { - children: React.ReactNode; - defaultValues?: HTTPAdvancedFields; -} - -export const initialValues: HTTPAdvancedFields = { - [ConfigKey.PASSWORD]: '', - [ConfigKey.PROXY_URL]: '', - [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: [], - [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: [], - [ConfigKey.RESPONSE_BODY_INDEX]: ResponseBodyIndexPolicy.ON_ERROR, - [ConfigKey.RESPONSE_HEADERS_CHECK]: {}, - [ConfigKey.RESPONSE_HEADERS_INDEX]: true, - [ConfigKey.RESPONSE_STATUS_CHECK]: [], - [ConfigKey.REQUEST_BODY_CHECK]: { - value: '', - type: Mode.PLAINTEXT, - }, - [ConfigKey.REQUEST_HEADERS_CHECK]: {}, - [ConfigKey.REQUEST_METHOD_CHECK]: HTTPMethod.GET, - [ConfigKey.USERNAME]: '', -}; - -export const defaultContext: HTTPAdvancedFieldsContext = { - setFields: (_fields: React.SetStateAction) => { - throw new Error('setFields was not initialized, set it when you invoke the context'); - }, - fields: initialValues, - defaultValues: initialValues, -}; - -export const HTTPAdvancedFieldsContext = createContext(defaultContext); - -export const HTTPAdvancedFieldsContextProvider = ({ - children, - defaultValues = initialValues, -}: HTTPAdvancedFieldsContextProvider) => { - const [fields, setFields] = useState(defaultValues); - - const value = useMemo(() => { - return { fields, setFields, defaultValues }; - }, [fields, defaultValues]); - - return ; -}; - -export const useHTTPAdvancedFieldsContext = () => useContext(HTTPAdvancedFieldsContext); diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/index.ts b/x-pack/plugins/synthetics/public/components/fleet_package/contexts/index.ts deleted file mode 100644 index 3f392c42983b6..0000000000000 --- a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/index.ts +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { DataStream, PolicyConfig } from '../types'; -import { initialValues as defaultHTTPSimpleFields } from './http_context'; -import { initialValues as defaultHTTPAdvancedFields } from './http_context_advanced'; -import { initialValues as defaultTCPSimpleFields } from './tcp_context'; -import { initialValues as defaultICMPSimpleFields } from './icmp_context'; -import { initialValues as defaultTCPAdvancedFields } from './tcp_context_advanced'; -import { initialValues as defaultBrowserSimpleFields } from './browser_context'; -import { initialValues as defaultBrowserAdvancedFields } from './browser_context_advanced'; -import { initialValues as defaultTLSFields } from './tls_fields_context'; - -export type { IPolicyConfigContextProvider } from './policy_config_context'; -export { - PolicyConfigContext, - PolicyConfigContextProvider, - initialValue as defaultPolicyConfig, - defaultContext as defaultPolicyConfigValues, - usePolicyConfigContext, -} from './policy_config_context'; -export { - HTTPSimpleFieldsContext, - HTTPSimpleFieldsContextProvider, - initialValues as defaultHTTPSimpleFields, - useHTTPSimpleFieldsContext, -} from './http_context'; -export { - HTTPAdvancedFieldsContext, - HTTPAdvancedFieldsContextProvider, - initialValues as defaultHTTPAdvancedFields, - useHTTPAdvancedFieldsContext, -} from './http_context_advanced'; -export { - TCPSimpleFieldsContext, - TCPSimpleFieldsContextProvider, - initialValues as defaultTCPSimpleFields, - useTCPSimpleFieldsContext, -} from './tcp_context'; -export { - ICMPSimpleFieldsContext, - ICMPSimpleFieldsContextProvider, - initialValues as defaultICMPSimpleFields, - useICMPSimpleFieldsContext, -} from './icmp_context'; -export { - TCPAdvancedFieldsContext, - TCPAdvancedFieldsContextProvider, - initialValues as defaultTCPAdvancedFields, - useTCPAdvancedFieldsContext, -} from './tcp_context_advanced'; -export { - BrowserSimpleFieldsContext, - BrowserSimpleFieldsContextProvider, - initialValues as defaultBrowserSimpleFields, - useBrowserSimpleFieldsContext, -} from './browser_context'; -export { - BrowserAdvancedFieldsContext, - BrowserAdvancedFieldsContextProvider, - initialValues as defaultBrowserAdvancedFields, - useBrowserAdvancedFieldsContext, -} from './browser_context_advanced'; -export { - TLSFieldsContext, - TLSFieldsContextProvider, - initialValues as defaultTLSFields, - useTLSFieldsContext, -} from './tls_fields_context'; -export { HTTPContextProvider } from './http_provider'; -export { TCPContextProvider } from './tcp_provider'; -export { BrowserContextProvider } from './browser_provider'; -export { SyntheticsProviders } from './synthetics_context_providers'; - -export const defaultConfig: PolicyConfig = { - [DataStream.HTTP]: { - ...defaultHTTPSimpleFields, - ...defaultHTTPAdvancedFields, - ...defaultTLSFields, - }, - [DataStream.TCP]: { - ...defaultTCPSimpleFields, - ...defaultTCPAdvancedFields, - ...defaultTLSFields, - }, - [DataStream.ICMP]: defaultICMPSimpleFields, - [DataStream.BROWSER]: { - ...defaultBrowserSimpleFields, - ...defaultBrowserAdvancedFields, - ...defaultTLSFields, - }, -}; diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/tls/default_values.ts b/x-pack/plugins/synthetics/public/components/fleet_package/tls/default_values.ts deleted file mode 100644 index 18f291ce20f35..0000000000000 --- a/x-pack/plugins/synthetics/public/components/fleet_package/tls/default_values.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 { TLSFields, ConfigKey, VerificationMode, TLSVersion } from '../types'; - -export const defaultValues: TLSFields = { - [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: '', - [ConfigKey.TLS_CERTIFICATE]: '', - [ConfigKey.TLS_KEY]: '', - [ConfigKey.TLS_KEY_PASSPHRASE]: '', - [ConfigKey.TLS_VERIFICATION_MODE]: VerificationMode.FULL, - [ConfigKey.TLS_VERSION]: [TLSVersion.ONE_ONE, TLSVersion.ONE_TWO, TLSVersion.ONE_THREE], -}; diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/types.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/types.tsx deleted file mode 100644 index d3df3a1926887..0000000000000 --- a/x-pack/plugins/synthetics/public/components/fleet_package/types.tsx +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { - HTTPFields, - TCPFields, - ICMPFields, - BrowserFields, - ConfigKey, - ContentType, - DataStream, - Mode, - ThrottlingConfigKey, - ThrottlingSuffix, - ThrottlingSuffixType, -} from '../../../common/runtime_types'; -export * from '../../../common/runtime_types/monitor_management'; -export * from '../../../common/types/monitor_validation'; - -export interface PolicyConfig { - [DataStream.HTTP]: HTTPFields; - [DataStream.TCP]: TCPFields; - [DataStream.ICMP]: ICMPFields; - [DataStream.BROWSER]: BrowserFields; -} - -export const contentTypesToMode = { - [ContentType.FORM]: Mode.FORM, - [ContentType.JSON]: Mode.JSON, - [ContentType.TEXT]: Mode.PLAINTEXT, - [ContentType.XML]: Mode.XML, -}; - -export const configKeyToThrottlingSuffix: Record = { - [ConfigKey.DOWNLOAD_SPEED]: ThrottlingSuffix.DOWNLOAD, - [ConfigKey.UPLOAD_SPEED]: ThrottlingSuffix.UPLOAD, - [ConfigKey.LATENCY]: ThrottlingSuffix.LATENCY, -}; diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/validation.test.ts b/x-pack/plugins/synthetics/public/components/fleet_package/validation.test.ts deleted file mode 100644 index 3495b163b9845..0000000000000 --- a/x-pack/plugins/synthetics/public/components/fleet_package/validation.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - ConfigKey, - DataStream, - HTTPFields, - BrowserFields, - MonitorFields, - ScheduleUnit, -} from '../../../common/runtime_types'; -import { validate } from './validation'; - -describe('[Monitor Management] validation', () => { - const commonPropsValid: Partial = { - [ConfigKey.SCHEDULE]: { number: '5', unit: ScheduleUnit.MINUTES }, - [ConfigKey.TIMEOUT]: '3m', - }; - - describe('HTTP', () => { - const httpPropsValid: Partial = { - ...commonPropsValid, - [ConfigKey.RESPONSE_STATUS_CHECK]: ['200', '204'], - [ConfigKey.RESPONSE_HEADERS_CHECK]: { 'Content-Type': 'application/json' }, - [ConfigKey.REQUEST_HEADERS_CHECK]: { 'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8' }, - [ConfigKey.MAX_REDIRECTS]: '3', - [ConfigKey.URLS]: 'https:// example-url.com', - }; - - it('should return false for all valid props', () => { - const validators = validate[DataStream.HTTP]; - const keysToValidate = [ - ConfigKey.SCHEDULE, - ConfigKey.TIMEOUT, - ConfigKey.RESPONSE_STATUS_CHECK, - ConfigKey.RESPONSE_HEADERS_CHECK, - ConfigKey.REQUEST_HEADERS_CHECK, - ConfigKey.MAX_REDIRECTS, - ConfigKey.URLS, - ]; - const validatorFns = keysToValidate.map((key) => validators[key]); - const result = validatorFns.map((fn) => fn?.(httpPropsValid) ?? true); - - expect(result).not.toEqual(expect.arrayContaining([true])); - }); - }); - - describe.each([ - [ConfigKey.SOURCE_INLINE, 'step(() => {});'], - [ConfigKey.SOURCE_ZIP_URL, 'https://test.zip'], - ])('Browser', (configKey, value) => { - const browserProps: Partial = { - ...commonPropsValid, - [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, - [ConfigKey.TIMEOUT]: null, - [configKey]: value, - }; - - it('should return false for all valid props', () => { - const validators = validate[DataStream.BROWSER]; - const keysToValidate = [ConfigKey.SCHEDULE, ConfigKey.TIMEOUT, configKey]; - const validatorFns = keysToValidate.map((key) => validators[key]); - const result = validatorFns.map((fn) => fn?.(browserProps) ?? true); - - expect(result).not.toEqual(expect.arrayContaining([true])); - }); - }); - - // TODO: Add test for other monitor types if needed -}); diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/types.ts b/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/types.ts deleted file mode 100644 index 19ae847ee3adb..0000000000000 --- a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/types.ts +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { NetworkEvent } from '../../../../../../common/runtime_types'; - -export enum Timings { - Blocked = 'blocked', - Dns = 'dns', - Connect = 'connect', - Ssl = 'ssl', - Send = 'send', - Wait = 'wait', - Receive = 'receive', -} - -export enum Metadata { - Status = 'status', - ResourceSize = 'resourceSize', - TransferSize = 'transferSize', - CertificateIssuer = 'certificateIssuer', - CertificateIssueDate = 'certificateIssueDate', - CertificateExpiryDate = 'certificateExpiryDate', - CertificateSubject = 'certificateSubject', - IP = 'ip', - MimeType = 'mimeType', - RequestStart = 'requestStart', -} - -export const FriendlyTimingLabels = { - [Timings.Blocked]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.timings.blocked', - { - defaultMessage: 'Queued / Blocked', - } - ), - [Timings.Dns]: i18n.translate('xpack.synthetics.synthetics.waterfallChart.labels.timings.dns', { - defaultMessage: 'DNS', - }), - [Timings.Connect]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.timings.connect', - { - defaultMessage: 'Connecting', - } - ), - [Timings.Ssl]: i18n.translate('xpack.synthetics.synthetics.waterfallChart.labels.timings.ssl', { - defaultMessage: 'TLS', - }), - [Timings.Send]: i18n.translate('xpack.synthetics.synthetics.waterfallChart.labels.timings.send', { - defaultMessage: 'Sending request', - }), - [Timings.Wait]: i18n.translate('xpack.synthetics.synthetics.waterfallChart.labels.timings.wait', { - defaultMessage: 'Waiting (TTFB)', - }), - [Timings.Receive]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.timings.receive', - { - defaultMessage: 'Content downloading', - } - ), -}; - -export const FriendlyFlyoutLabels = { - [Metadata.Status]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.status', - { - defaultMessage: 'Status', - } - ), - [Metadata.MimeType]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.contentType', - { - defaultMessage: 'Content type', - } - ), - [Metadata.RequestStart]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.requestStart', - { - defaultMessage: 'Request start', - } - ), - [Metadata.ResourceSize]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.resourceSize', - { - defaultMessage: 'Resource size', - } - ), - [Metadata.TransferSize]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.transferSize', - { - defaultMessage: 'Transfer size', - } - ), - [Metadata.CertificateIssuer]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.certificateIssuer', - { - defaultMessage: 'Issuer', - } - ), - [Metadata.CertificateIssueDate]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.certificateIssueDate', - { - defaultMessage: 'Valid from', - } - ), - [Metadata.CertificateExpiryDate]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.certificateExpiryDate', - { - defaultMessage: 'Valid until', - } - ), - [Metadata.CertificateSubject]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.certificateSubject', - { - defaultMessage: 'Common name', - } - ), - [Metadata.IP]: i18n.translate('xpack.synthetics.synthetics.waterfallChart.labels.metadata.ip', { - defaultMessage: 'IP', - }), -}; - -export const TIMING_ORDER = [ - Timings.Blocked, - Timings.Dns, - Timings.Connect, - Timings.Ssl, - Timings.Send, - Timings.Wait, - Timings.Receive, -] as const; - -export const META_DATA_ORDER_FLYOUT = [ - Metadata.MimeType, - Timings.Dns, - Timings.Connect, - Timings.Ssl, - Timings.Wait, - Timings.Receive, -] as const; - -export type CalculatedTimings = { - [K in Timings]?: number; -}; - -export enum MimeType { - Html = 'html', - Script = 'script', - Stylesheet = 'stylesheet', - Media = 'media', - Font = 'font', - XHR = 'xhr', - Other = 'other', -} - -export const FriendlyMimetypeLabels = { - [MimeType.Html]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.mimeTypes.html', - { - defaultMessage: 'HTML', - } - ), - [MimeType.Script]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.mimeTypes.script', - { - defaultMessage: 'JS', - } - ), - [MimeType.Stylesheet]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.mimeTypes.stylesheet', - { - defaultMessage: 'CSS', - } - ), - [MimeType.Media]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.mimeTypes.media', - { - defaultMessage: 'Media', - } - ), - [MimeType.Font]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.mimeTypes.font', - { - defaultMessage: 'Font', - } - ), - [MimeType.XHR]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.mimeTypes.xhr', - { - defaultMessage: 'XHR', - } - ), - [MimeType.Other]: i18n.translate( - 'xpack.synthetics.synthetics.waterfallChart.labels.mimeTypes.other', - { - defaultMessage: 'Other', - } - ), -}; - -// NOTE: This list tries to cover the standard spec compliant mime types, -// and a few popular non-standard ones, but it isn't exhaustive. -export const MimeTypesMap: Record = { - 'text/html': MimeType.Html, - 'application/javascript': MimeType.Script, - 'text/javascript': MimeType.Script, - 'text/css': MimeType.Stylesheet, - // Images - 'image/apng': MimeType.Media, - 'image/bmp': MimeType.Media, - 'image/gif': MimeType.Media, - 'image/x-icon': MimeType.Media, - 'image/jpeg': MimeType.Media, - 'image/png': MimeType.Media, - 'image/svg+xml': MimeType.Media, - 'image/tiff': MimeType.Media, - 'image/webp': MimeType.Media, - // Common audio / video formats - 'audio/wave': MimeType.Media, - 'audio/wav': MimeType.Media, - 'audio/x-wav': MimeType.Media, - 'audio/x-pn-wav': MimeType.Media, - 'audio/webm': MimeType.Media, - 'video/webm': MimeType.Media, - 'video/mp4': MimeType.Media, - 'audio/ogg': MimeType.Media, - 'video/ogg': MimeType.Media, - 'application/ogg': MimeType.Media, - // Fonts - 'font/otf': MimeType.Font, - 'font/ttf': MimeType.Font, - 'font/woff': MimeType.Font, - 'font/woff2': MimeType.Font, - 'application/x-font-opentype': MimeType.Font, - 'application/font-woff': MimeType.Font, - 'application/font-woff2': MimeType.Font, - 'application/vnd.ms-fontobject': MimeType.Font, - 'application/font-sfnt': MimeType.Font, - - // XHR - 'application/json': MimeType.XHR, -}; - -export type NetworkItem = NetworkEvent; -export type NetworkItems = NetworkItem[]; - -export type SidebarItem = Pick & { - isHighlighted: boolean; - index: number; - offsetIndex: number; -}; -export type SidebarItems = SidebarItem[]; - -export interface LegendItem { - name: string; - colour: string; -} -export type LegendItems = LegendItem[]; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_list.test.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_list.test.tsx deleted file mode 100644 index a641f7a76f3a7..0000000000000 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_list.test.tsx +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { - ConfigKey, - DataStream, - HTTPFields, - ScheduleUnit, - DEFAULT_THROTTLING, -} from '../../../../common/runtime_types'; -import { render } from '../../../lib/helper/rtl_helpers'; -import { MonitorManagementList as MonitorManagementListState } from '../../../state/reducers/monitor_management'; -import { MonitorManagementList, MonitorManagementListPageState } from './monitor_list'; - -describe('', () => { - const onUpdate = jest.fn(); - const onPageStateChange = jest.fn(); - const monitors = []; - for (let i = 0; i < 12; i++) { - monitors.push({ - id: `test-monitor-id-${i}`, - updated_at: '123', - attributes: { - name: `test-monitor-${i}`, - enabled: true, - schedule: { - unit: ScheduleUnit.MINUTES, - number: `${i * 10}`, - }, - urls: `https://test-${i}.co`, - type: DataStream.HTTP, - tags: [`tag-${i}`], - } as HTTPFields, - }); - } - const state = { - monitorManagementList: { - throttling: DEFAULT_THROTTLING, - list: { - perPage: 5, - page: 1, - total: 6, - monitors, - syncErrors: null, - }, - locations: [], - enablement: null, - error: { - serviceLocations: null, - monitorList: null, - enablement: null, - }, - loading: { - monitorList: true, - serviceLocations: false, - enablement: false, - }, - syntheticsService: { - loading: false, - signupUrl: null, - }, - } as MonitorManagementListState, - }; - - const pageState: MonitorManagementListPageState = { - pageIndex: 1, - pageSize: 10, - sortField: `${ConfigKey.NAME}.keyword`, - sortOrder: 'asc', - }; - - it.each(monitors)('navigates to edit monitor flow on edit pencil', (monitor) => { - render( - , - { state } - ); - - expect(screen.getByText(monitor.attributes.name)).toBeInTheDocument(); - expect(screen.getByText(monitor.attributes.urls)).toBeInTheDocument(); - monitor.attributes.tags.forEach((tag) => { - expect(screen.getByText(tag)).toBeInTheDocument(); - }); - expect(screen.getByText(monitor.attributes.schedule.number)).toBeInTheDocument(); - }); - - it('handles changing per page', () => { - render( - , - { state } - ); - - userEvent.click(screen.getByTestId('tablePaginationPopoverButton')); - - userEvent.click(screen.getByText('10 rows')); - - expect(onPageStateChange).toBeCalledWith(expect.objectContaining({ pageSize: 10 })); - }); - - it('handles refreshing and changing page when navigating to the next page', async () => { - const { getByTestId } = render( - , - { state } - ); - - userEvent.click(getByTestId('pagination-button-next')); - - expect(onPageStateChange).toBeCalledWith(expect.objectContaining({ pageIndex: 2 })); - }); -}); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_list.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_list.tsx deleted file mode 100644 index 6dade5a1e4186..0000000000000 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_list.tsx +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { - Criteria, - EuiBasicTable, - EuiBasicTableColumn, - EuiLink, - EuiPanel, - EuiSpacer, -} from '@elastic/eui'; -import { EuiTableSortingType } from '@elastic/eui/src/components/basic_table/table_types'; -import { i18n } from '@kbn/i18n'; -import React, { useCallback, useContext, useMemo } from 'react'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { - CommonFields, - ConfigKey, - FetchMonitorManagementListQueryArgs, - ICMPSimpleFields, - Ping, - ServiceLocations, - EncryptedSyntheticsMonitorWithId, - TCPSimpleFields, -} from '../../../../common/runtime_types'; -import { UptimeSettingsContext } from '../../../contexts'; -import { useBreakpoints } from '../../../hooks'; -import { MonitorManagementList as MonitorManagementListState } from '../../../state/reducers/monitor_management'; -import * as labels from '../../overview/monitor_list/translations'; -import { Actions } from './actions'; -import { MonitorEnabled } from './monitor_enabled'; -import { MonitorLocations } from './monitor_locations'; -import { MonitorTags } from './tags'; - -export interface MonitorManagementListPageState { - pageIndex: number; - pageSize: number; - sortField: - | `${ConfigKey.URLS}.keyword` - | `${ConfigKey.NAME}.keyword` - | `${ConfigKey.MONITOR_TYPE}.keyword`; - sortOrder: NonNullable; -} - -interface Props { - pageState: MonitorManagementListPageState; - monitorList: MonitorManagementListState; - onPageStateChange: (state: MonitorManagementListPageState) => void; - onUpdate: () => void; - errorSummaries?: Ping[]; -} - -export const MonitorManagementList = ({ - pageState: { pageIndex, pageSize, sortField, sortOrder }, - monitorList: { - list, - error: { monitorList: error }, - loading: { monitorList: loading }, - }, - onPageStateChange, - onUpdate, - errorSummaries, -}: Props) => { - const { basePath } = useContext(UptimeSettingsContext); - const isXl = useBreakpoints().up('xl'); - - const { total } = list as MonitorManagementListState['list']; - const monitors: EncryptedSyntheticsMonitorWithId[] = useMemo( - () => - list.monitors.map((monitor) => ({ - ...monitor.attributes, - id: monitor.id, - })), - [list.monitors] - ); - - const handleOnChange = useCallback( - ({ - page = { index: 0, size: 10 }, - sort = { field: ConfigKey.NAME, direction: 'asc' }, - }: Criteria) => { - const { index, size } = page; - const { field, direction } = sort; - - onPageStateChange({ - pageIndex: index + 1, // page index for Saved Objects is base 1 - pageSize: size, - sortField: `${field}.keyword` as MonitorManagementListPageState['sortField'], - sortOrder: direction, - }); - }, - [onPageStateChange] - ); - - const pagination = { - pageIndex: pageIndex - 1, // page index for EuiBasicTable is base 0 - pageSize, - totalItemCount: total || 0, - pageSizeOptions: [5, 10, 25, 50, 100], - }; - - const sorting: EuiTableSortingType = { - sort: { - field: sortField.replace('.keyword', '') as keyof EncryptedSyntheticsMonitorWithId, - direction: sortOrder, - }, - }; - - const canEdit: boolean = !!useKibana().services?.application?.capabilities.uptime.save; - - const columns = [ - { - align: 'left' as const, - field: ConfigKey.NAME as string, - name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.monitorName', { - defaultMessage: 'Monitor name', - }), - sortable: true, - render: (name: string, { id }: EncryptedSyntheticsMonitorWithId) => ( - - {name} - - ), - }, - { - align: 'left' as const, - field: ConfigKey.MONITOR_TYPE, - name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.monitorType', { - defaultMessage: 'Monitor type', - }), - sortable: true, - }, - { - align: 'left' as const, - field: ConfigKey.TAGS, - name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.tags', { - defaultMessage: 'Tags', - }), - render: (tags: string[]) => (tags ? : null), - }, - { - align: 'left' as const, - field: ConfigKey.LOCATIONS, - name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.locations', { - defaultMessage: 'Locations', - }), - render: (locations: ServiceLocations) => - locations ? : null, - }, - { - align: 'left' as const, - field: ConfigKey.SCHEDULE, - name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.schedule', { - defaultMessage: 'Frequency (min)', - }), - render: (schedule: CommonFields[ConfigKey.SCHEDULE]) => schedule?.number, - }, - { - align: 'left' as const, - field: ConfigKey.URLS, - name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.URL', { - defaultMessage: 'URL', - }), - sortable: true, - render: (urls: string, { hosts }: TCPSimpleFields | ICMPSimpleFields) => urls || hosts, - truncateText: true, - textOnly: true, - }, - { - align: 'left' as const, - field: ConfigKey.ENABLED as string, - name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.enabled', { - defaultMessage: 'Enabled', - }), - render: (_enabled: boolean, monitor: EncryptedSyntheticsMonitorWithId) => ( - - ), - }, - { - align: 'left' as const, - name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.actions', { - defaultMessage: 'Actions', - }), - render: (fields: EncryptedSyntheticsMonitorWithId) => ( - - ), - }, - ] as Array>; - - return ( - - - - - ); -}; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_list_container.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_list_container.tsx deleted file mode 100644 index 92b61cf1d744d..0000000000000 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_list_container.tsx +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useEffect, useReducer, useCallback, Reducer } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { useParams } from 'react-router-dom'; -import { useTrackPageview } from '@kbn/observability-plugin/public'; -import { ConfigKey } from '../../../../common/runtime_types'; -import { getMonitors } from '../../../state/actions'; -import { monitorManagementListSelector } from '../../../state/selectors'; -import { MonitorManagementListPageState } from './monitor_list'; -import { MonitorAsyncError } from './monitor_async_error'; -import { useInlineErrors } from '../hooks/use_inline_errors'; -import { MonitorListTabs } from './list_tabs'; -import { AllMonitors } from './all_monitors'; -import { InvalidMonitors } from './invalid_monitors'; -import { useInvalidMonitors } from '../hooks/use_invalid_monitors'; - -export const MonitorListContainer: React.FC = () => { - const [pageState, dispatchPageAction] = useReducer( - monitorManagementPageReducer, - { - pageIndex: 1, // saved objects page index is base 1 - pageSize: 10, - sortOrder: 'asc', - sortField: `${ConfigKey.NAME}.keyword`, - } - ); - - const onPageStateChange = useCallback( - (state) => { - dispatchPageAction({ type: 'update', payload: state }); - }, - [dispatchPageAction] - ); - - const onUpdate = useCallback(() => { - dispatchPageAction({ type: 'refresh' }); - }, [dispatchPageAction]); - - useTrackPageview({ app: 'uptime', path: 'manage-monitors' }); - useTrackPageview({ app: 'uptime', path: 'manage-monitors', delay: 15000 }); - - const dispatch = useDispatch(); - const monitorList = useSelector(monitorManagementListSelector); - - const { pageIndex, pageSize, sortField, sortOrder } = pageState as MonitorManagementListPageState; - - const { type: viewType } = useParams<{ type: 'all' | 'invalid' }>(); - const { errorSummaries, loading, count } = useInlineErrors({ - onlyInvalidMonitors: viewType === 'invalid', - sortField: pageState.sortField, - sortOrder: pageState.sortOrder, - }); - - useEffect(() => { - if (viewType === 'all') { - dispatch(getMonitors({ page: pageIndex, perPage: pageSize, sortField, sortOrder })); - } - }, [dispatch, pageState, pageIndex, pageSize, sortField, sortOrder, viewType]); - - const { data: monitorSavedObjects, loading: objectsLoading } = useInvalidMonitors(errorSummaries); - - return ( - <> - - - {viewType === 'all' ? ( - - ) : ( - - )} - - ); -}; - -type MonitorManagementPageAction = - | { - type: 'update'; - payload: MonitorManagementListPageState; - } - | { type: 'refresh' }; - -const monitorManagementPageReducer: Reducer< - MonitorManagementListPageState, - MonitorManagementPageAction -> = (state: MonitorManagementListPageState, action: MonitorManagementPageAction) => { - switch (action.type) { - case 'update': - return { - ...state, - ...action.payload, - }; - case 'refresh': - return { ...state }; - default: - throw new Error(`Action "${(action as MonitorManagementPageAction)?.type}" not recognizable`); - } -}; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/validation.test.ts b/x-pack/plugins/synthetics/public/components/monitor_management/validation.test.ts deleted file mode 100644 index 6fa57eb5cd964..0000000000000 --- a/x-pack/plugins/synthetics/public/components/monitor_management/validation.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - ConfigKey, - DataStream, - HTTPFields, - BrowserFields, - MonitorFields, - ScheduleUnit, - ServiceLocations, -} from '../../../common/runtime_types'; -import { validate, validateCommon } from './validation'; - -describe('[Monitor Management] validation', () => { - const commonPropsValid: Partial = { - [ConfigKey.SCHEDULE]: { number: '5', unit: ScheduleUnit.MINUTES }, - [ConfigKey.TIMEOUT]: '3m', - [ConfigKey.LOCATIONS]: [ - { - id: 'test-service-location', - isServiceManaged: true, - url: 'https:test-url.com', - geo: { lat: 33.33432323, lon: 73.23424221 }, - label: 'EU West', - }, - ] as ServiceLocations, - [ConfigKey.NAME]: 'test-name', - [ConfigKey.NAMESPACE]: 'namespace', - }; - - describe('Common monitor fields', () => { - it('should return false for all valid props', () => { - const result = Object.values(validateCommon).map((validator) => { - return validator ? validator(commonPropsValid) : true; - }); - - expect(result.reduce((previous, current) => previous || current)).toBeFalsy(); - }); - - it('should invalidate on invalid namespace', () => { - const validatorFn = validateCommon[ConfigKey.NAMESPACE]; - const result = [undefined, null, '', '*/&<>:', 'A', 'a'.repeat(101)].map((testValue) => - validatorFn?.({ [ConfigKey.NAMESPACE]: testValue } as Partial) - ); - - expect(result.reduce((previous, current) => previous && current)).toBeTruthy(); - }); - }); - - describe('HTTP', () => { - const httpPropsValid: Partial = { - ...commonPropsValid, - [ConfigKey.RESPONSE_STATUS_CHECK]: ['200', '204'], - [ConfigKey.RESPONSE_HEADERS_CHECK]: { 'Content-Type': 'application/json' }, - [ConfigKey.REQUEST_HEADERS_CHECK]: { 'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8' }, - [ConfigKey.MAX_REDIRECTS]: '3', - [ConfigKey.URLS]: 'https:// example-url.com', - }; - - it('should return false for all valid props', () => { - const validators = validate[DataStream.HTTP]; - const keysToValidate = [ - ConfigKey.SCHEDULE, - ConfigKey.TIMEOUT, - ConfigKey.LOCATIONS, - ConfigKey.RESPONSE_STATUS_CHECK, - ConfigKey.RESPONSE_HEADERS_CHECK, - ConfigKey.REQUEST_HEADERS_CHECK, - ConfigKey.MAX_REDIRECTS, - ConfigKey.URLS, - ]; - const validatorFns = keysToValidate.map((key) => validators[key]); - const result = validatorFns.map((fn) => fn?.(httpPropsValid) ?? true); - - expect(result).not.toEqual(expect.arrayContaining([true])); - }); - - it('should invalidate when locations is empty', () => { - const validators = validate[DataStream.HTTP]; - const validatorFn = validators[ConfigKey.LOCATIONS]; - const result = [undefined, null, []].map( - (testValue) => - validatorFn?.({ [ConfigKey.LOCATIONS]: testValue } as Partial) ?? false - ); - - expect(result).toEqual([true, true, true]); - }); - }); - - describe.each([ - [ConfigKey.SOURCE_INLINE, 'step(() => {});'], - [ConfigKey.SOURCE_ZIP_URL, 'https://test.zip'], - ])('Browser', (configKey, value) => { - const browserProps: Partial = { - ...commonPropsValid, - [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, - [ConfigKey.TIMEOUT]: undefined, - [configKey]: value, - }; - - it('should return false for all valid props', () => { - const validators = validate[DataStream.BROWSER]; - const keysToValidate = [ConfigKey.SCHEDULE, ConfigKey.TIMEOUT, configKey]; - const validatorFns = keysToValidate.map((key) => validators[key]); - const result = validatorFns.map((fn) => fn?.(browserProps) ?? true); - - expect(result).not.toEqual(expect.arrayContaining([true])); - }); - - it('should invalidate when locations is empty', () => { - const validators = validate[DataStream.BROWSER]; - const validatorFn = validators[ConfigKey.LOCATIONS]; - const result = [undefined, null, []].map( - (testValue) => - validatorFn?.({ [ConfigKey.LOCATIONS]: testValue } as Partial) ?? false - ); - - expect(result).toEqual([true, true, true]); - }); - }); - - // TODO: Add test for other monitor types if needed -}); diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx b/x-pack/plugins/synthetics/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx deleted file mode 100644 index 09b116c5bbc45..0000000000000 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useEffect } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; -import { isRight } from 'fp-ts/lib/Either'; -import { selectedFiltersSelector } from '../../../../state/selectors'; -import { AlertMonitorStatusComponent } from '../monitor_status_alert/alert_monitor_status'; -import { setSearchTextAction } from '../../../../state/actions'; -import { - AtomicStatusCheckParamsType, - GetMonitorAvailabilityParamsType, -} from '../../../../../common/runtime_types'; - -import { useSnapShotCount } from './use_snap_shot'; -import { FILTER_FIELDS } from '../../../../../common/constants'; - -const { TYPE, TAGS, LOCATION, PORT } = FILTER_FIELDS; - -interface Props { - ruleParams: { [key: string]: any }; - enabled: boolean; - numTimes: number; - setRuleParams: (key: string, value: any) => void; - timerange: { - from: string; - to: string; - }; -} - -export const AlertMonitorStatus: React.FC = ({ - enabled, - numTimes, - setRuleParams, - timerange, - ruleParams, -}) => { - const dispatch = useDispatch(); - - useEffect(() => { - if (ruleParams.search) { - dispatch(setSearchTextAction(ruleParams.search)); - } - }, [ruleParams, dispatch]); - - const { count, loading } = useSnapShotCount({ - query: ruleParams.search, - filters: ruleParams.filters, - }); - - const isOldAlert = React.useMemo( - () => - Object.entries(ruleParams).length > 0 && - !isRight(AtomicStatusCheckParamsType.decode(ruleParams)) && - !isRight(GetMonitorAvailabilityParamsType.decode(ruleParams)), - [ruleParams] - ); - - const selectedFilters = useSelector(selectedFiltersSelector); - useEffect(() => { - if (!ruleParams.filters && selectedFilters !== null) { - setRuleParams('filters', { - [PORT]: selectedFilters?.ports ?? [], - [LOCATION]: selectedFilters?.locations ?? [], - [TYPE]: selectedFilters?.schemes ?? [], - [TAGS]: selectedFilters?.tags ?? [], - }); - } - }, [ruleParams, setRuleParams, selectedFilters]); - - return ( - - ); -}; - -// eslint-disable-next-line import/no-default-export -export { AlertMonitorStatus as default }; diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx deleted file mode 100644 index 1fa06458e3a8b..0000000000000 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useCallback, useEffect, useState } from 'react'; -import { EuiCallOut, EuiSpacer, EuiHorizontalRule, EuiLoadingSpinner } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { FiltersExpressionsSelect, StatusExpressionSelect } from '../monitor_expressions'; -import { AddFilterButton } from './add_filter_btn'; -import { OldAlertCallOut } from './old_alert_call_out'; -import { AvailabilityExpressionSelect } from '../monitor_expressions/availability_expression_select'; -import { AlertQueryBar } from '../alert_query_bar/query_bar'; -import { useGetUrlParams } from '../../../../hooks'; -import { FILTER_FIELDS } from '../../../../../common/constants'; - -export interface AlertMonitorStatusProps { - ruleParams: { [key: string]: any }; - enabled: boolean; - isOldAlert: boolean; - snapshotCount: number; - snapshotLoading?: boolean; - numTimes: number; - setRuleParams: (key: string, value: any) => void; - timerange: { - from: string; - to: string; - }; -} - -export const hasFilters = (filters?: { [key: string]: string[] }) => { - if (!filters || Object.keys(filters).length === 0) { - return false; - } - - return Object.values(FILTER_FIELDS).some((f) => filters[f].length); -}; - -export const AlertMonitorStatusComponent: React.FC = (props) => { - const { ruleParams, isOldAlert, setRuleParams, snapshotCount, snapshotLoading } = props; - - const alertFilters = ruleParams?.filters ?? {}; - const [newFilters, setNewFilters] = useState( - Object.keys(alertFilters).filter((f) => alertFilters[f].length) - ); - - const { search = '' } = useGetUrlParams(); - - useEffect(() => { - if (search) { - setRuleParams('search', search); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const onSearchChange = useCallback( - (value: string) => { - setRuleParams('search', value); - }, - [setRuleParams] - ); - - return ( - <> - - - - {' '} - {snapshotLoading && } - - } - iconType="iInCircle" - /> - - - - - - - - { - setNewFilters([...newFilters, newFilter]); - }} - /> - - { - if (newFilters.includes(removeFilter)) { - setNewFilters(newFilters.filter((item) => item !== removeFilter)); - } - }} - setRuleParams={setRuleParams} - shouldUpdateUrl={false} - /> - - - - - - - - - - - - ); -}; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/status_badge.tsx b/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/status_badge.tsx deleted file mode 100644 index fe2c7730275db..0000000000000 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/status_badge.tsx +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiBadge, EuiToolTip } from '@elastic/eui'; -import React, { useContext, useState } from 'react'; -import { STATUS } from '../../../../../common/constants'; -import { getHealthMessage } from './monitor_status_column'; -import { UptimeThemeContext } from '../../../../contexts'; -import { PingError } from '../../../../../common/runtime_types'; -import { getInlineErrorLabel } from '../../../monitor_management/monitor_list/inline_error'; -import { StdErrorPopover } from '../../../monitor_management/monitor_list/stderr_logs_popover'; - -export const StatusBadge = ({ - status, - checkGroup, - summaryError, - monitorType, -}: { - status: string; - monitorType: string; - checkGroup?: string; - summaryError?: PingError; -}) => { - const { - colors: { dangerBehindText }, - } = useContext(UptimeThemeContext); - const [isOpen, setIsOpen] = useState(false); - - if (status === STATUS.UP) { - return ( - - {getHealthMessage(status)} - - ); - } - - const errorMessage = - monitorType !== 'browser' ? summaryError?.message : getInlineErrorLabel(summaryError?.message); - - const button = ( - - setIsOpen(true)} - onClickAriaLabel={errorMessage} - > - {getHealthMessage(status)} - - - ); - - if (monitorType !== 'browser') { - return button; - } - - return ( - - ); -}; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list.test.tsx b/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list.test.tsx deleted file mode 100644 index 495facf9c2cc8..0000000000000 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list.test.tsx +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { - MonitorSummariesResult, - CursorDirection, - SortOrder, - makePing, - Ping, - MonitorSummary, -} from '../../../../common/runtime_types'; -import { MonitorListComponent } from './monitor_list'; -import moment from 'moment'; -import { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public'; -import { mockMoment } from '../../../lib/helper/test_helpers'; -import { render } from '../../../lib/helper/rtl_helpers'; -import { NO_DATA_MESSAGE } from './translations'; - -const testFooPings: Ping[] = [ - makePing({ - docId: 'foo1', - id: 'foo', - type: 'icmp', - status: 'up', - duration: 123, - timestamp: '124', - ip: '127.0.0.1', - }), - makePing({ - docId: 'foo2', - id: 'foo', - type: 'icmp', - status: 'up', - duration: 123, - timestamp: '125', - ip: '127.0.0.2', - }), - makePing({ - docId: 'foo3', - id: 'foo', - type: 'icmp', - status: 'down', - duration: 123, - timestamp: '126', - ip: '127.0.0.3', - }), -]; - -const testFooSummary: MonitorSummary = { - monitor_id: 'foo', - state: { - monitor: { type: 'http', duration: { us: 1000 } }, - summaryPings: testFooPings, - summary: { - up: 1, - down: 2, - }, - timestamp: '123', - url: {}, - }, -}; - -const testBarPings: Ping[] = [ - makePing({ - docId: 'bar1', - id: 'bar', - type: 'icmp', - status: 'down', - duration: 123, - timestamp: '125', - ip: '127.0.0.1', - }), - makePing({ - docId: 'bar2', - id: 'bar', - type: 'icmp', - status: 'down', - duration: 123, - timestamp: '126', - ip: '127.0.0.1', - }), -]; - -const testBarSummary: MonitorSummary = { - monitor_id: 'bar', - state: { - monitor: { type: 'http', duration: { us: 1000 } }, - summaryPings: testBarPings, - summary: { - up: 2, - down: 0, - }, - timestamp: '125', - url: {}, - }, -}; - -describe('MonitorList component', () => { - let localStorageMock: any; - - beforeAll(() => { - mockMoment(); - global.innerWidth = 1200; - - // Trigger the window resize event. - global.dispatchEvent(new Event('resize')); - }); - - const getMonitorList = (timestamp?: string): MonitorSummariesResult => { - if (timestamp) { - testBarSummary.state.timestamp = timestamp; - testFooSummary.state.timestamp = timestamp; - } else { - testBarSummary.state.timestamp = '125'; - testFooSummary.state.timestamp = '123'; - } - return { - nextPagePagination: null, - prevPagePagination: null, - summaries: [testFooSummary, testBarSummary], - }; - }; - - beforeEach(() => { - localStorageMock = { - getItem: jest.fn().mockImplementation(() => '25'), - setItem: jest.fn(), - }; - - global.localStorage = localStorageMock; - }); - - it('renders a no items message when no data is provided', async () => { - const { findByText } = render( - - ); - expect(await findByText(NO_DATA_MESSAGE)).toBeInTheDocument(); - }); - - it('renders the monitor list', async () => { - const { findByLabelText } = render( - - ); - - expect( - await findByLabelText( - 'Monitor Status table with columns for Status, Name, URL, IP, Downtime History and Integrations. The table is currently displaying 2 items.' - ) - ).toBeInTheDocument(); - }); - - it('renders error list', async () => { - const { findByText } = render( - , - loading: false, - }} - pageSize={10} - setPageSize={jest.fn()} - refreshedMonitorIds={[]} - /> - ); - - expect(await findByText('foo message')).toBeInTheDocument(); - }); - - describe('MonitorListPagination component', () => { - let paginationResult: MonitorSummariesResult; - - beforeEach(() => { - paginationResult = { - prevPagePagination: JSON.stringify({ - cursorKey: { monitor_id: 123 }, - cursorDirection: CursorDirection.BEFORE, - sortOrder: SortOrder.ASC, - }), - nextPagePagination: JSON.stringify({ - cursorKey: { monitor_id: 456 }, - cursorDirection: CursorDirection.AFTER, - sortOrder: SortOrder.ASC, - }), - summaries: [testFooSummary, testBarSummary], - }; - }); - - it('renders the pagination', async () => { - const { findByText, findByLabelText } = render( - - ); - - expect(await findByText('Rows per page: 10')).toBeInTheDocument(); - expect(await findByLabelText('Prev page of results')).toBeInTheDocument(); - expect(await findByLabelText('Next page of results')).toBeInTheDocument(); - }); - }); - - describe('responsive behavior', () => { - describe('xl screens', () => { - beforeAll(() => { - global.innerWidth = 1200; - - // Trigger the window resize event. - global.dispatchEvent(new Event('resize')); - }); - - it('shows ping histogram and expand button on xl and xxl screens', async () => { - const list = getMonitorList(moment().subtract(5, 'minute').toISOString()); - const { getByTestId, getByText } = render( - - ); - - await waitFor(() => { - expect( - getByTestId( - `xpack.synthetics.monitorList.${list.summaries[0].monitor_id}.expandMonitorDetail` - ) - ).toBeInTheDocument(); - expect(getByText('Downtime history')).toBeInTheDocument(); - }); - }); - }); - - describe('large and medium screens', () => { - it('hides ping histogram and expand button on extra large screens', async () => { - global.innerWidth = 1199; - - // Trigger the window resize event. - global.dispatchEvent(new Event('resize')); - - const { queryByTestId, queryByText } = render( - - ); - - await waitFor(() => { - expect( - queryByTestId('xpack.synthetics.monitorList.always-down.expandMonitorDetail') - ).not.toBeInTheDocument(); - expect(queryByText('Downtime history')).not.toBeInTheDocument(); - }); - }); - }); - }); -}); diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list.tsx b/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list.tsx deleted file mode 100644 index bf33bc52ffb68..0000000000000 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list.tsx +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 useWindowSize from 'react-use/lib/useWindowSize'; -import useDebounce from 'react-use/lib/useDebounce'; -import { - EuiButtonIcon, - EuiBasicTable, - EuiFlexGroup, - EuiFlexItem, - EuiLink, - EuiPanel, - EuiSpacer, - getBreakpoint, -} from '@elastic/eui'; -import { euiStyled } from '@kbn/kibana-react-plugin/common'; -import { X509Expiry } from '../../../../common/runtime_types'; -import { MonitorSummary } from '../../../../common/runtime_types'; -import { MonitorListStatusColumn } from './columns/monitor_status_column'; -import { ExpandedRowMap } from './types'; -import { MonitorBarSeries } from '../../common/charts'; -import { OverviewPageLink } from './overview_page_link'; -import * as labels from './translations'; -import { MonitorListPageSizeSelect } from './monitor_list_page_size_select'; -import { MonitorListDrawer } from './monitor_list_drawer/list_drawer_container'; -import { MonitorListProps } from './monitor_list_container'; -import { MonitorList } from '../../../state/reducers/monitor_list'; -import { CertStatusColumn } from './columns/cert_status_column'; -import { MonitorListHeader } from './monitor_list_header'; -import { TAGS_LABEL, URL_LABEL } from '../../common/translations'; -import { EnableMonitorAlert } from './columns/enable_alert'; -import { STATUS_ALERT_COLUMN, TEST_NOW_COLUMN } from './translations'; -import { MonitorNameColumn } from './columns/monitor_name_col'; -import { MonitorTags } from '../../common/monitor_tags'; -import { useMonitorHistogram } from './use_monitor_histogram'; -import { TestNowColumn } from './columns/test_now_col'; -import { NoItemsMessage } from './no_items_message'; - -interface Props extends MonitorListProps { - pageSize: number; - setPageSize: (val: number) => void; - monitorList: MonitorList; - refreshedMonitorIds: string[]; -} - -export const MonitorListComponent: ({ - filters, - monitorList: { list, error, loading }, - pageSize, - refreshedMonitorIds, - setPageSize, -}: Props) => any = ({ - filters, - refreshedMonitorIds = [], - monitorList: { list, error, loading }, - pageSize, - setPageSize, -}) => { - const [expandedDrawerIds, updateExpandedDrawerIds] = useState([]); - const { width } = useWindowSize(); - const [hideExtraColumns, setHideExtraColumns] = useState(false); - - useDebounce( - () => { - setHideExtraColumns(['m', 'l'].includes(getBreakpoint(width) ?? '')); - }, - 50, - [width] - ); - - const items = list.summaries ?? []; - - const { histogramsById, minInterval } = useMonitorHistogram({ items }); - - const nextPagePagination = list.nextPagePagination ?? ''; - const prevPagePagination = list.prevPagePagination ?? ''; - - const toggleDrawer = (id: string) => { - if (expandedDrawerIds.includes(id)) { - updateExpandedDrawerIds(expandedDrawerIds.filter((p) => p !== id)); - } else { - updateExpandedDrawerIds([...expandedDrawerIds, id]); - } - }; - - const getExpandedRowMap = () => { - return expandedDrawerIds.reduce((map: ExpandedRowMap, id: string) => { - return { - ...map, - [id]: ( - monitorId === id)!} - /> - ), - }; - }, {}); - }; - - const columns = [ - ...[ - { - align: 'left' as const, - field: 'state.summary.status', - name: labels.STATUS_COLUMN_LABEL, - mobileOptions: { - fullWidth: true, - }, - render: ( - status: string, - { - monitor_id: monitorId, - state: { - timestamp, - summaryPings, - monitor: { type, duration, checkGroup }, - error: summaryError, - }, - configId, - }: MonitorSummary - ) => { - return ( - - ); - }, - }, - { - align: 'left' as const, - field: 'state.monitor.name', - name: labels.NAME_COLUMN_LABEL, - mobileOptions: { - fullWidth: true, - }, - render: (_name: string, summary: MonitorSummary) => , - sortable: true, - }, - { - align: 'left' as const, - field: 'state.url.full', - name: URL_LABEL, - width: '30%', - render: (url: string) => ( - - {url} - - ), - }, - { - align: 'left' as const, - field: 'state.monitor.name', - name: TAGS_LABEL, - width: '12%', - render: (_name: string, summary: MonitorSummary) => , - }, - { - align: 'left' as const, - field: 'state.tls.server.x509', - name: labels.TLS_COLUMN_LABEL, - render: (x509: X509Expiry) => , - }, - ], - ...(!hideExtraColumns - ? [ - { - align: 'left' as const, - field: 'monitor_id', - name: labels.HISTORY_COLUMN_LABEL, - mobileOptions: { - show: false, - }, - render: (monitorId: string) => ( - - ), - }, - ] - : []), - { - align: 'center' as const, - field: '', - name: STATUS_ALERT_COLUMN, - width: '100px', - render: (item: MonitorSummary) => ( - - ), - }, - { - align: 'center' as const, - field: '', - name: TEST_NOW_COLUMN, - width: '100px', - render: (item: MonitorSummary) => ( - - ), - }, - ...(!hideExtraColumns - ? [ - { - align: 'right' as const, - field: 'monitor_id', - name: '', - sortable: true, - isExpander: true, - width: '40px', - render: (id: string) => { - return ( - toggleDrawer(id)} - /> - ); - }, - }, - ] - : []), - ]; - - return ( - - - - } - columns={columns} - tableLayout={'auto'} - rowProps={ - hideExtraColumns - ? ({ monitor_id: monitorId }) => ({ - onClick: () => toggleDrawer(monitorId), - 'aria-label': labels.getExpandDrawerLabel(monitorId), - }) - : ({ monitor_id: monitorId }) => ({ - className: refreshedMonitorIds.includes(monitorId) ? 'refresh-row' : undefined, - }) - } - /> - - - - - - - - - - - - - - - - - - ); -}; - -const WrapperPanel = euiStyled(EuiPanel)` - &&& { - .refresh-row{ - background-color: #f0f4fb; - -webkit-transition: background-color 3000ms linear; - -ms-transition: background-color 3000ms linear; - transition: background-color 3000ms linear; - } - } -`; diff --git a/x-pack/plugins/synthetics/public/components/synthetics/status_badge.tsx b/x-pack/plugins/synthetics/public/components/synthetics/status_badge.tsx deleted file mode 100644 index be9eb6178deae..0000000000000 --- a/x-pack/plugins/synthetics/public/components/synthetics/status_badge.tsx +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React, { useContext, FC } from 'react'; -import { UptimeAppColors } from '../../apps/uptime_app'; -import { UptimeThemeContext } from '../../contexts'; - -interface StatusBadgeProps { - isMobile?: boolean; - status?: string; - stepNo: number; -} - -export function colorFromStatus(color: UptimeAppColors, status?: string) { - switch (status) { - case 'succeeded': - return color.success; - case 'failed': - return color.dangerBehindText; - default: - return 'default'; - } -} - -export function textFromStatus(status?: string) { - switch (status) { - case 'succeeded': - return i18n.translate('xpack.synthetics.synthetics.statusBadge.succeededMessage', { - defaultMessage: 'Succeeded', - }); - case 'failed': - return i18n.translate('xpack.synthetics.synthetics.statusBadge.failedMessage', { - defaultMessage: 'Failed', - }); - case 'skipped': - return i18n.translate('xpack.synthetics.synthetics.statusBadge.skippedMessage', { - defaultMessage: 'Skipped', - }); - default: - return null; - } -} - -export const StatusBadge: FC = ({ status, stepNo, isMobile }) => { - const theme = useContext(UptimeThemeContext); - return ( - - {!isMobile && ( - - - {stepNo}. - - - )} - - {textFromStatus(status)} - - - ); -}; diff --git a/x-pack/plugins/synthetics/public/hooks/index.ts b/x-pack/plugins/synthetics/public/hooks/index.ts deleted file mode 100644 index b93373481f9f3..0000000000000 --- a/x-pack/plugins/synthetics/public/hooks/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from './use_composite_image'; -export * from './update_kuery_string'; -export * from './use_monitor'; -export * from './use_search_text'; -export * from './use_cert_status'; -export * from './use_telemetry'; -export * from './use_url_params'; -export * from './use_breakpoints'; -export { useUptimeDataView } from '../contexts/uptime_data_view_context'; diff --git a/x-pack/plugins/synthetics/public/hooks/use_breadcrumbs.test.tsx b/x-pack/plugins/synthetics/public/hooks/use_breadcrumbs.test.tsx deleted file mode 100644 index e5b1a28ba72c6..0000000000000 --- a/x-pack/plugins/synthetics/public/hooks/use_breadcrumbs.test.tsx +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ChromeBreadcrumb } from '@kbn/core/public'; -import React from 'react'; -import { Route } from 'react-router-dom'; -import { mountWithRouter } from '../lib'; -import { OVERVIEW_ROUTE } from '../../common/constants'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { UptimeUrlParams, getSupportedUrlParams, MountWithReduxProvider } from '../lib/helper'; -import { makeBaseBreadcrumb, useBreadcrumbs } from './use_breadcrumbs'; - -describe('useBreadcrumbs', () => { - it('sets the given breadcrumbs', () => { - const [getBreadcrumbs, core] = mockCore(); - - const expectedCrumbs: ChromeBreadcrumb[] = [ - { text: 'Crumb: ', href: 'http://href.example.net' }, - { text: 'Crumb II: Son of Crumb', href: 'http://href2.example.net' }, - ]; - - const Component = () => { - useBreadcrumbs(expectedCrumbs); - return <>Hello; - }; - - mountWithRouter( - - - - - - - - ); - - const urlParams: UptimeUrlParams = getSupportedUrlParams({}); - expect(JSON.stringify(getBreadcrumbs())).toEqual( - JSON.stringify( - makeBaseBreadcrumb('/app/uptime', '/app/observability', urlParams).concat(expectedCrumbs) - ) - ); - }); -}); - -const mockCore: () => [() => ChromeBreadcrumb[], any] = () => { - let breadcrumbObj: ChromeBreadcrumb[] = []; - const get = () => { - return breadcrumbObj; - }; - const core = { - application: { - getUrlForApp: (app: string) => (app === 'uptime' ? '/app/uptime' : '/app/observability'), - navigateToUrl: jest.fn(), - }, - chrome: { - setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => { - breadcrumbObj = newBreadcrumbs; - }, - }, - }; - - return [get, core]; -}; diff --git a/x-pack/plugins/synthetics/public/hooks/use_breadcrumbs.ts b/x-pack/plugins/synthetics/public/hooks/use_breadcrumbs.ts deleted file mode 100644 index cac62a79d92bf..0000000000000 --- a/x-pack/plugins/synthetics/public/hooks/use_breadcrumbs.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ChromeBreadcrumb } from '@kbn/core/public'; -import { i18n } from '@kbn/i18n'; -import { MouseEvent, useEffect } from 'react'; -import { EuiBreadcrumb } from '@elastic/eui'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { UptimeUrlParams } from '../lib/helper'; -import { stringifyUrlParams } from '../lib/helper/stringify_url_params'; -import { useUrlParams } from '.'; -import { PLUGIN } from '../../common/constants/plugin'; - -const EMPTY_QUERY = '?'; - -function handleBreadcrumbClick( - breadcrumbs: ChromeBreadcrumb[], - navigateToHref?: (url: string) => Promise -) { - return breadcrumbs.map((bc) => ({ - ...bc, - ...(bc.href - ? { - onClick: (event: MouseEvent) => { - if (navigateToHref && bc.href) { - event.preventDefault(); - navigateToHref(bc.href); - } - }, - } - : {}), - })); -} - -export const makeBaseBreadcrumb = ( - uptimePath: string, - observabilityPath: string, - params?: UptimeUrlParams -): [EuiBreadcrumb, EuiBreadcrumb] => { - if (params) { - const crumbParams: Partial = { ...params }; - - delete crumbParams.statusFilter; - const query = stringifyUrlParams(crumbParams, true); - uptimePath += query === EMPTY_QUERY ? '' : query; - } - - return [ - { - text: i18n.translate('xpack.synthetics.breadcrumbs.observabilityText', { - defaultMessage: 'Observability', - }), - href: observabilityPath, - }, - { - text: i18n.translate('xpack.synthetics.breadcrumbs.overviewBreadcrumbText', { - defaultMessage: 'Uptime', - }), - href: uptimePath, - }, - ]; -}; - -export const useBreadcrumbs = (extraCrumbs: ChromeBreadcrumb[]) => { - const params = useUrlParams()[0](); - const kibana = useKibana(); - const setBreadcrumbs = kibana.services.chrome?.setBreadcrumbs; - const uptimePath = kibana.services.application?.getUrlForApp(PLUGIN.ID) ?? ''; - const observabilityPath = - kibana.services.application?.getUrlForApp('observability-overview') ?? ''; - const navigate = kibana.services.application?.navigateToUrl; - - useEffect(() => { - if (setBreadcrumbs) { - setBreadcrumbs( - handleBreadcrumbClick( - makeBaseBreadcrumb(uptimePath, observabilityPath, params).concat(extraCrumbs), - navigate - ) - ); - } - }, [uptimePath, observabilityPath, extraCrumbs, navigate, params, setBreadcrumbs]); -}; diff --git a/x-pack/plugins/synthetics/public/hooks/use_telemetry.ts b/x-pack/plugins/synthetics/public/hooks/use_telemetry.ts deleted file mode 100644 index d6e1a3ae28516..0000000000000 --- a/x-pack/plugins/synthetics/public/hooks/use_telemetry.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useEffect } from 'react'; -import { useGetUrlParams } from './use_url_params'; -import { apiService } from '../state/api/utils'; -import { API_URLS } from '../../common/constants'; - -export enum UptimePage { - Overview = 'Overview', - MappingError = 'MappingError', - Monitor = 'Monitor', - MonitorAdd = 'AddMonitor', - MonitorEdit = 'EditMonitor', - MonitorManagement = 'MonitorManagement', - Settings = 'Settings', - Certificates = 'Certificates', - StepDetail = 'StepDetail', - SyntheticCheckStepsPage = 'SyntheticCheckStepsPage', - NotFound = '__not-found__', -} - -export const useUptimeTelemetry = (page?: UptimePage) => { - const { dateRangeStart, dateRangeEnd, autorefreshInterval, autorefreshIsPaused } = - useGetUrlParams(); - - useEffect(() => { - if (!apiService.http) throw new Error('Core http services are not defined'); - - const params = { - page, - autorefreshInterval: autorefreshInterval / 1000, // divide by 1000 to keep it in secs - dateStart: dateRangeStart, - dateEnd: dateRangeEnd, - autoRefreshEnabled: !autorefreshIsPaused, - }; - setTimeout(() => { - apiService.post(API_URLS.LOG_PAGE_VIEW, params); - }, 100); - }, [autorefreshInterval, autorefreshIsPaused, dateRangeEnd, dateRangeStart, page]); -}; diff --git a/x-pack/plugins/synthetics/public/icons/heartbeat_white.svg b/x-pack/plugins/synthetics/public/icons/heartbeat_white.svg deleted file mode 100644 index 866206f32f5a1..0000000000000 --- a/x-pack/plugins/synthetics/public/icons/heartbeat_white.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - \ No newline at end of file diff --git a/x-pack/plugins/synthetics/public/index.ts b/x-pack/plugins/synthetics/public/index.ts index 86170e9bc863a..cc0ad290e6f0a 100644 --- a/x-pack/plugins/synthetics/public/index.ts +++ b/x-pack/plugins/synthetics/public/index.ts @@ -6,7 +6,7 @@ */ import { PluginInitializerContext } from '@kbn/core/public'; -import { UptimePlugin } from './apps/plugin'; +import { UptimePlugin } from './plugin'; export const plugin = (initializerContext: PluginInitializerContext) => new UptimePlugin(initializerContext); diff --git a/x-pack/plugins/synthetics/public/apps/__snapshots__/uptime_page_template.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/app/__snapshots__/uptime_page_template.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/apps/__snapshots__/uptime_page_template.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/app/__snapshots__/uptime_page_template.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/app/render_app.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/app/render_app.tsx new file mode 100644 index 0000000000000..446a329901815 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/app/render_app.tsx @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { i18n as i18nFormatter } from '@kbn/i18n'; +import { AppMountParameters, CoreStart } from '@kbn/core/public'; +import { getIntegratedAppAvailability } from '../lib/adapters/framework/capabilities_adapter'; +import { + DEFAULT_DARK_MODE, + DEFAULT_TIMEPICKER_QUICK_RANGES, + INTEGRATED_SOLUTIONS, +} from '../../../common/constants'; +import { UptimeApp, UptimeAppProps } from './uptime_app'; +import { ClientPluginsSetup, ClientPluginsStart } from '../../plugin'; + +export function renderApp( + core: CoreStart, + plugins: ClientPluginsSetup, + startPlugins: ClientPluginsStart, + appMountParameters: AppMountParameters, + isDev: boolean +) { + const { + application: { capabilities }, + chrome: { setBadge, setHelpExtension }, + docLinks, + http: { basePath }, + i18n, + } = core; + + const { apm, infrastructure, logs } = getIntegratedAppAvailability( + capabilities, + INTEGRATED_SOLUTIONS + ); + + const canSave = (capabilities.uptime.save ?? false) as boolean; + + const props: UptimeAppProps = { + isDev, + plugins, + canSave, + core, + i18n, + startPlugins, + basePath: basePath.get(), + darkMode: core.uiSettings.get(DEFAULT_DARK_MODE), + commonlyUsedRanges: core.uiSettings.get(DEFAULT_TIMEPICKER_QUICK_RANGES), + isApmAvailable: apm, + isInfraAvailable: infrastructure, + isLogsAvailable: logs, + renderGlobalHelpControls: () => + setHelpExtension({ + appName: i18nFormatter.translate('xpack.synthetics.legacyHeader.appName', { + defaultMessage: 'Uptime', + }), + links: [ + { + linkType: 'documentation', + href: `${docLinks.links.observability.monitorUptime}`, + }, + { + linkType: 'discuss', + href: 'https://discuss.elastic.co/c/uptime', + }, + ], + }), + setBadge, + appMountParameters, + setBreadcrumbs: core.chrome.setBreadcrumbs, + }; + + ReactDOM.render(, appMountParameters.element); + + return () => { + ReactDOM.unmountComponentAtNode(appMountParameters.element); + }; +} diff --git a/x-pack/plugins/synthetics/public/apps/uptime_app.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/app/uptime_app.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/apps/uptime_app.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/app/uptime_app.tsx index 9887fa81393bc..8d694bfe81aed 100644 --- a/x-pack/plugins/synthetics/public/apps/uptime_app.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/app/uptime_app.tsx @@ -18,7 +18,7 @@ import { } from '@kbn/kibana-react-plugin/public'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { InspectorContextProvider } from '@kbn/observability-plugin/public'; -import { ClientPluginsSetup, ClientPluginsStart } from './plugin'; +import { ClientPluginsSetup, ClientPluginsStart } from '../../plugin'; import { UMUpdateBadge } from '../lib/lib'; import { UptimeRefreshContextProvider, diff --git a/x-pack/plugins/synthetics/public/apps/uptime_overview_fetcher.ts b/x-pack/plugins/synthetics/public/legacy_uptime/app/uptime_overview_fetcher.ts similarity index 100% rename from x-pack/plugins/synthetics/public/apps/uptime_overview_fetcher.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/app/uptime_overview_fetcher.ts diff --git a/x-pack/plugins/synthetics/public/apps/uptime_page_template.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/app/uptime_page_template.test.tsx similarity index 89% rename from x-pack/plugins/synthetics/public/apps/uptime_page_template.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/app/uptime_page_template.test.tsx index 26a40074c7530..4efaf26a7ac11 100644 --- a/x-pack/plugins/synthetics/public/apps/uptime_page_template.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/app/uptime_page_template.test.tsx @@ -10,10 +10,10 @@ import React from 'react'; import 'jest-styled-components'; import { render } from '../lib/helper/rtl_helpers'; import { UptimePageTemplateComponent } from './uptime_page_template'; -import { OVERVIEW_ROUTE } from '../../common/constants'; -import { useBreakpoints } from '../hooks/use_breakpoints'; +import { OVERVIEW_ROUTE } from '../../../common/constants'; +import { useBreakpoints } from '../../hooks/use_breakpoints'; -jest.mock('../hooks/use_breakpoints', () => { +jest.mock('../../hooks/use_breakpoints', () => { const down = jest.fn().mockReturnValue(false); return { useBreakpoints: () => ({ down }), diff --git a/x-pack/plugins/synthetics/public/apps/uptime_page_template.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/app/uptime_page_template.tsx similarity index 93% rename from x-pack/plugins/synthetics/public/apps/uptime_page_template.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/app/uptime_page_template.tsx index e359916de1d22..fa3ad7e0805e8 100644 --- a/x-pack/plugins/synthetics/public/apps/uptime_page_template.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/app/uptime_page_template.tsx @@ -10,13 +10,13 @@ import styled from 'styled-components'; import { EuiPageHeaderProps, EuiPageTemplateProps } from '@elastic/eui'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { useInspectorContext } from '@kbn/observability-plugin/public'; -import { CERTIFICATES_ROUTE, OVERVIEW_ROUTE } from '../../common/constants'; -import { ClientPluginsStart } from './plugin'; +import { CERTIFICATES_ROUTE, OVERVIEW_ROUTE } from '../../../common/constants'; +import { ClientPluginsStart } from '../../plugin'; import { useNoDataConfig } from './use_no_data_config'; import { EmptyStateLoading } from '../components/overview/empty_state/empty_state_loading'; import { EmptyStateError } from '../components/overview/empty_state/empty_state_error'; import { useHasData } from '../components/overview/empty_state/use_has_data'; -import { useBreakpoints } from '../hooks/use_breakpoints'; +import { useBreakpoints } from '../../hooks/use_breakpoints'; interface Props { path: string; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/app/use_no_data_config.ts b/x-pack/plugins/synthetics/public/legacy_uptime/app/use_no_data_config.ts new file mode 100644 index 0000000000000..c485c498e6fa7 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/app/use_no_data_config.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { useContext } from 'react'; +import { useSelector } from 'react-redux'; +import { KibanaPageTemplateProps, useKibana } from '@kbn/kibana-react-plugin/public'; +import { UptimeSettingsContext } from '../contexts'; +import { ClientPluginsStart } from '../../plugin'; +import { indexStatusSelector } from '../state/selectors'; + +export function useNoDataConfig(): KibanaPageTemplateProps['noDataConfig'] { + const { basePath } = useContext(UptimeSettingsContext); + + const { + services: { docLinks }, + } = useKibana(); + + const { data } = useSelector(indexStatusSelector); + + // Returns no data config when there is no historical data + if (data && !data.indexExists) { + return { + solution: i18n.translate('xpack.synthetics.noDataConfig.solutionName', { + defaultMessage: 'Observability', + }), + actions: { + beats: { + title: i18n.translate('xpack.synthetics.noDataConfig.beatsCard.title', { + defaultMessage: 'Add monitors with Heartbeat', + }), + description: i18n.translate('xpack.synthetics.noDataConfig.beatsCard.description', { + defaultMessage: + 'Proactively monitor the availability of your sites and services. Receive alerts and resolve issues faster to optimize your users experience.', + }), + href: basePath + `/app/home#/tutorial/uptimeMonitors`, + }, + }, + docsLink: docLinks!.links.observability.guide, + }; + } +} diff --git a/x-pack/plugins/synthetics/public/components/certificates/__snapshots__/cert_monitors.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/__snapshots__/cert_monitors.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/certificates/__snapshots__/cert_monitors.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/__snapshots__/cert_monitors.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/certificates/__snapshots__/cert_search.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/__snapshots__/cert_search.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/certificates/__snapshots__/cert_search.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/__snapshots__/cert_search.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/certificates/__snapshots__/cert_status.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/__snapshots__/cert_status.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/certificates/__snapshots__/cert_status.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/__snapshots__/cert_status.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/certificates/cert_monitors.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/cert_monitors.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/certificates/cert_monitors.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/cert_monitors.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/certificates/cert_monitors.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/cert_monitors.tsx similarity index 93% rename from x-pack/plugins/synthetics/public/components/certificates/cert_monitors.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/cert_monitors.tsx index 30b8b572c783f..0688aae060fba 100644 --- a/x-pack/plugins/synthetics/public/components/certificates/cert_monitors.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/cert_monitors.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { EuiToolTip } from '@elastic/eui'; -import { CertMonitor } from '../../../common/runtime_types'; +import { CertMonitor } from '../../../../common/runtime_types'; import { MonitorPageLink } from '../common/monitor_page_link'; interface Props { diff --git a/x-pack/plugins/synthetics/public/components/certificates/cert_refresh_btn.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/cert_refresh_btn.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/certificates/cert_refresh_btn.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/cert_refresh_btn.tsx diff --git a/x-pack/plugins/synthetics/public/components/certificates/cert_search.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/cert_search.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/certificates/cert_search.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/cert_search.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/certificates/cert_search.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/cert_search.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/certificates/cert_search.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/cert_search.tsx diff --git a/x-pack/plugins/synthetics/public/components/certificates/cert_status.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/cert_status.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/certificates/cert_status.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/cert_status.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/certificates/cert_status.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/cert_status.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/certificates/cert_status.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/cert_status.tsx index 7ade37b33d622..ec11fc240d6a1 100644 --- a/x-pack/plugins/synthetics/public/components/certificates/cert_status.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/cert_status.tsx @@ -11,10 +11,10 @@ import styled from 'styled-components'; import { EuiHealth, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { useSelector } from 'react-redux'; -import { Cert } from '../../../common/runtime_types'; +import { Cert } from '../../../../common/runtime_types'; import { useCertStatus } from '../../hooks'; import * as labels from './translations'; -import { CERT_STATUS } from '../../../common/constants'; +import { CERT_STATUS } from '../../../../common/constants'; import { selectDynamicSettings } from '../../state/selectors'; interface Props { diff --git a/x-pack/plugins/synthetics/public/components/certificates/certificate_title.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/certificate_title.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/certificates/certificate_title.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/certificate_title.tsx diff --git a/x-pack/plugins/synthetics/public/components/certificates/certificates_list.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/certificates_list.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/certificates/certificates_list.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/certificates_list.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/certificates/certificates_list.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/certificates_list.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/certificates/certificates_list.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/certificates_list.tsx index 44d314b52356f..381ee9cd5b792 100644 --- a/x-pack/plugins/synthetics/public/components/certificates/certificates_list.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/certificates_list.tsx @@ -11,7 +11,7 @@ import { Direction, EuiBasicTable } from '@elastic/eui'; import { CertStatus } from './cert_status'; import { CertMonitors } from './cert_monitors'; import * as labels from './translations'; -import { Cert, CertMonitor, CertResult } from '../../../common/runtime_types'; +import { Cert, CertMonitor, CertResult } from '../../../../common/runtime_types'; import { FingerprintCol } from './fingerprint_col'; import { LOADING_CERTIFICATES, NO_CERTS_AVAILABLE } from './translations'; diff --git a/x-pack/plugins/synthetics/public/components/certificates/fingerprint_col.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/fingerprint_col.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/certificates/fingerprint_col.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/fingerprint_col.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/certificates/fingerprint_col.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/fingerprint_col.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/certificates/fingerprint_col.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/fingerprint_col.tsx index fa29c0b7d1e43..dac9f239eb104 100644 --- a/x-pack/plugins/synthetics/public/components/certificates/fingerprint_col.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/fingerprint_col.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { EuiButtonEmpty, EuiButtonIcon, EuiCopy, EuiToolTip } from '@elastic/eui'; import styled from 'styled-components'; -import { Cert } from '../../../common/runtime_types'; +import { Cert } from '../../../../common/runtime_types'; import { COPY_FINGERPRINT } from './translations'; const EmptyButton = styled(EuiButtonEmpty)` diff --git a/x-pack/plugins/synthetics/public/components/certificates/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/certificates/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/index.ts diff --git a/x-pack/plugins/synthetics/public/components/certificates/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/translations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/certificates/translations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/translations.ts diff --git a/x-pack/plugins/synthetics/public/components/certificates/use_cert_search.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/use_cert_search.ts similarity index 91% rename from x-pack/plugins/synthetics/public/components/certificates/use_cert_search.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/use_cert_search.ts index 47facebe4401f..4a13a7fc68d15 100644 --- a/x-pack/plugins/synthetics/public/components/certificates/use_cert_search.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/certificates/use_cert_search.ts @@ -9,7 +9,7 @@ import { useSelector } from 'react-redux'; import { useContext } from 'react'; import { createEsParams, useEsSearch } from '@kbn/observability-plugin/public'; -import { CertResult, GetCertsParams, Ping } from '../../../common/runtime_types'; +import { CertResult, GetCertsParams, Ping } from '../../../../common/runtime_types'; import { selectDynamicSettings } from '../../state/selectors'; import { @@ -20,7 +20,7 @@ import { DEFAULT_TO, getCertsRequestBody, processCertsResult, -} from '../../../common/requests/get_certs_request_body'; +} from '../../../../common/requests/get_certs_request_body'; import { UptimeRefreshContext } from '../../contexts'; export const useCertSearch = ({ diff --git a/x-pack/plugins/synthetics/public/components/common/__snapshots__/location_link.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/__snapshots__/location_link.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/__snapshots__/location_link.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/__snapshots__/location_link.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/common/__snapshots__/monitor_page_link.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/__snapshots__/monitor_page_link.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/__snapshots__/monitor_page_link.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/__snapshots__/monitor_page_link.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/common/__snapshots__/monitor_tags.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/__snapshots__/monitor_tags.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/__snapshots__/monitor_tags.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/__snapshots__/monitor_tags.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/common/alerts/uptime_edit_alert_flyout.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/alerts/uptime_edit_alert_flyout.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/alerts/uptime_edit_alert_flyout.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/alerts/uptime_edit_alert_flyout.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/charts/__snapshots__/chart_empty_state.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/chart_empty_state.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/__snapshots__/chart_empty_state.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/chart_empty_state.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/common/charts/__snapshots__/chart_wrapper.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/chart_wrapper.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/__snapshots__/chart_wrapper.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/chart_wrapper.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/common/charts/__snapshots__/donut_chart.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/__snapshots__/donut_chart.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/common/charts/__snapshots__/donut_chart_legend_row.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart_legend_row.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/__snapshots__/donut_chart_legend_row.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart_legend_row.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/common/charts/__snapshots__/monitor_bar_series.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/monitor_bar_series.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/__snapshots__/monitor_bar_series.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/monitor_bar_series.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/common/charts/annotation_tooltip.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/annotation_tooltip.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/annotation_tooltip.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/annotation_tooltip.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/charts/chart_empty_state.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/chart_empty_state.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/chart_empty_state.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/chart_empty_state.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/charts/chart_empty_state.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/chart_empty_state.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/chart_empty_state.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/chart_empty_state.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/charts/chart_wrapper.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/chart_wrapper.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/chart_wrapper.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/chart_wrapper.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/charts/chart_wrapper/chart_wrapper.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/chart_wrapper/chart_wrapper.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/chart_wrapper/chart_wrapper.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/chart_wrapper/chart_wrapper.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/charts/chart_wrapper/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/chart_wrapper/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/chart_wrapper/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/chart_wrapper/index.ts diff --git a/x-pack/plugins/synthetics/public/components/common/charts/donut_chart.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/donut_chart.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/donut_chart.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/donut_chart.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/charts/donut_chart.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/donut_chart.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/donut_chart.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/donut_chart.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/charts/donut_chart_legend.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/donut_chart_legend.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/donut_chart_legend.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/donut_chart_legend.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/charts/donut_chart_legend.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/donut_chart_legend.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/donut_chart_legend.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/donut_chart_legend.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/charts/donut_chart_legend_row.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/donut_chart_legend_row.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/donut_chart_legend_row.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/donut_chart_legend_row.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/charts/donut_chart_legend_row.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/donut_chart_legend_row.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/donut_chart_legend_row.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/donut_chart_legend_row.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/charts/duration_chart.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/duration_chart.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/common/charts/duration_chart.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/duration_chart.tsx index 2d82fbf1368b8..eb591fd538d98 100644 --- a/x-pack/plugins/synthetics/public/components/common/charts/duration_chart.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/duration_chart.tsx @@ -20,7 +20,7 @@ import { } from '@elastic/charts'; import { useSelector } from 'react-redux'; import { getChartDateLabel } from '../../../lib/helper'; -import { LocationDurationLine } from '../../../../common/types'; +import { LocationDurationLine } from '../../../../../common/types'; import { DurationLineSeriesList } from './duration_line_series_list'; import { ChartWrapper } from './chart_wrapper'; import { useUrlParams } from '../../../hooks'; diff --git a/x-pack/plugins/synthetics/public/components/common/charts/duration_charts.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/duration_charts.test.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/common/charts/duration_charts.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/duration_charts.test.tsx index fee4143df11f9..fc24e723528c9 100644 --- a/x-pack/plugins/synthetics/public/components/common/charts/duration_charts.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/duration_charts.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import DateMath from '@kbn/datemath'; import { DurationChartComponent } from './duration_chart'; -import { MonitorDurationResult } from '../../../../common/types'; +import { MonitorDurationResult } from '../../../../../common/types'; import { render } from '../../../lib/helper/rtl_helpers'; describe('MonitorCharts component', () => { diff --git a/x-pack/plugins/synthetics/public/components/common/charts/duration_line_bar_list.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/duration_line_bar_list.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/duration_line_bar_list.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/duration_line_bar_list.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/charts/duration_line_series_list.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/duration_line_series_list.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/common/charts/duration_line_series_list.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/duration_line_series_list.tsx index bc744be163223..3f8c2abb9fc2c 100644 --- a/x-pack/plugins/synthetics/public/components/common/charts/duration_line_series_list.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/duration_line_series_list.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { LineSeries, CurveType, Fit, ScaleType } from '@elastic/charts'; -import { LocationDurationLine } from '../../../../common/types'; +import { LocationDurationLine } from '../../../../../common/types'; import { microToMilli, microToSec } from '../../../lib/formatting'; import { MS_LABEL, SEC_LABEL } from '../translations'; diff --git a/x-pack/plugins/synthetics/public/components/common/charts/get_tick_format.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/get_tick_format.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/get_tick_format.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/get_tick_format.test.ts diff --git a/x-pack/plugins/synthetics/public/components/common/charts/get_tick_format.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/get_tick_format.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/get_tick_format.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/get_tick_format.ts diff --git a/x-pack/plugins/synthetics/public/components/common/charts/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/index.ts diff --git a/x-pack/plugins/synthetics/public/components/common/charts/monitor_bar_series.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/monitor_bar_series.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/common/charts/monitor_bar_series.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/monitor_bar_series.test.tsx index 792b357b3baba..e8a2afbd8af23 100644 --- a/x-pack/plugins/synthetics/public/components/common/charts/monitor_bar_series.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/monitor_bar_series.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { MonitorBarSeries, MonitorBarSeriesProps } from './monitor_bar_series'; import { renderWithRouter, shallowWithRouter, MountWithReduxProvider } from '../../../lib'; -import { HistogramPoint } from '../../../../common/runtime_types'; +import { HistogramPoint } from '../../../../../common/runtime_types'; describe('MonitorBarSeries component', () => { let props: MonitorBarSeriesProps; diff --git a/x-pack/plugins/synthetics/public/components/common/charts/monitor_bar_series.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/monitor_bar_series.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/common/charts/monitor_bar_series.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/monitor_bar_series.tsx index 6bb52a025154f..35b2b830d91bd 100644 --- a/x-pack/plugins/synthetics/public/components/common/charts/monitor_bar_series.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/monitor_bar_series.tsx @@ -22,7 +22,7 @@ import React, { useContext } from 'react'; import moment from 'moment'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiText, EuiToolTip } from '@elastic/eui'; -import { HistogramPoint } from '../../../../common/runtime_types'; +import { HistogramPoint } from '../../../../../common/runtime_types'; import { getChartDateLabel, seriesHasDownValues } from '../../../lib/helper'; import { useUrlParams } from '../../../hooks'; import { UptimeThemeContext } from '../../../contexts'; diff --git a/x-pack/plugins/synthetics/public/components/common/charts/ping_histogram.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/ping_histogram.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/ping_histogram.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/ping_histogram.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/charts/ping_histogram.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/ping_histogram.tsx similarity index 99% rename from x-pack/plugins/synthetics/public/components/common/charts/ping_histogram.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/ping_histogram.tsx index 95b5791bced8a..c3f9f5086ec9b 100644 --- a/x-pack/plugins/synthetics/public/components/common/charts/ping_histogram.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/ping_histogram.tsx @@ -28,7 +28,7 @@ import { createExploratoryViewUrl } from '@kbn/observability-plugin/public'; import { getChartDateLabel } from '../../../lib/helper'; import { ChartWrapper } from './chart_wrapper'; import { UptimeThemeContext } from '../../../contexts'; -import { HistogramResult } from '../../../../common/runtime_types'; +import { HistogramResult } from '../../../../../common/runtime_types'; import { useMonitorId, useUrlParams } from '../../../hooks'; import { ChartEmptyState } from './chart_empty_state'; import { getDateRangeFromChartElement } from './utils'; diff --git a/x-pack/plugins/synthetics/public/components/common/charts/utils.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/utils.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/utils.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/utils.test.ts diff --git a/x-pack/plugins/synthetics/public/components/common/charts/utils.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/utils.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/charts/utils.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/utils.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/action_menu.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/action_menu.tsx new file mode 100644 index 0000000000000..69821360aab8b --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/action_menu.tsx @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { HeaderMenuPortal } from '@kbn/observability-plugin/public'; +import { AppMountParameters } from '@kbn/core/public'; +import { ActionMenuContent } from './action_menu_content'; + +export const ActionMenu = ({ appMountParameters }: { appMountParameters: AppMountParameters }) => ( + + + +); diff --git a/x-pack/plugins/synthetics/public/components/common/header/action_menu_content.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/action_menu_content.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/header/action_menu_content.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/action_menu_content.test.tsx diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/action_menu_content.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/action_menu_content.tsx new file mode 100644 index 0000000000000..e1330d9924261 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/action_menu_content.tsx @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiHeaderLinks, EuiToolTip, EuiHeaderLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useHistory, useRouteMatch } from 'react-router-dom'; +import { useSelector } from 'react-redux'; +import { createExploratoryViewUrl } from '@kbn/observability-plugin/public'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { useUptimeSettingsContext } from '../../../contexts/uptime_settings_context'; +import { useGetUrlParams } from '../../../hooks'; +import { ToggleAlertFlyoutButton } from '../../overview/alerts/alerts_containers'; +import { MONITOR_ROUTE, SETTINGS_ROUTE } from '../../../../../common/constants'; +import { stringifyUrlParams } from '../../../../apps/synthetics/utils/url_params/stringify_url_params'; +import { InspectorHeaderLink } from './inspector_header_link'; +import { monitorStatusSelector } from '../../../state/selectors'; +import { ManageMonitorsBtn } from './manage_monitors_btn'; + +const ADD_DATA_LABEL = i18n.translate('xpack.synthetics.addDataButtonLabel', { + defaultMessage: 'Add data', +}); + +const ANALYZE_DATA = i18n.translate('xpack.synthetics.analyzeDataButtonLabel', { + defaultMessage: 'Explore data', +}); + +const ANALYZE_MESSAGE = i18n.translate('xpack.synthetics.analyzeDataButtonLabel.message', { + defaultMessage: + 'Explore Data allows you to select and filter result data in any dimension and look for the cause or impact of performance problems.', +}); + +export function ActionMenuContent(): React.ReactElement { + const kibana = useKibana(); + const { basePath } = useUptimeSettingsContext(); + const params = useGetUrlParams(); + const { dateRangeStart, dateRangeEnd } = params; + const history = useHistory(); + + const selectedMonitor = useSelector(monitorStatusSelector); + + const detailRouteMatch = useRouteMatch(MONITOR_ROUTE); + const monitorId = selectedMonitor?.monitor?.id; + + const syntheticExploratoryViewLink = createExploratoryViewUrl( + { + reportType: 'kpi-over-time', + allSeries: [ + { + dataType: 'synthetics', + seriesType: 'area', + selectedMetricField: 'monitor.duration.us', + time: { from: dateRangeStart, to: dateRangeEnd }, + breakdown: monitorId ? 'observer.geo.name' : 'monitor.type', + reportDefinitions: { + 'monitor.name': + selectedMonitor?.monitor?.name && detailRouteMatch?.isExact === true + ? [selectedMonitor?.monitor?.name] + : [], + 'url.full': ['ALL_VALUES'], + }, + name: monitorId ? `${monitorId}-response-duration` : 'All monitors response duration', + }, + ], + }, + basePath + ); + + return ( + + + + + + + + + + {ANALYZE_MESSAGE}

}> + + {ANALYZE_DATA} + +
+ + + {ADD_DATA_LABEL} + + +
+ ); +} diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/inspector_header_link.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/inspector_header_link.tsx new file mode 100644 index 0000000000000..a7c8c3790a50c --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/inspector_header_link.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiHeaderLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { enableInspectEsQueries, useInspectorContext } from '@kbn/observability-plugin/public'; +import { ClientPluginsStart } from '../../../../plugin'; +import { useUptimeSettingsContext } from '../../../contexts/uptime_settings_context'; + +export function InspectorHeaderLink() { + const { + services: { inspector, uiSettings }, + } = useKibana(); + + const { isDev } = useUptimeSettingsContext(); + + const { inspectorAdapters } = useInspectorContext(); + + const isInspectorEnabled = uiSettings?.get(enableInspectEsQueries); + + const inspect = () => { + inspector.open(inspectorAdapters); + }; + + if (!isInspectorEnabled && !isDev) { + return null; + } + + return ( + + {i18n.translate('xpack.synthetics.inspectButtonText', { + defaultMessage: 'Inspect', + })} + + ); +} diff --git a/x-pack/plugins/synthetics/public/components/common/header/manage_monitors_btn.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/manage_monitors_btn.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/common/header/manage_monitors_btn.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/manage_monitors_btn.tsx index 0e7215c0f5a03..2d0ac2654b71f 100644 --- a/x-pack/plugins/synthetics/public/components/common/header/manage_monitors_btn.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/manage_monitors_btn.tsx @@ -13,9 +13,9 @@ import { useHistory } from 'react-router-dom'; import useLocalStorage from 'react-use/lib/useLocalStorage'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { MONITOR_MANAGEMENT_ROUTE } from '../../../../common/constants'; +import { MONITOR_MANAGEMENT_ROUTE } from '../../../../../common/constants'; import { PUBLIC_BETA_DESCRIPTION } from '../../../pages/monitor_management/service_allowed_wrapper'; -import { ClientPluginsSetup } from '../../../apps/plugin'; +import { ClientPluginsSetup } from '../../../../plugin'; import { useUptimeSettingsContext } from '../../../contexts/uptime_settings_context'; export const ManageMonitorsBtn = () => { diff --git a/x-pack/plugins/synthetics/public/components/common/header/page_tabs.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/page_tabs.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/header/page_tabs.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/page_tabs.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/header/page_tabs.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/page_tabs.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/common/header/page_tabs.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/page_tabs.tsx index 1210dbb0f2290..08725ac203bab 100644 --- a/x-pack/plugins/synthetics/public/components/common/header/page_tabs.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/header/page_tabs.tsx @@ -10,9 +10,9 @@ import React, { useEffect, useState } from 'react'; import { EuiTabs, EuiTab } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useHistory, useRouteMatch } from 'react-router-dom'; -import { CERTIFICATES_ROUTE, OVERVIEW_ROUTE } from '../../../../common/constants'; +import { CERTIFICATES_ROUTE, OVERVIEW_ROUTE } from '../../../../../common/constants'; import { useGetUrlParams } from '../../../hooks'; -import { stringifyUrlParams } from '../../../lib/helper/stringify_url_params'; +import { stringifyUrlParams } from '../../../../apps/synthetics/utils/url_params/stringify_url_params'; const tabs = [ { diff --git a/x-pack/plugins/synthetics/public/components/common/higher_order/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/higher_order/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/higher_order/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/higher_order/index.ts diff --git a/x-pack/plugins/synthetics/public/components/common/higher_order/responsive_wrapper.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/higher_order/responsive_wrapper.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/higher_order/responsive_wrapper.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/higher_order/responsive_wrapper.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/higher_order/responsive_wrapper.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/higher_order/responsive_wrapper.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/higher_order/responsive_wrapper.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/higher_order/responsive_wrapper.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/location_link.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/location_link.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/location_link.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/location_link.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/location_link.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/location_link.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/location_link.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/location_link.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/monitor_page_link.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/monitor_page_link.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/monitor_page_link.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/monitor_page_link.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/monitor_page_link.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/monitor_page_link.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/monitor_page_link.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/monitor_page_link.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/monitor_tags.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/monitor_tags.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/monitor_tags.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/monitor_tags.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/monitor_tags.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/monitor_tags.tsx similarity index 94% rename from x-pack/plugins/synthetics/public/components/common/monitor_tags.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/monitor_tags.tsx index e8e8a61e7b303..14e185e3e9318 100644 --- a/x-pack/plugins/synthetics/public/components/common/monitor_tags.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/monitor_tags.tsx @@ -10,13 +10,13 @@ import { EuiBadge, EuiBadgeGroup, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useHistory } from 'react-router-dom'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { Ping } from '../../../common/runtime_types/ping'; -import { MonitorSummary } from '../../../common/runtime_types/monitor'; +import { Ping } from '../../../../common/runtime_types/ping'; +import { MonitorSummary } from '../../../../common/runtime_types/monitor'; import { useFilterUpdate } from '../../hooks/use_filter_update'; import { useGetUrlParams } from '../../hooks'; import { parseCurrentFilters } from '../overview/monitor_list/columns/monitor_name_col'; import { EXPAND_TAGS_LABEL } from '../overview/monitor_list/columns/translations'; -import { OVERVIEW_ROUTE } from '../../../common/constants'; +import { OVERVIEW_ROUTE } from '../../../../common/constants'; interface Props { ping?: Ping | null; diff --git a/x-pack/plugins/synthetics/public/components/common/react_router_helpers/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/react_router_helpers/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/react_router_helpers/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/react_router_helpers/index.ts diff --git a/x-pack/plugins/synthetics/public/components/common/react_router_helpers/link_events.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/react_router_helpers/link_events.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/react_router_helpers/link_events.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/react_router_helpers/link_events.test.ts diff --git a/x-pack/plugins/synthetics/public/components/common/react_router_helpers/link_events.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/react_router_helpers/link_events.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/react_router_helpers/link_events.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/react_router_helpers/link_events.ts diff --git a/x-pack/plugins/synthetics/public/components/common/react_router_helpers/link_for_eui.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/react_router_helpers/link_for_eui.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/react_router_helpers/link_for_eui.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/react_router_helpers/link_for_eui.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/react_router_helpers/link_for_eui.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/react_router_helpers/link_for_eui.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/react_router_helpers/link_for_eui.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/react_router_helpers/link_for_eui.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/step_detail_link.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/step_detail_link.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/step_detail_link.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/step_detail_link.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/translations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/translations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/translations.ts diff --git a/x-pack/plugins/synthetics/public/components/common/uptime_date_picker.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/uptime_date_picker.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/common/uptime_date_picker.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/uptime_date_picker.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/common/uptime_date_picker.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/uptime_date_picker.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/common/uptime_date_picker.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/common/uptime_date_picker.tsx index f509f9b53114b..ff01b9cb352ee 100644 --- a/x-pack/plugins/synthetics/public/components/common/uptime_date_picker.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/uptime_date_picker.tsx @@ -8,7 +8,7 @@ import React, { useContext, useEffect } from 'react'; import { EuiSuperDatePicker } from '@elastic/eui'; import { useUrlParams } from '../../hooks'; -import { CLIENT_DEFAULTS } from '../../../common/constants'; +import { CLIENT_DEFAULTS } from '../../../../common/constants'; import { UptimeRefreshContext, UptimeSettingsContext, diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/advanced_fields.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/advanced_fields.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/browser/advanced_fields.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/advanced_fields.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/advanced_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/advanced_fields.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/browser/advanced_fields.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/advanced_fields.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/formatters.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/formatters.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/browser/formatters.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/formatters.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/normalizers.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/browser/normalizers.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.test.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/browser/normalizers.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/normalizers.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/script_recorder_fields.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/script_recorder_fields.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/browser/script_recorder_fields.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/script_recorder_fields.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/script_recorder_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/script_recorder_fields.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/browser/script_recorder_fields.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/script_recorder_fields.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/simple_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/simple_fields.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/browser/simple_fields.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/simple_fields.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/source_field.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/source_field.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/fleet_package/browser/source_field.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/source_field.test.tsx index ae5db0662dd85..97001df29a69e 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/browser/source_field.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/source_field.test.tsx @@ -8,7 +8,7 @@ import 'jest-canvas-mock'; import React from 'react'; import { fireEvent, screen, waitFor } from '@testing-library/react'; -import { ConfigKey } from '../../../../common/runtime_types'; +import { ConfigKey } from '../../../../../common/runtime_types'; import { render } from '../../../lib/helper/rtl_helpers'; import { IPolicyConfigContextProvider } from '../contexts/policy_config_context'; import { SourceField, defaultValues } from './source_field'; diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/source_field.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/source_field.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/browser/source_field.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/source_field.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/throttling_fields.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/throttling_fields.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/browser/throttling_fields.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/throttling_fields.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/throttling_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/throttling_fields.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/browser/throttling_fields.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/throttling_fields.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/uploader.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/uploader.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/browser/uploader.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/uploader.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/zip_url_tls_fields.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/zip_url_tls_fields.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/browser/zip_url_tls_fields.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/zip_url_tls_fields.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/zip_url_tls_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/zip_url_tls_fields.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/browser/zip_url_tls_fields.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/browser/zip_url_tls_fields.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/code_editor.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/code_editor.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/code_editor.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/code_editor.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/combo_box.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/combo_box.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/combo_box.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/combo_box.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/combo_box.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/combo_box.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/combo_box.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/combo_box.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/common/common_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/common_fields.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/common/common_fields.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/common_fields.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/common/described_form_group_with_wrap.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/described_form_group_with_wrap.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/common/described_form_group_with_wrap.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/described_form_group_with_wrap.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/common/enabled.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/enabled.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/common/enabled.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/enabled.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/common/formatters.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/formatters.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/common/formatters.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/formatters.test.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/common/formatters.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/formatters.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/common/formatters.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/formatters.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/common/normalizers.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/normalizers.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/common/normalizers.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/normalizers.test.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/common/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/normalizers.ts similarity index 88% rename from x-pack/plugins/synthetics/public/components/fleet_package/common/normalizers.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/normalizers.ts index 8f1b7ad230d9c..0119b415faed8 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/common/normalizers.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/normalizers.ts @@ -7,9 +7,11 @@ import { NewPackagePolicyInput } from '@kbn/fleet-plugin/common'; import { CommonFields, ConfigKey, DataStream } from '../types'; -import { defaultValues as commonDefaultValues } from './default_values'; -import { DEFAULT_NAMESPACE_STRING } from '../../../../common/constants'; -import { defaultConfig } from '../contexts'; +import { + DEFAULT_COMMON_FIELDS, + DEFAULT_NAMESPACE_STRING, + DEFAULT_FIELDS, +} from '../../../../../common/constants/monitor_defaults'; // TO DO: create a standard input format that all fields resolve to export type Normalizer = (fields: NewPackagePolicyInput['vars']) => unknown; @@ -46,15 +48,15 @@ export function getCronNormalizer(key: string, defaultValues: Fields): N } export const getCommonNormalizer = (key: ConfigKey) => { - return getNormalizer(key, commonDefaultValues); + return getNormalizer(key, DEFAULT_COMMON_FIELDS); }; export const getCommonjsonToJavascriptNormalizer = (key: ConfigKey) => { - return getJsonToJavascriptNormalizer(key, commonDefaultValues); + return getJsonToJavascriptNormalizer(key, DEFAULT_COMMON_FIELDS); }; export const getCommonCronToSecondsNormalizer = (key: ConfigKey) => { - return getCronNormalizer(key, commonDefaultValues); + return getCronNormalizer(key, DEFAULT_COMMON_FIELDS); }; export const commonNormalizers: CommonNormalizerMap = { @@ -76,7 +78,7 @@ export const commonNormalizers: CommonNormalizerMap = { number, }; } else { - return defaultConfig[type][ConfigKey.SCHEDULE]; + return DEFAULT_FIELDS[type][ConfigKey.SCHEDULE]; } }, [ConfigKey.APM_SERVICE_NAME]: getCommonNormalizer(ConfigKey.APM_SERVICE_NAME), diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/common/simple_fields_wrapper.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/simple_fields_wrapper.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/common/simple_fields_wrapper.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/simple_fields_wrapper.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/common/tls_options.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/tls_options.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/common/tls_options.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/common/tls_options.tsx diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/browser_context.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/browser_context.tsx new file mode 100644 index 0000000000000..e89e4dcd1d5bd --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/browser_context.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { createContext, useContext, useMemo, useState } from 'react'; +import { BrowserSimpleFields } from '../types'; +import { DEFAULT_BROWSER_SIMPLE_FIELDS } from '../../../../../common/constants/monitor_defaults'; + +interface BrowserSimpleFieldsContext { + setFields: React.Dispatch>; + fields: BrowserSimpleFields; + defaultValues: BrowserSimpleFields; +} + +interface BrowserSimpleFieldsContextProvider { + children: React.ReactNode; + defaultValues?: BrowserSimpleFields; +} + +export const initialValues: BrowserSimpleFields = DEFAULT_BROWSER_SIMPLE_FIELDS; + +const defaultContext: BrowserSimpleFieldsContext = { + setFields: (_fields: React.SetStateAction) => { + throw new Error( + 'setFields was not initialized for Browser Simple Fields, set it when you invoke the context' + ); + }, + fields: initialValues, // mutable + defaultValues: initialValues, // immutable +}; + +export const BrowserSimpleFieldsContext = createContext(defaultContext); + +export const BrowserSimpleFieldsContextProvider = ({ + children, + defaultValues = initialValues, +}: BrowserSimpleFieldsContextProvider) => { + const [fields, setFields] = useState(defaultValues); + + const value = useMemo(() => { + return { fields, setFields, defaultValues }; + }, [fields, defaultValues]); + + return ; +}; + +export const useBrowserSimpleFieldsContext = () => useContext(BrowserSimpleFieldsContext); diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/browser_context_advanced.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/browser_context_advanced.tsx similarity index 75% rename from x-pack/plugins/synthetics/public/components/fleet_package/contexts/browser_context_advanced.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/browser_context_advanced.tsx index 3ddd7953a6310..d81e1dc631ee2 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/browser_context_advanced.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/browser_context_advanced.tsx @@ -6,7 +6,8 @@ */ import React, { createContext, useContext, useMemo, useState } from 'react'; -import { BrowserAdvancedFields, ConfigKey, ScreenshotOption } from '../types'; +import { BrowserAdvancedFields } from '../types'; +import { DEFAULT_BROWSER_ADVANCED_FIELDS } from '../../../../../common/constants/monitor_defaults'; interface BrowserAdvancedFieldsContext { setFields: React.Dispatch>; @@ -19,18 +20,7 @@ interface BrowserAdvancedFieldsContextProvider { defaultValues?: BrowserAdvancedFields; } -export const initialValues: BrowserAdvancedFields = { - [ConfigKey.SCREENSHOTS]: ScreenshotOption.ON, - [ConfigKey.SYNTHETICS_ARGS]: [], - [ConfigKey.JOURNEY_FILTERS_MATCH]: '', - [ConfigKey.JOURNEY_FILTERS_TAGS]: [], - [ConfigKey.IGNORE_HTTPS_ERRORS]: false, - [ConfigKey.IS_THROTTLING_ENABLED]: true, - [ConfigKey.DOWNLOAD_SPEED]: '5', - [ConfigKey.UPLOAD_SPEED]: '3', - [ConfigKey.LATENCY]: '20', - [ConfigKey.THROTTLING_CONFIG]: '5d/3u/20l', -}; +export const initialValues: BrowserAdvancedFields = DEFAULT_BROWSER_ADVANCED_FIELDS; const defaultContext: BrowserAdvancedFieldsContext = { setFields: (_fields: React.SetStateAction) => { diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/browser_provider.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/browser_provider.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/contexts/browser_provider.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/browser_provider.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/http_context.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/http_context.tsx similarity index 79% rename from x-pack/plugins/synthetics/public/components/fleet_package/contexts/http_context.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/http_context.tsx index a4a0c7bb16aa8..d45dfc01f0de6 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/http_context.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/http_context.tsx @@ -6,8 +6,8 @@ */ import React, { createContext, useContext, useMemo, useState } from 'react'; -import { HTTPSimpleFields, ConfigKey, DataStream } from '../types'; -import { defaultValues as commonDefaultValues } from '../common/default_values'; +import { HTTPSimpleFields } from '../types'; +import { DEFAULT_HTTP_SIMPLE_FIELDS } from '../../../../../common/constants/monitor_defaults'; interface HTTPSimpleFieldsContext { setFields: React.Dispatch>; @@ -20,15 +20,7 @@ interface HTTPSimpleFieldsContextProvider { defaultValues?: HTTPSimpleFields; } -export const initialValues: HTTPSimpleFields = { - ...commonDefaultValues, - [ConfigKey.METADATA]: { - is_tls_enabled: false, - }, - [ConfigKey.URLS]: '', - [ConfigKey.MAX_REDIRECTS]: '0', - [ConfigKey.MONITOR_TYPE]: DataStream.HTTP, -}; +export const initialValues: HTTPSimpleFields = DEFAULT_HTTP_SIMPLE_FIELDS; const defaultContext: HTTPSimpleFieldsContext = { setFields: (_fields: React.SetStateAction) => { diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/http_context_advanced.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/http_context_advanced.tsx new file mode 100644 index 0000000000000..549f314bc3d3a --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/http_context_advanced.tsx @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { createContext, useContext, useMemo, useState } from 'react'; +import { HTTPAdvancedFields } from '../types'; +import { DEFAULT_HTTP_ADVANCED_FIELDS } from '../../../../../common/constants/monitor_defaults'; + +interface HTTPAdvancedFieldsContext { + setFields: React.Dispatch>; + fields: HTTPAdvancedFields; + defaultValues: HTTPAdvancedFields; +} + +interface HTTPAdvancedFieldsContextProvider { + children: React.ReactNode; + defaultValues?: HTTPAdvancedFields; +} + +export const initialValues: HTTPAdvancedFields = DEFAULT_HTTP_ADVANCED_FIELDS; + +export const defaultContext: HTTPAdvancedFieldsContext = { + setFields: (_fields: React.SetStateAction) => { + throw new Error('setFields was not initialized, set it when you invoke the context'); + }, + fields: initialValues, + defaultValues: initialValues, +}; + +export const HTTPAdvancedFieldsContext = createContext(defaultContext); + +export const HTTPAdvancedFieldsContextProvider = ({ + children, + defaultValues = initialValues, +}: HTTPAdvancedFieldsContextProvider) => { + const [fields, setFields] = useState(defaultValues); + + const value = useMemo(() => { + return { fields, setFields, defaultValues }; + }, [fields, defaultValues]); + + return ; +}; + +export const useHTTPAdvancedFieldsContext = () => useContext(HTTPAdvancedFieldsContext); diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/http_provider.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/http_provider.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/contexts/http_provider.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/http_provider.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/icmp_context.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/icmp_context.tsx similarity index 82% rename from x-pack/plugins/synthetics/public/components/fleet_package/contexts/icmp_context.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/icmp_context.tsx index f0547e621afc4..4e7667650ff10 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/icmp_context.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/icmp_context.tsx @@ -6,8 +6,8 @@ */ import React, { createContext, useContext, useMemo, useState } from 'react'; -import { ICMPSimpleFields, ConfigKey, DataStream } from '../types'; -import { defaultValues as commonDefaultValues } from '../common/default_values'; +import { ICMPSimpleFields } from '../types'; +import { DEFAULT_ICMP_SIMPLE_FIELDS } from '../../../../../common/constants/monitor_defaults'; interface ICMPSimpleFieldsContext { setFields: React.Dispatch>; @@ -20,12 +20,7 @@ interface ICMPSimpleFieldsContextProvider { defaultValues?: ICMPSimpleFields; } -export const initialValues: ICMPSimpleFields = { - ...commonDefaultValues, - [ConfigKey.HOSTS]: '', - [ConfigKey.MONITOR_TYPE]: DataStream.ICMP, - [ConfigKey.WAIT]: '1', -}; +export const initialValues: ICMPSimpleFields = DEFAULT_ICMP_SIMPLE_FIELDS; const defaultContext: ICMPSimpleFieldsContext = { setFields: (_fields: React.SetStateAction) => { diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/index.ts new file mode 100644 index 0000000000000..a80e295e8bc40 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/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. + */ +export type { IPolicyConfigContextProvider } from './policy_config_context'; +export { + PolicyConfigContext, + PolicyConfigContextProvider, + initialValue as defaultPolicyConfig, + defaultContext as defaultPolicyConfigValues, + usePolicyConfigContext, +} from './policy_config_context'; +export { + HTTPSimpleFieldsContext, + HTTPSimpleFieldsContextProvider, + initialValues as defaultHTTPSimpleFields, + useHTTPSimpleFieldsContext, +} from './http_context'; +export { + HTTPAdvancedFieldsContext, + HTTPAdvancedFieldsContextProvider, + initialValues as defaultHTTPAdvancedFields, + useHTTPAdvancedFieldsContext, +} from './http_context_advanced'; +export { + TCPSimpleFieldsContext, + TCPSimpleFieldsContextProvider, + initialValues as defaultTCPSimpleFields, + useTCPSimpleFieldsContext, +} from './tcp_context'; +export { + ICMPSimpleFieldsContext, + ICMPSimpleFieldsContextProvider, + initialValues as defaultICMPSimpleFields, + useICMPSimpleFieldsContext, +} from './icmp_context'; +export { + TCPAdvancedFieldsContext, + TCPAdvancedFieldsContextProvider, + initialValues as defaultTCPAdvancedFields, + useTCPAdvancedFieldsContext, +} from './tcp_context_advanced'; +export { + BrowserSimpleFieldsContext, + BrowserSimpleFieldsContextProvider, + initialValues as defaultBrowserSimpleFields, + useBrowserSimpleFieldsContext, +} from './browser_context'; +export { + BrowserAdvancedFieldsContext, + BrowserAdvancedFieldsContextProvider, + initialValues as defaultBrowserAdvancedFields, + useBrowserAdvancedFieldsContext, +} from './browser_context_advanced'; +export { + TLSFieldsContext, + TLSFieldsContextProvider, + initialValues as defaultTLSFields, + useTLSFieldsContext, +} from './tls_fields_context'; +export { HTTPContextProvider } from './http_provider'; +export { TCPContextProvider } from './tcp_provider'; +export { BrowserContextProvider } from './browser_provider'; +export { SyntheticsProviders } from './synthetics_context_providers'; diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/policy_config_context.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/policy_config_context.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/fleet_package/contexts/policy_config_context.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/policy_config_context.tsx index 5d47b4e9d0984..49f83d9577661 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/policy_config_context.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/policy_config_context.tsx @@ -6,13 +6,13 @@ */ import React, { createContext, useContext, useMemo, useState } from 'react'; -import { DEFAULT_NAMESPACE_STRING } from '../../../../common/constants'; +import { DEFAULT_NAMESPACE_STRING } from '../../../../../common/constants/monitor_defaults'; import { ScheduleUnit, MonitorServiceLocations, ThrottlingOptions, DEFAULT_THROTTLING, -} from '../../../../common/runtime_types'; +} from '../../../../../common/runtime_types'; import { DataStream } from '../types'; interface IPolicyConfigContext { diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/synthetics_context_providers.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/synthetics_context_providers.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/contexts/synthetics_context_providers.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/synthetics_context_providers.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/tcp_context.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/tcp_context.tsx similarity index 80% rename from x-pack/plugins/synthetics/public/components/fleet_package/contexts/tcp_context.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/tcp_context.tsx index 38a15c3e39453..9b3e1052100e9 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/tcp_context.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/tcp_context.tsx @@ -6,8 +6,8 @@ */ import React, { createContext, useContext, useMemo, useState } from 'react'; -import { TCPSimpleFields, ConfigKey, DataStream } from '../types'; -import { defaultValues as commonDefaultValues } from '../common/default_values'; +import { TCPSimpleFields } from '../types'; +import { DEFAULT_TCP_SIMPLE_FIELDS } from '../../../../../common/constants/monitor_defaults'; interface TCPSimpleFieldsContext { setFields: React.Dispatch>; @@ -20,14 +20,7 @@ interface TCPSimpleFieldsContextProvider { defaultValues?: TCPSimpleFields; } -export const initialValues: TCPSimpleFields = { - ...commonDefaultValues, - [ConfigKey.METADATA]: { - is_tls_enabled: false, - }, - [ConfigKey.HOSTS]: '', - [ConfigKey.MONITOR_TYPE]: DataStream.TCP, -}; +export const initialValues: TCPSimpleFields = DEFAULT_TCP_SIMPLE_FIELDS; const defaultContext: TCPSimpleFieldsContext = { setFields: (_fields: React.SetStateAction) => { diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/tcp_context_advanced.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/tcp_context_advanced.tsx similarity index 84% rename from x-pack/plugins/synthetics/public/components/fleet_package/contexts/tcp_context_advanced.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/tcp_context_advanced.tsx index 7fe29f7648841..8d1174ed96bc6 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/tcp_context_advanced.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/tcp_context_advanced.tsx @@ -6,7 +6,8 @@ */ import React, { createContext, useContext, useMemo, useState } from 'react'; -import { TCPAdvancedFields, ConfigKey } from '../types'; +import { TCPAdvancedFields } from '../types'; +import { DEFAULT_TCP_ADVANCED_FIELDS } from '../../../../../common/constants/monitor_defaults'; interface TCPAdvancedFieldsContext { setFields: React.Dispatch>; @@ -19,12 +20,7 @@ interface TCPAdvancedFieldsContextProvider { defaultValues?: TCPAdvancedFields; } -export const initialValues: TCPAdvancedFields = { - [ConfigKey.PROXY_URL]: '', - [ConfigKey.PROXY_USE_LOCAL_RESOLVER]: false, - [ConfigKey.RESPONSE_RECEIVE_CHECK]: '', - [ConfigKey.REQUEST_SEND_CHECK]: '', -}; +export const initialValues: TCPAdvancedFields = DEFAULT_TCP_ADVANCED_FIELDS; const defaultContext: TCPAdvancedFieldsContext = { setFields: (_fields: React.SetStateAction) => { diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/tcp_provider.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/tcp_provider.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/contexts/tcp_provider.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/tcp_provider.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/tls_fields_context.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/tls_fields_context.tsx similarity index 90% rename from x-pack/plugins/synthetics/public/components/fleet_package/contexts/tls_fields_context.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/tls_fields_context.tsx index a76655a235c4f..14ec64bf640b7 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/contexts/tls_fields_context.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/contexts/tls_fields_context.tsx @@ -7,7 +7,7 @@ import React, { createContext, useContext, useMemo, useState } from 'react'; import { TLSFields } from '../types'; -import { defaultValues as tlsDefaultValues } from '../tls/default_values'; +import { DEFAULT_TLS_FIELDS } from '../../../../../common/constants/monitor_defaults'; interface TLSFieldsContext { setFields: React.Dispatch>; @@ -20,7 +20,7 @@ interface TLSFieldsContextProvider { defaultValues?: TLSFields; } -export const initialValues: TLSFields = tlsDefaultValues; +export const initialValues: TLSFields = DEFAULT_TLS_FIELDS; const defaultContext: TLSFieldsContext = { setFields: (_fields: React.SetStateAction) => { diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/custom_fields.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/custom_fields.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/custom_fields.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/custom_fields.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/custom_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/custom_fields.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/custom_fields.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/custom_fields.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/header_field.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/header_field.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/header_field.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/header_field.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/header_field.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/header_field.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/header_field.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/header_field.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/helpers/context_helpers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/helpers/context_helpers.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/helpers/context_helpers.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/helpers/context_helpers.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/helpers/formatters.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/helpers/formatters.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/helpers/formatters.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/helpers/formatters.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/helpers/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/helpers/normalizers.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/helpers/normalizers.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/helpers/normalizers.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/hooks/use_policy.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_policy.ts similarity index 84% rename from x-pack/plugins/synthetics/public/components/fleet_package/hooks/use_policy.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_policy.ts index 2fc33de1bab39..9f43cbb238a79 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/hooks/use_policy.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_policy.ts @@ -25,34 +25,10 @@ import { useTLSFieldsContext, useBrowserSimpleFieldsContext, useBrowserAdvancedFieldsContext, - defaultHTTPAdvancedFields, - defaultHTTPSimpleFields, - defaultICMPSimpleFields, - defaultTCPSimpleFields, - defaultTCPAdvancedFields, - defaultBrowserSimpleFields, - defaultBrowserAdvancedFields, - defaultTLSFields, } from '../contexts'; +import { DEFAULT_FIELDS } from '../../../../../common/constants/monitor_defaults'; -export const defaultConfig: PolicyConfig = { - [DataStream.HTTP]: { - ...defaultHTTPSimpleFields, - ...defaultHTTPAdvancedFields, - ...defaultTLSFields, - }, - [DataStream.TCP]: { - ...defaultTCPSimpleFields, - ...defaultTCPAdvancedFields, - ...defaultTLSFields, - }, - [DataStream.ICMP]: defaultICMPSimpleFields, - [DataStream.BROWSER]: { - ...defaultBrowserSimpleFields, - ...defaultBrowserAdvancedFields, - ...defaultTLSFields, - }, -}; +export const defaultConfig: PolicyConfig = DEFAULT_FIELDS; export const usePolicy = (fleetPolicyName: string = '') => { const { diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/hooks/use_update_policy.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_update_policy.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/hooks/use_update_policy.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_update_policy.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/hooks/use_update_policy.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_update_policy.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/hooks/use_update_policy.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/hooks/use_update_policy.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/http/advanced_fields.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/advanced_fields.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/http/advanced_fields.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/advanced_fields.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/http/advanced_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/advanced_fields.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/http/advanced_fields.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/advanced_fields.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/http/formatters.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/formatters.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/http/formatters.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/formatters.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/http/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/normalizers.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/http/normalizers.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/normalizers.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/http/simple_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/simple_fields.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/http/simple_fields.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/http/simple_fields.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/icmp/advanced_fields.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/icmp/advanced_fields.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/icmp/advanced_fields.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/icmp/advanced_fields.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/icmp/advanced_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/icmp/advanced_fields.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/icmp/advanced_fields.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/icmp/advanced_fields.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/icmp/formatters.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/icmp/formatters.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/icmp/formatters.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/icmp/formatters.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/icmp/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/icmp/normalizers.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/icmp/normalizers.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/icmp/normalizers.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/icmp/simple_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/icmp/simple_fields.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/icmp/simple_fields.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/icmp/simple_fields.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/index.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/index.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/index.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/index.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/index_response_body_field.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/index_response_body_field.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/index_response_body_field.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/index_response_body_field.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/index_response_body_field.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/index_response_body_field.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/index_response_body_field.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/index_response_body_field.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/key_value_field.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/key_value_field.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/key_value_field.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/key_value_field.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/key_value_field.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/key_value_field.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/key_value_field.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/key_value_field.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/lazy_synthetics_custom_assets_extension.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/lazy_synthetics_custom_assets_extension.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/lazy_synthetics_custom_assets_extension.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/lazy_synthetics_custom_assets_extension.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/lazy_synthetics_policy_create_extension.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/lazy_synthetics_policy_create_extension.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/lazy_synthetics_policy_create_extension.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/lazy_synthetics_policy_create_extension.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/lazy_synthetics_policy_edit_extension.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/lazy_synthetics_policy_edit_extension.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/lazy_synthetics_policy_edit_extension.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/lazy_synthetics_policy_edit_extension.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/optional_label.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/optional_label.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/optional_label.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/optional_label.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/request_body_field.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/request_body_field.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/request_body_field.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/request_body_field.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/request_body_field.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/request_body_field.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/request_body_field.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/request_body_field.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/schedule_field.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/schedule_field.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/schedule_field.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/schedule_field.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/schedule_field.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/schedule_field.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/schedule_field.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/schedule_field.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/synthetics_custom_assets_extension.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_custom_assets_extension.tsx similarity index 90% rename from x-pack/plugins/synthetics/public/components/fleet_package/synthetics_custom_assets_extension.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_custom_assets_extension.tsx index c21acee549986..64d1be74e71fd 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/synthetics_custom_assets_extension.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_custom_assets_extension.tsx @@ -14,8 +14,8 @@ import { CustomAssetsAccordion, } from '@kbn/fleet-plugin/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { ClientPluginsStart } from '../../apps/plugin'; -import { PLUGIN } from '../../../common/constants/plugin'; +import { ClientPluginsStart } from '../../../plugin'; +import { PLUGIN } from '../../../../common/constants/plugin'; export const SyntheticsCustomAssetsExtension: PackageAssetsComponent = () => { const { http } = useKibana().services; diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/synthetics_policy_create_extension.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_create_extension.tsx similarity index 76% rename from x-pack/plugins/synthetics/public/components/fleet_package/synthetics_policy_create_extension.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_create_extension.tsx index e93cc20e4d2ad..6247619f6a3b6 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/synthetics_policy_create_extension.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_create_extension.tsx @@ -10,40 +10,14 @@ import { PackagePolicyCreateExtensionComponentProps } from '@kbn/fleet-plugin/pu import { useTrackPageview } from '@kbn/observability-plugin/public'; import { DataStream } from './types'; import { PolicyConfig } from './types'; -import { - usePolicyConfigContext, - defaultHTTPSimpleFields, - defaultHTTPAdvancedFields, - defaultTCPSimpleFields, - defaultTCPAdvancedFields, - defaultICMPSimpleFields, - defaultBrowserSimpleFields, - defaultBrowserAdvancedFields, - defaultTLSFields, -} from './contexts'; +import { usePolicyConfigContext } from './contexts'; +import { DEFAULT_FIELDS } from '../../../../common/constants/monitor_defaults'; import { CustomFields } from './custom_fields'; import { useUpdatePolicy } from './hooks/use_update_policy'; import { usePolicy } from './hooks/use_policy'; import { validate } from './validation'; -export const defaultConfig: PolicyConfig = { - [DataStream.HTTP]: { - ...defaultHTTPSimpleFields, - ...defaultHTTPAdvancedFields, - ...defaultTLSFields, - }, - [DataStream.TCP]: { - ...defaultTCPSimpleFields, - ...defaultTCPAdvancedFields, - ...defaultTLSFields, - }, - [DataStream.ICMP]: defaultICMPSimpleFields, - [DataStream.BROWSER]: { - ...defaultBrowserSimpleFields, - ...defaultBrowserAdvancedFields, - ...defaultTLSFields, - }, -}; +export const defaultConfig: PolicyConfig = DEFAULT_FIELDS; /** * Exports Synthetics-specific package policy instructions diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/synthetics_policy_create_extension_wrapper.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_create_extension_wrapper.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/synthetics_policy_create_extension_wrapper.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_create_extension_wrapper.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/synthetics_policy_create_extension_wrapper.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_create_extension_wrapper.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/synthetics_policy_create_extension_wrapper.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_create_extension_wrapper.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/synthetics_policy_edit_extension.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/synthetics_policy_edit_extension.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/synthetics_policy_edit_extension_wrapper.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension_wrapper.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/synthetics_policy_edit_extension_wrapper.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension_wrapper.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/synthetics_policy_edit_extension_wrapper.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension_wrapper.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/synthetics_policy_edit_extension_wrapper.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/synthetics_policy_edit_extension_wrapper.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/tcp/advanced_fields.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/advanced_fields.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/tcp/advanced_fields.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/advanced_fields.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/tcp/advanced_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/advanced_fields.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/tcp/advanced_fields.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/advanced_fields.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/tcp/formatters.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/formatters.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/tcp/formatters.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/formatters.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/tcp/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/normalizers.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/tcp/normalizers.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/normalizers.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/tcp/simple_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/simple_fields.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/tcp/simple_fields.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tcp/simple_fields.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/tls/formatters.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tls/formatters.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/tls/formatters.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tls/formatters.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/tls/normalizers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tls/normalizers.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/tls/normalizers.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tls/normalizers.ts diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/tls_fields.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tls_fields.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/tls_fields.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tls_fields.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/tls_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tls_fields.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/tls_fields.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/tls_fields.tsx diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/types.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/types.tsx new file mode 100644 index 0000000000000..0a5de311c5cb3 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/types.tsx @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { + HTTPFields, + TCPFields, + ICMPFields, + BrowserFields, + ConfigKey, + ContentType, + DataStream, + Mode, + ThrottlingConfigKey, + ThrottlingSuffix, + ThrottlingSuffixType, +} from '../../../../common/runtime_types'; +export * from '../../../../common/runtime_types/monitor_management'; +export * from '../../../../common/types/monitor_validation'; + +export interface PolicyConfig { + [DataStream.HTTP]: HTTPFields; + [DataStream.TCP]: TCPFields; + [DataStream.ICMP]: ICMPFields; + [DataStream.BROWSER]: BrowserFields; +} + +export const contentTypesToMode = { + [ContentType.FORM]: Mode.FORM, + [ContentType.JSON]: Mode.JSON, + [ContentType.TEXT]: Mode.PLAINTEXT, + [ContentType.XML]: Mode.XML, +}; + +export const configKeyToThrottlingSuffix: Record = { + [ConfigKey.DOWNLOAD_SPEED]: ThrottlingSuffix.DOWNLOAD, + [ConfigKey.UPLOAD_SPEED]: ThrottlingSuffix.UPLOAD, + [ConfigKey.LATENCY]: ThrottlingSuffix.LATENCY, +}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/validation.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/validation.test.ts new file mode 100644 index 0000000000000..b2cca04b44d1b --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/validation.test.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + ConfigKey, + DataStream, + HTTPFields, + BrowserFields, + MonitorFields, + ScheduleUnit, +} from '../../../../common/runtime_types'; +import { validate } from './validation'; + +describe('[Monitor Management] validation', () => { + const commonPropsValid: Partial = { + [ConfigKey.SCHEDULE]: { number: '5', unit: ScheduleUnit.MINUTES }, + [ConfigKey.TIMEOUT]: '3m', + }; + + describe('HTTP', () => { + const httpPropsValid: Partial = { + ...commonPropsValid, + [ConfigKey.RESPONSE_STATUS_CHECK]: ['200', '204'], + [ConfigKey.RESPONSE_HEADERS_CHECK]: { 'Content-Type': 'application/json' }, + [ConfigKey.REQUEST_HEADERS_CHECK]: { 'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8' }, + [ConfigKey.MAX_REDIRECTS]: '3', + [ConfigKey.URLS]: 'https:// example-url.com', + }; + + it('should return false for all valid props', () => { + const validators = validate[DataStream.HTTP]; + const keysToValidate = [ + ConfigKey.SCHEDULE, + ConfigKey.TIMEOUT, + ConfigKey.RESPONSE_STATUS_CHECK, + ConfigKey.RESPONSE_HEADERS_CHECK, + ConfigKey.REQUEST_HEADERS_CHECK, + ConfigKey.MAX_REDIRECTS, + ConfigKey.URLS, + ]; + const validatorFns = keysToValidate.map((key) => validators[key]); + const result = validatorFns.map((fn) => fn?.(httpPropsValid) ?? true); + + expect(result).not.toEqual(expect.arrayContaining([true])); + }); + }); + + describe.each([ + [ConfigKey.SOURCE_INLINE, 'step(() => {});'], + [ConfigKey.SOURCE_ZIP_URL, 'https://test.zip'], + ])('Browser', (configKey, value) => { + const browserProps: Partial = { + ...commonPropsValid, + [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, + [ConfigKey.TIMEOUT]: null, + [configKey]: value, + }; + + it('should return false for all valid props', () => { + const validators = validate[DataStream.BROWSER]; + const keysToValidate = [ConfigKey.SCHEDULE, ConfigKey.TIMEOUT, configKey]; + const validatorFns = keysToValidate.map((key) => validators[key]); + const result = validatorFns.map((fn) => fn?.(browserProps) ?? true); + + expect(result).not.toEqual(expect.arrayContaining([true])); + }); + }); + + // TODO: Add test for other monitor types if needed +}); diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/validation.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/validation.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/fleet_package/validation.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/fleet_package/validation.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/__snapshots__/monitor_charts.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/__snapshots__/monitor_charts.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/__snapshots__/monitor_charts.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/__snapshots__/monitor_charts.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/monitor/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/index.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/__snapshots__/confirm_delete.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/__snapshots__/confirm_delete.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ml/__snapshots__/confirm_delete.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/__snapshots__/confirm_delete.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/__snapshots__/ml_integerations.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/__snapshots__/ml_integerations.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ml/__snapshots__/ml_integerations.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/__snapshots__/ml_integerations.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/confirm_alert_delete.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/confirm_alert_delete.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ml/confirm_alert_delete.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/confirm_alert_delete.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/confirm_delete.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/confirm_delete.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ml/confirm_delete.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/confirm_delete.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/confirm_delete.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/confirm_delete.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ml/confirm_delete.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/confirm_delete.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ml/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/index.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/license_info.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/license_info.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ml/license_info.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/license_info.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/license_info.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/license_info.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ml/license_info.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/license_info.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/manage_ml_job.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/manage_ml_job.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor/ml/manage_ml_job.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/manage_ml_job.tsx index 435eb7e4f9001..f6b8198a24699 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ml/manage_ml_job.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/manage_ml_job.tsx @@ -10,7 +10,7 @@ import React, { useCallback, useContext, useState } from 'react'; import { EuiButton, EuiContextMenu, EuiIcon, EuiPopover } from '@elastic/eui'; import { useSelector, useDispatch } from 'react-redux'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { CLIENT_ALERT_TYPES } from '../../../../common/constants/alerts'; +import { CLIENT_ALERT_TYPES } from '../../../../../common/constants/alerts'; import { canDeleteMLJobSelector, hasMLJobSelector, diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/ml_flyout.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_flyout.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor/ml/ml_flyout.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_flyout.test.tsx index 8669bc180f42f..6a0bda2c5fb04 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ml/ml_flyout.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_flyout.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { MLFlyoutView } from './ml_flyout'; import { UptimeSettingsContext } from '../../../contexts'; -import { CLIENT_DEFAULTS } from '../../../../common/constants'; +import { CLIENT_DEFAULTS } from '../../../../../common/constants'; import * as redux from 'react-redux'; import { render, forNearestButton } from '../../../lib/helper/rtl_helpers'; import * as labels from './translations'; diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/ml_flyout.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_flyout.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ml/ml_flyout.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_flyout.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/ml_flyout_container.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_flyout_container.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor/ml/ml_flyout_container.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_flyout_container.tsx index 482e3f70b3180..49eaef7b176ac 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ml/ml_flyout_container.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_flyout_container.tsx @@ -31,7 +31,7 @@ import { useGetUrlParams } from '../../../hooks'; import { getDynamicSettings } from '../../../state/actions/dynamic_settings'; import { useMonitorId } from '../../../hooks'; import { kibanaService } from '../../../state/kibana_service'; -import { CLIENT_ALERT_TYPES } from '../../../../common/constants/alerts'; +import { CLIENT_ALERT_TYPES } from '../../../../../common/constants/alerts'; interface Props { onClose: () => void; diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/ml_integeration.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_integeration.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor/ml/ml_integeration.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_integeration.tsx index 6396f99e4d62c..675554906694b 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ml/ml_integeration.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_integeration.tsx @@ -22,7 +22,7 @@ import { UptimeRefreshContext } from '../../../contexts'; import * as labels from './translations'; import { ManageMLJobComponent } from './manage_ml_job'; import { useMonitorId } from '../../../hooks'; -import { getMLJobId } from '../../../../common/lib'; +import { getMLJobId } from '../../../../../common/lib'; export const MLIntegrationComponent = () => { const [isMlFlyoutOpen, setIsMlFlyoutOpen] = useState(false); diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/ml_integerations.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_integerations.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ml/ml_integerations.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_integerations.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/ml_job_link.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_job_link.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ml/ml_job_link.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_job_link.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/ml_job_link.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_job_link.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/monitor/ml/ml_job_link.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_job_link.tsx index e14f37f617aac..2e9d771e11150 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ml/ml_job_link.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_job_link.tsx @@ -9,7 +9,7 @@ import React from 'react'; import url from 'url'; import { EuiButtonEmpty } from '@elastic/eui'; import rison, { RisonValue } from 'rison-node'; -import { getMLJobId } from '../../../../common/lib'; +import { getMLJobId } from '../../../../../common/lib'; interface Props { monitorId: string; diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/ml_manage_job.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_manage_job.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ml/ml_manage_job.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/ml_manage_job.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/translations.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/translations.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ml/translations.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/translations.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/use_anomaly_alert.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/use_anomaly_alert.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ml/use_anomaly_alert.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/use_anomaly_alert.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/monitor_charts.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_charts.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/monitor_charts.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_charts.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/monitor_charts.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_charts.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/monitor_charts.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_charts.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/monitor_duration/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_duration/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/monitor_duration/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_duration/index.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/monitor_duration/monitor_duration.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_duration/monitor_duration.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/monitor/monitor_duration/monitor_duration.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_duration/monitor_duration.tsx index 6d8615226f79b..822a476f5225a 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/monitor_duration/monitor_duration.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_duration/monitor_duration.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; -import { LocationDurationLine } from '../../../../common/types'; +import { LocationDurationLine } from '../../../../../common/types'; import { MLIntegrationComponent } from '../ml/ml_integeration'; import { AnomalyRecords } from '../../../state/actions'; import { DurationChartComponent } from '../../common/charts'; diff --git a/x-pack/plugins/synthetics/public/components/monitor/monitor_duration/monitor_duration_container.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_duration/monitor_duration_container.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/monitor/monitor_duration/monitor_duration_container.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_duration/monitor_duration_container.tsx index 8b88c8d59a47c..63f95fd966544 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/monitor_duration/monitor_duration_container.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_duration/monitor_duration_container.tsx @@ -23,8 +23,8 @@ import { } from '../../../state/selectors'; import { UptimeRefreshContext } from '../../../contexts'; import { MonitorDurationComponent } from './monitor_duration'; -import { MonitorIdParam } from '../../../../common/types'; -import { getMLJobId } from '../../../../common/lib'; +import { MonitorIdParam } from '../../../../../common/types'; +import { getMLJobId } from '../../../../../common/lib'; import { useUptimeSettingsContext } from '../../../contexts/uptime_settings_context'; export const MonitorDuration: React.FC = ({ monitorId }) => { diff --git a/x-pack/plugins/synthetics/public/components/monitor/monitor_title.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_title.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor/monitor_title.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_title.test.tsx index 726ad235f7f49..f9e3572ead511 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/monitor_title.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_title.test.tsx @@ -10,7 +10,7 @@ import moment from 'moment'; import { screen } from '@testing-library/react'; import { render } from '../../lib/helper/rtl_helpers'; import * as reactRouterDom from 'react-router-dom'; -import { Ping } from '../../../common/runtime_types'; +import { Ping } from '../../../../common/runtime_types'; import { MonitorPageTitle, MonitorPageTitleContent } from './monitor_title'; jest.mock('react-router-dom', () => { diff --git a/x-pack/plugins/synthetics/public/components/monitor/monitor_title.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_title.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor/monitor_title.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_title.tsx index 0d77179cd99ee..ce343ac1f6bd7 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/monitor_title.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/monitor_title.tsx @@ -12,7 +12,7 @@ import { useSelector } from 'react-redux'; import { useMonitorId } from '../../hooks'; import { monitorStatusSelector } from '../../state/selectors'; import { EnableMonitorAlert } from '../overview/monitor_list/columns/enable_alert'; -import { Ping } from '../../../common/runtime_types/ping'; +import { Ping } from '../../../../common/runtime_types/ping'; import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; const isAutogeneratedId = (id: string) => { diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_histogram/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_histogram/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_histogram/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_histogram/index.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_histogram/ping_histogram_container.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_histogram/ping_histogram_container.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_histogram/ping_histogram_container.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_histogram/ping_histogram_container.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/__snapshots__/expanded_row.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/__snapshots__/expanded_row.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/__snapshots__/expanded_row.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/__snapshots__/expanded_row.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/__snapshots__/ping_headers.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/__snapshots__/ping_headers.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/__snapshots__/ping_headers.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/__snapshots__/ping_headers.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/expand_row.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/expand_row.test.tsx similarity index 94% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/expand_row.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/expand_row.test.tsx index e3ac1f2e17125..06b43744a2ac2 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/expand_row.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/expand_row.test.tsx @@ -8,12 +8,12 @@ import React from 'react'; import { screen } from '@testing-library/react'; -import { makePing } from '../../../../../common/runtime_types'; +import { makePing } from '../../../../../../common/runtime_types'; import { render } from '../../../../lib/helper/rtl_helpers'; import { ExpandRowColumn } from './expand_row'; -import { Ping } from '../../../../../common/runtime_types/ping'; +import { Ping } from '../../../../../../common/runtime_types/ping'; describe('ExpandRowColumn', () => { const defaultPing = makePing({ diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/expand_row.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/expand_row.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/expand_row.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/expand_row.tsx index 1c18668741292..3e5104357463f 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/expand_row.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/expand_row.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonIcon } from '@elastic/eui'; -import { Ping } from '../../../../../common/runtime_types/ping'; +import { Ping } from '../../../../../../common/runtime_types/ping'; import { PingListExpandedRowComponent } from '../expanded_row'; export const toggleDetails = ( diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/failed_step.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/failed_step.tsx similarity index 88% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/failed_step.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/failed_step.tsx index 38f51a2bafebb..da3b93bd0e09d 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/failed_step.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/failed_step.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { FailedStepsApiResponse } from '../../../../../common/runtime_types/ping/synthetics'; +import { FailedStepsApiResponse } from '../../../../../../common/runtime_types/ping/synthetics'; interface Props { checkGroup?: string; diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_error.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_error.tsx similarity index 91% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_error.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_error.tsx index db7dc46fc9043..beb29b4201438 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_error.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_error.tsx @@ -7,7 +7,7 @@ import React from 'react'; import styled from 'styled-components'; -import { Ping } from '../../../../../common/runtime_types/ping'; +import { Ping } from '../../../../../../common/runtime_types/ping'; const StyledSpan = styled.span` display: -webkit-box; diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_status.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_status.tsx similarity index 93% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_status.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_status.tsx index 6c33018126983..6d46803da22fd 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_status.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_status.tsx @@ -9,8 +9,8 @@ import React, { useContext } from 'react'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; import { EuiBadge, EuiSpacer, EuiText } from '@elastic/eui'; -import { Ping } from '../../../../../common/runtime_types/ping'; -import { MONITOR_TYPES, STATUS } from '../../../../../common/constants'; +import { Ping } from '../../../../../../common/runtime_types/ping'; +import { MONITOR_TYPES, STATUS } from '../../../../../../common/constants'; import { UptimeThemeContext } from '../../../../contexts'; import { STATUS_COMPLETE_LABEL, diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/index.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/no_image_available.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/no_image_available.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/no_image_available.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/no_image_available.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/no_image_available.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/no_image_available.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/no_image_available.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/no_image_available.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/no_image_display.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/no_image_display.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/no_image_display.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/no_image_display.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/no_image_display.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/no_image_display.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/no_image_display.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/no_image_display.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx index 4083df8c98a1a..3bc443426d523 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx @@ -16,7 +16,7 @@ import { isScreenshotRef, ScreenshotImageBlob, ScreenshotRefImageData, -} from '../../../../../../common/runtime_types'; +} from '../../../../../../../common/runtime_types'; import { getJourneyScreenshot } from '../../../../../state/api/journey'; import { UptimeSettingsContext } from '../../../../../contexts'; diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx index eb0d8c30ae53d..73996c4e3a1b7 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx @@ -9,8 +9,8 @@ import React, { MouseEvent, useEffect } from 'react'; import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiText, useEuiTheme } from '@elastic/eui'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; -import { ScreenshotRefImageData } from '../../../../../../common/runtime_types'; -import { useBreakpoints } from '../../../../../hooks'; +import { ScreenshotRefImageData } from '../../../../../../../common/runtime_types'; +import { useBreakpoints } from '../../../../../../hooks/use_breakpoints'; import { nextAriaLabel, prevAriaLabel } from './translations'; diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx index 73c43da98bfc4..1d97e3e4e248a 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx @@ -8,7 +8,7 @@ import { EuiImage, EuiLoadingSpinner, EuiPopover } from '@elastic/eui'; import React from 'react'; import styled from 'styled-components'; -import { ScreenshotRefImageData } from '../../../../../../common/runtime_types/ping/synthetics'; +import { ScreenshotRefImageData } from '../../../../../../../common/runtime_types/ping/synthetics'; import { fullSizeImageAlt } from './translations'; import { useCompositeImage } from '../../../../../hooks/use_composite_image'; diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/translations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/translations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/translations.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/response_code.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/response_code.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/response_code.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/response_code.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/doc_link_body.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/doc_link_body.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/doc_link_body.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/doc_link_body.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/doc_link_body.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/doc_link_body.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/doc_link_body.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/doc_link_body.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/expanded_row.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/expanded_row.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/expanded_row.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/expanded_row.test.tsx index f87501a79e964..d65536bf75c4a 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/expanded_row.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/expanded_row.test.tsx @@ -8,7 +8,7 @@ import { mountWithIntl, renderWithIntl, shallowWithIntl } from '@kbn/test-jest-helpers'; import React from 'react'; import { PingListExpandedRowComponent } from './expanded_row'; -import { Ping } from '../../../../common/runtime_types'; +import { Ping } from '../../../../../common/runtime_types'; import { DocLinkForBody } from './doc_link_body'; describe('PingListExpandedRow', () => { diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/expanded_row.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/expanded_row.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/expanded_row.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/expanded_row.tsx index 3e14136ac3533..0d27d7cbb92b9 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/expanded_row.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/expanded_row.tsx @@ -18,7 +18,7 @@ import { } from '@elastic/eui'; import React from 'react'; import { i18n } from '@kbn/i18n'; -import { Ping, HttpResponseBody } from '../../../../common/runtime_types'; +import { Ping, HttpResponseBody } from '../../../../../common/runtime_types'; import { DocLinkForBody } from './doc_link_body'; import { PingRedirects } from './ping_redirects'; import { PingHeaders } from './headers'; diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/headers.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/headers.tsx similarity index 94% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/headers.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/headers.tsx index edc8119dd59d3..bff0e91f2cd05 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/headers.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/headers.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { EuiAccordion, EuiDescriptionList, EuiSpacer, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { PingHeaders as HeadersProp } from '../../../../common/runtime_types'; +import { PingHeaders as HeadersProp } from '../../../../../common/runtime_types'; interface Props { headers: HeadersProp; diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/index.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/index.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/index.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/index.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/location_name.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/location_name.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/location_name.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/location_name.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_headers.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/ping_headers.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_headers.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/ping_headers.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/ping_list.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/ping_list.test.tsx index bfcf359ac0525..ddb33e4dd5fea 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/ping_list.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { formatDuration, PingList } from './ping_list'; -import { Ping, PingsResponse } from '../../../../common/runtime_types'; +import { Ping, PingsResponse } from '../../../../../common/runtime_types'; import { ExpandedRowMap } from '../../overview/monitor_list/types'; import { rowShouldExpand, toggleDetails } from './columns/expand_row'; import * as pingListHook from './use_pings'; diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/ping_list.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/ping_list.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list_header.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/ping_list_header.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list_header.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/ping_list_header.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list_table.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/ping_list_table.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list_table.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/ping_list_table.tsx index da464b7e1a083..1dbda32925161 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list_table.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/ping_list_table.tsx @@ -12,11 +12,11 @@ import moment from 'moment'; import { useHistory } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import * as I18LABELS from './translations'; -import { FailedStepsApiResponse, Ping } from '../../../../common/runtime_types'; +import { FailedStepsApiResponse, Ping } from '../../../../../common/runtime_types'; import { PingStatusColumn } from './columns/ping_status'; import { ERROR_LABEL, LOCATION_LABEL, RES_CODE_LABEL, TIMESTAMP_LABEL } from './translations'; import { LocationName } from './location_name'; -import { MONITOR_TYPES } from '../../../../common/constants'; +import { MONITOR_TYPES } from '../../../../../common/constants'; import { PingTimestamp } from './columns/ping_timestamp'; import { getShortTimeStamp } from '../../overview/monitor_list/columns/monitor_status_column'; import { PingErrorCol } from './columns/ping_error'; diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_redirects.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/ping_redirects.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_redirects.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/ping_redirects.tsx index 37e5961305a60..365a52e8f54fe 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_redirects.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/ping_redirects.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; import { EuiListGroup, EuiListGroupItemProps, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui'; -import { Ping } from '../../../../common/runtime_types/ping'; +import { Ping } from '../../../../../common/runtime_types/ping'; const ListGroup = styled(EuiListGroup)` &&& { diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/response_code.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/response_code.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/response_code.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/response_code.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/translations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/translations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/translations.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/use_pings.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/use_pings.ts similarity index 94% rename from x-pack/plugins/synthetics/public/components/monitor/ping_list/use_pings.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/use_pings.ts index 94669b4aeb8e8..6306e8357018c 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/use_pings.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/use_pings.ts @@ -9,13 +9,13 @@ import { useDispatch, useSelector } from 'react-redux'; import { useCallback, useContext, useEffect } from 'react'; import { useFetcher } from '@kbn/observability-plugin/public'; import { selectPingList } from '../../../state/selectors'; -import { GetPingsParams, Ping } from '../../../../common/runtime_types/ping'; +import { GetPingsParams, Ping } from '../../../../../common/runtime_types/ping'; import { getPings as getPingsAction } from '../../../state/actions'; import { useGetUrlParams, useMonitorId } from '../../../hooks'; import { UptimeRefreshContext, UptimeSettingsContext } from '../../../contexts'; import { fetchJourneysFailedSteps } from '../../../state/api/journey'; import { useSelectedFilters } from '../../../hooks/use_selected_filters'; -import { MONITOR_TYPES } from '../../../../common/constants'; +import { MONITOR_TYPES } from '../../../../../common/constants'; interface Props { pageSize: number; diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/__snapshots__/monitor_status.bar.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/__snapshots__/monitor_status.bar.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/__snapshots__/monitor_status.bar.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/__snapshots__/monitor_status.bar.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/__snapshots__/ssl_certificate.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/__snapshots__/ssl_certificate.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/__snapshots__/ssl_certificate.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/__snapshots__/ssl_certificate.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/__snapshots__/status_by_location.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/__snapshots__/status_by_location.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/__snapshots__/status_by_location.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/__snapshots__/status_by_location.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/__snapshots__/tag_label.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/__snapshots__/tag_label.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/__snapshots__/tag_label.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/__snapshots__/tag_label.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/availability_reporting.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/availability_reporting.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/availability_reporting.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/availability_reporting.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/availability_reporting.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/availability_reporting.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/availability_reporting.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/availability_reporting.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/index.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/location_status_tags.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/location_status_tags.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/location_status_tags.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/location_status_tags.test.tsx index e43df25779ab9..48862695b2089 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/location_status_tags.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/location_status_tags.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { MonitorLocation } from '../../../../../common/runtime_types/monitor'; +import { MonitorLocation } from '../../../../../../common/runtime_types/monitor'; import { LocationStatusTags } from '.'; import { mockMoment } from '../../../../lib/helper/test_helpers'; import { render } from '../../../../lib/helper/rtl_helpers'; diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/location_status_tags.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/location_status_tags.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/location_status_tags.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/location_status_tags.tsx index 53be56d16b9fe..b50b84235dad7 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/location_status_tags.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/location_status_tags.tsx @@ -9,8 +9,8 @@ import React, { useContext } from 'react'; import moment from 'moment'; import styled from 'styled-components'; import { UptimeThemeContext } from '../../../../contexts'; -import { MonitorLocation } from '../../../../../common/runtime_types'; -import { SHORT_TIMESPAN_LOCALE, SHORT_TS_LOCALE } from '../../../../../common/constants'; +import { MonitorLocation } from '../../../../../../common/runtime_types'; +import { SHORT_TIMESPAN_LOCALE, SHORT_TS_LOCALE } from '../../../../../../common/constants'; import { AvailabilityReporting } from '..'; import { getShortTimeStamp } from '../../../overview/monitor_list/columns/monitor_status_column'; diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/tag_label.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/tag_label.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/tag_label.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/tag_label.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/tag_label.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/tag_label.tsx similarity index 92% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/tag_label.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/tag_label.tsx index 6bf73b9609e58..8b920848cdc53 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/availability_reporting/tag_label.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/availability_reporting/tag_label.tsx @@ -9,7 +9,7 @@ import React from 'react'; import styled from 'styled-components'; import { EuiBadge } from '@elastic/eui'; import { StatusTag } from './location_status_tags'; -import { STATUS } from '../../../../../common/constants'; +import { STATUS } from '../../../../../../common/constants'; const BadgeItem = styled.div` white-space: nowrap; diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/index.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/location_availability/location_availability.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/location_availability/location_availability.test.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/location_availability/location_availability.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/location_availability/location_availability.test.tsx index 855b8ef0c9767..a09e3530d5081 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/location_availability/location_availability.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/location_availability/location_availability.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { screen } from '@testing-library/react'; import { render } from '../../../../lib/helper/rtl_helpers'; import { LocationAvailability } from './location_availability'; -import { MonitorLocations } from '../../../../../common/runtime_types'; +import { MonitorLocations } from '../../../../../../common/runtime_types'; // Note For shallow test, we need absolute time strings describe('LocationAvailability component', () => { diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/location_availability/location_availability.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/location_availability/location_availability.tsx similarity index 94% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/location_availability/location_availability.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/location_availability/location_availability.tsx index c851369d63e9e..f7634c77eb017 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/location_availability/location_availability.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/location_availability/location_availability.tsx @@ -9,7 +9,7 @@ import React from 'react'; import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, EuiErrorBoundary, EuiTitle } from '@elastic/eui'; import { LocationStatusTags } from '../availability_reporting'; -import { MonitorLocations } from '../../../../../common/runtime_types'; +import { MonitorLocations } from '../../../../../../common/runtime_types'; import { MonitoringFrom } from '../translations'; const EuiFlexItemTags = styled(EuiFlexItem)` diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/monitor_status.bar.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/monitor_status.bar.test.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/monitor_status.bar.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/monitor_status.bar.test.tsx index af3c47b9caf30..640d207fbb138 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/monitor_status.bar.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/monitor_status.bar.test.tsx @@ -8,7 +8,7 @@ import moment from 'moment'; import React from 'react'; import { MonitorStatusBar } from './status_bar'; -import { Ping } from '../../../../common/runtime_types'; +import { Ping } from '../../../../../common/runtime_types'; import * as redux from 'react-redux'; import { renderWithRouter } from '../../../lib'; import { createMemoryHistory } from 'history'; diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/ssl_certificate.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/ssl_certificate.test.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/ssl_certificate.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/ssl_certificate.test.tsx index 03ce292e63621..3951ced34c0f9 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/ssl_certificate.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/ssl_certificate.test.tsx @@ -8,11 +8,11 @@ import React from 'react'; import moment from 'moment'; import { EuiIcon } from '@elastic/eui'; -import { Tls } from '../../../../common/runtime_types'; +import { Tls } from '../../../../../common/runtime_types'; import { MonitorSSLCertificate } from './status_bar'; import * as redux from 'react-redux'; import { mountWithRouter, renderWithRouter, shallowWithRouter } from '../../../lib'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; +import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../common/constants'; describe('SSL Certificate component', () => { let monitorTls: Tls; diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/index.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/monitor_redirects.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/monitor_redirects.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/monitor_redirects.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/monitor_redirects.tsx index f59f01e6aef33..2f1dba51bb933 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/monitor_redirects.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/monitor_redirects.tsx @@ -9,7 +9,7 @@ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiPopover } from '@elastic/eui'; import styled from 'styled-components'; -import { Ping } from '../../../../../common/runtime_types'; +import { Ping } from '../../../../../../common/runtime_types'; import { PingRedirects } from '../../ping_list/ping_redirects'; import { MonListDescription, MonListTitle } from './status_bar'; diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/ssl_certificate.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/ssl_certificate.tsx similarity index 91% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/ssl_certificate.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/ssl_certificate.tsx index 13be917075aca..5d421cbd4a448 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/ssl_certificate.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/ssl_certificate.tsx @@ -9,8 +9,8 @@ import React from 'react'; import { Link } from 'react-router-dom'; import { EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { Tls, X509Expiry } from '../../../../../common/runtime_types'; -import { CERTIFICATES_ROUTE } from '../../../../../common/constants'; +import { Tls, X509Expiry } from '../../../../../../common/runtime_types'; +import { CERTIFICATES_ROUTE } from '../../../../../../common/constants'; import { MonListDescription, MonListTitle } from './status_bar'; import { CertStatusColumn } from '../../../overview/monitor_list/columns/cert_status_column'; diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/status_bar.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/status_bar.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/status_bar.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/status_bar.test.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/status_bar.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/status_bar.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/status_bar.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/status_bar.tsx index 9746a6c676ab5..7ca855bc0a697 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/status_bar.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/status_bar.tsx @@ -22,7 +22,7 @@ import { StatusByLocations } from './status_by_location'; import { useStatusBar } from './use_status_bar'; import { MonitorIDLabel, OverallAvailability } from '../translations'; import { TAGS_LABEL, URL_LABEL } from '../../../common/translations'; -import { MonitorLocations } from '../../../../../common/runtime_types/monitor'; +import { MonitorLocations } from '../../../../../../common/runtime_types/monitor'; import { formatAvailabilityValue } from '../availability_reporting/availability_reporting'; import { MonitorRedirects } from './monitor_redirects'; import { MonitorTags } from '../../../common/monitor_tags'; diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/status_by_location.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/status_by_location.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/status_by_location.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/status_by_location.tsx index 60cd231a401c0..3296e784d621a 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/status_by_location.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/status_by_location.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { MonitorLocation } from '../../../../../common/runtime_types'; +import { MonitorLocation } from '../../../../../../common/runtime_types'; interface StatusByLocationsProps { locations: MonitorLocation[]; diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/use_status_bar.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/use_status_bar.ts similarity index 90% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/use_status_bar.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/use_status_bar.ts index 058227d1db1eb..57de00af67081 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/use_status_bar.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_bar/use_status_bar.ts @@ -12,8 +12,8 @@ import { useGetUrlParams, useMonitorId } from '../../../../hooks'; import { monitorLocationsSelector, monitorStatusSelector } from '../../../../state/selectors'; import { AppState } from '../../../../state'; import { getMonitorStatusAction } from '../../../../state/actions'; -import { Ping } from '../../../../../common/runtime_types/ping'; -import { MonitorLocations } from '../../../../../common/runtime_types/monitor'; +import { Ping } from '../../../../../../common/runtime_types/ping'; +import { MonitorLocations } from '../../../../../../common/runtime_types/monitor'; interface MonitorStatusBarProps { monitorId: string; diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_by_location.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_by_location.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/status_by_location.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_by_location.test.tsx index 9b58b0deb8ecc..0b3b08cba8343 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_by_location.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_by_location.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { renderWithIntl, shallowWithIntl } from '@kbn/test-jest-helpers'; -import { MonitorLocation } from '../../../../common/runtime_types'; +import { MonitorLocation } from '../../../../../common/runtime_types'; import { StatusByLocations } from '.'; describe('StatusByLocation component', () => { diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_details.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_details.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/status_details.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_details.tsx index 5b20e83f0ec85..0c9c347fc8c80 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_details.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_details.tsx @@ -10,7 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import styled from 'styled-components'; import { LocationAvailability } from './location_availability/location_availability'; import { UptimeRefreshContext } from '../../../contexts'; -import { MonitorLocations } from '../../../../common/runtime_types'; +import { MonitorLocations } from '../../../../../common/runtime_types'; import { MonitorStatusBar } from './status_bar'; interface MonitorStatusDetailsProps { diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_details_container.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_details_container.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/status_details_container.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_details_container.tsx index 1fb138da9e84b..7affbde63ba02 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_details_container.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/status_details_container.tsx @@ -13,7 +13,7 @@ import { getMonitorLocationsAction } from '../../../state/actions/monitor'; import { MonitorStatusDetailsComponent } from '.'; import { UptimeRefreshContext } from '../../../contexts'; import { AppState } from '../../../state'; -import { MonitorIdParam } from '../../../../common/types'; +import { MonitorIdParam } from '../../../../../common/types'; export const MonitorStatusDetails: React.FC = ({ monitorId }) => { const { lastRefresh } = useContext(UptimeRefreshContext); diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/translations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/status_details/translations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/status_details/translations.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/step_detail_container.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/step_detail_container.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/step_detail_container.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/step_detail_container.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/step_page_nav.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/step_page_nav.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/step_page_nav.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/step_page_nav.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/step_page_title.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/step_page_title.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/step_page_title.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/step_page_title.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx index 0d7b5894799c7..013516bb754bf 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { useBreadcrumbs } from '../../../../hooks/use_breadcrumbs'; import { JourneyState } from '../../../../state/reducers/journey'; -import { PLUGIN } from '../../../../../common/constants/plugin'; +import { PLUGIN } from '../../../../../../common/constants/plugin'; import { getShortTimeStamp } from '../../../overview/monitor_list/columns/monitor_status_column'; interface ActiveStep { diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx index a46fad2733bb2..2ae975803b524 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx @@ -11,8 +11,8 @@ import { Route } from 'react-router-dom'; import { of } from 'rxjs'; import { render } from '../../../../lib/helper/rtl_helpers'; import { useMonitorBreadcrumb } from './use_monitor_breadcrumb'; -import { OVERVIEW_ROUTE } from '../../../../../common/constants'; -import { Ping } from '../../../../../common/runtime_types/ping'; +import { OVERVIEW_ROUTE } from '../../../../../../common/constants'; +import { Ping } from '../../../../../../common/runtime_types/ping'; import { JourneyState } from '../../../../state/reducers/journey'; import { chromeServiceMock, uiSettingsServiceMock } from '@kbn/core/public/mocks'; diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/use_step_waterfall_metrics.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/use_step_waterfall_metrics.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/use_step_waterfall_metrics.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/use_step_waterfall_metrics.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/use_step_waterfall_metrics.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/use_step_waterfall_metrics.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/use_step_waterfall_metrics.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/use_step_waterfall_metrics.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts similarity index 99% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts index fa3db4d01fe9b..7323668dc3d52 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts @@ -23,7 +23,7 @@ import { LegendItems, } from './types'; import { WaterfallData, WaterfallMetadata } from '../../waterfall'; -import { NetworkEvent } from '../../../../../../common/runtime_types'; +import { NetworkEvent } from '../../../../../../../common/runtime_types'; export const extractItems = (data: NetworkEvent[]): NetworkItems => { // NOTE: This happens client side as the "payload" property is mapped diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/types.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/types.ts new file mode 100644 index 0000000000000..c396bec47e0e6 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/types.ts @@ -0,0 +1,262 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { NetworkEvent } from '../../../../../../../common/runtime_types'; + +export enum Timings { + Blocked = 'blocked', + Dns = 'dns', + Connect = 'connect', + Ssl = 'ssl', + Send = 'send', + Wait = 'wait', + Receive = 'receive', +} + +export enum Metadata { + Status = 'status', + ResourceSize = 'resourceSize', + TransferSize = 'transferSize', + CertificateIssuer = 'certificateIssuer', + CertificateIssueDate = 'certificateIssueDate', + CertificateExpiryDate = 'certificateExpiryDate', + CertificateSubject = 'certificateSubject', + IP = 'ip', + MimeType = 'mimeType', + RequestStart = 'requestStart', +} + +export const FriendlyTimingLabels = { + [Timings.Blocked]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.timings.blocked', + { + defaultMessage: 'Queued / Blocked', + } + ), + [Timings.Dns]: i18n.translate('xpack.synthetics.synthetics.waterfallChart.labels.timings.dns', { + defaultMessage: 'DNS', + }), + [Timings.Connect]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.timings.connect', + { + defaultMessage: 'Connecting', + } + ), + [Timings.Ssl]: i18n.translate('xpack.synthetics.synthetics.waterfallChart.labels.timings.ssl', { + defaultMessage: 'TLS', + }), + [Timings.Send]: i18n.translate('xpack.synthetics.synthetics.waterfallChart.labels.timings.send', { + defaultMessage: 'Sending request', + }), + [Timings.Wait]: i18n.translate('xpack.synthetics.synthetics.waterfallChart.labels.timings.wait', { + defaultMessage: 'Waiting (TTFB)', + }), + [Timings.Receive]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.timings.receive', + { + defaultMessage: 'Content downloading', + } + ), +}; + +export const FriendlyFlyoutLabels = { + [Metadata.Status]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.status', + { + defaultMessage: 'Status', + } + ), + [Metadata.MimeType]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.contentType', + { + defaultMessage: 'Content type', + } + ), + [Metadata.RequestStart]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.requestStart', + { + defaultMessage: 'Request start', + } + ), + [Metadata.ResourceSize]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.resourceSize', + { + defaultMessage: 'Resource size', + } + ), + [Metadata.TransferSize]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.transferSize', + { + defaultMessage: 'Transfer size', + } + ), + [Metadata.CertificateIssuer]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.certificateIssuer', + { + defaultMessage: 'Issuer', + } + ), + [Metadata.CertificateIssueDate]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.certificateIssueDate', + { + defaultMessage: 'Valid from', + } + ), + [Metadata.CertificateExpiryDate]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.certificateExpiryDate', + { + defaultMessage: 'Valid until', + } + ), + [Metadata.CertificateSubject]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.metadata.certificateSubject', + { + defaultMessage: 'Common name', + } + ), + [Metadata.IP]: i18n.translate('xpack.synthetics.synthetics.waterfallChart.labels.metadata.ip', { + defaultMessage: 'IP', + }), +}; + +export const TIMING_ORDER = [ + Timings.Blocked, + Timings.Dns, + Timings.Connect, + Timings.Ssl, + Timings.Send, + Timings.Wait, + Timings.Receive, +] as const; + +export const META_DATA_ORDER_FLYOUT = [ + Metadata.MimeType, + Timings.Dns, + Timings.Connect, + Timings.Ssl, + Timings.Wait, + Timings.Receive, +] as const; + +export type CalculatedTimings = { + [K in Timings]?: number; +}; + +export enum MimeType { + Html = 'html', + Script = 'script', + Stylesheet = 'stylesheet', + Media = 'media', + Font = 'font', + XHR = 'xhr', + Other = 'other', +} + +export const FriendlyMimetypeLabels = { + [MimeType.Html]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.mimeTypes.html', + { + defaultMessage: 'HTML', + } + ), + [MimeType.Script]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.mimeTypes.script', + { + defaultMessage: 'JS', + } + ), + [MimeType.Stylesheet]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.mimeTypes.stylesheet', + { + defaultMessage: 'CSS', + } + ), + [MimeType.Media]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.mimeTypes.media', + { + defaultMessage: 'Media', + } + ), + [MimeType.Font]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.mimeTypes.font', + { + defaultMessage: 'Font', + } + ), + [MimeType.XHR]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.mimeTypes.xhr', + { + defaultMessage: 'XHR', + } + ), + [MimeType.Other]: i18n.translate( + 'xpack.synthetics.synthetics.waterfallChart.labels.mimeTypes.other', + { + defaultMessage: 'Other', + } + ), +}; + +// NOTE: This list tries to cover the standard spec compliant mime types, +// and a few popular non-standard ones, but it isn't exhaustive. +export const MimeTypesMap: Record = { + 'text/html': MimeType.Html, + 'application/javascript': MimeType.Script, + 'text/javascript': MimeType.Script, + 'text/css': MimeType.Stylesheet, + // Images + 'image/apng': MimeType.Media, + 'image/bmp': MimeType.Media, + 'image/gif': MimeType.Media, + 'image/x-icon': MimeType.Media, + 'image/jpeg': MimeType.Media, + 'image/png': MimeType.Media, + 'image/svg+xml': MimeType.Media, + 'image/tiff': MimeType.Media, + 'image/webp': MimeType.Media, + // Common audio / video formats + 'audio/wave': MimeType.Media, + 'audio/wav': MimeType.Media, + 'audio/x-wav': MimeType.Media, + 'audio/x-pn-wav': MimeType.Media, + 'audio/webm': MimeType.Media, + 'video/webm': MimeType.Media, + 'video/mp4': MimeType.Media, + 'audio/ogg': MimeType.Media, + 'video/ogg': MimeType.Media, + 'application/ogg': MimeType.Media, + // Fonts + 'font/otf': MimeType.Font, + 'font/ttf': MimeType.Font, + 'font/woff': MimeType.Font, + 'font/woff2': MimeType.Font, + 'application/x-font-opentype': MimeType.Font, + 'application/font-woff': MimeType.Font, + 'application/font-woff2': MimeType.Font, + 'application/vnd.ms-fontobject': MimeType.Font, + 'application/font-sfnt': MimeType.Font, + + // XHR + 'application/json': MimeType.XHR, +}; + +export type NetworkItem = NetworkEvent; +export type NetworkItems = NetworkItem[]; + +export type SidebarItem = Pick & { + isHighlighted: boolean; + index: number; + offsetIndex: number; +}; +export type SidebarItems = SidebarItem[]; + +export interface LegendItem { + name: string; + colour: string; +} +export type LegendItems = LegendItem[]; diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx index b31af8e31d33a..95c42d505fbf2 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx @@ -15,7 +15,7 @@ import { networkEventsSelector } from '../../../../../state/selectors'; import { WaterfallChartWrapper } from './waterfall_chart_wrapper'; import { extractItems } from './data_formatting'; import { useStepWaterfallMetrics } from '../use_step_waterfall_metrics'; -import { JourneyStep } from '../../../../../../common/runtime_types'; +import { JourneyStep } from '../../../../../../../common/runtime_types'; export const NO_DATA_TEXT = i18n.translate( 'xpack.synthetics.synthetics.stepDetail.waterfallNoData', diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx index 4b50f86e6ee3b..0849832107aeb 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx @@ -15,7 +15,7 @@ import { WaterfallFilter } from './waterfall_filter'; import { WaterfallFlyout } from './waterfall_flyout'; import { WaterfallSidebarItem } from './waterfall_sidebar_item'; import { MarkerItems } from '../../waterfall/context/waterfall_chart'; -import { JourneyStep } from '../../../../../../common/runtime_types'; +import { JourneyStep } from '../../../../../../../common/runtime_types'; export const renderLegendItem: RenderItem = (item) => { return ( diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_filter.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_filter.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_filter.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_filter.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_filter.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_filter.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_filter.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_filter.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_flyout.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_flyout.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_flyout.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_flyout.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_flyout.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_flyout.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_flyout.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_flyout.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_sidebar_item.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_sidebar_item.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_sidebar_item.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_sidebar_item.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_sidebar_item.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_sidebar_item.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_sidebar_item.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/step_detail/waterfall/waterfall_sidebar_item.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/translations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/translations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/translations.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/README.md b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/README.md similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/README.md rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/README.md diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/constants.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/constants.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/constants.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/constants.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/legend.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/legend.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/legend.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/legend.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/middle_truncated_text.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/middle_truncated_text.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/middle_truncated_text.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/middle_truncated_text.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/network_requests_total.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/network_requests_total.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/network_requests_total.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/network_requests_total.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/network_requests_total.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/network_requests_total.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/network_requests_total.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/network_requests_total.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/sidebar.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/sidebar.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/sidebar.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/sidebar.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/styles.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/styles.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/styles.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/styles.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/translations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/translations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/translations.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/use_bar_charts.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/use_bar_charts.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/use_bar_charts.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/use_bar_charts.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/use_bar_charts.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/use_bar_charts.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/use_bar_charts.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/use_bar_charts.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/use_flyout.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/use_flyout.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/use_flyout.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/use_flyout.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/use_flyout.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/use_flyout.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/use_flyout.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/use_flyout.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_bar_chart.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_bar_chart.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_bar_chart.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_bar_chart.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_chart_fixed_axis.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_chart_fixed_axis.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_chart_fixed_axis.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_chart_fixed_axis.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_flyout_table.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_flyout_table.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_flyout_table.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_flyout_table.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_marker_icon.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_marker_icon.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_marker_icon.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_marker_icon.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_marker_icon.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_marker_icon.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_marker_icon.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_marker_icon.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_marker_test_helper.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_marker_test_helper.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_marker_test_helper.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_marker_test_helper.tsx index 696e6d4fa022f..691a74361d074 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_marker_test_helper.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_marker_test_helper.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { UptimeStartupPluginsContext } from '../../../../../contexts'; import { WaterfallContext } from '../context/waterfall_chart'; -import { JourneyStep } from '../../../../../../common/runtime_types'; +import { JourneyStep } from '../../../../../../../common/runtime_types'; const EmbeddableMock = ({ title, diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.test.tsx index b797cf1f3b63e..aeca7940ef773 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { render } from '../../../../../lib/helper/rtl_helpers'; import { WaterfallMarkerTrend } from './waterfall_marker_trend'; import moment from 'moment'; -import { JourneyStep } from '../../../../../../common/runtime_types'; +import { JourneyStep } from '../../../../../../../common/runtime_types'; import { TestWrapper } from './waterfall_marker_test_helper'; describe('', () => { diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_markers.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_markers.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_markers.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_markers.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_tooltip_content.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_tooltip_content.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_tooltip_content.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_tooltip_content.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_tooltip_content.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_tooltip_content.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_tooltip_content.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/components/waterfall_tooltip_content.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx index d495b7432bce7..fd38fbbfb6bb6 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx @@ -9,7 +9,7 @@ import React, { createContext, useContext, Context } from 'react'; import { WaterfallData, WaterfallDataEntry, WaterfallMetadata } from '../types'; import { OnSidebarClick, OnElementClick, OnProjectionClick } from '../components/use_flyout'; import { SidebarItems } from '../../step_detail/waterfall/types'; -import { JourneyStep } from '../../../../../../common/runtime_types'; +import { JourneyStep } from '../../../../../../../common/runtime_types'; export type MarkerItems = Array<{ id: diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/index.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/index.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/index.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/index.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/types.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/types.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/types.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/synthetics/waterfall/types.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/action_bar/action_bar.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/action_bar/action_bar.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor_management/action_bar/action_bar.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/action_bar/action_bar.test.tsx index 64b7984b00b40..e903f4d23424b 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/action_bar/action_bar.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/action_bar/action_bar.test.tsx @@ -15,7 +15,7 @@ import { HTTPFields, ScheduleUnit, SyntheticsMonitor, -} from '../../../../common/runtime_types'; +} from '../../../../../common/runtime_types'; import { ActionBar } from './action_bar'; describe('', () => { diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/action_bar/action_bar.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/action_bar/action_bar.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/monitor_management/action_bar/action_bar.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/action_bar/action_bar.tsx index a3eec01438876..80e614eb4d77f 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/action_bar/action_bar.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/action_bar/action_bar.tsx @@ -21,11 +21,11 @@ import { useSelector } from 'react-redux'; import { FETCH_STATUS, useFetcher } from '@kbn/observability-plugin/public'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; -import { MONITOR_MANAGEMENT_ROUTE } from '../../../../common/constants'; +import { MONITOR_MANAGEMENT_ROUTE } from '../../../../../common/constants'; import { UptimeSettingsContext } from '../../../contexts'; import { setMonitor } from '../../../state/api'; -import { SyntheticsMonitor } from '../../../../common/runtime_types'; +import { SyntheticsMonitor } from '../../../../../common/runtime_types'; import { TestRun } from '../test_now_mode/test_now_mode'; import { monitorManagementListSelector } from '../../../state/selectors'; @@ -65,7 +65,7 @@ export const ActionBar = ({ } return setMonitor({ monitor, - id: monitorId ? Buffer.from(monitorId, 'base64').toString('utf8') : undefined, + id: monitorId, }); }, [monitor, monitorId, isValid, isSaving]); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/action_bar/action_bar_errors.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/action_bar/action_bar_errors.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor_management/action_bar/action_bar_errors.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/action_bar/action_bar_errors.test.tsx index b92357f3ecc14..bb40077dab78d 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/action_bar/action_bar_errors.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/action_bar/action_bar_errors.test.tsx @@ -15,7 +15,7 @@ import { HTTPFields, ScheduleUnit, SyntheticsMonitor, -} from '../../../../common/runtime_types'; +} from '../../../../../common/runtime_types'; import { spyOnUseFetcher } from '../../../lib/helper/spy_use_fetcher'; import * as kibana from '../../../state/kibana_service'; import { ActionBar } from './action_bar'; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/action_bar/action_bar_portal.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/action_bar/action_bar_portal.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/action_bar/action_bar_portal.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/action_bar/action_bar_portal.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/add_monitor_btn.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/add_monitor_btn.tsx similarity index 99% rename from x-pack/plugins/synthetics/public/components/monitor_management/add_monitor_btn.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/add_monitor_btn.tsx index 3532659f04af2..90bec2d28f988 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/add_monitor_btn.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/add_monitor_btn.tsx @@ -11,7 +11,7 @@ import { EuiButton, EuiFlexItem, EuiFlexGroup, EuiToolTip, EuiSwitch } from '@el import { useHistory } from 'react-router-dom'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { kibanaService } from '../../state/kibana_service'; -import { MONITOR_ADD_ROUTE } from '../../../common/constants'; +import { MONITOR_ADD_ROUTE } from '../../../../common/constants'; import { useEnablement } from './hooks/use_enablement'; import { useSyntheticsServiceAllowed } from './hooks/use_service_allowed'; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/content/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/content/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/content/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/content/index.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/edit_monitor_config.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/edit_monitor_config.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/monitor_management/edit_monitor_config.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/edit_monitor_config.tsx index 28e0bb105e29a..245f058c48d94 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/edit_monitor_config.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/edit_monitor_config.tsx @@ -14,11 +14,11 @@ import { DataStream, ScheduleUnit, ThrottlingOptions, -} from '../../../common/runtime_types'; +} from '../../../../common/runtime_types'; import { SyntheticsProviders } from '../fleet_package/contexts'; import { PolicyConfig } from '../fleet_package/types'; import { MonitorConfig } from './monitor_config/monitor_config'; -import { DEFAULT_NAMESPACE_STRING } from '../../../common/constants'; +import { DEFAULT_NAMESPACE_STRING } from '../../../../common/constants/monitor_defaults'; interface Props { monitor: MonitorFields; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_enablement.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_enablement.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_enablement.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_enablement.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_format_monitor.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_format_monitor.ts similarity index 94% rename from x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_format_monitor.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_format_monitor.ts index 49d467d7b8799..5d600a10daf39 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_format_monitor.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_format_monitor.ts @@ -6,8 +6,8 @@ */ import { useEffect, useRef, useState } from 'react'; -import { ConfigKey, DataStream, MonitorFields } from '../../../../common/runtime_types'; -import { Validation } from '../../../../common/types'; +import { ConfigKey, DataStream, MonitorFields } from '../../../../../common/runtime_types'; +import { Validation } from '../../../../../common/types'; interface Props { monitorType: DataStream; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_inline_errors.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_inline_errors.test.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_inline_errors.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_inline_errors.test.tsx index fe20dc3ea6c9d..ebc9b55556d40 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_inline_errors.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_inline_errors.test.tsx @@ -8,7 +8,7 @@ import { renderHook } from '@testing-library/react-hooks'; import { MockRedux } from '../../../lib/helper/rtl_helpers'; import { useInlineErrors } from './use_inline_errors'; -import { DEFAULT_THROTTLING } from '../../../../common/runtime_types'; +import { DEFAULT_THROTTLING } from '../../../../../common/runtime_types'; import * as obsvPlugin from '@kbn/observability-plugin/public/hooks/use_es_search'; function mockNow(date: string | number | Date) { diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_inline_errors.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_inline_errors.ts similarity index 92% rename from x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_inline_errors.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_inline_errors.ts index adc07853a93cd..9970622ca416b 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_inline_errors.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_inline_errors.ts @@ -10,11 +10,11 @@ import moment from 'moment'; import { useMemo } from 'react'; import { useEsSearch } from '@kbn/observability-plugin/public'; import { monitorManagementListSelector } from '../../../state/selectors'; -import { Ping } from '../../../../common/runtime_types'; -import { EXCLUDE_RUN_ONCE_FILTER } from '../../../../common/constants/client_defaults'; +import { Ping } from '../../../../../common/runtime_types'; +import { EXCLUDE_RUN_ONCE_FILTER } from '../../../../../common/constants/client_defaults'; import { useUptimeRefreshContext } from '../../../contexts/uptime_refresh_context'; import { useInlineErrorsCount } from './use_inline_errors_count'; -import { SYNTHETICS_INDEX_PATTERN } from '../../../../common/constants'; +import { SYNTHETICS_INDEX_PATTERN } from '../../../../../common/constants'; const sortFieldMap: Record = { ['name.keyword']: 'monitor.name', diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_inline_errors_count.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_inline_errors_count.test.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_inline_errors_count.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_inline_errors_count.test.tsx index 7ad0d58488e99..b1dbb41880e65 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_inline_errors_count.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_inline_errors_count.test.tsx @@ -9,7 +9,7 @@ import { renderHook } from '@testing-library/react-hooks'; import { MockRedux } from '../../../lib/helper/rtl_helpers'; import { useInlineErrorsCount } from './use_inline_errors_count'; import * as obsvPlugin from '@kbn/observability-plugin/public/hooks/use_es_search'; -import { DEFAULT_THROTTLING } from '../../../../common/runtime_types'; +import { DEFAULT_THROTTLING } from '../../../../../common/runtime_types'; function mockNow(date: string | number | Date) { const fakeNow = new Date(date).getTime(); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_inline_errors_count.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_inline_errors_count.ts similarity index 94% rename from x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_inline_errors_count.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_inline_errors_count.ts index 64b37d371cac5..dc69346e6c9a5 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_inline_errors_count.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_inline_errors_count.ts @@ -11,7 +11,7 @@ import { useEsSearch } from '@kbn/observability-plugin/public'; import { monitorManagementListSelector } from '../../../state/selectors'; import { useUptimeRefreshContext } from '../../../contexts/uptime_refresh_context'; import { getInlineErrorFilters } from './use_inline_errors'; -import { SYNTHETICS_INDEX_PATTERN } from '../../../../common/constants'; +import { SYNTHETICS_INDEX_PATTERN } from '../../../../../common/constants'; export function useInlineErrorsCount() { const monitorList = useSelector(monitorManagementListSelector); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_invalid_monitors.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_invalid_monitors.tsx similarity index 90% rename from x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_invalid_monitors.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_invalid_monitors.tsx index cf63e7c4cd9e6..38e45e9a839d3 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_invalid_monitors.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_invalid_monitors.tsx @@ -8,8 +8,8 @@ import moment from 'moment'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { useFetcher } from '@kbn/observability-plugin/public'; -import { Ping, SyntheticsMonitor } from '../../../../common/runtime_types'; -import { syntheticsMonitorType } from '../../../../common/types/saved_objects'; +import { Ping, SyntheticsMonitor } from '../../../../../common/runtime_types'; +import { syntheticsMonitorType } from '../../../../../common/types/saved_objects'; export const useInvalidMonitors = (errorSummaries?: Ping[]) => { const { savedObjects } = useKibana().services; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_locations.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_locations.test.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_locations.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_locations.test.tsx index 46b8981b74a0f..020dea1209dff 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_locations.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_locations.test.tsx @@ -14,7 +14,7 @@ import { useLocations } from './use_locations'; import * as reactRedux from 'react-redux'; import { getServiceLocations } from '../../../state/actions'; -import { DEFAULT_THROTTLING } from '../../../../common/runtime_types'; +import { DEFAULT_THROTTLING } from '../../../../../common/runtime_types'; describe('useExpViewTimeRange', function () { const dispatch = jest.fn(); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_locations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_locations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_locations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_locations.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_monitor_list.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_monitor_list.ts new file mode 100644 index 0000000000000..e0899571f38b8 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_monitor_list.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect, useReducer, Reducer } from 'react'; +import { useDispatch } from 'react-redux'; +import { useParams } from 'react-router-dom'; +import { getMonitors } from '../../../state/actions'; +import { ConfigKey } from '../../../../../common/constants/monitor_management'; +import { MonitorManagementListPageState } from '../monitor_list/monitor_list'; + +export function useMonitorList() { + const dispatch = useDispatch(); + + const [pageState, dispatchPageAction] = useReducer( + monitorManagementPageReducer, + { + pageIndex: 1, // saved objects page index is base 1 + pageSize: 10, + sortOrder: 'asc', + sortField: `${ConfigKey.NAME}.keyword`, + } + ); + + const { pageIndex, pageSize, sortField, sortOrder } = pageState as MonitorManagementListPageState; + + const { type: viewType } = useParams<{ type: 'all' | 'invalid' }>(); + + useEffect(() => { + if (viewType === 'all') { + dispatch(getMonitors({ page: pageIndex, perPage: pageSize, sortOrder, sortField })); + } + }, [dispatch, pageIndex, pageSize, sortField, sortOrder, viewType, pageState]); + + return { + pageState, + dispatchPageAction, + viewType, + }; +} + +export type MonitorManagementPageAction = + | { + type: 'update'; + payload: MonitorManagementListPageState; + } + | { type: 'refresh' }; + +const monitorManagementPageReducer: Reducer< + MonitorManagementListPageState, + MonitorManagementPageAction +> = (state: MonitorManagementListPageState, action: MonitorManagementPageAction) => { + switch (action.type) { + case 'update': + return { + ...state, + ...action.payload, + }; + case 'refresh': + return { ...state }; + default: + throw new Error(`Action "${(action as MonitorManagementPageAction)?.type}" not recognizable`); + } +}; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_run_once_errors.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_run_once_errors.ts similarity index 97% rename from x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_run_once_errors.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_run_once_errors.ts index 204096f7e0181..916ca8c00b972 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_run_once_errors.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_run_once_errors.ts @@ -7,7 +7,7 @@ import { useEffect, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { Locations, ServiceLocationErrors } from '../../../../common/runtime_types'; +import { Locations, ServiceLocationErrors } from '../../../../../common/runtime_types'; export function useRunOnceErrors({ testRunId, diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_service_allowed.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_service_allowed.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_service_allowed.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/hooks/use_service_allowed.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/loader/loader.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/loader/loader.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/loader/loader.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/loader/loader.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/loader/loader.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/loader/loader.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/loader/loader.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/loader/loader.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/mocks/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/mocks/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/mocks/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/mocks/index.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/mocks/locations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/mocks/locations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/mocks/locations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/mocks/locations.ts diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/locations.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/locations.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/locations.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/locations.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/locations.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/locations.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/locations.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/locations.tsx index fff6e11890f11..029503196b6a3 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/locations.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/locations.tsx @@ -10,7 +10,7 @@ import { useSelector } from 'react-redux'; import { i18n } from '@kbn/i18n'; import { EuiCheckboxGroup, EuiFormRow } from '@elastic/eui'; import { monitorManagementListSelector } from '../../../state/selectors'; -import { MonitorServiceLocations } from '../../../../common/runtime_types'; +import { MonitorServiceLocations } from '../../../../../common/runtime_types'; interface Props { selectedLocations: MonitorServiceLocations; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_advanced_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_advanced_fields.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_advanced_fields.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_advanced_fields.tsx index 086efe36ff113..c4b57f66c227d 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_advanced_fields.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_advanced_fields.tsx @@ -8,8 +8,8 @@ import { EuiFieldText, EuiFormRow, EuiLink, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { memo } from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { ConfigKey } from '../../../../common/runtime_types'; -import type { Validation } from '../../../../common/types'; +import { ConfigKey } from '../../../../../common/runtime_types'; +import type { Validation } from '../../../../../common/types'; import { DescribedFormGroupWithWrap } from '../../fleet_package/common/described_form_group_with_wrap'; import { usePolicyConfigContext } from '../../fleet_package/contexts'; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_config.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_config.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_config.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_config.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_config.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_config.tsx similarity index 94% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_config.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_config.tsx index 8b62b7cde145f..3e85c932bf700 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_config.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_config.tsx @@ -17,7 +17,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { v4 as uuidv4 } from 'uuid'; -import { defaultConfig, usePolicyConfigContext } from '../../fleet_package/contexts'; +import { usePolicyConfigContext } from '../../fleet_package/contexts'; import { usePolicy } from '../../fleet_package/hooks/use_policy'; import { validate } from '../validation'; @@ -25,7 +25,8 @@ import { ActionBarPortal } from '../action_bar/action_bar_portal'; import { useFormatMonitor } from '../hooks/use_format_monitor'; import { MonitorFields } from './monitor_fields'; import { TestNowMode, TestRun } from '../test_now_mode/test_now_mode'; -import { MonitorFields as MonitorFieldsType } from '../../../../common/runtime_types'; +import { MonitorFields as MonitorFieldsType } from '../../../../../common/runtime_types'; +import { DEFAULT_FIELDS } from '../../../../../common/constants/monitor_defaults'; export const MonitorConfig = ({ isEdit = false }: { isEdit: boolean }) => { const { monitorType } = usePolicyConfigContext(); @@ -41,7 +42,7 @@ export const MonitorConfig = ({ isEdit = false }: { isEdit: boolean }) => { monitorType, validate, config: policyConfig[monitorType], - defaultConfig: defaultConfig[monitorType], + defaultConfig: DEFAULT_FIELDS[monitorType], }); const [hasBeenSubmitted, setHasBeenSubmitted] = useState(false); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_fields.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_fields.test.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_fields.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_fields.test.tsx index 7320410ae52eb..b4e97ba724393 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_fields.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_fields.test.tsx @@ -9,7 +9,7 @@ import { fireEvent } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; -import { ConfigKey, DataStream, HTTPFields } from '../../../../common/runtime_types'; +import { ConfigKey, DataStream, HTTPFields } from '../../../../../common/runtime_types'; import { render } from '../../../lib/helper/rtl_helpers'; import { BrowserContextProvider, diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_fields.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_fields.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_fields.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_fields.tsx index 655bb5cecac85..eb109dd5d1063 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_fields.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_fields.tsx @@ -7,7 +7,7 @@ import React, { useMemo, useState } from 'react'; import { EuiForm } from '@elastic/eui'; -import { ConfigKey, DataStream } from '../../../../common/runtime_types'; +import { ConfigKey, DataStream } from '../../../../../common/runtime_types'; import { usePolicyConfigContext } from '../../fleet_package/contexts'; import { CustomFields } from '../../fleet_package/custom_fields'; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_name_location.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_name_location.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_name_location.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_name_location.tsx index e49a9b3e04833..0f7206f65992f 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_name_location.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/monitor_name_location.tsx @@ -9,8 +9,8 @@ import React, { useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiFormRow, EuiFieldText } from '@elastic/eui'; -import { ConfigKey } from '../../../../common/runtime_types'; -import { Validation } from '../../../../common/types'; +import { ConfigKey } from '../../../../../common/runtime_types'; +import { Validation } from '../../../../../common/types'; import { usePolicyConfigContext } from '../../fleet_package/contexts'; import { ServiceLocations } from './locations'; import { useMonitorName } from './use_monitor_name'; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/use_monitor_name.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/use_monitor_name.test.tsx similarity index 87% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/use_monitor_name.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/use_monitor_name.test.tsx index ccfc1312cbf25..04879cd0da65b 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/use_monitor_name.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/use_monitor_name.test.tsx @@ -9,7 +9,16 @@ import { defaultCore, WrappedHelper } from '../../../lib/helper/rtl_helpers'; import { renderHook } from '@testing-library/react-hooks'; import { useMonitorName } from './use_monitor_name'; -import * as hooks from '../../../hooks/use_monitor'; +import * as reactRouter from 'react-router-dom'; + +const mockRouter = { + ...reactRouter, +}; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: jest.fn().mockReturnValue({}), +})); describe('useMonitorName', () => { it('returns expected results', () => { @@ -56,7 +65,7 @@ describe('useMonitorName', () => { }, }); - jest.spyOn(hooks, 'useMonitorId').mockReturnValue('test-id'); + jest.spyOn(mockRouter, 'useParams').mockReturnValue({ monitorId: 'test-id' }); const { result, waitForNextUpdate } = renderHook(() => useMonitorName({ search: 'Test' }), { wrapper: WrappedHelper, diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/use_monitor_name.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/use_monitor_name.ts similarity index 90% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/use_monitor_name.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/use_monitor_name.ts index 200a9599b405e..e8d3848856a2b 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/use_monitor_name.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_config/use_monitor_name.ts @@ -8,8 +8,8 @@ import { useEffect, useState } from 'react'; import { useFetcher } from '@kbn/observability-plugin/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { syntheticsMonitorType } from '../../../../common/types/saved_objects'; -import { useMonitorId } from '../../../hooks'; +import { useParams } from 'react-router-dom'; +import { syntheticsMonitorType } from '../../../../../common/types/saved_objects'; interface AggsResponse { monitorNames: { @@ -22,7 +22,7 @@ interface AggsResponse { export const useMonitorName = ({ search = '' }: { search?: string }) => { const [values, setValues] = useState([]); - const monitorId = useMonitorId(); + const { monitorId } = useParams<{ monitorId: string }>(); const { savedObjects } = useKibana().services; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/actions.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/actions.test.tsx similarity index 94% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/actions.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/actions.test.tsx index f60d54e9cb4f6..af2e54503d0b1 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/actions.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/actions.test.tsx @@ -19,7 +19,7 @@ describe('', () => { expect(screen.getByLabelText('Edit monitor')).toHaveAttribute( 'href', - '/app/uptime/edit-monitor/dGVzdC1pZA==' + '/app/uptime/edit-monitor/test-id' ); }); }); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/actions.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/actions.tsx similarity index 91% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/actions.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/actions.tsx index c290e58dc6225..ddd0d0cc0a63e 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/actions.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/actions.tsx @@ -12,7 +12,7 @@ import moment from 'moment'; import { UptimeSettingsContext } from '../../../contexts'; import { DeleteMonitor } from './delete_monitor'; import { InlineError } from './inline_error'; -import { MonitorManagementListResult, Ping } from '../../../../common/runtime_types'; +import { MonitorManagementListResult, Ping } from '../../../../../common/runtime_types'; interface Props { id: string; @@ -45,7 +45,7 @@ export const Actions = ({ id, name, onUpdate, isDisabled, errorSummaries, monito diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/all_monitors.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/all_monitors.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/all_monitors.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/all_monitors.tsx index 550d3b487a4ae..ac6fab66bbb1e 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/all_monitors.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/all_monitors.tsx @@ -10,7 +10,7 @@ import { useSelector } from 'react-redux'; import { MonitorManagementList, MonitorManagementListPageState } from './monitor_list'; import { monitorManagementListSelector } from '../../../state/selectors'; import { MonitorManagementList as MonitorManagementListState } from '../../../state/reducers/monitor_management'; -import { Ping } from '../../../../common/runtime_types'; +import { Ping } from '../../../../../common/runtime_types'; interface Props { pageState: MonitorManagementListPageState; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/delete_monitor.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/delete_monitor.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/delete_monitor.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/delete_monitor.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/delete_monitor.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/delete_monitor.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/delete_monitor.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/delete_monitor.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/enablement_empty_state.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/enablement_empty_state.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/enablement_empty_state.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/enablement_empty_state.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/inline_error.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/inline_error.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/inline_error.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/inline_error.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/inline_error.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/inline_error.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/inline_error.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/inline_error.tsx index d9f47802e6c28..7ba74def660c9 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/inline_error.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/inline_error.tsx @@ -8,7 +8,7 @@ import React, { useState } from 'react'; import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { Ping } from '../../../../common/runtime_types'; +import { Ping } from '../../../../../common/runtime_types'; import { StdErrorPopover } from './stderr_logs_popover'; export const InlineError = ({ errorSummary }: { errorSummary: Ping }) => { diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/invalid_monitors.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/invalid_monitors.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/invalid_monitors.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/invalid_monitors.tsx index 342b9c6547b1b..5dea7b7ee06fd 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/invalid_monitors.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/invalid_monitors.tsx @@ -13,7 +13,7 @@ import { MonitorManagementListResult, Ping, DEFAULT_THROTTLING, -} from '../../../../common/runtime_types'; +} from '../../../../../common/runtime_types'; interface Props { loading: boolean; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/list_tabs.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/list_tabs.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/list_tabs.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/list_tabs.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/list_tabs.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/list_tabs.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/list_tabs.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/list_tabs.tsx index 15012751af3e5..e2da72effa560 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/list_tabs.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/list_tabs.tsx @@ -18,7 +18,7 @@ import React, { useState, Fragment, useEffect } from 'react'; import { useHistory, useParams } from 'react-router-dom'; import { useUptimeRefreshContext } from '../../../contexts/uptime_refresh_context'; import { MonitorManagementListPageState } from './monitor_list'; -import { ConfigKey } from '../../../../common/runtime_types'; +import { ConfigKey } from '../../../../../common/runtime_types'; export const MonitorListTabs = ({ invalidTotal, diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_async_error.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_async_error.test.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_async_error.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_async_error.test.tsx index da89876cc9b0b..2592aabf9eb09 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_async_error.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_async_error.test.tsx @@ -7,7 +7,7 @@ import { screen } from '@testing-library/react'; import React from 'react'; -import { DEFAULT_THROTTLING } from '../../../../common/runtime_types'; +import { DEFAULT_THROTTLING } from '../../../../../common/runtime_types'; import { render } from '../../../lib/helper/rtl_helpers'; import { MonitorManagementList as MonitorManagementListState } from '../../../state/reducers/monitor_management'; import { MonitorAsyncError } from './monitor_async_error'; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_async_error.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_async_error.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_async_error.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_async_error.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_enabled.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_enabled.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_enabled.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_enabled.test.tsx index d7e04f0e6140f..9d30d985f09d9 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_enabled.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_enabled.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { ConfigKey, DataStream, SyntheticsMonitor } from '../../../../common/runtime_types'; +import { ConfigKey, DataStream, SyntheticsMonitor } from '../../../../../common/runtime_types'; import { render } from '../../../lib/helper/rtl_helpers'; import { FETCH_STATUS } from '@kbn/observability-plugin/public'; import { spyOnUseFetcher } from '../../../lib/helper/spy_use_fetcher'; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_enabled.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_enabled.tsx similarity index 99% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_enabled.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_enabled.tsx index 6bbad58767b20..4b00f96077548 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_enabled.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_enabled.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import React, { useEffect, useState } from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { FETCH_STATUS, useFetcher } from '@kbn/observability-plugin/public'; -import { ConfigKey, EncryptedSyntheticsMonitor } from '../../../../common/runtime_types'; +import { ConfigKey, EncryptedSyntheticsMonitor } from '../../../../../common/runtime_types'; import { setMonitor } from '../../../state/api'; interface Props { diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_list.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_list.test.tsx new file mode 100644 index 0000000000000..5c13b34366d59 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_list.test.tsx @@ -0,0 +1,131 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; +import { + ConfigKey, + DataStream, + HTTPFields, + ScheduleUnit, + DEFAULT_THROTTLING, +} from '../../../../../common/runtime_types'; +import { render } from '../../../lib/helper/rtl_helpers'; +import { MonitorManagementList as MonitorManagementListState } from '../../../state/reducers/monitor_management'; +import { MonitorManagementList, MonitorManagementListPageState } from './monitor_list'; + +describe('', () => { + const onUpdate = jest.fn(); + const onPageStateChange = jest.fn(); + const monitors = []; + for (let i = 0; i < 12; i++) { + monitors.push({ + id: `test-monitor-id-${i}`, + updated_at: '123', + attributes: { + name: `test-monitor-${i}`, + enabled: true, + schedule: { + unit: ScheduleUnit.MINUTES, + number: `${i * 10}`, + }, + urls: `https://test-${i}.co`, + type: DataStream.HTTP, + tags: [`tag-${i}`], + } as HTTPFields, + }); + } + const state = { + monitorManagementList: { + throttling: DEFAULT_THROTTLING, + list: { + perPage: 5, + page: 1, + total: 6, + monitors, + syncErrors: null, + }, + locations: [], + enablement: null, + error: { + serviceLocations: null, + monitorList: null, + enablement: null, + }, + loading: { + monitorList: true, + serviceLocations: false, + enablement: false, + }, + syntheticsService: { + loading: false, + signupUrl: null, + }, + } as MonitorManagementListState, + }; + + const pageState: MonitorManagementListPageState = { + pageIndex: 1, + pageSize: 10, + sortField: `${ConfigKey.NAME}.keyword`, + sortOrder: 'asc', + }; + + it.each(monitors)('navigates to edit monitor flow on edit pencil', (monitor) => { + render( + , + { state } + ); + + expect(screen.getByText(monitor.attributes.name)).toBeInTheDocument(); + expect(screen.getByText(monitor.attributes.urls)).toBeInTheDocument(); + monitor.attributes.tags.forEach((tag) => { + expect(screen.getByText(tag)).toBeInTheDocument(); + }); + expect(screen.getByText(monitor.attributes.schedule.number)).toBeInTheDocument(); + }); + + it('handles changing per page', () => { + render( + , + { state } + ); + + userEvent.click(screen.getByTestId('tablePaginationPopoverButton')); + + userEvent.click(screen.getByText('10 rows')); + + expect(onPageStateChange).toBeCalledWith(expect.objectContaining({ pageSize: 10 })); + }); + + it('handles refreshing and changing page when navigating to the next page', async () => { + const { getByTestId } = render( + , + { state } + ); + + userEvent.click(getByTestId('pagination-button-next')); + + expect(onPageStateChange).toBeCalledWith(expect.objectContaining({ pageIndex: 2 })); + }); +}); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_list.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_list.tsx new file mode 100644 index 0000000000000..4b9374e991e6b --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_list.tsx @@ -0,0 +1,225 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { + Criteria, + EuiBasicTable, + EuiBasicTableColumn, + EuiLink, + EuiPanel, + EuiSpacer, +} from '@elastic/eui'; +import { EuiTableSortingType } from '@elastic/eui/src/components/basic_table/table_types'; +import { i18n } from '@kbn/i18n'; +import React, { useCallback, useContext, useMemo } from 'react'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { + CommonFields, + ConfigKey, + FetchMonitorManagementListQueryArgs, + ICMPSimpleFields, + Ping, + ServiceLocations, + EncryptedSyntheticsMonitorWithId, + TCPSimpleFields, +} from '../../../../../common/runtime_types'; +import { UptimeSettingsContext } from '../../../contexts'; +import { useBreakpoints } from '../../../../hooks/use_breakpoints'; +import { MonitorManagementList as MonitorManagementListState } from '../../../state/reducers/monitor_management'; +import * as labels from '../../overview/monitor_list/translations'; +import { Actions } from './actions'; +import { MonitorEnabled } from './monitor_enabled'; +import { MonitorLocations } from './monitor_locations'; +import { MonitorTags } from './tags'; + +export interface MonitorManagementListPageState { + pageIndex: number; + pageSize: number; + sortField: + | `${ConfigKey.URLS}.keyword` + | `${ConfigKey.NAME}.keyword` + | `${ConfigKey.MONITOR_TYPE}.keyword`; + sortOrder: NonNullable; +} + +interface Props { + pageState: MonitorManagementListPageState; + monitorList: MonitorManagementListState; + onPageStateChange: (state: MonitorManagementListPageState) => void; + onUpdate: () => void; + errorSummaries?: Ping[]; +} + +export const MonitorManagementList = ({ + pageState: { pageIndex, pageSize, sortField, sortOrder }, + monitorList: { + list, + error: { monitorList: error }, + loading: { monitorList: loading }, + }, + onPageStateChange, + onUpdate, + errorSummaries, +}: Props) => { + const { basePath } = useContext(UptimeSettingsContext); + const isXl = useBreakpoints().up('xl'); + + const { total } = list as MonitorManagementListState['list']; + const monitors: EncryptedSyntheticsMonitorWithId[] = useMemo( + () => + list.monitors.map((monitor) => ({ + ...monitor.attributes, + id: monitor.id, + })), + [list.monitors] + ); + + const handleOnChange = useCallback( + ({ + page = { index: 0, size: 10 }, + sort = { field: ConfigKey.NAME, direction: 'asc' }, + }: Criteria) => { + const { index, size } = page; + const { field, direction } = sort; + + onPageStateChange({ + pageIndex: index + 1, // page index for Saved Objects is base 1 + pageSize: size, + sortField: `${field}.keyword` as MonitorManagementListPageState['sortField'], + sortOrder: direction, + }); + }, + [onPageStateChange] + ); + + const pagination = { + pageIndex: pageIndex - 1, // page index for EuiBasicTable is base 0 + pageSize, + totalItemCount: total || 0, + pageSizeOptions: [5, 10, 25, 50, 100], + }; + + const sorting: EuiTableSortingType = { + sort: { + field: sortField.replace('.keyword', '') as keyof EncryptedSyntheticsMonitorWithId, + direction: sortOrder, + }, + }; + + const canEdit: boolean = !!useKibana().services?.application?.capabilities.uptime.save; + + const columns = [ + { + align: 'left' as const, + field: ConfigKey.NAME as string, + name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.monitorName', { + defaultMessage: 'Monitor name', + }), + sortable: true, + render: (name: string, { id }: EncryptedSyntheticsMonitorWithId) => ( + {name} + ), + }, + { + align: 'left' as const, + field: ConfigKey.MONITOR_TYPE, + name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.monitorType', { + defaultMessage: 'Monitor type', + }), + sortable: true, + }, + { + align: 'left' as const, + field: ConfigKey.TAGS, + name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.tags', { + defaultMessage: 'Tags', + }), + render: (tags: string[]) => (tags ? : null), + }, + { + align: 'left' as const, + field: ConfigKey.LOCATIONS, + name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.locations', { + defaultMessage: 'Locations', + }), + render: (locations: ServiceLocations) => + locations ? : null, + }, + { + align: 'left' as const, + field: ConfigKey.SCHEDULE, + name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.schedule', { + defaultMessage: 'Frequency (min)', + }), + render: (schedule: CommonFields[ConfigKey.SCHEDULE]) => schedule?.number, + }, + { + align: 'left' as const, + field: ConfigKey.URLS, + name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.URL', { + defaultMessage: 'URL', + }), + sortable: true, + render: (urls: string, { hosts }: TCPSimpleFields | ICMPSimpleFields) => urls || hosts, + truncateText: true, + textOnly: true, + }, + { + align: 'left' as const, + field: ConfigKey.ENABLED as string, + name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.enabled', { + defaultMessage: 'Enabled', + }), + render: (_enabled: boolean, monitor: EncryptedSyntheticsMonitorWithId) => ( + + ), + }, + { + align: 'left' as const, + name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.actions', { + defaultMessage: 'Actions', + }), + render: (fields: EncryptedSyntheticsMonitorWithId) => ( + + ), + }, + ] as Array>; + + return ( + + + + + ); +}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_list_container.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_list_container.tsx new file mode 100644 index 0000000000000..727f4f6dee72b --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_list_container.tsx @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, Dispatch } from 'react'; +import { useSelector } from 'react-redux'; +import { useParams } from 'react-router-dom'; +import { useTrackPageview } from '@kbn/observability-plugin/public'; +import { monitorManagementListSelector } from '../../../state/selectors'; +import { MonitorAsyncError } from './monitor_async_error'; +import { useInlineErrors } from '../hooks/use_inline_errors'; +import { MonitorListTabs } from './list_tabs'; +import { AllMonitors } from './all_monitors'; +import { InvalidMonitors } from './invalid_monitors'; +import { useInvalidMonitors } from '../hooks/use_invalid_monitors'; +import { MonitorManagementListPageState } from './monitor_list'; +import { MonitorManagementPageAction } from '../hooks/use_monitor_list'; + +export const MonitorListContainer = ({ + isEnabled, + pageState, + dispatchPageAction, +}: { + isEnabled?: boolean; + pageState: MonitorManagementListPageState; + dispatchPageAction: Dispatch; +}) => { + const onPageStateChange = useCallback( + (state) => { + dispatchPageAction({ type: 'update', payload: state }); + }, + [dispatchPageAction] + ); + + const onUpdate = useCallback(() => { + dispatchPageAction({ type: 'refresh' }); + }, [dispatchPageAction]); + + useTrackPageview({ app: 'uptime', path: 'manage-monitors' }); + useTrackPageview({ app: 'uptime', path: 'manage-monitors', delay: 15000 }); + + const monitorList = useSelector(monitorManagementListSelector); + + const { type: viewType } = useParams<{ type: 'all' | 'invalid' }>(); + const { errorSummaries, loading, count } = useInlineErrors({ + onlyInvalidMonitors: viewType === 'invalid', + sortField: pageState.sortField, + sortOrder: pageState.sortOrder, + }); + + const { data: monitorSavedObjects, loading: objectsLoading } = useInvalidMonitors(errorSummaries); + + if (!isEnabled && monitorList.list.total === 0) { + return null; + } + + return ( + <> + + + {viewType === 'all' ? ( + + ) : ( + + )} + + ); +}; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_locations.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_locations.tsx similarity index 94% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_locations.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_locations.tsx index 940e6260d864c..5f9ad96bca464 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_locations.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/monitor_locations.tsx @@ -7,7 +7,7 @@ import React, { useState } from 'react'; import { EuiBadge, EuiBadgeGroup } from '@elastic/eui'; -import { ServiceLocations, ServiceLocation } from '../../../../common/runtime_types'; +import { ServiceLocations, ServiceLocation } from '../../../../../common/runtime_types'; import { EXPAND_LOCATIONS_LABEL } from '../../overview/monitor_list/columns/translations'; import { useLocations } from '../hooks/use_locations'; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/stderr_logs_popover.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/stderr_logs_popover.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/stderr_logs_popover.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/stderr_logs_popover.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/tags.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/tags.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/tags.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/monitor_list/tags.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/show_sync_errors.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/show_sync_errors.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor_management/show_sync_errors.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/show_sync_errors.tsx index 0ce1fdd0d768e..0fde06c764c08 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/show_sync_errors.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/show_sync_errors.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { kibanaService } from '../../state/kibana_service'; -import { ServiceLocationErrors, ServiceLocations } from '../../../common/runtime_types'; +import { ServiceLocationErrors, ServiceLocations } from '../../../../common/runtime_types'; export const showSyncErrors = (errors: ServiceLocationErrors, locations: ServiceLocations) => { Object.values(errors).forEach((location) => { diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/browser/browser_test_results.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/browser/browser_test_results.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/browser/browser_test_results.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/browser/browser_test_results.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/browser/browser_test_results.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/browser/browser_test_results.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/browser/browser_test_results.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/browser/browser_test_results.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/browser/use_browser_run_once_monitors.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/browser/use_browser_run_once_monitors.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/browser/use_browser_run_once_monitors.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/browser/use_browser_run_once_monitors.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/browser/use_browser_run_once_monitors.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/browser/use_browser_run_once_monitors.ts similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/browser/use_browser_run_once_monitors.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/browser/use_browser_run_once_monitors.ts index 68f96b5d7b487..034fad5b9a8d0 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/browser/use_browser_run_once_monitors.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/browser/use_browser_run_once_monitors.ts @@ -6,11 +6,11 @@ */ import { useEffect, useState, useRef } from 'react'; import { createEsParams, useEsSearch, useFetcher } from '@kbn/observability-plugin/public'; -import { JourneyStep } from '../../../../../common/runtime_types'; +import { JourneyStep } from '../../../../../../common/runtime_types'; import { useTickTick } from '../use_tick_tick'; import { fetchJourneySteps } from '../../../../state/api/journey'; import { isStepEnd } from '../../../synthetics/check_steps/steps_list'; -import { SYNTHETICS_INDEX_PATTERN } from '../../../../../common/constants'; +import { SYNTHETICS_INDEX_PATTERN } from '../../../../../../common/constants'; export interface CheckGroupResult { checkGroupId: string; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/simple/simple_test_results.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/simple/simple_test_results.test.tsx similarity index 99% rename from x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/simple/simple_test_results.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/simple/simple_test_results.test.tsx index 6ef4daf9fbdf3..c0bab63b1c11d 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/simple/simple_test_results.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/simple/simple_test_results.test.tsx @@ -11,7 +11,7 @@ import { render } from '../../../../lib/helper/rtl_helpers'; import { SimpleTestResults } from './simple_test_results'; import { kibanaService } from '../../../../state/kibana_service'; import * as runOnceHooks from './use_simple_run_once_monitors'; -import { Ping } from '../../../../../common/runtime_types'; +import { Ping } from '../../../../../../common/runtime_types'; describe('SimpleTestResults', function () { const onDone = jest.fn(); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/simple/simple_test_results.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/simple/simple_test_results.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/simple/simple_test_results.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/simple/simple_test_results.tsx index b1ac899a8951e..580c936272b59 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/simple/simple_test_results.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/simple/simple_test_results.tsx @@ -6,7 +6,7 @@ */ import React, { useEffect, useState } from 'react'; import { useSimpleRunOnceMonitors } from './use_simple_run_once_monitors'; -import { Ping } from '../../../../../common/runtime_types'; +import { Ping } from '../../../../../../common/runtime_types'; import { PingListTable } from '../../../monitor/ping_list/ping_list_table'; import { TestResultHeader } from '../test_result_header'; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/simple/use_simple_run_once_monitors.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/simple/use_simple_run_once_monitors.ts similarity index 94% rename from x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/simple/use_simple_run_once_monitors.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/simple/use_simple_run_once_monitors.ts index 3ca3691aef22c..f0282aaad47a2 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/simple/use_simple_run_once_monitors.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/simple/use_simple_run_once_monitors.ts @@ -7,9 +7,9 @@ import { useMemo, useRef } from 'react'; import { createEsParams, useEsSearch } from '@kbn/observability-plugin/public'; -import { Ping } from '../../../../../common/runtime_types'; +import { Ping } from '../../../../../../common/runtime_types'; import { useTickTick } from '../use_tick_tick'; -import { SYNTHETICS_INDEX_PATTERN } from '../../../../../common/constants'; +import { SYNTHETICS_INDEX_PATTERN } from '../../../../../../common/constants'; export const useSimpleRunOnceMonitors = ({ configId, diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_now_mode.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/test_now_mode.test.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_now_mode.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/test_now_mode.test.tsx index da06b41d16530..b040f7a27da33 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_now_mode.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/test_now_mode.test.tsx @@ -10,7 +10,7 @@ import { screen } from '@testing-library/react'; import { render } from '../../../lib/helper/rtl_helpers'; import { TestNowMode } from './test_now_mode'; import { kibanaService } from '../../../state/kibana_service'; -import { Locations, MonitorFields } from '../../../../common/runtime_types'; +import { Locations, MonitorFields } from '../../../../../common/runtime_types'; import * as runOnceErrorHooks from '../hooks/use_run_once_errors'; describe('TestNowMode', function () { diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_now_mode.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/test_now_mode.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_now_mode.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/test_now_mode.tsx index e89298c9aa46b..25a2f7d02a5fe 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_now_mode.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/test_now_mode.tsx @@ -18,7 +18,11 @@ import { import { useFetcher } from '@kbn/observability-plugin/public'; import { useRunOnceErrors } from '../hooks/use_run_once_errors'; import { TestRunResult } from './test_run_results'; -import { Locations, MonitorFields, ServiceLocationErrors } from '../../../../common/runtime_types'; +import { + Locations, + MonitorFields, + ServiceLocationErrors, +} from '../../../../../common/runtime_types'; import { runOnceMonitor } from '../../../state/api'; import { kibanaService } from '../../../state/kibana_service'; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_result_header.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/test_result_header.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_result_header.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/test_result_header.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_result_header.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/test_result_header.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_result_header.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/test_result_header.tsx index 62c1971d87ed1..4cd4df567882d 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_result_header.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/test_result_header.tsx @@ -17,7 +17,7 @@ import { import { i18n } from '@kbn/i18n'; import * as React from 'react'; import { formatDuration } from '../../monitor/ping_list/ping_list'; -import { JourneyStep, Ping } from '../../../../common/runtime_types'; +import { JourneyStep, Ping } from '../../../../../common/runtime_types'; import { useUptimeSettingsContext } from '../../../contexts/uptime_settings_context'; interface Props { diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_run_results.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/test_run_results.tsx similarity index 93% rename from x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_run_results.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/test_run_results.tsx index b1a6d0aba8cfc..6e9ef292ad6b2 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_run_results.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/test_run_results.tsx @@ -6,7 +6,7 @@ */ import * as React from 'react'; -import { SyntheticsMonitor } from '../../../../common/runtime_types'; +import { SyntheticsMonitor } from '../../../../../common/runtime_types'; import { BrowserTestRunResult } from './browser/browser_test_results'; import { SimpleTestResults } from './simple/simple_test_results'; diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/use_tick_tick.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/use_tick_tick.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/use_tick_tick.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/test_now_mode/use_tick_tick.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/validation.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/validation.test.ts new file mode 100644 index 0000000000000..ce7c08a45dbaf --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/validation.test.ts @@ -0,0 +1,128 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + ConfigKey, + DataStream, + HTTPFields, + BrowserFields, + MonitorFields, + ScheduleUnit, + ServiceLocations, +} from '../../../../common/runtime_types'; +import { validate, validateCommon } from './validation'; + +describe('[Monitor Management] validation', () => { + const commonPropsValid: Partial = { + [ConfigKey.SCHEDULE]: { number: '5', unit: ScheduleUnit.MINUTES }, + [ConfigKey.TIMEOUT]: '3m', + [ConfigKey.LOCATIONS]: [ + { + id: 'test-service-location', + isServiceManaged: true, + url: 'https:test-url.com', + geo: { lat: 33.33432323, lon: 73.23424221 }, + label: 'EU West', + }, + ] as ServiceLocations, + [ConfigKey.NAME]: 'test-name', + [ConfigKey.NAMESPACE]: 'namespace', + }; + + describe('Common monitor fields', () => { + it('should return false for all valid props', () => { + const result = Object.values(validateCommon).map((validator) => { + return validator ? validator(commonPropsValid) : true; + }); + + expect(result.reduce((previous, current) => previous || current)).toBeFalsy(); + }); + + it('should invalidate on invalid namespace', () => { + const validatorFn = validateCommon[ConfigKey.NAMESPACE]; + const result = [undefined, null, '', '*/&<>:', 'A', 'a'.repeat(101)].map((testValue) => + validatorFn?.({ [ConfigKey.NAMESPACE]: testValue } as Partial) + ); + + expect(result.reduce((previous, current) => previous && current)).toBeTruthy(); + }); + }); + + describe('HTTP', () => { + const httpPropsValid: Partial = { + ...commonPropsValid, + [ConfigKey.RESPONSE_STATUS_CHECK]: ['200', '204'], + [ConfigKey.RESPONSE_HEADERS_CHECK]: { 'Content-Type': 'application/json' }, + [ConfigKey.REQUEST_HEADERS_CHECK]: { 'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8' }, + [ConfigKey.MAX_REDIRECTS]: '3', + [ConfigKey.URLS]: 'https:// example-url.com', + }; + + it('should return false for all valid props', () => { + const validators = validate[DataStream.HTTP]; + const keysToValidate = [ + ConfigKey.SCHEDULE, + ConfigKey.TIMEOUT, + ConfigKey.LOCATIONS, + ConfigKey.RESPONSE_STATUS_CHECK, + ConfigKey.RESPONSE_HEADERS_CHECK, + ConfigKey.REQUEST_HEADERS_CHECK, + ConfigKey.MAX_REDIRECTS, + ConfigKey.URLS, + ]; + const validatorFns = keysToValidate.map((key) => validators[key]); + const result = validatorFns.map((fn) => fn?.(httpPropsValid) ?? true); + + expect(result).not.toEqual(expect.arrayContaining([true])); + }); + + it('should invalidate when locations is empty', () => { + const validators = validate[DataStream.HTTP]; + const validatorFn = validators[ConfigKey.LOCATIONS]; + const result = [undefined, null, []].map( + (testValue) => + validatorFn?.({ [ConfigKey.LOCATIONS]: testValue } as Partial) ?? false + ); + + expect(result).toEqual([true, true, true]); + }); + }); + + describe.each([ + [ConfigKey.SOURCE_INLINE, 'step(() => {});'], + [ConfigKey.SOURCE_ZIP_URL, 'https://test.zip'], + ])('Browser', (configKey, value) => { + const browserProps: Partial = { + ...commonPropsValid, + [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, + [ConfigKey.TIMEOUT]: undefined, + [configKey]: value, + }; + + it('should return false for all valid props', () => { + const validators = validate[DataStream.BROWSER]; + const keysToValidate = [ConfigKey.SCHEDULE, ConfigKey.TIMEOUT, configKey]; + const validatorFns = keysToValidate.map((key) => validators[key]); + const result = validatorFns.map((fn) => fn?.(browserProps) ?? true); + + expect(result).not.toEqual(expect.arrayContaining([true])); + }); + + it('should invalidate when locations is empty', () => { + const validators = validate[DataStream.BROWSER]; + const validatorFn = validators[ConfigKey.LOCATIONS]; + const result = [undefined, null, []].map( + (testValue) => + validatorFn?.({ [ConfigKey.LOCATIONS]: testValue } as Partial) ?? false + ); + + expect(result).toEqual([true, true, true]); + }); + }); + + // TODO: Add test for other monitor types if needed +}); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/validation.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/validation.ts similarity index 98% rename from x-pack/plugins/synthetics/public/components/monitor_management/validation.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/validation.ts index 78b0b8ba82b68..0975d00b38098 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/validation.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor_management/validation.ts @@ -11,8 +11,8 @@ import { ScheduleUnit, MonitorFields, isServiceLocationInvalid, -} from '../../../common/runtime_types'; -import { Validation } from '../../../common/types'; +} from '../../../../common/runtime_types'; +import { Validation } from '../../../../common/types'; export const digitsOnly = /^[0-9]*$/g; export const includesValidPort = /[^\:]+:[0-9]{1,5}$/g; diff --git a/x-pack/plugins/synthetics/public/components/overview/__snapshots__/snapshot_heading.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/__snapshots__/snapshot_heading.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/__snapshots__/snapshot_heading.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/__snapshots__/snapshot_heading.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/alert_expression_popover.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alert_expression_popover.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/alert_expression_popover.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alert_expression_popover.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/alert_field_number.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alert_field_number.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/alert_field_number.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alert_field_number.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/alert_field_number.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alert_field_number.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/alert_field_number.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alert_field_number.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/alert_query_bar/query_bar.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alert_query_bar/query_bar.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/alert_query_bar/query_bar.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alert_query_bar/query_bar.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/alert_tls.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alert_tls.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/alert_tls.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alert_tls.tsx diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alerts_containers/alert_monitor_status.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alerts_containers/alert_monitor_status.tsx new file mode 100644 index 0000000000000..54a8010e30a1c --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alerts_containers/alert_monitor_status.tsx @@ -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 React, { useEffect } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import { isRight } from 'fp-ts/lib/Either'; +import { selectedFiltersSelector } from '../../../../state/selectors'; +import { AlertMonitorStatusComponent } from '../monitor_status_alert/alert_monitor_status'; +import { setSearchTextAction } from '../../../../state/actions'; +import { + AtomicStatusCheckParamsType, + GetMonitorAvailabilityParamsType, +} from '../../../../../../common/runtime_types'; + +import { useSnapShotCount } from './use_snap_shot'; +import { FILTER_FIELDS } from '../../../../../../common/constants'; + +const { TYPE, TAGS, LOCATION, PORT } = FILTER_FIELDS; + +interface Props { + ruleParams: { [key: string]: any }; + enabled: boolean; + numTimes: number; + setRuleParams: (key: string, value: any) => void; + timerange: { + from: string; + to: string; + }; +} + +export const AlertMonitorStatus: React.FC = ({ + enabled, + numTimes, + setRuleParams, + timerange, + ruleParams, +}) => { + const dispatch = useDispatch(); + + useEffect(() => { + if (ruleParams.search) { + dispatch(setSearchTextAction(ruleParams.search)); + } + }, [ruleParams, dispatch]); + + const { count, loading } = useSnapShotCount({ + query: ruleParams.search, + filters: ruleParams.filters, + }); + + const isOldAlert = React.useMemo( + () => + Object.entries(ruleParams).length > 0 && + !isRight(AtomicStatusCheckParamsType.decode(ruleParams)) && + !isRight(GetMonitorAvailabilityParamsType.decode(ruleParams)), + [ruleParams] + ); + + const selectedFilters = useSelector(selectedFiltersSelector); + useEffect(() => { + if (!ruleParams.filters && selectedFilters !== null) { + setRuleParams('filters', { + [PORT]: selectedFilters?.ports ?? [], + [LOCATION]: selectedFilters?.locations ?? [], + [TYPE]: selectedFilters?.schemes ?? [], + [TAGS]: selectedFilters?.tags ?? [], + }); + } + }, [ruleParams, setRuleParams, selectedFilters]); + + return ( + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { AlertMonitorStatus as default }; diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/alerts_containers/alert_tls.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alerts_containers/alert_tls.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/alerts_containers/alert_tls.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alerts_containers/alert_tls.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/alerts_containers/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alerts_containers/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/alerts_containers/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alerts_containers/index.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/alerts_containers/use_snap_shot.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alerts_containers/use_snap_shot.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/alerts_containers/use_snap_shot.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/alerts_containers/use_snap_shot.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/anomaly_alert/anomaly_alert.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/anomaly_alert/anomaly_alert.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/anomaly_alert/anomaly_alert.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/anomaly_alert/anomaly_alert.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/anomaly_alert/select_severity.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/anomaly_alert/select_severity.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/anomaly_alert/select_severity.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/anomaly_alert/select_severity.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/anomaly_alert/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/anomaly_alert/translations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/anomaly_alert/translations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/anomaly_alert/translations.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/index.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/__snapshots__/down_number_select.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/__snapshots__/down_number_select.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/__snapshots__/down_number_select.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/__snapshots__/down_number_select.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/__snapshots__/time_expression_select.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/__snapshots__/time_expression_select.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/__snapshots__/time_expression_select.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/__snapshots__/time_expression_select.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/availability_expression_select.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/availability_expression_select.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/availability_expression_select.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/availability_expression_select.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/down_number_select.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/down_number_select.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/down_number_select.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/down_number_select.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/down_number_select.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/down_number_select.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/down_number_select.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/down_number_select.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/filters_expression_select.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/filters_expression_select.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/filters_expression_select.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/filters_expression_select.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/filters_expression_select.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/filters_expression_select.tsx index 3720179838899..12b28352a503e 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/filters_expression_select.tsx @@ -11,7 +11,7 @@ import { FieldValueSuggestions } from '@kbn/observability-plugin/public'; import { filterLabels } from '../../filter_group/translations'; import { alertFilterLabels, filterAriaLabels } from './translations'; import { useUptimeDataView } from '../../../../contexts/uptime_data_view_context'; -import { FILTER_FIELDS } from '../../../../../common/constants'; +import { FILTER_FIELDS } from '../../../../../../common/constants'; import { useGetUrlParams } from '../../../../hooks'; export interface FilterExpressionsSelectProps { diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/index.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/status_expression_select.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/status_expression_select.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/status_expression_select.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/status_expression_select.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/time_expression_select.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/time_expression_select.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/time_expression_select.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/time_expression_select.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/time_expression_select.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/time_expression_select.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/time_expression_select.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/time_expression_select.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/time_unit_selectable.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/time_unit_selectable.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/time_unit_selectable.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/time_unit_selectable.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/translations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/translations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/translations.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/add_filter_btn.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_status_alert/add_filter_btn.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/add_filter_btn.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_status_alert/add_filter_btn.test.tsx index f8f9e5a84bc5c..a929489e6b715 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/add_filter_btn.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_status_alert/add_filter_btn.test.tsx @@ -40,7 +40,6 @@ describe('AddFilterButton component', () => { panelPaddingSize="none" > { panelPaddingSize="none" > { panelPaddingSize="none" > diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/add_filter_btn.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_status_alert/add_filter_btn.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/add_filter_btn.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_status_alert/add_filter_btn.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_status_alert/alert_monitor_status.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_status_alert/alert_monitor_status.test.tsx diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx new file mode 100644 index 0000000000000..80958e3bdd767 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useEffect, useState } from 'react'; +import { EuiCallOut, EuiSpacer, EuiHorizontalRule, EuiLoadingSpinner } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { FiltersExpressionsSelect, StatusExpressionSelect } from '../monitor_expressions'; +import { AddFilterButton } from './add_filter_btn'; +import { OldAlertCallOut } from './old_alert_call_out'; +import { AvailabilityExpressionSelect } from '../monitor_expressions/availability_expression_select'; +import { AlertQueryBar } from '../alert_query_bar/query_bar'; +import { useGetUrlParams } from '../../../../hooks'; +import { FILTER_FIELDS } from '../../../../../../common/constants'; + +export interface AlertMonitorStatusProps { + ruleParams: { [key: string]: any }; + enabled: boolean; + isOldAlert: boolean; + snapshotCount: number; + snapshotLoading?: boolean; + numTimes: number; + setRuleParams: (key: string, value: any) => void; + timerange: { + from: string; + to: string; + }; +} + +export const hasFilters = (filters?: { [key: string]: string[] }) => { + if (!filters || Object.keys(filters).length === 0) { + return false; + } + + return Object.values(FILTER_FIELDS).some((f) => filters[f].length); +}; + +export const AlertMonitorStatusComponent: React.FC = (props) => { + const { ruleParams, isOldAlert, setRuleParams, snapshotCount, snapshotLoading } = props; + + const alertFilters = ruleParams?.filters ?? {}; + const [newFilters, setNewFilters] = useState( + Object.keys(alertFilters).filter((f) => alertFilters[f].length) + ); + + const { search = '' } = useGetUrlParams(); + + useEffect(() => { + if (search) { + setRuleParams('search', search); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const onSearchChange = useCallback( + (value: string) => { + setRuleParams('search', value); + }, + [setRuleParams] + ); + + return ( + <> + + + + {' '} + {snapshotLoading && } + + } + iconType="iInCircle" + /> + + + + + + + + { + setNewFilters([...newFilters, newFilter]); + }} + /> + + { + if (newFilters.includes(removeFilter)) { + setNewFilters(newFilters.filter((item) => item !== removeFilter)); + } + }} + setRuleParams={setRuleParams} + shouldUpdateUrl={false} + /> + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/old_alert_call_out.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_status_alert/old_alert_call_out.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/old_alert_call_out.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_status_alert/old_alert_call_out.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/old_alert_callout.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_status_alert/old_alert_callout.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/old_alert_callout.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_status_alert/old_alert_callout.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/settings_message_expression_popover.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/settings_message_expression_popover.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/settings_message_expression_popover.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/settings_message_expression_popover.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/toggle_alert_flyout_button.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/toggle_alert_flyout_button.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/toggle_alert_flyout_button.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/toggle_alert_flyout_button.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/toggle_alert_flyout_button.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/toggle_alert_flyout_button.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/overview/alerts/toggle_alert_flyout_button.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/toggle_alert_flyout_button.tsx index 894c3df70a405..86b745c7d227f 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/toggle_alert_flyout_button.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/toggle_alert_flyout_button.tsx @@ -17,8 +17,8 @@ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { CLIENT_ALERT_TYPES } from '../../../../common/constants/alerts'; -import { ClientPluginsStart } from '../../../apps/plugin'; +import { CLIENT_ALERT_TYPES } from '../../../../../common/constants/alerts'; +import { ClientPluginsStart } from '../../../../plugin'; import { ToggleFlyoutTranslations } from './translations'; import { ToggleAlertFlyoutButtonProps } from './alerts_containers'; diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/translations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/translations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/translations.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/empty_state/empty_state_error.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/empty_state/empty_state_error.tsx new file mode 100644 index 0000000000000..3f2150169e2df --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/empty_state/empty_state_error.tsx @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiEmptyPrompt, EuiPanel, EuiTitle, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { Fragment } from 'react'; +import { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public'; + +interface EmptyStateErrorProps { + errors: Array>; +} + +export const EmptyStateError = ({ errors }: EmptyStateErrorProps) => { + const unauthorized = errors.find( + (error) => error.message && error.message.includes('unauthorized') + ); + + return ( + + + + + {unauthorized ? ( +

+ {i18n.translate('xpack.synthetics.emptyStateError.notAuthorized', { + defaultMessage: + 'You are not authorized to view Uptime data, please contact your system administrator.', + })} +

+ ) : ( +

+ {i18n.translate('xpack.synthetics.emptyStateError.title', { + defaultMessage: 'Error', + })} +

+ )} + + } + body={ + + {!unauthorized && + errors.map((error) => ( +

+ {error.body?.message || error.message} +

+ ))} +
+ } + /> +
+
+
+ ); +}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/empty_state/empty_state_loading.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/empty_state/empty_state_loading.tsx new file mode 100644 index 0000000000000..0f71c9bafa962 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/empty_state/empty_state_loading.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 { EuiEmptyPrompt, EuiLoadingSpinner, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { Fragment } from 'react'; + +export const EmptyStateLoading = () => ( + + + + +

+ {i18n.translate('xpack.synthetics.emptyState.loadingMessage', { + defaultMessage: 'Loading…', + })} +

+
+ + } + /> +); diff --git a/x-pack/plugins/synthetics/public/components/overview/empty_state/use_has_data.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/empty_state/use_has_data.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/empty_state/use_has_data.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/empty_state/use_has_data.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/filter_group/filter_group.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/filter_group/filter_group.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/filter_group/filter_group.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/filter_group/filter_group.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/filter_group/filter_group.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/filter_group/filter_group.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/overview/filter_group/filter_group.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/filter_group/filter_group.tsx index 8a0ac4cbe81b1..16ab92a6862c2 100644 --- a/x-pack/plugins/synthetics/public/components/overview/filter_group/filter_group.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/filter_group/filter_group.tsx @@ -15,7 +15,7 @@ import { useSelectedFilters } from '../../../hooks/use_selected_filters'; import { SelectedFilters } from './selected_filters'; import { useUptimeDataView } from '../../../contexts/uptime_data_view_context'; import { useGetUrlParams } from '../../../hooks'; -import { EXCLUDE_RUN_ONCE_FILTER } from '../../../../common/constants/client_defaults'; +import { EXCLUDE_RUN_ONCE_FILTER } from '../../../../../common/constants/client_defaults'; const Container = styled(EuiFilterGroup)` margin-bottom: 10px; diff --git a/x-pack/plugins/synthetics/public/components/overview/filter_group/selected_filters.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/filter_group/selected_filters.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/filter_group/selected_filters.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/filter_group/selected_filters.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/filter_group/translations.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/filter_group/translations.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/filter_group/translations.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/filter_group/translations.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/index.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/__snapshots__/filter_status_button.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/__snapshots__/filter_status_button.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/__snapshots__/filter_status_button.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/__snapshots__/filter_status_button.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/__snapshots__/status_filter.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/__snapshots__/status_filter.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/__snapshots__/status_filter.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/__snapshots__/status_filter.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/cert_status_column.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/cert_status_column.tsx similarity index 93% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/cert_status_column.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/cert_status_column.tsx index 790342b593398..5d24b7d969984 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/cert_status_column.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/cert_status_column.tsx @@ -9,10 +9,10 @@ import React from 'react'; import moment from 'moment'; import styled from 'styled-components'; import { EuiIcon, EuiText, EuiToolTip } from '@elastic/eui'; -import { X509Expiry } from '../../../../../common/runtime_types'; +import { X509Expiry } from '../../../../../../common/runtime_types'; import { useCertStatus } from '../../../../hooks'; import { EXPIRED, EXPIRES, EXPIRES_SOON } from '../../../certificates/translations'; -import { CERT_STATUS } from '../../../../../common/constants'; +import { CERT_STATUS } from '../../../../../../common/constants'; interface Props { expiry: X509Expiry; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/define_connectors.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/define_connectors.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/define_connectors.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/define_connectors.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/define_connectors.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/define_connectors.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/define_connectors.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/define_connectors.tsx index f5ac702d5060b..5bb676178d9a7 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/define_connectors.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/define_connectors.tsx @@ -9,7 +9,7 @@ import React, { useState } from 'react'; import { EuiSwitch, EuiPopover, EuiText, EuiFormRow } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { ReactRouterEuiLink } from '../../../common/react_router_helpers'; -import { SETTINGS_ROUTE } from '../../../../../common/constants'; +import { SETTINGS_ROUTE } from '../../../../../../common/constants'; import { ENABLE_STATUS_ALERT } from './translations'; interface Props { diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/enable_alert.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/enable_alert.test.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/enable_alert.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/enable_alert.test.tsx index c5dff902cfb14..416dd8e963e9b 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/enable_alert.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/enable_alert.test.tsx @@ -9,8 +9,8 @@ import React from 'react'; import { EnableMonitorAlert } from './enable_alert'; import { fireEvent } from '@testing-library/dom'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../common/constants'; -import { makePing } from '../../../../../common/runtime_types/ping'; +import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../../common/constants'; +import { makePing } from '../../../../../../common/runtime_types/ping'; import { render } from '../../../../lib/helper/rtl_helpers'; import { DISABLE_STATUS_ALERT, ENABLE_STATUS_ALERT } from './translations'; import { mockState } from '../../../../lib/__mocks__/uptime_store.mock'; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/enable_alert.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/enable_alert.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/enable_alert.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/enable_alert.tsx index 3bc23592c4e64..b52aacd5abb6e 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/enable_alert.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/enable_alert.tsx @@ -18,10 +18,10 @@ import { isAlertDeletedSelector, newAlertSelector, } from '../../../../state/alerts/alerts'; -import { MONITOR_ROUTE } from '../../../../../common/constants'; +import { MONITOR_ROUTE } from '../../../../../../common/constants'; import { DefineAlertConnectors } from './define_connectors'; import { DISABLE_STATUS_ALERT, ENABLE_STATUS_ALERT } from './translations'; -import { Ping } from '../../../../../common/runtime_types/ping'; +import { Ping } from '../../../../../../common/runtime_types/ping'; interface Props { monitorId: string; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/monitor_name_col.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/monitor_name_col.tsx similarity index 93% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/monitor_name_col.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/monitor_name_col.tsx index 130ccbd7081c3..0b6cc173acde1 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/monitor_name_col.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/monitor_name_col.tsx @@ -10,8 +10,8 @@ import { i18n } from '@kbn/i18n'; import { EuiButtonEmpty, EuiText } from '@elastic/eui'; import { MonitorPageLink } from '../../../common/monitor_page_link'; import { useGetUrlParams } from '../../../../hooks'; -import { stringifyUrlParams } from '../../../../lib/helper/stringify_url_params'; -import { MonitorSummary } from '../../../../../common/runtime_types/monitor'; +import { stringifyUrlParams } from '../../../../../apps/synthetics/utils/url_params/stringify_url_params'; +import { MonitorSummary } from '../../../../../../common/runtime_types/monitor'; import { useFilterUpdate } from '../../../../hooks/use_filter_update'; interface Props { diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/monitor_status_column.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/monitor_status_column.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/monitor_status_column.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/monitor_status_column.test.tsx index a6c837ca13a7f..a69ebb3d349fd 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/monitor_status_column.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/monitor_status_column.test.tsx @@ -7,8 +7,8 @@ import React from 'react'; import { getLocationStatus, MonitorListStatusColumn } from './monitor_status_column'; -import { Ping } from '../../../../../common/runtime_types'; -import { STATUS } from '../../../../../common/constants'; +import { Ping } from '../../../../../../common/runtime_types'; +import { STATUS } from '../../../../../../common/constants'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { mockDate, mockMoment } from '../../../../lib/helper/test_helpers'; import { render } from '../../../../lib/helper/rtl_helpers'; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/monitor_status_column.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/monitor_status_column.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/monitor_status_column.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/monitor_status_column.tsx index 6fb3a463ebf77..7f64c1199c2b0 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/monitor_status_column.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/monitor_status_column.tsx @@ -23,13 +23,13 @@ import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { kibanaService } from '../../../../state/kibana_service'; import { useRunOnceErrors } from '../../../monitor_management/hooks/use_run_once_errors'; import { parseTimestamp } from '../parse_timestamp'; -import { DataStream, Ping, PingError } from '../../../../../common/runtime_types'; +import { DataStream, Ping, PingError } from '../../../../../../common/runtime_types'; import { STATUS, SHORT_TIMESPAN_LOCALE, UNNAMED_LOCATION, SHORT_TS_LOCALE, -} from '../../../../../common/constants'; +} from '../../../../../../common/constants'; import { STATUS_DOWN_LABEL, STATUS_UP_LABEL } from '../../../common/translations'; import { MonitorProgress } from './progress/monitor_progress'; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/progress/browser_monitor_progress.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/progress/browser_monitor_progress.tsx similarity index 93% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/progress/browser_monitor_progress.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/progress/browser_monitor_progress.tsx index b92916bd7b72b..0692af54b9bf4 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/progress/browser_monitor_progress.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/progress/browser_monitor_progress.tsx @@ -7,8 +7,8 @@ import { EuiBadge, EuiProgress } from '@elastic/eui'; import React, { useEffect, useRef, useState } from 'react'; -import { scheduleToMilli } from '../../../../../../common/lib/schedule_to_time'; -import { SyntheticsMonitorSchedule } from '../../../../../../common/runtime_types'; +import { scheduleToMilli } from '../../../../../../../common/lib/schedule_to_time'; +import { SyntheticsMonitorSchedule } from '../../../../../../../common/runtime_types'; import { useBrowserRunOnceMonitors } from '../../../../monitor_management/test_now_mode/browser/use_browser_run_once_monitors'; import { IN_PROGRESS_LABEL, diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/progress/monitor_progress.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/progress/monitor_progress.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/progress/monitor_progress.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/progress/monitor_progress.tsx index b03d278f09d45..ebc28310930ad 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/progress/monitor_progress.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/progress/monitor_progress.tsx @@ -9,7 +9,7 @@ import React, { useEffect } from 'react'; import { useSelector } from 'react-redux'; import { SimpleMonitorProgress } from './simple_monitor_progress'; import { BrowserMonitorProgress } from './browser_monitor_progress'; -import { DataStream, SyntheticsMonitorSchedule } from '../../../../../../common/runtime_types'; +import { DataStream, SyntheticsMonitorSchedule } from '../../../../../../../common/runtime_types'; import { useUpdatedMonitor } from './use_updated_monitor'; import { refreshedMonitorSelector } from '../../../../../state/reducers/monitor_list'; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/progress/simple_monitor_progress.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/progress/simple_monitor_progress.tsx similarity index 92% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/progress/simple_monitor_progress.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/progress/simple_monitor_progress.tsx index f93bcadd58519..bbc96eeda397b 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/progress/simple_monitor_progress.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/progress/simple_monitor_progress.tsx @@ -7,8 +7,8 @@ import { EuiBadge, EuiProgress } from '@elastic/eui'; import React, { useEffect, useRef, useState } from 'react'; -import { scheduleToMilli } from '../../../../../../common/lib/schedule_to_time'; -import { SyntheticsMonitorSchedule } from '../../../../../../common/runtime_types'; +import { scheduleToMilli } from '../../../../../../../common/lib/schedule_to_time'; +import { SyntheticsMonitorSchedule } from '../../../../../../../common/runtime_types'; import { useSimpleRunOnceMonitors } from '../../../../monitor_management/test_now_mode/simple/use_simple_run_once_monitors'; import { IN_PROGRESS_LABEL } from '../../../../monitor_management/test_now_mode/test_result_header'; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/progress/use_updated_monitor.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/progress/use_updated_monitor.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/progress/use_updated_monitor.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/progress/use_updated_monitor.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/status_badge.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/status_badge.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/status_badge.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/status_badge.test.tsx diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/status_badge.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/status_badge.tsx new file mode 100644 index 0000000000000..0328242ffc036 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/status_badge.tsx @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiBadge, EuiToolTip } from '@elastic/eui'; +import React, { useContext, useState } from 'react'; +import { STATUS } from '../../../../../../common/constants'; +import { getHealthMessage } from './monitor_status_column'; +import { UptimeThemeContext } from '../../../../contexts'; +import { PingError } from '../../../../../../common/runtime_types'; +import { getInlineErrorLabel } from '../../../monitor_management/monitor_list/inline_error'; +import { StdErrorPopover } from '../../../monitor_management/monitor_list/stderr_logs_popover'; + +export const StatusBadge = ({ + status, + checkGroup, + summaryError, + monitorType, +}: { + status: string; + monitorType: string; + checkGroup?: string; + summaryError?: PingError; +}) => { + const { + colors: { dangerBehindText }, + } = useContext(UptimeThemeContext); + const [isOpen, setIsOpen] = useState(false); + + if (status === STATUS.UP) { + return ( + + {getHealthMessage(status)} + + ); + } + + const errorMessage = + monitorType !== 'browser' ? summaryError?.message : getInlineErrorLabel(summaryError?.message); + + const button = ( + + setIsOpen(true)} + onClickAriaLabel={errorMessage} + > + {getHealthMessage(status)} + + + ); + + if (monitorType !== 'browser') { + return button; + } + + return ( + + ); +}; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/test_now_col.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/test_now_col.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/test_now_col.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/test_now_col.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/translations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/columns/translations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/columns/translations.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/filter_status_button.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/filter_status_button.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/filter_status_button.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/filter_status_button.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/filter_status_button.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/filter_status_button.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/filter_status_button.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/filter_status_button.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/index.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list.test.tsx new file mode 100644 index 0000000000000..d0026b1c5f0ba --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list.test.tsx @@ -0,0 +1,297 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { + MonitorSummariesResult, + CursorDirection, + SortOrder, + makePing, + Ping, + MonitorSummary, +} from '../../../../../common/runtime_types'; +import { MonitorListComponent } from './monitor_list'; +import moment from 'moment'; +import { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public'; +import { mockMoment } from '../../../lib/helper/test_helpers'; +import { render } from '../../../lib/helper/rtl_helpers'; +import { NO_DATA_MESSAGE } from './translations'; + +const testFooPings: Ping[] = [ + makePing({ + docId: 'foo1', + id: 'foo', + type: 'icmp', + status: 'up', + duration: 123, + timestamp: '124', + ip: '127.0.0.1', + }), + makePing({ + docId: 'foo2', + id: 'foo', + type: 'icmp', + status: 'up', + duration: 123, + timestamp: '125', + ip: '127.0.0.2', + }), + makePing({ + docId: 'foo3', + id: 'foo', + type: 'icmp', + status: 'down', + duration: 123, + timestamp: '126', + ip: '127.0.0.3', + }), +]; + +const testFooSummary: MonitorSummary = { + monitor_id: 'foo', + state: { + monitor: { type: 'http', duration: { us: 1000 } }, + summaryPings: testFooPings, + summary: { + up: 1, + down: 2, + }, + timestamp: '123', + url: {}, + }, +}; + +const testBarPings: Ping[] = [ + makePing({ + docId: 'bar1', + id: 'bar', + type: 'icmp', + status: 'down', + duration: 123, + timestamp: '125', + ip: '127.0.0.1', + }), + makePing({ + docId: 'bar2', + id: 'bar', + type: 'icmp', + status: 'down', + duration: 123, + timestamp: '126', + ip: '127.0.0.1', + }), +]; + +const testBarSummary: MonitorSummary = { + monitor_id: 'bar', + state: { + monitor: { type: 'http', duration: { us: 1000 } }, + summaryPings: testBarPings, + summary: { + up: 2, + down: 0, + }, + timestamp: '125', + url: {}, + }, +}; + +describe('MonitorList component', () => { + let localStorageMock: any; + + beforeAll(() => { + mockMoment(); + global.innerWidth = 1200; + + // Trigger the window resize event. + global.dispatchEvent(new Event('resize')); + }); + + const getMonitorList = (timestamp?: string): MonitorSummariesResult => { + if (timestamp) { + testBarSummary.state.timestamp = timestamp; + testFooSummary.state.timestamp = timestamp; + } else { + testBarSummary.state.timestamp = '125'; + testFooSummary.state.timestamp = '123'; + } + return { + nextPagePagination: null, + prevPagePagination: null, + summaries: [testFooSummary, testBarSummary], + }; + }; + + beforeEach(() => { + localStorageMock = { + getItem: jest.fn().mockImplementation(() => '25'), + setItem: jest.fn(), + }; + + global.localStorage = localStorageMock; + }); + + it('renders a no items message when no data is provided', async () => { + const { findByText } = render( + + ); + expect(await findByText(NO_DATA_MESSAGE)).toBeInTheDocument(); + }); + + it('renders the monitor list', async () => { + const { findByLabelText } = render( + + ); + + expect( + await findByLabelText( + 'Monitor Status table with columns for Status, Name, URL, IP, Downtime History and Integrations. The table is currently displaying 2 items.' + ) + ).toBeInTheDocument(); + }); + + it('renders error list', async () => { + const { findByText } = render( + , + loading: false, + }} + pageSize={10} + setPageSize={jest.fn()} + refreshedMonitorIds={[]} + /> + ); + + expect(await findByText('foo message')).toBeInTheDocument(); + }); + + describe('MonitorListPagination component', () => { + let paginationResult: MonitorSummariesResult; + + beforeEach(() => { + paginationResult = { + prevPagePagination: JSON.stringify({ + cursorKey: { monitor_id: 123 }, + cursorDirection: CursorDirection.BEFORE, + sortOrder: SortOrder.ASC, + }), + nextPagePagination: JSON.stringify({ + cursorKey: { monitor_id: 456 }, + cursorDirection: CursorDirection.AFTER, + sortOrder: SortOrder.ASC, + }), + summaries: [testFooSummary, testBarSummary], + }; + }); + + it('renders the pagination', async () => { + const { findByText, findByLabelText } = render( + + ); + + expect(await findByText('Rows per page: 10')).toBeInTheDocument(); + expect(await findByLabelText('Prev page of results')).toBeInTheDocument(); + expect(await findByLabelText('Next page of results')).toBeInTheDocument(); + }); + }); + + describe('responsive behavior', () => { + describe('xl screens', () => { + beforeAll(() => { + global.innerWidth = 1200; + + // Trigger the window resize event. + global.dispatchEvent(new Event('resize')); + }); + + it('shows ping histogram and expand button on xl and xxl screens', async () => { + const list = getMonitorList(moment().subtract(5, 'minute').toISOString()); + const { getByTestId, getByText } = render( + + ); + + await waitFor(() => { + expect( + getByTestId( + `xpack.synthetics.monitorList.${list.summaries[0].monitor_id}.expandMonitorDetail` + ) + ).toBeInTheDocument(); + expect(getByText('Downtime history')).toBeInTheDocument(); + }); + }); + }); + + describe('large and medium screens', () => { + it('hides ping histogram and expand button on extra large screens', async () => { + global.innerWidth = 1199; + + // Trigger the window resize event. + global.dispatchEvent(new Event('resize')); + + const { queryByTestId, queryByText } = render( + + ); + + await waitFor(() => { + expect( + queryByTestId('xpack.synthetics.monitorList.always-down.expandMonitorDetail') + ).not.toBeInTheDocument(); + expect(queryByText('Downtime history')).not.toBeInTheDocument(); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list.tsx new file mode 100644 index 0000000000000..c5a38067965c3 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list.tsx @@ -0,0 +1,302 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 useWindowSize from 'react-use/lib/useWindowSize'; +import useDebounce from 'react-use/lib/useDebounce'; +import { + EuiButtonIcon, + EuiBasicTable, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiPanel, + EuiSpacer, + getBreakpoint, +} from '@elastic/eui'; +import { euiStyled } from '@kbn/kibana-react-plugin/common'; +import { X509Expiry } from '../../../../../common/runtime_types'; +import { MonitorSummary } from '../../../../../common/runtime_types'; +import { MonitorListStatusColumn } from './columns/monitor_status_column'; +import { ExpandedRowMap } from './types'; +import { MonitorBarSeries } from '../../common/charts'; +import { OverviewPageLink } from './overview_page_link'; +import * as labels from './translations'; +import { MonitorListPageSizeSelect } from './monitor_list_page_size_select'; +import { MonitorListDrawer } from './monitor_list_drawer/list_drawer_container'; +import { MonitorListProps } from './monitor_list_container'; +import { MonitorList } from '../../../state/reducers/monitor_list'; +import { CertStatusColumn } from './columns/cert_status_column'; +import { MonitorListHeader } from './monitor_list_header'; +import { TAGS_LABEL, URL_LABEL } from '../../common/translations'; +import { EnableMonitorAlert } from './columns/enable_alert'; +import { STATUS_ALERT_COLUMN, TEST_NOW_COLUMN } from './translations'; +import { MonitorNameColumn } from './columns/monitor_name_col'; +import { MonitorTags } from '../../common/monitor_tags'; +import { useMonitorHistogram } from './use_monitor_histogram'; +import { TestNowColumn } from './columns/test_now_col'; +import { NoItemsMessage } from './no_items_message'; + +interface Props extends MonitorListProps { + pageSize: number; + setPageSize: (val: number) => void; + monitorList: MonitorList; + refreshedMonitorIds: string[]; +} + +export const MonitorListComponent: ({ + filters, + monitorList: { list, error, loading }, + pageSize, + refreshedMonitorIds, + setPageSize, +}: Props) => any = ({ + filters, + refreshedMonitorIds = [], + monitorList: { list, error, loading }, + pageSize, + setPageSize, +}) => { + const [expandedDrawerIds, updateExpandedDrawerIds] = useState([]); + const { width } = useWindowSize(); + const [hideExtraColumns, setHideExtraColumns] = useState(false); + + useDebounce( + () => { + setHideExtraColumns(['m', 'l'].includes(getBreakpoint(width) ?? '')); + }, + 50, + [width] + ); + + const items = list.summaries ?? []; + + const { histogramsById, minInterval } = useMonitorHistogram({ items }); + + const nextPagePagination = list.nextPagePagination ?? ''; + const prevPagePagination = list.prevPagePagination ?? ''; + + const toggleDrawer = (id: string) => { + if (expandedDrawerIds.includes(id)) { + updateExpandedDrawerIds(expandedDrawerIds.filter((p) => p !== id)); + } else { + updateExpandedDrawerIds([...expandedDrawerIds, id]); + } + }; + + const getExpandedRowMap = () => { + return expandedDrawerIds.reduce((map: ExpandedRowMap, id: string) => { + return { + ...map, + [id]: ( + monitorId === id)!} + /> + ), + }; + }, {}); + }; + + const columns = [ + ...[ + { + align: 'left' as const, + field: 'state.summary.status', + name: labels.STATUS_COLUMN_LABEL, + mobileOptions: { + fullWidth: true, + }, + render: ( + status: string, + { + monitor_id: monitorId, + state: { + timestamp, + summaryPings, + monitor: { type, duration, checkGroup }, + error: summaryError, + }, + configId, + }: MonitorSummary + ) => { + return ( + + ); + }, + }, + { + align: 'left' as const, + field: 'state.monitor.name', + name: labels.NAME_COLUMN_LABEL, + mobileOptions: { + fullWidth: true, + }, + render: (_name: string, summary: MonitorSummary) => , + sortable: true, + }, + { + align: 'left' as const, + field: 'state.url.full', + name: URL_LABEL, + width: '30%', + render: (url: string) => ( + + {url} + + ), + }, + { + align: 'left' as const, + field: 'state.monitor.name', + name: TAGS_LABEL, + width: '12%', + render: (_name: string, summary: MonitorSummary) => , + }, + { + align: 'left' as const, + field: 'state.tls.server.x509', + name: labels.TLS_COLUMN_LABEL, + render: (x509: X509Expiry) => , + }, + ], + ...(!hideExtraColumns + ? [ + { + align: 'left' as const, + field: 'monitor_id', + name: labels.HISTORY_COLUMN_LABEL, + mobileOptions: { + show: false, + }, + render: (monitorId: string) => ( + + ), + }, + ] + : []), + { + align: 'center' as const, + field: '', + name: STATUS_ALERT_COLUMN, + width: '100px', + render: (item: MonitorSummary) => ( + + ), + }, + { + align: 'center' as const, + field: '', + name: TEST_NOW_COLUMN, + width: '100px', + render: (item: MonitorSummary) => ( + + ), + }, + ...(!hideExtraColumns + ? [ + { + align: 'right' as const, + field: 'monitor_id', + name: '', + sortable: true, + isExpander: true, + width: '40px', + render: (id: string) => { + return ( + toggleDrawer(id)} + /> + ); + }, + }, + ] + : []), + ]; + + return ( + + + + } + columns={columns} + tableLayout={'auto'} + rowProps={ + hideExtraColumns + ? ({ monitor_id: monitorId }) => ({ + onClick: () => toggleDrawer(monitorId), + 'aria-label': labels.getExpandDrawerLabel(monitorId), + }) + : ({ monitor_id: monitorId }) => ({ + className: refreshedMonitorIds.includes(monitorId) ? 'refresh-row' : undefined, + }) + } + /> + + + + + + + + + + + + + + + + + + ); +}; + +const WrapperPanel = euiStyled(EuiPanel)` + &&& { + .refresh-row{ + background-color: #f0f4fb; + -webkit-transition: background-color 3000ms linear; + -ms-transition: background-color 3000ms linear; + transition: background-color 3000ms linear; + } + } +`; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_container.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_container.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_container.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_container.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/__snapshots__/integration_group.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/__snapshots__/integration_group.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/__snapshots__/integration_group.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/__snapshots__/integration_group.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/__snapshots__/integration_link.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/__snapshots__/integration_link.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/__snapshots__/integration_link.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/__snapshots__/integration_link.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/__snapshots__/monitor_list_drawer.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/__snapshots__/monitor_list_drawer.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/__snapshots__/monitor_list_drawer.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/__snapshots__/monitor_list_drawer.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/__snapshots__/most_recent_error.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/__snapshots__/most_recent_error.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/__snapshots__/most_recent_error.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/__snapshots__/most_recent_error.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx index eee1c461fb1cb..9a8b660cb41bb 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { EuiPopover, EuiButton } from '@elastic/eui'; import { IntegrationGroup } from './integration_group'; -import { MonitorSummary } from '../../../../../../common/runtime_types'; +import { MonitorSummary } from '../../../../../../../common/runtime_types'; import { toggleIntegrationsPopover, PopoverState } from '../../../../../state/actions'; export interface ActionsPopoverProps { diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover_container.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover_container.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover_container.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover_container.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx similarity index 99% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx index 97d1e8da61a7f..51d24978026e4 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx @@ -19,7 +19,7 @@ import { getLoggingIpHref, getLoggingKubernetesHref, } from '../../../../../lib/helper'; -import { MonitorSummary } from '../../../../../../common/runtime_types'; +import { MonitorSummary } from '../../../../../../../common/runtime_types'; import { UptimeSettingsContext } from '../../../../../contexts'; interface IntegrationGroupProps { diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_link.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_link.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_link.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_link.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/data.json b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/data.json similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/data.json rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/data.json diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/enabled_alerts.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/enabled_alerts.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/enabled_alerts.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/enabled_alerts.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/index.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/integration_group.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/integration_group.test.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/integration_group.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/integration_group.test.tsx index 5f50285063541..ee58d2f78d31a 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/integration_group.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/integration_group.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { MonitorSummary, makePing } from '../../../../../common/runtime_types'; +import { MonitorSummary, makePing } from '../../../../../../common/runtime_types'; import { shallowWithIntl } from '@kbn/test-jest-helpers'; import { IntegrationGroup, extractSummaryValues } from './actions_popover/integration_group'; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/integration_link.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/integration_link.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/integration_link.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/integration_link.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/list_drawer_container.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/list_drawer_container.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/list_drawer_container.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/list_drawer_container.tsx index 2b97f53f21356..a0708712c8cd3 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/list_drawer_container.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/list_drawer_container.tsx @@ -12,7 +12,7 @@ import { monitorDetailsLoadingSelector, monitorDetailsSelector } from '../../../ import { getMonitorDetailsAction } from '../../../../state/actions/monitor'; import { MonitorListDrawerComponent } from './monitor_list_drawer'; import { useGetUrlParams } from '../../../../hooks'; -import { MonitorSummary } from '../../../../../common/runtime_types'; +import { MonitorSummary } from '../../../../../../common/runtime_types'; import { alertsSelector } from '../../../../state/alerts/alerts'; import { UptimeRefreshContext } from '../../../../contexts'; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.test.tsx index 240697af470b0..78a70d14fa271 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { MonitorListDrawerComponent } from './monitor_list_drawer'; -import { MonitorDetails, MonitorSummary, makePing } from '../../../../../common/runtime_types'; +import { MonitorDetails, MonitorSummary, makePing } from '../../../../../../common/runtime_types'; import { shallowWithRouter } from '../../../../lib'; describe('MonitorListDrawer component', () => { diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx index 092614b870bda..81648bee153cd 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx @@ -11,7 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { Rule } from '@kbn/triggers-actions-ui-plugin/public'; import { MostRecentError } from './most_recent_error'; import { MonitorStatusList } from './monitor_status_list'; -import { MonitorDetails, MonitorSummary } from '../../../../../common/runtime_types'; +import { MonitorDetails, MonitorSummary } from '../../../../../../common/runtime_types'; import { ActionsPopover } from './actions_popover/actions_popover_container'; import { EnabledAlerts } from './enabled_alerts'; import { MonitorUrl } from './monitor_url'; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.test.tsx index ad9347ab84763..f1a9d1b2629a6 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { MonitorStatusList } from './monitor_status_list'; -import { Ping } from '../../../../../common/runtime_types'; +import { Ping } from '../../../../../../common/runtime_types'; import { mockMoment } from '../../../../lib/helper/test_helpers'; import { render } from '../../../../lib/helper/rtl_helpers'; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx similarity index 94% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx index 1207798a34023..753ae0b1390db 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx @@ -10,8 +10,8 @@ import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { LocationLink } from '../../../common/location_link'; import { MonitorStatusRow } from './monitor_status_row'; -import { Ping } from '../../../../../common/runtime_types'; -import { STATUS, UNNAMED_LOCATION } from '../../../../../common/constants'; +import { Ping } from '../../../../../../common/runtime_types'; +import { STATUS, UNNAMED_LOCATION } from '../../../../../../common/constants'; interface MonitorStatusListProps { /** diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_row.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_status_row.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_row.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_status_row.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_row.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_status_row.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_row.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_status_row.tsx index 4bfe079e73a3f..7804b9bc94b5e 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_row.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_status_row.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiBadge, EuiSpacer } from '@elastic/eui'; -import { UNNAMED_LOCATION, STATUS } from '../../../../../common/constants'; +import { UNNAMED_LOCATION, STATUS } from '../../../../../../common/constants'; import { getHealthMessage } from '../columns/monitor_status_column'; interface MonitorStatusRowProps { diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_url.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_url.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/monitor_url.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/monitor_url.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/most_recent_error.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/most_recent_error.test.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/most_recent_error.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/most_recent_error.test.tsx index 111a8a18ccad5..e3b36645ba7f3 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/most_recent_error.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/most_recent_error.test.tsx @@ -10,7 +10,7 @@ import React from 'react'; import moment from 'moment'; import { BrowserRouter as Router } from 'react-router-dom'; import { MostRecentError } from './most_recent_error'; -import { MonitorDetails, PingError } from '../../../../../common/runtime_types'; +import { MonitorDetails, PingError } from '../../../../../../common/runtime_types'; describe('MostRecentError component', () => { let monitorDetails: MonitorDetails; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/most_recent_error.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/most_recent_error.tsx similarity index 91% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/most_recent_error.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/most_recent_error.tsx index 0b7c270c1b636..c5cdc60253f94 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/most_recent_error.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/most_recent_error.tsx @@ -15,8 +15,8 @@ import moment from 'moment'; import { i18n } from '@kbn/i18n'; import { MonitorPageLink } from '../../../common/monitor_page_link'; import { useGetUrlParams } from '../../../../hooks'; -import { stringifyUrlParams } from '../../../../lib/helper/stringify_url_params'; -import { PingError } from '../../../../../common/runtime_types'; +import { stringifyUrlParams } from '../../../../../apps/synthetics/utils/url_params/stringify_url_params'; +import { PingError } from '../../../../../../common/runtime_types'; interface MostRecentErrorProps { /** diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/most_recent_run.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/most_recent_run.tsx similarity index 93% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/most_recent_run.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/most_recent_run.tsx index 32fff166a228e..06bb51c959404 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_drawer/most_recent_run.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_drawer/most_recent_run.tsx @@ -14,7 +14,7 @@ import { } from '@elastic/eui'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; -import { MonitorSummary } from '../../../../../common/runtime_types'; +import { MonitorSummary } from '../../../../../../common/runtime_types'; interface Props { summary: MonitorSummary; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_header.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_header.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_header.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_header.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_page_size_select.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_page_size_select.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_page_size_select.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_page_size_select.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_page_size_select.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_page_size_select.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/monitor_list_page_size_select.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/monitor_list_page_size_select.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/no_items_meesage.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/no_items_meesage.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/no_items_meesage.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/no_items_meesage.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/no_items_message.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/no_items_message.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/no_items_message.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/no_items_message.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/overview_page_link.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/overview_page_link.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/overview_page_link.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/overview_page_link.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/parse_timestamp.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/parse_timestamp.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/parse_timestamp.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/parse_timestamp.test.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/parse_timestamp.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/parse_timestamp.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/parse_timestamp.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/parse_timestamp.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/status_filter.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/status_filter.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/status_filter.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/status_filter.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/status_filter.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/status_filter.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/status_filter.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/status_filter.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/translations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/translations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/translations.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/troubleshoot_popover.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/troubleshoot_popover.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/troubleshoot_popover.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/troubleshoot_popover.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/types.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/types.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/types.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/types.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/use_monitor_histogram.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/use_monitor_histogram.ts similarity index 94% rename from x-pack/plugins/synthetics/public/components/overview/monitor_list/use_monitor_histogram.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/use_monitor_histogram.ts index 8a6fb5a174856..1a9b7b73732af 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/use_monitor_histogram.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/monitor_list/use_monitor_histogram.ts @@ -13,12 +13,12 @@ import { Histogram, HistogramPoint, MonitorSummary, -} from '../../../../common/runtime_types/monitor'; +} from '../../../../../common/runtime_types/monitor'; import { useGetUrlParams } from '../../../hooks'; import { UptimeRefreshContext } from '../../../contexts'; import { esKuerySelector } from '../../../state/selectors'; -import { getHistogramInterval } from '../../../../common/lib/get_histogram_interval'; -import { Ping } from '../../../../common/runtime_types'; +import { getHistogramInterval } from '../../../../../common/lib/get_histogram_interval'; +import { Ping } from '../../../../../common/runtime_types'; export const useMonitorHistogram = ({ items }: { items: MonitorSummary[] }) => { const { dateRangeStart, dateRangeEnd, statusFilter } = useGetUrlParams(); diff --git a/x-pack/plugins/synthetics/public/components/overview/query_bar/query_bar.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/query_bar/query_bar.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/query_bar/query_bar.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/query_bar/query_bar.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/query_bar/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/query_bar/translations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/query_bar/translations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/query_bar/translations.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/query_bar/use_query_bar.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/query_bar/use_query_bar.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/query_bar/use_query_bar.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/query_bar/use_query_bar.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/query_bar/use_query_bar.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/query_bar/use_query_bar.ts similarity index 98% rename from x-pack/plugins/synthetics/public/components/overview/query_bar/use_query_bar.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/query_bar/use_query_bar.ts index 30658081183d5..75351e5872f35 100644 --- a/x-pack/plugins/synthetics/public/components/overview/query_bar/use_query_bar.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/query_bar/use_query_bar.ts @@ -17,7 +17,7 @@ import { useUrlParams, } from '../../../hooks'; import { setEsKueryString } from '../../../state/actions'; -import { UptimePluginServices } from '../../../apps/plugin'; +import { UptimePluginServices } from '../../../../plugin'; export enum SyntaxType { text = 'text', diff --git a/x-pack/plugins/synthetics/public/components/overview/snapshot/__snapshots__/snapshot.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/snapshot/__snapshots__/snapshot.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/snapshot/__snapshots__/snapshot.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/snapshot/__snapshots__/snapshot.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/overview/snapshot/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/snapshot/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/snapshot/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/snapshot/index.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/snapshot/snapshot.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/snapshot/snapshot.test.tsx similarity index 91% rename from x-pack/plugins/synthetics/public/components/overview/snapshot/snapshot.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/snapshot/snapshot.test.tsx index 3cea8199544d4..322db8908cca7 100644 --- a/x-pack/plugins/synthetics/public/components/overview/snapshot/snapshot.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/snapshot/snapshot.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { shallowWithIntl } from '@kbn/test-jest-helpers'; import { SnapshotComponent } from './snapshot'; -import { Snapshot } from '../../../../common/runtime_types/snapshot'; +import { Snapshot } from '../../../../../common/runtime_types/snapshot'; import * as hook from './use_snap_shot'; describe('Snapshot component', () => { diff --git a/x-pack/plugins/synthetics/public/components/overview/snapshot/snapshot.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/snapshot/snapshot.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/snapshot/snapshot.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/snapshot/snapshot.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/snapshot/snapshot_heading.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/snapshot/snapshot_heading.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/snapshot/snapshot_heading.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/snapshot/snapshot_heading.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/snapshot/use_snap_shot.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/snapshot/use_snap_shot.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/snapshot/use_snap_shot.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/snapshot/use_snap_shot.ts diff --git a/x-pack/plugins/synthetics/public/components/overview/snapshot_heading.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/snapshot_heading.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/snapshot_heading.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/snapshot_heading.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/status_panel.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/status_panel.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/status_panel.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/status_panel.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/synthetics_callout.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/synthetics_callout.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/synthetics_callout.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/synthetics_callout.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/overview/synthetics_callout.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/synthetics_callout.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/overview/synthetics_callout.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/overview/synthetics_callout.tsx diff --git a/x-pack/plugins/synthetics/public/components/settings/__snapshots__/certificate_form.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/__snapshots__/certificate_form.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/settings/__snapshots__/certificate_form.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/settings/__snapshots__/certificate_form.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/settings/__snapshots__/indices_form.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/__snapshots__/indices_form.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/components/settings/__snapshots__/indices_form.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/components/settings/__snapshots__/indices_form.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/components/settings/add_connector_flyout.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/add_connector_flyout.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/settings/add_connector_flyout.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/settings/add_connector_flyout.tsx diff --git a/x-pack/plugins/synthetics/public/components/settings/alert_defaults_form.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/alert_defaults_form.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/settings/alert_defaults_form.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/settings/alert_defaults_form.tsx diff --git a/x-pack/plugins/synthetics/public/components/settings/certificate_form.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/certificate_form.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/settings/certificate_form.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/settings/certificate_form.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/settings/certificate_form.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/certificate_form.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/settings/certificate_form.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/settings/certificate_form.tsx index 2deecc8809074..dc44a7074c33f 100644 --- a/x-pack/plugins/synthetics/public/components/settings/certificate_form.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/certificate_form.tsx @@ -18,8 +18,8 @@ import { EuiFlexGroup, EuiFlexItem, } from '@elastic/eui'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../common/constants'; -import { DefaultEmail, DynamicSettings } from '../../../common/runtime_types'; +import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; +import { DefaultEmail, DynamicSettings } from '../../../../common/runtime_types'; import { SettingsFormProps } from '../../pages/settings'; import { certificateFormTranslations } from './translations'; diff --git a/x-pack/plugins/synthetics/public/components/settings/default_email.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/default_email.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/settings/default_email.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/settings/default_email.tsx index 5a777fa644f82..5ba6278664c61 100644 --- a/x-pack/plugins/synthetics/public/components/settings/default_email.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/default_email.tsx @@ -12,8 +12,8 @@ import { EuiDescribedFormGroup } from '@elastic/eui'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { OnFieldChangeType } from './certificate_form'; import { connectorsSelector } from '../../state/alerts/alerts'; -import { DefaultEmail as DefaultEmailType } from '../../../common/runtime_types'; -import { UptimePluginServices } from '../../apps/plugin'; +import { DefaultEmail as DefaultEmailType } from '../../../../common/runtime_types'; +import { UptimePluginServices } from '../../../plugin'; import { SettingsPageFieldErrors } from '../../pages/settings'; export function DefaultEmail({ diff --git a/x-pack/plugins/synthetics/public/components/settings/indices_form.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/indices_form.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/settings/indices_form.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/settings/indices_form.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/settings/indices_form.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/indices_form.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/settings/indices_form.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/settings/indices_form.tsx index f11626f231eae..4fe2917fbd357 100644 --- a/x-pack/plugins/synthetics/public/components/settings/indices_form.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/indices_form.tsx @@ -15,7 +15,7 @@ import { EuiTitle, EuiSpacer, } from '@elastic/eui'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../common/constants'; +import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; import { SettingsFormProps } from '../../pages/settings'; export const IndicesForm: React.FC = ({ diff --git a/x-pack/plugins/synthetics/public/components/settings/settings_actions.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/settings_actions.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/settings/settings_actions.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/settings/settings_actions.tsx diff --git a/x-pack/plugins/synthetics/public/components/settings/settings_bottom_bar.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/settings_bottom_bar.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/settings/settings_bottom_bar.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/settings/settings_bottom_bar.tsx diff --git a/x-pack/plugins/synthetics/public/components/settings/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/translations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/settings/translations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/settings/translations.ts diff --git a/x-pack/plugins/synthetics/public/components/settings/types.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/types.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/settings/types.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/settings/types.ts diff --git a/x-pack/plugins/synthetics/public/components/settings/use_settings_errors.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/use_settings_errors.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/settings/use_settings_errors.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/settings/use_settings_errors.ts diff --git a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/stderr_logs.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/stderr_logs.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/synthetics/check_steps/stderr_logs.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/stderr_logs.tsx index 6192ee3866913..d127b67efc8e6 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/stderr_logs.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/stderr_logs.tsx @@ -26,9 +26,9 @@ import { useSelector } from 'react-redux'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { useFetcher } from '@kbn/observability-plugin/public'; import { useStdErrorLogs } from './use_std_error_logs'; -import { ClientPluginsStart } from '../../../apps/plugin'; +import { ClientPluginsStart } from '../../../../plugin'; import { selectDynamicSettings } from '../../../state/selectors'; -import { Ping } from '../../../../common/runtime_types'; +import { Ping } from '../../../../../common/runtime_types'; export const StdErrorLogs = ({ configId, diff --git a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_duration.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_duration.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_duration.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_duration.tsx index 92a4ab24433f8..f0e05c2072782 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_duration.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_duration.tsx @@ -11,7 +11,7 @@ import * as React from 'react'; import { EuiButtonEmpty, EuiPopover, EuiText } from '@elastic/eui'; import { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; -import { JourneyStep } from '../../../../common/runtime_types'; +import { JourneyStep } from '../../../../../common/runtime_types'; import { StepFieldTrend } from './step_field_trend'; import { microToSec } from '../../../lib/formatting'; diff --git a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx index d4d2536e47851..2ef6c7718f7bc 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { ReactRouterEuiLink } from '../../../common/react_router_helpers'; -import { Ping } from '../../../../../common/runtime_types/ping/ping'; +import { Ping } from '../../../../../../common/runtime_types/ping/ping'; const LabelLink = euiStyled.div` margin-bottom: ${(props) => props.theme.eui.paddingSizes.xs}; diff --git a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx index 0277a47804fc6..d0db4a99a495e 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx @@ -12,8 +12,8 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { useFetcher } from '@kbn/observability-plugin/public'; import { StepScreenshotDisplay } from '../../step_screenshot_display'; -import { JourneyStep } from '../../../../../common/runtime_types/ping/synthetics'; -import { Ping } from '../../../../../common/runtime_types/ping/ping'; +import { JourneyStep } from '../../../../../../common/runtime_types/ping/synthetics'; +import { Ping } from '../../../../../../common/runtime_types/ping/ping'; import { fetchLastSuccessfulCheck } from '../../../../state/api/journey'; import { ScreenshotLink } from './screenshot_link'; import { getShortTimeStamp } from '../../../overview/monitor_list/columns/monitor_status_column'; diff --git a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_field_trend.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_field_trend.test.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_field_trend.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_field_trend.test.tsx index 165208c032705..741d00fcc072d 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_field_trend.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_field_trend.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { getLast48Intervals, StepFieldTrend } from './step_field_trend'; import { render } from '../../../lib/helper/rtl_helpers'; -import { JourneyStep } from '../../../../common/runtime_types'; +import { JourneyStep } from '../../../../../common/runtime_types'; const step: JourneyStep = { _id: 'docID', diff --git a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_field_trend.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_field_trend.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_field_trend.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_field_trend.tsx index cd1b9df222e58..e03656ed562dc 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_field_trend.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_field_trend.tsx @@ -13,7 +13,7 @@ import { useSelector } from 'react-redux'; import { AllSeries, createExploratoryViewUrl } from '@kbn/observability-plugin/public'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { JourneyStep } from '../../../../common/runtime_types'; +import { JourneyStep } from '../../../../../common/runtime_types'; import { useUptimeStartPlugins } from '../../../contexts/uptime_startup_plugins_context'; import { selectDynamicSettings } from '../../../state/selectors'; diff --git a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_image.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_image.tsx similarity index 92% rename from x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_image.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_image.tsx index 06410e79a22a2..f1280cfe368b1 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/step_image.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/step_image.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; -import { JourneyStep } from '../../../../common/runtime_types/ping/synthetics'; +import { JourneyStep } from '../../../../../common/runtime_types/ping/synthetics'; import { PingTimestamp } from '../../monitor/ping_list/columns/ping_timestamp'; interface Props { diff --git a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/steps_list.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/steps_list.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/synthetics/check_steps/steps_list.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/steps_list.test.tsx index 61535833822da..1a792dfa9f8d3 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/steps_list.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/steps_list.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { JourneyStep } from '../../../../common/runtime_types/ping'; +import { JourneyStep } from '../../../../../common/runtime_types/ping'; import { StepsList } from './steps_list'; import { render, forDesktopOnly, forMobileOnly } from '../../../lib/helper/rtl_helpers'; import { VIEW_PERFORMANCE } from '../../monitor/synthetics/translations'; diff --git a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/steps_list.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/steps_list.tsx similarity index 99% rename from x-pack/plugins/synthetics/public/components/synthetics/check_steps/steps_list.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/steps_list.tsx index 957c25225a614..d02b7ac0e51da 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/steps_list.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/steps_list.tsx @@ -17,7 +17,7 @@ import { import { i18n } from '@kbn/i18n'; import React, { MouseEvent, useState } from 'react'; import styled from 'styled-components'; -import { JourneyStep } from '../../../../common/runtime_types'; +import { JourneyStep } from '../../../../../common/runtime_types'; import { STATUS_LABEL } from '../../monitor/ping_list/translations'; import { COLLAPSE_LABEL, EXPAND_LABEL, STEP_NAME_LABEL } from '../translations'; import { StatusBadge } from '../status_badge'; diff --git a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/use_check_steps.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/use_check_steps.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/synthetics/check_steps/use_check_steps.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/use_check_steps.ts diff --git a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/use_expanded_row.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/use_expanded_row.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/synthetics/check_steps/use_expanded_row.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/use_expanded_row.test.tsx index c16522804eed6..6d1dc3f80fe60 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/use_expanded_row.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/use_expanded_row.test.tsx @@ -13,8 +13,8 @@ import { createMemoryHistory } from 'history'; import { EuiButtonIcon } from '@elastic/eui'; import { getExpandedStepCallback, useExpandedRow } from './use_expanded_row'; import { render } from '../../../lib/helper/rtl_helpers'; -import { JourneyStep } from '../../../../common/runtime_types'; -import { SYNTHETIC_CHECK_STEPS_ROUTE } from '../../../../common/constants'; +import { JourneyStep } from '../../../../../common/runtime_types'; +import { SYNTHETIC_CHECK_STEPS_ROUTE } from '../../../../../common/constants'; import { COLLAPSE_LABEL, EXPAND_LABEL } from '../translations'; import { act } from 'react-dom/test-utils'; diff --git a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/use_expanded_row.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/use_expanded_row.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/synthetics/check_steps/use_expanded_row.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/use_expanded_row.tsx index 1b3a641033dd7..51fe965c55fbc 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/use_expanded_row.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/use_expanded_row.tsx @@ -8,7 +8,7 @@ import React, { useEffect, useState, useCallback } from 'react'; import { useParams } from 'react-router-dom'; import { ExecutedStep } from '../executed_step'; -import { JourneyStep } from '../../../../common/runtime_types/ping'; +import { JourneyStep } from '../../../../../common/runtime_types/ping'; interface HookProps { loading: boolean; diff --git a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/use_std_error_logs.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/use_std_error_logs.ts similarity index 96% rename from x-pack/plugins/synthetics/public/components/synthetics/check_steps/use_std_error_logs.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/use_std_error_logs.ts index 11dfdcdcc434e..d6abd2445a9a7 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/check_steps/use_std_error_logs.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/check_steps/use_std_error_logs.ts @@ -8,7 +8,7 @@ import { useSelector } from 'react-redux'; import { createEsParams, useEsSearch } from '@kbn/observability-plugin/public'; import { selectDynamicSettings } from '../../../state/selectors'; -import { Ping } from '../../../../common/runtime_types'; +import { Ping } from '../../../../../common/runtime_types'; export const useStdErrorLogs = ({ configId, diff --git a/x-pack/plugins/synthetics/public/components/synthetics/code_block_accordion.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/code_block_accordion.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/synthetics/code_block_accordion.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/code_block_accordion.tsx diff --git a/x-pack/plugins/synthetics/public/components/synthetics/console_event.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/console_event.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/synthetics/console_event.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/console_event.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/synthetics/console_event.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/console_event.tsx similarity index 93% rename from x-pack/plugins/synthetics/public/components/synthetics/console_event.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/console_event.tsx index a9cde922e8926..aeceef2477e28 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/console_event.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/console_event.tsx @@ -8,7 +8,7 @@ import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; import React, { useContext, FC } from 'react'; import { UptimeThemeContext } from '../../contexts'; -import { JourneyStep } from '../../../common/runtime_types/ping'; +import { JourneyStep } from '../../../../common/runtime_types/ping'; interface Props { event: JourneyStep; diff --git a/x-pack/plugins/synthetics/public/components/synthetics/console_output_event_list.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/console_output_event_list.test.tsx similarity index 96% rename from x-pack/plugins/synthetics/public/components/synthetics/console_output_event_list.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/console_output_event_list.test.tsx index d35e526208ae9..9e63ff070c752 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/console_output_event_list.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/console_output_event_list.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { JourneyStep } from '../../../common/runtime_types/ping/synthetics'; +import { JourneyStep } from '../../../../common/runtime_types/ping/synthetics'; import { render } from '../../lib/helper/rtl_helpers'; import { ConsoleOutputEventList } from './console_output_event_list'; diff --git a/x-pack/plugins/synthetics/public/components/synthetics/console_output_event_list.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/console_output_event_list.tsx similarity index 95% rename from x-pack/plugins/synthetics/public/components/synthetics/console_output_event_list.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/console_output_event_list.tsx index 4bf33663d920a..1019532e0a610 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/console_output_event_list.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/console_output_event_list.tsx @@ -9,7 +9,7 @@ import { EuiCodeBlock, EuiSpacer, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { FC } from 'react'; import { ConsoleEvent } from './console_event'; -import { JourneyStep } from '../../../common/runtime_types/ping'; +import { JourneyStep } from '../../../../common/runtime_types/ping'; import { JourneyState } from '../../state/reducers/journey'; interface Props { diff --git a/x-pack/plugins/synthetics/public/components/synthetics/empty_journey.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/empty_journey.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/synthetics/empty_journey.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/empty_journey.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/synthetics/empty_journey.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/empty_journey.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/synthetics/empty_journey.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/empty_journey.tsx diff --git a/x-pack/plugins/synthetics/public/components/synthetics/executed_step.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/executed_step.test.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/components/synthetics/executed_step.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/executed_step.test.tsx index 81d506bd05413..8231fc5808506 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/executed_step.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/executed_step.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { ExecutedStep } from './executed_step'; import { render } from '../../lib/helper/rtl_helpers'; -import { JourneyStep } from '../../../common/runtime_types/ping'; +import { JourneyStep } from '../../../../common/runtime_types/ping'; describe('ExecutedStep', () => { let step: JourneyStep; diff --git a/x-pack/plugins/synthetics/public/components/synthetics/executed_step.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/executed_step.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/components/synthetics/executed_step.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/executed_step.tsx index f3021894ec5ff..063dd9db0eae3 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/executed_step.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/executed_step.tsx @@ -10,7 +10,7 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { CodeBlockAccordion } from './code_block_accordion'; -import { JourneyStep } from '../../../common/runtime_types/ping'; +import { JourneyStep } from '../../../../common/runtime_types/ping'; import { StepScreenshots } from './check_steps/step_expanded_row/step_screenshots'; const CODE_BLOCK_OVERFLOW_HEIGHT = 360; diff --git a/x-pack/plugins/synthetics/public/components/synthetics/status_badge.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/status_badge.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/synthetics/status_badge.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/status_badge.test.tsx diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/status_badge.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/status_badge.tsx new file mode 100644 index 0000000000000..4eba0f4b6a288 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/status_badge.tsx @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useContext, FC } from 'react'; +import { UptimeAppColors } from '../../app/uptime_app'; +import { UptimeThemeContext } from '../../contexts'; + +interface StatusBadgeProps { + isMobile?: boolean; + status?: string; + stepNo: number; +} + +export function colorFromStatus(color: UptimeAppColors, status?: string) { + switch (status) { + case 'succeeded': + return color.success; + case 'failed': + return color.dangerBehindText; + default: + return 'default'; + } +} + +export function textFromStatus(status?: string) { + switch (status) { + case 'succeeded': + return i18n.translate('xpack.synthetics.synthetics.statusBadge.succeededMessage', { + defaultMessage: 'Succeeded', + }); + case 'failed': + return i18n.translate('xpack.synthetics.synthetics.statusBadge.failedMessage', { + defaultMessage: 'Failed', + }); + case 'skipped': + return i18n.translate('xpack.synthetics.synthetics.statusBadge.skippedMessage', { + defaultMessage: 'Skipped', + }); + default: + return null; + } +} + +export const StatusBadge: FC = ({ status, stepNo, isMobile }) => { + const theme = useContext(UptimeThemeContext); + return ( + + {!isMobile && ( + + + {stepNo}. + + + )} + + {textFromStatus(status)} + + + ); +}; diff --git a/x-pack/plugins/synthetics/public/components/synthetics/step_screenshot_display.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/step_screenshot_display.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/components/synthetics/step_screenshot_display.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/step_screenshot_display.test.tsx diff --git a/x-pack/plugins/synthetics/public/components/synthetics/step_screenshot_display.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/step_screenshot_display.tsx similarity index 99% rename from x-pack/plugins/synthetics/public/components/synthetics/step_screenshot_display.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/step_screenshot_display.tsx index bc3cda6cd93bc..b5a82eac7bad1 100644 --- a/x-pack/plugins/synthetics/public/components/synthetics/step_screenshot_display.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/step_screenshot_display.tsx @@ -22,7 +22,7 @@ import { useFetcher } from '@kbn/observability-plugin/public'; import { isScreenshotRef as isAScreenshotRef, ScreenshotRefImageData, -} from '../../../common/runtime_types'; +} from '../../../../common/runtime_types'; import { UptimeRefreshContext, UptimeSettingsContext, UptimeThemeContext } from '../../contexts'; import { getJourneyScreenshot } from '../../state/api/journey'; import { useCompositeImage } from '../../hooks'; diff --git a/x-pack/plugins/synthetics/public/components/synthetics/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/translations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/components/synthetics/translations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/components/synthetics/translations.ts diff --git a/x-pack/plugins/synthetics/public/contexts/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/contexts/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/contexts/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/contexts/index.ts diff --git a/x-pack/plugins/synthetics/public/contexts/uptime_data_view_context.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/contexts/uptime_data_view_context.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/contexts/uptime_data_view_context.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/contexts/uptime_data_view_context.tsx diff --git a/x-pack/plugins/synthetics/public/contexts/uptime_refresh_context.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/contexts/uptime_refresh_context.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/contexts/uptime_refresh_context.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/contexts/uptime_refresh_context.tsx diff --git a/x-pack/plugins/synthetics/public/contexts/uptime_settings_context.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/contexts/uptime_settings_context.tsx similarity index 94% rename from x-pack/plugins/synthetics/public/contexts/uptime_settings_context.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/contexts/uptime_settings_context.tsx index 4fda00db57bd7..d192f7327879c 100644 --- a/x-pack/plugins/synthetics/public/contexts/uptime_settings_context.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/contexts/uptime_settings_context.tsx @@ -6,8 +6,8 @@ */ import React, { createContext, useContext, useMemo } from 'react'; -import { UptimeAppProps } from '../apps/uptime_app'; -import { CLIENT_DEFAULTS, CONTEXT_DEFAULTS } from '../../common/constants'; +import { UptimeAppProps } from '../app/uptime_app'; +import { CLIENT_DEFAULTS, CONTEXT_DEFAULTS } from '../../../common/constants'; import { CommonlyUsedRange } from '../components/common/uptime_date_picker'; import { useGetUrlParams } from '../hooks'; diff --git a/x-pack/plugins/synthetics/public/contexts/uptime_startup_plugins_context.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/contexts/uptime_startup_plugins_context.tsx similarity index 92% rename from x-pack/plugins/synthetics/public/contexts/uptime_startup_plugins_context.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/contexts/uptime_startup_plugins_context.tsx index 8fbc8ae749c81..76754a52a3dfb 100644 --- a/x-pack/plugins/synthetics/public/contexts/uptime_startup_plugins_context.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/contexts/uptime_startup_plugins_context.tsx @@ -6,7 +6,7 @@ */ import React, { createContext, useContext } from 'react'; -import { ClientPluginsStart } from '../apps/plugin'; +import { ClientPluginsStart } from '../../plugin'; export const UptimeStartupPluginsContext = createContext>({}); diff --git a/x-pack/plugins/synthetics/public/contexts/uptime_theme_context.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/contexts/uptime_theme_context.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/contexts/uptime_theme_context.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/contexts/uptime_theme_context.tsx index 2d25cf6e84e1b..b37856b03e87e 100644 --- a/x-pack/plugins/synthetics/public/contexts/uptime_theme_context.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/contexts/uptime_theme_context.tsx @@ -9,7 +9,7 @@ import { euiLightVars, euiDarkVars } from '@kbn/ui-theme'; import React, { createContext, useMemo } from 'react'; import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; import { DARK_THEME, LIGHT_THEME, PartialTheme, Theme } from '@elastic/charts'; -import { UptimeAppColors } from '../apps/uptime_app'; +import { UptimeAppColors } from '../app/uptime_app'; export interface UptimeThemeContextValues { colors: UptimeAppColors; diff --git a/x-pack/plugins/synthetics/public/hooks/__snapshots__/use_url_params.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/__snapshots__/use_url_params.test.tsx.snap similarity index 100% rename from x-pack/plugins/synthetics/public/hooks/__snapshots__/use_url_params.test.tsx.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/__snapshots__/use_url_params.test.tsx.snap diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/hooks/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/index.ts new file mode 100644 index 0000000000000..6d2a826caa4b4 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './use_composite_image'; +export * from './update_kuery_string'; +export * from './use_monitor'; +export * from './use_search_text'; +export * from './use_cert_status'; +export * from './use_telemetry'; +export * from './use_url_params'; +export { useUptimeDataView } from '../contexts/uptime_data_view_context'; diff --git a/x-pack/plugins/synthetics/public/hooks/update_kuery_string.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/update_kuery_string.ts similarity index 99% rename from x-pack/plugins/synthetics/public/hooks/update_kuery_string.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/update_kuery_string.ts index a8884279f49ba..007dfd12427f3 100644 --- a/x-pack/plugins/synthetics/public/hooks/update_kuery_string.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/update_kuery_string.ts @@ -7,7 +7,7 @@ import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; import { DataView } from '@kbn/data-views-plugin/public'; -import { combineFiltersAndUserSearch, stringifyKueries } from '../../common/lib'; +import { combineFiltersAndUserSearch, stringifyKueries } from '../../../common/lib'; const getKueryString = (urlFilters: string, excludedFilters?: string): string => { let kueryString = ''; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_breadcrumbs.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_breadcrumbs.test.tsx new file mode 100644 index 0000000000000..35b5a1aa0994c --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_breadcrumbs.test.tsx @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ChromeBreadcrumb } from '@kbn/core/public'; +import React from 'react'; +import { Route } from 'react-router-dom'; +import { mountWithRouter } from '../lib'; +import { OVERVIEW_ROUTE } from '../../../common/constants'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { UptimeUrlParams, getSupportedUrlParams, MountWithReduxProvider } from '../lib/helper'; +import { makeBaseBreadcrumb, useBreadcrumbs } from './use_breadcrumbs'; + +describe('useBreadcrumbs', () => { + it('sets the given breadcrumbs', () => { + const [getBreadcrumbs, core] = mockCore(); + + const expectedCrumbs: ChromeBreadcrumb[] = [ + { text: 'Crumb: ', href: 'http://href.example.net' }, + { text: 'Crumb II: Son of Crumb', href: 'http://href2.example.net' }, + ]; + + const Component = () => { + useBreadcrumbs(expectedCrumbs); + return <>Hello; + }; + + mountWithRouter( + + + + + + + + ); + + const urlParams: UptimeUrlParams = getSupportedUrlParams({}); + expect(JSON.stringify(getBreadcrumbs())).toEqual( + JSON.stringify( + makeBaseBreadcrumb('/app/uptime', '/app/observability', urlParams).concat(expectedCrumbs) + ) + ); + }); +}); + +const mockCore: () => [() => ChromeBreadcrumb[], any] = () => { + let breadcrumbObj: ChromeBreadcrumb[] = []; + const get = () => { + return breadcrumbObj; + }; + const core = { + application: { + getUrlForApp: (app: string) => (app === 'uptime' ? '/app/uptime' : '/app/observability'), + navigateToUrl: jest.fn(), + }, + chrome: { + setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => { + breadcrumbObj = newBreadcrumbs; + }, + }, + }; + + return [get, core]; +}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_breadcrumbs.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_breadcrumbs.ts new file mode 100644 index 0000000000000..0befbeb14832c --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_breadcrumbs.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ChromeBreadcrumb } from '@kbn/core/public'; +import { i18n } from '@kbn/i18n'; +import { MouseEvent, useEffect } from 'react'; +import { EuiBreadcrumb } from '@elastic/eui'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { UptimeUrlParams } from '../lib/helper'; +import { stringifyUrlParams } from '../../apps/synthetics/utils/url_params/stringify_url_params'; +import { useUrlParams } from '.'; +import { PLUGIN } from '../../../common/constants/plugin'; + +const EMPTY_QUERY = '?'; + +function handleBreadcrumbClick( + breadcrumbs: ChromeBreadcrumb[], + navigateToHref?: (url: string) => Promise +) { + return breadcrumbs.map((bc) => ({ + ...bc, + ...(bc.href + ? { + onClick: (event: MouseEvent) => { + if (navigateToHref && bc.href) { + event.preventDefault(); + navigateToHref(bc.href); + } + }, + } + : {}), + })); +} + +export const makeBaseBreadcrumb = ( + uptimePath: string, + observabilityPath: string, + params?: UptimeUrlParams +): [EuiBreadcrumb, EuiBreadcrumb] => { + if (params) { + const crumbParams: Partial = { ...params }; + + delete crumbParams.statusFilter; + const query = stringifyUrlParams(crumbParams, true); + uptimePath += query === EMPTY_QUERY ? '' : query; + } + + return [ + { + text: i18n.translate('xpack.synthetics.breadcrumbs.observabilityText', { + defaultMessage: 'Observability', + }), + href: observabilityPath, + }, + { + text: i18n.translate('xpack.synthetics.breadcrumbs.legacyOverviewBreadcrumbText', { + defaultMessage: 'Uptime', + }), + href: uptimePath, + }, + ]; +}; + +export const useBreadcrumbs = (extraCrumbs: ChromeBreadcrumb[]) => { + const params = useUrlParams()[0](); + const kibana = useKibana(); + const setBreadcrumbs = kibana.services.chrome?.setBreadcrumbs; + const uptimePath = kibana.services.application?.getUrlForApp(PLUGIN.ID) ?? ''; + const observabilityPath = + kibana.services.application?.getUrlForApp('observability-overview') ?? ''; + const navigate = kibana.services.application?.navigateToUrl; + + useEffect(() => { + if (setBreadcrumbs) { + setBreadcrumbs( + handleBreadcrumbClick( + makeBaseBreadcrumb(uptimePath, observabilityPath, params).concat(extraCrumbs), + navigate + ) + ); + } + }, [uptimePath, observabilityPath, extraCrumbs, navigate, params, setBreadcrumbs]); +}; diff --git a/x-pack/plugins/synthetics/public/hooks/use_cert_status.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_cert_status.ts similarity index 95% rename from x-pack/plugins/synthetics/public/hooks/use_cert_status.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_cert_status.ts index d82948632543b..934d72b71de6e 100644 --- a/x-pack/plugins/synthetics/public/hooks/use_cert_status.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_cert_status.ts @@ -8,7 +8,7 @@ import moment from 'moment'; import { useSelector } from 'react-redux'; import { selectDynamicSettings } from '../state/selectors'; -import { CERT_STATUS } from '../../common/constants'; +import { CERT_STATUS } from '../../../common/constants'; export const useCertStatus = (expiryDate?: string, issueDate?: string) => { const dss = useSelector(selectDynamicSettings); diff --git a/x-pack/plugins/synthetics/public/hooks/use_chart_theme.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_chart_theme.ts similarity index 100% rename from x-pack/plugins/synthetics/public/hooks/use_chart_theme.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_chart_theme.ts diff --git a/x-pack/plugins/synthetics/public/hooks/use_composite_image.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_composite_image.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/hooks/use_composite_image.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_composite_image.test.tsx index 9e2cb1e498b73..50fc366f50dbe 100644 --- a/x-pack/plugins/synthetics/public/hooks/use_composite_image.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_composite_image.test.tsx @@ -7,7 +7,7 @@ import * as redux from 'react-redux'; import { renderHook } from '@testing-library/react-hooks'; -import { ScreenshotRefImageData } from '../../common/runtime_types'; +import { ScreenshotRefImageData } from '../../../common/runtime_types'; import { ScreenshotBlockCache } from '../state/reducers/synthetics'; import { shouldCompose, useCompositeImage } from './use_composite_image'; import * as compose from '../lib/helper/compose_screenshot_images'; diff --git a/x-pack/plugins/synthetics/public/hooks/use_composite_image.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_composite_image.ts similarity index 97% rename from x-pack/plugins/synthetics/public/hooks/use_composite_image.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_composite_image.ts index 3af1e798d43e1..ca783bdd290c4 100644 --- a/x-pack/plugins/synthetics/public/hooks/use_composite_image.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_composite_image.ts @@ -8,7 +8,7 @@ import { useDispatch, useSelector } from 'react-redux'; import React from 'react'; import { composeScreenshotRef } from '../lib/helper/compose_screenshot_images'; -import { ScreenshotRefImageData } from '../../common/runtime_types/ping/synthetics'; +import { ScreenshotRefImageData } from '../../../common/runtime_types/ping/synthetics'; import { fetchBlocksAction, isPendingBlock, diff --git a/x-pack/plugins/synthetics/public/hooks/use_filter_update.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_filter_update.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/hooks/use_filter_update.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_filter_update.test.ts diff --git a/x-pack/plugins/synthetics/public/hooks/use_filter_update.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_filter_update.ts similarity index 100% rename from x-pack/plugins/synthetics/public/hooks/use_filter_update.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_filter_update.ts diff --git a/x-pack/plugins/synthetics/public/hooks/use_init_app.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_init_app.ts similarity index 100% rename from x-pack/plugins/synthetics/public/hooks/use_init_app.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_init_app.ts diff --git a/x-pack/plugins/synthetics/public/hooks/use_mapping_check.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_mapping_check.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/hooks/use_mapping_check.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_mapping_check.test.ts diff --git a/x-pack/plugins/synthetics/public/hooks/use_mapping_check.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_mapping_check.ts similarity index 94% rename from x-pack/plugins/synthetics/public/hooks/use_mapping_check.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_mapping_check.ts index d8a7e0fac4065..dfda2900c7904 100644 --- a/x-pack/plugins/synthetics/public/hooks/use_mapping_check.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_mapping_check.ts @@ -7,7 +7,7 @@ import { useEffect } from 'react'; import { useHistory } from 'react-router-dom'; -import { MAPPING_ERROR_ROUTE } from '../../common/constants'; +import { MAPPING_ERROR_ROUTE } from '../../../common/constants'; interface EsBadRequestError { body?: { diff --git a/x-pack/plugins/synthetics/public/hooks/use_monitor.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_monitor.ts similarity index 100% rename from x-pack/plugins/synthetics/public/hooks/use_monitor.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_monitor.ts diff --git a/x-pack/plugins/synthetics/public/hooks/use_overview_filter_check.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_overview_filter_check.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/hooks/use_overview_filter_check.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_overview_filter_check.test.tsx diff --git a/x-pack/plugins/synthetics/public/hooks/use_overview_filter_check.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_overview_filter_check.ts similarity index 100% rename from x-pack/plugins/synthetics/public/hooks/use_overview_filter_check.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_overview_filter_check.ts diff --git a/x-pack/plugins/synthetics/public/hooks/use_search_text.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_search_text.ts similarity index 100% rename from x-pack/plugins/synthetics/public/hooks/use_search_text.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_search_text.ts diff --git a/x-pack/plugins/synthetics/public/hooks/use_selected_filters.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_selected_filters.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/hooks/use_selected_filters.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_selected_filters.test.tsx diff --git a/x-pack/plugins/synthetics/public/hooks/use_selected_filters.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_selected_filters.ts similarity index 97% rename from x-pack/plugins/synthetics/public/hooks/use_selected_filters.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_selected_filters.ts index b10760aa85ed3..03904a2d67421 100644 --- a/x-pack/plugins/synthetics/public/hooks/use_selected_filters.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_selected_filters.ts @@ -9,7 +9,7 @@ import { useMemo } from 'react'; import { useRouteMatch } from 'react-router-dom'; import { useGetUrlParams } from './use_url_params'; import { filterLabels } from '../components/overview/filter_group/translations'; -import { FILTER_FIELDS, MONITOR_ROUTE } from '../../common/constants'; +import { FILTER_FIELDS, MONITOR_ROUTE } from '../../../common/constants'; import { parseFiltersMap } from './use_filter_update'; type FilterType = string[]; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_telemetry.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_telemetry.ts new file mode 100644 index 0000000000000..78062bb1ff7eb --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_telemetry.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 { useEffect } from 'react'; +import { useGetUrlParams } from './use_url_params'; +import { apiService } from '../state/api/utils'; +import { API_URLS } from '../../../common/constants'; + +export enum UptimePage { + Overview = 'Overview', + MappingError = 'MappingError', + Monitor = 'Monitor', + MonitorAdd = 'AddMonitor', + MonitorEdit = 'EditMonitor', + MonitorManagement = 'MonitorManagement', + Settings = 'Settings', + Certificates = 'Certificates', + StepDetail = 'StepDetail', + SyntheticCheckStepsPage = 'SyntheticCheckStepsPage', + NotFound = '__not-found__', +} + +export const useUptimeTelemetry = (page?: UptimePage) => { + const { dateRangeStart, dateRangeEnd, autorefreshInterval, autorefreshIsPaused } = + useGetUrlParams(); + + useEffect(() => { + if (!apiService.http) throw new Error('Core http services are not defined'); + + const params = { + page, + autorefreshInterval: autorefreshInterval / 1000, // divide by 1000 to keep it in secs + dateStart: dateRangeStart, + dateEnd: dateRangeEnd, + autoRefreshEnabled: !autorefreshIsPaused, + }; + setTimeout(() => { + apiService.post(API_URLS.LOG_PAGE_VIEW, params); + }, 100); + }, [autorefreshInterval, autorefreshIsPaused, dateRangeEnd, dateRangeStart, page]); +}; diff --git a/x-pack/plugins/synthetics/public/hooks/use_url_params.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_url_params.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/hooks/use_url_params.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_url_params.test.tsx diff --git a/x-pack/plugins/synthetics/public/hooks/use_url_params.ts b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_url_params.ts similarity index 100% rename from x-pack/plugins/synthetics/public/hooks/use_url_params.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/hooks/use_url_params.ts diff --git a/x-pack/plugins/synthetics/public/lib/__mocks__/screenshot_ref.mock.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/__mocks__/screenshot_ref.mock.ts similarity index 93% rename from x-pack/plugins/synthetics/public/lib/__mocks__/screenshot_ref.mock.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/__mocks__/screenshot_ref.mock.ts index a95aa77371b23..c812a9c67b38d 100644 --- a/x-pack/plugins/synthetics/public/lib/__mocks__/screenshot_ref.mock.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/__mocks__/screenshot_ref.mock.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ScreenshotRefImageData } from '../../../common/runtime_types'; +import { ScreenshotRefImageData } from '../../../../common/runtime_types'; export const mockRef: ScreenshotRefImageData = { maxSteps: 1, diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/__mocks__/uptime_plugin_start_mock.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/__mocks__/uptime_plugin_start_mock.ts new file mode 100644 index 0000000000000..981e6a5e2ae3b --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/__mocks__/uptime_plugin_start_mock.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. + */ + +interface InputTimeRange { + from: string; + to: string; +} + +export const startPlugins = { + data: { + query: { + timefilter: { + timefilter: { + getTime: () => ({ to: 'now-15m', from: 'now-30m' }), + setTime: jest.fn(({ from, to }: InputTimeRange) => {}), + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/synthetics/public/lib/__mocks__/uptime_store.mock.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/__mocks__/uptime_store.mock.ts similarity index 94% rename from x-pack/plugins/synthetics/public/lib/__mocks__/uptime_store.mock.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/__mocks__/uptime_store.mock.ts index 01a11fd809fe8..18a8885b1dcdb 100644 --- a/x-pack/plugins/synthetics/public/lib/__mocks__/uptime_store.mock.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/__mocks__/uptime_store.mock.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../common/constants'; -import { DEFAULT_THROTTLING } from '../../../common/runtime_types'; +import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; +import { DEFAULT_THROTTLING } from '../../../../common/runtime_types'; import { AppState } from '../../state'; /** diff --git a/x-pack/plugins/synthetics/public/lib/__mocks__/use_composite_image.mock.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/__mocks__/use_composite_image.mock.ts similarity index 86% rename from x-pack/plugins/synthetics/public/lib/__mocks__/use_composite_image.mock.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/__mocks__/use_composite_image.mock.ts index c4ab83ae6ee5d..64bc0776b8207 100644 --- a/x-pack/plugins/synthetics/public/lib/__mocks__/use_composite_image.mock.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/__mocks__/use_composite_image.mock.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ScreenshotRefImageData } from '../../../common/runtime_types/ping/synthetics'; +import { ScreenshotRefImageData } from '../../../../common/runtime_types/ping/synthetics'; import * as composeScreenshotImages from '../../hooks/use_composite_image'; jest diff --git a/x-pack/plugins/synthetics/public/lib/__mocks__/ut_router_history.mock.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/__mocks__/ut_router_history.mock.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/__mocks__/ut_router_history.mock.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/__mocks__/ut_router_history.mock.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/adapters/framework/capabilities_adapter.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/adapters/framework/capabilities_adapter.ts new file mode 100644 index 0000000000000..c00be7d8b0ca0 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/adapters/framework/capabilities_adapter.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. + */ + +interface IntegratedAppsAvailability { + [key: string]: boolean; +} + +export const getIntegratedAppAvailability = ( + capabilities: any, + integratedApps: string[] +): IntegratedAppsAvailability => { + return integratedApps.reduce((supportedSolutions: IntegratedAppsAvailability, solutionName) => { + supportedSolutions[solutionName] = + capabilities[solutionName] && capabilities[solutionName].show === true; + return supportedSolutions; + }, {}); +}; diff --git a/x-pack/plugins/synthetics/public/lib/alert_types/alert_messages.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/alert_messages.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/lib/alert_types/alert_messages.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/alert_messages.tsx diff --git a/x-pack/plugins/synthetics/public/lib/alert_types/common.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/common.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/alert_types/common.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/common.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/duration_anomaly.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/duration_anomaly.tsx new file mode 100644 index 0000000000000..5f0c8c07172bb --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/duration_anomaly.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import moment from 'moment'; + +import { ALERT_END, ALERT_STATUS, ALERT_STATUS_ACTIVE, ALERT_REASON } from '@kbn/rule-data-utils'; + +import { ObservabilityRuleTypeModel } from '@kbn/observability-plugin/public'; +import { AlertTypeInitializer } from '.'; +import { getMonitorRouteFromMonitorId } from '../../../../common/utils/get_monitor_url'; +import { CLIENT_ALERT_TYPES } from '../../../../common/constants/alerts'; +import { DurationAnomalyTranslations } from '../../../../common/translations'; + +const { defaultActionMessage, defaultRecoveryMessage, description } = DurationAnomalyTranslations; +const DurationAnomalyAlert = React.lazy(() => import('./lazy_wrapper/duration_anomaly')); + +export const initDurationAnomalyAlertType: AlertTypeInitializer = ({ + core, + plugins, +}): ObservabilityRuleTypeModel => ({ + id: CLIENT_ALERT_TYPES.DURATION_ANOMALY, + iconClass: 'uptimeApp', + documentationUrl(docLinks) { + return `${docLinks.links.observability.uptimeDurationAnomaly}`; + }, + ruleParamsExpression: (params: unknown) => ( + + ), + description, + validate: () => ({ errors: {} }), + defaultActionMessage, + defaultRecoveryMessage, + requiresAppContext: true, + format: ({ fields }) => ({ + reason: fields[ALERT_REASON] || '', + link: getMonitorRouteFromMonitorId({ + monitorId: fields['monitor.id']!, + dateRangeEnd: fields[ALERT_STATUS] === ALERT_STATUS_ACTIVE ? 'now' : fields[ALERT_END]!, + dateRangeStart: moment(new Date(fields['anomaly.start']!)).subtract('5', 'm').toISOString(), + }), + }), +}); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/index.ts new file mode 100644 index 0000000000000..a4eef4c4b19b2 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/index.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 { CoreStart } from '@kbn/core/public'; +import { ObservabilityRuleTypeModel } from '@kbn/observability-plugin/public'; +import { RuleTypeModel } from '@kbn/triggers-actions-ui-plugin/public'; +import { initMonitorStatusAlertType } from './monitor_status'; +import { initTlsAlertType } from './tls'; +import { initTlsLegacyAlertType } from './tls_legacy'; +import { ClientPluginsStart } from '../../../plugin'; +import { initDurationAnomalyAlertType } from './duration_anomaly'; + +export type AlertTypeInitializer = (dependenies: { + core: CoreStart; + plugins: ClientPluginsStart; +}) => TAlertTypeModel; + +export const alertTypeInitializers: AlertTypeInitializer[] = [ + initMonitorStatusAlertType, + initTlsAlertType, + initDurationAnomalyAlertType, +]; + +export const legacyAlertTypeInitializers: Array> = [ + initTlsLegacyAlertType, +]; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/lazy_wrapper/duration_anomaly.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/lazy_wrapper/duration_anomaly.tsx new file mode 100644 index 0000000000000..44c10f615543e --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/lazy_wrapper/duration_anomaly.tsx @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { Provider as ReduxProvider } from 'react-redux'; +import { CoreStart } from '@kbn/core/public'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { store } from '../../../state'; +import { AnomalyAlertComponent } from '../../../components/overview/alerts/anomaly_alert/anomaly_alert'; +import { ClientPluginsStart } from '../../../../plugin'; +import { kibanaService } from '../../../state/kibana_service'; + +interface Props { + core: CoreStart; + plugins: ClientPluginsStart; + params: any; +} + +// eslint-disable-next-line import/no-default-export +export default function DurationAnomalyAlert({ core, plugins, params }: Props) { + kibanaService.core = core; + return ( + + + + + + ); +} diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/lazy_wrapper/monitor_status.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/lazy_wrapper/monitor_status.tsx new file mode 100644 index 0000000000000..dd41ab29ed080 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/lazy_wrapper/monitor_status.tsx @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { Provider as ReduxProvider } from 'react-redux'; +import { CoreStart } from '@kbn/core/public'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { store } from '../../../state'; +import { ClientPluginsStart } from '../../../../plugin'; +import { kibanaService } from '../../../state/kibana_service'; +import { AlertMonitorStatus } from '../../../components/overview/alerts/alerts_containers/alert_monitor_status'; +import { UptimeDataViewContextProvider } from '../../../contexts/uptime_data_view_context'; + +interface Props { + core: CoreStart; + plugins: ClientPluginsStart; + params: any; +} + +// eslint-disable-next-line import/no-default-export +export default function MonitorStatusAlert({ core, plugins, params }: Props) { + kibanaService.core = core; + return ( + + + + + + + + ); +} diff --git a/x-pack/plugins/synthetics/public/lib/alert_types/lazy_wrapper/tls_alert.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/lazy_wrapper/tls_alert.tsx similarity index 94% rename from x-pack/plugins/synthetics/public/lib/alert_types/lazy_wrapper/tls_alert.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/lazy_wrapper/tls_alert.tsx index f4136a079c8b0..2d1b8c9898475 100644 --- a/x-pack/plugins/synthetics/public/lib/alert_types/lazy_wrapper/tls_alert.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/lazy_wrapper/tls_alert.tsx @@ -10,7 +10,7 @@ import { Provider as ReduxProvider } from 'react-redux'; import { CoreStart } from '@kbn/core/public'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { store } from '../../../state'; -import { ClientPluginsStart } from '../../../apps/plugin'; +import { ClientPluginsStart } from '../../../../plugin'; import { AlertTls } from '../../../components/overview/alerts/alerts_containers/alert_tls'; import { kibanaService } from '../../../state/kibana_service'; diff --git a/x-pack/plugins/synthetics/public/lib/alert_types/lazy_wrapper/validate_monitor_status.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/lazy_wrapper/validate_monitor_status.ts similarity index 97% rename from x-pack/plugins/synthetics/public/lib/alert_types/lazy_wrapper/validate_monitor_status.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/lazy_wrapper/validate_monitor_status.ts index 881afb0d8eb52..68e88fdd8bf86 100644 --- a/x-pack/plugins/synthetics/public/lib/alert_types/lazy_wrapper/validate_monitor_status.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/lazy_wrapper/validate_monitor_status.ts @@ -12,7 +12,7 @@ import { AtomicStatusCheckParamsType, MonitorAvailabilityType, StatusCheckParamsType, -} from '../../../../common/runtime_types/alerts'; +} from '../../../../../common/runtime_types/alerts'; export function validateMonitorStatusParams(ruleParams: any): ValidationResult { const errors: Record = {}; diff --git a/x-pack/plugins/synthetics/public/lib/alert_types/monitor_status.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/monitor_status.test.ts similarity index 95% rename from x-pack/plugins/synthetics/public/lib/alert_types/monitor_status.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/monitor_status.test.ts index 2f67219ac1ae5..c4d02806b5913 100644 --- a/x-pack/plugins/synthetics/public/lib/alert_types/monitor_status.test.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/monitor_status.test.ts @@ -202,7 +202,8 @@ describe('monitor status alert type', () => { }) ).toMatchInlineSnapshot(` Object { - "defaultActionMessage": "Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} from {{state.observerLocation}} {{{state.statusMessage}}} The latest error message is {{{state.latestErrorMessage}}}", + "defaultActionMessage": "Monitor {{context.monitorName}} with url {{{context.monitorUrl}}} from {{context.observerLocation}} {{{context.statusMessage}}} The latest error message is {{{context.latestErrorMessage}}}", + "defaultRecoveryMessage": "Alert for monitor {{context.monitorName}} with url {{{context.monitorUrl}}} from {{context.observerLocation}} has recovered", "description": "Alert when a monitor is down or an availability threshold is breached.", "documentationUrl": [Function], "format": [Function], diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/monitor_status.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/monitor_status.tsx new file mode 100644 index 0000000000000..f7584cb04320e --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/monitor_status.tsx @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import moment from 'moment'; + +import { + ALERT_END, + ALERT_START, + ALERT_STATUS, + ALERT_STATUS_ACTIVE, + ALERT_REASON, +} from '@kbn/rule-data-utils'; + +import { ObservabilityRuleTypeModel } from '@kbn/observability-plugin/public'; +import { ValidationResult } from '@kbn/triggers-actions-ui-plugin/public'; +import { AlertTypeInitializer } from '.'; +import { getMonitorRouteFromMonitorId } from '../../../../common/utils/get_monitor_url'; +import { MonitorStatusTranslations } from '../../../../common/translations'; +import { CLIENT_ALERT_TYPES } from '../../../../common/constants/alerts'; + +const { defaultActionMessage, defaultRecoveryMessage, description } = MonitorStatusTranslations; + +const MonitorStatusAlert = React.lazy(() => import('./lazy_wrapper/monitor_status')); + +let validateFunc: (ruleParams: any) => ValidationResult; + +export const initMonitorStatusAlertType: AlertTypeInitializer = ({ + core, + plugins, +}): ObservabilityRuleTypeModel => ({ + id: CLIENT_ALERT_TYPES.MONITOR_STATUS, + description, + iconClass: 'uptimeApp', + documentationUrl(docLinks) { + return `${docLinks.links.observability.monitorStatus}`; + }, + ruleParamsExpression: (params: any) => ( + + ), + validate: (ruleParams: any) => { + if (!validateFunc) { + (async function loadValidate() { + const { validateMonitorStatusParams } = await import( + './lazy_wrapper/validate_monitor_status' + ); + validateFunc = validateMonitorStatusParams; + })(); + } + return validateFunc ? validateFunc(ruleParams) : ({} as ValidationResult); + }, + defaultActionMessage, + defaultRecoveryMessage, + requiresAppContext: false, + format: ({ fields }) => ({ + reason: fields[ALERT_REASON] || '', + link: getMonitorRouteFromMonitorId({ + monitorId: fields['monitor.id']!, + dateRangeEnd: fields[ALERT_STATUS] === ALERT_STATUS_ACTIVE ? 'now' : fields[ALERT_END]!, + dateRangeStart: moment(new Date(fields[ALERT_START]!)).subtract('5', 'm').toISOString(), + filters: { + 'observer.geo.name': [fields['observer.geo.name'][0]], + }, + }), + }), +}); diff --git a/x-pack/plugins/synthetics/public/lib/alert_types/tls.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/tls.tsx similarity index 77% rename from x-pack/plugins/synthetics/public/lib/alert_types/tls.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/tls.tsx index 1ecfaefaa1797..b9ab025ecc021 100644 --- a/x-pack/plugins/synthetics/public/lib/alert_types/tls.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/tls.tsx @@ -8,13 +8,13 @@ import React from 'react'; import { ALERT_REASON } from '@kbn/rule-data-utils'; import { ObservabilityRuleTypeModel } from '@kbn/observability-plugin/public'; -import { CLIENT_ALERT_TYPES } from '../../../common/constants/alerts'; -import { TlsTranslations } from '../../../common/translations'; +import { CLIENT_ALERT_TYPES } from '../../../../common/constants/alerts'; +import { TlsTranslations } from '../../../../common/translations'; import { AlertTypeInitializer } from '.'; -import { CERTIFICATES_ROUTE } from '../../../common/constants/ui'; +import { CERTIFICATES_ROUTE } from '../../../../common/constants/ui'; -const { defaultActionMessage, description } = TlsTranslations; +const { defaultActionMessage, defaultRecoveryMessage, description } = TlsTranslations; const TLSAlert = React.lazy(() => import('./lazy_wrapper/tls_alert')); export const initTlsAlertType: AlertTypeInitializer = ({ core, @@ -29,6 +29,7 @@ export const initTlsAlertType: AlertTypeInitializer = ({ description, validate: () => ({ errors: {} }), defaultActionMessage, + defaultRecoveryMessage, requiresAppContext: false, format: ({ fields }) => ({ reason: fields[ALERT_REASON] || '', diff --git a/x-pack/plugins/synthetics/public/lib/alert_types/tls_legacy.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/tls_legacy.tsx similarity index 88% rename from x-pack/plugins/synthetics/public/lib/alert_types/tls_legacy.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/tls_legacy.tsx index cce2e3d011bfa..221bfaeb3375c 100644 --- a/x-pack/plugins/synthetics/public/lib/alert_types/tls_legacy.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/tls_legacy.tsx @@ -7,8 +7,8 @@ import React from 'react'; import { RuleTypeModel } from '@kbn/triggers-actions-ui-plugin/public'; -import { CLIENT_ALERT_TYPES } from '../../../common/constants/alerts'; -import { TlsTranslationsLegacy } from '../../../common/translations'; +import { CLIENT_ALERT_TYPES } from '../../../../common/constants/alerts'; +import { TlsTranslationsLegacy } from '../../../../common/translations'; import { AlertTypeInitializer } from '.'; const { defaultActionMessage, description } = TlsTranslationsLegacy; diff --git a/x-pack/plugins/synthetics/public/lib/formatting.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/formatting.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/formatting.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/formatting.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/charts/get_chart_date_label.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/charts/get_chart_date_label.ts similarity index 95% rename from x-pack/plugins/synthetics/public/lib/helper/charts/get_chart_date_label.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/charts/get_chart_date_label.ts index 4852e6ceade11..f7b8d315577f9 100644 --- a/x-pack/plugins/synthetics/public/lib/helper/charts/get_chart_date_label.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/charts/get_chart_date_label.ts @@ -7,7 +7,7 @@ import { isWithinCurrentDate } from './is_within_current_date'; import { getLabelFormat } from './get_label_format'; -import { CHART_FORMAT_LIMITS } from '../../../../common/constants'; +import { CHART_FORMAT_LIMITS } from '../../../../../common/constants'; /** * Generates an appropriate date formatting string intended for the y-axis diff --git a/x-pack/plugins/synthetics/public/lib/helper/charts/get_label_format.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/charts/get_label_format.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/charts/get_label_format.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/charts/get_label_format.test.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/charts/get_label_format.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/charts/get_label_format.ts similarity index 95% rename from x-pack/plugins/synthetics/public/lib/helper/charts/get_label_format.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/charts/get_label_format.ts index 3444e05b59127..298786cebaeb8 100644 --- a/x-pack/plugins/synthetics/public/lib/helper/charts/get_label_format.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/charts/get_label_format.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { CHART_FORMAT_LIMITS } from '../../../../common/constants'; +import { CHART_FORMAT_LIMITS } from '../../../../../common/constants'; const { EIGHT_MINUTES, FOUR_YEARS, THIRTY_SIX_HOURS, THREE_WEEKS, SIX_MONTHS, NINE_DAYS } = CHART_FORMAT_LIMITS; diff --git a/x-pack/plugins/synthetics/public/lib/helper/charts/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/charts/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/charts/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/charts/index.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/charts/is_within_current_date.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/charts/is_within_current_date.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/charts/is_within_current_date.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/charts/is_within_current_date.test.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/charts/is_within_current_date.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/charts/is_within_current_date.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/charts/is_within_current_date.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/charts/is_within_current_date.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/compose_screenshot_images.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/compose_screenshot_images.test.ts similarity index 96% rename from x-pack/plugins/synthetics/public/lib/helper/compose_screenshot_images.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/compose_screenshot_images.test.ts index 0bf809d4e7a40..c749433bb84e0 100644 --- a/x-pack/plugins/synthetics/public/lib/helper/compose_screenshot_images.test.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/compose_screenshot_images.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ScreenshotRefImageData } from '../../../common/runtime_types/ping/synthetics'; +import { ScreenshotRefImageData } from '../../../../common/runtime_types/ping/synthetics'; import { composeScreenshotRef } from './compose_screenshot_images'; describe('composeScreenshotRef', () => { diff --git a/x-pack/plugins/synthetics/public/lib/helper/compose_screenshot_images.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/compose_screenshot_images.ts similarity index 97% rename from x-pack/plugins/synthetics/public/lib/helper/compose_screenshot_images.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/compose_screenshot_images.ts index 60cd248c1487a..86c7a001b95ab 100644 --- a/x-pack/plugins/synthetics/public/lib/helper/compose_screenshot_images.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/compose_screenshot_images.ts @@ -8,7 +8,7 @@ import { isScreenshotBlockDoc, ScreenshotRefImageData, -} from '../../../common/runtime_types/ping/synthetics'; +} from '../../../../common/runtime_types/ping/synthetics'; import { ScreenshotBlockCache } from '../../state/reducers/synthetics'; /** diff --git a/x-pack/plugins/synthetics/public/lib/helper/convert_measurements.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/convert_measurements.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/convert_measurements.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/convert_measurements.test.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/convert_measurements.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/convert_measurements.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/convert_measurements.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/convert_measurements.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/enzyme_helpers.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/enzyme_helpers.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/enzyme_helpers.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/enzyme_helpers.tsx diff --git a/x-pack/plugins/synthetics/public/lib/helper/get_title.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/get_title.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/get_title.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/get_title.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/helper_with_redux.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/helper_with_redux.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/helper_with_redux.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/helper_with_redux.tsx diff --git a/x-pack/plugins/synthetics/public/lib/helper/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/index.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/observability_integration/add_base_path.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/add_base_path.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/observability_integration/add_base_path.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/add_base_path.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/observability_integration/build_href.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/build_href.ts similarity index 95% rename from x-pack/plugins/synthetics/public/lib/helper/observability_integration/build_href.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/build_href.ts index 491219bae5152..93e08b92fbafc 100644 --- a/x-pack/plugins/synthetics/public/lib/helper/observability_integration/build_href.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/build_href.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Ping } from '../../../../common/runtime_types'; +import { Ping } from '../../../../../common/runtime_types'; /** * Builds URLs to the designated features by extracting values from the provided diff --git a/x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_apm_href.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_apm_href.test.ts similarity index 96% rename from x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_apm_href.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_apm_href.test.ts index db946fb246964..146fc498b3dcc 100644 --- a/x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_apm_href.test.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_apm_href.test.ts @@ -6,7 +6,7 @@ */ import { getLegacyApmHref } from './get_apm_href'; -import { MonitorSummary, makePing } from '../../../../common/runtime_types'; +import { MonitorSummary, makePing } from '../../../../../common/runtime_types'; describe('getLegacyApmHref', () => { let summary: MonitorSummary; diff --git a/x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_apm_href.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_apm_href.ts similarity index 91% rename from x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_apm_href.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_apm_href.ts index fc5785ceecba1..f05f1ccb2de4f 100644 --- a/x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_apm_href.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_apm_href.ts @@ -6,7 +6,7 @@ */ import { addBasePath } from './add_base_path'; -import { MonitorSummary } from '../../../../common/runtime_types'; +import { MonitorSummary } from '../../../../../common/runtime_types'; export const getLegacyApmHref = ( summary: MonitorSummary, diff --git a/x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_infra_href.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_infra_href.test.ts similarity index 98% rename from x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_infra_href.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_infra_href.test.ts index 9a7cc6e7940ed..970ac86f3777d 100644 --- a/x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_infra_href.test.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_infra_href.test.ts @@ -6,7 +6,7 @@ */ import { getInfraContainerHref, getInfraKubernetesHref, getInfraIpHref } from './get_infra_href'; -import { MonitorSummary, makePing, Ping } from '../../../../common/runtime_types'; +import { MonitorSummary, makePing, Ping } from '../../../../../common/runtime_types'; describe('getInfraHref', () => { let summary: MonitorSummary; diff --git a/x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_infra_href.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_infra_href.ts similarity index 96% rename from x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_infra_href.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_infra_href.ts index 1603ef9cf170a..e1cf922983d21 100644 --- a/x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_infra_href.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_infra_href.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { MonitorSummary, Ping } from '../../../../common/runtime_types'; +import { MonitorSummary, Ping } from '../../../../../common/runtime_types'; import { addBasePath } from './add_base_path'; import { buildHref } from './build_href'; diff --git a/x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_logging_href.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_logging_href.test.ts similarity index 98% rename from x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_logging_href.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_logging_href.test.ts index 3c778400fae71..791d19a7949c2 100644 --- a/x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_logging_href.test.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_logging_href.test.ts @@ -10,7 +10,7 @@ import { getLoggingKubernetesHref, getLoggingIpHref, } from './get_logging_href'; -import { MonitorSummary, makePing } from '../../../../common/runtime_types'; +import { MonitorSummary, makePing } from '../../../../../common/runtime_types'; describe('getLoggingHref', () => { let summary: MonitorSummary; diff --git a/x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_logging_href.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_logging_href.ts similarity index 96% rename from x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_logging_href.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_logging_href.ts index a3e6108b7efe8..947c50f1f0320 100644 --- a/x-pack/plugins/synthetics/public/lib/helper/observability_integration/get_logging_href.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/get_logging_href.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { MonitorSummary, Ping } from '../../../../common/runtime_types'; +import { MonitorSummary, Ping } from '../../../../../common/runtime_types'; import { addBasePath } from './add_base_path'; import { buildHref } from './build_href'; diff --git a/x-pack/plugins/synthetics/public/lib/helper/observability_integration/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/observability_integration/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/observability_integration/index.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/parse_search.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/parse_search.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/parse_search.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/parse_search.test.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/parse_search.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/parse_search.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/parse_search.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/parse_search.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/rtl_helpers.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/rtl_helpers.tsx new file mode 100644 index 0000000000000..3a1fed73b90d6 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/rtl_helpers.tsx @@ -0,0 +1,360 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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, { ReactElement, ReactNode } from 'react'; +import { of } from 'rxjs'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { + render as reactTestLibRender, + MatcherFunction, + RenderOptions, +} from '@testing-library/react'; +import { Router, Route } from 'react-router-dom'; +import { merge } from 'lodash'; +import { createMemoryHistory, History } from 'history'; +import { CoreStart } from '@kbn/core/public'; +import { I18nProvider } from '@kbn/i18n-react'; +import { EuiPageTemplate } from '@elastic/eui'; +import { coreMock } from '@kbn/core/public/mocks'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { configure } from '@testing-library/dom'; +import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; +import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; +import { KibanaContextProvider, KibanaServices } from '@kbn/kibana-react-plugin/public'; +import { triggersActionsUiMock } from '@kbn/triggers-actions-ui-plugin/public/mocks'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { mockState } from '../__mocks__/uptime_store.mock'; +import { MountWithReduxProvider } from './helper_with_redux'; +import { AppState } from '../../state'; +import { stringifyUrlParams } from '../../../apps/synthetics/utils/url_params/stringify_url_params'; +import { ClientPluginsStart } from '../../../plugin'; +import { UptimeRefreshContextProvider, UptimeStartupPluginsContextProvider } from '../../contexts'; +import { kibanaService } from '../../state/kibana_service'; + +type DeepPartial = { + [P in keyof T]?: DeepPartial; +}; + +interface KibanaProps { + services?: KibanaServices; +} + +export interface KibanaProviderOptions { + core?: DeepPartial & Partial; + kibanaProps?: KibanaProps; +} + +interface MockKibanaProviderProps extends KibanaProviderOptions { + children: ReactElement | ReactNode; +} + +interface MockRouterProps extends MockKibanaProviderProps { + history?: History; + path?: string; +} + +type Url = + | string + | { + path: string; + queryParams: Record; + }; + +interface RenderRouterOptions extends KibanaProviderOptions { + history?: History; + renderOptions?: Omit; + state?: Partial | DeepPartial; + url?: Url; + path?: string; +} + +function getSetting(key: string): T { + return 'MMM D, YYYY @ HH:mm:ss.SSS' as unknown as T; +} + +function setSetting$(key: string): T { + return of('MMM D, YYYY @ HH:mm:ss.SSS') as unknown as T; +} + +const createMockStore = () => { + let store: Record = {}; + return { + get: jest.fn().mockImplementation((key) => store[key]), + set: jest.fn().mockImplementation((key, value) => (store[key] = value)), + remove: jest.fn().mockImplementation((key: string) => delete store[key]), + clear: jest.fn().mockImplementation(() => (store = {})), + }; +}; + +const mockAppUrls: Record = { + uptime: '/app/uptime', + observability: '/app/observability', + '/home#/tutorial/uptimeMonitors': '/home#/tutorial/uptimeMonitors', +}; + +/* default mock core */ +export const defaultCore = coreMock.createStart(); +export const mockCore: () => Partial = () => { + const core: Partial = { + ...defaultCore, + application: { + ...defaultCore.application, + getUrlForApp: (app: string) => mockAppUrls[app], + navigateToUrl: jest.fn(), + capabilities: { + ...defaultCore.application.capabilities, + uptime: { + 'alerting:save': true, + configureSettings: true, + save: true, + show: true, + }, + actions: { + save: true, + }, + }, + }, + uiSettings: { + ...defaultCore.uiSettings, + get: getSetting, + get$: setSetting$, + }, + usageCollection: { + reportUiCounter: () => {}, + }, + triggersActionsUi: triggersActionsUiMock.createStart(), + storage: createMockStore(), + data: dataPluginMock.createStartContract(), + observability: { + useRulesLink: () => ({ href: 'newRuleLink' }), + navigation: { + // @ts-ignore + PageTemplate: EuiPageTemplate, + }, + ExploratoryViewEmbeddable: () =>
Embeddable exploratory view
, + }, + }; + + return core; +}; + +/* Mock Provider Components */ +export function MockKibanaProvider({ + children, + core, + kibanaProps, +}: MockKibanaProviderProps) { + const coreOptions = merge({}, mockCore(), core); + + kibanaService.core = coreOptions as any; + + return ( + + + + + {children} + + + + + ); +} + +export function MockRouter({ + children, + core, + path, + history = createMemoryHistory(), + kibanaProps, +}: MockRouterProps) { + return ( + + + {children} + + + ); +} +configure({ testIdAttribute: 'data-test-subj' }); + +export const MockRedux = ({ + state, + history = createMemoryHistory(), + children, + path, +}: { + state: Partial; + history?: History; + children: React.ReactNode; + path?: string; + useRealStore?: boolean; +}) => { + const testState: AppState = { + ...mockState, + ...state, + }; + + return ( + + + {children} + + + ); +}; + +export function WrappedHelper({ + children, + core, + kibanaProps, + state, + url, + useRealStore, + path, + history = createMemoryHistory(), +}: RenderRouterOptions & { children: ReactElement; useRealStore?: boolean }) { + const testState: AppState = merge({}, mockState, state); + + return ( + + + {children} + + + ); +} + +/* Custom react testing library render */ +export function render( + ui: ReactElement, + { + history = createMemoryHistory(), + core, + kibanaProps, + renderOptions, + state, + url, + path, + useRealStore, + }: RenderRouterOptions & { useRealStore?: boolean } = {} +) { + if (url) { + history = getHistoryFromUrl(url); + } + + return { + ...reactTestLibRender( + + {ui} + , + renderOptions + ), + history, + }; +} + +const getHistoryFromUrl = (url: Url) => { + if (typeof url === 'string') { + return createMemoryHistory({ + initialEntries: [url], + }); + } + + return createMemoryHistory({ + initialEntries: [url.path + stringifyUrlParams(url.queryParams)], + }); +}; + +const forNearestTag = + (tag: string) => + (getByText: (f: MatcherFunction) => HTMLElement | null) => + (text: string): HTMLElement | null => + getByText((_content: string, node: Element | null) => { + if (!node) return false; + const noOtherButtonHasText = Array.from(node.children).every( + (child) => child && (child.textContent !== text || child.tagName.toLowerCase() !== tag) + ); + return ( + noOtherButtonHasText && node.textContent === text && node.tagName.toLowerCase() === tag + ); + }); + +// This function allows us to query for the nearest button with test +// no matter whether it has nested tags or not (as EuiButton elements do). +export const forNearestButton = forNearestTag('button'); + +export const forNearestAnchor = forNearestTag('a'); + +export const makeUptimePermissionsCore = ( + permissions: Partial<{ + 'alerting:save': boolean; + configureSettings: boolean; + save: boolean; + show: boolean; + }> +) => { + return { + application: { + capabilities: { + uptime: { + 'alerting:save': true, + configureSettings: true, + save: true, + show: true, + ...permissions, + }, + }, + }, + }; +}; + +// This function filters out the queried elements which appear only +// either on mobile or desktop. +// +// It does so by filtering those with the class passed as the `classWrapper`. +// For mobile, we filter classes which tell elements to be hidden on desktop. +// For desktop, we do the opposite. +// +// We have this function because EUI will manipulate the visibility of some +// elements through pure CSS, which we can't assert on tests. Therefore, +// we look for the corresponding class wrapper. +const finderWithClassWrapper = + (classWrapper: string) => + ( + getterFn: (f: MatcherFunction) => HTMLElement | null, + customAttribute?: keyof Element | keyof HTMLElement + ) => + (text: string): HTMLElement | null => + getterFn((_content: string, node: Element | null) => { + if (!node) return false; + // There are actually properties that are not in Element but which + // appear on the `node`, so we must cast the customAttribute as a keyof Element + const content = node[(customAttribute as keyof Element) ?? 'innerHTML']; + if (content === text && wrappedInClass(node, classWrapper)) return true; + return false; + }); + +const wrappedInClass = (element: HTMLElement | Element, classWrapper: string): boolean => { + if (element.className.includes(classWrapper)) return true; + if (element.parentElement) return wrappedInClass(element.parentElement, classWrapper); + return false; +}; + +export const forMobileOnly = finderWithClassWrapper('hideForDesktop'); +export const forDesktopOnly = finderWithClassWrapper('hideForMobile'); diff --git a/x-pack/plugins/synthetics/public/lib/helper/series_has_down_values.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/series_has_down_values.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/series_has_down_values.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/series_has_down_values.test.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/series_has_down_values.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/series_has_down_values.ts similarity index 85% rename from x-pack/plugins/synthetics/public/lib/helper/series_has_down_values.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/series_has_down_values.ts index 7090da1430bc5..d5ff1fc4e0715 100644 --- a/x-pack/plugins/synthetics/public/lib/helper/series_has_down_values.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/series_has_down_values.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { HistogramPoint } from '../../../common/runtime_types'; +import { HistogramPoint } from '../../../../common/runtime_types'; export const seriesHasDownValues = (series: HistogramPoint[] | null): boolean => { return series ? series.some((point) => !!point.down) : false; diff --git a/x-pack/plugins/synthetics/public/lib/helper/spy_use_fetcher.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/spy_use_fetcher.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/spy_use_fetcher.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/spy_use_fetcher.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/test_helpers.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/test_helpers.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/test_helpers.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/test_helpers.ts diff --git a/x-pack/plugins/synthetics/public/lib/helper/url_params/__snapshots__/get_supported_url_params.test.ts.snap b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/url_params/__snapshots__/get_supported_url_params.test.ts.snap similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/url_params/__snapshots__/get_supported_url_params.test.ts.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/url_params/__snapshots__/get_supported_url_params.test.ts.snap diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/url_params/get_supported_url_params.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/url_params/get_supported_url_params.test.ts new file mode 100644 index 0000000000000..14b1ce22f9bee --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/url_params/get_supported_url_params.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 DateMath from '@kbn/datemath'; +import { getSupportedUrlParams } from './get_supported_url_params'; +import { CLIENT_DEFAULTS } from '../../../../../common/constants'; + +describe('getSupportedUrlParams', () => { + let dateMathSpy: any; + const MOCK_DATE_VALUE = 20; + + beforeEach(() => { + dateMathSpy = jest.spyOn(DateMath, 'parse'); + dateMathSpy.mockReturnValue(MOCK_DATE_VALUE); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('returns custom values', () => { + const customValues = { + autorefreshInterval: '23', + autorefreshIsPaused: 'false', + dateRangeStart: 'now-15m', + dateRangeEnd: 'now', + monitorListPageIndex: '23', + monitorListPageSize: '50', + monitorListSortDirection: 'desc', + monitorListSortField: 'monitor.status', + search: 'monitor.status: down', + selectedPingStatus: 'up', + }; + const result = getSupportedUrlParams(customValues); + expect(result).toMatchSnapshot(); + }); + + it('returns default values', () => { + const { + AUTOREFRESH_INTERVAL, + AUTOREFRESH_IS_PAUSED, + DATE_RANGE_START, + DATE_RANGE_END, + FILTERS, + SEARCH, + STATUS_FILTER, + } = CLIENT_DEFAULTS; + const result = getSupportedUrlParams({}); + expect(result).toMatchSnapshot(); + expect(result).toEqual({ + absoluteDateRangeStart: MOCK_DATE_VALUE, + absoluteDateRangeEnd: MOCK_DATE_VALUE, + autorefreshInterval: AUTOREFRESH_INTERVAL, + autorefreshIsPaused: AUTOREFRESH_IS_PAUSED, + dateRangeStart: DATE_RANGE_START, + dateRangeEnd: DATE_RANGE_END, + excludedFilters: '', + filters: FILTERS, + focusConnectorField: false, + pagination: undefined, + search: SEARCH, + statusFilter: STATUS_FILTER, + query: '', + }); + }); + + it('returns the first item for string arrays', () => { + const result = getSupportedUrlParams({ + dateRangeStart: ['now-18d', 'now-11d', 'now-5m'], + }); + expect(result).toMatchSnapshot(); + }); + + it('provides defaults for undefined values', () => { + const result = getSupportedUrlParams({ + dateRangeStart: undefined, + }); + expect(result).toMatchSnapshot(); + }); + + it('provides defaults for empty string array values', () => { + const result = getSupportedUrlParams({ + dateRangeStart: [], + }); + expect(result).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/url_params/get_supported_url_params.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/url_params/get_supported_url_params.ts new file mode 100644 index 0000000000000..246db87b983fe --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/url_params/get_supported_url_params.ts @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { parseIsPaused } from '../../../../apps/synthetics/utils/url_params/parse_is_paused'; +import { parseUrlInt } from '../../../../apps/synthetics/utils/url_params/parse_url_int'; +import { CLIENT_DEFAULTS } from '../../../../../common/constants'; +import { parseAbsoluteDate } from '../../../../apps/synthetics/utils/url_params/parse_absolute_date'; + +export interface UptimeUrlParams { + absoluteDateRangeStart: number; + absoluteDateRangeEnd: number; + autorefreshInterval: number; + autorefreshIsPaused: boolean; + dateRangeStart: string; + dateRangeEnd: string; + pagination?: string; + filters: string; + excludedFilters: string; + search: string; + statusFilter: string; + focusConnectorField?: boolean; + query?: string; +} + +const { + ABSOLUTE_DATE_RANGE_START, + ABSOLUTE_DATE_RANGE_END, + AUTOREFRESH_INTERVAL, + AUTOREFRESH_IS_PAUSED, + DATE_RANGE_START, + DATE_RANGE_END, + SEARCH, + FILTERS, + STATUS_FILTER, +} = CLIENT_DEFAULTS; + +/** + * Gets the current URL values for the application. If no item is present + * for the URL, a default value is supplied. + * + * @param params A set of key-value pairs where the value is either + * undefined or a string/string array. If a string array is passed, + * only the first item is chosen. Support for lists in the URL will + * require further development. + */ +export const getSupportedUrlParams = (params: { + [key: string]: string | string[] | undefined | null; +}): UptimeUrlParams => { + const filteredParams: { [key: string]: string | undefined } = {}; + Object.keys(params).forEach((key) => { + let value: string | undefined; + if (params[key] === undefined) { + value = undefined; + } else if (Array.isArray(params[key])) { + // @ts-ignore this must be an array, and it's ok if the + // 0th element is undefined + value = params[key][0]; + } else { + // @ts-ignore this will not be an array because the preceding + // block tests for that + value = params[key]; + } + filteredParams[key] = value; + }); + + const { + autorefreshInterval, + autorefreshIsPaused, + dateRangeStart, + dateRangeEnd, + filters, + excludedFilters, + search, + statusFilter, + pagination, + focusConnectorField, + query, + } = filteredParams; + + return { + pagination, + absoluteDateRangeStart: parseAbsoluteDate( + dateRangeStart || DATE_RANGE_START, + ABSOLUTE_DATE_RANGE_START + ), + absoluteDateRangeEnd: parseAbsoluteDate( + dateRangeEnd || DATE_RANGE_END, + ABSOLUTE_DATE_RANGE_END, + { roundUp: true } + ), + autorefreshInterval: parseUrlInt(autorefreshInterval, AUTOREFRESH_INTERVAL), + autorefreshIsPaused: parseIsPaused(autorefreshIsPaused, AUTOREFRESH_IS_PAUSED), + dateRangeStart: dateRangeStart || DATE_RANGE_START, + dateRangeEnd: dateRangeEnd || DATE_RANGE_END, + filters: filters || FILTERS, + excludedFilters: excludedFilters || '', + search: search || SEARCH, + statusFilter: statusFilter || STATUS_FILTER, + focusConnectorField: !!focusConnectorField, + query: query || '', + }; +}; diff --git a/x-pack/plugins/synthetics/public/lib/helper/url_params/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/url_params/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/helper/url_params/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/helper/url_params/index.ts diff --git a/x-pack/plugins/synthetics/public/lib/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/lib/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/lib/index.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/lib.ts b/x-pack/plugins/synthetics/public/legacy_uptime/lib/lib.ts new file mode 100644 index 0000000000000..a846cdeb99fb6 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/lib.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. + */ + +import { ChromeBadge } from '@kbn/core/public'; +export type UMUpdateBadge = (badge?: ChromeBadge) => void; diff --git a/x-pack/plugins/synthetics/public/pages/certificates.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/certificates.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/pages/certificates.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/certificates.test.tsx diff --git a/x-pack/plugins/synthetics/public/pages/certificates.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/certificates.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/pages/certificates.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/certificates.tsx diff --git a/x-pack/plugins/synthetics/public/pages/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/pages/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/pages/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/index.ts diff --git a/x-pack/plugins/synthetics/public/pages/mapping_error.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/mapping_error.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/pages/mapping_error.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/mapping_error.tsx diff --git a/x-pack/plugins/synthetics/public/pages/monitor.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/pages/monitor.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor.test.tsx diff --git a/x-pack/plugins/synthetics/public/pages/monitor.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/pages/monitor.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor.tsx diff --git a/x-pack/plugins/synthetics/public/pages/monitor_management/action_bar_portal_node.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/action_bar_portal_node.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/pages/monitor_management/action_bar_portal_node.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/action_bar_portal_node.tsx diff --git a/x-pack/plugins/synthetics/public/pages/monitor_management/add_monitor.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/add_monitor.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/pages/monitor_management/add_monitor.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/add_monitor.tsx index 40283f1331086..5979c7ed4767a 100644 --- a/x-pack/plugins/synthetics/public/pages/monitor_management/add_monitor.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/add_monitor.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { useTrackPageview } from '@kbn/observability-plugin/public'; -import { ScheduleUnit } from '../../../common/runtime_types'; +import { ScheduleUnit } from '../../../../common/runtime_types'; import { SyntheticsProviders } from '../../components/fleet_package/contexts'; import { Loader } from '../../components/monitor_management/loader/loader'; import { MonitorConfig } from '../../components/monitor_management/monitor_config/monitor_config'; diff --git a/x-pack/plugins/synthetics/public/pages/monitor_management/bottom_bar.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/bottom_bar.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/pages/monitor_management/bottom_bar.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/bottom_bar.tsx diff --git a/x-pack/plugins/synthetics/public/pages/monitor_management/content.ts b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/content.ts similarity index 100% rename from x-pack/plugins/synthetics/public/pages/monitor_management/content.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/content.ts diff --git a/x-pack/plugins/synthetics/public/pages/monitor_management/edit_monitor.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/edit_monitor.tsx similarity index 92% rename from x-pack/plugins/synthetics/public/pages/monitor_management/edit_monitor.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/edit_monitor.tsx index 5c8c85d7c616d..d0397de8be960 100644 --- a/x-pack/plugins/synthetics/public/pages/monitor_management/edit_monitor.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/edit_monitor.tsx @@ -8,11 +8,11 @@ import React from 'react'; import { useParams } from 'react-router-dom'; import { useTrackPageview, FETCH_STATUS, useFetcher } from '@kbn/observability-plugin/public'; -import { MonitorFields } from '../../../common/runtime_types'; +import { MonitorFields } from '../../../../common/runtime_types'; import { EditMonitorConfig } from '../../components/monitor_management/edit_monitor_config'; import { Loader } from '../../components/monitor_management/loader/loader'; import { getMonitor } from '../../state/api'; -import { DecryptedSyntheticsMonitorSavedObject } from '../../../common/types'; +import { DecryptedSyntheticsMonitorSavedObject } from '../../../../common/types'; import { useLocations } from '../../components/monitor_management/hooks/use_locations'; import { useMonitorManagementBreadcrumbs } from './use_monitor_management_breadcrumbs'; import { @@ -31,7 +31,7 @@ export const EditMonitorPage: React.FC = () => { const { data, status } = useFetcher< Promise >(() => { - return getMonitor({ id: Buffer.from(monitorId, 'base64').toString('utf8') }); + return getMonitor({ id: monitorId }); }, [monitorId]); const monitor = data?.attributes as MonitorFields; diff --git a/x-pack/plugins/synthetics/public/pages/monitor_management/monitor_management.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/monitor_management.tsx similarity index 84% rename from x-pack/plugins/synthetics/public/pages/monitor_management/monitor_management.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/monitor_management.tsx index 52453e66d858a..ba243388c8a3b 100644 --- a/x-pack/plugins/synthetics/public/pages/monitor_management/monitor_management.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/monitor_management.tsx @@ -7,11 +7,9 @@ import React, { useEffect, useRef, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { useDispatch, useSelector } from 'react-redux'; +import { useSelector } from 'react-redux'; import { EuiCallOut, EuiButton, EuiSpacer, EuiLink } from '@elastic/eui'; import { useTrackPageview } from '@kbn/observability-plugin/public'; -import { ConfigKey } from '../../../common/runtime_types'; -import { getMonitors } from '../../state/actions'; import { monitorManagementListSelector } from '../../state/selectors'; import { useMonitorManagementBreadcrumbs } from './use_monitor_management_breadcrumbs'; import { MonitorListContainer } from '../../components/monitor_management/monitor_list/monitor_list_container'; @@ -20,12 +18,12 @@ import { useEnablement } from '../../components/monitor_management/hooks/use_ena import { useLocations } from '../../components/monitor_management/hooks/use_locations'; import { Loader } from '../../components/monitor_management/loader/loader'; import { ERROR_HEADING_LABEL } from './content'; +import { useMonitorList } from '../../components/monitor_management/hooks/use_monitor_list'; export const MonitorManagementPage: React.FC = () => { useTrackPageview({ app: 'uptime', path: 'manage-monitors' }); useTrackPageview({ app: 'uptime', path: 'manage-monitors', delay: 15000 }); useMonitorManagementBreadcrumbs(); - const dispatch = useDispatch(); const [shouldFocusEnablementButton, setShouldFocusEnablementButton] = useState(false); const { @@ -40,19 +38,6 @@ export const MonitorManagementPage: React.FC = () => { const isEnabledRef = useRef(isEnabled); - useEffect(() => { - if (monitorList.total === null) { - dispatch( - getMonitors({ - page: 1, // saved objects page index is base 1 - perPage: 10, - sortOrder: 'asc', - sortField: `${ConfigKey.NAME}.keyword`, - }) - ); - } - }, [dispatch, monitorList.total]); - useEffect(() => { if (!isEnabled && isEnabledRef.current === true) { /* shift focus to enable button when enable toggle disappears. Prevent @@ -62,10 +47,14 @@ export const MonitorManagementPage: React.FC = () => { isEnabledRef.current = Boolean(isEnabled); }, [isEnabled]); + const { pageState, dispatchPageAction } = useMonitorList(); + + const showEmptyState = isEnabled !== undefined && monitorList.total === 0; + return ( <> { ) : null} - {isEnabled || (!isEnabled && monitorList.total) ? : null} + - {isEnabled !== undefined && monitorList.total === 0 && ( - - )} + {showEmptyState && } ); }; diff --git a/x-pack/plugins/synthetics/public/pages/monitor_management/service_allowed_wrapper.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/service_allowed_wrapper.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/pages/monitor_management/service_allowed_wrapper.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/service_allowed_wrapper.test.tsx diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/service_allowed_wrapper.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/service_allowed_wrapper.tsx new file mode 100644 index 0000000000000..ff74fd97f0b34 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/service_allowed_wrapper.tsx @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiButton, EuiEmptyPrompt, EuiLoadingLogo } from '@elastic/eui'; +import { useSyntheticsServiceAllowed } from '../../components/monitor_management/hooks/use_service_allowed'; + +export const ServiceAllowedWrapper: React.FC = ({ children }) => { + const { isAllowed, signupUrl, loading } = useSyntheticsServiceAllowed(); + + // checking for explicit false + if (isAllowed === false) { + return ( + {MONITOR_MANAGEMENT_LABEL}} + body={

{PUBLIC_BETA_DESCRIPTION}

} + actions={[ + + {REQUEST_ACCESS_LABEL} + , + ]} + /> + ); + } + + return ( + <> + {loading && ( + } + title={

{LOADING_MONITOR_MANAGEMENT_LABEL}

} + /> + )} +
{children}
+ + ); +}; + +const REQUEST_ACCESS_LABEL = i18n.translate('xpack.synthetics.monitorManagement.requestAccess', { + defaultMessage: 'Request access', +}); + +export const MONITOR_MANAGEMENT_LABEL = i18n.translate('xpack.synthetics.monitorManagement.label', { + defaultMessage: 'Monitor Management', +}); + +const LOADING_MONITOR_MANAGEMENT_LABEL = i18n.translate( + 'xpack.synthetics.monitorManagement.loading.label', + { + defaultMessage: 'Loading Monitor Management', + } +); + +export const PUBLIC_BETA_DESCRIPTION = i18n.translate( + 'xpack.synthetics.monitorManagement.publicBetaDescription', + { + defaultMessage: + "We've got a brand new app on the way. In the meantime, we're excited to give you early access to our globally managed testing infrastructure. This will allow you to upload synthetic monitors using our new point and click script recorder and manage your monitors with a new UI.", + } +); diff --git a/x-pack/plugins/synthetics/public/pages/monitor_management/use_monitor_management_breadcrumbs.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/use_monitor_management_breadcrumbs.tsx similarity index 92% rename from x-pack/plugins/synthetics/public/pages/monitor_management/use_monitor_management_breadcrumbs.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/use_monitor_management_breadcrumbs.tsx index 03d07a122c730..2c7a4fe7a8259 100644 --- a/x-pack/plugins/synthetics/public/pages/monitor_management/use_monitor_management_breadcrumbs.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/use_monitor_management_breadcrumbs.tsx @@ -7,8 +7,8 @@ import { i18n } from '@kbn/i18n'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; -import { MONITOR_MANAGEMENT_ROUTE } from '../../../common/constants'; -import { PLUGIN } from '../../../common/constants/plugin'; +import { MONITOR_MANAGEMENT_ROUTE } from '../../../../common/constants'; +import { PLUGIN } from '../../../../common/constants/plugin'; export const useMonitorManagementBreadcrumbs = ({ isAddMonitor, diff --git a/x-pack/plugins/synthetics/public/pages/not_found.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/not_found.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/pages/not_found.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/not_found.test.tsx diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/pages/not_found.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/not_found.tsx new file mode 100644 index 0000000000000..c94a7a7a06b6a --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/pages/not_found.tsx @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiEmptyPrompt, + EuiPanel, + EuiTitle, + EuiFlexGroup, + EuiFlexItem, + EuiButton, +} from '@elastic/eui'; +import React from 'react'; +import { useHistory } from 'react-router-dom'; +import { FormattedMessage } from '@kbn/i18n-react'; + +export const NotFoundPage = () => { + const history = useHistory(); + return ( + + + + +

+ +

+ + } + body={ + + + + } + /> +
+
+
+ ); +}; diff --git a/x-pack/plugins/synthetics/public/pages/overview.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/overview.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/pages/overview.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/overview.test.tsx diff --git a/x-pack/plugins/synthetics/public/pages/overview.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/overview.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/pages/overview.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/overview.tsx diff --git a/x-pack/plugins/synthetics/public/pages/settings.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/settings.test.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/pages/settings.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/settings.test.tsx diff --git a/x-pack/plugins/synthetics/public/pages/settings.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/settings.tsx similarity index 97% rename from x-pack/plugins/synthetics/public/pages/settings.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/settings.tsx index a12b615bb703d..6726a10ab8747 100644 --- a/x-pack/plugins/synthetics/public/pages/settings.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/pages/settings.tsx @@ -11,7 +11,7 @@ import { useDispatch, useSelector } from 'react-redux'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { selectDynamicSettings } from '../state/selectors'; import { getDynamicSettings, setDynamicSettings } from '../state/actions/dynamic_settings'; -import { DynamicSettings } from '../../common/runtime_types'; +import { DynamicSettings } from '../../../common/runtime_types'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; import { IndicesForm } from '../components/settings/indices_form'; import { @@ -23,7 +23,7 @@ import * as Translations from './translations'; import { VALUE_MUST_BE_GREATER_THAN_ZERO, VALUE_MUST_BE_AN_INTEGER, -} from '../../common/translations'; +} from '../../../common/translations'; import { AlertDefaultsForm } from '../components/settings/alert_defaults_form'; import { SettingsActionBarPortal } from '../components/settings/settings_bottom_bar'; import { useSettingsErrors } from '../components/settings/use_settings_errors'; diff --git a/x-pack/plugins/synthetics/public/pages/synthetics/checks_navigation.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/synthetics/checks_navigation.tsx similarity index 93% rename from x-pack/plugins/synthetics/public/pages/synthetics/checks_navigation.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/synthetics/checks_navigation.tsx index 6dcef302506e4..f9d98b7b640c6 100644 --- a/x-pack/plugins/synthetics/public/pages/synthetics/checks_navigation.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/pages/synthetics/checks_navigation.tsx @@ -10,9 +10,9 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui import { FormattedMessage } from '@kbn/i18n-react'; import { useHistory } from 'react-router-dom'; import moment from 'moment'; -import { SyntheticsJourneyApiResponse } from '../../../common/runtime_types/ping'; +import { SyntheticsJourneyApiResponse } from '../../../../common/runtime_types/ping'; import { getShortTimeStamp } from '../../components/overview/monitor_list/columns/monitor_status_column'; -import { useBreakpoints } from '../../hooks/use_breakpoints'; +import { useBreakpoints } from '../../../hooks/use_breakpoints'; interface Props { timestamp: string; diff --git a/x-pack/plugins/synthetics/public/pages/synthetics/step_detail_page.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/synthetics/step_detail_page.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/pages/synthetics/step_detail_page.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/synthetics/step_detail_page.tsx index 2ded527f7bb57..7caa521e434f4 100644 --- a/x-pack/plugins/synthetics/public/pages/synthetics/step_detail_page.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/pages/synthetics/step_detail_page.tsx @@ -14,7 +14,7 @@ import { useInitApp } from '../../hooks/use_init_app'; import { StepDetailContainer } from '../../components/monitor/synthetics/step_detail/step_detail_container'; import { journeySelector } from '../../state/selectors'; import { JourneyState } from '../../state/reducers/journey'; -import { JourneyStep } from '../../../common/runtime_types/ping/synthetics'; +import { JourneyStep } from '../../../../common/runtime_types/ping/synthetics'; import { StepPageNavigation } from '../../components/monitor/synthetics/step_detail/step_page_nav'; import { StepPageTitleContent } from '../../components/monitor/synthetics/step_detail/step_page_title'; import { getJourneySteps } from '../../state/actions/journey'; diff --git a/x-pack/plugins/synthetics/public/pages/synthetics/synthetics_checks.test.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/synthetics/synthetics_checks.test.tsx similarity index 98% rename from x-pack/plugins/synthetics/public/pages/synthetics/synthetics_checks.test.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/synthetics/synthetics_checks.test.tsx index 290cdd453bd3c..312c10886c572 100644 --- a/x-pack/plugins/synthetics/public/pages/synthetics/synthetics_checks.test.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/pages/synthetics/synthetics_checks.test.tsx @@ -14,7 +14,7 @@ import { } from './synthetics_checks'; import { fetchJourneySteps } from '../../state/api/journey'; import { createMemoryHistory } from 'history'; -import { SYNTHETIC_CHECK_STEPS_ROUTE } from '../../../common/constants'; +import { SYNTHETIC_CHECK_STEPS_ROUTE } from '../../../../common/constants'; jest.mock('../../state/api/journey', () => ({ fetchJourneySteps: jest.fn(), diff --git a/x-pack/plugins/synthetics/public/pages/synthetics/synthetics_checks.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/synthetics/synthetics_checks.tsx similarity index 100% rename from x-pack/plugins/synthetics/public/pages/synthetics/synthetics_checks.tsx rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/synthetics/synthetics_checks.tsx diff --git a/x-pack/plugins/synthetics/public/pages/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/pages/translations.ts similarity index 100% rename from x-pack/plugins/synthetics/public/pages/translations.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/pages/translations.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/routes.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/routes.tsx new file mode 100644 index 0000000000000..ac9ffdd2b70e7 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/routes.tsx @@ -0,0 +1,329 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useEffect } from 'react'; +import { EuiPageTemplateProps, EuiBetaBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { Route, Switch } from 'react-router-dom'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; +import { APP_WRAPPER_CLASS } from '@kbn/core/public'; +import { useInspectorContext } from '@kbn/observability-plugin/public'; +import { + CERTIFICATES_ROUTE, + MAPPING_ERROR_ROUTE, + MONITOR_ROUTE, + MONITOR_ADD_ROUTE, + MONITOR_EDIT_ROUTE, + MONITOR_MANAGEMENT_ROUTE, + OVERVIEW_ROUTE, + SETTINGS_ROUTE, + STEP_DETAIL_ROUTE, + SYNTHETIC_CHECK_STEPS_ROUTE, +} from '../../common/constants'; +import { + MappingErrorPage, + MonitorPage, + AddMonitorPage, + EditMonitorPage, + MonitorManagementPage, + StepDetailPage, + NotFoundPage, + SettingsPage, + MonitorManagementBottomBar, +} from './pages'; +import { CertificatesPage } from './pages/certificates'; +import { UptimePage, useUptimeTelemetry } from './hooks'; +import { OverviewPageComponent } from './pages/overview'; +import { + SyntheticsCheckSteps, + SyntheticsCheckStepsPageHeader, + SyntheticsCheckStepsPageRightSideItem, +} from './pages/synthetics/synthetics_checks'; +import { MonitorPageTitle, MonitorPageTitleContent } from './components/monitor/monitor_title'; +import { UptimeDatePicker } from './components/common/uptime_date_picker'; +import { CertRefreshBtn } from './components/certificates/cert_refresh_btn'; +import { CertificateTitle } from './components/certificates/certificate_title'; +import { SyntheticsCallout } from './components/overview/synthetics_callout'; +import { + StepDetailPageChildren, + StepDetailPageHeader, + StepDetailPageRightSideItem, +} from './pages/synthetics/step_detail_page'; +import { UptimePageTemplateComponent } from './app/uptime_page_template'; +import { apiService } from './state/api/utils'; +import { AddMonitorBtn } from './components/monitor_management/add_monitor_btn'; +import { SettingsBottomBar } from './components/settings/settings_bottom_bar'; +import { ServiceAllowedWrapper } from './pages/monitor_management/service_allowed_wrapper'; + +type RouteProps = { + path: string; + component: React.FC; + dataTestSubj: string; + title: string; + telemetryId: UptimePage; + pageHeader: { + pageTitle: string | JSX.Element; + children?: JSX.Element; + rightSideItems?: JSX.Element[]; + }; +} & EuiPageTemplateProps; + +const baseTitle = i18n.translate('xpack.synthetics.routes.legacyBaseTitle', { + defaultMessage: 'Uptime - Kibana', +}); + +export const MONITORING_OVERVIEW_LABEL = i18n.translate('xpack.synthetics.overview.heading', { + defaultMessage: 'Monitors', +}); + +const getRoutes = (): RouteProps[] => { + return [ + { + title: i18n.translate('xpack.synthetics.monitorRoute.title', { + defaultMessage: 'Monitor | {baseTitle}', + values: { baseTitle }, + }), + path: MONITOR_ROUTE, + component: MonitorPage, + dataTestSubj: 'uptimeMonitorPage', + telemetryId: UptimePage.Monitor, + pageHeader: { + children: , + pageTitle: , + rightSideItems: [], + }, + }, + { + title: i18n.translate('xpack.synthetics.settingsRoute.title', { + defaultMessage: `Settings | {baseTitle}`, + values: { baseTitle }, + }), + path: SETTINGS_ROUTE, + component: SettingsPage, + dataTestSubj: 'uptimeSettingsPage', + telemetryId: UptimePage.Settings, + pageHeader: { + pageTitle: ( + + ), + }, + bottomBar: , + bottomBarProps: { paddingSize: 'm' as const }, + }, + { + title: i18n.translate('xpack.synthetics.certificatesRoute.title', { + defaultMessage: `Certificates | {baseTitle}`, + values: { baseTitle }, + }), + path: CERTIFICATES_ROUTE, + component: CertificatesPage, + dataTestSubj: 'uptimeCertificatesPage', + telemetryId: UptimePage.Certificates, + pageHeader: { + pageTitle: , + rightSideItems: [], + }, + }, + { + title: i18n.translate('xpack.synthetics.stepDetailRoute.title', { + defaultMessage: 'Synthetics detail | {baseTitle}', + values: { baseTitle }, + }), + path: STEP_DETAIL_ROUTE, + component: StepDetailPage, + dataTestSubj: 'uptimeStepDetailPage', + telemetryId: UptimePage.StepDetail, + pageHeader: { + children: , + pageTitle: , + rightSideItems: [], + }, + }, + { + title: baseTitle, + path: SYNTHETIC_CHECK_STEPS_ROUTE, + component: SyntheticsCheckSteps, + dataTestSubj: 'uptimeSyntheticCheckStepsPage', + telemetryId: UptimePage.SyntheticCheckStepsPage, + pageHeader: { + pageTitle: , + rightSideItems: [], + }, + }, + { + title: baseTitle, + path: OVERVIEW_ROUTE, + component: OverviewPageComponent, + dataTestSubj: 'uptimeOverviewPage', + telemetryId: UptimePage.Overview, + pageHeader: { + pageTitle: MONITORING_OVERVIEW_LABEL, + rightSideItems: [], + }, + }, + { + title: i18n.translate('xpack.synthetics.mappingErrorRoute.title', { + defaultMessage: 'Synthetics | mapping error', + }), + path: MAPPING_ERROR_ROUTE, + component: MappingErrorPage, + dataTestSubj: 'uptimeMappingErrorPage', + telemetryId: UptimePage.MappingError, + pageHeader: { + pageTitle: ( +
+ +
+ ), + rightSideItems: [], + }, + }, + { + title: i18n.translate('xpack.synthetics.addMonitorRoute.title', { + defaultMessage: 'Add Monitor | {baseTitle}', + values: { baseTitle }, + }), + path: MONITOR_ADD_ROUTE, + component: () => ( + + + + ), + dataTestSubj: 'uptimeMonitorAddPage', + telemetryId: UptimePage.MonitorAdd, + pageHeader: { + pageTitle: ( + + ), + }, + bottomBar: , + bottomBarProps: { paddingSize: 'm' as const }, + }, + { + title: i18n.translate('xpack.synthetics.editMonitorRoute.title', { + defaultMessage: 'Edit Monitor | {baseTitle}', + values: { baseTitle }, + }), + path: MONITOR_EDIT_ROUTE, + component: () => ( + + + + ), + dataTestSubj: 'uptimeMonitorEditPage', + telemetryId: UptimePage.MonitorEdit, + pageHeader: { + pageTitle: ( + + ), + }, + bottomBar: , + bottomBarProps: { paddingSize: 'm' as const }, + }, + { + title: i18n.translate('xpack.synthetics.monitorManagementRoute.title', { + defaultMessage: 'Monitor Management | {baseTitle}', + values: { baseTitle }, + }), + path: MONITOR_MANAGEMENT_ROUTE + '/:type', + component: () => ( + + + + ), + dataTestSubj: 'uptimeMonitorManagementListPage', + telemetryId: UptimePage.MonitorManagement, + pageHeader: { + pageTitle: ( + + + + + + + + + ), + rightSideItems: [], + }, + }, + ]; +}; + +const RouteInit: React.FC> = ({ + path, + title, + telemetryId, +}) => { + useUptimeTelemetry(telemetryId); + useEffect(() => { + document.title = title; + }, [path, title]); + return null; +}; + +export const PageRouter: FC = () => { + const routes = getRoutes(); + const { addInspectorRequest } = useInspectorContext(); + + apiService.addInspectorRequest = addInspectorRequest; + + return ( + + {routes.map( + ({ + title, + path, + component: RouteComponent, + dataTestSubj, + telemetryId, + pageHeader, + ...pageTemplateProps + }) => ( + +
+ + + + + +
+
+ ) + )} + +
+ ); +}; diff --git a/x-pack/plugins/synthetics/public/state/actions/alerts.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/alerts.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/actions/alerts.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/actions/alerts.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/dynamic_settings.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/dynamic_settings.ts new file mode 100644 index 0000000000000..6be8cc4547559 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/dynamic_settings.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 { createAction } from 'redux-actions'; +import { DynamicSettings } from '../../../../common/runtime_types'; + +export const getDynamicSettings = createAction('GET_DYNAMIC_SETTINGS'); +export const getDynamicSettingsSuccess = createAction( + 'GET_DYNAMIC_SETTINGS_SUCCESS' +); +export const getDynamicSettingsFail = createAction('GET_DYNAMIC_SETTINGS_FAIL'); + +export const setDynamicSettings = createAction('SET_DYNAMIC_SETTINGS'); +export const setDynamicSettingsSuccess = createAction( + 'SET_DYNAMIC_SETTINGS_SUCCESS' +); +export const setDynamicSettingsFail = createAction('SET_DYNAMIC_SETTINGS_FAIL'); diff --git a/x-pack/plugins/synthetics/public/state/actions/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/actions/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/actions/index.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/index_status.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/index_status.ts new file mode 100644 index 0000000000000..2de91a82c6762 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/index_status.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createAsyncAction } from './utils'; +import { StatesIndexStatus } from '../../../../common/runtime_types'; + +export const indexStatusAction = createAsyncAction('GET INDEX STATUS'); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/journey.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/journey.ts new file mode 100644 index 0000000000000..c9833ee25f95d --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/journey.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 { createAction } from 'redux-actions'; +import { SyntheticsJourneyApiResponse } from '../../../../common/runtime_types'; + +export interface FetchJourneyStepsParams { + checkGroup: string; + syntheticEventTypes?: string[]; +} + +export interface GetJourneyFailPayload { + checkGroup: string; + error: Error; +} + +export const getJourneySteps = createAction('GET_JOURNEY_STEPS'); +export const getJourneyStepsSuccess = createAction( + 'GET_JOURNEY_STEPS_SUCCESS' +); +export const getJourneyStepsFail = createAction('GET_JOURNEY_STEPS_FAIL'); +export const pruneJourneyState = createAction('PRUNE_JOURNEY_STATE'); diff --git a/x-pack/plugins/synthetics/public/state/actions/ml_anomaly.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/ml_anomaly.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/actions/ml_anomaly.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/actions/ml_anomaly.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/monitor.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/monitor.ts new file mode 100644 index 0000000000000..5fd74737d96a7 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/monitor.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 { createAction } from 'redux-actions'; +import { MonitorDetailsActionPayload } from './types'; +import { PingError } from '../../../../common/runtime_types'; +import { MonitorLocations } from '../../../../common/runtime_types'; +import { QueryParams } from './types'; +import { createAsyncAction } from './utils'; + +export interface MonitorLocationsPayload extends QueryParams { + monitorId: string; +} + +export interface MonitorDetailsState { + monitorId: string; + error: PingError; +} + +export const getMonitorDetailsAction = createAsyncAction< + MonitorDetailsActionPayload, + MonitorDetailsState +>('GET_MONITOR_DETAILS'); + +export const getMonitorLocationsAction = + createAction('GET_MONITOR_LOCATIONS'); +export const getMonitorLocationsActionSuccess = createAction( + 'GET_MONITOR_LOCATIONS_SUCCESS' +); +export const getMonitorLocationsActionFail = createAction('GET_MONITOR_LOCATIONS_FAIL'); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/monitor_duration.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/monitor_duration.ts new file mode 100644 index 0000000000000..52583e79f9ec3 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/monitor_duration.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 { createAction } from 'redux-actions'; +import { IHttpFetchError } from '@kbn/core/public'; +import { QueryParams } from './types'; +import { MonitorDurationResult } from '../../../../common/types'; + +type MonitorQueryParams = QueryParams & { monitorId: string }; + +export const getMonitorDurationAction = createAction('GET_MONITOR_DURATION'); +export const getMonitorDurationActionSuccess = createAction( + 'GET_MONITOR_DURATION_SUCCESS' +); +export const getMonitorDurationActionFail = createAction( + 'GET_MONITOR_DURATION_FAIL' +); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/monitor_list.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/monitor_list.ts new file mode 100644 index 0000000000000..c123ca95a5acd --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/monitor_list.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 { createAction } from 'redux-actions'; +import { + FetchMonitorStatesQueryArgs, + MonitorSummariesResult, +} from '../../../../common/runtime_types'; +import { createAsyncAction } from './utils'; +import { TestNowResponse } from '../api'; + +export const getMonitorList = createAction('GET_MONITOR_LIST'); +export const getMonitorListSuccess = createAction( + 'GET_MONITOR_LIST_SUCCESS' +); +export const getMonitorListFailure = createAction('GET_MONITOR_LIST_FAIL'); + +export const setUpdatingMonitorId = createAction('SET_UPDATING_MONITOR_ID'); +export const clearRefreshedMonitorId = createAction('CLEAR_REFRESH_MONITOR_ID'); + +export const testNowMonitorAction = createAsyncAction( + 'TEST_NOW_MONITOR_ACTION' +); + +export const clearTestNowMonitorAction = createAction('CLEAR_TEST_NOW_MONITOR_ACTION'); + +export const getUpdatedMonitor = createAsyncAction< + FetchMonitorStatesQueryArgs, + MonitorSummariesResult +>('GET_UPDATED_MONITOR'); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/monitor_management.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/monitor_management.ts new file mode 100644 index 0000000000000..79bec6f3e1b65 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/monitor_management.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 { createAction } from '@reduxjs/toolkit'; +import { + MonitorManagementListResult, + ServiceLocations, + ThrottlingOptions, + FetchMonitorManagementListQueryArgs, +} from '../../../../common/runtime_types'; +import { createAsyncAction } from './utils'; +import { SyntheticsServiceAllowed } from '../../../../common/types'; + +export const getMonitors = createAction( + 'GET_MONITOR_MANAGEMENT_LIST' +); +export const getMonitorsSuccess = createAction( + 'GET_MONITOR_MANAGEMENT_LIST_SUCCESS' +); +export const getMonitorsFailure = createAction('GET_MONITOR_MANAGEMENT_LIST_FAILURE'); + +export const getServiceLocations = createAction('GET_SERVICE_LOCATIONS_LIST'); +export const getServiceLocationsSuccess = createAction<{ + throttling: ThrottlingOptions | undefined; + locations: ServiceLocations; +}>('GET_SERVICE_LOCATIONS_LIST_SUCCESS'); +export const getServiceLocationsFailure = createAction('GET_SERVICE_LOCATIONS_LIST_FAILURE'); + +export const getSyntheticsEnablement = createAction('GET_SYNTHETICS_ENABLEMENT'); +export const getSyntheticsEnablementSuccess = createAction( + 'GET_SYNTHETICS_ENABLEMENT_SUCCESS' +); +export const getSyntheticsEnablementFailure = createAction( + 'GET_SYNTHETICS_ENABLEMENT_FAILURE' +); + +export const disableSynthetics = createAction('DISABLE_SYNTHETICS'); +export const disableSyntheticsSuccess = createAction('DISABLE_SYNTEHTICS_SUCCESS'); +export const disableSyntheticsFailure = createAction('DISABLE_SYNTHETICS_FAILURE'); + +export const enableSynthetics = createAction('ENABLE_SYNTHETICS'); +export const enableSyntheticsSuccess = createAction('ENABLE_SYNTEHTICS_SUCCESS'); +export const enableSyntheticsFailure = createAction('ENABLE_SYNTHETICS_FAILURE'); + +export const getSyntheticsServiceAllowed = createAsyncAction( + 'GET_SYNTHETICS_SERVICE_ALLOWED' +); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/monitor_status.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/monitor_status.ts new file mode 100644 index 0000000000000..e9fcf67a7b678 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/monitor_status.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 { createAction } from 'redux-actions'; +import { QueryParams } from './types'; +import { Ping } from '../../../../common/runtime_types'; + +export const getMonitorStatusAction = createAction('GET_MONITOR_STATUS'); +export const getMonitorStatusActionSuccess = createAction('GET_MONITOR_STATUS_SUCCESS'); +export const getMonitorStatusActionFail = createAction('GET_MONITOR_STATUS_FAIL'); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/network_events.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/network_events.ts new file mode 100644 index 0000000000000..1467289abe1e8 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/network_events.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 { createAction } from 'redux-actions'; +import { SyntheticsNetworkEventsApiResponse } from '../../../../common/runtime_types'; + +export interface FetchNetworkEventsParams { + checkGroup: string; + stepIndex: number; +} + +export interface FetchNetworkEventsFailPayload { + checkGroup: string; + stepIndex: number; + error: Error; +} + +export const getNetworkEvents = createAction('GET_NETWORK_EVENTS'); +export const getNetworkEventsSuccess = createAction< + Pick & SyntheticsNetworkEventsApiResponse +>('GET_NETWORK_EVENTS_SUCCESS'); +export const getNetworkEventsFail = + createAction('GET_NETWORK_EVENTS_FAIL'); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/ping.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/ping.ts new file mode 100644 index 0000000000000..73be085a86e03 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/ping.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 { createAction } from 'redux-actions'; +import { + GetPingHistogramParams, + HistogramResult, + PingsResponse, + GetPingsParams, +} from '../../../../common/runtime_types'; +import { createAsyncAction } from './utils'; + +export const clearPings = createAction('CLEAR PINGS'); + +export const getPingHistogram = createAsyncAction( + 'GET_PING_HISTOGRAM' +); + +export const getPings = createAction('GET PINGS'); +export const getPingsSuccess = createAction('GET PINGS SUCCESS'); +export const getPingsFail = createAction('GET PINGS FAIL'); diff --git a/x-pack/plugins/synthetics/public/state/actions/selected_filters.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/selected_filters.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/actions/selected_filters.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/actions/selected_filters.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/snapshot.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/snapshot.ts new file mode 100644 index 0000000000000..a495c917223a2 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/snapshot.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 { Snapshot } from '../../../../common/runtime_types'; +import { createAsyncAction } from './utils'; +import { SnapShotQueryParams } from '../api'; + +export const getSnapshotCountAction = createAsyncAction( + 'GET_SNAPSHOT_COUNT' +); diff --git a/x-pack/plugins/synthetics/public/state/actions/types.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/types.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/actions/types.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/actions/types.ts diff --git a/x-pack/plugins/synthetics/public/state/actions/ui.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/ui.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/actions/ui.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/actions/ui.ts diff --git a/x-pack/plugins/synthetics/public/state/actions/utils.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/utils.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/actions/utils.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/actions/utils.ts diff --git a/x-pack/plugins/synthetics/public/state/alerts/alerts.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/alerts/alerts.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/alerts/alerts.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/alerts/alerts.ts diff --git a/x-pack/plugins/synthetics/public/state/api/__snapshots__/snapshot.test.ts.snap b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/__snapshots__/snapshot.test.ts.snap similarity index 100% rename from x-pack/plugins/synthetics/public/state/api/__snapshots__/snapshot.test.ts.snap rename to x-pack/plugins/synthetics/public/legacy_uptime/state/api/__snapshots__/snapshot.test.ts.snap diff --git a/x-pack/plugins/synthetics/public/state/api/alert_actions.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/alert_actions.test.ts similarity index 83% rename from x-pack/plugins/synthetics/public/state/api/alert_actions.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/api/alert_actions.test.ts index 16c49d7c3afcb..068cdfd90b1ae 100644 --- a/x-pack/plugins/synthetics/public/state/api/alert_actions.test.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/alert_actions.test.ts @@ -50,7 +50,7 @@ describe('Alert Actions factory', () => { eventAction: 'trigger', severity: 'error', summary: - 'Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} from {{state.observerLocation}} {{{state.statusMessage}}} The latest error message is {{{state.latestErrorMessage}}}', + 'Monitor {{context.monitorName}} with url {{{context.monitorUrl}}} from {{context.observerLocation}} {{{context.statusMessage}}} The latest error message is {{{context.latestErrorMessage}}}', }, id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', }, @@ -75,7 +75,7 @@ describe('Alert Actions factory', () => { eventAction: 'trigger', severity: 'error', summary: - 'Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} from {{state.observerLocation}} {{{state.statusMessage}}} The latest error message is {{{state.latestErrorMessage}}}', + 'Monitor {{context.monitorName}} with url {{{context.monitorUrl}}} from {{context.observerLocation}} {{{context.statusMessage}}} The latest error message is {{{context.latestErrorMessage}}}', }, }, ]); @@ -93,7 +93,7 @@ describe('Alert Actions factory', () => { eventAction: 'trigger', severity: 'error', summary: - 'Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} from {{state.observerLocation}} {{{state.statusMessage}}} The latest error message is {{{state.latestErrorMessage}}}', + 'Monitor {{context.monitorName}} with url {{{context.monitorUrl}}} from {{context.observerLocation}} {{{context.statusMessage}}} The latest error message is {{{context.latestErrorMessage}}}', }, id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', }, @@ -118,7 +118,7 @@ describe('Alert Actions factory', () => { eventAction: 'trigger', severity: 'error', summary: - 'Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} from {{state.observerLocation}} {{{state.statusMessage}}} The latest error message is {{{state.latestErrorMessage}}}', + 'Monitor {{context.monitorName}} with url {{{context.monitorUrl}}} from {{context.observerLocation}} {{{context.statusMessage}}} The latest error message is {{{context.latestErrorMessage}}}', }, }, ]); diff --git a/x-pack/plugins/synthetics/public/state/api/alert_actions.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/alert_actions.ts similarity index 90% rename from x-pack/plugins/synthetics/public/state/api/alert_actions.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/api/alert_actions.ts index 2a76239ce183a..31d8c0577780c 100644 --- a/x-pack/plugins/synthetics/public/state/api/alert_actions.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/alert_actions.ts @@ -18,11 +18,11 @@ import { // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '@kbn/actions-plugin/server'; import { NewAlertParams } from './alerts'; -import { ACTION_GROUP_DEFINITIONS } from '../../../common/constants/alerts'; -import { MonitorStatusTranslations } from '../../../common/translations'; +import { ACTION_GROUP_DEFINITIONS } from '../../../../common/constants/alerts'; +import { MonitorStatusTranslations } from '../../../../common/translations'; import { ActionTypeId } from '../../components/settings/types'; -import { Ping } from '../../../common/runtime_types/ping'; -import { DefaultEmail } from '../../../common/runtime_types'; +import { Ping } from '../../../../common/runtime_types/ping'; +import { DefaultEmail } from '../../../../common/runtime_types'; export const SLACK_ACTION_ID: ActionTypeId = '.slack'; export const PAGER_DUTY_ACTION_ID: ActionTypeId = '.pagerduty'; @@ -127,11 +127,11 @@ function getIndexActionParams(selectedMonitor: Ping, recovery = false): IndexAct return { documents: [ { - monitorName: '{{state.monitorName}}', - monitorUrl: '{{{state.monitorUrl}}}', + monitorName: '{{context.monitorName}}', + monitorUrl: '{{{context.monitorUrl}}}', statusMessage: getRecoveryMessage(selectedMonitor), latestErrorMessage: '', - observerLocation: '{{state.observerLocation}}', + observerLocation: '{{context.observerLocation}}', }, ], indexOverride: null, @@ -140,11 +140,11 @@ function getIndexActionParams(selectedMonitor: Ping, recovery = false): IndexAct return { documents: [ { - monitorName: '{{state.monitorName}}', - monitorUrl: '{{{state.monitorUrl}}}', - statusMessage: '{{{state.statusMessage}}}', - latestErrorMessage: '{{{state.latestErrorMessage}}}', - observerLocation: '{{state.observerLocation}}', + monitorName: '{{context.monitorName}}', + monitorUrl: '{{{context.monitorUrl}}}', + statusMessage: '{{{context.statusMessage}}}', + latestErrorMessage: '{{{context.latestErrorMessage}}}', + observerLocation: '{{context.observerLocation}}', }, ], indexOverride: null, diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/alerts.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/alerts.ts new file mode 100644 index 0000000000000..2d5d47578a57b --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/alerts.ts @@ -0,0 +1,169 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ActionType, AsApiContract, Rule } from '@kbn/triggers-actions-ui-plugin/public'; +import { RuleTypeParams } from '@kbn/alerting-plugin/common'; +import { CLIENT_ALERT_TYPES } from '../../../../common/constants/alerts'; +import { apiService } from './utils'; +import { ActionConnector } from '../alerts/alerts'; + +import { AlertsResult, MonitorIdParam } from '../actions/types'; +import { API_URLS } from '../../../../common/constants'; +import { AtomicStatusCheckParams } from '../../../../common/runtime_types/alerts'; + +import { populateAlertActions, RuleAction } from './alert_actions'; +import { Ping } from '../../../../common/runtime_types/ping'; +import { DefaultEmail } from '../../../../common/runtime_types'; + +const UPTIME_AUTO_ALERT = 'UPTIME_AUTO'; + +export const fetchConnectors = async (): Promise => { + const response = (await apiService.get(API_URLS.RULE_CONNECTORS)) as Array< + AsApiContract + >; + return response.map( + ({ + connector_type_id: actionTypeId, + referenced_by_count: referencedByCount, + is_preconfigured: isPreconfigured, + is_deprecated: isDeprecated, + is_missing_secrets: isMissingSecrets, + ...res + }) => ({ + ...res, + actionTypeId, + referencedByCount, + isDeprecated, + isPreconfigured, + isMissingSecrets, + }) + ); +}; + +export interface NewAlertParams extends RuleTypeParams { + selectedMonitor: Ping; + defaultActions: ActionConnector[]; + defaultEmail?: DefaultEmail; +} + +type NewMonitorStatusAlert = Omit< + Rule, + | 'id' + | 'createdBy' + | 'updatedBy' + | 'createdAt' + | 'updatedAt' + | 'apiKey' + | 'apiKeyOwner' + | 'muteAll' + | 'mutedInstanceIds' + | 'executionStatus' + | 'ruleTypeId' + | 'notifyWhen' + | 'actions' +> & { + rule_type_id: Rule['ruleTypeId']; + notify_when: Rule['notifyWhen']; + actions: RuleAction[]; +}; + +export const createAlert = async ({ + defaultActions, + monitorId, + selectedMonitor, + defaultEmail, +}: NewAlertParams): Promise => { + const actions: RuleAction[] = populateAlertActions({ + defaultActions, + selectedMonitor, + defaultEmail, + }); + + const data: NewMonitorStatusAlert = { + actions, + params: { + numTimes: 1, + timerangeUnit: 'm', + timerangeCount: 1, + shouldCheckStatus: true, + shouldCheckAvailability: false, + isAutoGenerated: true, + search: `monitor.id : ${monitorId} `, + filters: { 'url.port': [], 'observer.geo.name': [], 'monitor.type': [], tags: [] }, + }, + consumer: 'uptime', + rule_type_id: CLIENT_ALERT_TYPES.MONITOR_STATUS, + schedule: { interval: '1m' }, + notify_when: 'onActionGroupChange', + tags: [UPTIME_AUTO_ALERT], + name: `${selectedMonitor?.monitor.name || selectedMonitor?.url?.full}(Simple status alert)`, + enabled: true, + throttle: null, + }; + + return await apiService.post(API_URLS.CREATE_RULE, data); +}; + +export const fetchMonitorAlertRecords = async (): Promise => { + const data = { + page: 1, + per_page: 500, + filter: `alert.attributes.alertTypeId:(${CLIENT_ALERT_TYPES.MONITOR_STATUS})`, + default_search_operator: 'AND', + sort_field: 'name.keyword', + sort_order: 'asc', + search_fields: ['name', 'tags'], + search: 'UPTIME_AUTO', + }; + return await apiService.get(API_URLS.RULES_FIND, data); +}; + +export const fetchAlertRecords = async ({ + monitorId, +}: MonitorIdParam): Promise> => { + const data = { + page: 1, + per_page: 500, + filter: `alert.attributes.alertTypeId:(${CLIENT_ALERT_TYPES.DURATION_ANOMALY})`, + default_search_operator: 'AND', + sort_field: 'name.keyword', + sort_order: 'asc', + }; + const rawRules = await apiService.get<{ + data: Array & { rule_type_id: string }>; + }>(API_URLS.RULES_FIND, data); + const monitorRule = rawRules.data.find( + (rule) => rule.params.monitorId === monitorId + ) as Rule & { rule_type_id: string }; + return { + ...monitorRule, + ruleTypeId: monitorRule.rule_type_id, + }; +}; + +export const disableAlertById = async ({ alertId }: { alertId: string }) => { + return await apiService.delete(API_URLS.DELETE_RULE + alertId); +}; + +export const fetchActionTypes = async (): Promise => { + const response = (await apiService.get(API_URLS.CONNECTOR_TYPES)) as Array< + AsApiContract + >; + return response.map( + ({ + enabled_in_config: enabledInConfig, + enabled_in_license: enabledInLicense, + minimum_license_required: minimumLicenseRequired, + ...res + }: AsApiContract) => ({ + ...res, + enabledInConfig, + enabledInLicense, + minimumLicenseRequired, + }) + ); +}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/dynamic_settings.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/dynamic_settings.ts new file mode 100644 index 0000000000000..e3c14adf1de74 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/dynamic_settings.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 { + DynamicSettingsType, + DynamicSettings, + DynamicSettingsSaveResponse, + DynamicSettingsSaveType, +} from '../../../../common/runtime_types'; +import { apiService } from './utils'; +import { API_URLS } from '../../../../common/constants'; + +const apiPath = API_URLS.DYNAMIC_SETTINGS; + +interface SaveApiRequest { + settings: DynamicSettings; +} + +export const getDynamicSettings = async (): Promise => { + return await apiService.get(apiPath, undefined, DynamicSettingsType); +}; + +export const setDynamicSettings = async ({ + settings, +}: SaveApiRequest): Promise => { + return await apiService.post(apiPath, settings, DynamicSettingsSaveType); +}; diff --git a/x-pack/plugins/synthetics/public/state/api/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/api/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/api/index.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/index_status.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/index_status.ts new file mode 100644 index 0000000000000..857915deb9023 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/index_status.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 { API_URLS } from '../../../../common/constants'; +import { StatesIndexStatus, StatesIndexStatusType } from '../../../../common/runtime_types'; +import { apiService } from './utils'; + +export const fetchIndexStatus = async (): Promise => { + return await apiService.get(API_URLS.INDEX_STATUS, undefined, StatesIndexStatusType); +}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/journey.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/journey.ts new file mode 100644 index 0000000000000..64a156eb26ed5 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/journey.ts @@ -0,0 +1,100 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { apiService } from './utils'; +import { FetchJourneyStepsParams } from '../actions/journey'; +import { Ping, PingType } from '../../../../common/runtime_types/ping/ping'; +import { + FailedStepsApiResponse, + FailedStepsApiResponseType, + ScreenshotBlockDoc, + ScreenshotImageBlob, + ScreenshotRefImageData, + SyntheticsJourneyApiResponse, + SyntheticsJourneyApiResponseType, +} from '../../../../common/runtime_types/ping/synthetics'; +import { API_URLS } from '../../../../common/constants'; + +export async function fetchScreenshotBlockSet(params: string[]): Promise { + return apiService.post(API_URLS.JOURNEY_SCREENSHOT_BLOCKS, { + hashes: params, + }); +} + +export async function fetchJourneySteps( + params: FetchJourneyStepsParams +): Promise { + return apiService.get( + `/internal/uptime/journey/${params.checkGroup}`, + { syntheticEventTypes: params.syntheticEventTypes }, + SyntheticsJourneyApiResponseType + ); +} + +export async function fetchJourneysFailedSteps({ + checkGroups, +}: { + checkGroups: string[]; +}): Promise { + return apiService.get(API_URLS.JOURNEY_FAILED_STEPS, { checkGroups }, FailedStepsApiResponseType); +} + +export async function fetchLastSuccessfulCheck({ + monitorId, + timestamp, + stepIndex, + location, +}: { + monitorId: string; + timestamp: string; + stepIndex: number; + location?: string; +}): Promise { + return await apiService.get( + API_URLS.SYNTHETICS_SUCCESSFUL_CHECK, + { + monitorId, + timestamp, + stepIndex, + location, + }, + PingType + ); +} + +export async function getJourneyScreenshot( + imgSrc: string +): Promise { + try { + const imgRequest = new Request(imgSrc); + + const response = await fetch(imgRequest); + + if (response.status !== 200) { + return null; + } + + const contentType = response.headers.get('content-type'); + const stepName = response.headers.get('caption-name'); + const maxSteps = Number(response.headers.get('max-steps') ?? 0); + if (contentType?.indexOf('application/json') !== -1) { + return { + stepName, + maxSteps, + ref: await response.json(), + }; + } else { + return { + stepName, + maxSteps, + src: URL.createObjectURL(await response.blob()), + }; + } + } catch (e) { + return null; + } +} diff --git a/x-pack/plugins/synthetics/public/state/api/ml_anomaly.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ml_anomaly.ts similarity index 95% rename from x-pack/plugins/synthetics/public/state/api/ml_anomaly.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/api/ml_anomaly.ts index e7655f37d3f01..18ce74d9823bb 100644 --- a/x-pack/plugins/synthetics/public/state/api/ml_anomaly.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ml_anomaly.ts @@ -14,14 +14,14 @@ import { import { extractErrorMessage } from '@kbn/ml-plugin/common'; import { apiService } from './utils'; import { AnomalyRecords, AnomalyRecordsParams } from '../actions'; -import { API_URLS, ML_MODULE_ID } from '../../../common/constants'; +import { API_URLS, ML_MODULE_ID } from '../../../../common/constants'; import { CreateMLJobSuccess, DeleteJobResults, HeartbeatIndicesParam, MonitorIdParam, } from '../actions/types'; -import { getJobPrefix, getMLJobId } from '../../../common/lib/ml'; +import { getJobPrefix, getMLJobId } from '../../../../common/lib/ml'; export const getMLCapabilities = async (): Promise => { return await apiService.get(API_URLS.ML_CAPABILITIES); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/monitor.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/monitor.ts new file mode 100644 index 0000000000000..5dfb200444134 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/monitor.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 { BaseParams } from './types'; +import { MonitorDetailsType, MonitorLocationsType } from '../../../../common/runtime_types'; +import { QueryParams } from '../actions/types'; +import { apiService } from './utils'; +import { API_URLS } from '../../../../common/constants'; + +interface ApiRequest { + monitorId: string; +} + +export type MonitorQueryParams = BaseParams & ApiRequest; + +export const fetchMonitorDetails = async ({ + monitorId, + dateStart, + dateEnd, +}: MonitorQueryParams) => { + const params = { + monitorId, + dateStart, + dateEnd, + }; + return await apiService.get(API_URLS.MONITOR_DETAILS, params, MonitorDetailsType); +}; + +type ApiParams = QueryParams & ApiRequest; + +export const fetchMonitorLocations = async ({ monitorId, dateStart, dateEnd }: ApiParams) => { + const params = { + dateStart, + dateEnd, + monitorId, + }; + return await apiService.get(API_URLS.MONITOR_LOCATIONS, params, MonitorLocationsType); +}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/monitor_duration.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/monitor_duration.ts new file mode 100644 index 0000000000000..3fc046170757c --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/monitor_duration.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 { BaseParams } from './types'; +import { apiService } from './utils'; +import { API_URLS } from '../../../../common/constants'; + +export const fetchMonitorDuration = async ({ monitorId, dateStart, dateEnd }: BaseParams) => { + const queryParams = { + monitorId, + dateStart, + dateEnd, + }; + + return await apiService.get(API_URLS.MONITOR_DURATION, queryParams); +}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/monitor_list.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/monitor_list.ts new file mode 100644 index 0000000000000..e328132abe465 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/monitor_list.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 { API_URLS } from '../../../../common/constants'; +import { apiService } from './utils'; +import { + FetchMonitorStatesQueryArgs, + MonitorSummariesResult, + MonitorSummariesResultType, +} from '../../../../common/runtime_types'; + +export const fetchMonitorList = async ( + params: FetchMonitorStatesQueryArgs +): Promise => { + return await apiService.get(API_URLS.MONITOR_LIST, params, MonitorSummariesResultType); +}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/monitor_management.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/monitor_management.ts new file mode 100644 index 0000000000000..c38fff649048b --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/monitor_management.ts @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { API_URLS } from '../../../../common/constants'; +import { + FetchMonitorManagementListQueryArgs, + MonitorManagementListResultCodec, + MonitorManagementListResult, + MonitorManagementEnablementResultCodec, + MonitorManagementEnablementResult, + ServiceLocations, + SyntheticsMonitor, + EncryptedSyntheticsMonitor, + ServiceLocationsApiResponseCodec, + ServiceLocationErrors, + ThrottlingOptions, + Locations, + SyntheticsMonitorSchedule, +} from '../../../../common/runtime_types'; +import { + DecryptedSyntheticsMonitorSavedObject, + SyntheticsServiceAllowed, +} from '../../../../common/types'; +import { apiService } from './utils'; + +export const setMonitor = async ({ + monitor, + id, +}: { + monitor: SyntheticsMonitor | EncryptedSyntheticsMonitor; + id?: string; +}): Promise<{ attributes: { errors: ServiceLocationErrors } } | SyntheticsMonitor> => { + if (id) { + return await apiService.put(`${API_URLS.SYNTHETICS_MONITORS}/${id}`, monitor); + } else { + return await apiService.post(API_URLS.SYNTHETICS_MONITORS, monitor); + } +}; + +export const getMonitor = async ({ + id, +}: { + id: string; +}): Promise => { + return await apiService.get(`${API_URLS.SYNTHETICS_MONITORS}/${id}`); +}; + +export const deleteMonitor = async ({ id }: { id: string }): Promise => { + return await apiService.delete(`${API_URLS.SYNTHETICS_MONITORS}/${id}`); +}; + +export const fetchMonitorManagementList = async ( + params: FetchMonitorManagementListQueryArgs +): Promise => { + return await apiService.get( + API_URLS.SYNTHETICS_MONITORS, + params, + MonitorManagementListResultCodec + ); +}; + +export const fetchServiceLocations = async (): Promise<{ + throttling: ThrottlingOptions | undefined; + locations: ServiceLocations; +}> => { + const { throttling, locations } = await apiService.get( + API_URLS.SERVICE_LOCATIONS, + undefined, + ServiceLocationsApiResponseCodec + ); + return { throttling, locations }; +}; + +export const runOnceMonitor = async ({ + monitor, + id, +}: { + monitor: SyntheticsMonitor; + id: string; +}): Promise<{ errors: Array<{ error: Error }> }> => { + return await apiService.post(API_URLS.RUN_ONCE_MONITOR + `/${id}`, monitor); +}; + +export interface TestNowResponse { + schedule: SyntheticsMonitorSchedule; + locations: Locations; + errors?: ServiceLocationErrors; + testRunId: string; + monitorId: string; +} + +export const triggerTestNowMonitor = async ( + configId: string +): Promise => { + return await apiService.get(API_URLS.TRIGGER_MONITOR + `/${configId}`); +}; + +export const fetchGetSyntheticsEnablement = + async (): Promise => { + return await apiService.get( + API_URLS.SYNTHETICS_ENABLEMENT, + undefined, + MonitorManagementEnablementResultCodec + ); + }; + +export const fetchDisableSynthetics = async (): Promise => { + return await apiService.delete(API_URLS.SYNTHETICS_ENABLEMENT); +}; + +export const fetchEnableSynthetics = async (): Promise => { + return await apiService.post(API_URLS.SYNTHETICS_ENABLEMENT); +}; + +export const fetchServiceAllowed = async (): Promise => { + return await apiService.get(API_URLS.SERVICE_ALLOWED); +}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/monitor_status.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/monitor_status.ts new file mode 100644 index 0000000000000..10534c18b7d38 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/monitor_status.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 { QueryParams } from '../actions/types'; +import { Ping } from '../../../../common/runtime_types'; +import { API_URLS } from '../../../../common/constants'; +import { apiService } from './utils'; + +export const fetchMonitorStatus = async ({ + monitorId, + dateStart, + dateEnd, +}: QueryParams): Promise => { + const queryParams = { + monitorId, + dateStart, + dateEnd, + }; + + return await apiService.get(API_URLS.MONITOR_STATUS, queryParams); +}; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/network_events.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/network_events.ts new file mode 100644 index 0000000000000..82dcd572ad624 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/network_events.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 { apiService } from './utils'; +import { FetchNetworkEventsParams } from '../actions/network_events'; +import { + SyntheticsNetworkEventsApiResponse, + SyntheticsNetworkEventsApiResponseType, +} from '../../../../common/runtime_types'; +import { API_URLS } from '../../../../common/constants'; + +export async function fetchNetworkEvents( + params: FetchNetworkEventsParams +): Promise { + return (await apiService.get( + API_URLS.NETWORK_EVENTS, + { + checkGroup: params.checkGroup, + stepIndex: params.stepIndex, + }, + SyntheticsNetworkEventsApiResponseType + )) as SyntheticsNetworkEventsApiResponse; +} diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ping.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ping.ts new file mode 100644 index 0000000000000..969102a8d77c6 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ping.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 { APIFn } from './types'; +import { + PingsResponseType, + PingsResponse, + GetPingsParams, + GetPingHistogramParams, + HistogramResult, +} from '../../../../common/runtime_types'; +import { apiService } from './utils'; +import { API_URLS } from '../../../../common/constants'; + +export const fetchPings: APIFn = async ({ + dateRange: { from, to }, + ...optional +}) => await apiService.get(API_URLS.PINGS, { from, to, ...optional }, PingsResponseType); + +export const fetchPingHistogram: APIFn = async ({ + monitorId, + dateStart, + dateEnd, + filters, + bucketSize, + query, +}) => { + const queryParams = { + dateStart, + dateEnd, + monitorId, + filters, + bucketSize, + query, + }; + + return await apiService.get(API_URLS.PING_HISTOGRAM, queryParams); +}; diff --git a/x-pack/plugins/synthetics/public/state/api/snapshot.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/snapshot.test.ts similarity index 97% rename from x-pack/plugins/synthetics/public/state/api/snapshot.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/api/snapshot.test.ts index 20a0a0d7a477c..97578deeb6fb7 100644 --- a/x-pack/plugins/synthetics/public/state/api/snapshot.test.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/snapshot.test.ts @@ -8,7 +8,7 @@ import { HttpFetchError } from '@kbn/core/public'; import { fetchSnapshotCount } from './snapshot'; import { apiService } from './utils'; -import { API_URLS } from '../../../common/constants'; +import { API_URLS } from '../../../../common/constants'; describe('snapshot API', () => { let fetchMock: jest.SpyInstance>; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/snapshot.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/snapshot.ts new file mode 100644 index 0000000000000..7734a0cec79b2 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/snapshot.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 { SnapshotType, Snapshot } from '../../../../common/runtime_types'; +import { apiService } from './utils'; +import { API_URLS } from '../../../../common/constants'; + +export interface SnapShotQueryParams { + dateRangeStart: string; + dateRangeEnd: string; + filters?: string; + query?: string; +} + +export const fetchSnapshotCount = async ({ + dateRangeStart, + dateRangeEnd, + filters, + query, +}: SnapShotQueryParams): Promise => { + const queryParams = { + dateRangeStart, + dateRangeEnd, + ...(filters && { filters }), + ...(query && { query }), + }; + + return await apiService.get(API_URLS.SNAPSHOT_COUNT, queryParams, SnapshotType); +}; diff --git a/x-pack/plugins/synthetics/public/state/api/types.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/types.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/api/types.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/api/types.ts diff --git a/x-pack/plugins/synthetics/public/state/api/utils.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/utils.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/api/utils.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/api/utils.ts diff --git a/x-pack/plugins/synthetics/public/state/certificates/certificates.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/certificates/certificates.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/certificates/certificates.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/certificates/certificates.ts diff --git a/x-pack/plugins/synthetics/public/state/effects/alerts.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/alerts.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/effects/alerts.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/effects/alerts.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/dynamic_settings.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/dynamic_settings.ts new file mode 100644 index 0000000000000..1200633976c6f --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/dynamic_settings.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 { takeLeading, put, call, takeLatest } from 'redux-saga/effects'; +import { Action } from 'redux-actions'; +import { i18n } from '@kbn/i18n'; +import { fetchEffectFactory } from './fetch_effect'; +import { + getDynamicSettings, + getDynamicSettingsSuccess, + getDynamicSettingsFail, + setDynamicSettingsSuccess, + setDynamicSettingsFail, + setDynamicSettings, +} from '../actions/dynamic_settings'; +import { + getDynamicSettings as getDynamicSettingsAPI, + setDynamicSettings as setDynamicSettingsAPI, +} from '../api'; +import { DynamicSettings } from '../../../../common/runtime_types'; +import { kibanaService } from '../kibana_service'; + +export function* fetchDynamicSettingsEffect() { + yield takeLeading( + String(getDynamicSettings), + fetchEffectFactory(getDynamicSettingsAPI, getDynamicSettingsSuccess, getDynamicSettingsFail) + ); +} + +export function* setDynamicSettingsEffect() { + const couldNotSaveSettingsText = i18n.translate('xpack.synthetics.settings.error.couldNotSave', { + defaultMessage: 'Could not save settings!', + }); + yield takeLatest(String(setDynamicSettings), function* (action: Action) { + try { + if (!action.payload) { + const err = new Error('Cannot fetch effect without a payload'); + yield put(setDynamicSettingsFail(err)); + + kibanaService.core.notifications.toasts.addError(err, { + title: couldNotSaveSettingsText, + }); + return; + } + yield call(setDynamicSettingsAPI, { settings: action.payload }); + yield put(setDynamicSettingsSuccess(action.payload)); + kibanaService.core.notifications.toasts.addSuccess( + i18n.translate('xpack.synthetics.settings.saveSuccess', { + defaultMessage: 'Settings saved!', + }) + ); + } catch (err) { + kibanaService.core.notifications.toasts.addError(err, { + title: couldNotSaveSettingsText, + }); + yield put(setDynamicSettingsFail(err)); + } + }); +} diff --git a/x-pack/plugins/synthetics/public/state/effects/fetch_effect.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/fetch_effect.test.ts similarity index 97% rename from x-pack/plugins/synthetics/public/state/effects/fetch_effect.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/effects/fetch_effect.test.ts index 5216260714cc8..4a19cfae58f6a 100644 --- a/x-pack/plugins/synthetics/public/state/effects/fetch_effect.test.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/fetch_effect.test.ts @@ -9,7 +9,7 @@ import { call, put } from 'redux-saga/effects'; import { fetchEffectFactory } from './fetch_effect'; import { indexStatusAction } from '../actions'; import { HttpFetchError } from '@kbn/core/public'; -import { StatesIndexStatus } from '../../../common/runtime_types'; +import { StatesIndexStatus } from '../../../../common/runtime_types'; import { fetchIndexStatus } from '../api'; describe('fetch saga effect factory', () => { diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/fetch_effect.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/fetch_effect.ts new file mode 100644 index 0000000000000..387ceeb56a514 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/fetch_effect.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 { call, put } from 'redux-saga/effects'; +import { Action } from 'redux-actions'; +import { IHttpFetchError } from '@kbn/core/public'; + +/** + * Factory function for a fetch effect. It expects three action creators, + * one to call for a fetch, one to call for success, and one to handle failures. + * @param fetch creates a fetch action + * @param success creates a success action + * @param fail creates a failure action + * @template T the action type expected by the fetch action + * @template R the type that the API request should return on success + * @template S the type of the success action + * @template F the type of the failure action + */ +export function fetchEffectFactory( + fetch: (request: T) => Promise, + success: (response: R) => Action, + fail: (error: IHttpFetchError) => Action +) { + return function* (action: Action): Generator { + try { + const response = yield call(fetch, action.payload); + if (response instanceof Error) { + // eslint-disable-next-line no-console + console.error(response); + + yield put(fail(response as IHttpFetchError)); + } else { + yield put(success(response as R)); + } + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + yield put(fail(error as IHttpFetchError)); + } + }; +} diff --git a/x-pack/plugins/synthetics/public/state/effects/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/effects/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/effects/index.ts diff --git a/x-pack/plugins/synthetics/public/state/effects/index_status.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/index_status.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/effects/index_status.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/effects/index_status.ts diff --git a/x-pack/plugins/synthetics/public/state/effects/journey.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/journey.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/effects/journey.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/effects/journey.test.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/journey.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/journey.ts new file mode 100644 index 0000000000000..14d3ce91a32de --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/journey.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 { Action } from 'redux-actions'; +import { call, put, takeEvery } from 'redux-saga/effects'; +import { + getJourneySteps, + getJourneyStepsSuccess, + getJourneyStepsFail, + FetchJourneyStepsParams, +} from '../actions/journey'; +import { fetchJourneySteps } from '../api/journey'; +import type { SyntheticsJourneyApiResponse } from '../../../../common/runtime_types'; + +const inFlightStepRequests: Record = {}; + +export function* fetchJourneyStepsEffect(): Generator { + yield takeEvery(getJourneySteps, function* (action: Action) { + if (inFlightStepRequests[action.payload.checkGroup]) return; + + try { + inFlightStepRequests[action.payload.checkGroup] = true; + const response = (yield call( + fetchJourneySteps, + action.payload + )) as SyntheticsJourneyApiResponse; + yield put(getJourneyStepsSuccess(response)); + } catch (e) { + yield put(getJourneyStepsFail({ checkGroup: action.payload.checkGroup, error: e })); + } finally { + delete inFlightStepRequests[action.payload.checkGroup]; + } + }); +} diff --git a/x-pack/plugins/synthetics/public/state/effects/ml_anomaly.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/ml_anomaly.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/effects/ml_anomaly.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/effects/ml_anomaly.ts diff --git a/x-pack/plugins/synthetics/public/state/effects/monitor.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/monitor.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/effects/monitor.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/effects/monitor.ts diff --git a/x-pack/plugins/synthetics/public/state/effects/monitor_duration.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/monitor_duration.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/effects/monitor_duration.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/effects/monitor_duration.ts diff --git a/x-pack/plugins/synthetics/public/state/effects/monitor_list.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/monitor_list.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/effects/monitor_list.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/effects/monitor_list.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/monitor_management.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/monitor_management.ts new file mode 100644 index 0000000000000..b5ee599b0a4f9 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/monitor_management.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { takeLatest, takeLeading } from 'redux-saga/effects'; +import { + getMonitors, + getMonitorsSuccess, + getMonitorsFailure, + getServiceLocations, + getServiceLocationsSuccess, + getServiceLocationsFailure, + getSyntheticsEnablement, + getSyntheticsEnablementSuccess, + getSyntheticsEnablementFailure, + disableSynthetics, + disableSyntheticsSuccess, + disableSyntheticsFailure, + enableSynthetics, + enableSyntheticsSuccess, + enableSyntheticsFailure, + getSyntheticsServiceAllowed, +} from '../actions'; +import { + fetchMonitorManagementList, + fetchServiceLocations, + fetchServiceAllowed, + fetchGetSyntheticsEnablement, + fetchDisableSynthetics, + fetchEnableSynthetics, +} from '../api'; +import { fetchEffectFactory } from './fetch_effect'; + +export function* fetchMonitorManagementEffect() { + yield takeLeading( + getMonitors, + fetchEffectFactory(fetchMonitorManagementList, getMonitorsSuccess, getMonitorsFailure) + ); + yield takeLeading( + getServiceLocations, + fetchEffectFactory( + fetchServiceLocations, + getServiceLocationsSuccess, + getServiceLocationsFailure + ) + ); + yield takeLeading( + getSyntheticsEnablement, + fetchEffectFactory( + fetchGetSyntheticsEnablement, + getSyntheticsEnablementSuccess, + getSyntheticsEnablementFailure + ) + ); + yield takeLatest( + disableSynthetics, + fetchEffectFactory(fetchDisableSynthetics, disableSyntheticsSuccess, disableSyntheticsFailure) + ); + yield takeLatest( + enableSynthetics, + fetchEffectFactory(fetchEnableSynthetics, enableSyntheticsSuccess, enableSyntheticsFailure) + ); +} + +export function* fetchSyntheticsServiceAllowedEffect() { + yield takeLeading( + getSyntheticsServiceAllowed.get, + fetchEffectFactory( + fetchServiceAllowed, + getSyntheticsServiceAllowed.success, + getSyntheticsServiceAllowed.fail + ) + ); +} diff --git a/x-pack/plugins/synthetics/public/state/effects/monitor_status.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/monitor_status.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/effects/monitor_status.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/effects/monitor_status.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/network_events.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/network_events.ts new file mode 100644 index 0000000000000..8c74e753c162c --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/network_events.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Action } from 'redux-actions'; +import { call, put, takeLatest } from 'redux-saga/effects'; +import { + getNetworkEvents, + getNetworkEventsSuccess, + getNetworkEventsFail, + FetchNetworkEventsParams, +} from '../actions/network_events'; +import { fetchNetworkEvents } from '../api/network_events'; +import type { SyntheticsNetworkEventsApiResponse } from '../../../../common/runtime_types'; + +export function* fetchNetworkEventsEffect() { + yield takeLatest( + getNetworkEvents, + function* (action: Action): Generator { + try { + const response = (yield call( + fetchNetworkEvents, + action.payload + )) as SyntheticsNetworkEventsApiResponse; + + yield put( + getNetworkEventsSuccess({ + checkGroup: action.payload.checkGroup, + stepIndex: action.payload.stepIndex, + ...response, + }) + ); + } catch (e) { + yield put( + getNetworkEventsFail({ + checkGroup: action.payload.checkGroup, + stepIndex: action.payload.stepIndex, + error: e, + }) + ); + } + } + ); +} diff --git a/x-pack/plugins/synthetics/public/state/effects/ping.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/ping.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/effects/ping.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/effects/ping.ts diff --git a/x-pack/plugins/synthetics/public/state/effects/synthetic_journey_blocks.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/synthetic_journey_blocks.ts similarity index 96% rename from x-pack/plugins/synthetics/public/state/effects/synthetic_journey_blocks.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/effects/synthetic_journey_blocks.ts index 829048747ddf7..6ffbeb6978f75 100644 --- a/x-pack/plugins/synthetics/public/state/effects/synthetic_journey_blocks.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/synthetic_journey_blocks.ts @@ -7,7 +7,7 @@ import { Action } from 'redux-actions'; import { call, fork, put, select, takeEvery, throttle } from 'redux-saga/effects'; -import { ScreenshotBlockDoc } from '../../../common/runtime_types/ping/synthetics'; +import { ScreenshotBlockDoc } from '../../../../common/runtime_types/ping/synthetics'; import { fetchScreenshotBlockSet } from '../api/journey'; import { fetchBlocksAction, diff --git a/x-pack/plugins/synthetics/public/state/effects/test_now_runs.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/effects/test_now_runs.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/effects/test_now_runs.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/effects/test_now_runs.ts diff --git a/x-pack/plugins/synthetics/public/state/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/index.ts diff --git a/x-pack/plugins/synthetics/public/state/kibana_service.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/kibana_service.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/kibana_service.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/kibana_service.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/dynamic_settings.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/dynamic_settings.ts new file mode 100644 index 0000000000000..d7c20b8edf7a3 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/dynamic_settings.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 { handleActions, Action } from 'redux-actions'; +import { + getDynamicSettings, + getDynamicSettingsSuccess, + getDynamicSettingsFail, + setDynamicSettings, + setDynamicSettingsSuccess, + setDynamicSettingsFail, +} from '../actions/dynamic_settings'; +import { DynamicSettings } from '../../../../common/runtime_types'; + +export interface DynamicSettingsState { + settings?: DynamicSettings; + loadError?: Error; + saveError?: Error; + loading: boolean; +} + +const initialState: DynamicSettingsState = { + loading: true, +}; + +export const dynamicSettingsReducer = handleActions( + { + [String(getDynamicSettings)]: (state) => ({ + ...state, + loading: true, + }), + [String(getDynamicSettingsSuccess)]: (_state, action: Action) => ({ + loading: false, + settings: action.payload, + }), + [String(getDynamicSettingsFail)]: (_state, action: Action) => ({ + loading: false, + loadError: action.payload, + }), + [String(setDynamicSettings)]: (state) => ({ + ...state, + loading: true, + }), + [String(setDynamicSettingsSuccess)]: (_state, action: Action) => ({ + settings: action.payload, + saveSucceded: true, + loading: false, + }), + [String(setDynamicSettingsFail)]: (state, action: Action) => ({ + ...state, + loading: false, + saveSucceeded: false, + saveError: action.payload, + }), + }, + initialState +); diff --git a/x-pack/plugins/synthetics/public/state/reducers/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/reducers/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/index.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/index_status.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/index_status.ts new file mode 100644 index 0000000000000..29ea59cabb9e5 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/index_status.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { handleActions } from 'redux-actions'; +import { indexStatusAction } from '../actions'; +import { asyncInitState, handleAsyncAction } from './utils'; +import { AsyncInitState } from './types'; +import { StatesIndexStatus } from '../../../../common/runtime_types'; + +export interface IndexStatusState { + indexStatus: AsyncInitState; +} + +const initialState: IndexStatusState = { + indexStatus: asyncInitState(), +}; + +type PayLoad = StatesIndexStatus & Error; + +export const indexStatusReducer = handleActions( + { + ...handleAsyncAction('indexStatus', indexStatusAction), + }, + initialState +); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/journey.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/journey.ts new file mode 100644 index 0000000000000..ae781b5839593 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/journey.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { handleActions, Action } from 'redux-actions'; +import { JourneyStep, SyntheticsJourneyApiResponse } from '../../../../common/runtime_types'; +import { pruneJourneyState } from '../actions/journey'; +import { + FetchJourneyStepsParams, + GetJourneyFailPayload, + getJourneySteps, + getJourneyStepsFail, + getJourneyStepsSuccess, +} from '../actions/journey'; + +export interface JourneyState { + checkGroup: string; + steps: JourneyStep[]; + details?: SyntheticsJourneyApiResponse['details']; + loading: boolean; + error?: Error; +} + +export interface JourneyKVP { + [checkGroup: string]: JourneyState; +} + +const initialState: JourneyKVP = {}; + +type Payload = FetchJourneyStepsParams & + SyntheticsJourneyApiResponse & + GetJourneyFailPayload & + string[]; + +export const journeyReducer = handleActions( + { + [String(getJourneySteps)]: ( + state: JourneyKVP, + { payload: { checkGroup } }: Action + ) => ({ + ...state, + // add an empty entry while fetching the check group, + // or update the previously-loaded entry to a new loading state + [checkGroup]: state[checkGroup] + ? { + ...state[checkGroup], + loading: true, + } + : { + checkGroup, + steps: [], + loading: true, + }, + }), + + [String(getJourneyStepsSuccess)]: ( + state: JourneyKVP, + { payload: { checkGroup, steps, details } }: Action + ) => ({ + ...state, + [checkGroup]: { + loading: false, + checkGroup, + steps, + details, + }, + }), + + [String(getJourneyStepsFail)]: ( + state: JourneyKVP, + { payload: { checkGroup, error } }: Action + ) => ({ + ...state, + [checkGroup]: state[checkGroup] + ? { + ...state[checkGroup], + loading: false, + error, + } + : { + checkGroup, + loading: false, + steps: [], + error, + }, + }), + + [String(pruneJourneyState)]: (state: JourneyKVP, action: Action) => + action.payload.reduce( + (prev, cur) => ({ + ...prev, + [cur]: state[cur], + }), + {} + ), + }, + initialState +); diff --git a/x-pack/plugins/synthetics/public/state/reducers/ml_anomaly.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/ml_anomaly.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/reducers/ml_anomaly.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/ml_anomaly.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor.ts new file mode 100644 index 0000000000000..23f2aac045b29 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Action } from 'redux-actions'; +import { + MonitorDetailsState, + getMonitorDetailsAction, + getMonitorLocationsAction, + getMonitorLocationsActionSuccess, + getMonitorLocationsActionFail, +} from '../actions/monitor'; +import { MonitorLocations } from '../../../../common/runtime_types'; + +type MonitorLocationsList = Map; + +export interface MonitorState { + loading: boolean; + errors: any[]; + monitorDetailsList: MonitorDetailsState[]; + monitorLocationsList: MonitorLocationsList; +} + +const initialState: MonitorState = { + monitorDetailsList: [], + monitorLocationsList: new Map(), + loading: false, + errors: [], +}; + +export function monitorReducer(state = initialState, action: Action): MonitorState { + switch (action.type) { + case String(getMonitorDetailsAction.get): + return { + ...state, + loading: true, + }; + case String(getMonitorDetailsAction.success): + const { monitorId } = action.payload; + return { + ...state, + monitorDetailsList: { + ...state.monitorDetailsList, + [monitorId]: action.payload, + }, + loading: false, + }; + case String(getMonitorDetailsAction.fail): + return { + ...state, + errors: [...state.errors, action.payload], + loading: false, + }; + case String(getMonitorLocationsAction): + return { + ...state, + loading: true, + }; + case String(getMonitorLocationsActionSuccess): + const monLocations = state.monitorLocationsList; + monLocations.set(action.payload.monitorId, action.payload); + return { + ...state, + monitorLocationsList: monLocations, + loading: false, + }; + case String(getMonitorLocationsActionFail): + return { + ...state, + errors: [...state.errors, action.payload], + }; + default: + return state; + } +} diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor_duration.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor_duration.ts new file mode 100644 index 0000000000000..71fa50b75f5d6 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor_duration.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 { handleActions, Action } from 'redux-actions'; +import { + getMonitorDurationAction, + getMonitorDurationActionSuccess, + getMonitorDurationActionFail, +} from '../actions'; +import { MonitorDurationResult } from '../../../../common/types'; + +export interface MonitorDuration { + durationLines: MonitorDurationResult | null; + errors: any[]; + loading: boolean; +} + +const initialState: MonitorDuration = { + durationLines: null, + loading: false, + errors: [], +}; + +type Payload = MonitorDurationResult & Error; + +export const monitorDurationReducer = handleActions( + { + [String(getMonitorDurationAction)]: (state: MonitorDuration) => ({ + ...state, + loading: true, + }), + + [String(getMonitorDurationActionSuccess)]: ( + state: MonitorDuration, + action: Action + ) => ({ + ...state, + loading: false, + durationLines: { ...action.payload }, + }), + + [String(getMonitorDurationActionFail)]: (state: MonitorDuration, action: Action) => ({ + ...state, + errors: [...state.errors, action.payload], + loading: false, + }), + }, + initialState +); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor_list.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor_list.ts new file mode 100644 index 0000000000000..6e325a699a114 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor_list.ts @@ -0,0 +1,128 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { handleActions, Action } from 'redux-actions'; +import { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public'; +import { + getMonitorList, + getMonitorListSuccess, + getMonitorListFailure, + getUpdatedMonitor, + clearRefreshedMonitorId, + setUpdatingMonitorId, +} from '../actions'; +import { MonitorSummariesResult } from '../../../../common/runtime_types'; +import { AppState } from '..'; +import { TestNowResponse } from '../api'; + +export interface MonitorList { + loading: boolean; + refreshedMonitorIds?: string[]; + isUpdating?: string[]; + list: MonitorSummariesResult; + error?: IHttpFetchError; +} + +export const initialState: MonitorList = { + list: { + nextPagePagination: null, + prevPagePagination: null, + summaries: [], + }, + loading: false, + refreshedMonitorIds: [], +}; + +type Payload = MonitorSummariesResult & + IHttpFetchError & + string & + TestNowResponse; + +export const monitorListReducer = handleActions( + { + [String(getMonitorList)]: (state: MonitorList) => ({ + ...state, + loading: true, + }), + [String(getMonitorListSuccess)]: ( + state: MonitorList, + action: Action + ) => ({ + ...state, + loading: false, + error: undefined, + list: { ...action.payload }, + }), + [String(getMonitorListFailure)]: ( + state: MonitorList, + action: Action> + ) => ({ + ...state, + error: action.payload, + loading: false, + }), + [String(setUpdatingMonitorId)]: (state: MonitorList, action: Action) => ({ + ...state, + isUpdating: [...(state.isUpdating ?? []), action.payload], + }), + [String(getUpdatedMonitor.get)]: (state: MonitorList) => ({ + ...state, + }), + [String(getUpdatedMonitor.success)]: ( + state: MonitorList, + action: Action + ) => { + const summaries = state.list.summaries; + + const newSummary = action.payload.summaries?.[0]; + + if (!newSummary) { + return { ...state, isUpdating: [] }; + } + + return { + ...state, + loading: false, + error: undefined, + isUpdating: state.isUpdating?.filter((item) => item !== newSummary.monitor_id), + refreshedMonitorIds: [...(state.refreshedMonitorIds ?? []), newSummary.monitor_id], + list: { + ...state.list, + summaries: summaries.map((summary) => { + if (summary.monitor_id === newSummary.monitor_id) { + return newSummary; + } + return summary; + }), + }, + }; + }, + [String(getUpdatedMonitor.fail)]: ( + state: MonitorList, + action: Action> + ) => ({ + ...state, + error: action.payload, + loading: false, + isUpdating: [], + }), + [String(clearRefreshedMonitorId)]: (state: MonitorList, action: Action) => ({ + ...state, + refreshedMonitorIds: (state.refreshedMonitorIds ?? []).filter( + (item) => item !== action.payload + ), + }), + }, + initialState +); + +export const refreshedMonitorSelector = ({ monitorList }: AppState) => { + return monitorList.refreshedMonitorIds ?? []; +}; + +export const isUpdatingMonitorSelector = ({ monitorList }: AppState) => + monitorList.isUpdating ?? []; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor_management.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor_management.ts new file mode 100644 index 0000000000000..18c247a655275 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor_management.ts @@ -0,0 +1,304 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createReducer, PayloadAction } from '@reduxjs/toolkit'; +import { WritableDraft } from 'immer/dist/types/types-external'; +import { + getMonitors, + getMonitorsSuccess, + getMonitorsFailure, + getServiceLocations, + getServiceLocationsSuccess, + getServiceLocationsFailure, + getSyntheticsEnablement, + getSyntheticsEnablementSuccess, + getSyntheticsEnablementFailure, + disableSynthetics, + disableSyntheticsSuccess, + disableSyntheticsFailure, + enableSynthetics, + enableSyntheticsSuccess, + enableSyntheticsFailure, + getSyntheticsServiceAllowed, +} from '../actions'; +import { + MonitorManagementEnablementResult, + MonitorManagementListResult, + ServiceLocations, + ThrottlingOptions, + DEFAULT_THROTTLING, +} from '../../../../common/runtime_types'; +import { SyntheticsServiceAllowed } from '../../../../common/types'; + +export interface MonitorManagementList { + error: Record<'monitorList' | 'serviceLocations' | 'enablement', Error | null>; + loading: Record<'monitorList' | 'serviceLocations' | 'enablement', boolean>; + list: MonitorManagementListResult; + locations: ServiceLocations; + enablement: MonitorManagementEnablementResult | null; + syntheticsService: { isAllowed?: boolean; signupUrl: string | null; loading: boolean }; + throttling: ThrottlingOptions; +} + +export const initialState: MonitorManagementList = { + list: { + page: 1, + perPage: 10, + total: null, + monitors: [], + syncErrors: [], + }, + locations: [], + enablement: null, + loading: { + monitorList: false, + serviceLocations: false, + enablement: false, + }, + error: { + monitorList: null, + serviceLocations: null, + enablement: null, + }, + syntheticsService: { + signupUrl: null, + loading: false, + }, + throttling: DEFAULT_THROTTLING, +}; + +export const monitorManagementListReducer = createReducer(initialState, (builder) => { + builder + .addCase(getMonitors, (state: WritableDraft) => ({ + ...state, + loading: { + ...state.loading, + monitorList: true, + }, + })) + .addCase( + getMonitorsSuccess, + ( + state: WritableDraft, + action: PayloadAction + ) => ({ + ...state, + loading: { + ...state.loading, + monitorList: false, + }, + error: { + ...state.error, + monitorList: null, + }, + list: { ...action.payload }, + }) + ) + .addCase( + getMonitorsFailure, + (state: WritableDraft, action: PayloadAction) => ({ + ...state, + loading: { + ...state.loading, + monitorList: false, + }, + error: { + ...state.error, + monitorList: action.payload, + }, + }) + ) + .addCase(getServiceLocations, (state: WritableDraft) => ({ + ...state, + loading: { + ...state.loading, + serviceLocations: true, + }, + })) + .addCase( + getServiceLocationsSuccess, + ( + state: WritableDraft, + action: PayloadAction<{ + throttling: ThrottlingOptions | undefined; + locations: ServiceLocations; + }> + ) => ({ + ...state, + loading: { + ...state.loading, + serviceLocations: false, + }, + error: { + ...state.error, + serviceLocations: null, + }, + locations: action.payload.locations, + throttling: action.payload.throttling || DEFAULT_THROTTLING, + }) + ) + .addCase( + getServiceLocationsFailure, + (state: WritableDraft, action: PayloadAction) => ({ + ...state, + loading: { + ...state.loading, + serviceLocations: false, + }, + error: { + ...state.error, + serviceLocations: action.payload, + }, + }) + ) + .addCase(getSyntheticsEnablement, (state: WritableDraft) => ({ + ...state, + loading: { + ...state.loading, + enablement: true, + }, + })) + .addCase( + getSyntheticsEnablementSuccess, + (state: WritableDraft, action: PayloadAction) => ({ + ...state, + loading: { + ...state.loading, + enablement: false, + }, + error: { + ...state.error, + enablement: null, + }, + enablement: action.payload, + }) + ) + .addCase( + getSyntheticsEnablementFailure, + (state: WritableDraft, action: PayloadAction) => ({ + ...state, + loading: { + ...state.loading, + enablement: false, + }, + error: { + ...state.error, + enablement: action.payload, + }, + }) + ) + .addCase(disableSynthetics, (state: WritableDraft) => ({ + ...state, + loading: { + ...state.loading, + enablement: true, + }, + })) + .addCase(disableSyntheticsSuccess, (state: WritableDraft) => ({ + ...state, + loading: { + ...state.loading, + enablement: false, + }, + error: { + ...state.error, + enablement: null, + }, + enablement: { + canEnable: state.enablement?.canEnable || false, + areApiKeysEnabled: state.enablement?.areApiKeysEnabled || false, + isEnabled: false, + }, + })) + .addCase( + disableSyntheticsFailure, + (state: WritableDraft, action: PayloadAction) => ({ + ...state, + loading: { + ...state.loading, + enablement: false, + }, + error: { + ...state.error, + enablement: action.payload, + }, + }) + ) + .addCase(enableSynthetics, (state: WritableDraft) => ({ + ...state, + loading: { + ...state.loading, + enablement: true, + }, + })) + .addCase(enableSyntheticsSuccess, (state: WritableDraft) => ({ + ...state, + loading: { + ...state.loading, + enablement: false, + }, + error: { + ...state.error, + enablement: null, + }, + enablement: { + canEnable: state.enablement?.canEnable || false, + areApiKeysEnabled: state.enablement?.areApiKeysEnabled || false, + isEnabled: true, + }, + })) + .addCase( + enableSyntheticsFailure, + (state: WritableDraft, action: PayloadAction) => ({ + ...state, + loading: { + ...state.loading, + enablement: false, + }, + error: { + ...state.error, + enablement: action.payload, + }, + }) + ) + .addCase( + String(getSyntheticsServiceAllowed.get), + (state: WritableDraft) => ({ + ...state, + syntheticsService: { + isAllowed: state.syntheticsService?.isAllowed, + signupUrl: state.syntheticsService?.signupUrl, + loading: true, + }, + }) + ) + .addCase( + String(getSyntheticsServiceAllowed.success), + ( + state: WritableDraft, + action: PayloadAction + ) => ({ + ...state, + syntheticsService: { + isAllowed: action.payload.serviceAllowed, + signupUrl: action.payload.signupUrl, + loading: false, + }, + }) + ) + .addCase( + String(getSyntheticsServiceAllowed.fail), + (state: WritableDraft, action: PayloadAction) => ({ + ...state, + syntheticsService: { + isAllowed: false, + signupUrl: null, + loading: false, + }, + }) + ); +}); diff --git a/x-pack/plugins/synthetics/public/state/reducers/monitor_status.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor_status.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/reducers/monitor_status.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor_status.test.ts diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor_status.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor_status.ts new file mode 100644 index 0000000000000..cd32a410503a3 --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/monitor_status.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { handleActions, Action } from 'redux-actions'; +import { + getMonitorStatusAction, + getMonitorStatusActionSuccess, + getMonitorStatusActionFail, +} from '../actions'; +import { Ping } from '../../../../common/runtime_types'; +import { QueryParams } from '../actions/types'; + +export interface MonitorStatusState { + status: Ping | null; + loading: boolean; +} + +export const initialState: MonitorStatusState = { + status: null, + loading: false, +}; + +export type MonitorStatusPayload = QueryParams & Ping; + +export const monitorStatusReducer = handleActions( + { + [String(getMonitorStatusAction)]: (state, action) => ({ + ...state, + // reset state if monitorId changes + status: action.payload.monitorId === state?.status?.monitor?.id ? state.status : null, + loading: true, + }), + + [String(getMonitorStatusActionSuccess)]: (state, action: Action) => { + return { + ...state, + loading: false, + // Keeping url from prev request to display, if there is no latest status + status: { + url: action.payload?.url || state.status?.url, + ...action.payload, + } as Ping, + }; + }, + + [String(getMonitorStatusActionFail)]: (state) => ({ + ...state, + loading: false, + }), + }, + initialState +); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/network_events.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/network_events.ts new file mode 100644 index 0000000000000..0abe4aeb2c7be --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/network_events.ts @@ -0,0 +1,154 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { handleActions, Action } from 'redux-actions'; +import { NetworkEvent, SyntheticsNetworkEventsApiResponse } from '../../../../common/runtime_types'; +import { + FetchNetworkEventsParams, + FetchNetworkEventsFailPayload, + getNetworkEvents, + getNetworkEventsFail, + getNetworkEventsSuccess, +} from '../actions/network_events'; + +export interface NetworkEventsState { + [checkGroup: string]: { + [stepIndex: number]: { + events: NetworkEvent[]; + total: number; + loading: boolean; + error?: Error; + isWaterfallSupported: boolean; + hasNavigationRequest?: boolean; + }; + }; +} + +const initialState: NetworkEventsState = {}; + +type Payload = FetchNetworkEventsParams & + SyntheticsNetworkEventsApiResponse & + FetchNetworkEventsFailPayload & + string[]; + +export const networkEventsReducer = handleActions( + { + [String(getNetworkEvents)]: ( + state: NetworkEventsState, + { payload: { checkGroup, stepIndex } }: Action + ) => ({ + ...state, + [checkGroup]: state[checkGroup] + ? { + [stepIndex]: state[checkGroup][stepIndex] + ? { + ...state[checkGroup][stepIndex], + loading: true, + events: [], + total: 0, + isWaterfallSupported: true, + } + : { + loading: true, + events: [], + total: 0, + isWaterfallSupported: true, + }, + } + : { + [stepIndex]: { + loading: true, + events: [], + total: 0, + isWaterfallSupported: true, + }, + }, + }), + + [String(getNetworkEventsSuccess)]: ( + state: NetworkEventsState, + { + payload: { + events, + total, + checkGroup, + stepIndex, + isWaterfallSupported, + hasNavigationRequest, + }, + }: Action + ) => { + return { + ...state, + [checkGroup]: state[checkGroup] + ? { + [stepIndex]: state[checkGroup][stepIndex] + ? { + ...state[checkGroup][stepIndex], + loading: false, + events, + total, + isWaterfallSupported, + hasNavigationRequest, + } + : { + loading: false, + events, + total, + isWaterfallSupported, + hasNavigationRequest, + }, + } + : { + [stepIndex]: { + loading: false, + events, + total, + isWaterfallSupported, + hasNavigationRequest, + }, + }, + }; + }, + + [String(getNetworkEventsFail)]: ( + state: NetworkEventsState, + { payload: { checkGroup, stepIndex, error } }: Action + ) => ({ + ...state, + [checkGroup]: state[checkGroup] + ? { + [stepIndex]: state[checkGroup][stepIndex] + ? { + ...state[checkGroup][stepIndex], + loading: false, + events: [], + total: 0, + error, + isWaterfallSupported: true, + } + : { + loading: false, + events: [], + total: 0, + error, + isWaterfallSupported: true, + }, + } + : { + [stepIndex]: { + loading: false, + events: [], + total: 0, + error, + isWaterfallSupported: true, + }, + }, + }), + }, + initialState +); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/ping.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/ping.ts new file mode 100644 index 0000000000000..f156a5596a84e --- /dev/null +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/ping.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { handleActions, Action } from 'redux-actions'; +import { getPingHistogram } from '../actions'; +import { HistogramResult } from '../../../../common/runtime_types'; + +export interface PingState { + pingHistogram: HistogramResult | null; + errors: any[]; + loading: boolean; +} + +const initialState: PingState = { + pingHistogram: null, + loading: false, + errors: [], +}; + +type MonitorStatusPayload = HistogramResult & Error; + +export const pingReducer = handleActions( + { + [String(getPingHistogram.get)]: (state) => ({ + ...state, + loading: true, + }), + + [String(getPingHistogram.success)]: (state: PingState, action: Action) => ({ + ...state, + loading: false, + pingHistogram: { ...action.payload }, + }), + + [String(getPingHistogram.fail)]: (state, action: Action) => ({ + ...state, + errors: [...state.errors, action.payload], + loading: false, + }), + }, + initialState +); diff --git a/x-pack/plugins/synthetics/public/state/reducers/ping_list.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/ping_list.ts similarity index 94% rename from x-pack/plugins/synthetics/public/state/reducers/ping_list.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/ping_list.ts index 09d9347cedd3b..4403c2484dedb 100644 --- a/x-pack/plugins/synthetics/public/state/reducers/ping_list.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/ping_list.ts @@ -6,7 +6,7 @@ */ import { handleActions, Action } from 'redux-actions'; -import { PingsResponse } from '../../../common/runtime_types'; +import { PingsResponse } from '../../../../common/runtime_types'; import { clearPings, getPings, getPingsSuccess, getPingsFail } from '../actions'; export interface PingListState { diff --git a/x-pack/plugins/synthetics/public/state/reducers/selected_filters.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/selected_filters.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/reducers/selected_filters.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/selected_filters.test.ts diff --git a/x-pack/plugins/synthetics/public/state/reducers/selected_filters.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/selected_filters.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/reducers/selected_filters.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/selected_filters.ts diff --git a/x-pack/plugins/synthetics/public/state/reducers/synthetics.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/synthetics.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/reducers/synthetics.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/synthetics.test.ts diff --git a/x-pack/plugins/synthetics/public/state/reducers/synthetics.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/synthetics.ts similarity index 99% rename from x-pack/plugins/synthetics/public/state/reducers/synthetics.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/synthetics.ts index 1e97c3972444b..2a0cf7188a9e8 100644 --- a/x-pack/plugins/synthetics/public/state/reducers/synthetics.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/synthetics.ts @@ -9,7 +9,7 @@ import { createAction, handleActions, Action } from 'redux-actions'; import { isScreenshotBlockDoc, ScreenshotBlockDoc, -} from '../../../common/runtime_types/ping/synthetics'; +} from '../../../../common/runtime_types/ping/synthetics'; export interface PendingBlock { status: 'pending' | 'loading'; diff --git a/x-pack/plugins/synthetics/public/state/reducers/test_now_runs.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/test_now_runs.ts similarity index 98% rename from x-pack/plugins/synthetics/public/state/reducers/test_now_runs.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/test_now_runs.ts index d081039e4d901..ffe49ff017259 100644 --- a/x-pack/plugins/synthetics/public/state/reducers/test_now_runs.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/test_now_runs.ts @@ -13,7 +13,7 @@ import { ScheduleUnit, ServiceLocationErrors, SyntheticsMonitorSchedule, -} from '../../../common/runtime_types'; +} from '../../../../common/runtime_types'; import { clearTestNowMonitorAction, testNowMonitorAction } from '../actions'; import { TestNowResponse } from '../api'; import { AppState } from '..'; diff --git a/x-pack/plugins/synthetics/public/state/reducers/types.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/types.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/reducers/types.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/types.ts diff --git a/x-pack/plugins/synthetics/public/state/reducers/ui.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/ui.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/reducers/ui.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/ui.test.ts diff --git a/x-pack/plugins/synthetics/public/state/reducers/ui.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/ui.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/reducers/ui.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/ui.ts diff --git a/x-pack/plugins/synthetics/public/state/reducers/utils.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/utils.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/reducers/utils.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/reducers/utils.ts diff --git a/x-pack/plugins/synthetics/public/state/selectors/index.test.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/selectors/index.test.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/selectors/index.test.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/selectors/index.test.ts diff --git a/x-pack/plugins/synthetics/public/state/selectors/index.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/selectors/index.ts similarity index 100% rename from x-pack/plugins/synthetics/public/state/selectors/index.ts rename to x-pack/plugins/synthetics/public/legacy_uptime/state/selectors/index.ts diff --git a/x-pack/plugins/synthetics/public/lib/alert_types/duration_anomaly.tsx b/x-pack/plugins/synthetics/public/lib/alert_types/duration_anomaly.tsx deleted file mode 100644 index 79b2b28652497..0000000000000 --- a/x-pack/plugins/synthetics/public/lib/alert_types/duration_anomaly.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import moment from 'moment'; - -import { ALERT_END, ALERT_STATUS, ALERT_STATUS_ACTIVE, ALERT_REASON } from '@kbn/rule-data-utils'; - -import { ObservabilityRuleTypeModel } from '@kbn/observability-plugin/public'; -import { AlertTypeInitializer } from '.'; -import { getMonitorRouteFromMonitorId } from '../../../common/utils/get_monitor_url'; -import { CLIENT_ALERT_TYPES } from '../../../common/constants/alerts'; -import { DurationAnomalyTranslations } from '../../../common/translations'; - -const { defaultActionMessage, description } = DurationAnomalyTranslations; -const DurationAnomalyAlert = React.lazy(() => import('./lazy_wrapper/duration_anomaly')); - -export const initDurationAnomalyAlertType: AlertTypeInitializer = ({ - core, - plugins, -}): ObservabilityRuleTypeModel => ({ - id: CLIENT_ALERT_TYPES.DURATION_ANOMALY, - iconClass: 'uptimeApp', - documentationUrl(docLinks) { - return `${docLinks.links.observability.uptimeDurationAnomaly}`; - }, - ruleParamsExpression: (params: unknown) => ( - - ), - description, - validate: () => ({ errors: {} }), - defaultActionMessage, - requiresAppContext: true, - format: ({ fields }) => ({ - reason: fields[ALERT_REASON] || '', - link: getMonitorRouteFromMonitorId({ - monitorId: fields['monitor.id']!, - dateRangeEnd: fields[ALERT_STATUS] === ALERT_STATUS_ACTIVE ? 'now' : fields[ALERT_END]!, - dateRangeStart: moment(new Date(fields['anomaly.start']!)).subtract('5', 'm').toISOString(), - }), - }), -}); diff --git a/x-pack/plugins/synthetics/public/lib/alert_types/index.ts b/x-pack/plugins/synthetics/public/lib/alert_types/index.ts deleted file mode 100644 index 7217e0a083e6b..0000000000000 --- a/x-pack/plugins/synthetics/public/lib/alert_types/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { CoreStart } from '@kbn/core/public'; -import { ObservabilityRuleTypeModel } from '@kbn/observability-plugin/public'; -import { RuleTypeModel } from '@kbn/triggers-actions-ui-plugin/public'; -import { initMonitorStatusAlertType } from './monitor_status'; -import { initTlsAlertType } from './tls'; -import { initTlsLegacyAlertType } from './tls_legacy'; -import { ClientPluginsStart } from '../../apps/plugin'; -import { initDurationAnomalyAlertType } from './duration_anomaly'; - -export type AlertTypeInitializer = (dependenies: { - core: CoreStart; - plugins: ClientPluginsStart; -}) => TAlertTypeModel; - -export const alertTypeInitializers: AlertTypeInitializer[] = [ - initMonitorStatusAlertType, - initTlsAlertType, - initDurationAnomalyAlertType, -]; - -export const legacyAlertTypeInitializers: Array> = [ - initTlsLegacyAlertType, -]; diff --git a/x-pack/plugins/synthetics/public/lib/alert_types/lazy_wrapper/duration_anomaly.tsx b/x-pack/plugins/synthetics/public/lib/alert_types/lazy_wrapper/duration_anomaly.tsx deleted file mode 100644 index bb1f84a45114d..0000000000000 --- a/x-pack/plugins/synthetics/public/lib/alert_types/lazy_wrapper/duration_anomaly.tsx +++ /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 React from 'react'; -import { Provider as ReduxProvider } from 'react-redux'; -import { CoreStart } from '@kbn/core/public'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { store } from '../../../state'; -import { AnomalyAlertComponent } from '../../../components/overview/alerts/anomaly_alert/anomaly_alert'; -import { ClientPluginsStart } from '../../../apps/plugin'; -import { kibanaService } from '../../../state/kibana_service'; - -interface Props { - core: CoreStart; - plugins: ClientPluginsStart; - params: any; -} - -// eslint-disable-next-line import/no-default-export -export default function DurationAnomalyAlert({ core, plugins, params }: Props) { - kibanaService.core = core; - return ( - - - - - - ); -} diff --git a/x-pack/plugins/synthetics/public/lib/alert_types/lazy_wrapper/monitor_status.tsx b/x-pack/plugins/synthetics/public/lib/alert_types/lazy_wrapper/monitor_status.tsx deleted file mode 100644 index 10aa71fa533e3..0000000000000 --- a/x-pack/plugins/synthetics/public/lib/alert_types/lazy_wrapper/monitor_status.tsx +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { Provider as ReduxProvider } from 'react-redux'; -import { CoreStart } from '@kbn/core/public'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { store } from '../../../state'; -import { ClientPluginsStart } from '../../../apps/plugin'; -import { kibanaService } from '../../../state/kibana_service'; -import { AlertMonitorStatus } from '../../../components/overview/alerts/alerts_containers/alert_monitor_status'; -import { UptimeDataViewContextProvider } from '../../../contexts/uptime_data_view_context'; - -interface Props { - core: CoreStart; - plugins: ClientPluginsStart; - params: any; -} - -// eslint-disable-next-line import/no-default-export -export default function MonitorStatusAlert({ core, plugins, params }: Props) { - kibanaService.core = core; - return ( - - - - - - - - ); -} diff --git a/x-pack/plugins/synthetics/public/lib/alert_types/monitor_status.tsx b/x-pack/plugins/synthetics/public/lib/alert_types/monitor_status.tsx deleted file mode 100644 index 99d8e22f11cdb..0000000000000 --- a/x-pack/plugins/synthetics/public/lib/alert_types/monitor_status.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import moment from 'moment'; - -import { - ALERT_END, - ALERT_START, - ALERT_STATUS, - ALERT_STATUS_ACTIVE, - ALERT_REASON, -} from '@kbn/rule-data-utils'; - -import { ObservabilityRuleTypeModel } from '@kbn/observability-plugin/public'; -import { ValidationResult } from '@kbn/triggers-actions-ui-plugin/public'; -import { AlertTypeInitializer } from '.'; -import { getMonitorRouteFromMonitorId } from '../../../common/utils/get_monitor_url'; -import { MonitorStatusTranslations } from '../../../common/translations'; -import { CLIENT_ALERT_TYPES } from '../../../common/constants/alerts'; - -const { defaultActionMessage, description } = MonitorStatusTranslations; - -const MonitorStatusAlert = React.lazy(() => import('./lazy_wrapper/monitor_status')); - -let validateFunc: (ruleParams: any) => ValidationResult; - -export const initMonitorStatusAlertType: AlertTypeInitializer = ({ - core, - plugins, -}): ObservabilityRuleTypeModel => ({ - id: CLIENT_ALERT_TYPES.MONITOR_STATUS, - description, - iconClass: 'uptimeApp', - documentationUrl(docLinks) { - return `${docLinks.links.observability.monitorStatus}`; - }, - ruleParamsExpression: (params: any) => ( - - ), - validate: (ruleParams: any) => { - if (!validateFunc) { - (async function loadValidate() { - const { validateMonitorStatusParams } = await import( - './lazy_wrapper/validate_monitor_status' - ); - validateFunc = validateMonitorStatusParams; - })(); - } - return validateFunc ? validateFunc(ruleParams) : ({} as ValidationResult); - }, - defaultActionMessage, - requiresAppContext: false, - format: ({ fields }) => ({ - reason: fields[ALERT_REASON] || '', - link: getMonitorRouteFromMonitorId({ - monitorId: fields['monitor.id']!, - dateRangeEnd: fields[ALERT_STATUS] === ALERT_STATUS_ACTIVE ? 'now' : fields[ALERT_END]!, - dateRangeStart: moment(new Date(fields[ALERT_START]!)).subtract('5', 'm').toISOString(), - filters: { - 'observer.geo.name': [fields['observer.geo.name'][0]], - }, - }), - }), -}); diff --git a/x-pack/plugins/synthetics/public/lib/helper/rtl_helpers.tsx b/x-pack/plugins/synthetics/public/lib/helper/rtl_helpers.tsx deleted file mode 100644 index 913f5e249c688..0000000000000 --- a/x-pack/plugins/synthetics/public/lib/helper/rtl_helpers.tsx +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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, { ReactElement, ReactNode } from 'react'; -import { of } from 'rxjs'; -// eslint-disable-next-line import/no-extraneous-dependencies -import { - render as reactTestLibRender, - MatcherFunction, - RenderOptions, -} from '@testing-library/react'; -import { Router, Route } from 'react-router-dom'; -import { merge } from 'lodash'; -import { createMemoryHistory, History } from 'history'; -import { CoreStart } from '@kbn/core/public'; -import { I18nProvider } from '@kbn/i18n-react'; -import { EuiPageTemplate } from '@elastic/eui'; -import { coreMock } from '@kbn/core/public/mocks'; -// eslint-disable-next-line import/no-extraneous-dependencies -import { configure } from '@testing-library/dom'; -import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; -import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; -import { KibanaContextProvider, KibanaServices } from '@kbn/kibana-react-plugin/public'; -import { triggersActionsUiMock } from '@kbn/triggers-actions-ui-plugin/public/mocks'; -import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; -import { mockState } from '../__mocks__/uptime_store.mock'; -import { MountWithReduxProvider } from './helper_with_redux'; -import { AppState } from '../../state'; -import { stringifyUrlParams } from './stringify_url_params'; -import { ClientPluginsStart } from '../../apps/plugin'; -import { UptimeRefreshContextProvider, UptimeStartupPluginsContextProvider } from '../../contexts'; -import { kibanaService } from '../../state/kibana_service'; - -type DeepPartial = { - [P in keyof T]?: DeepPartial; -}; - -interface KibanaProps { - services?: KibanaServices; -} - -export interface KibanaProviderOptions { - core?: DeepPartial & Partial; - kibanaProps?: KibanaProps; -} - -interface MockKibanaProviderProps extends KibanaProviderOptions { - children: ReactElement | ReactNode; -} - -interface MockRouterProps extends MockKibanaProviderProps { - history?: History; - path?: string; -} - -type Url = - | string - | { - path: string; - queryParams: Record; - }; - -interface RenderRouterOptions extends KibanaProviderOptions { - history?: History; - renderOptions?: Omit; - state?: Partial | DeepPartial; - url?: Url; - path?: string; -} - -function getSetting(key: string): T { - return 'MMM D, YYYY @ HH:mm:ss.SSS' as unknown as T; -} - -function setSetting$(key: string): T { - return of('MMM D, YYYY @ HH:mm:ss.SSS') as unknown as T; -} - -const createMockStore = () => { - let store: Record = {}; - return { - get: jest.fn().mockImplementation((key) => store[key]), - set: jest.fn().mockImplementation((key, value) => (store[key] = value)), - remove: jest.fn().mockImplementation((key: string) => delete store[key]), - clear: jest.fn().mockImplementation(() => (store = {})), - }; -}; - -const mockAppUrls: Record = { - uptime: '/app/uptime', - observability: '/app/observability', - '/home#/tutorial/uptimeMonitors': '/home#/tutorial/uptimeMonitors', -}; - -/* default mock core */ -export const defaultCore = coreMock.createStart(); -export const mockCore: () => Partial = () => { - const core: Partial = { - ...defaultCore, - application: { - ...defaultCore.application, - getUrlForApp: (app: string) => mockAppUrls[app], - navigateToUrl: jest.fn(), - capabilities: { - ...defaultCore.application.capabilities, - uptime: { - 'alerting:save': true, - configureSettings: true, - save: true, - show: true, - }, - actions: { - save: true, - }, - }, - }, - uiSettings: { - ...defaultCore.uiSettings, - get: getSetting, - get$: setSetting$, - }, - usageCollection: { - reportUiCounter: () => {}, - }, - triggersActionsUi: triggersActionsUiMock.createStart(), - storage: createMockStore(), - data: dataPluginMock.createStartContract(), - observability: { - useRulesLink: () => ({ href: 'newRuleLink' }), - navigation: { - // @ts-ignore - PageTemplate: EuiPageTemplate, - }, - ExploratoryViewEmbeddable: () =>
Embeddable exploratory view
, - }, - }; - - return core; -}; - -/* Mock Provider Components */ -export function MockKibanaProvider({ - children, - core, - kibanaProps, -}: MockKibanaProviderProps) { - const coreOptions = merge({}, mockCore(), core); - - kibanaService.core = coreOptions as any; - - return ( - - - - - {children} - - - - - ); -} - -export function MockRouter({ - children, - core, - path, - history = createMemoryHistory(), - kibanaProps, -}: MockRouterProps) { - return ( - - - {children} - - - ); -} -configure({ testIdAttribute: 'data-test-subj' }); - -export const MockRedux = ({ - state, - history = createMemoryHistory(), - children, - path, -}: { - state: Partial; - history?: History; - children: React.ReactNode; - path?: string; - useRealStore?: boolean; -}) => { - const testState: AppState = { - ...mockState, - ...state, - }; - - return ( - - - {children} - - - ); -}; - -export function WrappedHelper({ - children, - core, - kibanaProps, - state, - url, - useRealStore, - path, - history = createMemoryHistory(), -}: RenderRouterOptions & { children: ReactElement; useRealStore?: boolean }) { - const testState: AppState = merge({}, mockState, state); - - return ( - - - {children} - - - ); -} - -/* Custom react testing library render */ -export function render( - ui: ReactElement, - { - history = createMemoryHistory(), - core, - kibanaProps, - renderOptions, - state, - url, - path, - useRealStore, - }: RenderRouterOptions & { useRealStore?: boolean } = {} -) { - if (url) { - history = getHistoryFromUrl(url); - } - - return { - ...reactTestLibRender( - - {ui} - , - renderOptions - ), - history, - }; -} - -const getHistoryFromUrl = (url: Url) => { - if (typeof url === 'string') { - return createMemoryHistory({ - initialEntries: [url], - }); - } - - return createMemoryHistory({ - initialEntries: [url.path + stringifyUrlParams(url.queryParams)], - }); -}; - -const forNearestTag = - (tag: string) => - (getByText: (f: MatcherFunction) => HTMLElement | null) => - (text: string): HTMLElement | null => - getByText((_content: string, node: Element | null) => { - if (!node) return false; - const noOtherButtonHasText = Array.from(node.children).every( - (child) => child && (child.textContent !== text || child.tagName.toLowerCase() !== tag) - ); - return ( - noOtherButtonHasText && node.textContent === text && node.tagName.toLowerCase() === tag - ); - }); - -// This function allows us to query for the nearest button with test -// no matter whether it has nested tags or not (as EuiButton elements do). -export const forNearestButton = forNearestTag('button'); - -export const forNearestAnchor = forNearestTag('a'); - -export const makeUptimePermissionsCore = ( - permissions: Partial<{ - 'alerting:save': boolean; - configureSettings: boolean; - save: boolean; - show: boolean; - }> -) => { - return { - application: { - capabilities: { - uptime: { - 'alerting:save': true, - configureSettings: true, - save: true, - show: true, - ...permissions, - }, - }, - }, - }; -}; - -// This function filters out the queried elements which appear only -// either on mobile or desktop. -// -// It does so by filtering those with the class passed as the `classWrapper`. -// For mobile, we filter classes which tell elements to be hidden on desktop. -// For desktop, we do the opposite. -// -// We have this function because EUI will manipulate the visibility of some -// elements through pure CSS, which we can't assert on tests. Therefore, -// we look for the corresponding class wrapper. -const finderWithClassWrapper = - (classWrapper: string) => - ( - getterFn: (f: MatcherFunction) => HTMLElement | null, - customAttribute?: keyof Element | keyof HTMLElement - ) => - (text: string): HTMLElement | null => - getterFn((_content: string, node: Element | null) => { - if (!node) return false; - // There are actually properties that are not in Element but which - // appear on the `node`, so we must cast the customAttribute as a keyof Element - const content = node[(customAttribute as keyof Element) ?? 'innerHTML']; - if (content === text && wrappedInClass(node, classWrapper)) return true; - return false; - }); - -const wrappedInClass = (element: HTMLElement | Element, classWrapper: string): boolean => { - if (element.className.includes(classWrapper)) return true; - if (element.parentElement) return wrappedInClass(element.parentElement, classWrapper); - return false; -}; - -export const forMobileOnly = finderWithClassWrapper('hideForDesktop'); -export const forDesktopOnly = finderWithClassWrapper('hideForMobile'); diff --git a/x-pack/plugins/synthetics/public/lib/helper/url_params/get_supported_url_params.test.ts b/x-pack/plugins/synthetics/public/lib/helper/url_params/get_supported_url_params.test.ts deleted file mode 100644 index 4771d864e0bf8..0000000000000 --- a/x-pack/plugins/synthetics/public/lib/helper/url_params/get_supported_url_params.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import DateMath from '@kbn/datemath'; -import { getSupportedUrlParams } from './get_supported_url_params'; -import { CLIENT_DEFAULTS } from '../../../../common/constants'; - -describe('getSupportedUrlParams', () => { - let dateMathSpy: any; - const MOCK_DATE_VALUE = 20; - - beforeEach(() => { - dateMathSpy = jest.spyOn(DateMath, 'parse'); - dateMathSpy.mockReturnValue(MOCK_DATE_VALUE); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('returns custom values', () => { - const customValues = { - autorefreshInterval: '23', - autorefreshIsPaused: 'false', - dateRangeStart: 'now-15m', - dateRangeEnd: 'now', - monitorListPageIndex: '23', - monitorListPageSize: '50', - monitorListSortDirection: 'desc', - monitorListSortField: 'monitor.status', - search: 'monitor.status: down', - selectedPingStatus: 'up', - }; - const result = getSupportedUrlParams(customValues); - expect(result).toMatchSnapshot(); - }); - - it('returns default values', () => { - const { - AUTOREFRESH_INTERVAL, - AUTOREFRESH_IS_PAUSED, - DATE_RANGE_START, - DATE_RANGE_END, - FILTERS, - SEARCH, - STATUS_FILTER, - } = CLIENT_DEFAULTS; - const result = getSupportedUrlParams({}); - expect(result).toMatchSnapshot(); - expect(result).toEqual({ - absoluteDateRangeStart: MOCK_DATE_VALUE, - absoluteDateRangeEnd: MOCK_DATE_VALUE, - autorefreshInterval: AUTOREFRESH_INTERVAL, - autorefreshIsPaused: AUTOREFRESH_IS_PAUSED, - dateRangeStart: DATE_RANGE_START, - dateRangeEnd: DATE_RANGE_END, - excludedFilters: '', - filters: FILTERS, - focusConnectorField: false, - pagination: undefined, - search: SEARCH, - statusFilter: STATUS_FILTER, - query: '', - }); - }); - - it('returns the first item for string arrays', () => { - const result = getSupportedUrlParams({ - dateRangeStart: ['now-18d', 'now-11d', 'now-5m'], - }); - expect(result).toMatchSnapshot(); - }); - - it('provides defaults for undefined values', () => { - const result = getSupportedUrlParams({ - dateRangeStart: undefined, - }); - expect(result).toMatchSnapshot(); - }); - - it('provides defaults for empty string array values', () => { - const result = getSupportedUrlParams({ - dateRangeStart: [], - }); - expect(result).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/synthetics/public/lib/helper/url_params/get_supported_url_params.ts b/x-pack/plugins/synthetics/public/lib/helper/url_params/get_supported_url_params.ts deleted file mode 100644 index 727f18b39c764..0000000000000 --- a/x-pack/plugins/synthetics/public/lib/helper/url_params/get_supported_url_params.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { parseIsPaused } from './parse_is_paused'; -import { parseUrlInt } from './parse_url_int'; -import { CLIENT_DEFAULTS } from '../../../../common/constants'; -import { parseAbsoluteDate } from './parse_absolute_date'; - -export interface UptimeUrlParams { - absoluteDateRangeStart: number; - absoluteDateRangeEnd: number; - autorefreshInterval: number; - autorefreshIsPaused: boolean; - dateRangeStart: string; - dateRangeEnd: string; - pagination?: string; - filters: string; - excludedFilters: string; - search: string; - statusFilter: string; - focusConnectorField?: boolean; - query?: string; -} - -const { - ABSOLUTE_DATE_RANGE_START, - ABSOLUTE_DATE_RANGE_END, - AUTOREFRESH_INTERVAL, - AUTOREFRESH_IS_PAUSED, - DATE_RANGE_START, - DATE_RANGE_END, - SEARCH, - FILTERS, - STATUS_FILTER, -} = CLIENT_DEFAULTS; - -/** - * Gets the current URL values for the application. If no item is present - * for the URL, a default value is supplied. - * - * @param params A set of key-value pairs where the value is either - * undefined or a string/string array. If a string array is passed, - * only the first item is chosen. Support for lists in the URL will - * require further development. - */ -export const getSupportedUrlParams = (params: { - [key: string]: string | string[] | undefined | null; -}): UptimeUrlParams => { - const filteredParams: { [key: string]: string | undefined } = {}; - Object.keys(params).forEach((key) => { - let value: string | undefined; - if (params[key] === undefined) { - value = undefined; - } else if (Array.isArray(params[key])) { - // @ts-ignore this must be an array, and it's ok if the - // 0th element is undefined - value = params[key][0]; - } else { - // @ts-ignore this will not be an array because the preceding - // block tests for that - value = params[key]; - } - filteredParams[key] = value; - }); - - const { - autorefreshInterval, - autorefreshIsPaused, - dateRangeStart, - dateRangeEnd, - filters, - excludedFilters, - search, - statusFilter, - pagination, - focusConnectorField, - query, - } = filteredParams; - - return { - pagination, - absoluteDateRangeStart: parseAbsoluteDate( - dateRangeStart || DATE_RANGE_START, - ABSOLUTE_DATE_RANGE_START - ), - absoluteDateRangeEnd: parseAbsoluteDate( - dateRangeEnd || DATE_RANGE_END, - ABSOLUTE_DATE_RANGE_END, - { roundUp: true } - ), - autorefreshInterval: parseUrlInt(autorefreshInterval, AUTOREFRESH_INTERVAL), - autorefreshIsPaused: parseIsPaused(autorefreshIsPaused, AUTOREFRESH_IS_PAUSED), - dateRangeStart: dateRangeStart || DATE_RANGE_START, - dateRangeEnd: dateRangeEnd || DATE_RANGE_END, - filters: filters || FILTERS, - excludedFilters: excludedFilters || '', - search: search || SEARCH, - statusFilter: statusFilter || STATUS_FILTER, - focusConnectorField: !!focusConnectorField, - query: query || '', - }; -}; diff --git a/x-pack/plugins/synthetics/public/lib/lib.ts b/x-pack/plugins/synthetics/public/lib/lib.ts deleted file mode 100644 index 80be43e97b82b..0000000000000 --- a/x-pack/plugins/synthetics/public/lib/lib.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { UMBadge } from '../badge'; - -export type UMUpdateBadge = (badge: UMBadge) => void; diff --git a/x-pack/plugins/synthetics/public/pages/monitor_management/service_allowed_wrapper.tsx b/x-pack/plugins/synthetics/public/pages/monitor_management/service_allowed_wrapper.tsx deleted file mode 100644 index cda0e8a6cdbe9..0000000000000 --- a/x-pack/plugins/synthetics/public/pages/monitor_management/service_allowed_wrapper.tsx +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiButton, EuiEmptyPrompt, EuiLoadingLogo } from '@elastic/eui'; -import { useSyntheticsServiceAllowed } from '../../components/monitor_management/hooks/use_service_allowed'; - -export const ServiceAllowedWrapper: React.FC = ({ children }) => { - const { isAllowed, signupUrl, loading } = useSyntheticsServiceAllowed(); - - if (loading) { - return ( - } - title={

{LOADING_MONITOR_MANAGEMENT_LABEL}

} - /> - ); - } - - // checking for explicit false - if (isAllowed === false) { - return ( - {MONITOR_MANAGEMENT_LABEL}} - body={

{PUBLIC_BETA_DESCRIPTION}

} - actions={[ - - {REQUEST_ACCESS_LABEL} - , - ]} - /> - ); - } - - return <>{children}; -}; - -const REQUEST_ACCESS_LABEL = i18n.translate('xpack.synthetics.monitorManagement.requestAccess', { - defaultMessage: 'Request access', -}); - -export const MONITOR_MANAGEMENT_LABEL = i18n.translate('xpack.synthetics.monitorManagement.label', { - defaultMessage: 'Monitor Management', -}); - -const LOADING_MONITOR_MANAGEMENT_LABEL = i18n.translate( - 'xpack.synthetics.monitorManagement.loading.label', - { - defaultMessage: 'Loading Monitor Management', - } -); - -export const PUBLIC_BETA_DESCRIPTION = i18n.translate( - 'xpack.synthetics.monitorManagement.publicBetaDescription', - { - defaultMessage: - "We've got a brand new app on the way. In the meantime, we're excited to give you early access to our globally managed testing infrastructure. This will allow you to upload synthetic monitors using our new point and click script recorder and manage your monitors with a new UI.", - } -); diff --git a/x-pack/plugins/synthetics/public/plugin.ts b/x-pack/plugins/synthetics/public/plugin.ts new file mode 100644 index 0000000000000..0daf2fbe74de2 --- /dev/null +++ b/x-pack/plugins/synthetics/public/plugin.ts @@ -0,0 +1,319 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + CoreSetup, + CoreStart, + Plugin, + PluginInitializerContext, + AppMountParameters, +} from '@kbn/core/public'; +import { from } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { i18n } from '@kbn/i18n'; +import { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; +import { DiscoverStart } from '@kbn/discover-plugin/public'; +import { DEFAULT_APP_CATEGORIES } from '@kbn/core/public'; + +import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; +import { EmbeddableStart } from '@kbn/embeddable-plugin/public'; +import { + TriggersAndActionsUIPublicPluginSetup, + TriggersAndActionsUIPublicPluginStart, +} from '@kbn/triggers-actions-ui-plugin/public'; +import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; + +import { FleetStart } from '@kbn/fleet-plugin/public'; +import { + enableNewSyntheticsView, + FetchDataParams, + ObservabilityPublicSetup, + ObservabilityPublicStart, +} from '@kbn/observability-plugin/public'; +import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; +import { Start as InspectorPluginStart } from '@kbn/inspector-plugin/public'; +import { CasesUiStart } from '@kbn/cases-plugin/public'; +import { CloudSetup } from '@kbn/cloud-plugin/public'; +import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; +import { PLUGIN } from '../common/constants/plugin'; +import { + LazySyntheticsPolicyCreateExtension, + LazySyntheticsPolicyEditExtension, +} from './legacy_uptime/components/fleet_package'; +import { LazySyntheticsCustomAssetsExtension } from './legacy_uptime/components/fleet_package/lazy_synthetics_custom_assets_extension'; +import { uptimeOverviewNavigatorParams } from './apps/locators/overview'; +import { + alertTypeInitializers, + legacyAlertTypeInitializers, +} from './legacy_uptime/lib/alert_types'; + +export interface ClientPluginsSetup { + home?: HomePublicPluginSetup; + data: DataPublicPluginSetup; + observability: ObservabilityPublicSetup; + share: SharePluginSetup; + triggersActionsUi: TriggersAndActionsUIPublicPluginSetup; + cloud?: CloudSetup; +} + +export interface ClientPluginsStart { + fleet?: FleetStart; + data: DataPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; + discover: DiscoverStart; + inspector: InspectorPluginStart; + embeddable: EmbeddableStart; + observability: ObservabilityPublicStart; + share: SharePluginStart; + triggersActionsUi: TriggersAndActionsUIPublicPluginStart; + cases: CasesUiStart; + dataViews: DataViewsPublicPluginStart; +} + +export interface UptimePluginServices extends Partial { + embeddable: EmbeddableStart; + data: DataPublicPluginStart; + triggersActionsUi: TriggersAndActionsUIPublicPluginStart; + storage: IStorageWrapper; +} + +export type ClientSetup = void; +export type ClientStart = void; + +export class UptimePlugin + implements Plugin +{ + constructor(private readonly initContext: PluginInitializerContext) {} + + public setup(core: CoreSetup, plugins: ClientPluginsSetup): void { + if (plugins.home) { + plugins.home.featureCatalogue.register({ + id: PLUGIN.ID, + title: PLUGIN.TITLE, + description: PLUGIN.DESCRIPTION, + icon: 'uptimeApp', + path: '/app/uptime', + showOnHomePage: false, + category: 'data', + }); + } + const getUptimeDataHelper = async () => { + const [coreStart] = await core.getStartServices(); + const { UptimeDataHelper } = await import('./legacy_uptime/app/uptime_overview_fetcher'); + + return UptimeDataHelper(coreStart); + }; + + plugins.share.url.locators.create(uptimeOverviewNavigatorParams); + + plugins.observability.dashboard.register({ + appName: 'synthetics', + hasData: async () => { + const dataHelper = await getUptimeDataHelper(); + const status = await dataHelper.indexStatus(); + return { hasData: status.docCount > 0, indices: status.indices }; + }, + fetchData: async (params: FetchDataParams) => { + const dataHelper = await getUptimeDataHelper(); + return await dataHelper.overviewData(params); + }, + }); + + registerUptimeRoutesWithNavigation(core, plugins); + + const { observabilityRuleTypeRegistry } = plugins.observability; + + core.getStartServices().then(([coreStart, clientPluginsStart]) => { + alertTypeInitializers.forEach((init) => { + const alertInitializer = init({ + core: coreStart, + plugins: clientPluginsStart, + }); + if ( + clientPluginsStart.triggersActionsUi && + !clientPluginsStart.triggersActionsUi.ruleTypeRegistry.has(alertInitializer.id) + ) { + observabilityRuleTypeRegistry.register(alertInitializer); + } + }); + + legacyAlertTypeInitializers.forEach((init) => { + const alertInitializer = init({ + core: coreStart, + plugins: clientPluginsStart, + }); + if ( + clientPluginsStart.triggersActionsUi && + !clientPluginsStart.triggersActionsUi.ruleTypeRegistry.has(alertInitializer.id) + ) { + plugins.triggersActionsUi.ruleTypeRegistry.register(alertInitializer); + } + }); + }); + + const appKeywords = [ + 'Synthetics', + 'pings', + 'checks', + 'availability', + 'response duration', + 'response time', + 'outside in', + 'reachability', + 'reachable', + 'digital', + 'performance', + 'web performance', + 'web perf', + ]; + + core.application.register({ + id: PLUGIN.ID, + euiIconType: 'logoObservability', + order: 8400, + title: PLUGIN.TITLE, + category: DEFAULT_APP_CATEGORIES.observability, + keywords: appKeywords, + deepLinks: [ + { id: 'Down monitors', title: 'Down monitors', path: '/?statusFilter=down' }, + { id: 'Certificates', title: 'TLS Certificates', path: '/certificates' }, + { id: 'Settings', title: 'Settings', path: '/settings' }, + ], + mount: async (params: AppMountParameters) => { + const [coreStart, corePlugins] = await core.getStartServices(); + + const { renderApp } = await import('./legacy_uptime/app/render_app'); + return renderApp(coreStart, plugins, corePlugins, params, this.initContext.env.mode.dev); + }, + }); + + const isSyntheticsViewEnabled = core.uiSettings.get(enableNewSyntheticsView); + + if (isSyntheticsViewEnabled) { + registerSyntheticsRoutesWithNavigation(core, plugins); + + // Register the Synthetics UI plugin + core.application.register({ + id: 'synthetics', + euiIconType: 'logoObservability', + order: 8400, + title: PLUGIN.SYNTHETICS, + category: DEFAULT_APP_CATEGORIES.observability, + keywords: appKeywords, + deepLinks: [], + mount: async (params: AppMountParameters) => { + const [coreStart, corePlugins] = await core.getStartServices(); + + const { renderApp } = await import('./apps/synthetics/render_app'); + return renderApp(coreStart, plugins, corePlugins, params, this.initContext.env.mode.dev); + }, + }); + } + } + + public start(start: CoreStart, plugins: ClientPluginsStart): void { + if (plugins.fleet) { + const { registerExtension } = plugins.fleet; + + registerExtension({ + package: 'synthetics', + view: 'package-policy-create', + Component: LazySyntheticsPolicyCreateExtension, + }); + + registerExtension({ + package: 'synthetics', + view: 'package-policy-edit', + useLatestPackageVersion: true, + Component: LazySyntheticsPolicyEditExtension, + }); + + registerExtension({ + package: 'synthetics', + view: 'package-detail-assets', + Component: LazySyntheticsCustomAssetsExtension, + }); + } + } + + public stop(): void {} +} + +function registerSyntheticsRoutesWithNavigation( + core: CoreSetup, + plugins: ClientPluginsSetup +) { + plugins.observability.navigation.registerSections( + from(core.getStartServices()).pipe( + map(([coreStart]) => { + if (coreStart.application.capabilities.uptime.show) { + return [ + { + label: 'Synthetics', + sortKey: 499, + entries: [ + { + label: i18n.translate('xpack.synthetics.overview.heading', { + defaultMessage: 'Monitors', + }), + app: 'synthetics', + path: '/manage-monitors', + matchFullPath: true, + ignoreTrailingSlash: true, + }, + ], + }, + ]; + } + + return []; + }) + ) + ); +} + +function registerUptimeRoutesWithNavigation( + core: CoreSetup, + plugins: ClientPluginsSetup +) { + plugins.observability.navigation.registerSections( + from(core.getStartServices()).pipe( + map(([coreStart]) => { + if (coreStart.application.capabilities.uptime.show) { + return [ + { + label: 'Uptime', + sortKey: 500, + entries: [ + { + label: i18n.translate('xpack.synthetics.overview.heading', { + defaultMessage: 'Monitors', + }), + app: 'uptime', + path: '/', + matchFullPath: true, + ignoreTrailingSlash: true, + }, + { + label: i18n.translate('xpack.synthetics.certificatesPage.heading', { + defaultMessage: 'TLS Certificates', + }), + app: 'uptime', + path: '/certificates', + matchFullPath: true, + }, + ], + }, + ]; + } + + return []; + }) + ) + ); +} diff --git a/x-pack/plugins/synthetics/public/routes.tsx b/x-pack/plugins/synthetics/public/routes.tsx deleted file mode 100644 index 0c43b99467f3b..0000000000000 --- a/x-pack/plugins/synthetics/public/routes.tsx +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { FC, useEffect } from 'react'; -import { EuiPageTemplateProps, EuiBetaBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { Route, Switch } from 'react-router-dom'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { i18n } from '@kbn/i18n'; -import { APP_WRAPPER_CLASS } from '@kbn/core/public'; -import { useInspectorContext } from '@kbn/observability-plugin/public'; -import { - CERTIFICATES_ROUTE, - MAPPING_ERROR_ROUTE, - MONITOR_ROUTE, - MONITOR_ADD_ROUTE, - MONITOR_EDIT_ROUTE, - MONITOR_MANAGEMENT_ROUTE, - OVERVIEW_ROUTE, - SETTINGS_ROUTE, - STEP_DETAIL_ROUTE, - SYNTHETIC_CHECK_STEPS_ROUTE, -} from '../common/constants'; -import { - MappingErrorPage, - MonitorPage, - AddMonitorPage, - EditMonitorPage, - MonitorManagementPage, - StepDetailPage, - NotFoundPage, - SettingsPage, - MonitorManagementBottomBar, -} from './pages'; -import { CertificatesPage } from './pages/certificates'; -import { UptimePage, useUptimeTelemetry } from './hooks'; -import { OverviewPageComponent } from './pages/overview'; -import { - SyntheticsCheckSteps, - SyntheticsCheckStepsPageHeader, - SyntheticsCheckStepsPageRightSideItem, -} from './pages/synthetics/synthetics_checks'; -import { MonitorPageTitle, MonitorPageTitleContent } from './components/monitor/monitor_title'; -import { UptimeDatePicker } from './components/common/uptime_date_picker'; -import { CertRefreshBtn } from './components/certificates/cert_refresh_btn'; -import { CertificateTitle } from './components/certificates/certificate_title'; -import { SyntheticsCallout } from './components/overview/synthetics_callout'; -import { - StepDetailPageChildren, - StepDetailPageHeader, - StepDetailPageRightSideItem, -} from './pages/synthetics/step_detail_page'; -import { UptimePageTemplateComponent } from './apps/uptime_page_template'; -import { apiService } from './state/api/utils'; -import { AddMonitorBtn } from './components/monitor_management/add_monitor_btn'; -import { SettingsBottomBar } from './components/settings/settings_bottom_bar'; -import { ServiceAllowedWrapper } from './pages/monitor_management/service_allowed_wrapper'; - -type RouteProps = { - path: string; - component: React.FC; - dataTestSubj: string; - title: string; - telemetryId: UptimePage; - pageHeader: { - pageTitle: string | JSX.Element; - children?: JSX.Element; - rightSideItems?: JSX.Element[]; - }; -} & EuiPageTemplateProps; - -const baseTitle = i18n.translate('xpack.synthetics.routes.baseTitle', { - defaultMessage: 'Uptime - Kibana', -}); - -export const MONITORING_OVERVIEW_LABEL = i18n.translate('xpack.synthetics.overview.heading', { - defaultMessage: 'Monitors', -}); - -const getRoutes = (): RouteProps[] => { - return [ - { - title: i18n.translate('xpack.synthetics.monitorRoute.title', { - defaultMessage: 'Monitor | {baseTitle}', - values: { baseTitle }, - }), - path: MONITOR_ROUTE, - component: MonitorPage, - dataTestSubj: 'uptimeMonitorPage', - telemetryId: UptimePage.Monitor, - pageHeader: { - children: , - pageTitle: , - rightSideItems: [], - }, - }, - { - title: i18n.translate('xpack.synthetics.settingsRoute.title', { - defaultMessage: `Settings | {baseTitle}`, - values: { baseTitle }, - }), - path: SETTINGS_ROUTE, - component: SettingsPage, - dataTestSubj: 'uptimeSettingsPage', - telemetryId: UptimePage.Settings, - pageHeader: { - pageTitle: ( - - ), - }, - bottomBar: , - bottomBarProps: { paddingSize: 'm' as const }, - }, - { - title: i18n.translate('xpack.synthetics.certificatesRoute.title', { - defaultMessage: `Certificates | {baseTitle}`, - values: { baseTitle }, - }), - path: CERTIFICATES_ROUTE, - component: CertificatesPage, - dataTestSubj: 'uptimeCertificatesPage', - telemetryId: UptimePage.Certificates, - pageHeader: { - pageTitle: , - rightSideItems: [], - }, - }, - { - title: i18n.translate('xpack.synthetics.stepDetailRoute.title', { - defaultMessage: 'Synthetics detail | {baseTitle}', - values: { baseTitle }, - }), - path: STEP_DETAIL_ROUTE, - component: StepDetailPage, - dataTestSubj: 'uptimeStepDetailPage', - telemetryId: UptimePage.StepDetail, - pageHeader: { - children: , - pageTitle: , - rightSideItems: [], - }, - }, - { - title: baseTitle, - path: SYNTHETIC_CHECK_STEPS_ROUTE, - component: SyntheticsCheckSteps, - dataTestSubj: 'uptimeSyntheticCheckStepsPage', - telemetryId: UptimePage.SyntheticCheckStepsPage, - pageHeader: { - pageTitle: , - rightSideItems: [], - }, - }, - { - title: baseTitle, - path: OVERVIEW_ROUTE, - component: OverviewPageComponent, - dataTestSubj: 'uptimeOverviewPage', - telemetryId: UptimePage.Overview, - pageHeader: { - pageTitle: MONITORING_OVERVIEW_LABEL, - rightSideItems: [], - }, - }, - { - title: i18n.translate('xpack.synthetics.mappingErrorRoute.title', { - defaultMessage: 'Synthetics | mapping error', - }), - path: MAPPING_ERROR_ROUTE, - component: MappingErrorPage, - dataTestSubj: 'uptimeMappingErrorPage', - telemetryId: UptimePage.MappingError, - pageHeader: { - pageTitle: ( -
- -
- ), - rightSideItems: [], - }, - }, - { - title: i18n.translate('xpack.synthetics.addMonitorRoute.title', { - defaultMessage: 'Add Monitor | {baseTitle}', - values: { baseTitle }, - }), - path: MONITOR_ADD_ROUTE, - component: () => ( - - - - ), - dataTestSubj: 'uptimeMonitorAddPage', - telemetryId: UptimePage.MonitorAdd, - pageHeader: { - pageTitle: ( - - ), - }, - bottomBar: , - bottomBarProps: { paddingSize: 'm' as const }, - }, - { - title: i18n.translate('xpack.synthetics.editMonitorRoute.title', { - defaultMessage: 'Edit Monitor | {baseTitle}', - values: { baseTitle }, - }), - path: MONITOR_EDIT_ROUTE, - component: () => ( - - - - ), - dataTestSubj: 'uptimeMonitorEditPage', - telemetryId: UptimePage.MonitorEdit, - pageHeader: { - pageTitle: ( - - ), - }, - bottomBar: , - bottomBarProps: { paddingSize: 'm' as const }, - }, - { - title: i18n.translate('xpack.synthetics.monitorManagementRoute.title', { - defaultMessage: 'Monitor Management | {baseTitle}', - values: { baseTitle }, - }), - path: MONITOR_MANAGEMENT_ROUTE + '/:type', - component: () => ( - - - - ), - dataTestSubj: 'uptimeMonitorManagementListPage', - telemetryId: UptimePage.MonitorManagement, - pageHeader: { - pageTitle: ( - - - - - - - - - ), - rightSideItems: [], - }, - }, - ]; -}; - -const RouteInit: React.FC> = ({ - path, - title, - telemetryId, -}) => { - useUptimeTelemetry(telemetryId); - useEffect(() => { - document.title = title; - }, [path, title]); - return null; -}; - -export const PageRouter: FC = () => { - const routes = getRoutes(); - const { addInspectorRequest } = useInspectorContext(); - - apiService.addInspectorRequest = addInspectorRequest; - - return ( - - {routes.map( - ({ - title, - path, - component: RouteComponent, - dataTestSubj, - telemetryId, - pageHeader, - ...pageTemplateProps - }) => ( - -
- - - - - -
-
- ) - )} - -
- ); -}; diff --git a/x-pack/plugins/synthetics/public/state/actions/dynamic_settings.ts b/x-pack/plugins/synthetics/public/state/actions/dynamic_settings.ts deleted file mode 100644 index 7b7939688010f..0000000000000 --- a/x-pack/plugins/synthetics/public/state/actions/dynamic_settings.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 { createAction } from 'redux-actions'; -import { DynamicSettings } from '../../../common/runtime_types'; - -export const getDynamicSettings = createAction('GET_DYNAMIC_SETTINGS'); -export const getDynamicSettingsSuccess = createAction( - 'GET_DYNAMIC_SETTINGS_SUCCESS' -); -export const getDynamicSettingsFail = createAction('GET_DYNAMIC_SETTINGS_FAIL'); - -export const setDynamicSettings = createAction('SET_DYNAMIC_SETTINGS'); -export const setDynamicSettingsSuccess = createAction( - 'SET_DYNAMIC_SETTINGS_SUCCESS' -); -export const setDynamicSettingsFail = createAction('SET_DYNAMIC_SETTINGS_FAIL'); diff --git a/x-pack/plugins/synthetics/public/state/actions/index_status.ts b/x-pack/plugins/synthetics/public/state/actions/index_status.ts deleted file mode 100644 index 306565c1f507f..0000000000000 --- a/x-pack/plugins/synthetics/public/state/actions/index_status.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { createAsyncAction } from './utils'; -import { StatesIndexStatus } from '../../../common/runtime_types'; - -export const indexStatusAction = createAsyncAction('GET INDEX STATUS'); diff --git a/x-pack/plugins/synthetics/public/state/actions/journey.ts b/x-pack/plugins/synthetics/public/state/actions/journey.ts deleted file mode 100644 index bc03c443331c1..0000000000000 --- a/x-pack/plugins/synthetics/public/state/actions/journey.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { createAction } from 'redux-actions'; -import { SyntheticsJourneyApiResponse } from '../../../common/runtime_types'; - -export interface FetchJourneyStepsParams { - checkGroup: string; - syntheticEventTypes?: string[]; -} - -export interface GetJourneyFailPayload { - checkGroup: string; - error: Error; -} - -export const getJourneySteps = createAction('GET_JOURNEY_STEPS'); -export const getJourneyStepsSuccess = createAction( - 'GET_JOURNEY_STEPS_SUCCESS' -); -export const getJourneyStepsFail = createAction('GET_JOURNEY_STEPS_FAIL'); -export const pruneJourneyState = createAction('PRUNE_JOURNEY_STATE'); diff --git a/x-pack/plugins/synthetics/public/state/actions/monitor.ts b/x-pack/plugins/synthetics/public/state/actions/monitor.ts deleted file mode 100644 index f9dcd4bd57538..0000000000000 --- a/x-pack/plugins/synthetics/public/state/actions/monitor.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { createAction } from 'redux-actions'; -import { MonitorDetailsActionPayload } from './types'; -import { PingError } from '../../../common/runtime_types'; -import { MonitorLocations } from '../../../common/runtime_types'; -import { QueryParams } from './types'; -import { createAsyncAction } from './utils'; - -export interface MonitorLocationsPayload extends QueryParams { - monitorId: string; -} - -export interface MonitorDetailsState { - monitorId: string; - error: PingError; -} - -export const getMonitorDetailsAction = createAsyncAction< - MonitorDetailsActionPayload, - MonitorDetailsState ->('GET_MONITOR_DETAILS'); - -export const getMonitorLocationsAction = - createAction('GET_MONITOR_LOCATIONS'); -export const getMonitorLocationsActionSuccess = createAction( - 'GET_MONITOR_LOCATIONS_SUCCESS' -); -export const getMonitorLocationsActionFail = createAction('GET_MONITOR_LOCATIONS_FAIL'); diff --git a/x-pack/plugins/synthetics/public/state/actions/monitor_duration.ts b/x-pack/plugins/synthetics/public/state/actions/monitor_duration.ts deleted file mode 100644 index 1dd88c663cec5..0000000000000 --- a/x-pack/plugins/synthetics/public/state/actions/monitor_duration.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 { createAction } from 'redux-actions'; -import { IHttpFetchError } from '@kbn/core/public'; -import { QueryParams } from './types'; -import { MonitorDurationResult } from '../../../common/types'; - -type MonitorQueryParams = QueryParams & { monitorId: string }; - -export const getMonitorDurationAction = createAction('GET_MONITOR_DURATION'); -export const getMonitorDurationActionSuccess = createAction( - 'GET_MONITOR_DURATION_SUCCESS' -); -export const getMonitorDurationActionFail = createAction( - 'GET_MONITOR_DURATION_FAIL' -); diff --git a/x-pack/plugins/synthetics/public/state/actions/monitor_list.ts b/x-pack/plugins/synthetics/public/state/actions/monitor_list.ts deleted file mode 100644 index b86853dcfbefe..0000000000000 --- a/x-pack/plugins/synthetics/public/state/actions/monitor_list.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 { createAction } from 'redux-actions'; -import { FetchMonitorStatesQueryArgs, MonitorSummariesResult } from '../../../common/runtime_types'; -import { createAsyncAction } from './utils'; -import { TestNowResponse } from '../api'; - -export const getMonitorList = createAction('GET_MONITOR_LIST'); -export const getMonitorListSuccess = createAction( - 'GET_MONITOR_LIST_SUCCESS' -); -export const getMonitorListFailure = createAction('GET_MONITOR_LIST_FAIL'); - -export const setUpdatingMonitorId = createAction('SET_UPDATING_MONITOR_ID'); -export const clearRefreshedMonitorId = createAction('CLEAR_REFRESH_MONITOR_ID'); - -export const testNowMonitorAction = createAsyncAction( - 'TEST_NOW_MONITOR_ACTION' -); - -export const clearTestNowMonitorAction = createAction('CLEAR_TEST_NOW_MONITOR_ACTION'); - -export const getUpdatedMonitor = createAsyncAction< - FetchMonitorStatesQueryArgs, - MonitorSummariesResult ->('GET_UPDATED_MONITOR'); diff --git a/x-pack/plugins/synthetics/public/state/actions/monitor_management.ts b/x-pack/plugins/synthetics/public/state/actions/monitor_management.ts deleted file mode 100644 index 278f8fe9a4b99..0000000000000 --- a/x-pack/plugins/synthetics/public/state/actions/monitor_management.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { createAction } from '@reduxjs/toolkit'; -import { - MonitorManagementListResult, - ServiceLocations, - ThrottlingOptions, - FetchMonitorManagementListQueryArgs, -} from '../../../common/runtime_types'; -import { createAsyncAction } from './utils'; -import { SyntheticsServiceAllowed } from '../../../common/types'; - -export const getMonitors = createAction( - 'GET_MONITOR_MANAGEMENT_LIST' -); -export const getMonitorsSuccess = createAction( - 'GET_MONITOR_MANAGEMENT_LIST_SUCCESS' -); -export const getMonitorsFailure = createAction('GET_MONITOR_MANAGEMENT_LIST_FAILURE'); - -export const getServiceLocations = createAction('GET_SERVICE_LOCATIONS_LIST'); -export const getServiceLocationsSuccess = createAction<{ - throttling: ThrottlingOptions | undefined; - locations: ServiceLocations; -}>('GET_SERVICE_LOCATIONS_LIST_SUCCESS'); -export const getServiceLocationsFailure = createAction('GET_SERVICE_LOCATIONS_LIST_FAILURE'); - -export const getSyntheticsEnablement = createAction('GET_SYNTHETICS_ENABLEMENT'); -export const getSyntheticsEnablementSuccess = createAction( - 'GET_SYNTHETICS_ENABLEMENT_SUCCESS' -); -export const getSyntheticsEnablementFailure = createAction( - 'GET_SYNTHETICS_ENABLEMENT_FAILURE' -); - -export const disableSynthetics = createAction('DISABLE_SYNTHETICS'); -export const disableSyntheticsSuccess = createAction('DISABLE_SYNTEHTICS_SUCCESS'); -export const disableSyntheticsFailure = createAction('DISABLE_SYNTHETICS_FAILURE'); - -export const enableSynthetics = createAction('ENABLE_SYNTHETICS'); -export const enableSyntheticsSuccess = createAction('ENABLE_SYNTEHTICS_SUCCESS'); -export const enableSyntheticsFailure = createAction('ENABLE_SYNTHETICS_FAILURE'); - -export const getSyntheticsServiceAllowed = createAsyncAction( - 'GET_SYNTHETICS_SERVICE_ALLOWED' -); diff --git a/x-pack/plugins/synthetics/public/state/actions/monitor_status.ts b/x-pack/plugins/synthetics/public/state/actions/monitor_status.ts deleted file mode 100644 index 3928cd539a5c0..0000000000000 --- a/x-pack/plugins/synthetics/public/state/actions/monitor_status.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { createAction } from 'redux-actions'; -import { QueryParams } from './types'; -import { Ping } from '../../../common/runtime_types'; - -export const getMonitorStatusAction = createAction('GET_MONITOR_STATUS'); -export const getMonitorStatusActionSuccess = createAction('GET_MONITOR_STATUS_SUCCESS'); -export const getMonitorStatusActionFail = createAction('GET_MONITOR_STATUS_FAIL'); diff --git a/x-pack/plugins/synthetics/public/state/actions/network_events.ts b/x-pack/plugins/synthetics/public/state/actions/network_events.ts deleted file mode 100644 index f078888d6eae8..0000000000000 --- a/x-pack/plugins/synthetics/public/state/actions/network_events.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 { createAction } from 'redux-actions'; -import { SyntheticsNetworkEventsApiResponse } from '../../../common/runtime_types'; - -export interface FetchNetworkEventsParams { - checkGroup: string; - stepIndex: number; -} - -export interface FetchNetworkEventsFailPayload { - checkGroup: string; - stepIndex: number; - error: Error; -} - -export const getNetworkEvents = createAction('GET_NETWORK_EVENTS'); -export const getNetworkEventsSuccess = createAction< - Pick & SyntheticsNetworkEventsApiResponse ->('GET_NETWORK_EVENTS_SUCCESS'); -export const getNetworkEventsFail = - createAction('GET_NETWORK_EVENTS_FAIL'); diff --git a/x-pack/plugins/synthetics/public/state/actions/ping.ts b/x-pack/plugins/synthetics/public/state/actions/ping.ts deleted file mode 100644 index 6b997ba184b0b..0000000000000 --- a/x-pack/plugins/synthetics/public/state/actions/ping.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 { createAction } from 'redux-actions'; -import { - GetPingHistogramParams, - HistogramResult, - PingsResponse, - GetPingsParams, -} from '../../../common/runtime_types'; -import { createAsyncAction } from './utils'; - -export const clearPings = createAction('CLEAR PINGS'); - -export const getPingHistogram = createAsyncAction( - 'GET_PING_HISTOGRAM' -); - -export const getPings = createAction('GET PINGS'); -export const getPingsSuccess = createAction('GET PINGS SUCCESS'); -export const getPingsFail = createAction('GET PINGS FAIL'); diff --git a/x-pack/plugins/synthetics/public/state/actions/snapshot.ts b/x-pack/plugins/synthetics/public/state/actions/snapshot.ts deleted file mode 100644 index b1ff299600943..0000000000000 --- a/x-pack/plugins/synthetics/public/state/actions/snapshot.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Snapshot } from '../../../common/runtime_types'; -import { createAsyncAction } from './utils'; -import { SnapShotQueryParams } from '../api'; - -export const getSnapshotCountAction = createAsyncAction( - 'GET_SNAPSHOT_COUNT' -); diff --git a/x-pack/plugins/synthetics/public/state/api/alerts.ts b/x-pack/plugins/synthetics/public/state/api/alerts.ts deleted file mode 100644 index 1b97422cc67a5..0000000000000 --- a/x-pack/plugins/synthetics/public/state/api/alerts.ts +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { ActionType, AsApiContract, Rule } from '@kbn/triggers-actions-ui-plugin/public'; -import { RuleTypeParams } from '@kbn/alerting-plugin/common'; -import { CLIENT_ALERT_TYPES } from '../../../common/constants/alerts'; -import { apiService } from './utils'; -import { ActionConnector } from '../alerts/alerts'; - -import { AlertsResult, MonitorIdParam } from '../actions/types'; -import { API_URLS } from '../../../common/constants'; -import { AtomicStatusCheckParams } from '../../../common/runtime_types/alerts'; - -import { populateAlertActions, RuleAction } from './alert_actions'; -import { Ping } from '../../../common/runtime_types/ping'; -import { DefaultEmail } from '../../../common/runtime_types'; - -const UPTIME_AUTO_ALERT = 'UPTIME_AUTO'; - -export const fetchConnectors = async (): Promise => { - const response = (await apiService.get(API_URLS.RULE_CONNECTORS)) as Array< - AsApiContract - >; - return response.map( - ({ - connector_type_id: actionTypeId, - referenced_by_count: referencedByCount, - is_preconfigured: isPreconfigured, - is_deprecated: isDeprecated, - is_missing_secrets: isMissingSecrets, - ...res - }) => ({ - ...res, - actionTypeId, - referencedByCount, - isDeprecated, - isPreconfigured, - isMissingSecrets, - }) - ); -}; - -export interface NewAlertParams extends RuleTypeParams { - selectedMonitor: Ping; - defaultActions: ActionConnector[]; - defaultEmail?: DefaultEmail; -} - -type NewMonitorStatusAlert = Omit< - Rule, - | 'id' - | 'createdBy' - | 'updatedBy' - | 'createdAt' - | 'updatedAt' - | 'apiKey' - | 'apiKeyOwner' - | 'muteAll' - | 'mutedInstanceIds' - | 'executionStatus' - | 'ruleTypeId' - | 'notifyWhen' - | 'actions' -> & { - rule_type_id: Rule['ruleTypeId']; - notify_when: Rule['notifyWhen']; - actions: RuleAction[]; -}; - -export const createAlert = async ({ - defaultActions, - monitorId, - selectedMonitor, - defaultEmail, -}: NewAlertParams): Promise => { - const actions: RuleAction[] = populateAlertActions({ - defaultActions, - selectedMonitor, - defaultEmail, - }); - - const data: NewMonitorStatusAlert = { - actions, - params: { - numTimes: 1, - timerangeUnit: 'm', - timerangeCount: 1, - shouldCheckStatus: true, - shouldCheckAvailability: false, - isAutoGenerated: true, - search: `monitor.id : ${monitorId} `, - filters: { 'url.port': [], 'observer.geo.name': [], 'monitor.type': [], tags: [] }, - }, - consumer: 'uptime', - rule_type_id: CLIENT_ALERT_TYPES.MONITOR_STATUS, - schedule: { interval: '1m' }, - notify_when: 'onActionGroupChange', - tags: [UPTIME_AUTO_ALERT], - name: `${selectedMonitor?.monitor.name || selectedMonitor?.url?.full}(Simple status alert)`, - enabled: true, - throttle: null, - }; - - return await apiService.post(API_URLS.CREATE_RULE, data); -}; - -export const fetchMonitorAlertRecords = async (): Promise => { - const data = { - page: 1, - per_page: 500, - filter: `alert.attributes.alertTypeId:(${CLIENT_ALERT_TYPES.MONITOR_STATUS})`, - default_search_operator: 'AND', - sort_field: 'name.keyword', - sort_order: 'asc', - search_fields: ['name', 'tags'], - search: 'UPTIME_AUTO', - }; - return await apiService.get(API_URLS.RULES_FIND, data); -}; - -export const fetchAlertRecords = async ({ - monitorId, -}: MonitorIdParam): Promise> => { - const data = { - page: 1, - per_page: 500, - filter: `alert.attributes.alertTypeId:(${CLIENT_ALERT_TYPES.DURATION_ANOMALY})`, - default_search_operator: 'AND', - sort_field: 'name.keyword', - sort_order: 'asc', - }; - const rawRules = await apiService.get<{ - data: Array & { rule_type_id: string }>; - }>(API_URLS.RULES_FIND, data); - const monitorRule = rawRules.data.find( - (rule) => rule.params.monitorId === monitorId - ) as Rule & { rule_type_id: string }; - return { - ...monitorRule, - ruleTypeId: monitorRule.rule_type_id, - }; -}; - -export const disableAlertById = async ({ alertId }: { alertId: string }) => { - return await apiService.delete(API_URLS.DELETE_RULE + alertId); -}; - -export const fetchActionTypes = async (): Promise => { - const response = (await apiService.get(API_URLS.CONNECTOR_TYPES)) as Array< - AsApiContract - >; - return response.map( - ({ - enabled_in_config: enabledInConfig, - enabled_in_license: enabledInLicense, - minimum_license_required: minimumLicenseRequired, - ...res - }: AsApiContract) => ({ - ...res, - enabledInConfig, - enabledInLicense, - minimumLicenseRequired, - }) - ); -}; diff --git a/x-pack/plugins/synthetics/public/state/api/dynamic_settings.ts b/x-pack/plugins/synthetics/public/state/api/dynamic_settings.ts deleted file mode 100644 index a7bacfbba3462..0000000000000 --- a/x-pack/plugins/synthetics/public/state/api/dynamic_settings.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 { - DynamicSettingsType, - DynamicSettings, - DynamicSettingsSaveResponse, - DynamicSettingsSaveType, -} from '../../../common/runtime_types'; -import { apiService } from './utils'; -import { API_URLS } from '../../../common/constants'; - -const apiPath = API_URLS.DYNAMIC_SETTINGS; - -interface SaveApiRequest { - settings: DynamicSettings; -} - -export const getDynamicSettings = async (): Promise => { - return await apiService.get(apiPath, undefined, DynamicSettingsType); -}; - -export const setDynamicSettings = async ({ - settings, -}: SaveApiRequest): Promise => { - return await apiService.post(apiPath, settings, DynamicSettingsSaveType); -}; diff --git a/x-pack/plugins/synthetics/public/state/api/index_status.ts b/x-pack/plugins/synthetics/public/state/api/index_status.ts deleted file mode 100644 index c6d8f96403eeb..0000000000000 --- a/x-pack/plugins/synthetics/public/state/api/index_status.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { API_URLS } from '../../../common/constants'; -import { StatesIndexStatus, StatesIndexStatusType } from '../../../common/runtime_types'; -import { apiService } from './utils'; - -export const fetchIndexStatus = async (): Promise => { - return await apiService.get(API_URLS.INDEX_STATUS, undefined, StatesIndexStatusType); -}; diff --git a/x-pack/plugins/synthetics/public/state/api/journey.ts b/x-pack/plugins/synthetics/public/state/api/journey.ts deleted file mode 100644 index 958ef5e474fee..0000000000000 --- a/x-pack/plugins/synthetics/public/state/api/journey.ts +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { apiService } from './utils'; -import { FetchJourneyStepsParams } from '../actions/journey'; -import { Ping, PingType } from '../../../common/runtime_types/ping/ping'; -import { - FailedStepsApiResponse, - FailedStepsApiResponseType, - ScreenshotBlockDoc, - ScreenshotImageBlob, - ScreenshotRefImageData, - SyntheticsJourneyApiResponse, - SyntheticsJourneyApiResponseType, -} from '../../../common/runtime_types/ping/synthetics'; -import { API_URLS } from '../../../common/constants'; - -export async function fetchScreenshotBlockSet(params: string[]): Promise { - return apiService.post(API_URLS.JOURNEY_SCREENSHOT_BLOCKS, { - hashes: params, - }); -} - -export async function fetchJourneySteps( - params: FetchJourneyStepsParams -): Promise { - return apiService.get( - `/internal/uptime/journey/${params.checkGroup}`, - { syntheticEventTypes: params.syntheticEventTypes }, - SyntheticsJourneyApiResponseType - ); -} - -export async function fetchJourneysFailedSteps({ - checkGroups, -}: { - checkGroups: string[]; -}): Promise { - return apiService.get(API_URLS.JOURNEY_FAILED_STEPS, { checkGroups }, FailedStepsApiResponseType); -} - -export async function fetchLastSuccessfulCheck({ - monitorId, - timestamp, - stepIndex, - location, -}: { - monitorId: string; - timestamp: string; - stepIndex: number; - location?: string; -}): Promise { - return await apiService.get( - API_URLS.SYNTHETICS_SUCCESSFUL_CHECK, - { - monitorId, - timestamp, - stepIndex, - location, - }, - PingType - ); -} - -export async function getJourneyScreenshot( - imgSrc: string -): Promise { - try { - const imgRequest = new Request(imgSrc); - - const response = await fetch(imgRequest); - - if (response.status !== 200) { - return null; - } - - const contentType = response.headers.get('content-type'); - const stepName = response.headers.get('caption-name'); - const maxSteps = Number(response.headers.get('max-steps') ?? 0); - if (contentType?.indexOf('application/json') !== -1) { - return { - stepName, - maxSteps, - ref: await response.json(), - }; - } else { - return { - stepName, - maxSteps, - src: URL.createObjectURL(await response.blob()), - }; - } - } catch (e) { - return null; - } -} diff --git a/x-pack/plugins/synthetics/public/state/api/monitor.ts b/x-pack/plugins/synthetics/public/state/api/monitor.ts deleted file mode 100644 index ef04b38f37469..0000000000000 --- a/x-pack/plugins/synthetics/public/state/api/monitor.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { BaseParams } from './types'; -import { MonitorDetailsType, MonitorLocationsType } from '../../../common/runtime_types'; -import { QueryParams } from '../actions/types'; -import { apiService } from './utils'; -import { API_URLS } from '../../../common/constants'; - -interface ApiRequest { - monitorId: string; -} - -export type MonitorQueryParams = BaseParams & ApiRequest; - -export const fetchMonitorDetails = async ({ - monitorId, - dateStart, - dateEnd, -}: MonitorQueryParams) => { - const params = { - monitorId, - dateStart, - dateEnd, - }; - return await apiService.get(API_URLS.MONITOR_DETAILS, params, MonitorDetailsType); -}; - -type ApiParams = QueryParams & ApiRequest; - -export const fetchMonitorLocations = async ({ monitorId, dateStart, dateEnd }: ApiParams) => { - const params = { - dateStart, - dateEnd, - monitorId, - }; - return await apiService.get(API_URLS.MONITOR_LOCATIONS, params, MonitorLocationsType); -}; diff --git a/x-pack/plugins/synthetics/public/state/api/monitor_duration.ts b/x-pack/plugins/synthetics/public/state/api/monitor_duration.ts deleted file mode 100644 index c8010e18d0868..0000000000000 --- a/x-pack/plugins/synthetics/public/state/api/monitor_duration.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { BaseParams } from './types'; -import { apiService } from './utils'; -import { API_URLS } from '../../../common/constants'; - -export const fetchMonitorDuration = async ({ monitorId, dateStart, dateEnd }: BaseParams) => { - const queryParams = { - monitorId, - dateStart, - dateEnd, - }; - - return await apiService.get(API_URLS.MONITOR_DURATION, queryParams); -}; diff --git a/x-pack/plugins/synthetics/public/state/api/monitor_list.ts b/x-pack/plugins/synthetics/public/state/api/monitor_list.ts deleted file mode 100644 index 860a46d3e3abc..0000000000000 --- a/x-pack/plugins/synthetics/public/state/api/monitor_list.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { API_URLS } from '../../../common/constants'; -import { apiService } from './utils'; -import { - FetchMonitorStatesQueryArgs, - MonitorSummariesResult, - MonitorSummariesResultType, -} from '../../../common/runtime_types'; - -export const fetchMonitorList = async ( - params: FetchMonitorStatesQueryArgs -): Promise => { - return await apiService.get(API_URLS.MONITOR_LIST, params, MonitorSummariesResultType); -}; diff --git a/x-pack/plugins/synthetics/public/state/api/monitor_management.ts b/x-pack/plugins/synthetics/public/state/api/monitor_management.ts deleted file mode 100644 index a776d33b8147b..0000000000000 --- a/x-pack/plugins/synthetics/public/state/api/monitor_management.ts +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { API_URLS } from '../../../common/constants'; -import { - FetchMonitorManagementListQueryArgs, - MonitorManagementListResultCodec, - MonitorManagementListResult, - MonitorManagementEnablementResultCodec, - MonitorManagementEnablementResult, - ServiceLocations, - SyntheticsMonitor, - EncryptedSyntheticsMonitor, - ServiceLocationsApiResponseCodec, - ServiceLocationErrors, - ThrottlingOptions, - Locations, - SyntheticsMonitorSchedule, -} from '../../../common/runtime_types'; -import { - DecryptedSyntheticsMonitorSavedObject, - SyntheticsServiceAllowed, -} from '../../../common/types'; -import { apiService } from './utils'; - -export const setMonitor = async ({ - monitor, - id, -}: { - monitor: SyntheticsMonitor | EncryptedSyntheticsMonitor; - id?: string; -}): Promise<{ attributes: { errors: ServiceLocationErrors } } | SyntheticsMonitor> => { - if (id) { - return await apiService.put(`${API_URLS.SYNTHETICS_MONITORS}/${id}`, monitor); - } else { - return await apiService.post(API_URLS.SYNTHETICS_MONITORS, monitor); - } -}; - -export const getMonitor = async ({ - id, -}: { - id: string; -}): Promise => { - return await apiService.get(`${API_URLS.SYNTHETICS_MONITORS}/${id}`); -}; - -export const deleteMonitor = async ({ id }: { id: string }): Promise => { - return await apiService.delete(`${API_URLS.SYNTHETICS_MONITORS}/${id}`); -}; - -export const fetchMonitorManagementList = async ( - params: FetchMonitorManagementListQueryArgs -): Promise => { - return await apiService.get( - API_URLS.SYNTHETICS_MONITORS, - params, - MonitorManagementListResultCodec - ); -}; - -export const fetchServiceLocations = async (): Promise<{ - throttling: ThrottlingOptions | undefined; - locations: ServiceLocations; -}> => { - const { throttling, locations } = await apiService.get( - API_URLS.SERVICE_LOCATIONS, - undefined, - ServiceLocationsApiResponseCodec - ); - return { throttling, locations }; -}; - -export const runOnceMonitor = async ({ - monitor, - id, -}: { - monitor: SyntheticsMonitor; - id: string; -}): Promise<{ errors: Array<{ error: Error }> }> => { - return await apiService.post(API_URLS.RUN_ONCE_MONITOR + `/${id}`, monitor); -}; - -export interface TestNowResponse { - schedule: SyntheticsMonitorSchedule; - locations: Locations; - errors?: ServiceLocationErrors; - testRunId: string; - monitorId: string; -} - -export const triggerTestNowMonitor = async ( - configId: string -): Promise => { - return await apiService.get(API_URLS.TRIGGER_MONITOR + `/${configId}`); -}; - -export const fetchGetSyntheticsEnablement = - async (): Promise => { - return await apiService.get( - API_URLS.SYNTHETICS_ENABLEMENT, - undefined, - MonitorManagementEnablementResultCodec - ); - }; - -export const fetchDisableSynthetics = async (): Promise => { - return await apiService.delete(API_URLS.SYNTHETICS_ENABLEMENT); -}; - -export const fetchEnableSynthetics = async (): Promise => { - return await apiService.post(API_URLS.SYNTHETICS_ENABLEMENT); -}; - -export const fetchServiceAllowed = async (): Promise => { - return await apiService.get(API_URLS.SERVICE_ALLOWED); -}; diff --git a/x-pack/plugins/synthetics/public/state/api/monitor_status.ts b/x-pack/plugins/synthetics/public/state/api/monitor_status.ts deleted file mode 100644 index 9c4e7e3eaf5d3..0000000000000 --- a/x-pack/plugins/synthetics/public/state/api/monitor_status.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 { QueryParams } from '../actions/types'; -import { Ping } from '../../../common/runtime_types'; -import { API_URLS } from '../../../common/constants'; -import { apiService } from './utils'; - -export const fetchMonitorStatus = async ({ - monitorId, - dateStart, - dateEnd, -}: QueryParams): Promise => { - const queryParams = { - monitorId, - dateStart, - dateEnd, - }; - - return await apiService.get(API_URLS.MONITOR_STATUS, queryParams); -}; diff --git a/x-pack/plugins/synthetics/public/state/api/network_events.ts b/x-pack/plugins/synthetics/public/state/api/network_events.ts deleted file mode 100644 index d9b3d518444a3..0000000000000 --- a/x-pack/plugins/synthetics/public/state/api/network_events.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 { apiService } from './utils'; -import { FetchNetworkEventsParams } from '../actions/network_events'; -import { - SyntheticsNetworkEventsApiResponse, - SyntheticsNetworkEventsApiResponseType, -} from '../../../common/runtime_types'; -import { API_URLS } from '../../../common/constants'; - -export async function fetchNetworkEvents( - params: FetchNetworkEventsParams -): Promise { - return (await apiService.get( - API_URLS.NETWORK_EVENTS, - { - checkGroup: params.checkGroup, - stepIndex: params.stepIndex, - }, - SyntheticsNetworkEventsApiResponseType - )) as SyntheticsNetworkEventsApiResponse; -} diff --git a/x-pack/plugins/synthetics/public/state/api/ping.ts b/x-pack/plugins/synthetics/public/state/api/ping.ts deleted file mode 100644 index e4fc5cc620b55..0000000000000 --- a/x-pack/plugins/synthetics/public/state/api/ping.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { APIFn } from './types'; -import { - PingsResponseType, - PingsResponse, - GetPingsParams, - GetPingHistogramParams, - HistogramResult, -} from '../../../common/runtime_types'; -import { apiService } from './utils'; -import { API_URLS } from '../../../common/constants'; - -export const fetchPings: APIFn = async ({ - dateRange: { from, to }, - ...optional -}) => await apiService.get(API_URLS.PINGS, { from, to, ...optional }, PingsResponseType); - -export const fetchPingHistogram: APIFn = async ({ - monitorId, - dateStart, - dateEnd, - filters, - bucketSize, - query, -}) => { - const queryParams = { - dateStart, - dateEnd, - monitorId, - filters, - bucketSize, - query, - }; - - return await apiService.get(API_URLS.PING_HISTOGRAM, queryParams); -}; diff --git a/x-pack/plugins/synthetics/public/state/api/snapshot.ts b/x-pack/plugins/synthetics/public/state/api/snapshot.ts deleted file mode 100644 index d8f38128e3202..0000000000000 --- a/x-pack/plugins/synthetics/public/state/api/snapshot.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 { SnapshotType, Snapshot } from '../../../common/runtime_types'; -import { apiService } from './utils'; -import { API_URLS } from '../../../common/constants'; - -export interface SnapShotQueryParams { - dateRangeStart: string; - dateRangeEnd: string; - filters?: string; - query?: string; -} - -export const fetchSnapshotCount = async ({ - dateRangeStart, - dateRangeEnd, - filters, - query, -}: SnapShotQueryParams): Promise => { - const queryParams = { - dateRangeStart, - dateRangeEnd, - ...(filters && { filters }), - ...(query && { query }), - }; - - return await apiService.get(API_URLS.SNAPSHOT_COUNT, queryParams, SnapshotType); -}; diff --git a/x-pack/plugins/synthetics/public/state/effects/dynamic_settings.ts b/x-pack/plugins/synthetics/public/state/effects/dynamic_settings.ts deleted file mode 100644 index 04cdb13f5612c..0000000000000 --- a/x-pack/plugins/synthetics/public/state/effects/dynamic_settings.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { takeLeading, put, call, takeLatest } from 'redux-saga/effects'; -import { Action } from 'redux-actions'; -import { i18n } from '@kbn/i18n'; -import { fetchEffectFactory } from './fetch_effect'; -import { - getDynamicSettings, - getDynamicSettingsSuccess, - getDynamicSettingsFail, - setDynamicSettingsSuccess, - setDynamicSettingsFail, - setDynamicSettings, -} from '../actions/dynamic_settings'; -import { - getDynamicSettings as getDynamicSettingsAPI, - setDynamicSettings as setDynamicSettingsAPI, -} from '../api'; -import { DynamicSettings } from '../../../common/runtime_types'; -import { kibanaService } from '../kibana_service'; - -export function* fetchDynamicSettingsEffect() { - yield takeLeading( - String(getDynamicSettings), - fetchEffectFactory(getDynamicSettingsAPI, getDynamicSettingsSuccess, getDynamicSettingsFail) - ); -} - -export function* setDynamicSettingsEffect() { - const couldNotSaveSettingsText = i18n.translate('xpack.synthetics.settings.error.couldNotSave', { - defaultMessage: 'Could not save settings!', - }); - yield takeLatest(String(setDynamicSettings), function* (action: Action) { - try { - if (!action.payload) { - const err = new Error('Cannot fetch effect without a payload'); - yield put(setDynamicSettingsFail(err)); - - kibanaService.core.notifications.toasts.addError(err, { - title: couldNotSaveSettingsText, - }); - return; - } - yield call(setDynamicSettingsAPI, { settings: action.payload }); - yield put(setDynamicSettingsSuccess(action.payload)); - kibanaService.core.notifications.toasts.addSuccess( - i18n.translate('xpack.synthetics.settings.saveSuccess', { - defaultMessage: 'Settings saved!', - }) - ); - } catch (err) { - kibanaService.core.notifications.toasts.addError(err, { - title: couldNotSaveSettingsText, - }); - yield put(setDynamicSettingsFail(err)); - } - }); -} diff --git a/x-pack/plugins/synthetics/public/state/effects/journey.ts b/x-pack/plugins/synthetics/public/state/effects/journey.ts deleted file mode 100644 index f7c1e23742e69..0000000000000 --- a/x-pack/plugins/synthetics/public/state/effects/journey.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 { Action } from 'redux-actions'; -import { call, put, takeEvery } from 'redux-saga/effects'; -import { - getJourneySteps, - getJourneyStepsSuccess, - getJourneyStepsFail, - FetchJourneyStepsParams, -} from '../actions/journey'; -import { fetchJourneySteps } from '../api/journey'; -import type { SyntheticsJourneyApiResponse } from '../../../common/runtime_types'; - -const inFlightStepRequests: Record = {}; - -export function* fetchJourneyStepsEffect(): Generator { - yield takeEvery(getJourneySteps, function* (action: Action) { - if (inFlightStepRequests[action.payload.checkGroup]) return; - - try { - inFlightStepRequests[action.payload.checkGroup] = true; - const response = (yield call( - fetchJourneySteps, - action.payload - )) as SyntheticsJourneyApiResponse; - yield put(getJourneyStepsSuccess(response)); - } catch (e) { - yield put(getJourneyStepsFail({ checkGroup: action.payload.checkGroup, error: e })); - } finally { - delete inFlightStepRequests[action.payload.checkGroup]; - } - }); -} diff --git a/x-pack/plugins/synthetics/public/state/effects/monitor_management.ts b/x-pack/plugins/synthetics/public/state/effects/monitor_management.ts deleted file mode 100644 index 60a6676721784..0000000000000 --- a/x-pack/plugins/synthetics/public/state/effects/monitor_management.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { takeLatest, takeLeading } from 'redux-saga/effects'; -import { - getMonitors, - getMonitorsSuccess, - getMonitorsFailure, - getServiceLocations, - getServiceLocationsSuccess, - getServiceLocationsFailure, - getSyntheticsEnablement, - getSyntheticsEnablementSuccess, - getSyntheticsEnablementFailure, - disableSynthetics, - disableSyntheticsSuccess, - disableSyntheticsFailure, - enableSynthetics, - enableSyntheticsSuccess, - enableSyntheticsFailure, - getSyntheticsServiceAllowed, -} from '../actions'; -import { - fetchMonitorManagementList, - fetchServiceLocations, - fetchServiceAllowed, - fetchGetSyntheticsEnablement, - fetchDisableSynthetics, - fetchEnableSynthetics, -} from '../api'; -import { fetchEffectFactory } from './fetch_effect'; - -export function* fetchMonitorManagementEffect() { - yield takeLatest( - getMonitors, - fetchEffectFactory(fetchMonitorManagementList, getMonitorsSuccess, getMonitorsFailure) - ); - yield takeLeading( - getServiceLocations, - fetchEffectFactory( - fetchServiceLocations, - getServiceLocationsSuccess, - getServiceLocationsFailure - ) - ); - yield takeLatest( - getSyntheticsEnablement, - fetchEffectFactory( - fetchGetSyntheticsEnablement, - getSyntheticsEnablementSuccess, - getSyntheticsEnablementFailure - ) - ); - yield takeLatest( - disableSynthetics, - fetchEffectFactory(fetchDisableSynthetics, disableSyntheticsSuccess, disableSyntheticsFailure) - ); - yield takeLatest( - enableSynthetics, - fetchEffectFactory(fetchEnableSynthetics, enableSyntheticsSuccess, enableSyntheticsFailure) - ); -} - -export function* fetchSyntheticsServiceAllowedEffect() { - yield takeLeading( - getSyntheticsServiceAllowed.get, - fetchEffectFactory( - fetchServiceAllowed, - getSyntheticsServiceAllowed.success, - getSyntheticsServiceAllowed.fail - ) - ); -} diff --git a/x-pack/plugins/synthetics/public/state/effects/network_events.ts b/x-pack/plugins/synthetics/public/state/effects/network_events.ts deleted file mode 100644 index 75ea3d4467eb4..0000000000000 --- a/x-pack/plugins/synthetics/public/state/effects/network_events.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { Action } from 'redux-actions'; -import { call, put, takeLatest } from 'redux-saga/effects'; -import { - getNetworkEvents, - getNetworkEventsSuccess, - getNetworkEventsFail, - FetchNetworkEventsParams, -} from '../actions/network_events'; -import { fetchNetworkEvents } from '../api/network_events'; -import type { SyntheticsNetworkEventsApiResponse } from '../../../common/runtime_types'; - -export function* fetchNetworkEventsEffect() { - yield takeLatest( - getNetworkEvents, - function* (action: Action): Generator { - try { - const response = (yield call( - fetchNetworkEvents, - action.payload - )) as SyntheticsNetworkEventsApiResponse; - - yield put( - getNetworkEventsSuccess({ - checkGroup: action.payload.checkGroup, - stepIndex: action.payload.stepIndex, - ...response, - }) - ); - } catch (e) { - yield put( - getNetworkEventsFail({ - checkGroup: action.payload.checkGroup, - stepIndex: action.payload.stepIndex, - error: e, - }) - ); - } - } - ); -} diff --git a/x-pack/plugins/synthetics/public/state/reducers/dynamic_settings.ts b/x-pack/plugins/synthetics/public/state/reducers/dynamic_settings.ts deleted file mode 100644 index 93f30c77f5536..0000000000000 --- a/x-pack/plugins/synthetics/public/state/reducers/dynamic_settings.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 { handleActions, Action } from 'redux-actions'; -import { - getDynamicSettings, - getDynamicSettingsSuccess, - getDynamicSettingsFail, - setDynamicSettings, - setDynamicSettingsSuccess, - setDynamicSettingsFail, -} from '../actions/dynamic_settings'; -import { DynamicSettings } from '../../../common/runtime_types'; - -export interface DynamicSettingsState { - settings?: DynamicSettings; - loadError?: Error; - saveError?: Error; - loading: boolean; -} - -const initialState: DynamicSettingsState = { - loading: true, -}; - -export const dynamicSettingsReducer = handleActions( - { - [String(getDynamicSettings)]: (state) => ({ - ...state, - loading: true, - }), - [String(getDynamicSettingsSuccess)]: (_state, action: Action) => ({ - loading: false, - settings: action.payload, - }), - [String(getDynamicSettingsFail)]: (_state, action: Action) => ({ - loading: false, - loadError: action.payload, - }), - [String(setDynamicSettings)]: (state) => ({ - ...state, - loading: true, - }), - [String(setDynamicSettingsSuccess)]: (_state, action: Action) => ({ - settings: action.payload, - saveSucceded: true, - loading: false, - }), - [String(setDynamicSettingsFail)]: (state, action: Action) => ({ - ...state, - loading: false, - saveSucceeded: false, - saveError: action.payload, - }), - }, - initialState -); diff --git a/x-pack/plugins/synthetics/public/state/reducers/index_status.ts b/x-pack/plugins/synthetics/public/state/reducers/index_status.ts deleted file mode 100644 index 7c3aecd98f706..0000000000000 --- a/x-pack/plugins/synthetics/public/state/reducers/index_status.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { handleActions } from 'redux-actions'; -import { indexStatusAction } from '../actions'; -import { asyncInitState, handleAsyncAction } from './utils'; -import { AsyncInitState } from './types'; -import { StatesIndexStatus } from '../../../common/runtime_types'; - -export interface IndexStatusState { - indexStatus: AsyncInitState; -} - -const initialState: IndexStatusState = { - indexStatus: asyncInitState(), -}; - -type PayLoad = StatesIndexStatus & Error; - -export const indexStatusReducer = handleActions( - { - ...handleAsyncAction('indexStatus', indexStatusAction), - }, - initialState -); diff --git a/x-pack/plugins/synthetics/public/state/reducers/journey.ts b/x-pack/plugins/synthetics/public/state/reducers/journey.ts deleted file mode 100644 index 98bbd93a24e0c..0000000000000 --- a/x-pack/plugins/synthetics/public/state/reducers/journey.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { handleActions, Action } from 'redux-actions'; -import { JourneyStep, SyntheticsJourneyApiResponse } from '../../../common/runtime_types'; -import { pruneJourneyState } from '../actions/journey'; -import { - FetchJourneyStepsParams, - GetJourneyFailPayload, - getJourneySteps, - getJourneyStepsFail, - getJourneyStepsSuccess, -} from '../actions/journey'; - -export interface JourneyState { - checkGroup: string; - steps: JourneyStep[]; - details?: SyntheticsJourneyApiResponse['details']; - loading: boolean; - error?: Error; -} - -export interface JourneyKVP { - [checkGroup: string]: JourneyState; -} - -const initialState: JourneyKVP = {}; - -type Payload = FetchJourneyStepsParams & - SyntheticsJourneyApiResponse & - GetJourneyFailPayload & - string[]; - -export const journeyReducer = handleActions( - { - [String(getJourneySteps)]: ( - state: JourneyKVP, - { payload: { checkGroup } }: Action - ) => ({ - ...state, - // add an empty entry while fetching the check group, - // or update the previously-loaded entry to a new loading state - [checkGroup]: state[checkGroup] - ? { - ...state[checkGroup], - loading: true, - } - : { - checkGroup, - steps: [], - loading: true, - }, - }), - - [String(getJourneyStepsSuccess)]: ( - state: JourneyKVP, - { payload: { checkGroup, steps, details } }: Action - ) => ({ - ...state, - [checkGroup]: { - loading: false, - checkGroup, - steps, - details, - }, - }), - - [String(getJourneyStepsFail)]: ( - state: JourneyKVP, - { payload: { checkGroup, error } }: Action - ) => ({ - ...state, - [checkGroup]: state[checkGroup] - ? { - ...state[checkGroup], - loading: false, - error, - } - : { - checkGroup, - loading: false, - steps: [], - error, - }, - }), - - [String(pruneJourneyState)]: (state: JourneyKVP, action: Action) => - action.payload.reduce( - (prev, cur) => ({ - ...prev, - [cur]: state[cur], - }), - {} - ), - }, - initialState -); diff --git a/x-pack/plugins/synthetics/public/state/reducers/monitor.ts b/x-pack/plugins/synthetics/public/state/reducers/monitor.ts deleted file mode 100644 index 3c5be59ea9dda..0000000000000 --- a/x-pack/plugins/synthetics/public/state/reducers/monitor.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Action } from 'redux-actions'; -import { - MonitorDetailsState, - getMonitorDetailsAction, - getMonitorLocationsAction, - getMonitorLocationsActionSuccess, - getMonitorLocationsActionFail, -} from '../actions/monitor'; -import { MonitorLocations } from '../../../common/runtime_types'; - -type MonitorLocationsList = Map; - -export interface MonitorState { - loading: boolean; - errors: any[]; - monitorDetailsList: MonitorDetailsState[]; - monitorLocationsList: MonitorLocationsList; -} - -const initialState: MonitorState = { - monitorDetailsList: [], - monitorLocationsList: new Map(), - loading: false, - errors: [], -}; - -export function monitorReducer(state = initialState, action: Action): MonitorState { - switch (action.type) { - case String(getMonitorDetailsAction.get): - return { - ...state, - loading: true, - }; - case String(getMonitorDetailsAction.success): - const { monitorId } = action.payload; - return { - ...state, - monitorDetailsList: { - ...state.monitorDetailsList, - [monitorId]: action.payload, - }, - loading: false, - }; - case String(getMonitorDetailsAction.fail): - return { - ...state, - errors: [...state.errors, action.payload], - loading: false, - }; - case String(getMonitorLocationsAction): - return { - ...state, - loading: true, - }; - case String(getMonitorLocationsActionSuccess): - const monLocations = state.monitorLocationsList; - monLocations.set(action.payload.monitorId, action.payload); - return { - ...state, - monitorLocationsList: monLocations, - loading: false, - }; - case String(getMonitorLocationsActionFail): - return { - ...state, - errors: [...state.errors, action.payload], - }; - default: - return state; - } -} diff --git a/x-pack/plugins/synthetics/public/state/reducers/monitor_duration.ts b/x-pack/plugins/synthetics/public/state/reducers/monitor_duration.ts deleted file mode 100644 index 51eae6049b309..0000000000000 --- a/x-pack/plugins/synthetics/public/state/reducers/monitor_duration.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { handleActions, Action } from 'redux-actions'; -import { - getMonitorDurationAction, - getMonitorDurationActionSuccess, - getMonitorDurationActionFail, -} from '../actions'; -import { MonitorDurationResult } from '../../../common/types'; - -export interface MonitorDuration { - durationLines: MonitorDurationResult | null; - errors: any[]; - loading: boolean; -} - -const initialState: MonitorDuration = { - durationLines: null, - loading: false, - errors: [], -}; - -type Payload = MonitorDurationResult & Error; - -export const monitorDurationReducer = handleActions( - { - [String(getMonitorDurationAction)]: (state: MonitorDuration) => ({ - ...state, - loading: true, - }), - - [String(getMonitorDurationActionSuccess)]: ( - state: MonitorDuration, - action: Action - ) => ({ - ...state, - loading: false, - durationLines: { ...action.payload }, - }), - - [String(getMonitorDurationActionFail)]: (state: MonitorDuration, action: Action) => ({ - ...state, - errors: [...state.errors, action.payload], - loading: false, - }), - }, - initialState -); diff --git a/x-pack/plugins/synthetics/public/state/reducers/monitor_list.ts b/x-pack/plugins/synthetics/public/state/reducers/monitor_list.ts deleted file mode 100644 index 3630842cb9f8f..0000000000000 --- a/x-pack/plugins/synthetics/public/state/reducers/monitor_list.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { handleActions, Action } from 'redux-actions'; -import { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public'; -import { - getMonitorList, - getMonitorListSuccess, - getMonitorListFailure, - getUpdatedMonitor, - clearRefreshedMonitorId, - setUpdatingMonitorId, -} from '../actions'; -import { MonitorSummariesResult } from '../../../common/runtime_types'; -import { AppState } from '..'; -import { TestNowResponse } from '../api'; - -export interface MonitorList { - loading: boolean; - refreshedMonitorIds?: string[]; - isUpdating?: string[]; - list: MonitorSummariesResult; - error?: IHttpFetchError; -} - -export const initialState: MonitorList = { - list: { - nextPagePagination: null, - prevPagePagination: null, - summaries: [], - }, - loading: false, - refreshedMonitorIds: [], -}; - -type Payload = MonitorSummariesResult & - IHttpFetchError & - string & - TestNowResponse; - -export const monitorListReducer = handleActions( - { - [String(getMonitorList)]: (state: MonitorList) => ({ - ...state, - loading: true, - }), - [String(getMonitorListSuccess)]: ( - state: MonitorList, - action: Action - ) => ({ - ...state, - loading: false, - error: undefined, - list: { ...action.payload }, - }), - [String(getMonitorListFailure)]: ( - state: MonitorList, - action: Action> - ) => ({ - ...state, - error: action.payload, - loading: false, - }), - [String(setUpdatingMonitorId)]: (state: MonitorList, action: Action) => ({ - ...state, - isUpdating: [...(state.isUpdating ?? []), action.payload], - }), - [String(getUpdatedMonitor.get)]: (state: MonitorList) => ({ - ...state, - }), - [String(getUpdatedMonitor.success)]: ( - state: MonitorList, - action: Action - ) => { - const summaries = state.list.summaries; - - const newSummary = action.payload.summaries?.[0]; - - if (!newSummary) { - return { ...state, isUpdating: [] }; - } - - return { - ...state, - loading: false, - error: undefined, - isUpdating: state.isUpdating?.filter((item) => item !== newSummary.monitor_id), - refreshedMonitorIds: [...(state.refreshedMonitorIds ?? []), newSummary.monitor_id], - list: { - ...state.list, - summaries: summaries.map((summary) => { - if (summary.monitor_id === newSummary.monitor_id) { - return newSummary; - } - return summary; - }), - }, - }; - }, - [String(getUpdatedMonitor.fail)]: ( - state: MonitorList, - action: Action> - ) => ({ - ...state, - error: action.payload, - loading: false, - isUpdating: [], - }), - [String(clearRefreshedMonitorId)]: (state: MonitorList, action: Action) => ({ - ...state, - refreshedMonitorIds: (state.refreshedMonitorIds ?? []).filter( - (item) => item !== action.payload - ), - }), - }, - initialState -); - -export const refreshedMonitorSelector = ({ monitorList }: AppState) => { - return monitorList.refreshedMonitorIds ?? []; -}; - -export const isUpdatingMonitorSelector = ({ monitorList }: AppState) => - monitorList.isUpdating ?? []; diff --git a/x-pack/plugins/synthetics/public/state/reducers/monitor_management.ts b/x-pack/plugins/synthetics/public/state/reducers/monitor_management.ts deleted file mode 100644 index d60de74c1f54c..0000000000000 --- a/x-pack/plugins/synthetics/public/state/reducers/monitor_management.ts +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { createReducer, PayloadAction } from '@reduxjs/toolkit'; -import { WritableDraft } from 'immer/dist/types/types-external'; -import { - getMonitors, - getMonitorsSuccess, - getMonitorsFailure, - getServiceLocations, - getServiceLocationsSuccess, - getServiceLocationsFailure, - getSyntheticsEnablement, - getSyntheticsEnablementSuccess, - getSyntheticsEnablementFailure, - disableSynthetics, - disableSyntheticsSuccess, - disableSyntheticsFailure, - enableSynthetics, - enableSyntheticsSuccess, - enableSyntheticsFailure, - getSyntheticsServiceAllowed, -} from '../actions'; -import { - MonitorManagementEnablementResult, - MonitorManagementListResult, - ServiceLocations, - ThrottlingOptions, - DEFAULT_THROTTLING, -} from '../../../common/runtime_types'; -import { SyntheticsServiceAllowed } from '../../../common/types'; - -export interface MonitorManagementList { - error: Record<'monitorList' | 'serviceLocations' | 'enablement', Error | null>; - loading: Record<'monitorList' | 'serviceLocations' | 'enablement', boolean>; - list: MonitorManagementListResult; - locations: ServiceLocations; - enablement: MonitorManagementEnablementResult | null; - syntheticsService: { isAllowed?: boolean; signupUrl: string | null; loading: boolean }; - throttling: ThrottlingOptions; -} - -export const initialState: MonitorManagementList = { - list: { - page: 1, - perPage: 10, - total: null, - monitors: [], - syncErrors: [], - }, - locations: [], - enablement: null, - loading: { - monitorList: false, - serviceLocations: false, - enablement: false, - }, - error: { - monitorList: null, - serviceLocations: null, - enablement: null, - }, - syntheticsService: { - signupUrl: null, - loading: false, - }, - throttling: DEFAULT_THROTTLING, -}; - -export const monitorManagementListReducer = createReducer(initialState, (builder) => { - builder - .addCase(getMonitors, (state: WritableDraft) => ({ - ...state, - loading: { - ...state.loading, - monitorList: true, - }, - })) - .addCase( - getMonitorsSuccess, - ( - state: WritableDraft, - action: PayloadAction - ) => ({ - ...state, - loading: { - ...state.loading, - monitorList: false, - }, - error: { - ...state.error, - monitorList: null, - }, - list: { ...action.payload }, - }) - ) - .addCase( - getMonitorsFailure, - (state: WritableDraft, action: PayloadAction) => ({ - ...state, - loading: { - ...state.loading, - monitorList: false, - }, - error: { - ...state.error, - monitorList: action.payload, - }, - }) - ) - .addCase(getServiceLocations, (state: WritableDraft) => ({ - ...state, - loading: { - ...state.loading, - serviceLocations: true, - }, - })) - .addCase( - getServiceLocationsSuccess, - ( - state: WritableDraft, - action: PayloadAction<{ - throttling: ThrottlingOptions | undefined; - locations: ServiceLocations; - }> - ) => ({ - ...state, - loading: { - ...state.loading, - serviceLocations: false, - }, - error: { - ...state.error, - serviceLocations: null, - }, - locations: action.payload.locations, - throttling: action.payload.throttling || DEFAULT_THROTTLING, - }) - ) - .addCase( - getServiceLocationsFailure, - (state: WritableDraft, action: PayloadAction) => ({ - ...state, - loading: { - ...state.loading, - serviceLocations: false, - }, - error: { - ...state.error, - serviceLocations: action.payload, - }, - }) - ) - .addCase(getSyntheticsEnablement, (state: WritableDraft) => ({ - ...state, - loading: { - ...state.loading, - enablement: true, - }, - })) - .addCase( - getSyntheticsEnablementSuccess, - (state: WritableDraft, action: PayloadAction) => ({ - ...state, - loading: { - ...state.loading, - enablement: false, - }, - error: { - ...state.error, - enablement: null, - }, - enablement: action.payload, - }) - ) - .addCase( - getSyntheticsEnablementFailure, - (state: WritableDraft, action: PayloadAction) => ({ - ...state, - loading: { - ...state.loading, - enablement: false, - }, - error: { - ...state.error, - enablement: action.payload, - }, - }) - ) - .addCase(disableSynthetics, (state: WritableDraft) => ({ - ...state, - loading: { - ...state.loading, - enablement: true, - }, - })) - .addCase(disableSyntheticsSuccess, (state: WritableDraft) => ({ - ...state, - loading: { - ...state.loading, - enablement: false, - }, - error: { - ...state.error, - enablement: null, - }, - enablement: { - canEnable: state.enablement?.canEnable || false, - areApiKeysEnabled: state.enablement?.areApiKeysEnabled || false, - isEnabled: false, - }, - })) - .addCase( - disableSyntheticsFailure, - (state: WritableDraft, action: PayloadAction) => ({ - ...state, - loading: { - ...state.loading, - enablement: false, - }, - error: { - ...state.error, - enablement: action.payload, - }, - }) - ) - .addCase(enableSynthetics, (state: WritableDraft) => ({ - ...state, - loading: { - ...state.loading, - enablement: true, - }, - })) - .addCase(enableSyntheticsSuccess, (state: WritableDraft) => ({ - ...state, - loading: { - ...state.loading, - enablement: false, - }, - error: { - ...state.error, - enablement: null, - }, - enablement: { - canEnable: state.enablement?.canEnable || false, - areApiKeysEnabled: state.enablement?.areApiKeysEnabled || false, - isEnabled: true, - }, - })) - .addCase( - enableSyntheticsFailure, - (state: WritableDraft, action: PayloadAction) => ({ - ...state, - loading: { - ...state.loading, - enablement: false, - }, - error: { - ...state.error, - enablement: action.payload, - }, - }) - ) - .addCase( - String(getSyntheticsServiceAllowed.get), - (state: WritableDraft) => ({ - ...state, - syntheticsService: { - isAllowed: state.syntheticsService?.isAllowed, - signupUrl: state.syntheticsService?.signupUrl, - loading: true, - }, - }) - ) - .addCase( - String(getSyntheticsServiceAllowed.success), - ( - state: WritableDraft, - action: PayloadAction - ) => ({ - ...state, - syntheticsService: { - isAllowed: action.payload.serviceAllowed, - signupUrl: action.payload.signupUrl, - loading: false, - }, - }) - ) - .addCase( - String(getSyntheticsServiceAllowed.fail), - (state: WritableDraft, action: PayloadAction) => ({ - ...state, - syntheticsService: { - isAllowed: false, - signupUrl: null, - loading: false, - }, - }) - ); -}); diff --git a/x-pack/plugins/synthetics/public/state/reducers/monitor_status.ts b/x-pack/plugins/synthetics/public/state/reducers/monitor_status.ts deleted file mode 100644 index b27de0d944a8e..0000000000000 --- a/x-pack/plugins/synthetics/public/state/reducers/monitor_status.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { handleActions, Action } from 'redux-actions'; -import { - getMonitorStatusAction, - getMonitorStatusActionSuccess, - getMonitorStatusActionFail, -} from '../actions'; -import { Ping } from '../../../common/runtime_types'; -import { QueryParams } from '../actions/types'; - -export interface MonitorStatusState { - status: Ping | null; - loading: boolean; -} - -export const initialState: MonitorStatusState = { - status: null, - loading: false, -}; - -export type MonitorStatusPayload = QueryParams & Ping; - -export const monitorStatusReducer = handleActions( - { - [String(getMonitorStatusAction)]: (state, action) => ({ - ...state, - // reset state if monitorId changes - status: action.payload.monitorId === state?.status?.monitor?.id ? state.status : null, - loading: true, - }), - - [String(getMonitorStatusActionSuccess)]: (state, action: Action) => { - return { - ...state, - loading: false, - // Keeping url from prev request to display, if there is no latest status - status: { - url: action.payload?.url || state.status?.url, - ...action.payload, - } as Ping, - }; - }, - - [String(getMonitorStatusActionFail)]: (state) => ({ - ...state, - loading: false, - }), - }, - initialState -); diff --git a/x-pack/plugins/synthetics/public/state/reducers/network_events.ts b/x-pack/plugins/synthetics/public/state/reducers/network_events.ts deleted file mode 100644 index e58a8f75c7fa3..0000000000000 --- a/x-pack/plugins/synthetics/public/state/reducers/network_events.ts +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { handleActions, Action } from 'redux-actions'; -import { NetworkEvent, SyntheticsNetworkEventsApiResponse } from '../../../common/runtime_types'; -import { - FetchNetworkEventsParams, - FetchNetworkEventsFailPayload, - getNetworkEvents, - getNetworkEventsFail, - getNetworkEventsSuccess, -} from '../actions/network_events'; - -export interface NetworkEventsState { - [checkGroup: string]: { - [stepIndex: number]: { - events: NetworkEvent[]; - total: number; - loading: boolean; - error?: Error; - isWaterfallSupported: boolean; - hasNavigationRequest?: boolean; - }; - }; -} - -const initialState: NetworkEventsState = {}; - -type Payload = FetchNetworkEventsParams & - SyntheticsNetworkEventsApiResponse & - FetchNetworkEventsFailPayload & - string[]; - -export const networkEventsReducer = handleActions( - { - [String(getNetworkEvents)]: ( - state: NetworkEventsState, - { payload: { checkGroup, stepIndex } }: Action - ) => ({ - ...state, - [checkGroup]: state[checkGroup] - ? { - [stepIndex]: state[checkGroup][stepIndex] - ? { - ...state[checkGroup][stepIndex], - loading: true, - events: [], - total: 0, - isWaterfallSupported: true, - } - : { - loading: true, - events: [], - total: 0, - isWaterfallSupported: true, - }, - } - : { - [stepIndex]: { - loading: true, - events: [], - total: 0, - isWaterfallSupported: true, - }, - }, - }), - - [String(getNetworkEventsSuccess)]: ( - state: NetworkEventsState, - { - payload: { - events, - total, - checkGroup, - stepIndex, - isWaterfallSupported, - hasNavigationRequest, - }, - }: Action - ) => { - return { - ...state, - [checkGroup]: state[checkGroup] - ? { - [stepIndex]: state[checkGroup][stepIndex] - ? { - ...state[checkGroup][stepIndex], - loading: false, - events, - total, - isWaterfallSupported, - hasNavigationRequest, - } - : { - loading: false, - events, - total, - isWaterfallSupported, - hasNavigationRequest, - }, - } - : { - [stepIndex]: { - loading: false, - events, - total, - isWaterfallSupported, - hasNavigationRequest, - }, - }, - }; - }, - - [String(getNetworkEventsFail)]: ( - state: NetworkEventsState, - { payload: { checkGroup, stepIndex, error } }: Action - ) => ({ - ...state, - [checkGroup]: state[checkGroup] - ? { - [stepIndex]: state[checkGroup][stepIndex] - ? { - ...state[checkGroup][stepIndex], - loading: false, - events: [], - total: 0, - error, - isWaterfallSupported: true, - } - : { - loading: false, - events: [], - total: 0, - error, - isWaterfallSupported: true, - }, - } - : { - [stepIndex]: { - loading: false, - events: [], - total: 0, - error, - isWaterfallSupported: true, - }, - }, - }), - }, - initialState -); diff --git a/x-pack/plugins/synthetics/public/state/reducers/ping.ts b/x-pack/plugins/synthetics/public/state/reducers/ping.ts deleted file mode 100644 index a91734d77b4ab..0000000000000 --- a/x-pack/plugins/synthetics/public/state/reducers/ping.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { handleActions, Action } from 'redux-actions'; -import { getPingHistogram } from '../actions'; -import { HistogramResult } from '../../../common/runtime_types'; - -export interface PingState { - pingHistogram: HistogramResult | null; - errors: any[]; - loading: boolean; -} - -const initialState: PingState = { - pingHistogram: null, - loading: false, - errors: [], -}; - -type MonitorStatusPayload = HistogramResult & Error; - -export const pingReducer = handleActions( - { - [String(getPingHistogram.get)]: (state) => ({ - ...state, - loading: true, - }), - - [String(getPingHistogram.success)]: (state: PingState, action: Action) => ({ - ...state, - loading: false, - pingHistogram: { ...action.payload }, - }), - - [String(getPingHistogram.fail)]: (state, action: Action) => ({ - ...state, - errors: [...state.errors, action.payload], - loading: false, - }), - }, - initialState -); diff --git a/x-pack/plugins/synthetics/public/utils/api_service/api_service.ts b/x-pack/plugins/synthetics/public/utils/api_service/api_service.ts new file mode 100644 index 0000000000000..cbba8cf02cf22 --- /dev/null +++ b/x-pack/plugins/synthetics/public/utils/api_service/api_service.ts @@ -0,0 +1,127 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isRight } from 'fp-ts/lib/Either'; +import { formatErrors } from '@kbn/securitysolution-io-ts-utils'; +import { HttpFetchQuery, HttpSetup } from '@kbn/core/public'; +import { FETCH_STATUS, AddInspectorRequest } from '@kbn/observability-plugin/public'; + +class ApiService { + private static instance: ApiService; + private _http!: HttpSetup; + private _addInspectorRequest!: AddInspectorRequest; + + public get http() { + return this._http; + } + + public set http(httpSetup: HttpSetup) { + this._http = httpSetup; + } + + public get addInspectorRequest() { + return this._addInspectorRequest; + } + + public set addInspectorRequest(addInspectorRequest: AddInspectorRequest) { + this._addInspectorRequest = addInspectorRequest; + } + + private constructor() {} + + static getInstance(): ApiService { + if (!ApiService.instance) { + ApiService.instance = new ApiService(); + } + + return ApiService.instance; + } + + public async get( + apiUrl: string, + params?: HttpFetchQuery, + decodeType?: any, + asResponse = false + ) { + const response = await this._http!.fetch({ + path: apiUrl, + query: params, + asResponse, + }); + + this.addInspectorRequest?.({ data: response, status: FETCH_STATUS.SUCCESS, loading: false }); + + if (decodeType) { + const decoded = decodeType.decode(response); + if (isRight(decoded)) { + return decoded.right as T; + } else { + // eslint-disable-next-line no-console + console.error( + `API ${apiUrl} is not returning expected response, ${formatErrors( + decoded.left + )} for response`, + response + ); + } + } + + return response; + } + + public async post(apiUrl: string, data?: any, decodeType?: any) { + const response = await this._http!.post(apiUrl, { + method: 'POST', + body: JSON.stringify(data), + }); + + this.addInspectorRequest?.({ data: response, status: FETCH_STATUS.SUCCESS, loading: false }); + + if (decodeType) { + const decoded = decodeType.decode(response); + if (isRight(decoded)) { + return decoded.right as T; + } else { + // eslint-disable-next-line no-console + console.warn( + `API ${apiUrl} is not returning expected response, ${formatErrors(decoded.left)}` + ); + } + } + return response; + } + + public async put(apiUrl: string, data?: any, decodeType?: any) { + const response = await this._http!.put(apiUrl, { + method: 'PUT', + body: JSON.stringify(data), + }); + + if (decodeType) { + const decoded = decodeType.decode(response); + if (isRight(decoded)) { + return decoded.right as T; + } else { + // eslint-disable-next-line no-console + console.warn( + `API ${apiUrl} is not returning expected response, ${formatErrors(decoded.left)}` + ); + } + } + return response; + } + + public async delete(apiUrl: string) { + const response = await this._http!.delete(apiUrl); + if (response instanceof Error) { + throw response; + } + return response; + } +} + +export const apiService = ApiService.getInstance(); diff --git a/x-pack/plugins/synthetics/public/utils/api_service/index.ts b/x-pack/plugins/synthetics/public/utils/api_service/index.ts new file mode 100644 index 0000000000000..566bde2d4b777 --- /dev/null +++ b/x-pack/plugins/synthetics/public/utils/api_service/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 './api_service'; diff --git a/x-pack/plugins/synthetics/public/utils/kibana_service/index.ts b/x-pack/plugins/synthetics/public/utils/kibana_service/index.ts new file mode 100644 index 0000000000000..c623088ee9ba4 --- /dev/null +++ b/x-pack/plugins/synthetics/public/utils/kibana_service/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 './kibana_service'; diff --git a/x-pack/plugins/synthetics/public/utils/kibana_service/kibana_service.ts b/x-pack/plugins/synthetics/public/utils/kibana_service/kibana_service.ts new file mode 100644 index 0000000000000..06fa2b892b4a0 --- /dev/null +++ b/x-pack/plugins/synthetics/public/utils/kibana_service/kibana_service.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Observable } from 'rxjs'; +import type { CoreStart, CoreTheme } from '@kbn/core/public'; +import { apiService } from '../api_service/api_service'; + +class KibanaService { + private static instance: KibanaService; + private _core!: CoreStart; + private _theme!: Observable; + + public get core() { + return this._core; + } + + public set core(coreStart: CoreStart) { + this._core = coreStart; + apiService.http = this._core.http; + } + + public get theme() { + return this._theme; + } + + public set theme(coreTheme: Observable) { + this._theme = coreTheme; + } + + public get toasts() { + return this._core.notifications.toasts; + } + + private constructor() {} + + static getInstance(): KibanaService { + if (!KibanaService.instance) { + KibanaService.instance = new KibanaService(); + } + + return KibanaService.instance; + } +} + +export const kibanaService = KibanaService.getInstance(); diff --git a/x-pack/plugins/synthetics/scripts/e2e.js b/x-pack/plugins/synthetics/scripts/e2e.js index a329a6bf03c4b..cfdd7b09f806e 100644 --- a/x-pack/plugins/synthetics/scripts/e2e.js +++ b/x-pack/plugins/synthetics/scripts/e2e.js @@ -61,7 +61,7 @@ const config = './playwright_run.ts'; function executeRunner() { if (server) { childProcess.execSync( - `node ../../../scripts/${ftrScript} --config ${config} --kibana-install-dir '${kibanaInstallDir}'`, + `node ../../../../scripts/${ftrScript} --config ${config} --kibana-install-dir '${kibanaInstallDir}'`, { cwd: e2eDir, stdio: 'inherit', @@ -69,7 +69,7 @@ function executeRunner() { ); } else if (runner) { childProcess.execSync( - `node ../../../scripts/${ftrScript} --config ${config} --kibana-install-dir '${kibanaInstallDir}' --headless ${headless} --grep '${grep}'`, + `node ../../../../scripts/${ftrScript} --config ${config} --kibana-install-dir '${kibanaInstallDir}' --headless ${headless} --grep '${grep}'`, { cwd: e2eDir, stdio: 'inherit', @@ -77,7 +77,7 @@ function executeRunner() { ); } else { childProcess.execSync( - `node ../../../scripts/${ftrScript} --config ${config} --kibana-install-dir '${kibanaInstallDir}' --grep '${grep}'`, + `node ../../../../scripts/${ftrScript} --config ${config} --kibana-install-dir '${kibanaInstallDir}' --grep '${grep}'`, { cwd: e2eDir, stdio: 'inherit', diff --git a/x-pack/plugins/synthetics/server/kibana.index.ts b/x-pack/plugins/synthetics/server/kibana.index.ts index 2791640115469..14ac9edd5ff2f 100644 --- a/x-pack/plugins/synthetics/server/kibana.index.ts +++ b/x-pack/plugins/synthetics/server/kibana.index.ts @@ -44,7 +44,7 @@ export const initServerWithKibana = ( name: PLUGIN.NAME, order: 1000, category: DEFAULT_APP_CATEGORIES.observability, - app: ['uptime', 'kibana'], + app: ['uptime', 'kibana', 'synthetics'], catalogue: ['uptime'], management: { insightsAndAlerting: ['triggersActions'], @@ -52,7 +52,7 @@ export const initServerWithKibana = ( alerting: UPTIME_RULE_TYPES, privileges: { all: { - app: ['uptime', 'kibana'], + app: ['uptime', 'kibana', 'synthetics'], catalogue: ['uptime'], api: ['uptime-read', 'uptime-write', 'lists-all'], savedObject: { @@ -73,7 +73,7 @@ export const initServerWithKibana = ( ui: ['save', 'configureSettings', 'show', 'alerting:save'], }, read: { - app: ['uptime', 'kibana'], + app: ['uptime', 'kibana', 'synthetics'], catalogue: ['uptime'], api: ['uptime-read', 'lists-read'], savedObject: { diff --git a/x-pack/plugins/synthetics/server/lib/alerts/common.ts b/x-pack/plugins/synthetics/server/lib/alerts/common.ts index 8381adce21d2c..f370b258b482f 100644 --- a/x-pack/plugins/synthetics/server/lib/alerts/common.ts +++ b/x-pack/plugins/synthetics/server/lib/alerts/common.ts @@ -8,6 +8,7 @@ import { isRight } from 'fp-ts/lib/Either'; import Mustache from 'mustache'; import { IBasePath } from '@kbn/core/server'; +import { RuleExecutorServices } from '@kbn/alerting-plugin/server'; import { UptimeCommonState, UptimeCommonStateType } from '../../../common/runtime_types'; export type UpdateUptimeAlertState = ( @@ -59,9 +60,17 @@ export const updateState: UpdateUptimeAlertState = (state, isTriggeredNow) => { }; export const generateAlertMessage = (messageTemplate: string, fields: Record) => { - return Mustache.render(messageTemplate, { state: { ...fields } }); + return Mustache.render(messageTemplate, { context: { ...fields }, state: { ...fields } }); }; export const getViewInAppUrl = (relativeViewInAppUrl: string, basePath: IBasePath) => basePath.publicBaseUrl ? new URL(basePath.prepend(relativeViewInAppUrl), basePath.publicBaseUrl).toString() : relativeViewInAppUrl; + +export const setRecoveredAlertsContext = (alertFactory: RuleExecutorServices['alertFactory']) => { + const { getRecoveredAlerts } = alertFactory.done(); + for (const alert of getRecoveredAlerts()) { + const state = alert.getState(); + alert.setContext(state); + } +}; diff --git a/x-pack/plugins/synthetics/server/lib/alerts/duration_anomaly.test.ts b/x-pack/plugins/synthetics/server/lib/alerts/duration_anomaly.test.ts index eb4509850414b..ad821a509b77b 100644 --- a/x-pack/plugins/synthetics/server/lib/alerts/duration_anomaly.test.ts +++ b/x-pack/plugins/synthetics/server/lib/alerts/duration_anomaly.test.ts @@ -12,7 +12,6 @@ import { import { durationAnomalyAlertFactory } from './duration_anomaly'; import { DURATION_ANOMALY } from '../../../common/constants/alerts'; import { AnomaliesTableRecord, AnomalyRecordDoc } from '@kbn/ml-plugin/common/types/anomalies'; -import { DynamicSettings } from '../../../common/runtime_types'; import { createRuleTypeMocks, bootstrapDependencies } from './test_utils'; import { getSeverityType } from '@kbn/ml-plugin/common/util/anomaly_utils'; import { Ping } from '../../../common/runtime_types/ping'; @@ -33,34 +32,6 @@ interface MockAnomalyResult { const monitorId = 'uptime-monitor'; const mockUrl = 'https://elastic.co'; -/** - * This function aims to provide an easy way to give mock props that will - * reduce boilerplate for tests. - * @param dynamic the expiration and aging thresholds received at alert creation time - * @param params the params received at alert creation time - * @param state the state the alert maintains - */ -const mockOptions = ( - dynamicCertSettings?: { - certExpirationThreshold: DynamicSettings['certExpirationThreshold']; - certAgeThreshold: DynamicSettings['certAgeThreshold']; - }, - state = {}, - params = { - timerange: { from: 'now-15m', to: 'now' }, - monitorId, - severity: 'warning', - } -): any => { - const { services } = createRuleTypeMocks(dynamicCertSettings); - - return { - params, - state, - services, - }; -}; - const mockAnomaliesResult: MockAnomalyResult = { anomalies: [ { @@ -94,6 +65,50 @@ const mockPing: Partial = { }, }; +const mockRecoveredAlerts = mockAnomaliesResult.anomalies.map((result) => ({ + firstCheckedAt: 'date', + firstTriggeredAt: undefined, + lastCheckedAt: 'date', + lastResolvedAt: undefined, + isTriggered: false, + anomalyStartTimestamp: 'date', + currentTriggerStarted: undefined, + expectedResponseTime: `${Math.round(result.typicalSort / 1000)} ms`, + lastTriggeredAt: undefined, + monitor: monitorId, + monitorUrl: mockPing.url?.full, + observerLocation: result.entityValue, + severity: getSeverityType(result.severity), + severityScore: result.severity, + slowestAnomalyResponse: `${Math.round(result.actualSort / 1000)} ms`, + bucketSpan: result.source.bucket_span, +})); + +/** + * This function aims to provide an easy way to give mock props that will + * reduce boilerplate for tests. + * @param dynamic the expiration and aging thresholds received at alert creation time + * @param params the params received at alert creation time + * @param state the state the alert maintains + */ +const mockOptions = ( + state = {}, + params = { + timerange: { from: 'now-15m', to: 'now' }, + monitorId, + severity: 'warning', + } +): any => { + const { services, setContext } = createRuleTypeMocks(mockRecoveredAlerts); + + return { + params, + state, + services, + setContext, + }; +}; + describe('duration anomaly alert', () => { let toISOStringSpy: jest.SpyInstance; const mockDate = 'date'; @@ -206,7 +221,7 @@ Response times as high as ${slowestResponse} ms have been detected from location )} level) response time detected on uptime-monitor with url ${ mockPing.url?.full } at date. Anomaly severity score is ${anomaly.severity}. - Response times as high as ${slowestResponse} ms have been detected from location ${ +Response times as high as ${slowestResponse} ms have been detected from location ${ anomaly.entityValue }. Expected response time is ${typicalResponse} ms.`; @@ -218,7 +233,17 @@ Response times as high as ${slowestResponse} ms have been detected from location Array [ "xpack.uptime.alerts.actionGroups.durationAnomaly", Object { - "${ALERT_REASON_MSG}": "${reasonMessages[0]}", + "anomalyStartTimestamp": "date", + "bucketSpan": 900, + "expectedResponseTime": "10 ms", + "monitor": "uptime-monitor", + "monitorUrl": "https://elastic.co", + "observerLocation": "harrisburg", + "${ALERT_REASON_MSG}": "Abnormal (minor level) response time detected on uptime-monitor with url https://elastic.co at date. Anomaly severity score is 25. + Response times as high as 200 ms have been detected from location harrisburg. Expected response time is 10 ms.", + "severity": "minor", + "severityScore": 25, + "slowestAnomalyResponse": "200 ms", "${VIEW_IN_APP_URL}": "http://localhost:5601/hfe/app/uptime/monitor/eHBhY2sudXB0aW1lLmFsZXJ0cy5hY3Rpb25Hcm91cHMuZHVyYXRpb25Bbm9tYWx5MA==?dateRangeEnd=now&dateRangeStart=2022-03-17T13%3A13%3A33.755Z", }, ] @@ -227,11 +252,52 @@ Response times as high as ${slowestResponse} ms have been detected from location Array [ "xpack.uptime.alerts.actionGroups.durationAnomaly", Object { - "${ALERT_REASON_MSG}": "${reasonMessages[1]}", + "anomalyStartTimestamp": "date", + "bucketSpan": 900, + "expectedResponseTime": "20 ms", + "monitor": "uptime-monitor", + "monitorUrl": "https://elastic.co", + "observerLocation": "fairbanks", + "${ALERT_REASON_MSG}": "Abnormal (warning level) response time detected on uptime-monitor with url https://elastic.co at date. Anomaly severity score is 10. + Response times as high as 300 ms have been detected from location fairbanks. Expected response time is 20 ms.", + "severity": "warning", + "severityScore": 10, + "slowestAnomalyResponse": "300 ms", "${VIEW_IN_APP_URL}": "http://localhost:5601/hfe/app/uptime/monitor/eHBhY2sudXB0aW1lLmFsZXJ0cy5hY3Rpb25Hcm91cHMuZHVyYXRpb25Bbm9tYWx5MQ==?dateRangeEnd=now&dateRangeStart=2022-03-17T13%3A13%3A33.755Z", }, ] `); }); + + it('sets alert recovery context for recovered alerts', async () => { + toISOStringSpy.mockImplementation(() => mockDate); + const mockResultServiceProviderGetter: jest.Mock<{ + getAnomaliesTableData: jest.Mock; + }> = jest.fn(); + const mockGetAnomliesTableDataGetter: jest.Mock = jest.fn(); + const mockGetLatestMonitorGetter: jest.Mock> = jest.fn(); + + mockGetLatestMonitorGetter.mockReturnValue(mockPing); + mockGetAnomliesTableDataGetter.mockReturnValue(mockAnomaliesResult); + mockResultServiceProviderGetter.mockReturnValue({ + getAnomaliesTableData: mockGetAnomliesTableDataGetter, + }); + const { server, libs, plugins } = bootstrapDependencies( + { getLatestMonitor: mockGetLatestMonitorGetter }, + { + ml: { + resultsServiceProvider: mockResultServiceProviderGetter, + }, + } + ); + const alert = durationAnomalyAlertFactory(server, libs, plugins); + const options = mockOptions(); + // @ts-ignore the executor can return `void`, but ours never does + const state: Record = await alert.executor(options); + expect(options.setContext).toHaveBeenCalledTimes(2); + mockRecoveredAlerts.forEach((alertState) => { + expect(options.setContext).toHaveBeenCalledWith(alertState); + }); + }); }); }); diff --git a/x-pack/plugins/synthetics/server/lib/alerts/duration_anomaly.ts b/x-pack/plugins/synthetics/server/lib/alerts/duration_anomaly.ts index f2ec05b11f5ea..a93d44013708b 100644 --- a/x-pack/plugins/synthetics/server/lib/alerts/duration_anomaly.ts +++ b/x-pack/plugins/synthetics/server/lib/alerts/duration_anomaly.ts @@ -15,7 +15,12 @@ import { import { ActionGroupIdsOf } from '@kbn/alerting-plugin/common'; import { AnomaliesTableRecord } from '@kbn/ml-plugin/common/types/anomalies'; import { getSeverityType } from '@kbn/ml-plugin/common/util/anomaly_utils'; -import { updateState, generateAlertMessage, getViewInAppUrl } from './common'; +import { + updateState, + generateAlertMessage, + getViewInAppUrl, + setRecoveredAlertsContext, +} from './common'; import { CLIENT_ALERT_TYPES, DURATION_ANOMALY } from '../../../common/constants/alerts'; import { commonStateTranslations, durationAnomalyTranslations } from './translations'; import { UptimeCorePluginsSetup } from '../adapters/framework'; @@ -94,14 +99,26 @@ export const durationAnomalyAlertFactory: UptimeAlertTypeFactory }, ], actionVariables: { - context: [ACTION_VARIABLES[ALERT_REASON_MSG], ACTION_VARIABLES[VIEW_IN_APP_URL]], + context: [ + ACTION_VARIABLES[ALERT_REASON_MSG], + ACTION_VARIABLES[VIEW_IN_APP_URL], + ...durationAnomalyTranslations.actionVariables, + ...commonStateTranslations, + ], state: [...durationAnomalyTranslations.actionVariables, ...commonStateTranslations], }, isExportable: true, minimumLicenseRequired: 'platinum', + doesSetRecoveryContext: true, async executor({ params, - services: { alertWithLifecycle, scopedClusterClient, savedObjectsClient, getAlertStartedDate }, + services: { + alertWithLifecycle, + scopedClusterClient, + savedObjectsClient, + getAlertStartedDate, + alertFactory, + }, state, startedAt, }) { @@ -160,10 +177,13 @@ export const durationAnomalyAlertFactory: UptimeAlertTypeFactory alertInstance.scheduleActions(DURATION_ANOMALY.id, { [ALERT_REASON_MSG]: alertReasonMessage, [VIEW_IN_APP_URL]: getViewInAppUrl(relativeViewInAppUrl, basePath), + ...summary, }); }); } + setRecoveredAlertsContext(alertFactory); + return updateState(state, foundAnomalies); }, }); diff --git a/x-pack/plugins/synthetics/server/lib/alerts/status_check.test.ts b/x-pack/plugins/synthetics/server/lib/alerts/status_check.test.ts index 84e7c0d68400c..b9a90ee18038a 100644 --- a/x-pack/plugins/synthetics/server/lib/alerts/status_check.test.ts +++ b/x-pack/plugins/synthetics/server/lib/alerts/status_check.test.ts @@ -56,6 +56,53 @@ const mockMonitors = [ }, ]; +const mockRecoveredAlerts = [ + { + currentTriggerStarted: '2022-04-25T14:36:31.511Z', + firstCheckedAt: '2022-04-25T14:10:30.785Z', + firstTriggeredAt: '2022-04-25T14:10:30.785Z', + lastCheckedAt: '2022-04-25T14:36:31.511Z', + lastTriggeredAt: '2022-04-25T14:36:31.511Z', + lastResolvedAt: '2022-04-25T14:23:43.007Z', + isTriggered: true, + monitorUrl: 'https://expired.badssl.com/', + monitorId: 'expired-badssl', + monitorName: 'BadSSL Expired', + monitorType: 'http', + latestErrorMessage: + 'Get "https://expired.badssl.com/": x509: certificate has expired or is not yet valid: current time 2022-04-25T10:36:27-04:00 is after 2015-04-12T23:59:59Z', + observerLocation: 'Unnamed-location', + observerHostname: 'Dominiques-MacBook-Pro-2.local', + reason: + 'BadSSL Expired from Unnamed-location failed 2 times in the last 3 mins. Alert when > 1.', + statusMessage: 'failed 2 times in the last 3 mins. Alert when > 1.', + start: '2022-04-25T14:36:31.621Z', + duration: 315110000000, + }, + { + currentTriggerStarted: '2022-04-25T14:36:31.511Z', + firstCheckedAt: '2022-04-25T14:10:30.785Z', + firstTriggeredAt: '2022-04-25T14:10:30.785Z', + lastCheckedAt: '2022-04-25T14:36:31.511Z', + lastTriggeredAt: '2022-04-25T14:36:31.511Z', + lastResolvedAt: '2022-04-25T14:23:43.007Z', + isTriggered: true, + monitorUrl: 'https://invalid.badssl.com/', + monitorId: 'expired-badssl', + monitorName: 'BadSSL Expired', + monitorType: 'http', + latestErrorMessage: + 'Get "https://invalid.badssl.com/": x509: certificate has expired or is not yet valid: current time 2022-04-25T10:36:27-04:00 is after 2015-04-12T23:59:59Z', + observerLocation: 'Unnamed-location', + observerHostname: 'Dominiques-MacBook-Pro-2.local', + reason: + 'BadSSL Expired from Unnamed-location failed 2 times in the last 3 mins. Alert when > 1.', + statusMessage: 'failed 2 times in the last 3 mins. Alert when > 1.', + start: '2022-04-25T14:36:31.621Z', + duration: 315110000000, + }, +]; + const mockCommonAlertDocumentFields = (monitorInfo: GetMonitorStatusResult['monitorInfo']) => ({ 'agent.name': monitorInfo.agent?.name, 'error.message': monitorInfo.error?.message, @@ -121,13 +168,14 @@ const mockOptions = ( }, } ): any => { - const { services } = createRuleTypeMocks(); + const { services, setContext } = createRuleTypeMocks(mockRecoveredAlerts); return { params, state, services, rule, + setContext, }; }; @@ -142,6 +190,7 @@ describe('status check alert', () => { afterEach(() => { jest.clearAllMocks(); }); + describe('executor', () => { it('does not trigger when there are no monitors down', async () => { expect.assertions(5); @@ -242,7 +291,15 @@ describe('status check alert', () => { Array [ "xpack.uptime.alerts.actionGroups.monitorStatus", Object { + "latestErrorMessage": "error message 1", + "monitorId": "first", + "monitorName": "First", + "monitorType": "myType", + "monitorUrl": "localhost:8080", + "observerHostname": undefined, + "observerLocation": "harrisburg", "reason": "First from harrisburg failed 234 times in the last 15 mins. Alert when > 5.", + "statusMessage": "failed 234 times in the last 15 mins. Alert when > 5.", "viewInAppUrl": "http://localhost:5601/hfe/app/uptime/monitor/Zmlyc3Q=?dateRangeEnd=now&dateRangeStart=2022-03-17T13%3A13%3A33.755Z&filters=%5B%5B%22observer.geo.name%22%2C%5B%22harrisburg%22%5D%5D%5D", }, ] @@ -313,7 +370,15 @@ describe('status check alert', () => { Array [ "xpack.uptime.alerts.actionGroups.monitorStatus", Object { + "latestErrorMessage": "error message 1", + "monitorId": "first", + "monitorName": "First", + "monitorType": "myType", + "monitorUrl": "localhost:8080", + "observerHostname": undefined, + "observerLocation": "harrisburg", "reason": "First from harrisburg failed 234 times in the last 15m. Alert when > 5.", + "statusMessage": "failed 234 times in the last 15m. Alert when > 5.", "viewInAppUrl": "http://localhost:5601/hfe/app/uptime/monitor/Zmlyc3Q=?dateRangeEnd=now&dateRangeStart=2022-03-17T13%3A13%3A33.755Z&filters=%5B%5B%22observer.geo.name%22%2C%5B%22harrisburg%22%5D%5D%5D", }, ] @@ -785,28 +850,60 @@ describe('status check alert', () => { Array [ "xpack.uptime.alerts.actionGroups.monitorStatus", Object { + "latestErrorMessage": undefined, + "monitorId": "foo", + "monitorName": "Foo", + "monitorType": "myType", + "monitorUrl": "https://foo.com", + "observerHostname": undefined, + "observerLocation": "harrisburg", "reason": "Foo from harrisburg 35 days availability is 99.28%. Alert when < 99.34%.", + "statusMessage": "35 days availability is 99.28%. Alert when < 99.34%.", "viewInAppUrl": "http://localhost:5601/hfe/app/uptime/monitor/Zm9v?dateRangeEnd=now&dateRangeStart=2022-03-17T13%3A13%3A33.755Z&filters=%5B%5B%22observer.geo.name%22%2C%5B%22harrisburg%22%5D%5D%5D", }, ], Array [ "xpack.uptime.alerts.actionGroups.monitorStatus", Object { + "latestErrorMessage": undefined, + "monitorId": "foo", + "monitorName": "Foo", + "monitorType": "myType", + "monitorUrl": "https://foo.com", + "observerHostname": undefined, + "observerLocation": "fairbanks", "reason": "Foo from fairbanks 35 days availability is 98.03%. Alert when < 99.34%.", + "statusMessage": "35 days availability is 98.03%. Alert when < 99.34%.", "viewInAppUrl": "http://localhost:5601/hfe/app/uptime/monitor/Zm9v?dateRangeEnd=now&dateRangeStart=2022-03-17T13%3A13%3A33.755Z&filters=%5B%5B%22observer.geo.name%22%2C%5B%22fairbanks%22%5D%5D%5D", }, ], Array [ "xpack.uptime.alerts.actionGroups.monitorStatus", Object { + "latestErrorMessage": undefined, + "monitorId": "unreliable", + "monitorName": "Unreliable", + "monitorType": "myType", + "monitorUrl": "https://unreliable.co", + "observerHostname": undefined, + "observerLocation": "fairbanks", "reason": "Unreliable from fairbanks 35 days availability is 90.92%. Alert when < 99.34%.", + "statusMessage": "35 days availability is 90.92%. Alert when < 99.34%.", "viewInAppUrl": "http://localhost:5601/hfe/app/uptime/monitor/dW5yZWxpYWJsZQ==?dateRangeEnd=now&dateRangeStart=2022-03-17T13%3A13%3A33.755Z&filters=%5B%5B%22observer.geo.name%22%2C%5B%22fairbanks%22%5D%5D%5D", }, ], Array [ "xpack.uptime.alerts.actionGroups.monitorStatus", Object { + "latestErrorMessage": undefined, + "monitorId": "no-name", + "monitorName": "no-name", + "monitorType": "myType", + "monitorUrl": "https://no-name.co", + "observerHostname": undefined, + "observerLocation": "fairbanks", "reason": "no-name from fairbanks 35 days availability is 90.92%. Alert when < 99.34%.", + "statusMessage": "35 days availability is 90.92%. Alert when < 99.34%.", "viewInAppUrl": "http://localhost:5601/hfe/app/uptime/monitor/bm8tbmFtZQ==?dateRangeEnd=now&dateRangeStart=2022-03-17T13%3A13%3A33.755Z&filters=%5B%5B%22observer.geo.name%22%2C%5B%22fairbanks%22%5D%5D%5D", }, ], @@ -909,6 +1006,26 @@ describe('status check alert', () => { }) ); }); + + it('sets alert recovery context for recovered alerts', async () => { + toISOStringSpy.mockImplementation(() => 'foo date string'); + const mockGetter: jest.Mock = jest.fn(); + + mockGetter.mockReturnValue(mockMonitors); + const { server, libs, plugins } = bootstrapDependencies({ getMonitorStatus: mockGetter }); + const alert = statusCheckAlertFactory(server, libs, plugins); + const options = mockOptions(); + // @ts-ignore the executor can return `void`, but ours never does + const state: Record = await alert.executor(options); + expect(options.setContext).toHaveBeenCalledTimes(2); + mockRecoveredAlerts.forEach((alertState) => { + expect(options.setContext).toHaveBeenCalledWith(alertState); + }); + }); + }); + + describe('alert recovery', () => { + it('sets context for alert recovery', () => {}); }); describe('alert factory', () => { diff --git a/x-pack/plugins/synthetics/server/lib/alerts/status_check.ts b/x-pack/plugins/synthetics/server/lib/alerts/status_check.ts index d305dedea3e10..243749f686106 100644 --- a/x-pack/plugins/synthetics/server/lib/alerts/status_check.ts +++ b/x-pack/plugins/synthetics/server/lib/alerts/status_check.ts @@ -21,7 +21,7 @@ import { GetMonitorAvailabilityParams, } from '../../../common/runtime_types'; import { CLIENT_ALERT_TYPES, MONITOR_STATUS } from '../../../common/constants/alerts'; -import { updateState, getViewInAppUrl } from './common'; +import { updateState, getViewInAppUrl, setRecoveredAlertsContext } from './common'; import { commonMonitorStateI18, commonStateTranslations, @@ -47,6 +47,7 @@ import { import { getMonitorRouteFromMonitorId } from '../../../common/utils/get_monitor_url'; export type ActionGroupIds = ActionGroupIdsOf; + /** * Returns the appropriate range for filtering the documents by `@timestamp`. * @@ -75,22 +76,6 @@ export function getTimestampRange({ }; } -const getMonIdByLoc = (monitorId: string, location: string) => { - return monitorId + '-' + location; -}; - -const uniqueDownMonitorIds = (items: GetMonitorStatusResult[]): Set => - items.reduce( - (acc, { monitorId, location }) => acc.add(getMonIdByLoc(monitorId, location)), - new Set() - ); - -const uniqueAvailMonitorIds = (items: GetMonitorAvailabilityResult[]): Set => - items.reduce( - (acc, { monitorId, location }) => acc.add(getMonIdByLoc(monitorId, location)), - new Set() - ); - export const getUniqueIdsByLoc = ( downMonitorsByLocation: GetMonitorStatusResult[], availabilityResults: GetMonitorAvailabilityResult[] @@ -161,7 +146,7 @@ export const getMonitorSummary = (monitorInfo: Ping, statusMessage: string) => { return { ...summary, - reason: `${monitorName} from ${observerLocation} ${statusMessage}`, + [ALERT_REASON_MSG]: `${monitorName} from ${observerLocation} ${statusMessage}`, }; }; @@ -222,6 +207,22 @@ export const getInstanceId = (monitorInfo: Ping, monIdByLoc: string) => { return `${urlText}_${monIdByLoc}`; }; +const getMonIdByLoc = (monitorId: string, location: string) => { + return monitorId + '-' + location; +}; + +const uniqueDownMonitorIds = (items: GetMonitorStatusResult[]): Set => + items.reduce( + (acc, { monitorId, location }) => acc.add(getMonIdByLoc(monitorId, location)), + new Set() + ); + +const uniqueAvailMonitorIds = (items: GetMonitorAvailabilityResult[]): Set => + items.reduce( + (acc, { monitorId, location }) => acc.add(getMonIdByLoc(monitorId, location)), + new Set() + ); + export const statusCheckAlertFactory: UptimeAlertTypeFactory = (server, libs) => ({ id: CLIENT_ALERT_TYPES.MONITOR_STATUS, producer: 'uptime', @@ -281,15 +282,23 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = ( ACTION_VARIABLES[MONITOR_WITH_GEO], ACTION_VARIABLES[ALERT_REASON_MSG], ACTION_VARIABLES[VIEW_IN_APP_URL], + ...commonMonitorStateI18, ], state: [...commonMonitorStateI18, ...commonStateTranslations], }, isExportable: true, minimumLicenseRequired: 'basic', + doesSetRecoveryContext: true, async executor({ params: rawParams, state, - services: { savedObjectsClient, scopedClusterClient, alertWithLifecycle, getAlertStartedDate }, + services: { + savedObjectsClient, + scopedClusterClient, + alertWithLifecycle, + getAlertStartedDate, + alertFactory, + }, rule: { schedule: { interval }, }, @@ -314,14 +323,12 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = ( }); const filterString = await formatFilterString(uptimeEsClient, filters, search, libs); - const timespanInterval = `${String(timerangeCount)}${timerangeUnit}`; // Range filter for `monitor.timespan`, the range of time the ping is valid const timespanRange = oldVersionTimeRange || { from: `now-${timespanInterval}`, to: 'now', }; - // Range filter for `@timestamp`, the time the document was indexed const timestampRange = getTimestampRange({ ruleScheduleLookback: `now-${interval}`, @@ -364,10 +371,14 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = ( fields: getMonitorAlertDocument(monitorSummary), }); - alert.replaceState({ - ...state, + const context = { ...monitorSummary, statusMessage, + }; + + alert.replaceState({ + ...state, + ...context, ...updateState(state, true), }); @@ -381,10 +392,11 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = ( }); alert.scheduleActions(MONITOR_STATUS.id, { - [ALERT_REASON_MSG]: monitorSummary.reason, [VIEW_IN_APP_URL]: getViewInAppUrl(relativeViewInAppUrl, basePath), + ...context, }); } + setRecoveredAlertsContext(alertFactory); return updateState(state, downMonitorsByLocation.length > 0); } @@ -436,11 +448,16 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = ( fields: getMonitorAlertDocument(monitorSummary), }); - alert.replaceState({ - ...updateState(state, true), + const context = { ...monitorSummary, statusMessage, + }; + + alert.replaceState({ + ...updateState(state, true), + ...context, }); + const relativeViewInAppUrl = getMonitorRouteFromMonitorId({ monitorId: monitorSummary.monitorId, dateRangeEnd: 'now', @@ -451,10 +468,11 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = ( }); alert.scheduleActions(MONITOR_STATUS.id, { - [ALERT_REASON_MSG]: monitorSummary.reason, [VIEW_IN_APP_URL]: getViewInAppUrl(relativeViewInAppUrl, basePath), + ...context, }); }); + setRecoveredAlertsContext(alertFactory); return updateState(state, downMonitorsByLocation.length > 0); }, }); diff --git a/x-pack/plugins/synthetics/server/lib/alerts/test_utils/index.ts b/x-pack/plugins/synthetics/server/lib/alerts/test_utils/index.ts index af248af730eee..456b0675eee87 100644 --- a/x-pack/plugins/synthetics/server/lib/alerts/test_utils/index.ts +++ b/x-pack/plugins/synthetics/server/lib/alerts/test_utils/index.ts @@ -13,8 +13,6 @@ import { UMServerLibs } from '../../lib'; import { UptimeCorePluginsSetup, UptimeServerSetup } from '../../adapters'; import type { UptimeRouter } from '../../../types'; import { getUptimeESMockClient } from '../../requests/helper'; -import { DynamicSettings } from '../../../../common/runtime_types'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; /** * The alert takes some dependencies as parameters; these are things like @@ -41,15 +39,7 @@ export const bootstrapDependencies = (customRequests?: any, customPlugins: any = return { server, libs, plugins }; }; -export const createRuleTypeMocks = ( - dynamicCertSettings: { - certAgeThreshold: DynamicSettings['certAgeThreshold']; - certExpirationThreshold: DynamicSettings['certExpirationThreshold']; - } = { - certAgeThreshold: DYNAMIC_SETTINGS_DEFAULTS.certAgeThreshold, - certExpirationThreshold: DYNAMIC_SETTINGS_DEFAULTS.certExpirationThreshold, - } -) => { +export const createRuleTypeMocks = (recoveredAlerts: Array> = []) => { const loggerMock = { debug: jest.fn(), warn: jest.fn(), @@ -58,10 +48,17 @@ export const createRuleTypeMocks = ( const scheduleActions = jest.fn(); const replaceState = jest.fn(); + const setContext = jest.fn(); const services = { ...getUptimeESMockClient(), ...alertsMock.createRuleExecutorServices(), + alertFactory: { + ...alertsMock.createRuleExecutorServices().alertFactory, + done: () => ({ + getRecoveredAlerts: () => createRecoveredAlerts(recoveredAlerts, setContext), + }), + }, alertWithLifecycle: jest.fn().mockReturnValue({ scheduleActions, replaceState }), getAlertStartedDate: jest.fn().mockReturnValue('2022-03-17T13:13:33.755Z'), logger: loggerMock, @@ -77,5 +74,14 @@ export const createRuleTypeMocks = ( services, scheduleActions, replaceState, + setContext, }; }; + +const createRecoveredAlerts = (alerts: Array>, setContext: jest.Mock) => { + return alerts.map((alert) => ({ + getState: () => alert, + setContext, + context: {}, + })); +}; diff --git a/x-pack/plugins/synthetics/server/lib/alerts/tls.test.ts b/x-pack/plugins/synthetics/server/lib/alerts/tls.test.ts index 31a5e98bf9f02..88f8b964eb590 100644 --- a/x-pack/plugins/synthetics/server/lib/alerts/tls.test.ts +++ b/x-pack/plugins/synthetics/server/lib/alerts/tls.test.ts @@ -7,7 +7,7 @@ import moment from 'moment'; import { tlsAlertFactory, getCertSummary } from './tls'; import { TLS } from '../../../common/constants/alerts'; -import { CertResult, DynamicSettings } from '../../../common/runtime_types'; +import { CertResult } from '../../../common/runtime_types'; import { createRuleTypeMocks, bootstrapDependencies } from './test_utils'; import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../common/constants'; @@ -19,24 +19,6 @@ import { savedObjectsAdapter, UMSavedObjectsAdapter } from '../saved_objects/sav * @param params the params received at alert creation time * @param state the state the alert maintains */ -const mockOptions = ( - dynamicCertSettings?: { - certExpirationThreshold: DynamicSettings['certExpirationThreshold']; - certAgeThreshold: DynamicSettings['certAgeThreshold']; - }, - state = {} -): any => { - const { services } = createRuleTypeMocks(dynamicCertSettings); - const params = { - timerange: { from: 'now-15m', to: 'now' }, - }; - - return { - params, - state, - services, - }; -}; const mockCertResult: CertResult = { certs: [ @@ -76,6 +58,35 @@ const mockCertResult: CertResult = { total: 4, }; +const mockRecoveredAlerts = [ + { + commonName: mockCertResult.certs[0].common_name ?? '', + issuer: mockCertResult.certs[0].issuer ?? '', + summary: 'sample summary', + status: 'expired', + }, + { + commonName: mockCertResult.certs[1].common_name ?? '', + issuer: mockCertResult.certs[1].issuer ?? '', + summary: 'sample summary 2', + status: 'aging', + }, +]; + +const mockOptions = (state = {}): any => { + const { services, setContext } = createRuleTypeMocks(mockRecoveredAlerts); + const params = { + timerange: { from: 'now-15m', to: 'now' }, + }; + + return { + params, + state, + services, + setContext, + }; +}; + describe('tls alert', () => { let toISOStringSpy: jest.SpyInstance; let savedObjectsAdapterSpy: jest.SpyInstance< @@ -131,16 +142,18 @@ describe('tls alert', () => { const [{ value: alertInstanceMock }] = alertWithLifecycle.mock.results; expect(alertInstanceMock.replaceState).toHaveBeenCalledTimes(4); mockCertResult.certs.forEach((cert) => { - expect(alertInstanceMock.replaceState).toBeCalledWith( - expect.objectContaining({ - commonName: cert.common_name, - issuer: cert.issuer, - status: 'expired', - }) + const context = { + commonName: cert.common_name, + issuer: cert.issuer, + status: 'expired', + }; + expect(alertInstanceMock.replaceState).toBeCalledWith(expect.objectContaining(context)); + expect(alertInstanceMock.scheduleActions).toBeCalledWith( + TLS.id, + expect.objectContaining(context) ); }); expect(alertInstanceMock.scheduleActions).toHaveBeenCalledTimes(4); - expect(alertInstanceMock.scheduleActions).toBeCalledWith(TLS.id); }); it('handles dynamic settings for aging or expiration threshold', async () => { @@ -167,6 +180,22 @@ describe('tls alert', () => { }) ); }); + + it('sets alert recovery context for recovered alerts', async () => { + toISOStringSpy.mockImplementation(() => 'foo date string'); + const mockGetter: jest.Mock = jest.fn(); + + mockGetter.mockReturnValue(mockCertResult); + const { server, libs, plugins } = bootstrapDependencies({ getCerts: mockGetter }); + const alert = tlsAlertFactory(server, libs, plugins); + const options = mockOptions(); + // @ts-ignore the executor can return `void`, but ours never does + const state: Record = await alert.executor(options); + expect(options.setContext).toHaveBeenCalledTimes(2); + mockRecoveredAlerts.forEach((alertState) => { + expect(options.setContext).toHaveBeenCalledWith(alertState); + }); + }); }); describe('getCertSummary', () => { diff --git a/x-pack/plugins/synthetics/server/lib/alerts/tls.ts b/x-pack/plugins/synthetics/server/lib/alerts/tls.ts index 0a6fb24c88156..127171eab0f4d 100644 --- a/x-pack/plugins/synthetics/server/lib/alerts/tls.ts +++ b/x-pack/plugins/synthetics/server/lib/alerts/tls.ts @@ -9,7 +9,7 @@ import { schema } from '@kbn/config-schema'; import { ALERT_REASON } from '@kbn/rule-data-utils'; import { ActionGroupIdsOf } from '@kbn/alerting-plugin/common'; import { UptimeAlertTypeFactory } from './types'; -import { updateState, generateAlertMessage } from './common'; +import { updateState, generateAlertMessage, setRecoveredAlertsContext } from './common'; import { CLIENT_ALERT_TYPES, TLS } from '../../../common/constants/alerts'; import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../common/constants'; import { Cert, CertResult } from '../../../common/runtime_types'; @@ -108,13 +108,14 @@ export const tlsAlertFactory: UptimeAlertTypeFactory = (_server, }, ], actionVariables: { - context: [], + context: [...tlsTranslations.actionVariables, ...commonStateTranslations], state: [...tlsTranslations.actionVariables, ...commonStateTranslations], }, isExportable: true, minimumLicenseRequired: 'basic', + doesSetRecoveryContext: true, async executor({ - services: { alertWithLifecycle, savedObjectsClient, scopedClusterClient }, + services: { alertWithLifecycle, savedObjectsClient, scopedClusterClient, alertFactory }, state, }) { const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient); @@ -173,10 +174,12 @@ export const tlsAlertFactory: UptimeAlertTypeFactory = (_server, ...updateState(state, foundCerts), ...summary, }); - alertInstance.scheduleActions(TLS.id); + alertInstance.scheduleActions(TLS.id, { ...summary }); }); } + setRecoveredAlertsContext(alertFactory); + return updateState(state, foundCerts); }, }); diff --git a/x-pack/plugins/synthetics/server/lib/synthetics_service/formatters/browser.ts b/x-pack/plugins/synthetics/server/lib/synthetics_service/formatters/browser.ts index 616d764c50abb..876827e453adf 100644 --- a/x-pack/plugins/synthetics/server/lib/synthetics_service/formatters/browser.ts +++ b/x-pack/plugins/synthetics/server/lib/synthetics_service/formatters/browser.ts @@ -10,6 +10,21 @@ import { BrowserFields, ConfigKey } from '../../../../common/runtime_types/monit export type BrowserFormatMap = Record; +const throttlingFormatter: Formatter = (fields) => { + if (!fields[ConfigKey.IS_THROTTLING_ENABLED]) return false; + + const getThrottlingValue = (v: string | undefined, suffix: 'd' | 'u' | 'l') => + v !== '' && v !== undefined ? `${v}${suffix}` : null; + + return [ + getThrottlingValue(fields[ConfigKey.DOWNLOAD_SPEED], 'd'), + getThrottlingValue(fields[ConfigKey.UPLOAD_SPEED], 'u'), + getThrottlingValue(fields[ConfigKey.LATENCY], 'l'), + ] + .filter((v) => v !== null) + .join('/'); +}; + export const browserFormatters: BrowserFormatMap = { [ConfigKey.METADATA]: (fields) => objectFormatter(fields[ConfigKey.METADATA]), [ConfigKey.URLS]: null, @@ -31,12 +46,7 @@ export const browserFormatters: BrowserFormatMap = { [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: null, [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: null, [ConfigKey.IS_THROTTLING_ENABLED]: null, - [ConfigKey.THROTTLING_CONFIG]: (fields) => { - if (fields[ConfigKey.IS_THROTTLING_ENABLED] === false) { - return false; - } - return fields[ConfigKey.THROTTLING_CONFIG] ?? false; - }, + [ConfigKey.THROTTLING_CONFIG]: (fields) => throttlingFormatter(fields), [ConfigKey.DOWNLOAD_SPEED]: null, [ConfigKey.UPLOAD_SPEED]: null, [ConfigKey.LATENCY]: null, diff --git a/x-pack/plugins/synthetics/server/lib/synthetics_service/formatters/convert_to_data_stream.ts b/x-pack/plugins/synthetics/server/lib/synthetics_service/formatters/convert_to_data_stream.ts index 7d518b189afef..52ae921c78643 100644 --- a/x-pack/plugins/synthetics/server/lib/synthetics_service/formatters/convert_to_data_stream.ts +++ b/x-pack/plugins/synthetics/server/lib/synthetics_service/formatters/convert_to_data_stream.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { DEFAULT_NAMESPACE_STRING } from '../../../../common/constants'; +import { DEFAULT_NAMESPACE_STRING } from '../../../../common/constants/monitor_defaults'; import { DataStream, MonitorFields } from '../../../../common/runtime_types'; interface DataStreamConfig { diff --git a/x-pack/plugins/synthetics/tsconfig.json b/x-pack/plugins/synthetics/tsconfig.json index 17088fe6f1fd9..023c98c66a4a5 100644 --- a/x-pack/plugins/synthetics/tsconfig.json +++ b/x-pack/plugins/synthetics/tsconfig.json @@ -10,7 +10,7 @@ "common/**/*", "scripts/**/*", "public/**/*", - "public/components/monitor/status_details/location_map/embeddables/low_poly_layer.json", + "public/legacy_uptime/components/monitor/status_details/location_map/embeddables/low_poly_layer.json", "server/**/*", "server/lib/requests/__fixtures__/monitor_charts_mock.json", "../../../typings/**/*" diff --git a/x-pack/plugins/task_manager/server/task_events.test.ts b/x-pack/plugins/task_manager/server/task_events.test.ts index 607453b7ea92f..9c6b9f9cdede8 100644 --- a/x-pack/plugins/task_manager/server/task_events.test.ts +++ b/x-pack/plugins/task_manager/server/task_events.test.ts @@ -11,17 +11,28 @@ const DelayIterations = 4; const DelayMillis = 250; const DelayTotal = DelayIterations * DelayMillis; -async function nonBlockingDelay(millis: number) { +async function basicNonBlockingDelay(millis: number) { await new Promise((resolve) => setTimeout(resolve, millis)); } +async function nonBlockingDelay(millis: number) { + // can't just use basicNonBlockingDelay because: + // https://github.com/nodejs/node/issues/26578 + const end = Date.now() + millis; + + while (Date.now() <= end) { + await basicNonBlockingDelay(millis); + } +} + async function blockingDelay(millis: number) { // get task in async queue await nonBlockingDelay(0); const end = Date.now() + millis; + // eslint-disable-next-line no-empty - while (Date.now() < end) {} + while (Date.now() <= end) {} } async function nonBlockingTask() { @@ -45,8 +56,7 @@ describe('task_events', () => { expect(result.eventLoopBlockMs).toBe(undefined); }); - // FLAKY: https://github.com/elastic/kibana/issues/128441 - describe.skip('startTaskTimerWithEventLoopMonitoring', () => { + describe('startTaskTimerWithEventLoopMonitoring', () => { test('non-blocking', async () => { const stopTaskTimer = startTaskTimerWithEventLoopMonitoring({ monitor: true, diff --git a/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts b/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts index 73d30b022c626..5cce445e7bb4c 100644 --- a/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts +++ b/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts @@ -1517,6 +1517,54 @@ describe('TaskManagerRunner', () => { `Skipping reschedule for task bar \"${id}\" due to the task expiring` ); }); + + test('Prints debug logs on task start/end', async () => { + const { runner, logger } = await readyToRunStageSetup({ + definitions: { + bar: { + title: 'Bar!', + createTaskRunner: () => ({ + async run() { + return { state: {} }; + }, + }), + }, + }, + }); + await runner.run(); + + expect(logger.debug).toHaveBeenCalledTimes(2); + expect(logger.debug).toHaveBeenNthCalledWith(1, 'Running task bar "foo"', { + tags: ['task:start', 'foo', 'bar'], + }); + expect(logger.debug).toHaveBeenNthCalledWith(2, 'Task bar "foo" ended', { + tags: ['task:end', 'foo', 'bar'], + }); + }); + + test('Prints debug logs on task start/end even if it throws error', async () => { + const { runner, logger } = await readyToRunStageSetup({ + definitions: { + bar: { + title: 'Bar!', + createTaskRunner: () => ({ + async run() { + throw new Error(); + }, + }), + }, + }, + }); + await runner.run(); + + expect(logger.debug).toHaveBeenCalledTimes(2); + expect(logger.debug).toHaveBeenNthCalledWith(1, 'Running task bar "foo"', { + tags: ['task:start', 'foo', 'bar'], + }); + expect(logger.debug).toHaveBeenNthCalledWith(2, 'Task bar "foo" ended', { + tags: ['task:end', 'foo', 'bar'], + }); + }); }); interface TestOpts { diff --git a/x-pack/plugins/task_manager/server/task_running/task_runner.ts b/x-pack/plugins/task_manager/server/task_running/task_runner.ts index b6199f06300f1..d305a49bef55e 100644 --- a/x-pack/plugins/task_manager/server/task_running/task_runner.ts +++ b/x-pack/plugins/task_manager/server/task_running/task_runner.ts @@ -283,7 +283,7 @@ export class TaskManagerRunner implements TaskRunner { }` ); } - this.logger.debug(`Running task ${this}`); + this.logger.debug(`Running task ${this}`, { tags: ['task:start', this.id, this.taskType] }); const apmTrans = apm.startTransaction(this.taskType, TASK_MANAGER_RUN_TRANSACTION_TYPE, { childOf: this.instance.task.traceparent, @@ -324,6 +324,8 @@ export class TaskManagerRunner implements TaskRunner { ); if (apmTrans) apmTrans.end('failure'); return processedResult; + } finally { + this.logger.debug(`Task ${this} ended`, { tags: ['task:end', this.id, this.taskType] }); } } 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 9eba9b7fde512..aac8d2e40f650 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -4557,19 +4557,6 @@ } } }, - "search-session": { - "properties": { - "transientCount": { - "type": "long" - }, - "persistedCount": { - "type": "long" - }, - "totalCount": { - "type": "long" - } - } - }, "discoverEnhanced": { "properties": { "exploreDataInChartActionEnabled": { @@ -6989,6 +6976,9 @@ }, "browser_screenshot_error": { "type": "long" + }, + "visual_reporting_soft_disabled_error": { + "type": "long" } } } @@ -7104,6 +7094,9 @@ }, "browser_screenshot_error": { "type": "long" + }, + "visual_reporting_soft_disabled_error": { + "type": "long" } } } @@ -7251,6 +7244,9 @@ }, "browser_screenshot_error": { "type": "long" + }, + "visual_reporting_soft_disabled_error": { + "type": "long" } } } @@ -7398,6 +7394,9 @@ }, "browser_screenshot_error": { "type": "long" + }, + "visual_reporting_soft_disabled_error": { + "type": "long" } } } @@ -8252,6 +8251,9 @@ }, "browser_screenshot_error": { "type": "long" + }, + "visual_reporting_soft_disabled_error": { + "type": "long" } } } @@ -8367,6 +8369,9 @@ }, "browser_screenshot_error": { "type": "long" + }, + "visual_reporting_soft_disabled_error": { + "type": "long" } } } @@ -8514,6 +8519,9 @@ }, "browser_screenshot_error": { "type": "long" + }, + "visual_reporting_soft_disabled_error": { + "type": "long" } } } @@ -8661,6 +8669,9 @@ }, "browser_screenshot_error": { "type": "long" + }, + "visual_reporting_soft_disabled_error": { + "type": "long" } } } @@ -12801,98 +12812,6 @@ } } }, - "task_manager": { - "properties": { - "ephemeral_tasks_enabled": { - "type": "boolean" - }, - "ephemeral_request_capacity": { - "type": "short" - }, - "ephemeral_stats": { - "properties": { - "status": { - "type": "keyword" - }, - "queued_tasks": { - "properties": { - "p50": { - "type": "long" - }, - "p90": { - "type": "long" - }, - "p95": { - "type": "long" - }, - "p99": { - "type": "long" - } - } - }, - "load": { - "properties": { - "p50": { - "type": "long" - }, - "p90": { - "type": "long" - }, - "p95": { - "type": "long" - }, - "p99": { - "type": "long" - } - } - }, - "executions_per_cycle": { - "properties": { - "p50": { - "type": "long" - }, - "p90": { - "type": "long" - }, - "p95": { - "type": "long" - }, - "p99": { - "type": "long" - } - } - } - } - }, - "task_type_exclusion": { - "type": "array", - "items": { - "type": "keyword" - } - }, - "failed_tasks": { - "type": "long" - } - } - }, - "upgrade-assistant-telemetry": { - "properties": { - "features": { - "properties": { - "deprecation_logging": { - "properties": { - "enabled": { - "type": "boolean", - "_meta": { - "description": "Whether user has enabled Elasticsearch deprecation logging" - } - } - } - } - } - } - } - }, "uptime": { "properties": { "last_24_hours": { @@ -13033,6 +12952,98 @@ } } } + }, + "task_manager": { + "properties": { + "ephemeral_tasks_enabled": { + "type": "boolean" + }, + "ephemeral_request_capacity": { + "type": "short" + }, + "ephemeral_stats": { + "properties": { + "status": { + "type": "keyword" + }, + "queued_tasks": { + "properties": { + "p50": { + "type": "long" + }, + "p90": { + "type": "long" + }, + "p95": { + "type": "long" + }, + "p99": { + "type": "long" + } + } + }, + "load": { + "properties": { + "p50": { + "type": "long" + }, + "p90": { + "type": "long" + }, + "p95": { + "type": "long" + }, + "p99": { + "type": "long" + } + } + }, + "executions_per_cycle": { + "properties": { + "p50": { + "type": "long" + }, + "p90": { + "type": "long" + }, + "p95": { + "type": "long" + }, + "p99": { + "type": "long" + } + } + } + } + }, + "task_type_exclusion": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "failed_tasks": { + "type": "long" + } + } + }, + "upgrade-assistant-telemetry": { + "properties": { + "features": { + "properties": { + "deprecation_logging": { + "properties": { + "enabled": { + "type": "boolean", + "_meta": { + "description": "Whether user has enabled Elasticsearch deprecation logging" + } + } + } + } + } + } + } } } } diff --git a/x-pack/plugins/timelines/common/types/timeline/index.ts b/x-pack/plugins/timelines/common/types/timeline/index.ts index 867264fa81546..528c6e4293cf4 100644 --- a/x-pack/plugins/timelines/common/types/timeline/index.ts +++ b/x-pack/plugins/timelines/common/types/timeline/index.ts @@ -314,7 +314,7 @@ export enum TimelineId { usersPageExternalAlerts = 'users-page-external-alerts', hostsPageEvents = 'hosts-page-events', hostsPageExternalAlerts = 'hosts-page-external-alerts', - hostsPageSessions = 'hosts-page-sessions', + hostsPageSessions = 'hosts-page-sessions-v2', detectionsRulesDetailsPage = 'detections-rules-details-page', detectionsPage = 'detections-page', networkPageExternalAlerts = 'network-page-external-alerts', diff --git a/x-pack/plugins/timelines/kibana.json b/x-pack/plugins/timelines/kibana.json index 11adf42b3a6b4..77a5dcc699bdc 100644 --- a/x-pack/plugins/timelines/kibana.json +++ b/x-pack/plugins/timelines/kibana.json @@ -10,6 +10,6 @@ "extraPublicDirs": ["common"], "server": true, "ui": true, - "requiredPlugins": ["alerting", "cases", "data", "dataEnhanced", "kibanaReact", "kibanaUtils"], + "requiredPlugins": ["alerting", "cases", "data", "kibanaReact", "kibanaUtils"], "optionalPlugins": ["security"] } diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_table.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_table.test.tsx index 151ed99c3621c..77d5a754d6a97 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_table.test.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_table.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { render } from '@testing-library/react'; +import { render, RenderResult } from '@testing-library/react'; import { mockBrowserFields, TestProviders } from '../../../../mock'; import { tGridActions } from '../../../../store/t_grid'; import { defaultColumnHeaderType } from '../../body/column_headers/default_headers'; @@ -155,50 +155,117 @@ describe('FieldTable', () => { expect(checkbox).toHaveAttribute('checked'); }); - it('should dispatch remove column action on field unchecked', () => { - const result = render( - - - - ); + describe('selection', () => { + it('should dispatch remove column action on field unchecked', () => { + const result = render( + + + + ); - result.getByTestId(`field-${timestampFieldId}-checkbox`).click(); + result.getByTestId(`field-${timestampFieldId}-checkbox`).click(); - expect(mockDispatch).toHaveBeenCalledTimes(1); - expect(mockDispatch).toHaveBeenCalledWith( - tGridActions.removeColumn({ id: timelineId, columnId: timestampFieldId }) - ); + expect(mockDispatch).toHaveBeenCalledTimes(1); + expect(mockDispatch).toHaveBeenCalledWith( + tGridActions.removeColumn({ id: timelineId, columnId: timestampFieldId }) + ); + }); + + it('should dispatch upsert column action on field checked', () => { + const result = render( + + + + ); + + result.getByTestId(`field-${timestampFieldId}-checkbox`).click(); + + expect(mockDispatch).toHaveBeenCalledTimes(1); + expect(mockDispatch).toHaveBeenCalledWith( + tGridActions.upsertColumn({ + id: timelineId, + column: { + columnHeaderType: defaultColumnHeaderType, + id: timestampFieldId, + initialWidth: DEFAULT_COLUMN_MIN_WIDTH, + }, + index: 1, + }) + ); + }); }); - it('should dispatch upsert column action on field checked', () => { - const result = render( - + describe('pagination', () => { + const isAtFirstPage = (result: RenderResult) => + result.getByTestId('pagination-button-0').classList.contains('euiPaginationButton-isActive'); + + const changePage = (result: RenderResult) => { + result.getByTestId('pagination-button-1').click(); + }; + + const defaultPaginationProps: FieldTableProps = { + ...defaultProps, + filteredBrowserFields: mockBrowserFields, + }; + + it('should paginate on page clicked', () => { + const result = render( + + + + ); + + expect(isAtFirstPage(result)).toBeTruthy(); + + changePage(result); + + expect(isAtFirstPage(result)).toBeFalsy(); + }); + + it('should not reset on field checked', () => { + const result = render( + + + + ); + + changePage(result); + + result.getAllByRole('checkbox').at(0)?.click(); + expect(mockDispatch).toHaveBeenCalled(); // assert some field has been selected + + expect(isAtFirstPage(result)).toBeFalsy(); + }); + + it('should reset on filter change', () => { + const result = render( , + { wrapper: TestProviders } + ); + + changePage(result); + expect(isAtFirstPage(result)).toBeFalsy(); + + result.rerender( + - - ); + ); - result.getByTestId(`field-${timestampFieldId}-checkbox`).click(); - - expect(mockDispatch).toHaveBeenCalledTimes(1); - expect(mockDispatch).toHaveBeenCalledWith( - tGridActions.upsertColumn({ - id: timelineId, - column: { - columnHeaderType: defaultColumnHeaderType, - id: timestampFieldId, - initialWidth: DEFAULT_COLUMN_MIN_WIDTH, - }, - index: 1, - }) - ); + expect(isAtFirstPage(result)).toBeTruthy(); + }); }); }); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_table.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_table.tsx index 684b09d0395ab..4f62cdd246871 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_table.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_table.tsx @@ -5,9 +5,9 @@ * 2.0. */ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; -import { EuiInMemoryTable } from '@elastic/eui'; +import { EuiInMemoryTable, Pagination, Direction } from '@elastic/eui'; import { useDispatch } from 'react-redux'; import { BrowserFields, ColumnHeaderOptions } from '../../../../../common'; import { getColumnHeader, getFieldColumns, getFieldItems, isActionsColumn } from './field_items'; @@ -16,6 +16,11 @@ import { tGridActions } from '../../../../store/t_grid'; import type { GetFieldTableColumns } from '../../../../../common/types/fields_browser'; import { FieldTableHeader } from './field_table_header'; +const DEFAULT_SORTING: { field: string; direction: Direction } = { + field: '', + direction: 'asc', +} as const; + export interface FieldTableProps { timelineId: string; columnHeaders: ColumnHeaderOptions[]; @@ -69,6 +74,12 @@ const FieldTableComponent: React.FC = ({ timelineId, onHide, }) => { + const [pageIndex, setPageIndex] = useState(0); + const [pageSize, setPageSize] = useState(10); + + const [sortField, setSortField] = useState(DEFAULT_SORTING.field); + const [sortDirection, setSortDirection] = useState(DEFAULT_SORTING.direction); + const dispatch = useDispatch(); const fieldItems = useMemo( @@ -103,6 +114,51 @@ const FieldTableComponent: React.FC = ({ [columnHeaders, dispatch, timelineId] ); + /** + * Pagination controls + */ + const pagination: Pagination = useMemo( + () => ({ + pageIndex, + pageSize, + totalItemCount: fieldItems.length, + pageSizeOptions: [10, 25, 50], + }), + [fieldItems.length, pageIndex, pageSize] + ); + + useEffect(() => { + // Resets the pagination when some filter has changed, consequently, the number of fields is different + setPageIndex(0); + }, [fieldItems.length]); + + /** + * Sorting controls + */ + const sorting = useMemo( + () => ({ + sort: { + field: sortField, + direction: sortDirection, + }, + }), + [sortDirection, sortField] + ); + + const onTableChange = useCallback(({ page, sort = DEFAULT_SORTING }) => { + const { index, size } = page; + const { field, direction } = sort; + + setPageIndex(index); + setPageSize(size); + + setSortField(field); + setSortDirection(direction); + }, []); + + /** + * Process columns + */ const columns = useMemo( () => getFieldColumns({ highlight: searchInput, onToggleColumn, getFieldTableColumns, onHide }), [onToggleColumn, searchInput, getFieldTableColumns, onHide] @@ -124,9 +180,10 @@ const FieldTableComponent: React.FC = ({ items={fieldItems} itemId="name" columns={columns} - pagination={true} - sorting={true} + pagination={pagination} + sorting={sorting} hasActions={hasActions} + onChange={onTableChange} compressed /> diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/helpers.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/helpers.tsx index d6c2c7de5aa18..fd590c468b7e7 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/helpers.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/helpers.tsx @@ -6,7 +6,6 @@ */ import { EuiBadge, EuiLoadingSpinner } from '@elastic/eui'; -import { pickBy } from 'lodash/fp'; import styled from 'styled-components'; import { TimelineId } from '../../../../types'; @@ -108,27 +107,46 @@ export const filterSelectedBrowserFields = ({ }): BrowserFields => { const selectedFieldIds = new Set(columnHeaders.map(({ id }) => id)); - const filteredBrowserFields: BrowserFields = Object.keys(browserFields).reduce( - (filteredCategories, categoryId) => ({ - ...filteredCategories, - [categoryId]: { - ...browserFields[categoryId], - fields: pickBy( - ({ name }) => name != null && selectedFieldIds.has(name), - browserFields[categoryId].fields - ), - }, - }), - {} - ); - - // only pick non-empty categories from the filtered browser fields - const nonEmptyCategories: BrowserFields = pickBy( - (category) => categoryHasFields(category), - filteredBrowserFields - ); - - return nonEmptyCategories; + const result: Record> = {}; + + for (const [categoryName, categoryDescriptor] of Object.entries(browserFields)) { + if (!categoryDescriptor.fields) { + // ignore any category that is missing fields. This is not expected to happen. + // eslint-disable-next-line no-continue + continue; + } + + // keep track of whether this category had a selected field, if so, we should emit it into the result + let hadSelected = false; + + // The selected fields for this `categoryName` + const selectedFields: Record> = {}; + + for (const [fieldName, fieldDescriptor] of Object.entries(categoryDescriptor.fields)) { + // For historical reasons, we consider the name as it appears on the field descriptor, not the `fieldName` (attribute name) itself. + // It is unclear if there is any point in continuing to do this. + const fieldNameFromDescriptor = fieldDescriptor.name; + + if (!fieldNameFromDescriptor) { + // Ignore any field that is missing a name in its descriptor. This is not expected to happen. + // eslint-disable-next-line no-continue + continue; + } + + if (selectedFieldIds.has(fieldNameFromDescriptor)) { + hadSelected = true; + selectedFields[fieldName] = fieldDescriptor; + } + } + + if (hadSelected) { + result[categoryName] = { + ...browserFields[categoryName], + fields: selectedFields, + }; + } + } + return result; }; export const getAlertColumnHeader = (timelineId: string, fieldId: string) => diff --git a/x-pack/plugins/timelines/public/store/t_grid/types.ts b/x-pack/plugins/timelines/public/store/t_grid/types.ts index c4627b3accd71..8e0b7e995dbcd 100644 --- a/x-pack/plugins/timelines/public/store/t_grid/types.ts +++ b/x-pack/plugins/timelines/public/store/t_grid/types.ts @@ -46,7 +46,7 @@ export enum TimelineId { usersPageExternalAlerts = 'users-page-external-alerts', hostsPageEvents = 'hosts-page-events', hostsPageExternalAlerts = 'hosts-page-external-alerts', - hostsPageSessions = 'hosts-page-sessions', + hostsPageSessions = 'hosts-page-sessions-v2', detectionsRulesDetailsPage = 'detections-rules-details-page', detectionsPage = 'detections-page', networkPageExternalAlerts = 'network-page-external-alerts', diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/index.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/index.ts index 980f19ac2950c..d450daadf4689 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/index.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/index.ts @@ -209,17 +209,13 @@ const timelineSessionsSearchStrategy = ({ }; const collapse = { - field: 'process.entity_id', - inner_hits: { - name: 'last_event', - size: 1, - sort: [{ '@timestamp': 'desc' }], - }, + field: 'process.entry_leader.entity_id', }; + const aggs = { total: { cardinality: { - field: 'process.entity_id', + field: 'process.entry_leader.entity_id', }, }, }; diff --git a/x-pack/plugins/timelines/tsconfig.json b/x-pack/plugins/timelines/tsconfig.json index 9677c0e64dd88..3063c1acda545 100644 --- a/x-pack/plugins/timelines/tsconfig.json +++ b/x-pack/plugins/timelines/tsconfig.json @@ -20,7 +20,6 @@ { "path": "../../../src/core/tsconfig.json" }, { "path": "../../../src/plugins/data/tsconfig.json" }, { "path": "../../../src/plugins/home/tsconfig.json" }, - { "path": "../data_enhanced/tsconfig.json" }, { "path": "../features/tsconfig.json" }, { "path": "../cases/tsconfig.json" }, { "path": "../licensing/tsconfig.json" }, diff --git a/x-pack/plugins/transform/kibana.json b/x-pack/plugins/transform/kibana.json index e0d549b634a21..6045d50ea26b9 100644 --- a/x-pack/plugins/transform/kibana.json +++ b/x-pack/plugins/transform/kibana.json @@ -5,6 +5,7 @@ "ui": true, "requiredPlugins": [ "data", + "dataViews", "home", "licensing", "management", diff --git a/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx b/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx index 42708f2a3f2e2..f90faf53e87b5 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx +++ b/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx @@ -23,7 +23,7 @@ import { indexService } from '../services/es_index_service'; export const useDeleteIndexAndTargetIndex = (items: TransformListRow[]) => { const { http, - savedObjects, + data: { dataViews: dataViewsContract }, ml: { extractErrorMessage }, application: { capabilities }, } = useAppDependencies(); @@ -46,9 +46,8 @@ export const useDeleteIndexAndTargetIndex = (items: TransformListRow[]) => { const checkDataViewExists = useCallback( async (indexName: string) => { try { - if (await indexService.dataViewExists(savedObjects.client, indexName)) { - setDataViewExists(true); - } + const dvExists = await indexService.dataViewExists(dataViewsContract, indexName); + setDataViewExists(dvExists); } catch (e) { const error = extractErrorMessage(e); @@ -63,7 +62,7 @@ export const useDeleteIndexAndTargetIndex = (items: TransformListRow[]) => { ); } }, - [savedObjects.client, toastNotifications, extractErrorMessage] + [dataViewsContract, toastNotifications, extractErrorMessage] ); const checkUserIndexPermission = useCallback(async () => { diff --git a/x-pack/plugins/transform/public/app/hooks/use_search_items/common.ts b/x-pack/plugins/transform/public/app/hooks/use_search_items/common.ts index 07c0a9ae9bcbe..60b2a080bba29 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_search_items/common.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_search_items/common.ts @@ -6,9 +6,9 @@ */ import { buildEsQuery } from '@kbn/es-query'; -import { SavedObjectsClientContract, SimpleSavedObject, IUiSettingsClient } from '@kbn/core/public'; +import type { IUiSettingsClient } from '@kbn/core/public'; import { getEsQueryConfig } from '@kbn/data-plugin/public'; -import { DataView, DataViewAttributes, DataViewsContract } from '@kbn/data-views-plugin/public'; +import type { DataView, DataViewsContract } from '@kbn/data-views-plugin/public'; import { matchAllQuery } from '../../common'; @@ -16,58 +16,21 @@ import { isDataView } from '../../../../common/types/data_view'; export type SavedSearchQuery = object; -type DataViewId = string; - -let dataViewCache: Array>> = []; -let fullDataViews; -let currentDataView = null; +let dataViewCache: DataView[] = []; export let refreshDataViews: () => Promise; -export function loadDataViews( - savedObjectsClient: SavedObjectsClientContract, - dataViews: DataViewsContract -) { - fullDataViews = dataViews; - return savedObjectsClient - .find({ - type: 'index-pattern', - fields: ['id', 'title', 'type', 'fields'], - perPage: 10000, - }) - .then((response) => { - dataViewCache = response.savedObjects; - - if (refreshDataViews === null) { - refreshDataViews = () => { - return new Promise((resolve, reject) => { - loadDataViews(savedObjectsClient, dataViews) - .then((resp) => { - resolve(resp); - }) - .catch((error) => { - reject(error); - }); - }); - }; - } - - return dataViewCache; - }); +export async function loadDataViews(dataViewsContract: DataViewsContract) { + dataViewCache = await dataViewsContract.find('*', 10000); + return dataViewCache; } export function getDataViewIdByTitle(dataViewTitle: string): string | undefined { - return dataViewCache.find((d) => d?.attributes?.title === dataViewTitle)?.id; + return dataViewCache.find(({ title }) => title === dataViewTitle)?.id; } type CombinedQuery = Record<'bool', any> | object; -export function loadCurrentDataView(dataViews: DataViewsContract, dataViewId: DataViewId) { - fullDataViews = dataViews; - currentDataView = fullDataViews.get(dataViewId); - return currentDataView; -} - export interface SearchItems { dataView: DataView; savedSearch: any; diff --git a/x-pack/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts b/x-pack/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts index 76fdc77c523e4..cd24d092f754c 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts @@ -15,33 +15,24 @@ import { getSavedSearch, getSavedSearchUrlConflictMessage } from '../../../share import { useAppDependencies } from '../../app_dependencies'; -import { - createSearchItems, - getDataViewIdByTitle, - loadCurrentDataView, - loadDataViews, - SearchItems, -} from './common'; +import { createSearchItems, getDataViewIdByTitle, loadDataViews, SearchItems } from './common'; export const useSearchItems = (defaultSavedObjectId: string | undefined) => { const [savedObjectId, setSavedObjectId] = useState(defaultSavedObjectId); const [error, setError] = useState(); const appDeps = useAppDependencies(); - const dataViews = appDeps.data.dataViews; + const dataViewsContract = appDeps.data.dataViews; const uiSettings = appDeps.uiSettings; - const savedObjectsClient = appDeps.savedObjects.client; const [searchItems, setSearchItems] = useState(undefined); async function fetchSavedObject(id: string) { - await loadDataViews(savedObjectsClient, dataViews); - let fetchedDataView; let fetchedSavedSearch; try { - fetchedDataView = await loadCurrentDataView(dataViews, id); + fetchedDataView = await dataViewsContract.get(id); } catch (e) { // Just let fetchedDataView stay undefined in case it doesn't exist. } diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_clone/use_clone_action.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_clone/use_clone_action.tsx index f6c700aef67cc..61fd9afdbee14 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_clone/use_clone_action.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_clone/use_clone_action.tsx @@ -21,8 +21,7 @@ export type CloneAction = ReturnType; export const useCloneAction = (forceDisable: boolean, transformNodes: number) => { const history = useHistory(); const appDeps = useAppDependencies(); - const savedObjectsClient = appDeps.savedObjects.client; - const dataViews = appDeps.data.dataViews; + const dataViewsContract = appDeps.data.dataViews; const toastNotifications = useToastNotifications(); const { getDataViewIdByTitle, loadDataViews } = useSearchItems(undefined); @@ -32,7 +31,7 @@ export const useCloneAction = (forceDisable: boolean, transformNodes: number) => const clickHandler = useCallback( async (item: TransformListRow) => { try { - await loadDataViews(savedObjectsClient, dataViews); + await loadDataViews(dataViewsContract); const dataViewTitle = Array.isArray(item.config.source.index) ? item.config.source.index.join(',') : item.config.source.index; @@ -57,14 +56,7 @@ export const useCloneAction = (forceDisable: boolean, transformNodes: number) => }); } }, - [ - history, - savedObjectsClient, - dataViews, - toastNotifications, - loadDataViews, - getDataViewIdByTitle, - ] + [history, dataViewsContract, toastNotifications, loadDataViews, getDataViewIdByTitle] ); const action: TransformListAction = useMemo( diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_discover/use_action_discover.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_discover/use_action_discover.tsx index 9194d9f63045e..aeb5c3e2d09a3 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_discover/use_action_discover.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_discover/use_action_discover.tsx @@ -25,11 +25,12 @@ const getDataViewTitleFromTargetIndex = (item: TransformListRow) => export type DiscoverAction = ReturnType; export const useDiscoverAction = (forceDisable: boolean) => { - const appDeps = useAppDependencies(); - const { share } = appDeps; - const savedObjectsClient = appDeps.savedObjects.client; - const dataViews = appDeps.data.dataViews; - const isDiscoverAvailable = !!appDeps.application.capabilities.discover?.show; + const { + share, + data: { dataViews: dataViewsContract }, + application: { capabilities }, + } = useAppDependencies(); + const isDiscoverAvailable = !!capabilities.discover?.show; const { getDataViewIdByTitle, loadDataViews } = useSearchItems(undefined); @@ -37,12 +38,12 @@ export const useDiscoverAction = (forceDisable: boolean) => { useEffect(() => { async function checkDataViewAvailability() { - await loadDataViews(savedObjectsClient, dataViews); + await loadDataViews(dataViewsContract); setDataViewsLoaded(true); } checkDataViewAvailability(); - }, [dataViews, loadDataViews, savedObjectsClient]); + }, [loadDataViews, dataViewsContract]); const clickHandler = useCallback( (item: TransformListRow) => { diff --git a/x-pack/plugins/transform/public/app/services/es_index_service.ts b/x-pack/plugins/transform/public/app/services/es_index_service.ts index 5a0f907b78e22..609018c30b226 100644 --- a/x-pack/plugins/transform/public/app/services/es_index_service.ts +++ b/x-pack/plugins/transform/public/app/services/es_index_service.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { HttpSetup, SavedObjectsClientContract } from '@kbn/core/public'; -import { DataView } from '@kbn/data-views-plugin/public'; +import type { HttpSetup } from '@kbn/core/public'; +import type { DataViewsContract } from '@kbn/data-views-plugin/public'; import { API_BASE_PATH } from '../../../common/constants'; export class IndexService { @@ -18,16 +18,8 @@ export class IndexService { return privilege.hasAllPrivileges; } - async dataViewExists(savedObjectsClient: SavedObjectsClientContract, indexName: string) { - const response = await savedObjectsClient.find({ - type: 'index-pattern', - perPage: 1, - search: `"${indexName}"`, - searchFields: ['title'], - fields: ['title'], - }); - const ip = response.savedObjects.find((obj) => obj.attributes.title === indexName); - return ip !== undefined; + async dataViewExists(dataViewsContract: DataViewsContract, indexName: string) { + return (await dataViewsContract.find(indexName)).some(({ title }) => title === indexName); } } diff --git a/x-pack/plugins/transform/public/plugin.ts b/x-pack/plugins/transform/public/plugin.ts index 74cd845dd49da..762dfd2bcaab8 100644 --- a/x-pack/plugins/transform/public/plugin.ts +++ b/x-pack/plugins/transform/public/plugin.ts @@ -9,6 +9,7 @@ import { i18n as kbnI18n } from '@kbn/i18n'; import type { CoreSetup } from '@kbn/core/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import type { SavedObjectsStart } from '@kbn/saved-objects-plugin/public'; import type { ManagementSetup } from '@kbn/management-plugin/public'; @@ -21,6 +22,7 @@ import { getTransformHealthRuleType } from './alerting'; export interface PluginsDependencies { data: DataPublicPluginStart; + dataViews: DataViewsPublicPluginStart; management: ManagementSetup; home: HomePublicPluginSetup; savedObjects: SavedObjectsStart; diff --git a/x-pack/plugins/transform/readme.md b/x-pack/plugins/transform/readme.md index 51a89f224fb29..8a0ea7eb4f660 100644 --- a/x-pack/plugins/transform/readme.md +++ b/x-pack/plugins/transform/readme.md @@ -106,8 +106,8 @@ and Kibana instance that the tests will be run against. 1. Functional UI tests with `Trial` license (default config): - node scripts/functional_tests_server.js - node scripts/functional_test_runner.js --include-tag transform + node scripts/functional_tests_server.js --config test/functional/apps/transform/config.ts + node scripts/functional_test_runner.js --config test/functional/apps/transform/config.ts Transform functional `Trial` license tests are located in `x-pack/test/functional/apps/transform`. @@ -120,7 +120,7 @@ and Kibana instance that the tests will be run against. 1. API integration tests with `Trial` license: - node scripts/functional_tests_server.js + node scripts/functional_tests_server.js --config test/api_integration/config.ts node scripts/functional_test_runner.js --config test/api_integration/config.ts --include-tag transform Transform API integration `Trial` license tests are located in `x-pack/test/api_integration/apis/transform`. diff --git a/x-pack/plugins/transform/server/plugin.ts b/x-pack/plugins/transform/server/plugin.ts index c803785de2c4b..3eab84ca0b5fc 100644 --- a/x-pack/plugins/transform/server/plugin.ts +++ b/x-pack/plugins/transform/server/plugin.ts @@ -6,11 +6,11 @@ */ import { i18n } from '@kbn/i18n'; -import { CoreSetup, Plugin, Logger, PluginInitializerContext } from '@kbn/core/server'; +import { CoreSetup, CoreStart, Plugin, Logger, PluginInitializerContext } from '@kbn/core/server'; import { LicenseType } from '@kbn/licensing-plugin/common/types'; -import { Dependencies } from './types'; +import { PluginSetupDependencies, PluginStartDependencies } from './types'; import { ApiRoutes } from './routes'; import { License } from './services'; import { registerTransformHealthRuleType } from './lib/alerting'; @@ -38,8 +38,8 @@ export class TransformServerPlugin implements Plugin<{}, void, any, any> { } setup( - { http, getStartServices, elasticsearch }: CoreSetup, - { licensing, features, alerting }: Dependencies + { http, getStartServices, elasticsearch }: CoreSetup, + { licensing, features, alerting }: PluginSetupDependencies ): {} { const router = http.createRouter(); @@ -74,6 +74,7 @@ export class TransformServerPlugin implements Plugin<{}, void, any, any> { this.apiRoutes.setup({ router, license: this.license, + getStartServices, }); if (alerting) { @@ -83,7 +84,7 @@ export class TransformServerPlugin implements Plugin<{}, void, any, any> { return {}; } - start() {} + start(core: CoreStart, plugins: PluginStartDependencies) {} stop() {} } diff --git a/x-pack/plugins/transform/server/routes/api/transforms.ts b/x-pack/plugins/transform/server/routes/api/transforms.ts index f1c5e74056a94..7c4df878456ba 100644 --- a/x-pack/plugins/transform/server/routes/api/transforms.ts +++ b/x-pack/plugins/transform/server/routes/api/transforms.ts @@ -13,10 +13,9 @@ import { KibanaResponseFactory, RequestHandler, RequestHandlerContext, - SavedObjectsClientContract, } from '@kbn/core/server'; -import { DataView } from '@kbn/data-views-plugin/common'; +import { DataViewsService } from '@kbn/data-views-plugin/common'; import { TRANSFORM_STATE } from '../../../common/constants'; import { transformIdParamSchema, @@ -74,7 +73,7 @@ enum TRANSFORM_ACTIONS { } export function registerTransformsRoutes(routeDependencies: RouteDependencies) { - const { router, license } = routeDependencies; + const { router, license, getStartServices } = routeDependencies; /** * @apiGroup Transforms * @@ -303,7 +302,16 @@ export function registerTransformsRoutes(routeDependencies: RouteDependencies) { license.guardApiRoute( async (ctx, req, res) => { try { - const body = await deleteTransforms(req.body, ctx, res); + const [{ savedObjects, elasticsearch }, { dataViews }] = await getStartServices(); + const savedObjectsClient = savedObjects.getScopedClient(req); + const esClient = elasticsearch.client.asScoped(req).asCurrentUser; + + const dataViewsService = await dataViews.dataViewsServiceFactory( + savedObjectsClient, + esClient, + req + ); + const body = await deleteTransforms(req.body, ctx, res, dataViewsService); if (body && body.status) { if (body.status === 404) { @@ -456,29 +464,20 @@ export function registerTransformsRoutes(routeDependencies: RouteDependencies) { registerTransformNodesRoutes(routeDependencies); } -async function getDataViewId(indexName: string, savedObjectsClient: SavedObjectsClientContract) { - const response = await savedObjectsClient.find({ - type: 'index-pattern', - perPage: 1, - search: `"${indexName}"`, - searchFields: ['title'], - fields: ['title'], - }); - const ip = response.saved_objects.find((obj) => obj.attributes.title === indexName); - return ip?.id; +async function getDataViewId(indexName: string, dataViewsService: DataViewsService) { + const dv = (await dataViewsService.find(indexName)).find(({ title }) => title === indexName); + return dv?.id; } -async function deleteDestDataViewById( - dataViewId: string, - savedObjectsClient: SavedObjectsClientContract -) { - return await savedObjectsClient.delete('index-pattern', dataViewId); +async function deleteDestDataViewById(dataViewId: string, dataViewsService: DataViewsService) { + return await dataViewsService.delete(dataViewId); } async function deleteTransforms( reqBody: DeleteTransformsRequestSchema, ctx: RequestHandlerContext, - response: KibanaResponseFactory + response: KibanaResponseFactory, + dataViewsService: DataViewsService ) { const { transformsInfo } = reqBody; @@ -491,7 +490,6 @@ async function deleteTransforms( const coreContext = await ctx.core; const esClient = coreContext.elasticsearch.client; - const soClient = coreContext.savedObjects.client; for (const transformInfo of transformsInfo) { let destinationIndex: string | undefined; @@ -548,9 +546,9 @@ async function deleteTransforms( // Delete the data view if there's a data view that matches the name of dest index if (destinationIndex && deleteDestDataView) { try { - const dataViewId = await getDataViewId(destinationIndex, soClient); + const dataViewId = await getDataViewId(destinationIndex, dataViewsService); if (dataViewId) { - await deleteDestDataViewById(dataViewId, soClient); + await deleteDestDataViewById(dataViewId, dataViewsService); destDataViewDeleted.success = true; } } catch (deleteDestDataViewError) { diff --git a/x-pack/plugins/transform/server/types.ts b/x-pack/plugins/transform/server/types.ts index f9be90a1a20da..26bdf02acb5c0 100644 --- a/x-pack/plugins/transform/server/types.ts +++ b/x-pack/plugins/transform/server/types.ts @@ -5,19 +5,25 @@ * 2.0. */ -import { IRouter } from '@kbn/core/server'; +import { IRouter, CoreSetup } from '@kbn/core/server'; +import { PluginStart as DataViewsServerPluginStart } from '@kbn/data-views-plugin/server'; import { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server'; import type { AlertingPlugin } from '@kbn/alerting-plugin/server'; import { License } from './services'; -export interface Dependencies { +export interface PluginSetupDependencies { licensing: LicensingPluginSetup; features: FeaturesPluginSetup; alerting?: AlertingPlugin['setup']; } +export interface PluginStartDependencies { + dataViews: DataViewsServerPluginStart; +} + export interface RouteDependencies { router: IRouter; license: License; + getStartServices: CoreSetup['getStartServices']; } diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 3cdeff1d91709..513f83e23f177 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -365,7 +365,6 @@ "xpack.lens.functions.counterRate.args.outputColumnNameHelpText": "Nom de la colonne dans laquelle le taux de compteur résultant sera stocké", "xpack.lens.functions.counterRate.help": "Calcule le taux de compteur d'une colonne dans un tableau de données", "xpack.lens.functions.lastValue.missingSortField": "Cette vue de données ne contient aucun champ de date.", - "xpack.lens.functions.mergeTables.help": "Aide pour fusionner n'importe quel nombre de tableaux Kibana en un tableau unique et l'exposer via un adaptateur d'inspecteur", "xpack.lens.functions.renameColumns.help": "Aide pour renommer les colonnes d'un tableau de données", "xpack.lens.functions.renameColumns.idMap.help": "Un objet encodé JSON dans lequel les clés sont les anciens ID de colonne et les valeurs sont les nouveaux ID correspondants. Tous les autres ID de colonne sont conservés.", "xpack.lens.functions.timeScale.dateColumnMissingMessage": "L'ID de colonne de date {columnId} n'existe pas.", @@ -440,28 +439,18 @@ "xpack.lens.indexPattern.dateHistogram.autoAdvancedExplanation": "L'intervalle suit cette logique :", "xpack.lens.indexPattern.dateHistogram.autoBasicExplanation": "L'histogramme des dates automatique divise un champ de données en groupes par intervalle.", "xpack.lens.indexPattern.dateHistogram.autoBoundHeader": "Intervalle cible mesuré", - "xpack.lens.indexPattern.dateHistogram.autoHelpText": "Fonctionnement", - "xpack.lens.indexPattern.dateHistogram.autoInterval": "Personnaliser l'intervalle de temps", "xpack.lens.indexPattern.dateHistogram.autoIntervalHeader": "Intervalle utilisé", "xpack.lens.indexPattern.dateHistogram.autoLongerExplanation": "Pour choisir l'intervalle, Lens divise la plage temporelle spécifiée par le paramètre {targetBarSetting}. Lens calcule le meilleur intervalle pour vos données. Par exemple 30m, 1h et 12. Le nombre maximal de barres est défini par la valeur {maxBarSetting}.", "xpack.lens.indexPattern.dateHistogram.bindToGlobalTimePicker": "Lier au sélecteur d'heure globale", - "xpack.lens.indexPattern.dateHistogram.days": "jours", "xpack.lens.indexPattern.dateHistogram.dropPartialBuckets": "Abandonner les compartiments partiels", "xpack.lens.indexPattern.dateHistogram.dropPartialBucketsHelp": "L’abandon des compartiments partiels est désactivé, car ceux-ci ne peuvent être calculés que pour un champ temporel lié au sélecteur d’heure globale en haut à droite.", "xpack.lens.indexPattern.dateHistogram.globalTimePickerHelp": "Filtrez le champ sélectionné à l’aide du sélecteur d’heure globale en haut à droite. Ce paramètre ne peut pas être désactivé pour le champ temporel par défaut de la vue de données actuelle.", - "xpack.lens.indexPattern.dateHistogram.hours": "heures", "xpack.lens.indexPattern.dateHistogram.includeEmptyRows": "Inclure les lignes vides", - "xpack.lens.indexPattern.dateHistogram.milliseconds": "millisecondes", "xpack.lens.indexPattern.dateHistogram.minimumInterval": "Intervalle minimal", - "xpack.lens.indexPattern.dateHistogram.minutes": "minutes", - "xpack.lens.indexPattern.dateHistogram.month": "mois", "xpack.lens.indexPattern.dateHistogram.moreThanYear": "Plus d'un an", "xpack.lens.indexPattern.dateHistogram.restrictedInterval": "Intervalle fixé à {intervalValue} en raison de restrictions d'agrégation.", - "xpack.lens.indexPattern.dateHistogram.seconds": "secondes", "xpack.lens.indexPattern.dateHistogram.titleHelp": "Fonctionnement de l'histogramme des dates automatique", "xpack.lens.indexPattern.dateHistogram.upTo": "Jusqu'à", - "xpack.lens.indexPattern.dateHistogram.week": "semaine", - "xpack.lens.indexPattern.dateHistogram.year": "an", "xpack.lens.indexPattern.dateHistogramTimeShift": "Dans un calque unique, vous ne pouvez pas combiner un décalage de plage temporelle précédent avec des histogrammes de dates. Utilisez une durée de décalage temporel explicite dans \"{column}\" ou remplacez l’histogramme de dates.", "xpack.lens.indexPattern.decimalPlacesLabel": "Décimales", "xpack.lens.indexPattern.defaultFormatLabel": "Par défaut", @@ -538,7 +527,6 @@ "xpack.lens.indexPattern.incompleteOperation": "(incomplet)", "xpack.lens.indexPattern.intervals": "Intervalles", "xpack.lens.indexPattern.invalidFieldLabel": "Champ non valide. Vérifiez votre vue de données ou choisissez un autre champ.", - "xpack.lens.indexPattern.invalidInterval": "Valeur d'intervalle non valide", "xpack.lens.indexPattern.invalidOperationLabel": "Ce champ ne fonctionne pas avec la fonction sélectionnée.", "xpack.lens.indexPattern.invalidReferenceConfiguration": "La dimension \"{dimensionLabel}\" n'est pas configurée correctement", "xpack.lens.indexPattern.invalidTimeShift": "Décalage non valide. Entrez un entier positif suivi par l'une des unités suivantes : s, m, h, d, w, M, y. Par exemple, 3h pour 3 heures", @@ -573,7 +561,6 @@ "xpack.lens.indexPattern.moving_average.signature": "indicateur : nombre, [window] : nombre", "xpack.lens.indexPattern.movingAverage": "Moyenne mobile", "xpack.lens.indexPattern.movingAverage.basicExplanation": "La moyenne mobile fait glisser une fenêtre sur les données et affiche la valeur moyenne. La moyenne mobile est prise en charge uniquement par les histogrammes des dates.", - "xpack.lens.indexPattern.movingAverage.helpText": "Fonctionnement", "xpack.lens.indexPattern.movingAverage.limitations": "La première valeur de moyenne mobile commence au deuxième élément.", "xpack.lens.indexPattern.movingAverage.longerExplanation": "Pour calculer la moyenne mobile, Lens utilise la moyenne de la fenêtre et applique une politique d'omission pour les blancs. Pour les valeurs manquantes, le groupe est ignoré, et le calcul est effectué sur la valeur suivante.", "xpack.lens.indexPattern.movingAverage.tableExplanation": "Par exemple, avec les données [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], vous pouvez calculer une moyenne mobile simple avec une taille de fenêtre de 5 :", @@ -707,13 +694,10 @@ "xpack.lens.indexPattern.timeShiftSmallWarning": "{label} utilise un décalage temporel de {columnTimeShift} qui est inférieur à l'intervalle de l'histogramme des dates de {interval}. Pour éviter une non-correspondance des données, utilisez un multiple de {interval} comme décalage.", "xpack.lens.indexPattern.uniqueLabel": "{label} [{num}]", "xpack.lens.indexPattern.useAsTopLevelAgg": "Regrouper d'abord en fonction de ce champ", - "xpack.lens.indexPatterns.actionsPopoverLabel": "Paramètres Vue de données", - "xpack.lens.indexPatterns.addFieldButton": "Ajouter un champ à la vue de données", "xpack.lens.indexPatterns.clearFiltersLabel": "Effacer le nom et saisissez les filtres", "xpack.lens.indexPatterns.fieldFiltersLabel": "Filtrer par type", "xpack.lens.indexPatterns.fieldSearchLiveRegion": "{availableFields} {availableFields, plural, one {champ} other {champs}} disponible(s). {emptyFields} {emptyFields, plural, one {champ} other {champs}} vide(s). {metaFields} {metaFields, plural, one {champ} other {champs}} méta.", "xpack.lens.indexPatterns.filterByNameLabel": "Rechercher les noms de champs", - "xpack.lens.indexPatterns.manageFieldButton": "Gérer les champs de la vue de données", "xpack.lens.indexPatterns.noAvailableDataLabel": "Aucun champ disponible ne contient de données.", "xpack.lens.indexPatterns.noDataLabel": "Aucun champ.", "xpack.lens.indexPatterns.noEmptyDataLabel": "Aucun champ vide.", @@ -811,7 +795,6 @@ "xpack.lens.shared.axisNameLabel": "Titre de l'axe", "xpack.lens.shared.chartValueLabelVisibilityLabel": "Étiquettes", "xpack.lens.shared.curveLabel": "Options visuelles", - "xpack.lens.shared.legend.filterForValueButtonAriaLabel": "Filtre pour la valeur", "xpack.lens.shared.legend.filterOptionsLegend": "{legendDataLabel}, options de filtre", "xpack.lens.shared.legend.filterOutValueButtonAriaLabel": "Filtrer la valeur", "xpack.lens.shared.legendAlignmentLabel": "Alignement", @@ -2621,6 +2604,107 @@ "data.searchSessions.sessionService.sessionObjectFetchError": "Échec de récupération des informations de la session de recherche", "data.triggers.applyFilterDescription": "Lorsque le filtre Kibana est appliqué. Peut être un filtre simple ou un filtre de plage.", "data.triggers.applyFilterTitle": "Appliquer le filtre", + "data.mgmt.searchSessions.actionDelete": "Supprimer", + "data.mgmt.searchSessions.actionExtend": "Étendre", + "data.mgmt.searchSessions.actionRename": "Modifier le nom", + "data.mgmt.searchSessions.actions.tooltip.moreActions": "Plus d'actions", + "data.mgmt.searchSessions.api.deleted": "La session de recherche a été supprimée.", + "data.mgmt.searchSessions.api.deletedError": "Impossible de supprimer la session de recherche !", + "data.mgmt.searchSessions.api.extended": "La session de recherche a été étendue.", + "data.mgmt.searchSessions.api.extendError": "Impossible d'étendre la session de recherche !", + "data.mgmt.searchSessions.api.fetchError": "Impossible d'actualiser la page !", + "data.mgmt.searchSessions.api.fetchTimeout": "La récupération des informations de la session de recherche a expiré après {timeout} secondes", + "data.mgmt.searchSessions.api.rename": "La session de recherche a été renommée", + "data.mgmt.searchSessions.api.renameError": "Impossible de renommer la session de recherche", + "data.mgmt.searchSessions.appTitle": "Sessions de recherche", + "data.mgmt.searchSessions.ariaLabel.moreActions": "Plus d'actions", + "data.mgmt.searchSessions.cancelModal.cancelButton": "Annuler", + "data.mgmt.searchSessions.cancelModal.deleteButton": "Supprimer", + "data.mgmt.searchSessions.cancelModal.message": "La suppression de la session de recherche \"{name}\" supprime tous les résultats mis en cache.", + "data.mgmt.searchSessions.cancelModal.title": "Supprimer la session de recherche", + "data.mgmt.searchSessions.extendModal.dontExtendButton": "Annuler", + "data.mgmt.searchSessions.extendModal.extendButton": "Étendre l'expiration", + "data.mgmt.searchSessions.extendModal.extendMessage": "L'expiration de la session de recherche \"{name}\" sera étendue jusqu'à {newExpires}.", + "data.mgmt.searchSessions.extendModal.title": "Étendre l'expiration de la session de recherche", + "data.mgmt.searchSessions.flyoutTitle": "Inspecter", + "data.mgmt.searchSessions.main.backgroundSessionsDocsLinkText": "Documentation", + "data.mgmt.searchSessions.main.sectionDescription": "Gérez vos sessions de recherche enregistrées.", + "data.mgmt.searchSessions.main.sectionTitle": "Sessions de recherche", + "data.mgmt.searchSessions.renameModal.cancelButton": "Annuler", + "data.mgmt.searchSessions.renameModal.renameButton": "Enregistrer", + "data.mgmt.searchSessions.renameModal.searchSessionNameInputLabel": "Nom de la session de recherche", + "data.mgmt.searchSessions.renameModal.title": "Modifier le nom de la session de recherche", + "data.mgmt.searchSessions.search.filterApp": "Application", + "data.mgmt.searchSessions.search.filterStatus": "Statut", + "data.mgmt.searchSessions.search.tools.refresh": "Actualiser", + "data.mgmt.searchSessions.status.expireDateUnknown": "inconnu", + "data.mgmt.searchSessions.status.expiresOn": "Expire le {expireDate}", + "data.mgmt.searchSessions.status.expiresSoonInDays": "Expire dans {numDays} jours", + "data.mgmt.searchSessions.status.expiresSoonInDaysTooltip": "{numDays} jours", + "data.mgmt.searchSessions.status.expiresSoonInHours": "Cette session expire dans {numHours} heures", + "data.mgmt.searchSessions.status.expiresSoonInHoursTooltip": "{numHours} heures", + "data.mgmt.searchSessions.status.label.cancelled": "Annulé", + "data.mgmt.searchSessions.status.label.complete": "Terminé", + "data.mgmt.searchSessions.status.label.error": "Erreur", + "data.mgmt.searchSessions.status.label.expired": "Expiré", + "data.mgmt.searchSessions.status.label.inProgress": "En cours", + "data.mgmt.searchSessions.status.message.cancelled": "Annulé par l'utilisateur", + "data.mgmt.searchSessions.status.message.createdOn": "Expire le {expireDate}", + "data.mgmt.searchSessions.status.message.error": "Erreur : {error}", + "data.mgmt.searchSessions.status.message.expiredOn": "Expiré le {expireDate}", + "data.mgmt.searchSessions.table.headerExpiration": "Expiration", + "data.mgmt.searchSessions.table.headerName": "Nom", + "data.mgmt.searchSessions.table.headerStarted": "Créé", + "data.mgmt.searchSessions.table.headerStatus": "Statut", + "data.mgmt.searchSessions.table.headerType": "Application", + "data.mgmt.searchSessions.table.notRestorableWarning": "La session de recherche va être de nouveau exécutée. Vous pouvez ensuite l'enregistrer pour une utilisation ultérieure.", + "data.mgmt.searchSessions.table.numSearches": "# recherches", + "data.mgmt.searchSessions.table.versionIncompatibleWarning": "Cette session de recherche a été créée dans une instance Kibana exécutant une version différente. Il se peut qu'elle ne soit pas correctement restaurée.", + "data.mgmt.searchSessions.table.mlAppName": "Machine Learning", + "data.search.statusError": "Recherche terminée avec un statut {errorCode}", + "data.search.statusThrow": "Le statut de la recherche a généré un statut d'erreur {message} ({errorCode})", + "data.searchSessionIndicator.cancelButtonText": "Arrêter la session", + "data.searchSessionIndicator.canceledDescriptionText": "Vous visualisez des données incomplètes", + "data.searchSessionIndicator.canceledIconAriaLabel": "La session de recherche s'est arrêtée", + "data.searchSessionIndicator.canceledTitleText": "La session de recherche s'est arrêtée", + "data.searchSessionIndicator.canceledTooltipText": "La session de recherche s'est arrêtée", + "data.searchSessionIndicator.canceledWhenText": "Arrêtée {when}", + "data.searchSessionIndicator.continueInBackgroundButtonText": "Enregistrer la session", + "data.searchSessionIndicator.disabledDueToDisabledGloballyMessage": "Vous ne disposez pas d'autorisations pour gérer les sessions de recherche", + "data.searchSessionIndicator.disabledDueToTimeoutMessage": "Les résultats de la session de recherche ont expiré.", + "data.searchSessionIndicator.loadingInTheBackgroundDescriptionText": "Vous pouvez retourner aux résultats terminés à partir de la page Gestion", + "data.searchSessionIndicator.loadingInTheBackgroundIconAriaLabel": "Session enregistrée en cours", + "data.searchSessionIndicator.loadingInTheBackgroundIconTooltipText": "Session enregistrée en cours", + "data.searchSessionIndicator.loadingInTheBackgroundTitleText": "Session enregistrée en cours", + "data.searchSessionIndicator.loadingInTheBackgroundWhenText": "Débuté {when}", + "data.searchSessionIndicator.loadingResultsDescription": "Enregistrer votre session, poursuivre votre travail et retourner aux résultats terminés", + "data.searchSessionIndicator.loadingResultsIconAriaLabel": "Chargement de la session de recherche", + "data.searchSessionIndicator.loadingResultsIconTooltipText": "Chargement de la session de recherche", + "data.searchSessionIndicator.loadingResultsTitle": "Votre recherche prend un certain temps…", + "data.searchSessionIndicator.loadingResultsWhenText": "Débuté {when}", + "data.searchSessionIndicator.restoredDescriptionText": "Vous affichez des données mises en cache d'une plage temporelle spécifique. La modification de la plage temporelle ou des filtres entraînera la réexécution de la session", + "data.searchSessionIndicator.restoredResultsIconAriaLabel": "Session enregistrée restaurée", + "data.searchSessionIndicator.restoredResultsTooltipText": "Session de recherche restaurée", + "data.searchSessionIndicator.restoredTitleText": "Session de recherche restaurée", + "data.searchSessionIndicator.restoredWhenText": "Terminé {when}", + "data.searchSessionIndicator.resultLoadedInTheBackgroundDescriptionText": "Vous pouvez retourner à ces résultats à partir de la page de gestion", + "data.searchSessionIndicator.resultLoadedInTheBackgroundIconAriaLabel": "Session enregistrée terminée", + "data.searchSessionIndicator.resultLoadedInTheBackgroundIconTooltipText": "Session enregistrée terminée", + "data.searchSessionIndicator.resultLoadedInTheBackgroundTitleText": "Session de recherche enregistrée", + "data.searchSessionIndicator.resultLoadedInTheBackgroundWhenText": "Terminé {when}", + "data.searchSessionIndicator.resultsLoadedDescriptionText": "Enregistrer votre session pour y revenir ultérieurement", + "data.searchSessionIndicator.resultsLoadedIconAriaLabel": "Session de recherche terminée", + "data.searchSessionIndicator.resultsLoadedIconTooltipText": "Session de recherche terminée", + "data.searchSessionIndicator.resultsLoadedText": "Session de recherche terminée", + "data.searchSessionIndicator.resultsLoadedWhenText": "Terminé {when}", + "data.searchSessionIndicator.saveButtonText": "Enregistrer la session", + "data.searchSessionIndicator.viewSearchSessionsLinkText": "Gérer les sessions", + "data.searchSessionName.ariaLabelText": "Nom de la session de recherche", + "data.searchSessionName.editAriaLabelText": "Modifier le nom de la session de recherche", + "data.searchSessionName.placeholderText": "Entrer un nom pour la session de recherche", + "data.searchSessionName.saveButtonText": "Enregistrer", + "data.sessions.management.flyoutText": "Configuration de cette session de recherche", + "data.sessions.management.flyoutTitle": "Inspecter la session de recherche", "dataViews.deprecations.scriptedFields.manualStepOneMessage": "Accédez à Gestion de la Suite > Kibana > Vues de données.", "dataViews.deprecations.scriptedFields.manualStepTwoMessage": "Mettez à jour les vues de données {numberOfIndexPatternsWithScriptedFields} qui ont des champs scriptés pour qu’elles utilisent des champs d’exécution. Dans la plupart des cas, pour migrer des scripts existants, vous devrez remplacer \"return ;\" par \"emit();\". Vues de données avec au moins un champ scripté : {allTitles}", "dataViews.deprecations.scriptedFieldsMessage": "Vous avez {numberOfIndexPatternsWithScriptedFields} vues de données ({titlesPreview}...) qui utilisent des champs scriptés. Les champs scriptés sont déclassés et seront supprimés à l’avenir. Utilisez plutôt des champs d’exécution.", @@ -2788,7 +2872,6 @@ "discover.field.mappingConflict": "Ce champ est défini avec plusieurs types (chaîne, entier, etc.) dans les différents index qui correspondent à ce modèle. Vous pouvez toujours utiliser ce champ conflictuel, mais il sera indisponible pour les fonctions qui nécessitent que Kibana en connaisse le type. Pour corriger ce problème, vous devrez réindexer vos données.", "discover.field.mappingConflict.title": "Conflit de mapping", "discover.field.title": "{fieldName} ({fieldDisplayName})", - "discover.fieldChooser.dataViews.createNewDataView": "Créer une nouvelle vue de données", "discover.fieldChooser.detailViews.emptyStringText": "Chaîne vide", "discover.fieldChooser.detailViews.existsInRecordsText": "Existe dans {value} / {totalValue} enregistrements", "discover.fieldChooser.detailViews.filterOutValueButtonAriaLabel": "Exclure le {field} : \"{value}\"", @@ -2825,10 +2908,6 @@ "discover.fieldChooser.filter.toggleButton.no": "non", "discover.fieldChooser.filter.toggleButton.yes": "oui", "discover.fieldChooser.filter.typeLabel": "Type", - "discover.fieldChooser.indexPattern.changeDataViewTitle": "Changer de vue de données", - "discover.fieldChooser.indexPatterns.actionsPopoverLabel": "Paramètres Vue de données", - "discover.fieldChooser.indexPatterns.addFieldButton": "Ajouter un champ", - "discover.fieldChooser.indexPatterns.manageFieldButton": "Gérer les paramètres", "discover.fieldChooser.searchPlaceHolder": "Rechercher les noms de champs", "discover.fieldChooser.toggleFieldFilterButtonHideAriaLabel": "Masquer les paramètres de filtre de champs", "discover.fieldChooser.toggleFieldFilterButtonShowAriaLabel": "Afficher les paramètres de filtre de champs", @@ -3365,11 +3444,11 @@ "expressionXY.axisTitlesVisibilityConfig.yLeft.help": "Spécifie si le titre de l'axe Y de gauche est visible ou non.", "expressionXY.axisTitlesVisibilityConfig.yRight.help": "Spécifie si le titre de l'axe Y de droite est visible ou non.", "expressionXY.dataLayer.accessors.help": "Les colonnes à afficher sur l’axe y.", - "expressionXY.dataLayer.columnToLabel.help": "Paires clé-valeur JSON de l’ID de colonne pour l’étiquette", + "expressionXY.layer.columnToLabel.help": "Paires clé-valeur JSON de l’ID de colonne pour l’étiquette", "expressionXY.dataLayer.help": "Configurer un calque dans le graphique xy", "expressionXY.dataLayer.hide.help": "Afficher/masquer l’axe", "expressionXY.dataLayer.isHistogram.help": "Disposer le graphique sous forme d’histogramme ou non", - "expressionXY.dataLayer.layerId.help": "ID du calque", + "expressionXY.layers.layerId.help": "ID du calque", "expressionXY.dataLayer.palette.help": "Palette", "expressionXY.dataLayer.seriesType.help": "Type de graphique à afficher.", "expressionXY.dataLayer.splitAccessor.help": "Colonne selon laquelle effectuer la division", @@ -3400,9 +3479,7 @@ "expressionXY.legendConfig.showSingleSeries.help": "Spécifie si une légende comportant une seule entrée doit être affichée", "expressionXY.legendConfig.verticalAlignment.help": "Spécifie l'alignement vertical de la légende lorsqu'elle est affichée à l'intérieur du graphique.", "expressionXY.referenceLineLayer.accessors.help": "Les colonnes à afficher sur l’axe y.", - "expressionXY.referenceLineLayer.columnToLabel.help": "Paires clé-valeur JSON de l’ID de colonne pour l’étiquette", "expressionXY.referenceLineLayer.help": "Configurer une ligne de référence dans le graphique xy", - "expressionXY.referenceLineLayer.layerId.help": "ID du calque", "expressionXY.referenceLineLayer.yConfig.help": "Configuration supplémentaire pour les axes y", "expressionXY.tickLabelsConfig.help": "Configurer l’aspect des étiquettes de coche du graphique xy", "expressionXY.tickLabelsConfig.x.help": "Spécifie si les étiquettes de graduation de l'axe X sont visibles ou non.", @@ -3433,7 +3510,7 @@ "expressionXY.xyVis.help": "Graphique X/Y", "expressionXY.xyVis.hideEndzones.help": "Masquer les marqueurs de zone de fin pour les données partielles", "expressionXY.xyVis.labelsOrientation.help": "Définit la rotation des étiquettes des axes", - "expressionXY.xyVis.layers.help": "Calques de série visuelle", + "expressionXY.layeredXyVis.layers.help": "Calques de série visuelle", "expressionXY.xyVis.legend.help": "Configurez la légende du graphique.", "expressionXY.xyVis.logDatatable.breakDown": "Répartir par", "expressionXY.xyVis.logDatatable.metric": "Axe vertical", @@ -7279,7 +7356,6 @@ "xpack.apm.filter.environment.allLabel": "Tous", "xpack.apm.filter.environment.label": "Environnement", "xpack.apm.filter.environment.notDefinedLabel": "Non défini", - "xpack.apm.filter.environment.selectEnvironmentLabel": "Sélectionner l'environnement", "xpack.apm.fleet_integration.settings.advancedOptionsLavel": "Options avancées", "xpack.apm.fleet_integration.settings.agentAuthorization.anonymousAllowAgentHelpText": "Noms d'agents autorisés pour l'accès anonyme.", "xpack.apm.fleet_integration.settings.agentAuthorization.anonymousAllowAgentLabel": "Agents autorisés", @@ -7382,8 +7458,6 @@ "xpack.apm.fleetIntegration.apmAgent.editDisacoveryRule.type": "Type", "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.addRule": "Ajouter une règle", "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.autoAttachment": "Rattachement automatique", - "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.betaBadge.label": "BÊTA", - "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.betaBadge.tooltipContent": "Le rattachement automatique pour Java n'est pas disponible. Nous vous remercions de bien vouloir nous aider en nous signalant tout bug.", "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.discoveryRules": "Règles de découverte", "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.editRule.add": "Ajouter", "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.editRule.helpText": "Choisir parmi les paramètres autorisés", @@ -7506,7 +7580,6 @@ "xpack.apm.propertiesTable.tabs.metadataLabel": "Métadonnées", "xpack.apm.propertiesTable.tabs.timelineLabel": "Chronologie", "xpack.apm.searchInput.filter": "Filtrer…", - "xpack.apm.selectCustomOptionText": "Ajouter \\{searchValue\\} en tant que nouvelle option", "xpack.apm.selectPlaceholder": "Sélectionner une option :", "xpack.apm.serviceDependencies.breakdownChartTitle": "Temps consacré par dépendance", "xpack.apm.serviceDetails.dependenciesTabLabel": "Dépendances", @@ -7820,7 +7893,6 @@ "xpack.apm.settings.customLink.delete": "Supprimer", "xpack.apm.settings.customLink.delete.failed": "Impossible de supprimer le lien personnalisé", "xpack.apm.settings.customLink.delete.successed": "Lien personnalisé supprimé.", - "xpack.apm.settings.customLink.emptyPromptText": "Nous allons y remédier ! Vous pouvez ajouter des liens personnalisés au menu contextuel Actions à partir des détails de transaction de chaque service. Créez un lien utile vers le portail d'assistance de votre société, ou ouvrez un nouveau rapport de bug. Pour en savoir plus, consultez notre documentation.", "xpack.apm.settings.customLink.emptyPromptTitle": "Aucun lien trouvé.", "xpack.apm.settings.customLink.flyout.action.title": "Lien", "xpack.apm.settings.customLink.flyout.close": "Fermer", @@ -8160,7 +8232,6 @@ "xpack.apm.views.metrics.title": "Indicateurs", "xpack.apm.views.nodes.title": "JVM", "xpack.apm.views.overview.title": "Aperçu", - "xpack.apm.views.serviceGroups.breadcrumbLabel": "Services", "xpack.apm.views.serviceGroups.title": "Groupes de services", "xpack.apm.views.serviceInventory.title": "Services", "xpack.apm.views.serviceMap.title": "Carte des services", @@ -9642,7 +9713,6 @@ "xpack.cases.recentCases.viewAllCasesLink": "Afficher tous les cas", "xpack.cases.settings.syncAlertsSwitchLabelOff": "Arrêt", "xpack.cases.settings.syncAlertsSwitchLabelOn": "Marche", - "xpack.cases.status.all": "Tout", "xpack.cases.status.closed": "Fermé", "xpack.cases.status.iconAria": "Modifier le statut", "xpack.cases.status.inProgress": "En cours", @@ -10042,7 +10112,6 @@ "xpack.csp.passed": "Approuvé", "xpack.csp.posture_score": "Score du niveau", "xpack.csp.posture_score_trend": "Tendance du score du niveau", - "xpack.csp.resource_type": "Type de ressource", "xpack.csp.rules.activateAllButtonLabel": "Activer {count, plural, one {# règle} other {# règles}}", "xpack.csp.rules.bulkActionsButtonLabel": "Actions groupées", "xpack.csp.rules.cancelButtonLabel": "Annuler", @@ -10078,107 +10147,6 @@ "xpack.dashboard.drilldown.goToDashboard": "Accéder au tableau de bord", "xpack.dashboard.FlyoutCreateDrilldownAction.displayName": "Créer une recherche", "xpack.dashboard.panel.openFlyoutEditDrilldown.displayName": "Gérer les recherches", - "xpack.data.mgmt.searchSessions.actionDelete": "Supprimer", - "xpack.data.mgmt.searchSessions.actionExtend": "Étendre", - "xpack.data.mgmt.searchSessions.actionRename": "Modifier le nom", - "xpack.data.mgmt.searchSessions.actions.tooltip.moreActions": "Plus d'actions", - "xpack.data.mgmt.searchSessions.api.deleted": "La session de recherche a été supprimée.", - "xpack.data.mgmt.searchSessions.api.deletedError": "Impossible de supprimer la session de recherche !", - "xpack.data.mgmt.searchSessions.api.extended": "La session de recherche a été étendue.", - "xpack.data.mgmt.searchSessions.api.extendError": "Impossible d'étendre la session de recherche !", - "xpack.data.mgmt.searchSessions.api.fetchError": "Impossible d'actualiser la page !", - "xpack.data.mgmt.searchSessions.api.fetchTimeout": "La récupération des informations de la session de recherche a expiré après {timeout} secondes", - "xpack.data.mgmt.searchSessions.api.rename": "La session de recherche a été renommée", - "xpack.data.mgmt.searchSessions.api.renameError": "Impossible de renommer la session de recherche", - "xpack.data.mgmt.searchSessions.appTitle": "Sessions de recherche", - "xpack.data.mgmt.searchSessions.ariaLabel.moreActions": "Plus d'actions", - "xpack.data.mgmt.searchSessions.cancelModal.cancelButton": "Annuler", - "xpack.data.mgmt.searchSessions.cancelModal.deleteButton": "Supprimer", - "xpack.data.mgmt.searchSessions.cancelModal.message": "La suppression de la session de recherche \"{name}\" supprime tous les résultats mis en cache.", - "xpack.data.mgmt.searchSessions.cancelModal.title": "Supprimer la session de recherche", - "xpack.data.mgmt.searchSessions.extendModal.dontExtendButton": "Annuler", - "xpack.data.mgmt.searchSessions.extendModal.extendButton": "Étendre l'expiration", - "xpack.data.mgmt.searchSessions.extendModal.extendMessage": "L'expiration de la session de recherche \"{name}\" sera étendue jusqu'à {newExpires}.", - "xpack.data.mgmt.searchSessions.extendModal.title": "Étendre l'expiration de la session de recherche", - "xpack.data.mgmt.searchSessions.flyoutTitle": "Inspecter", - "xpack.data.mgmt.searchSessions.main.backgroundSessionsDocsLinkText": "Documentation", - "xpack.data.mgmt.searchSessions.main.sectionDescription": "Gérez vos sessions de recherche enregistrées.", - "xpack.data.mgmt.searchSessions.main.sectionTitle": "Sessions de recherche", - "xpack.data.mgmt.searchSessions.renameModal.cancelButton": "Annuler", - "xpack.data.mgmt.searchSessions.renameModal.renameButton": "Enregistrer", - "xpack.data.mgmt.searchSessions.renameModal.searchSessionNameInputLabel": "Nom de la session de recherche", - "xpack.data.mgmt.searchSessions.renameModal.title": "Modifier le nom de la session de recherche", - "xpack.data.mgmt.searchSessions.search.filterApp": "Application", - "xpack.data.mgmt.searchSessions.search.filterStatus": "Statut", - "xpack.data.mgmt.searchSessions.search.tools.refresh": "Actualiser", - "xpack.data.mgmt.searchSessions.status.expireDateUnknown": "inconnu", - "xpack.data.mgmt.searchSessions.status.expiresOn": "Expire le {expireDate}", - "xpack.data.mgmt.searchSessions.status.expiresSoonInDays": "Expire dans {numDays} jours", - "xpack.data.mgmt.searchSessions.status.expiresSoonInDaysTooltip": "{numDays} jours", - "xpack.data.mgmt.searchSessions.status.expiresSoonInHours": "Cette session expire dans {numHours} heures", - "xpack.data.mgmt.searchSessions.status.expiresSoonInHoursTooltip": "{numHours} heures", - "xpack.data.mgmt.searchSessions.status.label.cancelled": "Annulé", - "xpack.data.mgmt.searchSessions.status.label.complete": "Terminé", - "xpack.data.mgmt.searchSessions.status.label.error": "Erreur", - "xpack.data.mgmt.searchSessions.status.label.expired": "Expiré", - "xpack.data.mgmt.searchSessions.status.label.inProgress": "En cours", - "xpack.data.mgmt.searchSessions.status.message.cancelled": "Annulé par l'utilisateur", - "xpack.data.mgmt.searchSessions.status.message.createdOn": "Expire le {expireDate}", - "xpack.data.mgmt.searchSessions.status.message.error": "Erreur : {error}", - "xpack.data.mgmt.searchSessions.status.message.expiredOn": "Expiré le {expireDate}", - "xpack.data.mgmt.searchSessions.table.headerExpiration": "Expiration", - "xpack.data.mgmt.searchSessions.table.headerName": "Nom", - "xpack.data.mgmt.searchSessions.table.headerStarted": "Créé", - "xpack.data.mgmt.searchSessions.table.headerStatus": "Statut", - "xpack.data.mgmt.searchSessions.table.headerType": "Application", - "xpack.data.mgmt.searchSessions.table.mlAppName": "Machine Learning", - "xpack.data.mgmt.searchSessions.table.notRestorableWarning": "La session de recherche va être de nouveau exécutée. Vous pouvez ensuite l'enregistrer pour une utilisation ultérieure.", - "xpack.data.mgmt.searchSessions.table.numSearches": "# recherches", - "xpack.data.mgmt.searchSessions.table.versionIncompatibleWarning": "Cette session de recherche a été créée dans une instance Kibana exécutant une version différente. Il se peut qu'elle ne soit pas correctement restaurée.", - "xpack.data.search.statusError": "Recherche terminée avec un statut {errorCode}", - "xpack.data.search.statusThrow": "Le statut de la recherche a généré un statut d'erreur {message} ({errorCode})", - "xpack.data.searchSessionIndicator.cancelButtonText": "Arrêter la session", - "xpack.data.searchSessionIndicator.canceledDescriptionText": "Vous visualisez des données incomplètes", - "xpack.data.searchSessionIndicator.canceledIconAriaLabel": "La session de recherche s'est arrêtée", - "xpack.data.searchSessionIndicator.canceledTitleText": "La session de recherche s'est arrêtée", - "xpack.data.searchSessionIndicator.canceledTooltipText": "La session de recherche s'est arrêtée", - "xpack.data.searchSessionIndicator.canceledWhenText": "Arrêtée {when}", - "xpack.data.searchSessionIndicator.continueInBackgroundButtonText": "Enregistrer la session", - "xpack.data.searchSessionIndicator.disabledDueToDisabledGloballyMessage": "Vous ne disposez pas d'autorisations pour gérer les sessions de recherche", - "xpack.data.searchSessionIndicator.disabledDueToTimeoutMessage": "Les résultats de la session de recherche ont expiré.", - "xpack.data.searchSessionIndicator.loadingInTheBackgroundDescriptionText": "Vous pouvez retourner aux résultats terminés à partir de la page Gestion", - "xpack.data.searchSessionIndicator.loadingInTheBackgroundIconAriaLabel": "Session enregistrée en cours", - "xpack.data.searchSessionIndicator.loadingInTheBackgroundIconTooltipText": "Session enregistrée en cours", - "xpack.data.searchSessionIndicator.loadingInTheBackgroundTitleText": "Session enregistrée en cours", - "xpack.data.searchSessionIndicator.loadingInTheBackgroundWhenText": "Débuté {when}", - "xpack.data.searchSessionIndicator.loadingResultsDescription": "Enregistrer votre session, poursuivre votre travail et retourner aux résultats terminés", - "xpack.data.searchSessionIndicator.loadingResultsIconAriaLabel": "Chargement de la session de recherche", - "xpack.data.searchSessionIndicator.loadingResultsIconTooltipText": "Chargement de la session de recherche", - "xpack.data.searchSessionIndicator.loadingResultsTitle": "Votre recherche prend un certain temps…", - "xpack.data.searchSessionIndicator.loadingResultsWhenText": "Débuté {when}", - "xpack.data.searchSessionIndicator.restoredDescriptionText": "Vous affichez des données mises en cache d'une plage temporelle spécifique. La modification de la plage temporelle ou des filtres entraînera la réexécution de la session", - "xpack.data.searchSessionIndicator.restoredResultsIconAriaLabel": "Session enregistrée restaurée", - "xpack.data.searchSessionIndicator.restoredResultsTooltipText": "Session de recherche restaurée", - "xpack.data.searchSessionIndicator.restoredTitleText": "Session de recherche restaurée", - "xpack.data.searchSessionIndicator.restoredWhenText": "Terminé {when}", - "xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundDescriptionText": "Vous pouvez retourner à ces résultats à partir de la page de gestion", - "xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundIconAriaLabel": "Session enregistrée terminée", - "xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundIconTooltipText": "Session enregistrée terminée", - "xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundTitleText": "Session de recherche enregistrée", - "xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundWhenText": "Terminé {when}", - "xpack.data.searchSessionIndicator.resultsLoadedDescriptionText": "Enregistrer votre session pour y revenir ultérieurement", - "xpack.data.searchSessionIndicator.resultsLoadedIconAriaLabel": "Session de recherche terminée", - "xpack.data.searchSessionIndicator.resultsLoadedIconTooltipText": "Session de recherche terminée", - "xpack.data.searchSessionIndicator.resultsLoadedText": "Session de recherche terminée", - "xpack.data.searchSessionIndicator.resultsLoadedWhenText": "Terminé {when}", - "xpack.data.searchSessionIndicator.saveButtonText": "Enregistrer la session", - "xpack.data.searchSessionIndicator.viewSearchSessionsLinkText": "Gérer les sessions", - "xpack.data.searchSessionName.ariaLabelText": "Nom de la session de recherche", - "xpack.data.searchSessionName.editAriaLabelText": "Modifier le nom de la session de recherche", - "xpack.data.searchSessionName.placeholderText": "Entrer un nom pour la session de recherche", - "xpack.data.searchSessionName.saveButtonText": "Enregistrer", - "xpack.data.sessions.management.flyoutText": "Configuration de cette session de recherche", - "xpack.data.sessions.management.flyoutTitle": "Inspecter la session de recherche", "xpack.dataVisualizer.addCombinedFieldsLabel": "Ajouter un champ combiné", "xpack.dataVisualizer.choroplethMap.topValuesCount": "Compte des valeurs les plus élevées pour {fieldName}", "xpack.dataVisualizer.chrome.help.appName": "Data Visualizer (Visualiseur de données)", @@ -11813,7 +11781,6 @@ "xpack.enterpriseSearch.workplaceSearch.groups.newGroup.action": "Gérer le groupe", "xpack.enterpriseSearch.workplaceSearch.groups.newGroupSavedSuccess": "{groupName} créé avec succès", "xpack.enterpriseSearch.workplaceSearch.groups.noSourcesMessage": "Aucune source de contenu organisationnelle", - "xpack.enterpriseSearch.workplaceSearch.groups.noUsersMessage": "Aucun utilisateur", "xpack.enterpriseSearch.workplaceSearch.groups.overview.confirmRemoveButtonText": "Supprimer {name}", "xpack.enterpriseSearch.workplaceSearch.groups.overview.confirmRemoveDescription": "Votre groupe sera supprimé de Workplace Search. Voulez-vous vraiment supprimer {name} ?", "xpack.enterpriseSearch.workplaceSearch.groups.overview.confirmTitleText": "Confirmer", @@ -21659,12 +21626,6 @@ "xpack.observability.noDataConfig.beatsCard.title": "Ajouter des intégrations", "xpack.observability.noDataConfig.solutionName": "Observabilité", "xpack.observability.notAvailable": "S. O.", - "xpack.observability.overview.alert.allTypes": "Tous les types", - "xpack.observability.overview.alert.appLink": "Afficher toutes les alertes", - "xpack.observability.overview.alert.errorMessage": "Une erreur s’est produite lors du chargement des données d’alerte. Réessayez plus tard.", - "xpack.observability.overview.alert.errorTitle": "Impossible de charger les données d’alerte.", - "xpack.observability.overview.alerts.appLink": "Afficher les alertes", - "xpack.observability.overview.alerts.muted": "Muet", "xpack.observability.overview.alerts.title": "Alertes", "xpack.observability.overview.apm.appLink": "Afficher l’inventaire des services", "xpack.observability.overview.apm.services": "Services", @@ -21750,13 +21711,10 @@ "xpack.observability.rules.rulesTable.columns.statusTitle": "Statut", "xpack.observability.rules.rulesTable.pluralTitle": "règles", "xpack.observability.rules.rulesTable.ruleStatusActive": "Actif", - "xpack.observability.rules.rulesTable.ruleStatusDisabled": "Désactivé", - "xpack.observability.rules.rulesTable.ruleStatusEnabled": "Activé", "xpack.observability.rules.rulesTable.ruleStatusError": "Erreur", "xpack.observability.rules.rulesTable.ruleStatusLicenseError": "Erreur de licence", "xpack.observability.rules.rulesTable.ruleStatusOk": "Ok", "xpack.observability.rules.rulesTable.ruleStatusPending": "En attente", - "xpack.observability.rules.rulesTable.ruleStatusSnoozedIndefinitely": "Répété indéfiniment", "xpack.observability.rules.rulesTable.ruleStatusUnknown": "Inconnu", "xpack.observability.rules.rulesTable.ruleStatusWarning": "avertissement", "xpack.observability.rules.rulesTable.singleTitle": "règle", @@ -21972,9 +21930,6 @@ "xpack.osquery.liveQueryDetails.viewLiveQueriesHistoryTitle": "Afficher l'historique des recherches en direct", "xpack.osquery.liveQueryForm.form.saveForLaterButtonLabel": "Enregistrer pour plus tard", "xpack.osquery.liveQueryForm.form.submitButtonLabel": "Envoyer", - "xpack.osquery.liveQueryForm.steps.agentsStepHeading": "Sélectionner les agents", - "xpack.osquery.liveQueryForm.steps.queryStepHeading": "Entrer la recherche", - "xpack.osquery.liveQueryForm.steps.resultsStepHeading": "Vérifier les résultats", "xpack.osquery.liveQueryResults.table.agentColumnTitle": "agent", "xpack.osquery.liveQueryResults.table.fieldMappedLabel": "Le champ est mappé à", "xpack.osquery.newLiveQuery.pageTitle": "Nouvelle recherche en direct", @@ -22456,7 +22411,6 @@ "xpack.reporting.publicNotifier.csvContainsFormulas.formulaReportMessage": "Le rapport \"{reportObjectTitle}\" contient des caractères que les applications de feuilles de calcul peuvent considérer comme des formules.", "xpack.reporting.publicNotifier.csvContainsFormulas.formulaReportTitle": "Les rapports du type {reportType} peuvent contenir des formules.", "xpack.reporting.publicNotifier.downloadReportButtonLabel": "Télécharger le rapport", - "xpack.reporting.publicNotifier.error.calloutTitle": "La tâche de reporting a échoué", "xpack.reporting.publicNotifier.error.checkManagement": "Accédez à {path} pour plus d’informations.", "xpack.reporting.publicNotifier.error.couldNotCreateReportTitle": "Impossible de créer un rapport {reportType} pour \"{reportObjectTitle}\".", "xpack.reporting.publicNotifier.error.reportingSectionUrlLinkLabel": "Gestion de la Suite > Kibana > Reporting", @@ -23808,13 +23762,10 @@ "xpack.securitySolution.console.builtInCommands.clearAbout": "Purger la mémoire tampon de la console", "xpack.securitySolution.console.builtInCommands.helpAbout": "Afficher la liste des commandes disponibles", "xpack.securitySolution.console.commandList.footerText": "Pour plus d’informations sur les commandes ci-dessus, utilisez l’argument {helpOption}. Exemple : {cmdExample}", - "xpack.securitySolution.console.commandOutput.runInBackgroundButtonLabel": "Exécuter en arrière-plan", - "xpack.securitySolution.console.commandOutput.runInBackgroundMsg": "La réponse à la commande prend un peu de temps. Cliquez ici pour l’exécuter en arrière-plan et être averti lors de la réception de la réponse.", "xpack.securitySolution.console.commandUsage.atLeastOneOptionRequiredMessage": "Remarque : au moins une option doit être utilisée.", "xpack.securitySolution.console.commandUsage.inputUsage": "Utilisation :", "xpack.securitySolution.console.commandUsage.optionsLabel": "Options :", "xpack.securitySolution.console.commandValidation.argSupportedOnlyOnce": "cet argument ne peut être utilisé qu’une fois : {argName}.", - "xpack.securitySolution.console.commandValidation.cmdHelpTitle": "commande {cmdName}", "xpack.securitySolution.console.commandValidation.invalidArgValue": "valeur d’argument non valide : {argName}. {error}", "xpack.securitySolution.console.commandValidation.missingRequiredArg": "argument requis manquant : {argName}", "xpack.securitySolution.console.commandValidation.mustHaveArgs": "arguments requis manquants : {requiredArgs}", @@ -25879,7 +25830,6 @@ "xpack.securitySolution.hosts.hostScoreOverTime.riskScore": "Score de risque", "xpack.securitySolution.hosts.hostScoreOverTime.riskyLabel": "À risque", "xpack.securitySolution.hosts.hostScoreOverTime.riskyThresholdHeader": "Seuil du niveau À risque", - "xpack.securitySolution.hosts.hostScoreOverTime.title": "Score de risque de l'hôte sur la durée", "xpack.securitySolution.hosts.kqlPlaceholder": "par ex. hôte.nom : \"foo\"", "xpack.securitySolution.hosts.navigation.alertsTitle": "Alertes externes", "xpack.securitySolution.hosts.navigation.allHostsTitle": "Tous les hôtes", @@ -29268,7 +29218,6 @@ "xpack.triggersActionsUI.sections.rulesList.ruleStatusActive": "Actif", "xpack.triggersActionsUI.sections.rulesList.ruleStatusDropdownMenuLabel": "Modifier le statut de la règle ou répéter", "xpack.triggersActionsUI.sections.rulesList.ruleStatusError": "Erreur", - "xpack.triggersActionsUI.sections.rulesList.ruleStatusFilterLabel": "Dernière réponse", "xpack.triggersActionsUI.sections.rulesList.ruleStatusLicenseError": "Erreur de licence", "xpack.triggersActionsUI.sections.rulesList.ruleStatusOk": "Ok", "xpack.triggersActionsUI.sections.rulesList.ruleStatusPending": "En attente", @@ -31015,48 +30964,28 @@ "unifiedSearch.noDataPopover.title": "Ensemble de données vide", "unifiedSearch.query.queryBar.clearInputLabel": "Effacer l'entrée", "unifiedSearch.query.queryBar.comboboxAriaLabel": "Rechercher et filtrer la page {pageType}", - "unifiedSearch.query.queryBar.kqlFullLanguageName": "Langage de requête Kibana", "unifiedSearch.query.queryBar.kqlLanguageName": "KQL", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoDocLinkText": "documents", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoOptOutText": "Ne plus afficher", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoText": "Il semblerait que votre requête porte sur un champ imbriqué. Selon le résultat visé, il existe plusieurs façons de construire une syntaxe KQL pour des requêtes imbriquées. Apprenez-en plus avec notre {link}.", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoTitle": "Syntaxe de requête imbriquée KQL", - "unifiedSearch.query.queryBar.kqlOffLabel": "Désactivé", - "unifiedSearch.query.queryBar.kqlOnLabel": "Activé", - "unifiedSearch.query.queryBar.languageSwitcher.toText": "Passer au langage de requête Kibana pour la recherche", "unifiedSearch.query.queryBar.luceneLanguageName": "Lucene", "unifiedSearch.query.queryBar.searchInputAriaLabel": "Commencer à taper pour rechercher et filtrer la page {pageType}", - "unifiedSearch.query.queryBar.searchInputPlaceholder": "Recherche", - "unifiedSearch.query.queryBar.syntaxOptionsDescription": "{docsLink} (KQL) offre une syntaxe de requête simplifiée et la prise en charge des champs scriptés. KQL offre également une fonctionnalité de saisie semi-automatique. Si vous désactivez KQL, {nonKqlModeHelpText}.", - "unifiedSearch.query.queryBar.syntaxOptionsDescription.nonKqlModeHelpText": "Kibana utilise Lucene.", - "unifiedSearch.search.searchBar.savedQueryDescriptionLabelText": "Description", "unifiedSearch.search.searchBar.savedQueryDescriptionText": "Enregistrez le texte et les filtres de la requête que vous souhaitez réutiliser.", "unifiedSearch.search.searchBar.savedQueryForm.titleConflictText": "Ce nom est en conflit avec une requête enregistrée existante.", - "unifiedSearch.search.searchBar.savedQueryFormCancelButtonText": "Annuler", "unifiedSearch.search.searchBar.savedQueryFormSaveButtonText": "Enregistrer", - "unifiedSearch.search.searchBar.savedQueryFormTitle": "Enregistrer la requête", "unifiedSearch.search.searchBar.savedQueryIncludeFiltersLabelText": "Inclure les filtres", "unifiedSearch.search.searchBar.savedQueryIncludeTimeFilterLabelText": "Inclure le filtre temporel", "unifiedSearch.search.searchBar.savedQueryNameHelpText": "Un nom est requis. Le nom ne peut pas contenir d'espace vide au début ou à la fin. Le nom doit être unique.", "unifiedSearch.search.searchBar.savedQueryNameLabelText": "Nom", "unifiedSearch.search.searchBar.savedQueryNoSavedQueriesText": "Aucune requête enregistrée.", - "unifiedSearch.search.searchBar.savedQueryPopoverButtonText": "Voir les requêtes enregistrées", - "unifiedSearch.search.searchBar.savedQueryPopoverClearButtonAriaLabel": "Effacer la requête enregistrée en cours", - "unifiedSearch.search.searchBar.savedQueryPopoverClearButtonText": "Effacer", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionCancelButtonText": "Annuler", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionConfirmButtonText": "Supprimer", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionTitle": "Supprimer \"{savedQueryName}\" ?", - "unifiedSearch.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel": "Supprimer la requête enregistrée {savedQueryName}", "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel": "Enregistrer en tant que nouvelle requête enregistrée", "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonText": "Enregistrer en tant que nouvelle", - "unifiedSearch.search.searchBar.savedQueryPopoverSaveButtonAriaLabel": "Enregistrer une nouvelle requête enregistrée", - "unifiedSearch.search.searchBar.savedQueryPopoverSaveButtonText": "Enregistrer la requête en cours", "unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonAriaLabel": "Enregistrer les modifications apportées à {title}", "unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonText": "Enregistrer les modifications", - "unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemButtonAriaLabel": "Bouton de requête enregistrée {savedQueryName}", - "unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemDescriptionAriaLabel": "Description de {savedQueryName}", - "unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemSelectedButtonAriaLabel": "Bouton de requête enregistrée {savedQueryName} sélectionné. Appuyez pour effacer les modifications.", - "unifiedSearch.search.searchBar.savedQueryPopoverTitleText": "Requêtes enregistrées", "unifiedSearch.search.unableToGetSavedQueryToastTitle": "Impossible de charger la requête enregistrée {savedQueryId}", "unifiedSearch.query.queryBar.syntaxOptionsTitle": "Options de syntaxe", "unifiedSearch.filter.applyFilterActionTitle": "Appliquer le filtre à la vue en cours", @@ -31108,22 +31037,17 @@ "unifiedSearch.filter.filterEditor.rangeEndInputPlaceholder": "Fin de la plage", "unifiedSearch.filter.filterEditor.rangeInputLabel": "Plage", "unifiedSearch.filter.filterEditor.rangeStartInputPlaceholder": "Début de la plage", - "unifiedSearch.filter.filterEditor.saveButtonLabel": "Enregistrer", "unifiedSearch.filter.filterEditor.trueOptionLabel": "vrai", "unifiedSearch.filter.filterEditor.valueInputLabel": "Valeur", "unifiedSearch.filter.filterEditor.valueInputPlaceholder": "Saisir une valeur", "unifiedSearch.filter.filterEditor.valueSelectPlaceholder": "Sélectionner une valeur", "unifiedSearch.filter.filterEditor.valuesSelectLabel": "Valeurs", "unifiedSearch.filter.filterEditor.valuesSelectPlaceholder": "Sélectionner des valeurs", - "unifiedSearch.filter.options.changeAllFiltersButtonLabel": "Changer tous les filtres", - "unifiedSearch.filter.options.deleteAllFiltersButtonLabel": "Tout supprimer", "unifiedSearch.filter.options.disableAllFiltersButtonLabel": "Tout désactiver", "unifiedSearch.filter.options.enableAllFiltersButtonLabel": "Tout activer", - "unifiedSearch.filter.options.invertDisabledFiltersButtonLabel": "Inverser l’activation/désactivation", "unifiedSearch.filter.options.invertNegatedFiltersButtonLabel": "Inverser l'inclusion", "unifiedSearch.filter.options.pinAllFiltersButtonLabel": "Tout épingler", "unifiedSearch.filter.options.unpinAllFiltersButtonLabel": "Tout désépingler", - "unifiedSearch.filter.searchBar.changeAllFiltersTitle": "Changer tous les filtres", "unifiedSearch.kueryAutocomplete.andOperatorDescription": "Nécessite que {bothArguments} soient ''vrai''.", "unifiedSearch.kueryAutocomplete.andOperatorDescription.bothArgumentsText": "les deux arguments", "unifiedSearch.kueryAutocomplete.equalOperatorDescription": "{equals} une certaine valeur", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 5adf5a0561ebe..df42895ac2cbc 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -367,7 +367,6 @@ "xpack.lens.functions.counterRate.args.outputColumnNameHelpText": "結果のカウンターレートを格納する列の名前", "xpack.lens.functions.counterRate.help": "データテーブルの列のカウンターレートを計算します", "xpack.lens.functions.lastValue.missingSortField": "このデータビューには日付フィールドが含まれていません", - "xpack.lens.functions.mergeTables.help": "任意の数の Kibana 表を 1 つの表に結合し、インスペクターアダプター経由で公開するヘルパー", "xpack.lens.functions.renameColumns.help": "データベースの列の名前の変更をアシストします", "xpack.lens.functions.renameColumns.idMap.help": "キーが古い列 ID で値が対応する新しい列 ID となるように JSON エンコーディングされたオブジェクトです。他の列 ID はすべてのそのままです。", "xpack.lens.functions.timeScale.dateColumnMissingMessage": "指定した dateColumnId {columnId} は存在しません。", @@ -442,28 +441,18 @@ "xpack.lens.indexPattern.dateHistogram.autoAdvancedExplanation": "間隔は次のロジックに従います。", "xpack.lens.indexPattern.dateHistogram.autoBasicExplanation": "自動日付ヒストグラムは、間隔でデータフィールドをバケットに分割します。", "xpack.lens.indexPattern.dateHistogram.autoBoundHeader": "対象間隔の測定", - "xpack.lens.indexPattern.dateHistogram.autoHelpText": "仕組み", - "xpack.lens.indexPattern.dateHistogram.autoInterval": "時間範囲のカスタマイズ", "xpack.lens.indexPattern.dateHistogram.autoIntervalHeader": "使用される間隔", "xpack.lens.indexPattern.dateHistogram.autoLongerExplanation": "間隔を選択するには、Lensは指定された時間範囲を{targetBarSetting}設定で除算します。Lensはデータに最適な間隔を計算します。たとえば、30分、1時間、12です。棒の最大数は{maxBarSetting}値で設定します。", "xpack.lens.indexPattern.dateHistogram.bindToGlobalTimePicker": "グローバル時刻ピッカーにバインド", - "xpack.lens.indexPattern.dateHistogram.days": "日", "xpack.lens.indexPattern.dateHistogram.dropPartialBuckets": "不完全なバケットをドロップ", "xpack.lens.indexPattern.dateHistogram.dropPartialBucketsHelp": "不完全なバケットのドロップは無効です。これらは、右上のグローバル時刻ピッカーにバインドされた時刻フィールドでのみ計算できます。", "xpack.lens.indexPattern.dateHistogram.globalTimePickerHelp": "右上のグローバル時刻ピッカーで選択したフィールドをフィルタリングします。現在のデータビューのデフォルト時刻フィールドではこの設定をオンにできません。", - "xpack.lens.indexPattern.dateHistogram.hours": "時間", "xpack.lens.indexPattern.dateHistogram.includeEmptyRows": "空の行を含める", - "xpack.lens.indexPattern.dateHistogram.milliseconds": "ミリ秒", "xpack.lens.indexPattern.dateHistogram.minimumInterval": "最低間隔", - "xpack.lens.indexPattern.dateHistogram.minutes": "分", - "xpack.lens.indexPattern.dateHistogram.month": "月", "xpack.lens.indexPattern.dateHistogram.moreThanYear": "1年を超える", "xpack.lens.indexPattern.dateHistogram.restrictedInterval": "集約の制限により間隔は {intervalValue} に固定されています。", - "xpack.lens.indexPattern.dateHistogram.seconds": "秒", "xpack.lens.indexPattern.dateHistogram.titleHelp": "自動日付ヒストグラムの仕組み", "xpack.lens.indexPattern.dateHistogram.upTo": "最大", - "xpack.lens.indexPattern.dateHistogram.week": "週", - "xpack.lens.indexPattern.dateHistogram.year": "年", "xpack.lens.indexPattern.dateHistogramTimeShift": "単一のレイヤーでは、前の時間範囲シフトと日付ヒストグラムを結合できません。\"{column}\"で明示的な時間シフト期間を使用するか、日付ヒストグラムを置換してください。", "xpack.lens.indexPattern.decimalPlacesLabel": "小数点以下", "xpack.lens.indexPattern.defaultFormatLabel": "デフォルト", @@ -540,7 +529,6 @@ "xpack.lens.indexPattern.incompleteOperation": "(未完了)", "xpack.lens.indexPattern.intervals": "間隔", "xpack.lens.indexPattern.invalidFieldLabel": "無効なフィールドです。データビューを確認するか、別のフィールドを選択してください。", - "xpack.lens.indexPattern.invalidInterval": "無効な間隔値", "xpack.lens.indexPattern.invalidOperationLabel": "選択した関数はこのフィールドで動作しません。", "xpack.lens.indexPattern.invalidReferenceConfiguration": "ディメンション\"{dimensionLabel}\"の構成が正しくありません", "xpack.lens.indexPattern.invalidTimeShift": "無効な時間シフトです。正の整数の後に単位s、m、h、d、w、M、yのいずれかを入力します。例:3時間は3hです", @@ -575,7 +563,6 @@ "xpack.lens.indexPattern.moving_average.signature": "メトリック:数値、[window]:数値", "xpack.lens.indexPattern.movingAverage": "移動平均", "xpack.lens.indexPattern.movingAverage.basicExplanation": "移動平均はデータ全体でウィンドウをスライドし、平均値を表示します。移動平均は日付ヒストグラムでのみサポートされています。", - "xpack.lens.indexPattern.movingAverage.helpText": "仕組み", "xpack.lens.indexPattern.movingAverage.limitations": "最初の移動平均値は2番目の項目から開始します。", "xpack.lens.indexPattern.movingAverage.longerExplanation": "移動平均を計算するには、Lensはウィンドウの平均値を使用し、ギャップのスキップポリシーを適用します。 見つからない値がある場合、バケットがスキップされます。次の値に対して計算が実行されます。", "xpack.lens.indexPattern.movingAverage.tableExplanation": "たとえば、データ[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]がある場合、ウィンドウサイズ5でシンプルな移動平均を計算できます。", @@ -713,13 +700,10 @@ "xpack.lens.indexPattern.timeShiftSmallWarning": "{label}は{columnTimeShift}の時間シフトを使用しています。これは{interval}の日付ヒストグラム間隔よりも小さいです。不一致のデータを防止するには、時間シフトとして{interval}を使用します。", "xpack.lens.indexPattern.uniqueLabel": "{label} [{num}]", "xpack.lens.indexPattern.useAsTopLevelAgg": "最初にこのフィールドでグループ化", - "xpack.lens.indexPatterns.actionsPopoverLabel": "データビュー設定", - "xpack.lens.indexPatterns.addFieldButton": "フィールドをデータビューに追加", "xpack.lens.indexPatterns.clearFiltersLabel": "名前とタイプフィルターを消去", "xpack.lens.indexPatterns.fieldFiltersLabel": "タイプでフィルタリング", "xpack.lens.indexPatterns.fieldSearchLiveRegion": "{availableFields}使用可能な{availableFields, plural, other {フィールド}}。{emptyFields}空の{emptyFields, plural, other {フィールド}}。 {metaFields}メタ{metaFields, plural, other {フィールド}}。", "xpack.lens.indexPatterns.filterByNameLabel": "検索フィールド名", - "xpack.lens.indexPatterns.manageFieldButton": "データビューフィールドを管理", "xpack.lens.indexPatterns.noAvailableDataLabel": "データを含むフィールドはありません。", "xpack.lens.indexPatterns.noDataLabel": "フィールドがありません。", "xpack.lens.indexPatterns.noEmptyDataLabel": "空のフィールドがありません。", @@ -1012,7 +996,6 @@ "xpack.lens.xyChart.verticalRightAxisLabel": "縦右軸", "xpack.lens.xySuggestions.asPercentageTitle": "割合(%)", "xpack.lens.xySuggestions.barChartTitle": "棒グラフ", - "xpack.lens.xySuggestions.dateSuggestion": "{xTitle}の上の {yTitle}", "xpack.lens.xySuggestions.emptyAxisTitle": "(空)", "xpack.lens.xySuggestions.flipTitle": "反転", "xpack.lens.xySuggestions.lineChartTitle": "折れ線グラフ", @@ -2715,10 +2698,110 @@ "data.searchSessions.sessionService.sessionObjectFetchError": "検索セッション情報を取得できませんでした", "data.triggers.applyFilterDescription": "Kibanaフィルターが適用されるとき。単一の値または範囲フィルターにすることができます。", "data.triggers.applyFilterTitle": "フィルターを適用", - "dataViews.deprecations.scriptedFields.manualStepOneMessage": "[スタック管理]>[Kibana]>[データビュー]に移動します。", - "dataViews.deprecations.scriptedFields.manualStepTwoMessage": "ランタイムフィールドを使用するには、スクリプト化されたフィールドがある{numberOfIndexPatternsWithScriptedFields}データビューを更新します。ほとんどの場合、既存のスクリプトを移行するには、「return ;」から「emit();」に変更する必要があります。1つ以上のスクリプト化されたフィールドがあるデータビュー:{allTitles}", - "dataViews.deprecations.scriptedFieldsMessage": "スクリプト化されたフィールドを使用する{numberOfIndexPatternsWithScriptedFields}データビュー({titlesPreview}...)があります。スクリプト化されたフィールドは廃止予定であり、今後は削除されます。ランタイムフィールドを使用してください。", - "dataViews.deprecations.scriptedFieldsTitle": "スクリプト化されたフィールドを使用しているデータビューが見つかりました。", + "data.mgmt.searchSessions.actionDelete": "削除", + "data.mgmt.searchSessions.actionExtend": "延長", + "data.mgmt.searchSessions.actionRename": "名前を編集", + "data.mgmt.searchSessions.actions.tooltip.moreActions": "さらにアクションを表示", + "data.mgmt.searchSessions.api.deleted": "検索セッションが削除されました。", + "data.mgmt.searchSessions.api.deletedError": "検索セッションを削除できませんでした。", + "data.mgmt.searchSessions.api.extended": "検索セッションが延長されました。", + "data.mgmt.searchSessions.api.extendError": "検索セッションを延長できませんでした。", + "data.mgmt.searchSessions.api.fetchError": "ページを更新できませんでした。", + "data.mgmt.searchSessions.api.fetchTimeout": "{timeout}秒後に検索セッション情報の取得がタイムアウトしました", + "data.mgmt.searchSessions.api.rename": "検索セッション名が変更されました", + "data.mgmt.searchSessions.api.renameError": "検索セッション名を変更できませんでした", + "data.mgmt.searchSessions.appTitle": "検索セッション", + "data.mgmt.searchSessions.ariaLabel.moreActions": "さらにアクションを表示", + "data.mgmt.searchSessions.cancelModal.cancelButton": "キャンセル", + "data.mgmt.searchSessions.cancelModal.deleteButton": "削除", + "data.mgmt.searchSessions.cancelModal.message": "検索セッション'{name}'を削除すると、キャッシュに保存されているすべての結果が削除されます。", + "data.mgmt.searchSessions.cancelModal.title": "検索セッションの削除", + "data.mgmt.searchSessions.extendModal.dontExtendButton": "キャンセル", + "data.mgmt.searchSessions.extendModal.extendButton": "有効期限を延長", + "data.mgmt.searchSessions.extendModal.extendMessage": "検索セッション'{name}'の有効期限が{newExpires}まで延長されます。", + "data.mgmt.searchSessions.extendModal.title": "検索セッションの有効期限を延長", + "data.mgmt.searchSessions.flyoutTitle": "検査", + "data.mgmt.searchSessions.main.backgroundSessionsDocsLinkText": "ドキュメント", + "data.mgmt.searchSessions.main.sectionDescription": "保存された検索セッションを管理します。", + "data.mgmt.searchSessions.main.sectionTitle": "検索セッション", + "data.mgmt.searchSessions.renameModal.cancelButton": "キャンセル", + "data.mgmt.searchSessions.renameModal.renameButton": "保存", + "data.mgmt.searchSessions.renameModal.searchSessionNameInputLabel": "検索セッション名", + "data.mgmt.searchSessions.renameModal.title": "検索セッション名を編集", + "data.mgmt.searchSessions.search.filterApp": "アプリ", + "data.mgmt.searchSessions.search.filterStatus": "ステータス", + "data.mgmt.searchSessions.search.tools.refresh": "更新", + "data.mgmt.searchSessions.status.expireDateUnknown": "不明", + "data.mgmt.searchSessions.status.expiresOn": "有効期限:{expireDate}", + "data.mgmt.searchSessions.status.expiresSoonInDays": "{numDays}日後に期限切れ", + "data.mgmt.searchSessions.status.expiresSoonInDaysTooltip": "{numDays}日", + "data.mgmt.searchSessions.status.expiresSoonInHours": "このセッションは{numHours}時間後に期限切れになります", + "data.mgmt.searchSessions.status.expiresSoonInHoursTooltip": "{numHours}時間", + "data.mgmt.searchSessions.status.label.cancelled": "キャンセル済み", + "data.mgmt.searchSessions.status.label.complete": "完了", + "data.mgmt.searchSessions.status.label.error": "エラー", + "data.mgmt.searchSessions.status.label.expired": "期限切れ", + "data.mgmt.searchSessions.status.label.inProgress": "進行中", + "data.mgmt.searchSessions.status.message.cancelled": "ユーザーがキャンセル", + "data.mgmt.searchSessions.status.message.createdOn": "有効期限:{expireDate}", + "data.mgmt.searchSessions.status.message.error": "エラー:{error}", + "data.mgmt.searchSessions.status.message.expiredOn": "有効期限:{expireDate}", + "data.mgmt.searchSessions.table.headerExpiration": "有効期限", + "data.mgmt.searchSessions.table.headerName": "名前", + "data.mgmt.searchSessions.table.headerStarted": "作成済み", + "data.mgmt.searchSessions.table.headerStatus": "ステータス", + "data.mgmt.searchSessions.table.headerType": "アプリ", + "data.mgmt.searchSessions.table.notRestorableWarning": "検索セッションはもう一度実行されます。今後使用するために保存できます。", + "data.mgmt.searchSessions.table.numSearches": "# 検索", + "data.mgmt.searchSessions.table.versionIncompatibleWarning": "この検索は別のバージョンを実行しているKibanaインスタンスで作成されました。正常に復元されない可能性があります。", + "data.search.statusError": "検索は{errorCode}ステータスで完了しました", + "data.search.statusThrow": "検索ステータスはエラー{message}({errorCode})ステータスを返しました", + "data.searchSessionIndicator.cancelButtonText": "セッションの停止", + "data.searchSessionIndicator.canceledDescriptionText": "不完全なデータを表示しています。", + "data.searchSessionIndicator.canceledIconAriaLabel": "検索セッションが停止しました", + "data.searchSessionIndicator.canceledTitleText": "検索セッションが停止しました", + "data.searchSessionIndicator.canceledTooltipText": "検索セッションが停止しました", + "data.searchSessionIndicator.canceledWhenText": "停止:{when}", + "data.searchSessionIndicator.continueInBackgroundButtonText": "セッションの保存", + "data.searchSessionIndicator.disabledDueToDisabledGloballyMessage": "検索セッションを管理するアクセス権がありません", + "data.searchSessionIndicator.disabledDueToTimeoutMessage": "検索セッション結果が期限切れです。", + "data.searchSessionIndicator.loadingInTheBackgroundDescriptionText": "管理から完了した結果に戻ることができます。", + "data.searchSessionIndicator.loadingInTheBackgroundIconAriaLabel": "保存されたセッションを実行中です", + "data.searchSessionIndicator.loadingInTheBackgroundIconTooltipText": "保存されたセッションを実行中です", + "data.searchSessionIndicator.loadingInTheBackgroundTitleText": "保存されたセッションを実行中です", + "data.searchSessionIndicator.loadingInTheBackgroundWhenText": "開始:{when}", + "data.searchSessionIndicator.loadingResultsDescription": "セッションを保存して作業を続け、完了した結果に戻ってください。", + "data.searchSessionIndicator.loadingResultsIconAriaLabel": "検索セッションを読み込んでいます", + "data.searchSessionIndicator.loadingResultsIconTooltipText": "検索セッションを読み込んでいます", + "data.searchSessionIndicator.loadingResultsTitle": "検索に少し時間がかかっています...", + "data.searchSessionIndicator.loadingResultsWhenText": "開始:{when}", + "data.searchSessionIndicator.restoredDescriptionText": "特定の時間範囲からキャッシュに保存されたデータを表示しています。時間範囲またはフィルターを変更すると、セッションが再実行されます。", + "data.searchSessionIndicator.restoredResultsIconAriaLabel": "保存されたセッションが復元されました", + "data.searchSessionIndicator.restoredResultsTooltipText": "検索セッションが復元されました", + "data.searchSessionIndicator.restoredTitleText": "検索セッションが復元されました", + "data.searchSessionIndicator.restoredWhenText": "完了:{when}", + "data.searchSessionIndicator.resultLoadedInTheBackgroundDescriptionText": "管理からこれらの結果に戻ることができます。", + "data.searchSessionIndicator.resultLoadedInTheBackgroundIconAriaLabel": "保存されたセッションが完了しました", + "data.searchSessionIndicator.resultLoadedInTheBackgroundIconTooltipText": "保存されたセッションが完了しました", + "data.searchSessionIndicator.resultLoadedInTheBackgroundTitleText": "検索セッションが保存されました", + "data.searchSessionIndicator.resultLoadedInTheBackgroundWhenText": "完了:{when}", + "data.searchSessionIndicator.resultsLoadedDescriptionText": "セッションを保存して、後から戻ります。", + "data.searchSessionIndicator.resultsLoadedIconAriaLabel": "検索セッションが完了しました", + "data.searchSessionIndicator.resultsLoadedIconTooltipText": "検索セッションが完了しました", + "data.searchSessionIndicator.resultsLoadedText": "検索セッションが完了しました", + "data.searchSessionIndicator.resultsLoadedWhenText": "完了:{when}", + "data.searchSessionIndicator.saveButtonText": "セッションの保存", + "data.searchSessionIndicator.viewSearchSessionsLinkText": "セッションの管理", + "data.searchSessionName.ariaLabelText": "検索セッション名", + "data.searchSessionName.editAriaLabelText": "検索セッション名を編集", + "data.searchSessionName.placeholderText": "検索セッションの名前を入力", + "data.searchSessionName.saveButtonText": "保存", + "data.sessions.management.flyoutText": "この検索セッションの構成", + "data.sessions.management.flyoutTitle": "検索セッションの検査", + "dataViews.deprecations.scriptedFields.manualStepOneMessage": "[スタック管理]>[Kibana]>[インデックスパターン]に移動します。", + "dataViews.deprecations.scriptedFields.manualStepTwoMessage": "ランタイムフィールドを使用するには、スクリプト化されたフィールドがある{numberOfIndexPatternsWithScriptedFields}インデックスパターンを更新します。ほとんどの場合、既存のスクリプトを移行するには、「return ;」から「emit();」に変更する必要があります。1つ以上のスクリプト化されたフィールドがあるインデックスパターン:{allTitles}", + "dataViews.deprecations.scriptedFieldsMessage": "スクリプト化されたフィールドを使用する{numberOfIndexPatternsWithScriptedFields}インデックスパターン({titlesPreview}...)があります。スクリプト化されたフィールドは廃止予定であり、今後は削除されます。ランタイムフィールドを使用してください。", + "dataViews.deprecations.scriptedFieldsTitle": "スクリプト化されたフィールドを使用しているインデックスパターンが見つかりました", "dataViews.ensureDefaultIndexPattern.bannerLabel": "Kibanaでデータの可視化と閲覧を行うには、Elasticsearchからデータを取得するためのインデックスパターンの作成が必要です。", "dataViews.fetchFieldErrorTitle": "データビューのフィールド取得中にエラーが発生 {title}(ID:{id})", "dataViews.functions.dataViewLoad.help": "データビューを読み込みます", @@ -2886,7 +2969,6 @@ "discover.field.mappingConflict": "このフィールドは、このパターンと一致するインデックス全体に対して複数の型(文字列、整数など)として定義されています。この競合フィールドを使用することはできますが、Kibana で型を認識する必要がある関数では使用できません。この問題を修正するにはデータのレンダリングが必要です。", "discover.field.mappingConflict.title": "マッピングの矛盾", "discover.field.title": "{fieldName} ({fieldDisplayName})", - "discover.fieldChooser.dataViews.createNewDataView": "新しいデータビューを作成", "discover.fieldChooser.detailViews.emptyStringText": "空の文字列", "discover.fieldChooser.detailViews.existsInRecordsText": "{value} / {totalValue}件のレコードに存在", "discover.fieldChooser.detailViews.filterOutValueButtonAriaLabel": "{field}を除外:\"{value}\"", @@ -2923,10 +3005,6 @@ "discover.fieldChooser.filter.toggleButton.no": "いいえ", "discover.fieldChooser.filter.toggleButton.yes": "はい", "discover.fieldChooser.filter.typeLabel": "型", - "discover.fieldChooser.indexPattern.changeDataViewTitle": "データビューを変更", - "discover.fieldChooser.indexPatterns.actionsPopoverLabel": "データビュー設定", - "discover.fieldChooser.indexPatterns.addFieldButton": "フィールドの追加", - "discover.fieldChooser.indexPatterns.manageFieldButton": "設定の管理", "discover.fieldChooser.searchPlaceHolder": "検索フィールド名", "discover.fieldChooser.toggleFieldFilterButtonHideAriaLabel": "フィールド設定を非表示", "discover.fieldChooser.toggleFieldFilterButtonShowAriaLabel": "フィールド設定を表示", @@ -3463,11 +3541,11 @@ "expressionXY.axisTitlesVisibilityConfig.yLeft.help": "左y軸のタイトルを表示するかどうかを指定します。", "expressionXY.axisTitlesVisibilityConfig.yRight.help": "右y軸のタイトルを表示するかどうかを指定します。", "expressionXY.dataLayer.accessors.help": "y軸に表示する列。", - "expressionXY.dataLayer.columnToLabel.help": "ラベリングする列IDのJSONキー値のペア", + "expressionXY.layer.columnToLabel.help": "ラベリングする列IDのJSONキー値のペア", "expressionXY.dataLayer.help": "xyグラフでレイヤーを構成", "expressionXY.dataLayer.hide.help": "軸を表示/非表示", "expressionXY.dataLayer.isHistogram.help": "グラフをヒストグラムとしてレイアウトするかどうか", - "expressionXY.dataLayer.layerId.help": "レイヤーID", + "expressionXY.layers.layerId.help": "レイヤーID", "expressionXY.dataLayer.palette.help": "パレット", "expressionXY.dataLayer.seriesType.help": "表示するグラフのタイプ。", "expressionXY.dataLayer.splitAccessor.help": "分割の基準となる列", @@ -3498,9 +3576,7 @@ "expressionXY.legendConfig.showSingleSeries.help": "エントリが1件の凡例を表示するかどうかを指定します", "expressionXY.legendConfig.verticalAlignment.help": "凡例がグラフ内に表示されるときに凡例の縦の配置を指定します。", "expressionXY.referenceLineLayer.accessors.help": "y軸に表示する列。", - "expressionXY.referenceLineLayer.columnToLabel.help": "ラベリングする列IDのJSONキー値のペア", "expressionXY.referenceLineLayer.help": "xyグラフで基準線を構成", - "expressionXY.referenceLineLayer.layerId.help": "レイヤーID", "expressionXY.referenceLineLayer.yConfig.help": "y軸の詳細構成", "expressionXY.tickLabelsConfig.help": "xyグラフのティックラベルの表示を構成", "expressionXY.tickLabelsConfig.x.help": "x軸の目盛ラベルを表示するかどうかを指定します。", @@ -3531,7 +3607,7 @@ "expressionXY.xyVis.help": "X/Y チャート", "expressionXY.xyVis.hideEndzones.help": "部分データの終了ゾーンマーカーを非表示", "expressionXY.xyVis.labelsOrientation.help": "軸ラベルの回転を定義します", - "expressionXY.xyVis.layers.help": "視覚的な系列のレイヤー", + "expressionXY.layeredXyVis.layers.help": "視覚的な系列のレイヤー", "expressionXY.xyVis.legend.help": "チャートの凡例を構成します。", "expressionXY.xyVis.logDatatable.breakDown": "内訳の基準", "expressionXY.xyVis.logDatatable.metric": "縦軸", @@ -7379,7 +7455,6 @@ "xpack.apm.filter.environment.allLabel": "すべて", "xpack.apm.filter.environment.label": "環境", "xpack.apm.filter.environment.notDefinedLabel": "未定義", - "xpack.apm.filter.environment.selectEnvironmentLabel": "環境を選択", "xpack.apm.fleet_integration.settings.advancedOptionsLavel": "高度なオプション", "xpack.apm.fleet_integration.settings.agentAuthorization.anonymousAllowAgentHelpText": "匿名アクセスの許可されたエージェント名。", "xpack.apm.fleet_integration.settings.agentAuthorization.anonymousAllowAgentLabel": "許可されたエージェント", @@ -7482,8 +7557,6 @@ "xpack.apm.fleetIntegration.apmAgent.editDisacoveryRule.type": "型", "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.addRule": "ルールを追加", "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.autoAttachment": "自動接続", - "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.betaBadge.label": "BETA", - "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.betaBadge.tooltipContent": "Javaの自動接続は一般公開されていません。不具合が発生したら報告してください。", "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.discoveryRules": "検出ルール", "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.editRule.add": "追加", "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.editRule.helpText": "許可されたパラメーターから選択", @@ -7604,7 +7677,6 @@ "xpack.apm.propertiesTable.tabs.metadataLabel": "メタデータ", "xpack.apm.propertiesTable.tabs.timelineLabel": "Timeline", "xpack.apm.searchInput.filter": "フィルター...", - "xpack.apm.selectCustomOptionText": "\\{searchValue\\}を新しいオプションとして追加", "xpack.apm.selectPlaceholder": "オプションを選択:", "xpack.apm.serviceDependencies.breakdownChartTitle": "依存関係にかかった時間", "xpack.apm.serviceDetails.dependenciesTabLabel": "依存関係", @@ -7918,7 +7990,6 @@ "xpack.apm.settings.customLink.delete": "削除", "xpack.apm.settings.customLink.delete.failed": "カスタムリンクを削除できませんでした", "xpack.apm.settings.customLink.delete.successed": "カスタムリンクを削除しました。", - "xpack.apm.settings.customLink.emptyPromptText": "変更しましょう。サービスごとのトランザクションの詳細でアクションコンテキストメニューにカスタムリンクを追加できます。自社のサポートポータルへの役立つリンクを作成するか、新しい不具合レポートを発行します。詳細はドキュメントをご覧ください。", "xpack.apm.settings.customLink.emptyPromptTitle": "リンクが見つかりません。", "xpack.apm.settings.customLink.flyout.action.title": "リンク", "xpack.apm.settings.customLink.flyout.close": "閉じる", @@ -8257,7 +8328,6 @@ "xpack.apm.views.metrics.title": "メトリック", "xpack.apm.views.nodes.title": "JVM", "xpack.apm.views.overview.title": "概要", - "xpack.apm.views.serviceGroups.breadcrumbLabel": "サービス", "xpack.apm.views.serviceGroups.title": "サービスグループ", "xpack.apm.views.serviceInventory.title": "サービス", "xpack.apm.views.serviceMap.title": "サービスマップ", @@ -9739,7 +9809,6 @@ "xpack.cases.recentCases.viewAllCasesLink": "すべてのケースを表示", "xpack.cases.settings.syncAlertsSwitchLabelOff": "オフ", "xpack.cases.settings.syncAlertsSwitchLabelOn": "オン", - "xpack.cases.status.all": "すべて", "xpack.cases.status.closed": "終了", "xpack.cases.status.iconAria": "ステータスの変更", "xpack.cases.status.inProgress": "進行中", @@ -10139,7 +10208,6 @@ "xpack.csp.passed": "合格", "xpack.csp.posture_score": "態勢スコア", "xpack.csp.posture_score_trend": "態勢スコア傾向", - "xpack.csp.resource_type": "リソースタイプ", "xpack.csp.rules.activateAllButtonLabel": "{count, plural, other {#個のルール}}をアクティブ化", "xpack.csp.rules.activatedLabel": "有効化", "xpack.csp.rules.activateLabel": "有効化", @@ -10190,107 +10258,6 @@ "xpack.dashboard.drilldown.goToDashboard": "ダッシュボードに移動", "xpack.dashboard.FlyoutCreateDrilldownAction.displayName": "ドリルダウンを作成", "xpack.dashboard.panel.openFlyoutEditDrilldown.displayName": "ドリルダウンを管理", - "xpack.data.mgmt.searchSessions.actionDelete": "削除", - "xpack.data.mgmt.searchSessions.actionExtend": "延長", - "xpack.data.mgmt.searchSessions.actionRename": "名前を編集", - "xpack.data.mgmt.searchSessions.actions.tooltip.moreActions": "さらにアクションを表示", - "xpack.data.mgmt.searchSessions.api.deleted": "検索セッションが削除されました。", - "xpack.data.mgmt.searchSessions.api.deletedError": "検索セッションを削除できませんでした。", - "xpack.data.mgmt.searchSessions.api.extended": "検索セッションが延長されました。", - "xpack.data.mgmt.searchSessions.api.extendError": "検索セッションを延長できませんでした。", - "xpack.data.mgmt.searchSessions.api.fetchError": "ページを更新できませんでした。", - "xpack.data.mgmt.searchSessions.api.fetchTimeout": "{timeout}秒後に検索セッション情報の取得がタイムアウトしました", - "xpack.data.mgmt.searchSessions.api.rename": "検索セッション名が変更されました", - "xpack.data.mgmt.searchSessions.api.renameError": "検索セッション名を変更できませんでした", - "xpack.data.mgmt.searchSessions.appTitle": "検索セッション", - "xpack.data.mgmt.searchSessions.ariaLabel.moreActions": "さらにアクションを表示", - "xpack.data.mgmt.searchSessions.cancelModal.cancelButton": "キャンセル", - "xpack.data.mgmt.searchSessions.cancelModal.deleteButton": "削除", - "xpack.data.mgmt.searchSessions.cancelModal.message": "検索セッション'{name}'を削除すると、キャッシュに保存されているすべての結果が削除されます。", - "xpack.data.mgmt.searchSessions.cancelModal.title": "検索セッションの削除", - "xpack.data.mgmt.searchSessions.extendModal.dontExtendButton": "キャンセル", - "xpack.data.mgmt.searchSessions.extendModal.extendButton": "有効期限を延長", - "xpack.data.mgmt.searchSessions.extendModal.extendMessage": "検索セッション'{name}'の有効期限が{newExpires}まで延長されます。", - "xpack.data.mgmt.searchSessions.extendModal.title": "検索セッションの有効期限を延長", - "xpack.data.mgmt.searchSessions.flyoutTitle": "検査", - "xpack.data.mgmt.searchSessions.main.backgroundSessionsDocsLinkText": "ドキュメント", - "xpack.data.mgmt.searchSessions.main.sectionDescription": "保存された検索セッションを管理します。", - "xpack.data.mgmt.searchSessions.main.sectionTitle": "検索セッション", - "xpack.data.mgmt.searchSessions.renameModal.cancelButton": "キャンセル", - "xpack.data.mgmt.searchSessions.renameModal.renameButton": "保存", - "xpack.data.mgmt.searchSessions.renameModal.searchSessionNameInputLabel": "検索セッション名", - "xpack.data.mgmt.searchSessions.renameModal.title": "検索セッション名を編集", - "xpack.data.mgmt.searchSessions.search.filterApp": "アプリ", - "xpack.data.mgmt.searchSessions.search.filterStatus": "ステータス", - "xpack.data.mgmt.searchSessions.search.tools.refresh": "更新", - "xpack.data.mgmt.searchSessions.status.expireDateUnknown": "不明", - "xpack.data.mgmt.searchSessions.status.expiresOn": "有効期限:{expireDate}", - "xpack.data.mgmt.searchSessions.status.expiresSoonInDays": "{numDays}日後に期限切れ", - "xpack.data.mgmt.searchSessions.status.expiresSoonInDaysTooltip": "{numDays}日", - "xpack.data.mgmt.searchSessions.status.expiresSoonInHours": "このセッションは{numHours}時間後に期限切れになります", - "xpack.data.mgmt.searchSessions.status.expiresSoonInHoursTooltip": "{numHours}時間", - "xpack.data.mgmt.searchSessions.status.label.cancelled": "キャンセル済み", - "xpack.data.mgmt.searchSessions.status.label.complete": "完了", - "xpack.data.mgmt.searchSessions.status.label.error": "エラー", - "xpack.data.mgmt.searchSessions.status.label.expired": "期限切れ", - "xpack.data.mgmt.searchSessions.status.label.inProgress": "進行中", - "xpack.data.mgmt.searchSessions.status.message.cancelled": "ユーザーがキャンセル", - "xpack.data.mgmt.searchSessions.status.message.createdOn": "有効期限:{expireDate}", - "xpack.data.mgmt.searchSessions.status.message.error": "エラー:{error}", - "xpack.data.mgmt.searchSessions.status.message.expiredOn": "有効期限:{expireDate}", - "xpack.data.mgmt.searchSessions.table.headerExpiration": "有効期限", - "xpack.data.mgmt.searchSessions.table.headerName": "名前", - "xpack.data.mgmt.searchSessions.table.headerStarted": "作成済み", - "xpack.data.mgmt.searchSessions.table.headerStatus": "ステータス", - "xpack.data.mgmt.searchSessions.table.headerType": "アプリ", - "xpack.data.mgmt.searchSessions.table.mlAppName": "機械学習", - "xpack.data.mgmt.searchSessions.table.notRestorableWarning": "検索セッションはもう一度実行されます。今後使用するために保存できます。", - "xpack.data.mgmt.searchSessions.table.numSearches": "# 検索", - "xpack.data.mgmt.searchSessions.table.versionIncompatibleWarning": "この検索は別のバージョンを実行しているKibanaインスタンスで作成されました。正常に復元されない可能性があります。", - "xpack.data.search.statusError": "検索は{errorCode}ステータスで完了しました", - "xpack.data.search.statusThrow": "検索ステータスはエラー{message}({errorCode})ステータスを返しました", - "xpack.data.searchSessionIndicator.cancelButtonText": "セッションの停止", - "xpack.data.searchSessionIndicator.canceledDescriptionText": "不完全なデータを表示しています。", - "xpack.data.searchSessionIndicator.canceledIconAriaLabel": "検索セッションが停止しました", - "xpack.data.searchSessionIndicator.canceledTitleText": "検索セッションが停止しました", - "xpack.data.searchSessionIndicator.canceledTooltipText": "検索セッションが停止しました", - "xpack.data.searchSessionIndicator.canceledWhenText": "停止:{when}", - "xpack.data.searchSessionIndicator.continueInBackgroundButtonText": "セッションの保存", - "xpack.data.searchSessionIndicator.disabledDueToDisabledGloballyMessage": "検索セッションを管理するアクセス権がありません", - "xpack.data.searchSessionIndicator.disabledDueToTimeoutMessage": "検索セッション結果が期限切れです。", - "xpack.data.searchSessionIndicator.loadingInTheBackgroundDescriptionText": "管理から完了した結果に戻ることができます。", - "xpack.data.searchSessionIndicator.loadingInTheBackgroundIconAriaLabel": "保存されたセッションを実行中です", - "xpack.data.searchSessionIndicator.loadingInTheBackgroundIconTooltipText": "保存されたセッションを実行中です", - "xpack.data.searchSessionIndicator.loadingInTheBackgroundTitleText": "保存されたセッションを実行中です", - "xpack.data.searchSessionIndicator.loadingInTheBackgroundWhenText": "開始:{when}", - "xpack.data.searchSessionIndicator.loadingResultsDescription": "セッションを保存して作業を続け、完了した結果に戻ってください。", - "xpack.data.searchSessionIndicator.loadingResultsIconAriaLabel": "検索セッションを読み込んでいます", - "xpack.data.searchSessionIndicator.loadingResultsIconTooltipText": "検索セッションを読み込んでいます", - "xpack.data.searchSessionIndicator.loadingResultsTitle": "検索に少し時間がかかっています...", - "xpack.data.searchSessionIndicator.loadingResultsWhenText": "開始:{when}", - "xpack.data.searchSessionIndicator.restoredDescriptionText": "特定の時間範囲からキャッシュに保存されたデータを表示しています。時間範囲またはフィルターを変更すると、セッションが再実行されます。", - "xpack.data.searchSessionIndicator.restoredResultsIconAriaLabel": "保存されたセッションが復元されました", - "xpack.data.searchSessionIndicator.restoredResultsTooltipText": "検索セッションが復元されました", - "xpack.data.searchSessionIndicator.restoredTitleText": "検索セッションが復元されました", - "xpack.data.searchSessionIndicator.restoredWhenText": "完了:{when}", - "xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundDescriptionText": "管理からこれらの結果に戻ることができます。", - "xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundIconAriaLabel": "保存されたセッションが完了しました", - "xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundIconTooltipText": "保存されたセッションが完了しました", - "xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundTitleText": "検索セッションが保存されました", - "xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundWhenText": "完了:{when}", - "xpack.data.searchSessionIndicator.resultsLoadedDescriptionText": "セッションを保存して、後から戻ります。", - "xpack.data.searchSessionIndicator.resultsLoadedIconAriaLabel": "検索セッションが完了しました", - "xpack.data.searchSessionIndicator.resultsLoadedIconTooltipText": "検索セッションが完了しました", - "xpack.data.searchSessionIndicator.resultsLoadedText": "検索セッションが完了しました", - "xpack.data.searchSessionIndicator.resultsLoadedWhenText": "完了:{when}", - "xpack.data.searchSessionIndicator.saveButtonText": "セッションの保存", - "xpack.data.searchSessionIndicator.viewSearchSessionsLinkText": "セッションの管理", - "xpack.data.searchSessionName.ariaLabelText": "検索セッション名", - "xpack.data.searchSessionName.editAriaLabelText": "検索セッション名を編集", - "xpack.data.searchSessionName.placeholderText": "検索セッションの名前を入力", - "xpack.data.searchSessionName.saveButtonText": "保存", - "xpack.data.sessions.management.flyoutText": "この検索セッションの構成", - "xpack.data.sessions.management.flyoutTitle": "検索セッションの検査", "xpack.dataVisualizer.addCombinedFieldsLabel": "結合されたフィールドを追加", "xpack.dataVisualizer.choroplethMap.topValuesCount": "{fieldName}の上位の値件数", "xpack.dataVisualizer.chrome.help.appName": "データビジュアライザー", @@ -11926,7 +11893,6 @@ "xpack.enterpriseSearch.workplaceSearch.groups.newGroup.action": "グループを管理", "xpack.enterpriseSearch.workplaceSearch.groups.newGroupSavedSuccess": "{groupName}が正常に作成されました", "xpack.enterpriseSearch.workplaceSearch.groups.noSourcesMessage": "組織コンテンツソースがありません", - "xpack.enterpriseSearch.workplaceSearch.groups.noUsersMessage": "ユーザーがありません", "xpack.enterpriseSearch.workplaceSearch.groups.overview.confirmRemoveButtonText": "{name}を削除", "xpack.enterpriseSearch.workplaceSearch.groups.overview.confirmRemoveDescription": "グループはWorkplace Searchから削除されます。{name}を削除してよろしいですか?", "xpack.enterpriseSearch.workplaceSearch.groups.overview.confirmTitleText": "確認", @@ -19970,13 +19936,8 @@ "xpack.ml.trainedModels.testModelsFlyout.headerLabel": "学習済みモデルのテスト", "xpack.ml.trainedModels.testModelsFlyout.inferenceError": "エラーが発生しました", "xpack.ml.trainedModels.testModelsFlyout.langIdent.inputText": "入力テキスト", - "xpack.ml.trainedModels.testModelsFlyout.langIdent.markupTab": "アウトプット", - "xpack.ml.trainedModels.testModelsFlyout.langIdent.output.language_title": "言語", - "xpack.ml.trainedModels.testModelsFlyout.langIdent.output.probability_title": "確率", "xpack.ml.trainedModels.testModelsFlyout.langIdent.output.title": "これは{lang}のようになります", "xpack.ml.trainedModels.testModelsFlyout.langIdent.output.titleUnknown": "不明な言語コード:{code}", - "xpack.ml.trainedModels.testModelsFlyout.langIdent.rawOutput": "元の出力", - "xpack.ml.trainedModels.testModelsFlyout.langIdent.runButton": "テスト", "xpack.ml.trainedModels.testModelsFlyout.ner.output.probabilityTitle": "確率", "xpack.ml.trainedModels.testModelsFlyout.ner.output.typeTitle": "型", "xpack.ml.trainedModelsBreadcrumbs.nodeOverviewLabel": "ノード", @@ -21816,12 +21777,6 @@ "xpack.observability.noDataConfig.beatsCard.title": "統合の追加", "xpack.observability.noDataConfig.solutionName": "Observability", "xpack.observability.notAvailable": "N/A", - "xpack.observability.overview.alert.allTypes": "すべてのタイプ", - "xpack.observability.overview.alert.appLink": "すべてのアラートを表示", - "xpack.observability.overview.alert.errorMessage": "アラートデータの読み込みエラーが発生しました。しばらくたってから再試行してください。", - "xpack.observability.overview.alert.errorTitle": "アラートデータを読み込めませんでした", - "xpack.observability.overview.alerts.appLink": "アラートを表示", - "xpack.observability.overview.alerts.muted": "ミュート", "xpack.observability.overview.alerts.title": "アラート", "xpack.observability.overview.apm.appLink": "サービスインベントリを表示", "xpack.observability.overview.apm.services": "サービス", @@ -21907,13 +21862,10 @@ "xpack.observability.rules.rulesTable.columns.statusTitle": "ステータス", "xpack.observability.rules.rulesTable.pluralTitle": "ルール", "xpack.observability.rules.rulesTable.ruleStatusActive": "アクティブ", - "xpack.observability.rules.rulesTable.ruleStatusDisabled": "無効", - "xpack.observability.rules.rulesTable.ruleStatusEnabled": "有効", "xpack.observability.rules.rulesTable.ruleStatusError": "エラー", "xpack.observability.rules.rulesTable.ruleStatusLicenseError": "ライセンスエラー", "xpack.observability.rules.rulesTable.ruleStatusOk": "OK", "xpack.observability.rules.rulesTable.ruleStatusPending": "保留中", - "xpack.observability.rules.rulesTable.ruleStatusSnoozedIndefinitely": "無期限にスヌーズ", "xpack.observability.rules.rulesTable.ruleStatusUnknown": "不明", "xpack.observability.rules.rulesTable.ruleStatusWarning": "警告", "xpack.observability.rules.rulesTable.singleTitle": "ルール", @@ -22128,9 +22080,6 @@ "xpack.osquery.liveQueryDetails.viewLiveQueriesHistoryTitle": "ライブクエリ履歴を表示", "xpack.osquery.liveQueryForm.form.saveForLaterButtonLabel": "後で使用するために保存する", "xpack.osquery.liveQueryForm.form.submitButtonLabel": "送信", - "xpack.osquery.liveQueryForm.steps.agentsStepHeading": "エージェントを選択", - "xpack.osquery.liveQueryForm.steps.queryStepHeading": "クエリを入力", - "xpack.osquery.liveQueryForm.steps.resultsStepHeading": "結果を確認", "xpack.osquery.liveQueryResults.table.agentColumnTitle": "エージェント", "xpack.osquery.liveQueryResults.table.fieldMappedLabel": "フィールドは以下にマッピングされています", "xpack.osquery.newLiveQuery.pageTitle": "新しいライブクエリ", @@ -22611,7 +22560,6 @@ "xpack.reporting.publicNotifier.csvContainsFormulas.formulaReportMessage": "レポート'{reportObjectTitle}'には、スプレッドシートアプリケーションで式と解釈される可能性のある文字が含まれています。", "xpack.reporting.publicNotifier.csvContainsFormulas.formulaReportTitle": "{reportType}には式を含めることができます", "xpack.reporting.publicNotifier.downloadReportButtonLabel": "レポートをダウンロード", - "xpack.reporting.publicNotifier.error.calloutTitle": "レポートジョブが失敗しました", "xpack.reporting.publicNotifier.error.checkManagement": "詳細については、{path}に移動してください。", "xpack.reporting.publicNotifier.error.couldNotCreateReportTitle": "'{reportObjectTitle}'の{reportType}レポートを作成できませんでした。", "xpack.reporting.publicNotifier.error.reportingSectionUrlLinkLabel": "スタック管理 > Kibana > レポート", @@ -23963,13 +23911,10 @@ "xpack.securitySolution.console.builtInCommands.clearAbout": "コンソールバッファーを消去", "xpack.securitySolution.console.builtInCommands.helpAbout": "使用可能なコマンドのリストを表示", "xpack.securitySolution.console.commandList.footerText": "上記のコマンドの詳細については、{helpOption}引数を使用してください。例:{cmdExample}", - "xpack.securitySolution.console.commandOutput.runInBackgroundButtonLabel": "バックグラウンドで実行", - "xpack.securitySolution.console.commandOutput.runInBackgroundMsg": "コマンド応答には時間がかかります。ここをクリックするとバックグラウンドで実行し、応答を受信したときに通知が表示されます", "xpack.securitySolution.console.commandUsage.atLeastOneOptionRequiredMessage": "注記:1つ以上のオプションを使用する必要があります", "xpack.securitySolution.console.commandUsage.inputUsage": "使用方法:", "xpack.securitySolution.console.commandUsage.optionsLabel": "オプション:", "xpack.securitySolution.console.commandValidation.argSupportedOnlyOnce": "引数{argName}は一度だけ使用できます", - "xpack.securitySolution.console.commandValidation.cmdHelpTitle": "{cmdName}コマンド", "xpack.securitySolution.console.commandValidation.invalidArgValue": "無効な引数値:{argName}。{error}", "xpack.securitySolution.console.commandValidation.missingRequiredArg": "不足している必須の引数:{argName}", "xpack.securitySolution.console.commandValidation.mustHaveArgs": "不足している必須の引数:{requiredArgs}", @@ -26047,7 +25992,6 @@ "xpack.securitySolution.hosts.hostScoreOverTime.riskScore": "リスクスコア", "xpack.securitySolution.hosts.hostScoreOverTime.riskyLabel": "高リスク", "xpack.securitySolution.hosts.hostScoreOverTime.riskyThresholdHeader": "高リスクしきい値", - "xpack.securitySolution.hosts.hostScoreOverTime.title": "経時的なホストリスクスコア", "xpack.securitySolution.hosts.kqlPlaceholder": "例:host.name:\"foo\"", "xpack.securitySolution.hosts.navigation.alertsTitle": "外部アラート", "xpack.securitySolution.hosts.navigation.allHostsTitle": "すべてのホスト", @@ -29452,7 +29396,6 @@ "xpack.triggersActionsUI.sections.rulesList.ruleStatusActive": "アクティブ", "xpack.triggersActionsUI.sections.rulesList.ruleStatusDropdownMenuLabel": "ルールステータスまたはスヌーズを変更", "xpack.triggersActionsUI.sections.rulesList.ruleStatusError": "エラー", - "xpack.triggersActionsUI.sections.rulesList.ruleStatusFilterLabel": "前回の応答", "xpack.triggersActionsUI.sections.rulesList.ruleStatusLicenseError": "ライセンスエラー", "xpack.triggersActionsUI.sections.rulesList.ruleStatusOk": "OK", "xpack.triggersActionsUI.sections.rulesList.ruleStatusPending": "保留中", @@ -31225,54 +31168,34 @@ "xpack.watcher.watchActions.webhook.usernameIsRequiredIfPasswordValidationMessage": "ユーザー名が必要です。", "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "フィールドを選択してください。", "xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。", - "unifiedSearch.search.searchBar.savedQueryDescriptionLabelText": "説明", "unifiedSearch.search.searchBar.savedQueryDescriptionText": "再度使用するクエリテキストとフィルターを保存します。", "unifiedSearch.search.searchBar.savedQueryForm.titleConflictText": "タイトルがすでに保存されているクエリに使用されています", - "unifiedSearch.search.searchBar.savedQueryFormCancelButtonText": "キャンセル", "unifiedSearch.search.searchBar.savedQueryFormSaveButtonText": "保存", - "unifiedSearch.search.searchBar.savedQueryFormTitle": "クエリを保存", "unifiedSearch.search.searchBar.savedQueryIncludeFiltersLabelText": "フィルターを含める", "unifiedSearch.search.searchBar.savedQueryIncludeTimeFilterLabelText": "時間フィルターを含める", "unifiedSearch.search.searchBar.savedQueryNameHelpText": "名前は必須です。名前の始めと終わりにはスペースを使用できません。名前は一意でなければなりません。", "unifiedSearch.search.searchBar.savedQueryNameLabelText": "名前", "unifiedSearch.search.searchBar.savedQueryNoSavedQueriesText": "保存されたクエリがありません。", - "unifiedSearch.search.searchBar.savedQueryPopoverButtonText": "保存されたクエリを表示", - "unifiedSearch.search.searchBar.savedQueryPopoverClearButtonAriaLabel": "現在保存されているクエリを消去", - "unifiedSearch.search.searchBar.savedQueryPopoverClearButtonText": "クリア", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionCancelButtonText": "キャンセル", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionConfirmButtonText": "削除", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionTitle": "「{savedQueryName}」を削除しますか?", - "unifiedSearch.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel": "保存されたクエリ {savedQueryName} を削除", "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel": "新規保存クエリを保存", "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonText": "新規保存", - "unifiedSearch.search.searchBar.savedQueryPopoverSaveButtonAriaLabel": "新規保存クエリを保存", - "unifiedSearch.search.searchBar.savedQueryPopoverSaveButtonText": "現在のクエリを保存", "unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonAriaLabel": "{title} への変更を保存", "unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonText": "変更を保存", - "unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemButtonAriaLabel": "保存クエリボタン {savedQueryName}", - "unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemDescriptionAriaLabel": "{savedQueryName}の説明", - "unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemSelectedButtonAriaLabel": "選択されたクエリボタン {savedQueryName} を保存しました。変更を破棄するには押してください。", - "unifiedSearch.search.searchBar.savedQueryPopoverTitleText": "保存されたクエリ", "unifiedSearch.noDataPopover.content": "この時間範囲にはデータが含まれていません。表示するフィールドを増やし、グラフを作成するには、時間範囲を広げるか、調整してください。", "unifiedSearch.noDataPopover.dismissAction": "今後表示しない", "unifiedSearch.noDataPopover.subtitle": "ヒント", "unifiedSearch.noDataPopover.title": "空のデータセット", "unifiedSearch.query.queryBar.clearInputLabel": "インプットを消去", "unifiedSearch.query.queryBar.comboboxAriaLabel": "{pageType} ページの検索とフィルタリング", - "unifiedSearch.query.queryBar.kqlFullLanguageName": "Kibana クエリ言語", "unifiedSearch.query.queryBar.kqlLanguageName": "KQL", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoDocLinkText": "ドキュメント", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoOptOutText": "今後表示しない", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoText": "ネストされたフィールドをクエリされているようです。ネストされたクエリに対しては、ご希望の結果により異なる方法で KQL 構文を構築することができます。詳細については、{link}をご覧ください。", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoTitle": "KQL ネストされたクエリ構文", - "unifiedSearch.query.queryBar.kqlOffLabel": "オフ", - "unifiedSearch.query.queryBar.kqlOnLabel": "オン", - "unifiedSearch.query.queryBar.languageSwitcher.toText": "検索用にKibana Query Languageに切り替える", "unifiedSearch.query.queryBar.luceneLanguageName": "Lucene", "unifiedSearch.query.queryBar.searchInputAriaLabel": "{pageType} ページの検索とフィルタリングを行うには入力を開始してください", - "unifiedSearch.query.queryBar.searchInputPlaceholder": "検索", - "unifiedSearch.query.queryBar.syntaxOptionsDescription": "{docsLink}(KQL)は、シンプルなクエリ構文とスクリプトフィールドのサポートを提供します。KQLにはオートコンプリート機能もあります。KQLをオフにする場合は、{nonKqlModeHelpText}", - "unifiedSearch.query.queryBar.syntaxOptionsDescription.nonKqlModeHelpText": "KibanaはLuceneを使用します。", "unifiedSearch.search.unableToGetSavedQueryToastTitle": "保存したクエリ {savedQueryId} を読み込めません", "unifiedSearch.query.queryBar.syntaxOptionsTitle": "構文オプション", "unifiedSearch.filter.applyFilterActionTitle": "現在のビューにフィルターを適用", @@ -31325,22 +31248,17 @@ "unifiedSearch.filter.filterEditor.rangeEndInputPlaceholder": "範囲の終了値", "unifiedSearch.filter.filterEditor.rangeInputLabel": "範囲", "unifiedSearch.filter.filterEditor.rangeStartInputPlaceholder": "範囲の開始値", - "unifiedSearch.filter.filterEditor.saveButtonLabel": "保存", "unifiedSearch.filter.filterEditor.trueOptionLabel": "true", "unifiedSearch.filter.filterEditor.valueInputLabel": "値", "unifiedSearch.filter.filterEditor.valueInputPlaceholder": "値を入力", "unifiedSearch.filter.filterEditor.valueSelectPlaceholder": "値を選択", "unifiedSearch.filter.filterEditor.valuesSelectLabel": "値", "unifiedSearch.filter.filterEditor.valuesSelectPlaceholder": "値を選択", - "unifiedSearch.filter.options.changeAllFiltersButtonLabel": "すべてのフィルターの変更", - "unifiedSearch.filter.options.deleteAllFiltersButtonLabel": "すべて削除", "unifiedSearch.filter.options.disableAllFiltersButtonLabel": "すべて無効にする", "unifiedSearch.filter.options.enableAllFiltersButtonLabel": "すべて有効にする", - "unifiedSearch.filter.options.invertDisabledFiltersButtonLabel": "有効・無効を反転", "unifiedSearch.filter.options.invertNegatedFiltersButtonLabel": "含める・除外を反転", "unifiedSearch.filter.options.pinAllFiltersButtonLabel": "すべてピン付け", "unifiedSearch.filter.options.unpinAllFiltersButtonLabel": "すべてのピンを外す", - "unifiedSearch.filter.searchBar.changeAllFiltersTitle": "すべてのフィルターの変更", "unifiedSearch.kueryAutocomplete.andOperatorDescription": "{bothArguments} が true であることを条件とする", "unifiedSearch.kueryAutocomplete.andOperatorDescription.bothArgumentsText": "両方の引数", "unifiedSearch.kueryAutocomplete.equalOperatorDescription": "一部の値に{equals}", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index a9e19a19413cb..6783295921119 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -372,7 +372,6 @@ "xpack.lens.functions.counterRate.args.outputColumnNameHelpText": "要存储结果计数率的列名称", "xpack.lens.functions.counterRate.help": "在数据表中计算列的计数率", "xpack.lens.functions.lastValue.missingSortField": "此数据视图不包含任何日期字段", - "xpack.lens.functions.mergeTables.help": "将任意数目的 kibana 表合并到单个表中并通过检查器适配器将其开放的助手", "xpack.lens.functions.renameColumns.help": "用于重命名数据表列的助手", "xpack.lens.functions.renameColumns.idMap.help": "旧列 ID 为键且相应新列 ID 为值的 JSON 编码对象。所有其他列 ID 都将保留。", "xpack.lens.functions.timeScale.dateColumnMissingMessage": "指定的 dateColumnId {columnId} 不存在。", @@ -447,28 +446,18 @@ "xpack.lens.indexPattern.dateHistogram.autoAdvancedExplanation": "时间间隔遵循以下逻辑:", "xpack.lens.indexPattern.dateHistogram.autoBasicExplanation": "自动日期直方图按时间间隔将数据字段拆分为桶。", "xpack.lens.indexPattern.dateHistogram.autoBoundHeader": "已度量目标时间间隔", - "xpack.lens.indexPattern.dateHistogram.autoHelpText": "运作方式", - "xpack.lens.indexPattern.dateHistogram.autoInterval": "定制时间间隔", "xpack.lens.indexPattern.dateHistogram.autoIntervalHeader": "已用时间间隔", "xpack.lens.indexPattern.dateHistogram.autoLongerExplanation": "要选择时间间隔,Lens 按 {targetBarSetting} 设置分割指定的时间范围。Lens 为您的数据计算最佳时间间隔。例如 30m、1h 和 12。最大条形数由 {maxBarSetting} 值设置。", "xpack.lens.indexPattern.dateHistogram.bindToGlobalTimePicker": "绑定到全局时间选取器", - "xpack.lens.indexPattern.dateHistogram.days": "天", "xpack.lens.indexPattern.dateHistogram.dropPartialBuckets": "丢弃部分存储桶", "xpack.lens.indexPattern.dateHistogram.dropPartialBucketsHelp": "禁止丢弃部分存储桶,因为只可以为绑定到右上角的全局时间选取器的时间字段计算这些存储桶。", "xpack.lens.indexPattern.dateHistogram.globalTimePickerHelp": "按右上角的全局时间选取器筛选选定字段。不能对当前数据视图的默认时间字段关闭此设置。", - "xpack.lens.indexPattern.dateHistogram.hours": "小时", "xpack.lens.indexPattern.dateHistogram.includeEmptyRows": "包括空行", - "xpack.lens.indexPattern.dateHistogram.milliseconds": "毫秒", "xpack.lens.indexPattern.dateHistogram.minimumInterval": "最小时间间隔", - "xpack.lens.indexPattern.dateHistogram.minutes": "分钟", - "xpack.lens.indexPattern.dateHistogram.month": "个月", "xpack.lens.indexPattern.dateHistogram.moreThanYear": "1 年以上", "xpack.lens.indexPattern.dateHistogram.restrictedInterval": "由于聚合限制,时间间隔固定为 {intervalValue}。", - "xpack.lens.indexPattern.dateHistogram.seconds": "秒", "xpack.lens.indexPattern.dateHistogram.titleHelp": "自动日期直方图的工作原理", "xpack.lens.indexPattern.dateHistogram.upTo": "最多", - "xpack.lens.indexPattern.dateHistogram.week": "周", - "xpack.lens.indexPattern.dateHistogram.year": "年", "xpack.lens.indexPattern.dateHistogramTimeShift": "在单个图层中,您无法组合上一时间范围偏移与 Date Histogram。在“{column}”中使用显式时间偏移持续时间,或替换 Date Histogram。", "xpack.lens.indexPattern.decimalPlacesLabel": "小数", "xpack.lens.indexPattern.defaultFormatLabel": "默认", @@ -545,7 +534,6 @@ "xpack.lens.indexPattern.incompleteOperation": "(不完整)", "xpack.lens.indexPattern.intervals": "时间间隔", "xpack.lens.indexPattern.invalidFieldLabel": "字段无效。检查数据视图或选取其他字段。", - "xpack.lens.indexPattern.invalidInterval": "时间间隔值无效", "xpack.lens.indexPattern.invalidOperationLabel": "此字段不适用于选定函数。", "xpack.lens.indexPattern.invalidReferenceConfiguration": "维度“{dimensionLabel}”配置不正确", "xpack.lens.indexPattern.invalidTimeShift": "时间偏移无效。输入正整数数量,后跟以下单位之一:s、m、h、d、w、M、y。例如,3h 表示 3 小时", @@ -580,7 +568,6 @@ "xpack.lens.indexPattern.moving_average.signature": "指标:数字,[window]:数字", "xpack.lens.indexPattern.movingAverage": "移动平均值", "xpack.lens.indexPattern.movingAverage.basicExplanation": "移动平均值在数据上滑动时间窗并显示平均值。仅日期直方图支持移动平均值。", - "xpack.lens.indexPattern.movingAverage.helpText": "运作方式", "xpack.lens.indexPattern.movingAverage.limitations": "第一个移动平均值开始于第二项。", "xpack.lens.indexPattern.movingAverage.longerExplanation": "要计算移动平均值,Lens 使用时间窗的平均值,并为缺口应用跳过策略。 对于缺失值,将跳过桶,计算将基于下一个值执行。", "xpack.lens.indexPattern.movingAverage.tableExplanation": "例如,如果数据为 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],则可以使用时间窗大小 5 计算简单移动平均值:", @@ -718,13 +705,10 @@ "xpack.lens.indexPattern.timeShiftSmallWarning": "{label} 使用的时间偏移 {columnTimeShift} 小于 Date Histogram 时间间隔 {interval} 。要防止数据不匹配,请使用 {interval} 的倍数作为时间偏移。", "xpack.lens.indexPattern.uniqueLabel": "{label} [{num}]", "xpack.lens.indexPattern.useAsTopLevelAgg": "先按此字段分组", - "xpack.lens.indexPatterns.actionsPopoverLabel": "数据视图设置", - "xpack.lens.indexPatterns.addFieldButton": "将字段添加到数据视图", "xpack.lens.indexPatterns.clearFiltersLabel": "清除名称和类型筛选", "xpack.lens.indexPatterns.fieldFiltersLabel": "按类型筛选", "xpack.lens.indexPatterns.fieldSearchLiveRegion": "{availableFields} 个可用{availableFields, plural, other {字段}}。{emptyFields} 个空{emptyFields, plural, other {字段}}。{metaFields} 个元{metaFields, plural,other {字段}}。", "xpack.lens.indexPatterns.filterByNameLabel": "搜索字段名称", - "xpack.lens.indexPatterns.manageFieldButton": "管理数据视图字段", "xpack.lens.indexPatterns.noAvailableDataLabel": "没有包含数据的可用字段。", "xpack.lens.indexPatterns.noDataLabel": "无字段。", "xpack.lens.indexPatterns.noEmptyDataLabel": "无空字段。", @@ -2722,6 +2706,106 @@ "data.searchSessions.sessionService.sessionObjectFetchError": "无法提取搜索会话信息", "data.triggers.applyFilterDescription": "应用 kibana 筛选时。可能是单个值或范围筛选。", "data.triggers.applyFilterTitle": "应用筛选", + "data.mgmt.searchSessions.actionDelete": "删除", + "data.mgmt.searchSessions.actionExtend": "延长", + "data.mgmt.searchSessions.actionRename": "编辑名称", + "data.mgmt.searchSessions.actions.tooltip.moreActions": "更多操作", + "data.mgmt.searchSessions.api.deleted": "搜索会话已删除。", + "data.mgmt.searchSessions.api.deletedError": "无法删除搜索会话!", + "data.mgmt.searchSessions.api.extended": "搜索会话已延长。", + "data.mgmt.searchSessions.api.extendError": "无法延长搜索会话!", + "data.mgmt.searchSessions.api.fetchError": "无法刷新页面!", + "data.mgmt.searchSessions.api.fetchTimeout": "获取搜索会话信息在 {timeout} 秒后已超时", + "data.mgmt.searchSessions.api.rename": "搜索会话已重命名", + "data.mgmt.searchSessions.api.renameError": "无法重命名搜索会话", + "data.mgmt.searchSessions.appTitle": "搜索会话", + "data.mgmt.searchSessions.ariaLabel.moreActions": "更多操作", + "data.mgmt.searchSessions.cancelModal.cancelButton": "取消", + "data.mgmt.searchSessions.cancelModal.deleteButton": "删除", + "data.mgmt.searchSessions.cancelModal.message": "删除搜索会话“{name}”将会删除所有缓存的结果。", + "data.mgmt.searchSessions.cancelModal.title": "删除搜索会话", + "data.mgmt.searchSessions.extendModal.dontExtendButton": "取消", + "data.mgmt.searchSessions.extendModal.extendButton": "延长过期时间", + "data.mgmt.searchSessions.extendModal.extendMessage": "搜索会话“{name}”过期时间将延长至 {newExpires}。", + "data.mgmt.searchSessions.extendModal.title": "延长搜索会话过期时间", + "data.mgmt.searchSessions.flyoutTitle": "检查", + "data.mgmt.searchSessions.main.backgroundSessionsDocsLinkText": "文档", + "data.mgmt.searchSessions.main.sectionDescription": "管理已保存搜索会话。", + "data.mgmt.searchSessions.main.sectionTitle": "搜索会话", + "data.mgmt.searchSessions.renameModal.cancelButton": "取消", + "data.mgmt.searchSessions.renameModal.renameButton": "保存", + "data.mgmt.searchSessions.renameModal.searchSessionNameInputLabel": "搜索会话名称", + "data.mgmt.searchSessions.renameModal.title": "编辑搜索会话名称", + "data.mgmt.searchSessions.search.filterApp": "应用", + "data.mgmt.searchSessions.search.filterStatus": "状态", + "data.mgmt.searchSessions.search.tools.refresh": "刷新", + "data.mgmt.searchSessions.status.expireDateUnknown": "未知", + "data.mgmt.searchSessions.status.expiresOn": "于 {expireDate}过期", + "data.mgmt.searchSessions.status.expiresSoonInDays": "将于 {numDays} 天后过期", + "data.mgmt.searchSessions.status.expiresSoonInDaysTooltip": "{numDays} 天", + "data.mgmt.searchSessions.status.expiresSoonInHours": "此会话将于 {numHours} 小时后过期", + "data.mgmt.searchSessions.status.expiresSoonInHoursTooltip": "{numHours} 小时", + "data.mgmt.searchSessions.status.label.cancelled": "已取消", + "data.mgmt.searchSessions.status.label.complete": "已完成", + "data.mgmt.searchSessions.status.label.error": "错误", + "data.mgmt.searchSessions.status.label.expired": "已过期", + "data.mgmt.searchSessions.status.label.inProgress": "进行中", + "data.mgmt.searchSessions.status.message.cancelled": "用户已取消", + "data.mgmt.searchSessions.status.message.createdOn": "于 {expireDate}过期", + "data.mgmt.searchSessions.status.message.error": "错误:{error}", + "data.mgmt.searchSessions.status.message.expiredOn": "已于 {expireDate}过期", + "data.mgmt.searchSessions.table.headerExpiration": "到期", + "data.mgmt.searchSessions.table.headerName": "名称", + "data.mgmt.searchSessions.table.headerStarted": "创建时间", + "data.mgmt.searchSessions.table.headerStatus": "状态", + "data.mgmt.searchSessions.table.headerType": "应用", + "data.mgmt.searchSessions.table.notRestorableWarning": "搜索会话将重新执行。然后,您可以将其保存,供未来使用。", + "data.mgmt.searchSessions.table.numSearches": "搜索数", + "data.mgmt.searchSessions.table.versionIncompatibleWarning": "此搜索会话在运行不同版本的 Kibana 实例中已创建。其可能不会正确还原。", + "data.search.statusError": "搜索完成,状态为 {errorCode}", + "data.search.statusThrow": "搜索状态引发错误 {message} ({errorCode}) 状态", + "data.searchSessionIndicator.cancelButtonText": "停止会话", + "data.searchSessionIndicator.canceledDescriptionText": "您正查看不完整的数据", + "data.searchSessionIndicator.canceledIconAriaLabel": "搜索会话已停止", + "data.searchSessionIndicator.canceledTitleText": "搜索会话已停止", + "data.searchSessionIndicator.canceledTooltipText": "搜索会话已停止", + "data.searchSessionIndicator.canceledWhenText": "已于 {when} 停止", + "data.searchSessionIndicator.continueInBackgroundButtonText": "保存会话", + "data.searchSessionIndicator.disabledDueToDisabledGloballyMessage": "您无权管理搜索会话", + "data.searchSessionIndicator.disabledDueToTimeoutMessage": "搜索会话结果已过期。", + "data.searchSessionIndicator.loadingInTheBackgroundDescriptionText": "可以从“管理”中返回至完成的结果", + "data.searchSessionIndicator.loadingInTheBackgroundIconAriaLabel": "已保存会话正在进行中", + "data.searchSessionIndicator.loadingInTheBackgroundIconTooltipText": "已保存会话正在进行中", + "data.searchSessionIndicator.loadingInTheBackgroundTitleText": "已保存会话正在进行中", + "data.searchSessionIndicator.loadingInTheBackgroundWhenText": "已于 {when} 启动", + "data.searchSessionIndicator.loadingResultsDescription": "保存您的会话,继续您的工作,然后返回到完成的结果", + "data.searchSessionIndicator.loadingResultsIconAriaLabel": "搜索会话正在加载", + "data.searchSessionIndicator.loadingResultsIconTooltipText": "搜索会话正在加载", + "data.searchSessionIndicator.loadingResultsTitle": "您的搜索将需要一些时间......", + "data.searchSessionIndicator.loadingResultsWhenText": "已于 {when} 启动", + "data.searchSessionIndicator.restoredDescriptionText": "您在查看特定时间范围的缓存数据。更改时间范围或筛选将会重新运行会话", + "data.searchSessionIndicator.restoredResultsIconAriaLabel": "已保存会话已还原", + "data.searchSessionIndicator.restoredResultsTooltipText": "搜索会话已还原", + "data.searchSessionIndicator.restoredTitleText": "搜索会话已还原", + "data.searchSessionIndicator.restoredWhenText": "已于 {when} 完成", + "data.searchSessionIndicator.resultLoadedInTheBackgroundDescriptionText": "可以从“管理”中返回到这些结果", + "data.searchSessionIndicator.resultLoadedInTheBackgroundIconAriaLabel": "已保存会话已完成", + "data.searchSessionIndicator.resultLoadedInTheBackgroundIconTooltipText": "已保存会话已完成", + "data.searchSessionIndicator.resultLoadedInTheBackgroundTitleText": "搜索会话已保存", + "data.searchSessionIndicator.resultLoadedInTheBackgroundWhenText": "已于 {when} 完成", + "data.searchSessionIndicator.resultsLoadedDescriptionText": "保存您的会话,之后可返回", + "data.searchSessionIndicator.resultsLoadedIconAriaLabel": "搜索会话已完成", + "data.searchSessionIndicator.resultsLoadedIconTooltipText": "搜索会话已完成", + "data.searchSessionIndicator.resultsLoadedText": "搜索会话已完成", + "data.searchSessionIndicator.resultsLoadedWhenText": "已于 {when} 完成", + "data.searchSessionIndicator.saveButtonText": "保存会话", + "data.searchSessionIndicator.viewSearchSessionsLinkText": "管理会话", + "data.searchSessionName.ariaLabelText": "搜索会话名称", + "data.searchSessionName.editAriaLabelText": "编辑搜索会话名称", + "data.searchSessionName.placeholderText": "为搜索会话输入名称", + "data.searchSessionName.saveButtonText": "保存", + "data.sessions.management.flyoutText": "此搜索会话的配置", + "data.sessions.management.flyoutTitle": "检查搜索会话", "dataViews.deprecations.scriptedFields.manualStepOneMessage": "导航到“堆栈管理”>“Kibana”>“数据视图”。", "dataViews.deprecations.scriptedFields.manualStepTwoMessage": "更新 {numberOfIndexPatternsWithScriptedFields} 个具有脚本字段的数据视图以改为使用运行时字段。多数情况下,要迁移现有脚本,您需要将“return ;”更改为“emit();”。至少有一个脚本字段的数据视图:{allTitles}", "dataViews.deprecations.scriptedFieldsMessage": "您具有 {numberOfIndexPatternsWithScriptedFields} 个使用脚本字段的数据视图 ({titlesPreview}...)。脚本字段已过时,将在未来移除。请改为使用运行时脚本。", @@ -2896,7 +2980,6 @@ "discover.field.mappingConflict": "此字段在匹配此模式的各个索引中已定义为若干类型(字符串、整数等)。您可能仍可以使用此冲突字段,但它无法用于需要 Kibana 知道其类型的函数。要解决此问题,需要重新索引您的数据。", "discover.field.mappingConflict.title": "映射冲突", "discover.field.title": "{fieldName} ({fieldDisplayName})", - "discover.fieldChooser.dataViews.createNewDataView": "创建新的数据视图", "discover.fieldChooser.detailViews.emptyStringText": "空字符串", "discover.fieldChooser.detailViews.existsInRecordsText": "存在于 {value} / {totalValue} 条记录中", "discover.fieldChooser.detailViews.filterOutValueButtonAriaLabel": "筛除 {field}:“{value}”", @@ -2933,10 +3016,6 @@ "discover.fieldChooser.filter.toggleButton.no": "否", "discover.fieldChooser.filter.toggleButton.yes": "是", "discover.fieldChooser.filter.typeLabel": "类型", - "discover.fieldChooser.indexPattern.changeDataViewTitle": "更改数据视图", - "discover.fieldChooser.indexPatterns.actionsPopoverLabel": "数据视图设置", - "discover.fieldChooser.indexPatterns.addFieldButton": "添加字段", - "discover.fieldChooser.indexPatterns.manageFieldButton": "管理设置", "discover.fieldChooser.searchPlaceHolder": "搜索字段名称", "discover.fieldChooser.toggleFieldFilterButtonHideAriaLabel": "隐藏字段筛选设置", "discover.fieldChooser.toggleFieldFilterButtonShowAriaLabel": "显示字段筛选设置", @@ -3473,11 +3552,11 @@ "expressionXY.axisTitlesVisibilityConfig.yLeft.help": "指定左侧 y 轴的标题是否可见。", "expressionXY.axisTitlesVisibilityConfig.yRight.help": "指定右侧 y 轴的标题是否可见。", "expressionXY.dataLayer.accessors.help": "要在 y 轴上显示的列。", - "expressionXY.dataLayer.columnToLabel.help": "要标记的列 ID 的 JSON 键值对", + "expressionXY.layer.columnToLabel.help": "要标记的列 ID 的 JSON 键值对", "expressionXY.dataLayer.help": "配置 xy 图表中的图层", "expressionXY.dataLayer.hide.help": "显示/隐藏轴", "expressionXY.dataLayer.isHistogram.help": "是否将图表布局为直方图", - "expressionXY.dataLayer.layerId.help": "图层 ID", + "expressionXY.layers.layerId.help": "图层 ID", "expressionXY.dataLayer.palette.help": "调色板", "expressionXY.dataLayer.seriesType.help": "要显示的图表的类型。", "expressionXY.dataLayer.splitAccessor.help": "拆分要依据的列", @@ -3508,9 +3587,7 @@ "expressionXY.legendConfig.showSingleSeries.help": "指定是否应显示只包含一个条目的图例", "expressionXY.legendConfig.verticalAlignment.help": "指定图例显示在图表内时垂直对齐。", "expressionXY.referenceLineLayer.accessors.help": "要在 y 轴上显示的列。", - "expressionXY.referenceLineLayer.columnToLabel.help": "要标记的列 ID 的 JSON 键值对", "expressionXY.referenceLineLayer.help": "配置 xy 图表中的参考线", - "expressionXY.referenceLineLayer.layerId.help": "图层 ID", "expressionXY.referenceLineLayer.yConfig.help": "y 轴的其他配置", "expressionXY.tickLabelsConfig.help": "配置 xy 图表的刻度标签外观", "expressionXY.tickLabelsConfig.x.help": "指定 x 轴的刻度标签是否可见。", @@ -3541,7 +3618,7 @@ "expressionXY.xyVis.help": "X/Y 图表", "expressionXY.xyVis.hideEndzones.help": "隐藏部分数据的末日区域标记", "expressionXY.xyVis.labelsOrientation.help": "定义轴标签的旋转", - "expressionXY.xyVis.layers.help": "可视序列的图层", + "expressionXY.layeredXyVis.layers.help": "可视序列的图层", "expressionXY.xyVis.legend.help": "配置图表图例。", "expressionXY.xyVis.logDatatable.breakDown": "细分方式", "expressionXY.xyVis.logDatatable.metric": "垂直轴", @@ -7396,7 +7473,6 @@ "xpack.apm.filter.environment.allLabel": "全部", "xpack.apm.filter.environment.label": "环境", "xpack.apm.filter.environment.notDefinedLabel": "未定义", - "xpack.apm.filter.environment.selectEnvironmentLabel": "选择环境", "xpack.apm.fleet_integration.settings.advancedOptionsLavel": "高级选项", "xpack.apm.fleet_integration.settings.agentAuthorization.anonymousAllowAgentHelpText": "允许进行匿名访问的代理名称。", "xpack.apm.fleet_integration.settings.agentAuthorization.anonymousAllowAgentLabel": "允许的代理", @@ -7499,8 +7575,6 @@ "xpack.apm.fleetIntegration.apmAgent.editDisacoveryRule.type": "类型", "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.addRule": "添加规则", "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.autoAttachment": "自动附件", - "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.betaBadge.label": "公测版", - "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.betaBadge.tooltipContent": "用于 Java 的自动附件不是 GA 版。请通过报告错误来帮助我们。", "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.discoveryRules": "发现规则", "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.editRule.add": "添加", "xpack.apm.fleetIntegration.apmAgent.runtimeAttachment.editRule.helpText": "从允许的参数中选择", @@ -7623,7 +7697,6 @@ "xpack.apm.propertiesTable.tabs.metadataLabel": "元数据", "xpack.apm.propertiesTable.tabs.timelineLabel": "时间线", "xpack.apm.searchInput.filter": "筛选...", - "xpack.apm.selectCustomOptionText": "将 \\{searchValue\\} 添加为新选项", "xpack.apm.selectPlaceholder": "选择选项:", "xpack.apm.serviceDependencies.breakdownChartTitle": "依赖项花费的时间", "xpack.apm.serviceDetails.dependenciesTabLabel": "依赖项", @@ -7937,7 +8010,6 @@ "xpack.apm.settings.customLink.delete": "删除", "xpack.apm.settings.customLink.delete.failed": "无法删除定制链接", "xpack.apm.settings.customLink.delete.successed": "已删除定制链接。", - "xpack.apm.settings.customLink.emptyPromptText": "让我们改动一下!可以通过每个服务的事务详情将定制链接添加到“操作”上下文菜单。创建指向公司支持门户或用于提交新错误报告的有用链接。在我们的文档中详细了解。", "xpack.apm.settings.customLink.emptyPromptTitle": "未找到链接。", "xpack.apm.settings.customLink.flyout.action.title": "链接", "xpack.apm.settings.customLink.flyout.close": "关闭", @@ -8277,7 +8349,6 @@ "xpack.apm.views.metrics.title": "指标", "xpack.apm.views.nodes.title": "JVM", "xpack.apm.views.overview.title": "概览", - "xpack.apm.views.serviceGroups.breadcrumbLabel": "服务", "xpack.apm.views.serviceGroups.title": "服务组", "xpack.apm.views.serviceInventory.title": "服务", "xpack.apm.views.serviceMap.title": "服务地图", @@ -9760,7 +9831,6 @@ "xpack.cases.recentCases.viewAllCasesLink": "查看所有案例", "xpack.cases.settings.syncAlertsSwitchLabelOff": "关闭", "xpack.cases.settings.syncAlertsSwitchLabelOn": "开启", - "xpack.cases.status.all": "全部", "xpack.cases.status.closed": "已关闭", "xpack.cases.status.iconAria": "更改状态", "xpack.cases.status.inProgress": "进行中", @@ -10160,7 +10230,6 @@ "xpack.csp.passed": "通过", "xpack.csp.posture_score": "态势分数", "xpack.csp.posture_score_trend": "态势分数趋势", - "xpack.csp.resource_type": "资源类型", "xpack.csp.rules.activateAllButtonLabel": "激活 {count, plural, other {# 个规则}}", "xpack.csp.rules.activatedLabel": "已激活", "xpack.csp.rules.activateLabel": "激活", @@ -10211,107 +10280,6 @@ "xpack.dashboard.drilldown.goToDashboard": "前往仪表板", "xpack.dashboard.FlyoutCreateDrilldownAction.displayName": "创建向下钻取", "xpack.dashboard.panel.openFlyoutEditDrilldown.displayName": "管理向下钻取", - "xpack.data.mgmt.searchSessions.actionDelete": "删除", - "xpack.data.mgmt.searchSessions.actionExtend": "延长", - "xpack.data.mgmt.searchSessions.actionRename": "编辑名称", - "xpack.data.mgmt.searchSessions.actions.tooltip.moreActions": "更多操作", - "xpack.data.mgmt.searchSessions.api.deleted": "搜索会话已删除。", - "xpack.data.mgmt.searchSessions.api.deletedError": "无法删除搜索会话!", - "xpack.data.mgmt.searchSessions.api.extended": "搜索会话已延长。", - "xpack.data.mgmt.searchSessions.api.extendError": "无法延长搜索会话!", - "xpack.data.mgmt.searchSessions.api.fetchError": "无法刷新页面!", - "xpack.data.mgmt.searchSessions.api.fetchTimeout": "获取搜索会话信息在 {timeout} 秒后已超时", - "xpack.data.mgmt.searchSessions.api.rename": "搜索会话已重命名", - "xpack.data.mgmt.searchSessions.api.renameError": "无法重命名搜索会话", - "xpack.data.mgmt.searchSessions.appTitle": "搜索会话", - "xpack.data.mgmt.searchSessions.ariaLabel.moreActions": "更多操作", - "xpack.data.mgmt.searchSessions.cancelModal.cancelButton": "取消", - "xpack.data.mgmt.searchSessions.cancelModal.deleteButton": "删除", - "xpack.data.mgmt.searchSessions.cancelModal.message": "删除搜索会话“{name}”将会删除所有缓存的结果。", - "xpack.data.mgmt.searchSessions.cancelModal.title": "删除搜索会话", - "xpack.data.mgmt.searchSessions.extendModal.dontExtendButton": "取消", - "xpack.data.mgmt.searchSessions.extendModal.extendButton": "延长过期时间", - "xpack.data.mgmt.searchSessions.extendModal.extendMessage": "搜索会话“{name}”过期时间将延长至 {newExpires}。", - "xpack.data.mgmt.searchSessions.extendModal.title": "延长搜索会话过期时间", - "xpack.data.mgmt.searchSessions.flyoutTitle": "检查", - "xpack.data.mgmt.searchSessions.main.backgroundSessionsDocsLinkText": "文档", - "xpack.data.mgmt.searchSessions.main.sectionDescription": "管理已保存搜索会话。", - "xpack.data.mgmt.searchSessions.main.sectionTitle": "搜索会话", - "xpack.data.mgmt.searchSessions.renameModal.cancelButton": "取消", - "xpack.data.mgmt.searchSessions.renameModal.renameButton": "保存", - "xpack.data.mgmt.searchSessions.renameModal.searchSessionNameInputLabel": "搜索会话名称", - "xpack.data.mgmt.searchSessions.renameModal.title": "编辑搜索会话名称", - "xpack.data.mgmt.searchSessions.search.filterApp": "应用", - "xpack.data.mgmt.searchSessions.search.filterStatus": "状态", - "xpack.data.mgmt.searchSessions.search.tools.refresh": "刷新", - "xpack.data.mgmt.searchSessions.status.expireDateUnknown": "未知", - "xpack.data.mgmt.searchSessions.status.expiresOn": "于 {expireDate}过期", - "xpack.data.mgmt.searchSessions.status.expiresSoonInDays": "将于 {numDays} 天后过期", - "xpack.data.mgmt.searchSessions.status.expiresSoonInDaysTooltip": "{numDays} 天", - "xpack.data.mgmt.searchSessions.status.expiresSoonInHours": "此会话将于 {numHours} 小时后过期", - "xpack.data.mgmt.searchSessions.status.expiresSoonInHoursTooltip": "{numHours} 小时", - "xpack.data.mgmt.searchSessions.status.label.cancelled": "已取消", - "xpack.data.mgmt.searchSessions.status.label.complete": "已完成", - "xpack.data.mgmt.searchSessions.status.label.error": "错误", - "xpack.data.mgmt.searchSessions.status.label.expired": "已过期", - "xpack.data.mgmt.searchSessions.status.label.inProgress": "进行中", - "xpack.data.mgmt.searchSessions.status.message.cancelled": "用户已取消", - "xpack.data.mgmt.searchSessions.status.message.createdOn": "于 {expireDate}过期", - "xpack.data.mgmt.searchSessions.status.message.error": "错误:{error}", - "xpack.data.mgmt.searchSessions.status.message.expiredOn": "已于 {expireDate}过期", - "xpack.data.mgmt.searchSessions.table.headerExpiration": "到期", - "xpack.data.mgmt.searchSessions.table.headerName": "名称", - "xpack.data.mgmt.searchSessions.table.headerStarted": "创建时间", - "xpack.data.mgmt.searchSessions.table.headerStatus": "状态", - "xpack.data.mgmt.searchSessions.table.headerType": "应用", - "xpack.data.mgmt.searchSessions.table.mlAppName": "Machine Learning", - "xpack.data.mgmt.searchSessions.table.notRestorableWarning": "搜索会话将重新执行。然后,您可以将其保存,供未来使用。", - "xpack.data.mgmt.searchSessions.table.numSearches": "搜索数", - "xpack.data.mgmt.searchSessions.table.versionIncompatibleWarning": "此搜索会话在运行不同版本的 Kibana 实例中已创建。其可能不会正确还原。", - "xpack.data.search.statusError": "搜索完成,状态为 {errorCode}", - "xpack.data.search.statusThrow": "搜索状态引发错误 {message} ({errorCode}) 状态", - "xpack.data.searchSessionIndicator.cancelButtonText": "停止会话", - "xpack.data.searchSessionIndicator.canceledDescriptionText": "您正查看不完整的数据", - "xpack.data.searchSessionIndicator.canceledIconAriaLabel": "搜索会话已停止", - "xpack.data.searchSessionIndicator.canceledTitleText": "搜索会话已停止", - "xpack.data.searchSessionIndicator.canceledTooltipText": "搜索会话已停止", - "xpack.data.searchSessionIndicator.canceledWhenText": "已于 {when} 停止", - "xpack.data.searchSessionIndicator.continueInBackgroundButtonText": "保存会话", - "xpack.data.searchSessionIndicator.disabledDueToDisabledGloballyMessage": "您无权管理搜索会话", - "xpack.data.searchSessionIndicator.disabledDueToTimeoutMessage": "搜索会话结果已过期。", - "xpack.data.searchSessionIndicator.loadingInTheBackgroundDescriptionText": "可以从“管理”中返回至完成的结果", - "xpack.data.searchSessionIndicator.loadingInTheBackgroundIconAriaLabel": "已保存会话正在进行中", - "xpack.data.searchSessionIndicator.loadingInTheBackgroundIconTooltipText": "已保存会话正在进行中", - "xpack.data.searchSessionIndicator.loadingInTheBackgroundTitleText": "已保存会话正在进行中", - "xpack.data.searchSessionIndicator.loadingInTheBackgroundWhenText": "已于 {when} 启动", - "xpack.data.searchSessionIndicator.loadingResultsDescription": "保存您的会话,继续您的工作,然后返回到完成的结果", - "xpack.data.searchSessionIndicator.loadingResultsIconAriaLabel": "搜索会话正在加载", - "xpack.data.searchSessionIndicator.loadingResultsIconTooltipText": "搜索会话正在加载", - "xpack.data.searchSessionIndicator.loadingResultsTitle": "您的搜索将需要一些时间......", - "xpack.data.searchSessionIndicator.loadingResultsWhenText": "已于 {when} 启动", - "xpack.data.searchSessionIndicator.restoredDescriptionText": "您在查看特定时间范围的缓存数据。更改时间范围或筛选将会重新运行会话", - "xpack.data.searchSessionIndicator.restoredResultsIconAriaLabel": "已保存会话已还原", - "xpack.data.searchSessionIndicator.restoredResultsTooltipText": "搜索会话已还原", - "xpack.data.searchSessionIndicator.restoredTitleText": "搜索会话已还原", - "xpack.data.searchSessionIndicator.restoredWhenText": "已于 {when} 完成", - "xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundDescriptionText": "可以从“管理”中返回到这些结果", - "xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundIconAriaLabel": "已保存会话已完成", - "xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundIconTooltipText": "已保存会话已完成", - "xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundTitleText": "搜索会话已保存", - "xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundWhenText": "已于 {when} 完成", - "xpack.data.searchSessionIndicator.resultsLoadedDescriptionText": "保存您的会话,之后可返回", - "xpack.data.searchSessionIndicator.resultsLoadedIconAriaLabel": "搜索会话已完成", - "xpack.data.searchSessionIndicator.resultsLoadedIconTooltipText": "搜索会话已完成", - "xpack.data.searchSessionIndicator.resultsLoadedText": "搜索会话已完成", - "xpack.data.searchSessionIndicator.resultsLoadedWhenText": "已于 {when} 完成", - "xpack.data.searchSessionIndicator.saveButtonText": "保存会话", - "xpack.data.searchSessionIndicator.viewSearchSessionsLinkText": "管理会话", - "xpack.data.searchSessionName.ariaLabelText": "搜索会话名称", - "xpack.data.searchSessionName.editAriaLabelText": "编辑搜索会话名称", - "xpack.data.searchSessionName.placeholderText": "为搜索会话输入名称", - "xpack.data.searchSessionName.saveButtonText": "保存", - "xpack.data.sessions.management.flyoutText": "此搜索会话的配置", - "xpack.data.sessions.management.flyoutTitle": "检查搜索会话", "xpack.dataVisualizer.addCombinedFieldsLabel": "添加组合字段", "xpack.dataVisualizer.choroplethMap.topValuesCount": "{fieldName} 的排名最前值计数", "xpack.dataVisualizer.chrome.help.appName": "数据可视化工具", @@ -11948,7 +11916,6 @@ "xpack.enterpriseSearch.workplaceSearch.groups.newGroup.action": "管理组", "xpack.enterpriseSearch.workplaceSearch.groups.newGroupSavedSuccess": "已成功创建 {groupName}", "xpack.enterpriseSearch.workplaceSearch.groups.noSourcesMessage": "无组织内容源", - "xpack.enterpriseSearch.workplaceSearch.groups.noUsersMessage": "无用户", "xpack.enterpriseSearch.workplaceSearch.groups.overview.confirmRemoveButtonText": "删除 {name}", "xpack.enterpriseSearch.workplaceSearch.groups.overview.confirmRemoveDescription": "您的组将从 Workplace Search 中删除。确定要移除 {name}?", "xpack.enterpriseSearch.workplaceSearch.groups.overview.confirmTitleText": "确认", @@ -20000,13 +19967,8 @@ "xpack.ml.trainedModels.testModelsFlyout.headerLabel": "测试已训练模型", "xpack.ml.trainedModels.testModelsFlyout.inferenceError": "发生错误", "xpack.ml.trainedModels.testModelsFlyout.langIdent.inputText": "输入文本", - "xpack.ml.trainedModels.testModelsFlyout.langIdent.markupTab": "输出", - "xpack.ml.trainedModels.testModelsFlyout.langIdent.output.language_title": "语言", - "xpack.ml.trainedModels.testModelsFlyout.langIdent.output.probability_title": "可能性", "xpack.ml.trainedModels.testModelsFlyout.langIdent.output.title": "这像是 {lang}", "xpack.ml.trainedModels.testModelsFlyout.langIdent.output.titleUnknown": "语言代码未知:{code}", - "xpack.ml.trainedModels.testModelsFlyout.langIdent.rawOutput": "原始输出", - "xpack.ml.trainedModels.testModelsFlyout.langIdent.runButton": "测试", "xpack.ml.trainedModels.testModelsFlyout.ner.output.probabilityTitle": "可能性", "xpack.ml.trainedModels.testModelsFlyout.ner.output.typeTitle": "类型", "xpack.ml.trainedModelsBreadcrumbs.nodeOverviewLabel": "节点", @@ -21848,12 +21810,6 @@ "xpack.observability.noDataConfig.beatsCard.title": "添加集成", "xpack.observability.noDataConfig.solutionName": "Observability", "xpack.observability.notAvailable": "不可用", - "xpack.observability.overview.alert.allTypes": "所有类型", - "xpack.observability.overview.alert.appLink": "显示所有告警", - "xpack.observability.overview.alert.errorMessage": "加载告警数据时出现错误。请稍后重试。", - "xpack.observability.overview.alert.errorTitle": "我们无法加载告警数据", - "xpack.observability.overview.alerts.appLink": "显示告警", - "xpack.observability.overview.alerts.muted": "已静音", "xpack.observability.overview.alerts.title": "告警", "xpack.observability.overview.apm.appLink": "显示服务库存", "xpack.observability.overview.apm.services": "服务", @@ -21938,13 +21894,10 @@ "xpack.observability.rules.rulesTable.columns.statusTitle": "状态", "xpack.observability.rules.rulesTable.pluralTitle": "规则", "xpack.observability.rules.rulesTable.ruleStatusActive": "活动", - "xpack.observability.rules.rulesTable.ruleStatusDisabled": "已禁用", - "xpack.observability.rules.rulesTable.ruleStatusEnabled": "已启用", "xpack.observability.rules.rulesTable.ruleStatusError": "错误", "xpack.observability.rules.rulesTable.ruleStatusLicenseError": "许可证错误", "xpack.observability.rules.rulesTable.ruleStatusOk": "确定", "xpack.observability.rules.rulesTable.ruleStatusPending": "待处理", - "xpack.observability.rules.rulesTable.ruleStatusSnoozedIndefinitely": "已无限期暂停", "xpack.observability.rules.rulesTable.ruleStatusUnknown": "未知", "xpack.observability.rules.rulesTable.ruleStatusWarning": "警告", "xpack.observability.rules.rulesTable.singleTitle": "规则", @@ -22159,9 +22112,6 @@ "xpack.osquery.liveQueryDetails.viewLiveQueriesHistoryTitle": "查看实时查询历史记录", "xpack.osquery.liveQueryForm.form.saveForLaterButtonLabel": "保存,以后继续", "xpack.osquery.liveQueryForm.form.submitButtonLabel": "提交", - "xpack.osquery.liveQueryForm.steps.agentsStepHeading": "选择代理", - "xpack.osquery.liveQueryForm.steps.queryStepHeading": "输入查询", - "xpack.osquery.liveQueryForm.steps.resultsStepHeading": "检查结果", "xpack.osquery.liveQueryResults.table.agentColumnTitle": "代理", "xpack.osquery.liveQueryResults.table.fieldMappedLabel": "字段已映射到", "xpack.osquery.newLiveQuery.pageTitle": "新建实时查询", @@ -22643,7 +22593,6 @@ "xpack.reporting.publicNotifier.csvContainsFormulas.formulaReportMessage": "报告“{reportObjectTitle}”包含电子表格应用程序可解释为公式的字符。", "xpack.reporting.publicNotifier.csvContainsFormulas.formulaReportTitle": "{reportType} 可能包含公式", "xpack.reporting.publicNotifier.downloadReportButtonLabel": "下载报告", - "xpack.reporting.publicNotifier.error.calloutTitle": "报告作业失败", "xpack.reporting.publicNotifier.error.checkManagement": "前往 {path} 了解详情。", "xpack.reporting.publicNotifier.error.couldNotCreateReportTitle": "无法为“{reportObjectTitle}”创建 {reportType} 报告。", "xpack.reporting.publicNotifier.error.reportingSectionUrlLinkLabel": "堆栈管理 > Kibana > Reporting", @@ -23995,13 +23944,10 @@ "xpack.securitySolution.console.builtInCommands.clearAbout": "清除控制台缓冲区", "xpack.securitySolution.console.builtInCommands.helpAbout": "查看可用命令列表", "xpack.securitySolution.console.commandList.footerText": "有关上述命令的更多详情,请使用 {helpOption} 参数。示例:{cmdExample}", - "xpack.securitySolution.console.commandOutput.runInBackgroundButtonLabel": "在后台运行", - "xpack.securitySolution.console.commandOutput.runInBackgroundMsg": "命令响应花费的时间略长。单击此处以在后台运行,并在收到响应时发送通知", "xpack.securitySolution.console.commandUsage.atLeastOneOptionRequiredMessage": "注意:必须至少使用一个选项", "xpack.securitySolution.console.commandUsage.inputUsage": "用法:", "xpack.securitySolution.console.commandUsage.optionsLabel": "选项:", "xpack.securitySolution.console.commandValidation.argSupportedOnlyOnce": "参数只能使用一次:{argName}", - "xpack.securitySolution.console.commandValidation.cmdHelpTitle": "{cmdName} 命令", "xpack.securitySolution.console.commandValidation.invalidArgValue": "无效的参数值:{argName}。{error}", "xpack.securitySolution.console.commandValidation.missingRequiredArg": "缺少所需参数:{argName}", "xpack.securitySolution.console.commandValidation.mustHaveArgs": "缺少所需参数:{requiredArgs}", @@ -26081,7 +26027,6 @@ "xpack.securitySolution.hosts.hostScoreOverTime.riskScore": "风险分数", "xpack.securitySolution.hosts.hostScoreOverTime.riskyLabel": "有风险", "xpack.securitySolution.hosts.hostScoreOverTime.riskyThresholdHeader": "有风险的阈值", - "xpack.securitySolution.hosts.hostScoreOverTime.title": "一段时间的主机风险分数", "xpack.securitySolution.hosts.kqlPlaceholder": "例如 host.name:“foo”", "xpack.securitySolution.hosts.navigation.alertsTitle": "外部告警", "xpack.securitySolution.hosts.navigation.allHostsTitle": "所有主机", @@ -29486,7 +29431,6 @@ "xpack.triggersActionsUI.sections.rulesList.ruleStatusActive": "活动", "xpack.triggersActionsUI.sections.rulesList.ruleStatusDropdownMenuLabel": "更改规则状态或暂停", "xpack.triggersActionsUI.sections.rulesList.ruleStatusError": "错误", - "xpack.triggersActionsUI.sections.rulesList.ruleStatusFilterLabel": "上次响应", "xpack.triggersActionsUI.sections.rulesList.ruleStatusLicenseError": "许可证错误", "xpack.triggersActionsUI.sections.rulesList.ruleStatusOk": "确定", "xpack.triggersActionsUI.sections.rulesList.ruleStatusPending": "待处理", @@ -31260,54 +31204,34 @@ "xpack.watcher.watchActions.webhook.usernameIsRequiredIfPasswordValidationMessage": "“用户名”必填。", "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "此字段必填。", "xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。", - "unifiedSearch.search.searchBar.savedQueryDescriptionLabelText": "描述", "unifiedSearch.search.searchBar.savedQueryDescriptionText": "保存想要再次使用的查询文本和筛选。", "unifiedSearch.search.searchBar.savedQueryForm.titleConflictText": "标题与现有已保存查询有冲突", - "unifiedSearch.search.searchBar.savedQueryFormCancelButtonText": "取消", "unifiedSearch.search.searchBar.savedQueryFormSaveButtonText": "保存", - "unifiedSearch.search.searchBar.savedQueryFormTitle": "保存查询", "unifiedSearch.search.searchBar.savedQueryIncludeFiltersLabelText": "包括筛选", "unifiedSearch.search.searchBar.savedQueryIncludeTimeFilterLabelText": "包括时间筛选", "unifiedSearch.search.searchBar.savedQueryNameHelpText": "名称必填,其中不能包含前导或尾随空格,并且必须唯一。", "unifiedSearch.search.searchBar.savedQueryNameLabelText": "名称", "unifiedSearch.search.searchBar.savedQueryNoSavedQueriesText": "没有已保存查询。", - "unifiedSearch.search.searchBar.savedQueryPopoverButtonText": "查看已保存查询", - "unifiedSearch.search.searchBar.savedQueryPopoverClearButtonAriaLabel": "清除当前已保存查询", - "unifiedSearch.search.searchBar.savedQueryPopoverClearButtonText": "清除", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionCancelButtonText": "取消", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionConfirmButtonText": "删除", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionTitle": "删除“{savedQueryName}”?", - "unifiedSearch.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel": "删除已保存查询 {savedQueryName}", "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel": "另存为新的已保存查询", "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonText": "另存为新的", - "unifiedSearch.search.searchBar.savedQueryPopoverSaveButtonAriaLabel": "保存新的已保存查询", - "unifiedSearch.search.searchBar.savedQueryPopoverSaveButtonText": "保存当前查询", "unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonAriaLabel": "将更改保存到 {title}", "unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonText": "保存更改", - "unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemButtonAriaLabel": "已保存查询按钮 {savedQueryName}", - "unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemDescriptionAriaLabel": "{savedQueryName} 描述", - "unifiedSearch.search.searchBar.savedQueryPopoverSavedQueryListItemSelectedButtonAriaLabel": "已保存查询按钮已选择 {savedQueryName}。按下可清除任何更改。", - "unifiedSearch.search.searchBar.savedQueryPopoverTitleText": "已保存查询", "unifiedSearch.noDataPopover.content": "此时间范围不包含任何数据。增大或调整时间范围,以查看更多的字段并创建图表。", "unifiedSearch.noDataPopover.dismissAction": "不再显示", "unifiedSearch.noDataPopover.subtitle": "提示", "unifiedSearch.noDataPopover.title": "空数据集", "unifiedSearch.query.queryBar.clearInputLabel": "清除输入", "unifiedSearch.query.queryBar.comboboxAriaLabel": "搜索并筛选 {pageType} 页面", - "unifiedSearch.query.queryBar.kqlFullLanguageName": "Kibana 查询语言", "unifiedSearch.query.queryBar.kqlLanguageName": "KQL", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoDocLinkText": "文档", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoOptOutText": "不再显示", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoText": "似乎您正在查询嵌套字段。您可以使用不同的方式构造嵌套查询的 KQL 语法,具体取决于您想要的结果。详细了解我们的 {link}。", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoTitle": "KQL 嵌套查询语法", - "unifiedSearch.query.queryBar.kqlOffLabel": "关闭", - "unifiedSearch.query.queryBar.kqlOnLabel": "开启", - "unifiedSearch.query.queryBar.languageSwitcher.toText": "切换到 Kibana 查询语言以进行搜索", "unifiedSearch.query.queryBar.luceneLanguageName": "Lucene", "unifiedSearch.query.queryBar.searchInputAriaLabel": "开始键入内容,以搜索并筛选 {pageType} 页面", - "unifiedSearch.query.queryBar.searchInputPlaceholder": "搜索", - "unifiedSearch.query.queryBar.syntaxOptionsDescription": "{docsLink} (KQL) 提供简化查询语法并支持脚本字段。KQL 还提供自动完成功能。如果关闭 KQL,{nonKqlModeHelpText}", - "unifiedSearch.query.queryBar.syntaxOptionsDescription.nonKqlModeHelpText": "Kibana 使用 Lucene。", "unifiedSearch.search.unableToGetSavedQueryToastTitle": "无法加载已保存查询 {savedQueryId}", "unifiedSearch.query.queryBar.syntaxOptionsTitle": "语法选项", "unifiedSearch.filter.applyFilterActionTitle": "将筛选应用于当前视图", @@ -31360,22 +31284,17 @@ "unifiedSearch.filter.filterEditor.rangeEndInputPlaceholder": "范围结束", "unifiedSearch.filter.filterEditor.rangeInputLabel": "范围", "unifiedSearch.filter.filterEditor.rangeStartInputPlaceholder": "范围开始", - "unifiedSearch.filter.filterEditor.saveButtonLabel": "保存", "unifiedSearch.filter.filterEditor.trueOptionLabel": "true", "unifiedSearch.filter.filterEditor.valueInputLabel": "值", "unifiedSearch.filter.filterEditor.valueInputPlaceholder": "输入值", "unifiedSearch.filter.filterEditor.valueSelectPlaceholder": "选择值", "unifiedSearch.filter.filterEditor.valuesSelectLabel": "值", "unifiedSearch.filter.filterEditor.valuesSelectPlaceholder": "选择值", - "unifiedSearch.filter.options.changeAllFiltersButtonLabel": "更改所有筛选", - "unifiedSearch.filter.options.deleteAllFiltersButtonLabel": "全部删除", "unifiedSearch.filter.options.disableAllFiltersButtonLabel": "全部禁用", "unifiedSearch.filter.options.enableAllFiltersButtonLabel": "全部启用", - "unifiedSearch.filter.options.invertDisabledFiltersButtonLabel": "反向已启用/已禁用", "unifiedSearch.filter.options.invertNegatedFiltersButtonLabel": "反向包括", "unifiedSearch.filter.options.pinAllFiltersButtonLabel": "全部固定", "unifiedSearch.filter.options.unpinAllFiltersButtonLabel": "全部取消固定", - "unifiedSearch.filter.searchBar.changeAllFiltersTitle": "更改所有筛选", "unifiedSearch.kueryAutocomplete.andOperatorDescription": "需要{bothArguments}为 true", "unifiedSearch.kueryAutocomplete.andOperatorDescription.bothArgumentsText": "两个参数都", "unifiedSearch.kueryAutocomplete.equalOperatorDescription": "{equals}某一值", diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md index f4a1f79da1546..7fde1f1512302 100644 --- a/x-pack/plugins/triggers_actions_ui/README.md +++ b/x-pack/plugins/triggers_actions_ui/README.md @@ -252,7 +252,8 @@ Each alert type should be defined as `RuleTypeModel` object with the these prope |iconClass|Icon of the alert type that will be displayed on the select card in the UI.| |validate|Validation function for the alert params.| |ruleParamsExpression| A lazy loaded React component for building UI of the current alert type params.| -|defaultActionMessage|Optional property for providing default message for all added actions with `message` property.| +|defaultActionMessage|Optional property for providing default messages for all added actions, excluding the Recovery action group, with `message` property. | +|defaultRecoveryMessage|Optional property for providing a default message for all added actions with `message` property for the Recovery action group.| |requiresAppContext|Define if alert type is enabled for create and edit in the alerting management UI.| IMPORTANT: The current UI supports a single action group only. diff --git a/x-pack/plugins/triggers_actions_ui/common/experimental_features.ts b/x-pack/plugins/triggers_actions_ui/common/experimental_features.ts index 21835a5977216..33f5fdc44afcd 100644 --- a/x-pack/plugins/triggers_actions_ui/common/experimental_features.ts +++ b/x-pack/plugins/triggers_actions_ui/common/experimental_features.ts @@ -15,6 +15,8 @@ export const allowedExperimentalValues = Object.freeze({ rulesListDatagrid: true, internalAlertsTable: false, internalShareableComponentsSandbox: false, + ruleTagFilter: false, + ruleStatusFilter: false, rulesDetailLogs: true, }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx index 71bcb2ee7d760..c4c273bd003c5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx @@ -31,7 +31,7 @@ import { } from '../types'; import { Section, routeToRuleDetails, legacyRouteToRuleDetails } from './constants'; -import { setSavedObjectsClient } from '../common/lib/data_apis'; +import { setDataViewsService } from '../common/lib/data_apis'; import { KibanaContextProvider } from '../common/lib/kibana'; const TriggersActionsUIHome = lazy(() => import('./home')); @@ -67,12 +67,12 @@ export const renderApp = (deps: TriggersAndActionsUiServices) => { }; export const App = ({ deps }: { deps: TriggersAndActionsUiServices }) => { - const { savedObjects, uiSettings, theme$ } = deps; + const { dataViews, uiSettings, theme$ } = deps; const sections: Section[] = ['rules', 'connectors', 'alerts', '__components_sandbox']; const isDarkMode = useObservable(uiSettings.get$('theme:darkMode')); const sectionsRegex = sections.join('|'); - setSavedObjectsClient(savedObjects.client); + setDataViewsService(dataViews); return ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rule_status_filter_sandbox.tsx b/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rule_status_filter_sandbox.tsx new file mode 100644 index 0000000000000..99ddd8daf16ac --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rule_status_filter_sandbox.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import { RuleStatusFilterProps } from '../../../types'; +import { getRuleStatusFilterLazy } from '../../../common/get_rule_status_filter'; + +export const RuleStatusFilterSandbox = () => { + const [selectedStatuses, setSelectedStatuses] = useState< + RuleStatusFilterProps['selectedStatuses'] + >([]); + + return ( +
+ {getRuleStatusFilterLazy({ + selectedStatuses, + onChange: setSelectedStatuses, + })} +
Selected states: {JSON.stringify(selectedStatuses)}
+
+ ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rule_tag_badge_sandbox.tsx b/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rule_tag_badge_sandbox.tsx new file mode 100644 index 0000000000000..097fb00969d27 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rule_tag_badge_sandbox.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * Note: This exists only temporarily as we refactor to remove the sandboxes + * to its own plugin. Ideally we do not want to use an internal page within + * the triggers_actions_ui plugin + */ + +import React, { useState } from 'react'; +import { getRuleTagBadgeLazy } from '../../../common/get_rule_tag_badge'; + +const tags = ['tag1', 'tag2', 'tag3', 'tag4']; + +export const RuleTagBadgeSandbox = () => { + const [isOpen, setIsOpen] = useState(false); + + return ( +
+ {getRuleTagBadgeLazy({ + isOpen, + tags, + onClick: () => setIsOpen(true), + onClose: () => setIsOpen(false), + })} +
+ ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rule_tag_filter_sandbox.tsx b/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rule_tag_filter_sandbox.tsx new file mode 100644 index 0000000000000..58603fdb8f178 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rule_tag_filter_sandbox.tsx @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useState } from 'react'; +import { EuiSpacer } from '@elastic/eui'; +import { getRuleTagFilterLazy } from '../../../common/get_rule_tag_filter'; + +export const RuleTagFilterSandbox = () => { + const [selectedTags, setSelectedTags] = useState([]); + + return ( +
+ {getRuleTagFilterLazy({ + tags: ['tag1', 'tag2', 'tag3', 'tag4'], + selectedTags, + onChange: setSelectedTags, + })} + +
selected tags: {JSON.stringify(selectedTags)}
+
+ ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/shareable_components_sandbox.tsx b/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/shareable_components_sandbox.tsx index 97366832bda0e..668b1ccb5aa69 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/shareable_components_sandbox.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/shareable_components_sandbox.tsx @@ -7,11 +7,17 @@ import React from 'react'; import { RuleStatusDropdownSandbox } from './rule_status_dropdown_sandbox'; +import { RuleTagFilterSandbox } from './rule_tag_filter_sandbox'; +import { RuleStatusFilterSandbox } from './rule_status_filter_sandbox'; +import { RuleTagBadgeSandbox } from './rule_tag_badge_sandbox'; export const InternalShareableComponentsSandbox: React.FC<{}> = () => { return ( <> + + + ); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts index 74b8243519428..bc4957b65b1bc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts @@ -29,3 +29,5 @@ export function hasAllPrivilege(rule: InitialRule, ruleType?: RuleType): boolean export function hasReadPrivilege(rule: InitialRule, ruleType?: RuleType): boolean { return ruleType?.authorizedConsumers[rule.consumer]?.read ?? false; } +export const hasManageApiKeysCapability = (capabilities: Capabilities) => + capabilities?.management?.security?.api_keys; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.test.ts index 46653e5bc3911..5377e4269f46e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.test.ts @@ -6,7 +6,7 @@ */ import { httpServiceMock } from '@kbn/core/public/mocks'; -import { loadRuleAggregations } from './aggregate'; +import { loadRuleAggregations, loadRuleTags } from './aggregate'; const http = httpServiceMock.createStartContract(); @@ -209,4 +209,148 @@ describe('loadRuleAggregations', () => { ] `); }); + + test('should call aggregate API with ruleStatusesFilter', async () => { + const resolvedValue = { + rule_execution_status: { + ok: 4, + active: 2, + error: 1, + pending: 1, + unknown: 0, + }, + }; + http.get.mockResolvedValue(resolvedValue); + + let result = await loadRuleAggregations({ + http, + ruleStatusesFilter: ['enabled'], + }); + + expect(result).toEqual({ + ruleExecutionStatus: { + ok: 4, + active: 2, + error: 1, + pending: 1, + unknown: 0, + }, + }); + + expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/_aggregate", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": "alert.attributes.enabled:(true) and not (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)", + "search": undefined, + "search_fields": undefined, + }, + }, + ] + `); + + result = await loadRuleAggregations({ + http, + ruleStatusesFilter: ['enabled', 'snoozed'], + }); + + expect(http.get.mock.calls[1]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/_aggregate", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": "alert.attributes.enabled:(true) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)", + "search": undefined, + "search_fields": undefined, + }, + }, + ] + `); + + result = await loadRuleAggregations({ + http, + ruleStatusesFilter: ['enabled', 'disabled', 'snoozed'], + }); + + expect(http.get.mock.calls[1]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/_aggregate", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": "alert.attributes.enabled:(true) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)", + "search": undefined, + "search_fields": undefined, + }, + }, + ] + `); + }); + + test('should call aggregate API with tagsFilter', async () => { + const resolvedValue = { + rule_execution_status: { + ok: 4, + active: 2, + error: 1, + pending: 1, + unknown: 0, + }, + }; + http.get.mockResolvedValueOnce(resolvedValue); + + const result = await loadRuleAggregations({ + http, + searchText: 'baz', + tagsFilter: ['a', 'b', 'c'], + }); + + expect(result).toEqual({ + ruleExecutionStatus: { + ok: 4, + active: 2, + error: 1, + pending: 1, + unknown: 0, + }, + }); + + expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/_aggregate", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": "alert.attributes.tags:(a or b or c)", + "search": "baz", + "search_fields": "[\\"name\\",\\"tags\\"]", + }, + }, + ] + `); + }); + + test('loadRuleTags should call the aggregate API with no filters', async () => { + const resolvedValue = { + rule_tags: ['a', 'b', 'c'], + }; + http.get.mockResolvedValueOnce(resolvedValue); + + const result = await loadRuleTags({ + http, + }); + + expect(result).toEqual({ + ruleTags: ['a', 'b', 'c'], + }); + + expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/_aggregate", + ] + `); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.ts index c7bcd438ef697..1df6177443657 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/aggregate.ts @@ -6,15 +6,20 @@ */ import { HttpSetup } from '@kbn/core/public'; import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; -import { RuleAggregations } from '../../../types'; +import { RuleAggregations, RuleStatus } from '../../../types'; import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants'; import { mapFiltersToKql } from './map_filters_to_kql'; +export interface RuleTagsAggregations { + ruleTags: string[]; +} + const rewriteBodyRes: RewriteRequestCase = ({ rule_execution_status: ruleExecutionStatus, rule_enabled_status: ruleEnabledStatus, rule_muted_status: ruleMutedStatus, rule_snoozed_status: ruleSnoozedStatus, + rule_tags: ruleTags, ...rest }: any) => ({ ...rest, @@ -22,22 +27,47 @@ const rewriteBodyRes: RewriteRequestCase = ({ ruleEnabledStatus, ruleMutedStatus, ruleSnoozedStatus, + ruleTags, +}); + +const rewriteTagsBodyRes: RewriteRequestCase = ({ + rule_tags: ruleTags, +}: any) => ({ + ruleTags, }); +// TODO: https://github.com/elastic/kibana/issues/131682 +export async function loadRuleTags({ http }: { http: HttpSetup }): Promise { + const res = await http.get>( + `${INTERNAL_BASE_ALERTING_API_PATH}/rules/_aggregate` + ); + return rewriteTagsBodyRes(res); +} + export async function loadRuleAggregations({ http, searchText, typesFilter, actionTypesFilter, + ruleExecutionStatusesFilter, ruleStatusesFilter, + tagsFilter, }: { http: HttpSetup; searchText?: string; typesFilter?: string[]; actionTypesFilter?: string[]; - ruleStatusesFilter?: string[]; + ruleExecutionStatusesFilter?: string[]; + ruleStatusesFilter?: RuleStatus[]; + tagsFilter?: string[]; }): Promise { - const filters = mapFiltersToKql({ typesFilter, actionTypesFilter, ruleStatusesFilter }); + const filters = mapFiltersToKql({ + typesFilter, + actionTypesFilter, + ruleExecutionStatusesFilter, + ruleStatusesFilter, + tagsFilter, + }); const res = await http.get>( `${INTERNAL_BASE_ALERTING_API_PATH}/rules/_aggregate`, { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/index.ts index 89ede79f4a21d..c9834dd140ea4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/index.ts @@ -7,7 +7,7 @@ export { alertingFrameworkHealth } from './health'; export { mapFiltersToKql } from './map_filters_to_kql'; -export { loadRuleAggregations } from './aggregate'; +export { loadRuleAggregations, loadRuleTags } from './aggregate'; export { createRule } from './create'; export { deleteRules } from './delete'; export { disableRule, disableRules } from './disable'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/map_filters_to_kql.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/map_filters_to_kql.test.ts index e1dd14a7a9fde..f67a27ef5409c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/map_filters_to_kql.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/map_filters_to_kql.test.ts @@ -32,14 +32,70 @@ describe('mapFiltersToKql', () => { ]); }); - test('should handle ruleStatusesFilter', () => { + test('should handle ruleExecutionStatusesFilter', () => { expect( mapFiltersToKql({ - ruleStatusesFilter: ['alert', 'statuses', 'filter'], + ruleExecutionStatusesFilter: ['alert', 'statuses', 'filter'], }) ).toEqual(['alert.attributes.executionStatus.status:(alert or statuses or filter)']); }); + test('should handle ruleStatusesFilter', () => { + expect( + mapFiltersToKql({ + ruleStatusesFilter: ['enabled'], + }) + ).toEqual([ + 'alert.attributes.enabled:(true) and not (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)', + ]); + + expect( + mapFiltersToKql({ + ruleStatusesFilter: ['disabled'], + }) + ).toEqual([ + 'alert.attributes.enabled:(false) and not (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)', + ]); + + expect( + mapFiltersToKql({ + ruleStatusesFilter: ['snoozed'], + }) + ).toEqual(['(alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)']); + + expect( + mapFiltersToKql({ + ruleStatusesFilter: ['enabled', 'snoozed'], + }) + ).toEqual([ + 'alert.attributes.enabled:(true) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)', + ]); + + expect( + mapFiltersToKql({ + ruleStatusesFilter: ['disabled', 'snoozed'], + }) + ).toEqual([ + 'alert.attributes.enabled:(false) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)', + ]); + + expect( + mapFiltersToKql({ + ruleStatusesFilter: ['enabled', 'disabled', 'snoozed'], + }) + ).toEqual([ + 'alert.attributes.enabled:(true or false) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)', + ]); + }); + + test('should handle tagsFilter', () => { + expect( + mapFiltersToKql({ + tagsFilter: ['a', 'b', 'c'], + }) + ).toEqual(['alert.attributes.tags:(a or b or c)']); + }); + test('should handle typesFilter and actionTypesFilter', () => { expect( mapFiltersToKql({ @@ -52,17 +108,19 @@ describe('mapFiltersToKql', () => { ]); }); - test('should handle typesFilter, actionTypesFilter and ruleStatusesFilter', () => { + test('should handle typesFilter, actionTypesFilter, ruleExecutionStatusesFilter, and tagsFilter', () => { expect( mapFiltersToKql({ typesFilter: ['type', 'filter'], actionTypesFilter: ['action', 'types', 'filter'], - ruleStatusesFilter: ['alert', 'statuses', 'filter'], + ruleExecutionStatusesFilter: ['alert', 'statuses', 'filter'], + tagsFilter: ['a', 'b', 'c'], }) ).toEqual([ 'alert.attributes.alertTypeId:(type or filter)', '(alert.attributes.actions:{ actionTypeId:action } OR alert.attributes.actions:{ actionTypeId:types } OR alert.attributes.actions:{ actionTypeId:filter })', 'alert.attributes.executionStatus.status:(alert or statuses or filter)', + 'alert.attributes.tags:(a or b or c)', ]); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/map_filters_to_kql.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/map_filters_to_kql.ts index d7b22a7a4aee4..ff2a49e3a5e45 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/map_filters_to_kql.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/map_filters_to_kql.ts @@ -5,16 +5,36 @@ * 2.0. */ +import { RuleStatus } from '../../../types'; + +const getEnablementFilter = (ruleStatusFilter: RuleStatus[] = []) => { + const enablementFilters = ruleStatusFilter.reduce((result, filter) => { + if (filter === 'enabled') { + return [...result, 'true']; + } + if (filter === 'disabled') { + return [...result, 'false']; + } + return result; + }, []); + return `alert.attributes.enabled:(${enablementFilters.join(' or ')})`; +}; + export const mapFiltersToKql = ({ typesFilter, actionTypesFilter, + ruleExecutionStatusesFilter, ruleStatusesFilter, + tagsFilter, }: { typesFilter?: string[]; actionTypesFilter?: string[]; - ruleStatusesFilter?: string[]; + tagsFilter?: string[]; + ruleExecutionStatusesFilter?: string[]; + ruleStatusesFilter?: RuleStatus[]; }): string[] => { const filters = []; + if (typesFilter && typesFilter.length) { filters.push(`alert.attributes.alertTypeId:(${typesFilter.join(' or ')})`); } @@ -29,8 +49,30 @@ export const mapFiltersToKql = ({ ].join('') ); } + if (ruleExecutionStatusesFilter && ruleExecutionStatusesFilter.length) { + filters.push( + `alert.attributes.executionStatus.status:(${ruleExecutionStatusesFilter.join(' or ')})` + ); + } + if (ruleStatusesFilter && ruleStatusesFilter.length) { - filters.push(`alert.attributes.executionStatus.status:(${ruleStatusesFilter.join(' or ')})`); + const enablementFilter = getEnablementFilter(ruleStatusesFilter); + const snoozedFilter = `(alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)`; + const hasEnablement = + ruleStatusesFilter.includes('enabled') || ruleStatusesFilter.includes('disabled'); + const hasSnoozed = ruleStatusesFilter.includes('snoozed'); + + if (hasEnablement && !hasSnoozed) { + filters.push(`${enablementFilter} and not ${snoozedFilter}`); + } else if (!hasEnablement && hasSnoozed) { + filters.push(snoozedFilter); + } else { + filters.push(`${enablementFilter} or ${snoozedFilter}`); + } } + if (tagsFilter && tagsFilter.length) { + filters.push(`alert.attributes.tags:(${tagsFilter.join(' or ')})`); + } + return filters; }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules.test.ts index 5f6c6e938a0a7..2a20c9d9469f5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules.test.ts @@ -239,4 +239,139 @@ describe('loadRules', () => { ] `); }); + + test('should call find API with ruleStatusesilter', async () => { + const resolvedValue = { + page: 1, + per_page: 10, + total: 0, + data: [], + }; + http.get.mockResolvedValue(resolvedValue); + + let result = await loadRules({ + http, + ruleStatusesFilter: ['enabled', 'snoozed'], + page: { index: 0, size: 10 }, + }); + expect(result).toEqual({ + page: 1, + perPage: 10, + total: 0, + data: [], + }); + expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/_find", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": "alert.attributes.enabled:(true) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)", + "page": 1, + "per_page": 10, + "search": undefined, + "search_fields": undefined, + "sort_field": "name", + "sort_order": "asc", + }, + }, + ] + `); + + result = await loadRules({ + http, + ruleStatusesFilter: ['disabled'], + page: { index: 0, size: 10 }, + }); + expect(result).toEqual({ + page: 1, + perPage: 10, + total: 0, + data: [], + }); + expect(http.get.mock.calls[1]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/_find", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": "alert.attributes.enabled:(false) and not (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)", + "page": 1, + "per_page": 10, + "search": undefined, + "search_fields": undefined, + "sort_field": "name", + "sort_order": "asc", + }, + }, + ] + `); + + result = await loadRules({ + http, + ruleStatusesFilter: ['enabled', 'disabled', 'snoozed'], + page: { index: 0, size: 10 }, + }); + expect(result).toEqual({ + page: 1, + perPage: 10, + total: 0, + data: [], + }); + expect(http.get.mock.calls[2]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/_find", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": "alert.attributes.enabled:(true or false) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)", + "page": 1, + "per_page": 10, + "search": undefined, + "search_fields": undefined, + "sort_field": "name", + "sort_order": "asc", + }, + }, + ] + `); + }); + + test('should call find API with tagsFilter', async () => { + const resolvedValue = { + page: 1, + per_page: 10, + total: 0, + data: [], + }; + http.get.mockResolvedValueOnce(resolvedValue); + const result = await loadRules({ + http, + tagsFilter: ['a', 'b', 'c'], + page: { index: 0, size: 10 }, + }); + expect(result).toEqual({ + page: 1, + perPage: 10, + total: 0, + data: [], + }); + expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/_find", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": "alert.attributes.tags:(a or b or c)", + "page": 1, + "per_page": 10, + "search": undefined, + "search_fields": undefined, + "sort_field": "name", + "sort_order": "asc", + }, + }, + ] + `); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules.ts index 52ba09a5c0adf..6e527989cc91f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules.ts @@ -7,7 +7,7 @@ import { HttpSetup } from '@kbn/core/public'; import { AsApiContract } from '@kbn/actions-plugin/common'; import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants'; -import { Rule, Pagination, Sorting } from '../../../types'; +import { Rule, Pagination, Sorting, RuleStatus } from '../../../types'; import { mapFiltersToKql } from './map_filters_to_kql'; import { transformRule } from './common_transformations'; @@ -21,7 +21,9 @@ export async function loadRules({ searchText, typesFilter, actionTypesFilter, + ruleExecutionStatusesFilter, ruleStatusesFilter, + tagsFilter, sort = { field: 'name', direction: 'asc' }, }: { http: HttpSetup; @@ -29,7 +31,9 @@ export async function loadRules({ searchText?: string; typesFilter?: string[]; actionTypesFilter?: string[]; - ruleStatusesFilter?: string[]; + tagsFilter?: string[]; + ruleExecutionStatusesFilter?: string[]; + ruleStatusesFilter?: RuleStatus[]; sort?: Sorting; }): Promise<{ page: number; @@ -37,7 +41,13 @@ export async function loadRules({ total: number; data: Rule[]; }> { - const filters = mapFiltersToKql({ typesFilter, actionTypesFilter, ruleStatusesFilter }); + const filters = mapFiltersToKql({ + typesFilter, + actionTypesFilter, + tagsFilter, + ruleExecutionStatusesFilter, + ruleStatusesFilter, + }); const res = await http.get< AsApiContract<{ page: number; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.test.tsx new file mode 100644 index 0000000000000..08b68bd342a5b --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; +import { act } from 'react-dom/test-utils'; +import { AlertsFlyout } from './alerts_flyout'; +import { AlertsField } from '../../../../types'; + +const onClose = jest.fn(); +const onPaginate = jest.fn(); +const props = { + alert: { + [AlertsField.name]: ['one'], + [AlertsField.reason]: ['two'], + }, + flyoutIndex: 0, + alertsCount: 4, + isLoading: false, + onClose, + onPaginate, +}; + +describe('AlertsFlyout', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should render high level details from the alert', async () => { + const wrapper = mountWithIntl(); + await act(async () => { + await nextTick(); + wrapper.update(); + }); + expect(wrapper.find('[data-test-subj="alertsFlyoutName"]').first().text()).toBe('one'); + expect(wrapper.find('[data-test-subj="alertsFlyoutReason"]').first().text()).toBe('two'); + }); + + it('should allow pagination with next', async () => { + const wrapper = mountWithIntl(); + await act(async () => { + await nextTick(); + wrapper.update(); + }); + wrapper.find('[data-test-subj="pagination-button-next"]').first().simulate('click'); + expect(onPaginate).toHaveBeenCalledWith(1); + }); + + it('should allow pagination with previous', async () => { + const customProps = { + ...props, + flyoutIndex: 1, + }; + const wrapper = mountWithIntl(); + await act(async () => { + await nextTick(); + wrapper.update(); + }); + wrapper.find('[data-test-subj="pagination-button-previous"]').first().simulate('click'); + expect(onPaginate).toHaveBeenCalledWith(0); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.tsx new file mode 100644 index 0000000000000..44236a8d993f5 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/alerts_flyout.tsx @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { get } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiSpacer, + EuiTitle, + EuiText, + EuiHorizontalRule, + EuiFlexGroup, + EuiFlexItem, + EuiPagination, + EuiProgress, + EuiLoadingContent, +} from '@elastic/eui'; +import { AlertsField, AlertsData } from '../../../../types'; + +const SAMPLE_TITLE_LABEL = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.sampleTitle', + { + defaultMessage: 'Sample title', + } +); + +const NAME_LABEL = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.name', + { + defaultMessage: 'Name', + } +); + +const REASON_LABEL = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.reason', + { + defaultMessage: 'Reason', + } +); + +const PAGINATION_LABEL = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsTable.alertsFlyout.paginationLabel', + { + defaultMessage: 'Alert navigation', + } +); + +interface AlertsFlyoutProps { + alert: AlertsData; + flyoutIndex: number; + alertsCount: number; + isLoading: boolean; + onClose: () => void; + onPaginate: (pageIndex: number) => void; +} +export const AlertsFlyout: React.FunctionComponent = ({ + alert, + flyoutIndex, + alertsCount, + isLoading, + onClose, + onPaginate, +}: AlertsFlyoutProps) => { + return ( + + {isLoading && } + + +

{SAMPLE_TITLE_LABEL}

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

{NAME_LABEL}

+
+ + {isLoading ? ( + + ) : ( + + {get(alert, AlertsField.name, [])[0]} + + )} +
+ + +

{REASON_LABEL}

+
+ + {isLoading ? ( + + ) : ( + + {get(alert, AlertsField.reason, [])[0]} + + )} +
+
+ + +
+
+ ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/index.ts new file mode 100644 index 0000000000000..f1f90cc602c58 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_flyout/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. + */ +import { AlertsFlyout } from './alerts_flyout'; +// eslint-disable-next-line import/no-default-export +export default AlertsFlyout; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_page/alerts_page.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_page/alerts_page.tsx index 5d961eaba2455..64f89a570d8b4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_page/alerts_page.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_page/alerts_page.tsx @@ -7,7 +7,6 @@ import React, { useState, useCallback, useEffect } from 'react'; import { get } from 'lodash'; import { - EuiDataGridCellValueElementProps, EuiDataGridControlColumn, EuiFlexItem, EuiFlexGroup, @@ -25,7 +24,7 @@ import { AbortError } from '@kbn/kibana-utils-plugin/common'; import { PLUGIN_ID } from '../../../../common/constants'; import { AlertsTable } from '../alerts_table'; import { useKibana } from '../../../../common/lib/kibana'; -import { AlertsData } from '../../../../types'; +import { AlertsData, RenderCellValueProps } from '../../../../types'; const consumers = [ AlertConsumers.APM, @@ -142,18 +141,16 @@ const AlertsPage: React.FunctionComponent = () => { deletedEventIds: [], disabledCellActions: [], pageSize: defaultPagination.pageSize, - pageSizeOptions: [2, 5, 10, 20, 50, 100], + pageSizeOptions: [1, 2, 5, 10, 20, 50, 100], leadingControlColumns: [], - renderCellValue: (rcvProps: EuiDataGridCellValueElementProps) => { - const { columnId, visibleRowIndex } = rcvProps as EuiDataGridCellValueElementProps & { - visibleRowIndex: number; - }; - const value = (get(alerts[visibleRowIndex], columnId) ?? [])[0]; + renderCellValue: ({ alert, field }: RenderCellValueProps) => { + const value = get(alert, field, [])[0]; return value ?? 'N/A'; }, showCheckboxes, trailingControlColumns: [], useFetchAlertsData, + alerts, 'data-test-subj': 'internalAlertsPage', }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.scss b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.scss new file mode 100644 index 0000000000000..fa62ba0307bf0 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.scss @@ -0,0 +1,3 @@ +.alertsTableActiveRow { + background-color: tintOrShade($euiColorLightShade, 50%, 0); +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx index 89171f5e06e8d..6a8c6a0ff9680 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { AlertConsumers } from '@kbn/rule-data-utils'; import { AlertsTable } from './alerts_table'; -import { AlertsData } from '../../../types'; +import { AlertsData, AlertsField } from '../../../types'; import { PLUGIN_ID } from '../../../common/constants'; import { useKibana } from '../../../common/lib/kibana'; import { render } from '@testing-library/react'; @@ -50,12 +50,12 @@ describe('AlertsTable', () => { const alerts: AlertsData[] = [ { - field1: ['one'], - field2: ['two'], + [AlertsField.name]: ['one'], + [AlertsField.reason]: ['two'], }, { - field1: ['three'], - field2: ['four'], + [AlertsField.name]: ['three'], + [AlertsField.reason]: ['four'], }, ]; @@ -83,13 +83,14 @@ describe('AlertsTable', () => { deletedEventIds: [], disabledCellActions: [], pageSize: 1, - pageSizeOptions: [1, 2, 5, 10, 20, 50, 100], + pageSizeOptions: [1, 10, 20, 50, 100], leadingControlColumns: [], renderCellValue: jest.fn().mockImplementation((props) => { return `${props.colIndex}:${props.rowIndex}`; }), showCheckboxes: false, trailingControlColumns: [], + alerts, useFetchAlertsData, 'data-test-subj': 'testTable', }; @@ -115,6 +116,73 @@ describe('AlertsTable', () => { userEvent.click(renderResult.getByTestId('pagination-button-1')); expect(fetchAlertsData.onPageChange).toHaveBeenCalledWith({ pageIndex: 1, pageSize: 1 }); }); + + describe('flyout', () => { + it('should show a flyout when selecting an alert', async () => { + const wrapper = render( + + ); + userEvent.click(wrapper.queryByTestId('expandColumnCellOpenFlyoutButton-0')!); + + const result = await wrapper.findAllByTestId('alertsFlyout'); + expect(result.length).toBe(1); + + expect(wrapper.queryByTestId('alertsFlyoutName')?.textContent).toBe('one'); + expect(wrapper.queryByTestId('alertsFlyoutReason')?.textContent).toBe('two'); + + // Should paginate too + userEvent.click(wrapper.queryAllByTestId('pagination-button-next')[0]); + expect(wrapper.queryByTestId('alertsFlyoutName')?.textContent).toBe('three'); + expect(wrapper.queryByTestId('alertsFlyoutReason')?.textContent).toBe('four'); + + userEvent.click(wrapper.queryAllByTestId('pagination-button-previous')[0]); + expect(wrapper.queryByTestId('alertsFlyoutName')?.textContent).toBe('one'); + expect(wrapper.queryByTestId('alertsFlyoutReason')?.textContent).toBe('two'); + }); + + it('should refetch data if flyout pagination exceeds the current page', async () => { + const wrapper = render(); + + userEvent.click(wrapper.queryByTestId('expandColumnCellOpenFlyoutButton-0')!); + const result = await wrapper.findAllByTestId('alertsFlyout'); + expect(result.length).toBe(1); + + userEvent.click(wrapper.queryAllByTestId('pagination-button-next')[0]); + expect(fetchAlertsData.onPageChange).toHaveBeenCalledWith({ pageIndex: 1, pageSize: 1 }); + + userEvent.click(wrapper.queryAllByTestId('pagination-button-previous')[0]); + expect(fetchAlertsData.onPageChange).toHaveBeenCalledWith({ pageIndex: 0, pageSize: 1 }); + }); + }); + + describe('leading control columns', () => { + it('should return at least the flyout action control', async () => { + const wrapper = render(); + expect(wrapper.getByTestId('expandColumnHeaderLabel').textContent).toBe('Actions'); + }); + + it('should render other leading controls', () => { + const customTableProps = { + ...tableProps, + leadingControlColumns: [ + { + id: 'selection', + width: 67, + headerCellRender: () => Test header, + rowCellRender: () =>

Test cell

, + }, + ], + }; + const wrapper = render(); + expect(wrapper.queryByTestId('testHeader')).not.toBe(null); + expect(wrapper.queryByTestId('testCell')).not.toBe(null); + }); + }); }); describe('Alerts table configuration registry', () => { @@ -127,7 +195,7 @@ describe('AlertsTable', () => { it('should render an empty error state when the plugin id owner is not registered', async () => { const props = { ...tableProps, configurationId: 'none' }; const result = render(); - expect(result.getByTestId('alerts-table-no-configuration')).toBeTruthy(); + expect(result.getByTestId('alertsTableNoConfiguration')).toBeTruthy(); }); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx index 4b0a7ef6e0e16..dca547e65ae27 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx @@ -4,12 +4,33 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useState } from 'react'; -import { EuiDataGrid, EuiEmptyPrompt } from '@elastic/eui'; + +import React, { useState, Suspense, lazy, useCallback, useMemo, useEffect } from 'react'; +import { + EuiDataGrid, + EuiEmptyPrompt, + EuiDataGridCellValueElementProps, + EuiDataGridCellValueProps, + EuiFlexGroup, + EuiFlexItem, + EuiToolTip, + EuiButtonIcon, + EuiDataGridStyle, +} from '@elastic/eui'; import { useSorting, usePagination } from './hooks'; -import { AlertsTableProps } from '../../../types'; +import { AlertsTableProps, AlertsField } from '../../../types'; import { useKibana } from '../../../common/lib/kibana'; -import { ALERTS_TABLE_CONF_ERROR_MESSAGE, ALERTS_TABLE_CONF_ERROR_TITLE } from './translations'; +import { + ALERTS_TABLE_CONF_ERROR_MESSAGE, + ALERTS_TABLE_CONF_ERROR_TITLE, + ALERTS_TABLE_CONTROL_COLUMNS_ACTIONS_LABEL, + ALERTS_TABLE_CONTROL_COLUMNS_VIEW_DETAILS_LABEL, +} from './translations'; +import './alerts_table.scss'; + +export const ACTIVE_ROW_CLASS = 'alertsTableActiveRow'; + +const AlertsFlyout = lazy(() => import('./alerts_flyout')); const emptyConfiguration = { id: '', @@ -17,17 +38,26 @@ const emptyConfiguration = { }; const AlertsTable: React.FunctionComponent = (props: AlertsTableProps) => { - const { activePage, alertsCount, onPageChange, onSortChange } = props.useFetchAlertsData(); + const [rowClasses, setRowClasses] = useState({}); + const { activePage, alertsCount, onPageChange, onSortChange, isLoading } = + props.useFetchAlertsData(); const { sortingColumns, onSort } = useSorting(onSortChange); - const { pagination, onChangePageSize, onChangePageIndex } = usePagination({ + const { + pagination, + onChangePageSize, + onChangePageIndex, + onPaginateFlyout, + flyoutAlertIndex, + setFlyoutAlertIndex, + } = usePagination({ onPageChange, pageIndex: activePage, pageSize: props.pageSize, + alertsCount, }); const alertsTableConfigurationRegistry = useKibana().services.alertsTableConfigurationRegistry; const hasAlertsTableConfiguration = alertsTableConfigurationRegistry.has(props.configurationId); - const alertsTableConfiguration = hasAlertsTableConfiguration ? alertsTableConfigurationRegistry.get(props.configurationId) : emptyConfiguration; @@ -36,15 +66,88 @@ const AlertsTable: React.FunctionComponent = (props: AlertsTab alertsTableConfiguration.columns.map(({ id }) => id) ); + const leadingControlColumns = useMemo(() => { + return [ + { + id: 'expandColumn', + width: 50, + headerCellRender: () => { + return ( + + {ALERTS_TABLE_CONTROL_COLUMNS_ACTIONS_LABEL} + + ); + }, + rowCellRender: (cveProps: EuiDataGridCellValueElementProps) => { + const { visibleRowIndex } = cveProps as EuiDataGridCellValueElementProps & { + visibleRowIndex: number; + }; + return ( + + + + { + setFlyoutAlertIndex(visibleRowIndex); + }} + data-test-subj={`expandColumnCellOpenFlyoutButton-${visibleRowIndex}`} + aria-label={ALERTS_TABLE_CONTROL_COLUMNS_VIEW_DETAILS_LABEL} + /> + + + + ); + }, + }, + ...props.leadingControlColumns, + ]; + }, [props.leadingControlColumns, setFlyoutAlertIndex]); + + useEffect(() => { + // Row classes do not deal with visible row indices so we need to handle page offset + const rowIndex = flyoutAlertIndex + pagination.pageIndex * pagination.pageSize; + setRowClasses({ + [rowIndex]: ACTIVE_ROW_CLASS, + }); + }, [flyoutAlertIndex, pagination.pageIndex, pagination.pageSize]); + + const handleFlyoutClose = useCallback(() => setFlyoutAlertIndex(-1), [setFlyoutAlertIndex]); + return hasAlertsTableConfiguration ? (
+ {flyoutAlertIndex > -1 && ( + + + + )} { + const rcvProps = improper as EuiDataGridCellValueElementProps & EuiDataGridCellValueProps; + const alert = props.alerts[rcvProps.visibleRowIndex]; + return props.renderCellValue({ + ...rcvProps, + alert, + field: rcvProps.columnId as AlertsField, + }); + }} + gridStyle={{ rowClasses }} sorting={{ columns: sortingColumns, onSort }} pagination={{ ...pagination, @@ -56,7 +159,7 @@ const AlertsTable: React.FunctionComponent = (props: AlertsTab
) : ( {ALERTS_TABLE_CONF_ERROR_TITLE}} body={

{ALERTS_TABLE_CONF_ERROR_MESSAGE}

} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.test.ts index 8b8ff68f106c2..70bc4c4ade8fb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.test.ts @@ -11,20 +11,25 @@ describe('usePagination', () => { const onPageChange = jest.fn(); const pageIndex = 0; const pageSize = 10; + const alertsCount = 5; beforeEach(() => { onPageChange.mockClear(); }); it('should return the pagination information and callback functions', () => { - const { result } = renderHook(() => usePagination({ onPageChange, pageIndex, pageSize })); + const { result } = renderHook(() => + usePagination({ onPageChange, pageIndex, pageSize, alertsCount }) + ); expect(result.current.pagination).toStrictEqual({ pageIndex, pageSize }); expect(result.current.onChangePageSize).toBeDefined(); expect(result.current.onChangePageIndex).toBeDefined(); }); it('should change the pagination when `onChangePageSize` is called', () => { - const { result } = renderHook(() => usePagination({ onPageChange, pageIndex, pageSize })); + const { result } = renderHook(() => + usePagination({ onPageChange, pageIndex, pageSize, alertsCount }) + ); act(() => { result.current.onChangePageSize(20); @@ -34,7 +39,9 @@ describe('usePagination', () => { }); it('should change the pagination when `onChangePageIndex` is called', () => { - const { result } = renderHook(() => usePagination({ onPageChange, pageIndex, pageSize })); + const { result } = renderHook(() => + usePagination({ onPageChange, pageIndex, pageSize, alertsCount }) + ); act(() => { result.current.onChangePageIndex(1); @@ -42,4 +49,62 @@ describe('usePagination', () => { expect(result.current.pagination).toStrictEqual({ pageIndex: 1, pageSize }); }); + + it('should paginate the alert flyout', () => { + const { result } = renderHook(() => + usePagination({ onPageChange, pageIndex, pageSize, alertsCount }) + ); + + expect(result.current.flyoutAlertIndex).toBe(-1); + + act(() => { + result.current.onPaginateFlyout(0); + }); + + expect(result.current.flyoutAlertIndex).toBe(0); + + act(() => { + result.current.onPaginateFlyout(1); + }); + + expect(result.current.flyoutAlertIndex).toBe(1); + + act(() => { + result.current.onPaginateFlyout(0); + }); + + expect(result.current.flyoutAlertIndex).toBe(0); + }); + + it('should paginate the flyout when we need to change the page index going back', () => { + const { result } = renderHook(() => + usePagination({ onPageChange, pageIndex: 0, pageSize: 1, alertsCount }) + ); + + act(() => { + result.current.onPaginateFlyout(-2); + }); + + // It should reset to the first alert in the table + expect(result.current.flyoutAlertIndex).toBe(0); + + // It should go to the last page + expect(result.current.pagination).toStrictEqual({ pageIndex: 4, pageSize: 1 }); + }); + + it('should paginate the flyout when we need to change the page index going forward', () => { + const { result } = renderHook(() => + usePagination({ onPageChange, pageIndex: 0, pageSize: 1, alertsCount }) + ); + + act(() => { + result.current.onPaginateFlyout(1); + }); + + // It should reset to the first alert in the table + expect(result.current.flyoutAlertIndex).toBe(0); + + // It should go to the first page + expect(result.current.pagination).toStrictEqual({ pageIndex: 1, pageSize: 1 }); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.ts index 7c78636559d38..76f4f0fa546c4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_pagination.ts @@ -9,13 +9,15 @@ import { RuleRegistrySearchRequestPagination } from '@kbn/rule-registry-plugin/c type PaginationProps = RuleRegistrySearchRequestPagination & { onPageChange: (pagination: RuleRegistrySearchRequestPagination) => void; + alertsCount: number; }; -export function usePagination({ onPageChange, pageIndex, pageSize }: PaginationProps) { +export function usePagination({ onPageChange, pageIndex, pageSize, alertsCount }: PaginationProps) { const [pagination, setPagination] = useState({ pageIndex, pageSize, }); + const [flyoutAlertIndex, setFlyoutAlertIndex] = useState(-1); const onChangePageSize = useCallback( (_pageSize) => { setPagination((state) => ({ @@ -34,5 +36,43 @@ export function usePagination({ onPageChange, pageIndex, pageSize }: PaginationP }, [setPagination, onPageChange, pagination.pageSize] ); - return { pagination, onChangePageSize, onChangePageIndex }; + + const paginateFlyout = useCallback( + (newFlyoutAlertIndex: number) => { + const lastPage = Math.floor(alertsCount / pagination.pageSize) - 1; + if (newFlyoutAlertIndex < 0) { + setFlyoutAlertIndex(pagination.pageSize - 1); + onChangePageIndex(pagination.pageIndex === 0 ? lastPage : pagination.pageIndex - 1); + return; + } + + if (newFlyoutAlertIndex >= pagination.pageSize) { + setFlyoutAlertIndex(0); + onChangePageIndex( + pagination.pageIndex === lastPage ? 0 : Math.min(pagination.pageIndex + 1, lastPage) + ); + return; + } + + setFlyoutAlertIndex(newFlyoutAlertIndex); + }, + [pagination, alertsCount, onChangePageIndex] + ); + + const onPaginateFlyout = useCallback( + (nextPageIndex: number) => { + nextPageIndex -= pagination.pageSize * pagination.pageIndex; + paginateFlyout(nextPageIndex); + }, + [paginateFlyout, pagination.pageSize, pagination.pageIndex] + ); + + return { + pagination, + onChangePageSize, + onChangePageIndex, + onPaginateFlyout, + flyoutAlertIndex, + setFlyoutAlertIndex, + }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/translations.ts index 5bd6b09db427a..b1e916840bfce 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/translations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/translations.ts @@ -20,3 +20,17 @@ export const ALERTS_TABLE_CONF_ERROR_MESSAGE = i18n.translate( 'There was an error loading the alerts table. This table is missing the necessary configuration. Please contact your administrator for help', } ); + +export const ALERTS_TABLE_CONTROL_COLUMNS_ACTIONS_LABEL = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsTable.column.actions', + { + defaultMessage: 'Actions', + } +); + +export const ALERTS_TABLE_CONTROL_COLUMNS_VIEW_DETAILS_LABEL = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsTable.leadingControl.viewDetails', + { + defaultMessage: 'View details', + } +); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/execution_duration_chart.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/execution_duration_chart.tsx index 08fabc3fba2fd..140dae9dc14a5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/execution_duration_chart.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/execution_duration_chart.tsx @@ -38,7 +38,7 @@ const NUM_EXECUTIONS_OPTIONS = [120, 60, 30, 15].map((value) => ({ text: i18n.translate( 'xpack.triggersActionsUI.sections.executionDurationChart.numberOfExecutionsOption', { - defaultMessage: '{value} executions', + defaultMessage: '{value} runs', values: { value, }, @@ -70,7 +70,7 @@ export const ExecutionDurationChart: React.FunctionComponent = ({

@@ -84,7 +84,7 @@ export const ExecutionDurationChart: React.FunctionComponent = ({ aria-label={i18n.translate( 'xpack.triggersActionsUI.sections.executionDurationChart.selectNumberOfExecutionDurationsLabel', { - defaultMessage: 'Select number of executions', + defaultMessage: 'Select number of runs', } )} onChange={onChange} @@ -161,7 +161,7 @@ export const ExecutionDurationChart: React.FunctionComponent = ({

diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx index 0aaa3195b7c52..9ab31ae12402f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx @@ -32,3 +32,12 @@ export const ActionForm = suspendedComponentWithProps( export const RuleStatusDropdown = suspendedComponentWithProps( lazy(() => import('./rules_list/components/rule_status_dropdown')) ); +export const RuleTagFilter = suspendedComponentWithProps( + lazy(() => import('./rules_list/components/rule_tag_filter')) +); +export const RuleStatusFilter = suspendedComponentWithProps( + lazy(() => import('./rules_list/components/rule_status_filter')) +); +export const RuleTagBadge = suspendedComponentWithProps( + lazy(() => import('./rules_list/components/rule_tag_badge')) +); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx index b70eaf20a051d..9d62fc2f8e37a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx @@ -33,7 +33,7 @@ import { withBulkRuleOperations, } from '../../common/components/with_bulk_rule_api_operations'; import './rule.scss'; -import { getHealthColor } from '../../rules_list/components/rule_status_filter'; +import { getHealthColor } from '../../rules_list/components/rule_execution_status_filter'; import { rulesStatusesTranslationsMapping, ALERT_STATUS_LICENSE_ERROR, @@ -122,7 +122,7 @@ export function RuleComponent({ { id: EVENT_LOG_LIST_TAB, name: i18n.translate('xpack.triggersActionsUI.sections.ruleDetails.rule.eventLogTabText', { - defaultMessage: 'Execution history', + defaultMessage: 'Run history', }), 'data-test-subj': 'eventLogListTab', content: suspendedComponentWithProps( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.test.tsx index fe17dde8c1282..7857eb172eedb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.test.tsx @@ -48,6 +48,7 @@ jest.mock('../../../lib/capabilities', () => ({ hasAllPrivilege: jest.fn(() => true), hasSaveRulesCapability: jest.fn(() => true), hasExecuteActionsCapability: jest.fn(() => true), + hasManageApiKeysCapability: jest.fn(() => true), })); const useKibanaMock = useKibana as jest.Mocked; const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -100,6 +101,26 @@ describe('rule_details', () => { ).toBeTruthy(); }); + it('renders the API key owner badge when user can manage API keys', () => { + const rule = mockRule(); + expect( + shallow( + + ).find({rule.apiKeyOwner}) + ).toBeTruthy(); + }); + + it(`doesn't render the API key owner badge when user can't manage API keys`, () => { + const { hasManageApiKeysCapability } = jest.requireMock('../../../lib/capabilities'); + hasManageApiKeysCapability.mockReturnValueOnce(false); + const rule = mockRule(); + expect( + shallow() + .find({rule.apiKeyOwner}) + .exists() + ).toBeFalsy(); + }); + it('renders the rule error banner with error message, when rule has a license error', () => { const rule = mockRule({ enabled: true, @@ -871,7 +892,7 @@ function mockRule(overloads: Partial = {}): Rule { updatedBy: null, createdAt: new Date(), updatedAt: new Date(), - apiKeyOwner: null, + apiKeyOwner: 'bob', throttle: null, notifyWhen: null, muteAll: false, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.tsx index b3363159851d0..0389e6b0d9b30 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.tsx @@ -27,7 +27,11 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { RuleExecutionStatusErrorReasons, parseDuration } from '@kbn/alerting-plugin/common'; -import { hasAllPrivilege, hasExecuteActionsCapability } from '../../../lib/capabilities'; +import { + hasAllPrivilege, + hasExecuteActionsCapability, + hasManageApiKeysCapability, +} from '../../../lib/capabilities'; import { getAlertingSectionBreadcrumb, getRuleDetailsBreadcrumb } from '../../../lib/breadcrumb'; import { getCurrentDocTitle } from '../../../lib/doc_title'; import { @@ -310,6 +314,27 @@ export const RuleDetails: React.FunctionComponent = ({
+ {hasManageApiKeysCapability(capabilities) ? ( + + + + +

+ +

+
+
+ + + {rule.apiKeyOwner} + + +
+
+ ) : null} {uniqueActions && uniqueActions.length ? ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.test.tsx index f64a3c011187a..cf6a1350d389d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.test.tsx @@ -11,10 +11,12 @@ import { ReactWrapper } from 'enzyme'; import { act } from 'react-dom/test-utils'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { ruleTypeRegistryMock } from '../../rule_type_registry.mock'; +import { ActionForm } from '../action_connector_form'; import { ValidationResult, Rule, RuleType, + RuleTypeModel, ConnectorValidationResult, GenericValidationResult, } from '../../../types'; @@ -30,6 +32,12 @@ jest.mock('../../hooks/use_load_rule_types', () => ({ useLoadRuleTypes: jest.fn(), })); jest.mock('../../../common/lib/kibana'); +jest.mock('../../lib/capabilities', () => ({ + hasAllPrivilege: jest.fn(() => true), + hasSaveRulesCapability: jest.fn(() => true), + hasShowActionsCapability: jest.fn(() => true), + hasExecuteActionsCapability: jest.fn(() => true), +})); describe('rule_form', () => { const ruleType = { @@ -91,6 +99,144 @@ describe('rule_form', () => { const useKibanaMock = useKibana as jest.Mocked; + describe('rule recovery message', () => { + let wrapper: ReactWrapper; + const defaultRecoveryMessage = 'Sample default recovery message'; + + async function setup(enforceMinimum = false, schedule = '1m') { + const mocks = coreMock.createSetup(); + const { useLoadRuleTypes } = jest.requireMock('../../hooks/use_load_rule_types'); + const myRuleModel = { + id: 'my-rule-type', + description: 'Sample rule type model', + iconClass: 'sampleIconClass', + defaultActionMessage: 'Sample default action message', + defaultRecoveryMessage, + requiresAppContext: false, + }; + const myRule = { + id: 'my-rule-type', + name: 'Test', + actionGroups: [ + { + id: 'testActionGroup', + name: 'Test Action Group', + }, + { + id: 'recovered', + name: 'Recovered', + }, + ], + defaultActionGroupId: 'testActionGroup', + minimumLicenseRequired: 'basic', + recoveryActionGroup: RecoveredActionGroup, + producer: ALERTS_FEATURE_ID, + authorizedConsumers: { + [ALERTS_FEATURE_ID]: { read: true, all: true }, + test: { read: true, all: true }, + }, + actionVariables: { + params: [], + state: [], + }, + enabledInLicense: true, + }; + const disabledByLicenseRule = { + id: 'disabled-by-license', + name: 'Test', + actionGroups: [ + { + id: 'testActionGroup', + name: 'Test Action Group', + }, + ], + defaultActionGroupId: 'testActionGroup', + minimumLicenseRequired: 'gold', + recoveryActionGroup: RecoveredActionGroup, + producer: ALERTS_FEATURE_ID, + authorizedConsumers: { + [ALERTS_FEATURE_ID]: { read: true, all: true }, + test: { read: true, all: true }, + }, + actionVariables: { + params: [], + state: [], + }, + enabledInLicense: false, + }; + useLoadRuleTypes.mockReturnValue({ + ruleTypes: [myRule, disabledByLicenseRule], + ruleTypeIndex: new Map([ + [myRule.id, myRule], + [disabledByLicenseRule.id, disabledByLicenseRule], + ]), + }); + const [ + { + application: { capabilities }, + }, + ] = await mocks.getStartServices(); + // eslint-disable-next-line react-hooks/rules-of-hooks + useKibanaMock().services.application.capabilities = { + ...capabilities, + rules: { + show: true, + save: true, + delete: true, + }, + }; + ruleTypeRegistry.list.mockReturnValue([ + ruleType, + ruleTypeNonEditable, + disabledByLicenseRuleType, + ]); + ruleTypeRegistry.has.mockReturnValue(true); + ruleTypeRegistry.get.mockReturnValue(myRuleModel as RuleTypeModel); + actionTypeRegistry.list.mockReturnValue([actionType]); + actionTypeRegistry.has.mockReturnValue(true); + actionTypeRegistry.get.mockReturnValue(actionType); + const initialRule = { + name: 'test', + params: {}, + consumer: ALERTS_FEATURE_ID, + schedule: { + interval: schedule, + }, + actions: [], + tags: [], + muteAll: false, + enabled: false, + mutedInstanceIds: [], + ruleTypeId: 'my-rule-type', + } as unknown as Rule; + + wrapper = mountWithIntl( + {}} + errors={{ name: [], 'schedule.interval': [], ruleTypeId: [], actionConnectors: [] }} + operation="create" + actionTypeRegistry={actionTypeRegistry} + ruleTypeRegistry={ruleTypeRegistry} + /> + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + } + + it('renders defaultRecoveryMessage for recovery action when specified', async () => { + await setup(); + const actionForm = wrapper.find(ActionForm); + expect(actionForm.first().prop('actionGroups')?.[1]).toEqual( + expect.objectContaining({ defaultActionMessage: defaultRecoveryMessage }) + ); + }); + }); + describe('rule_form create rule', () => { let wrapper: ReactWrapper; @@ -282,7 +428,6 @@ describe('rule_form', () => { async function setup() { const { useLoadRuleTypes } = jest.requireMock('../../hooks/use_load_rule_types'); - useLoadRuleTypes.mockReturnValue({ ruleTypes: [ { @@ -384,7 +529,7 @@ describe('rule_form', () => { rule={initialRule} config={{ minimumScheduleInterval: { value: '1m', enforce: false } }} dispatch={() => {}} - errors={{ name: [], 'schedule.interval': [], ruleTypeId: [] }} + errors={{ name: [], 'schedule.interval': [], ruleTypeId: [], actionConnectors: [] }} operation="create" actionTypeRegistry={actionTypeRegistry} ruleTypeRegistry={ruleTypeRegistry} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx index 80923617da064..1bca80a08c936 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx @@ -488,7 +488,7 @@ export const RuleForm = ({ > )} @@ -560,7 +560,8 @@ export const RuleForm = ({ omitMessageVariables: selectedRuleType.doesSetRecoveryContext ? 'keepContext' : 'all', - defaultActionMessage: recoveredActionGroupMessage, + defaultActionMessage: + ruleTypeModel?.defaultRecoveryMessage || recoveredActionGroupMessage, } : { ...actionGroup, defaultActionMessage: ruleTypeModel?.defaultActionMessage } )} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_execution_status_filter.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_execution_status_filter.tsx new file mode 100644 index 0000000000000..9acb8489fa09a --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_execution_status_filter.tsx @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + EuiFilterGroup, + EuiPopover, + EuiFilterButton, + EuiFilterSelectItem, + EuiHealth, +} from '@elastic/eui'; +import { RuleExecutionStatuses, RuleExecutionStatusValues } from '@kbn/alerting-plugin/common'; +import { rulesStatusesTranslationsMapping } from '../translations'; + +interface RuleExecutionStatusFilterProps { + selectedStatuses: string[]; + onChange?: (selectedRuleStatusesIds: string[]) => void; +} + +export const RuleExecutionStatusFilter: React.FunctionComponent = ({ + selectedStatuses, + onChange, +}: RuleExecutionStatusFilterProps) => { + const [selectedValues, setSelectedValues] = useState(selectedStatuses); + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + useEffect(() => { + if (onChange) { + onChange(selectedValues); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedValues]); + + useEffect(() => { + setSelectedValues(selectedStatuses); + }, [selectedStatuses]); + + return ( + + setIsPopoverOpen(false)} + button={ + 0} + numActiveFilters={selectedValues.length} + numFilters={selectedValues.length} + onClick={() => setIsPopoverOpen(!isPopoverOpen)} + data-test-subj="ruleExecutionStatusFilterButton" + > + + + } + > +
+ {[...RuleExecutionStatusValues].sort().map((item: RuleExecutionStatuses) => { + const healthColor = getHealthColor(item); + return ( + { + const isPreviouslyChecked = selectedValues.includes(item); + if (isPreviouslyChecked) { + setSelectedValues(selectedValues.filter((val) => val !== item)); + } else { + setSelectedValues(selectedValues.concat(item)); + } + }} + checked={selectedValues.includes(item) ? 'on' : undefined} + data-test-subj={`ruleExecutionStatus${item}FilterOption`} + > + {rulesStatusesTranslationsMapping[item]} + + ); + })} +
+
+
+ ); +}; + +export function getHealthColor(status: RuleExecutionStatuses) { + switch (status) { + case 'active': + return 'success'; + case 'error': + return 'danger'; + case 'ok': + return 'primary'; + case 'pending': + return 'accent'; + case 'warning': + return 'warning'; + default: + return 'subdued'; + } +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_status_filter.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_status_filter.test.tsx new file mode 100644 index 0000000000000..f1f2957f9cada --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_status_filter.test.tsx @@ -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 React from 'react'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { EuiFilterButton, EuiFilterSelectItem } from '@elastic/eui'; +import { RuleStatusFilter } from './rule_status_filter'; + +const onChangeMock = jest.fn(); + +describe('rule_state_filter', () => { + beforeEach(() => { + onChangeMock.mockReset(); + }); + + it('renders correctly', () => { + const wrapper = mountWithIntl( + + ); + + expect(wrapper.find(EuiFilterSelectItem).exists()).toBeFalsy(); + expect(wrapper.find(EuiFilterButton).exists()).toBeTruthy(); + + expect(wrapper.find('.euiNotificationBadge').text()).toEqual('0'); + }); + + it('can open the popover correctly', () => { + const wrapper = mountWithIntl( + + ); + + expect(wrapper.find('[data-test-subj="ruleStateFilterSelect"]').exists()).toBeFalsy(); + + wrapper.find(EuiFilterButton).simulate('click'); + + const statusItems = wrapper.find(EuiFilterSelectItem); + expect(statusItems.length).toEqual(3); + }); + + it('can select statuses', () => { + const wrapper = mountWithIntl( + + ); + + wrapper.find(EuiFilterButton).simulate('click'); + + wrapper.find(EuiFilterSelectItem).at(0).simulate('click'); + expect(onChangeMock).toHaveBeenCalledWith(['enabled']); + + wrapper.setProps({ + selectedStatuses: ['enabled'], + }); + + wrapper.find(EuiFilterSelectItem).at(0).simulate('click'); + expect(onChangeMock).toHaveBeenCalledWith([]); + + wrapper.find(EuiFilterSelectItem).at(1).simulate('click'); + expect(onChangeMock).toHaveBeenCalledWith(['enabled', 'disabled']); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_status_filter.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_status_filter.tsx index cbb1a7f5455da..6d286ec6d09d7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_status_filter.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_status_filter.tsx @@ -4,82 +4,87 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import React, { useEffect, useState } from 'react'; +import React, { useState, useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { - EuiFilterGroup, - EuiPopover, - EuiFilterButton, - EuiFilterSelectItem, - EuiHealth, -} from '@elastic/eui'; -import { RuleExecutionStatuses, RuleExecutionStatusValues } from '@kbn/alerting-plugin/common'; -import { rulesStatusesTranslationsMapping } from '../translations'; +import { EuiFilterButton, EuiPopover, EuiFilterGroup, EuiFilterSelectItem } from '@elastic/eui'; +import { RuleStatus } from '../../../../types'; + +const statuses: RuleStatus[] = ['enabled', 'disabled', 'snoozed']; + +const optionStyles = { + textTransform: 'capitalize' as const, +}; -interface RuleStatusFilterProps { - selectedStatuses: string[]; - onChange?: (selectedRuleStatusesIds: string[]) => void; +const getOptionDataTestSubj = (status: RuleStatus) => `ruleStatusFilterOption-${status}`; + +export interface RuleStatusFilterProps { + selectedStatuses: RuleStatus[]; + dataTestSubj?: string; + selectDataTestSubj?: string; + buttonDataTestSubj?: string; + optionDataTestSubj?: (status: RuleStatus) => string; + onChange: (selectedStatuses: RuleStatus[]) => void; } -export const RuleStatusFilter: React.FunctionComponent = ({ - selectedStatuses, - onChange, -}: RuleStatusFilterProps) => { - const [selectedValues, setSelectedValues] = useState(selectedStatuses); +export const RuleStatusFilter = (props: RuleStatusFilterProps) => { + const { + selectedStatuses = [], + dataTestSubj = 'ruleStatusFilter', + selectDataTestSubj = 'ruleStatusFilterSelect', + buttonDataTestSubj = 'ruleStatusFilterButton', + optionDataTestSubj = getOptionDataTestSubj, + onChange = () => {}, + } = props; + const [isPopoverOpen, setIsPopoverOpen] = useState(false); - useEffect(() => { - if (onChange) { - onChange(selectedValues); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedValues]); + const onFilterItemClick = useCallback( + (newOption: RuleStatus) => () => { + if (selectedStatuses.includes(newOption)) { + onChange(selectedStatuses.filter((option) => option !== newOption)); + return; + } + onChange([...selectedStatuses, newOption]); + }, + [selectedStatuses, onChange] + ); - useEffect(() => { - setSelectedValues(selectedStatuses); - }, [selectedStatuses]); + const onClick = useCallback(() => { + setIsPopoverOpen((prevIsOpen) => !prevIsOpen); + }, [setIsPopoverOpen]); return ( - + setIsPopoverOpen(false)} button={ 0} - numActiveFilters={selectedValues.length} - numFilters={selectedValues.length} - onClick={() => setIsPopoverOpen(!isPopoverOpen)} - data-test-subj="ruleStatusFilterButton" + hasActiveFilters={selectedStatuses.length > 0} + numActiveFilters={selectedStatuses.length} + numFilters={selectedStatuses.length} + onClick={onClick} > } > -
- {[...RuleExecutionStatusValues].sort().map((item: RuleExecutionStatuses) => { - const healthColor = getHealthColor(item); +
+ {statuses.map((status) => { return ( { - const isPreviouslyChecked = selectedValues.includes(item); - if (isPreviouslyChecked) { - setSelectedValues(selectedValues.filter((val) => val !== item)); - } else { - setSelectedValues(selectedValues.concat(item)); - } - }} - checked={selectedValues.includes(item) ? 'on' : undefined} - data-test-subj={`ruleStatus${item}FilerOption`} + key={status} + style={optionStyles} + data-test-subj={optionDataTestSubj(status)} + onClick={onFilterItemClick(status)} + checked={selectedStatuses.includes(status) ? 'on' : undefined} > - {rulesStatusesTranslationsMapping[item]} + {status} ); })} @@ -89,19 +94,5 @@ export const RuleStatusFilter: React.FunctionComponent = ); }; -export function getHealthColor(status: RuleExecutionStatuses) { - switch (status) { - case 'active': - return 'success'; - case 'error': - return 'danger'; - case 'ok': - return 'primary'; - case 'pending': - return 'accent'; - case 'warning': - return 'warning'; - default: - return 'subdued'; - } -} +// eslint-disable-next-line import/no-default-export +export { RuleStatusFilter as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_badge.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_badge.test.tsx new file mode 100644 index 0000000000000..606d60ff6bfeb --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_badge.test.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { RuleTagBadge } from './rule_tag_badge'; + +const onClickMock = jest.fn(); +const onCloseMock = jest.fn(); + +const tags = ['a', 'b', 'c']; + +describe('RuleTagBadge', () => { + beforeEach(() => { + onClickMock.mockReset(); + onCloseMock.mockReset(); + }); + + it('renders the initial badge count correctly', () => { + const wrapper = mountWithIntl( + + ); + + expect(wrapper.find('[data-test-subj="ruleTagBadge"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="ruleTagBadge"]').first().text()).toEqual( + `${tags.length}` + ); + }); + + it('can open and close the popover', () => { + const wrapper = mountWithIntl( + + ); + + expect(wrapper.find('[data-test-subj="ruleTagBadgeItem-a"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="ruleTagBadgeItem-b"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="ruleTagBadgeItem-c"]').exists()).toBeFalsy(); + + wrapper.find('[data-test-subj="ruleTagBadge"]').at(1).simulate('click'); + + expect(onClickMock).toHaveBeenCalledTimes(1); + + wrapper.setProps({ + isOpen: true, + }); + + expect(wrapper.find('[data-test-subj="ruleTagBadgeItem-a"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="ruleTagBadgeItem-b"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="ruleTagBadgeItem-c"]').exists()).toBeTruthy(); + + wrapper.find('[data-test-subj="ruleTagBadge"]').at(1).simulate('click'); + + expect(onClickMock).toHaveBeenCalledTimes(2); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_badge.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_badge.tsx new file mode 100644 index 0000000000000..c7da398d14403 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_badge.tsx @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiPopover, EuiBadge, EuiPopoverTitle } from '@elastic/eui'; + +const tagTitle = i18n.translate( + 'xpack.triggersActionsUI.sections.rules_list.rules_tag_badge.tagTitle', + { + defaultMessage: 'Tag', + } +); + +export interface RuleTagBadgeProps { + isOpen: boolean; + tags: string[]; + onClick: React.MouseEventHandler; + onClose: () => void; + badgeDataTestSubj?: string; + titleDataTestSubj?: string; + tagItemDataTestSubj?: (tag: string) => string; +} + +const containerStyle = { + width: '300px', +}; + +const getTagItemDataTestSubj = (tag: string) => `ruleTagBadgeItem-${tag}`; + +export const RuleTagBadge = (props: RuleTagBadgeProps) => { + const { + isOpen = false, + tags = [], + onClick, + onClose, + badgeDataTestSubj = 'ruleTagBadge', + titleDataTestSubj = 'ruleTagPopoverTitle', + tagItemDataTestSubj = getTagItemDataTestSubj, + } = props; + + const badge = useMemo(() => { + return ( + + {tags.length} + + ); + }, [tags, badgeDataTestSubj, onClick]); + + const tagBadges = useMemo( + () => + tags.map((tag, index) => ( + + {tag} + + )), + [tags, tagItemDataTestSubj] + ); + + return ( + + {tagTitle} +
{tagBadges}
+
+ ); +}; + +// eslint-disable-next-line import/no-default-export +export { RuleTagBadge as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_filter.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_filter.test.tsx new file mode 100644 index 0000000000000..a6b60b1099391 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_filter.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 { mountWithIntl } from '@kbn/test-jest-helpers'; +import { EuiFilterButton, EuiSelectable } from '@elastic/eui'; +import { RuleTagFilter } from './rule_tag_filter'; + +const onChangeMock = jest.fn(); + +const tags = ['a', 'b', 'c', 'd', 'e', 'f']; + +describe('rule_tag_filter', () => { + beforeEach(() => { + onChangeMock.mockReset(); + }); + + it('renders correctly', () => { + const wrapper = mountWithIntl( + + ); + + expect(wrapper.find(EuiFilterButton).exists()).toBeTruthy(); + expect(wrapper.find('.euiNotificationBadge').text()).toEqual('0'); + }); + + it('can open the popover correctly', () => { + const wrapper = mountWithIntl( + + ); + + expect(wrapper.find('[data-test-subj="ruleTagFilterSelectable"]').exists()).toBeFalsy(); + + wrapper.find(EuiFilterButton).simulate('click'); + + expect(wrapper.find('[data-test-subj="ruleTagFilterSelectable"]').exists()).toBeTruthy(); + expect(wrapper.find('li').length).toEqual(tags.length); + }); + + it('can select tags', () => { + const wrapper = mountWithIntl( + + ); + + wrapper.find(EuiFilterButton).simulate('click'); + + wrapper.find('[data-test-subj="ruleTagFilterOption-a"]').at(0).simulate('click'); + expect(onChangeMock).toHaveBeenCalledWith(['a']); + + wrapper.setProps({ + selectedTags: ['a'], + }); + + wrapper.find('[data-test-subj="ruleTagFilterOption-a"]').at(0).simulate('click'); + expect(onChangeMock).toHaveBeenCalledWith([]); + + wrapper.find('[data-test-subj="ruleTagFilterOption-b"]').at(0).simulate('click'); + expect(onChangeMock).toHaveBeenCalledWith(['a', 'b']); + }); + + it('renders selected tags even if they get deleted from the tags array', () => { + const selectedTags = ['g', 'h']; + const wrapper = mountWithIntl( + + ); + + wrapper.find(EuiFilterButton).simulate('click'); + + expect(wrapper.find(EuiSelectable).props().options.length).toEqual( + tags.length + selectedTags.length + ); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_filter.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_filter.tsx new file mode 100644 index 0000000000000..6aa8aa8c69213 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_tag_filter.tsx @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo, useState, useCallback } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + EuiSelectable, + EuiFilterGroup, + EuiFilterButton, + EuiPopover, + EuiSelectableProps, + EuiSelectableOption, + EuiSpacer, +} from '@elastic/eui'; + +export interface RuleTagFilterProps { + tags: string[]; + selectedTags: string[]; + isLoading?: boolean; + loadingMessage?: EuiSelectableProps['loadingMessage']; + noMatchesMessage?: EuiSelectableProps['noMatchesMessage']; + emptyMessage?: EuiSelectableProps['emptyMessage']; + errorMessage?: EuiSelectableProps['errorMessage']; + dataTestSubj?: string; + selectableDataTestSubj?: string; + optionDataTestSubj?: (tag: string) => string; + buttonDataTestSubj?: string; + onChange: (tags: string[]) => void; +} + +const getOptionDataTestSubj = (tag: string) => `ruleTagFilterOption-${tag}`; + +export const RuleTagFilter = (props: RuleTagFilterProps) => { + const { + tags = [], + selectedTags = [], + isLoading = false, + loadingMessage, + noMatchesMessage, + emptyMessage, + errorMessage, + dataTestSubj = 'ruleTagFilter', + selectableDataTestSubj = 'ruleTagFilterSelectable', + optionDataTestSubj = getOptionDataTestSubj, + buttonDataTestSubj = 'ruleTagFilterButton', + onChange = () => {}, + } = props; + + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + const allTags = useMemo(() => { + return [...new Set([...tags, ...selectedTags])].sort(); + }, [tags, selectedTags]); + + const options: EuiSelectableOption[] = useMemo( + () => + allTags.map((tag) => ({ + label: tag, + checked: selectedTags.includes(tag) ? 'on' : undefined, + 'data-test-subj': optionDataTestSubj(tag), + })), + [allTags, selectedTags, optionDataTestSubj] + ); + + const onChangeInternal = useCallback( + (newOptions: EuiSelectableOption[]) => { + const newSelectedTags = newOptions.reduce((result, option) => { + if (option.checked === 'on') { + result = [...result, option.label]; + } + return result; + }, []); + + onChange(newSelectedTags); + }, + [onChange] + ); + + const onClosePopover = () => { + setIsPopoverOpen(!isPopoverOpen); + }; + + const renderButton = () => { + return ( + 0} + numActiveFilters={selectedTags.length} + numFilters={selectedTags.length} + onClick={onClosePopover} + > + + + ); + }; + + return ( + + + + {(list, search) => ( + <> + {search} + + {list} + + )} + + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { RuleTagFilter as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.test.tsx index f87cee4c6547f..12e1b0f1e4a6e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.test.tsx @@ -20,6 +20,7 @@ import { parseDuration, } from '@kbn/alerting-plugin/common'; import { getFormattedDuration, getFormattedMilliseconds } from '../../../lib/monitoring_utils'; +import { getIsExperimentalFeatureEnabled } from '../../../../common/get_experimental_features'; import { useKibana } from '../../../../common/lib/kibana'; jest.mock('../../../../common/lib/kibana'); @@ -32,6 +33,7 @@ jest.mock('../../../lib/rule_api', () => ({ loadRules: jest.fn(), loadRuleTypes: jest.fn(), loadRuleAggregations: jest.fn(), + loadRuleTags: jest.fn(), alertingFrameworkHealth: jest.fn(() => ({ isSufficientlySecure: true, hasPermanentEncryptionKey: true, @@ -59,7 +61,13 @@ jest.mock('../../../lib/capabilities', () => ({ hasShowActionsCapability: jest.fn(() => true), hasExecuteActionsCapability: jest.fn(() => true), })); -const { loadRules, loadRuleTypes, loadRuleAggregations } = +jest.mock('../../../../common/get_experimental_features', () => ({ + getIsExperimentalFeatureEnabled: jest.fn(), +})); + +const ruleTags = ['a', 'b', 'c', 'd']; + +const { loadRules, loadRuleTypes, loadRuleAggregations, loadRuleTags } = jest.requireMock('../../../lib/rule_api'); const { loadActionTypes, loadAllActions } = jest.requireMock('../../../lib/action_connector_api'); const actionTypeRegistry = actionTypeRegistryMock.create(); @@ -95,6 +103,10 @@ ruleTypeRegistry.list.mockReturnValue([ruleType]); actionTypeRegistry.list.mockReturnValue([]); const useKibanaMock = useKibana as jest.Mocked; +beforeEach(() => { + (getIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(() => false); +}); + describe('rules_list component empty', () => { let wrapper: ReactWrapper; async function setup() { @@ -387,6 +399,10 @@ describe('rules_list component with items', () => { ruleEnabledStatus: { enabled: 2, disabled: 0 }, ruleExecutionStatus: { ok: 1, active: 2, error: 3, pending: 4, unknown: 5, warning: 6 }, ruleMutedStatus: { muted: 0, unmuted: 2 }, + ruleTags, + }); + loadRuleTags.mockResolvedValue({ + ruleTags, }); const ruleTypeMock: RuleTypeModel = { @@ -439,7 +455,7 @@ describe('rules_list component with items', () => { wrapper.find('EuiTableRowCell[data-test-subj="rulesTableCell-tagsPopover"]').length ).toEqual(mockedRulesData.length); // only show tags popover if tags exist on rule - const tagsBadges = wrapper.find('EuiBadge[data-test-subj="ruleTagsBadge"]'); + const tagsBadges = wrapper.find('EuiBadge[data-test-subj="ruleTagBadge"]'); expect(tagsBadges.length).toEqual( mockedRulesData.filter((data) => data.tags.length > 0).length ); @@ -459,7 +475,7 @@ describe('rules_list component with items', () => { jest.runAllTimers(); wrapper.update(); - expect(wrapper.find('.euiToolTipPopover').text()).toBe('Start time of the last execution.'); + expect(wrapper.find('.euiToolTipPopover').text()).toBe('Start time of the last run.'); wrapper .find('[data-test-subj="rulesTableCell-lastExecutionDateTooltip"]') @@ -801,6 +817,73 @@ describe('rules_list component with items', () => { 'Warning: 6' ); }); + + it('does not render the status filter if the feature flag is off', async () => { + await setup(); + expect(wrapper.find('[data-test-subj="ruleStatusFilter"]').exists()).toBeFalsy(); + }); + + it('renders the status filter if the experiment is on', async () => { + (getIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(() => true); + await setup(); + expect(wrapper.find('[data-test-subj="ruleStatusFilter"]').exists()).toBeTruthy(); + }); + + it('can filter by rule states', async () => { + (getIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(() => true); + loadRules.mockReset(); + await setup(); + + expect(loadRules.mock.calls[0][0].ruleStatusesFilter).toEqual([]); + + wrapper.find('[data-test-subj="ruleStatusFilterButton"] button').simulate('click'); + + wrapper.find('[data-test-subj="ruleStatusFilterOption-enabled"]').first().simulate('click'); + + expect(loadRules.mock.calls[1][0].ruleStatusesFilter).toEqual(['enabled']); + + wrapper.find('[data-test-subj="ruleStatusFilterOption-snoozed"]').first().simulate('click'); + + expect(loadRules.mock.calls[2][0].ruleStatusesFilter).toEqual(['enabled', 'snoozed']); + + wrapper.find('[data-test-subj="ruleStatusFilterOption-snoozed"]').first().simulate('click'); + + expect(loadRules.mock.calls[3][0].ruleStatusesFilter).toEqual(['enabled']); + }); + + it('does not render the tag filter is the feature flag is off', async () => { + await setup(); + expect(wrapper.find('[data-test-subj="ruleTagFilter"]').exists()).toBeFalsy(); + }); + + it('renders the tag filter if the experiment is on', async () => { + (getIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(() => true); + await setup(); + expect(wrapper.find('[data-test-subj="ruleTagFilter"]').exists()).toBeTruthy(); + }); + + it('can filter by tags', async () => { + (getIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(() => true); + loadRules.mockReset(); + await setup(); + + expect(loadRules.mock.calls[0][0].tagsFilter).toEqual([]); + + wrapper.find('[data-test-subj="ruleTagFilterButton"] button').simulate('click'); + + const tagFilterListItems = wrapper.find( + '[data-test-subj="ruleTagFilterSelectable"] .euiSelectableListItem' + ); + expect(tagFilterListItems.length).toEqual(ruleTags.length); + + tagFilterListItems.at(0).simulate('click'); + + expect(loadRules.mock.calls[1][0].tagsFilter).toEqual(['a']); + + tagFilterListItems.at(1).simulate('click'); + + expect(loadRules.mock.calls[2][0].tagsFilter).toEqual(['a', 'b']); + }); }); describe('rules_list component empty with show only capability', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx index 59515ca3c3622..a5b9661835131 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx @@ -14,7 +14,6 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React, { useEffect, useState, useMemo, ReactNode, useCallback } from 'react'; import { EuiBasicTable, - EuiBadge, EuiButton, EuiFieldSearch, EuiFlexGroup, @@ -30,8 +29,6 @@ import { EuiTableSortingType, EuiButtonIcon, EuiHorizontalRule, - EuiPopover, - EuiPopoverTitle, EuiSelectableOption, EuiIcon, EuiScreenReaderOnly, @@ -61,6 +58,7 @@ import { RuleTableItem, RuleType, RuleTypeIndex, + RuleStatus, Pagination, Percentiles, TriggersActionsUiConfig, @@ -71,10 +69,11 @@ import { RuleQuickEditButtonsWithApi as RuleQuickEditButtons } from '../../commo import { CollapsedItemActionsWithApi as CollapsedItemActions } from './collapsed_item_actions'; import { TypeFilter } from './type_filter'; import { ActionTypeFilter } from './action_type_filter'; -import { RuleStatusFilter, getHealthColor } from './rule_status_filter'; +import { RuleExecutionStatusFilter, getHealthColor } from './rule_execution_status_filter'; import { loadRules, loadRuleAggregations, + loadRuleTags, loadRuleTypes, disableRule, enableRule, @@ -95,11 +94,15 @@ import { CenterJustifiedSpinner } from '../../../components/center_justified_spi import { ManageLicenseModal } from './manage_license_modal'; import { checkRuleTypeEnabled } from '../../../lib/check_rule_type_enabled'; import { RuleStatusDropdown } from './rule_status_dropdown'; +import { RuleTagBadge } from './rule_tag_badge'; import { PercentileSelectablePopover } from './percentile_selectable_popover'; import { RuleDurationFormat } from './rule_duration_format'; import { shouldShowDurationWarning } from '../../../lib/execution_duration_utils'; import { getFormattedSuccessRatio } from '../../../lib/monitoring_utils'; import { triggersActionsUiConfig } from '../../../../common/lib/config_api'; +import { RuleTagFilter } from './rule_tag_filter'; +import { RuleStatusFilter } from './rule_status_filter'; +import { getIsExperimentalFeatureEnabled } from '../../../../common/get_experimental_features'; const ENTER_KEY = 13; @@ -155,7 +158,10 @@ export const RulesList: React.FunctionComponent = () => { const [inputText, setInputText] = useState(); const [typesFilter, setTypesFilter] = useState([]); const [actionTypesFilter, setActionTypesFilter] = useState([]); - const [ruleStatusesFilter, setRuleStatusesFilter] = useState([]); + const [ruleExecutionStatusesFilter, setRuleExecutionStatusesFilter] = useState([]); + const [ruleStatusesFilter, setRuleStatusesFilter] = useState([]); + const [tags, setTags] = useState([]); + const [tagsFilter, setTagsFilter] = useState([]); const [ruleFlyoutVisible, setRuleFlyoutVisibility] = useState(false); const [editFlyoutVisible, setEditFlyoutVisibility] = useState(false); const [currentRuleToEdit, setCurrentRuleToEdit] = useState(null); @@ -165,6 +171,9 @@ export const RulesList: React.FunctionComponent = () => { ); const [showErrors, setShowErrors] = useState(false); + const isRuleTagFilterEnabled = getIsExperimentalFeatureEnabled('ruleTagFilter'); + const isRuleStatusFilterEnabled = getIsExperimentalFeatureEnabled('ruleStatusFilter'); + useEffect(() => { (async () => { setConfig(await triggersActionsUiConfig({ http })); @@ -227,7 +236,9 @@ export const RulesList: React.FunctionComponent = () => { percentileOptions, JSON.stringify(typesFilter), JSON.stringify(actionTypesFilter), + JSON.stringify(ruleExecutionStatusesFilter), JSON.stringify(ruleStatusesFilter), + JSON.stringify(tagsFilter), ]); useEffect(() => { @@ -286,9 +297,12 @@ export const RulesList: React.FunctionComponent = () => { searchText, typesFilter, actionTypesFilter, + ruleExecutionStatusesFilter, ruleStatusesFilter, + tagsFilter, sort, }); + await loadRuleTagsAggs(); await loadRuleAggs(); setRulesState({ isLoading: false, @@ -304,7 +318,9 @@ export const RulesList: React.FunctionComponent = () => { isEmpty(searchText) && isEmpty(typesFilter) && isEmpty(actionTypesFilter) && - isEmpty(ruleStatusesFilter) + isEmpty(ruleExecutionStatusesFilter) && + isEmpty(ruleStatusesFilter) && + isEmpty(tagsFilter) ); setNoData(rulesResponse.data.length === 0 && !isFilterApplied); @@ -330,7 +346,9 @@ export const RulesList: React.FunctionComponent = () => { searchText, typesFilter, actionTypesFilter, + ruleExecutionStatusesFilter, ruleStatusesFilter, + tagsFilter, }); if (rulesAggs?.ruleExecutionStatus) { setRulesStatusesTotal(rulesAggs.ruleExecutionStatus); @@ -347,6 +365,24 @@ export const RulesList: React.FunctionComponent = () => { } } + async function loadRuleTagsAggs() { + if (!isRuleTagFilterEnabled) { + return; + } + try { + const ruleTagsAggs = await loadRuleTags({ http }); + if (ruleTagsAggs?.ruleTags) { + setTags(ruleTagsAggs.ruleTags); + } + } catch (e) { + toasts.addDanger({ + title: i18n.translate('xpack.triggersActionsUI.sections.rulesList.unableToLoadRuleTags', { + defaultMessage: 'Unable to load rule tags', + }), + }); + } + } + const renderRuleStatusDropdown = (ruleEnabled: boolean | undefined, item: RuleTableItem) => { return ( { content={i18n.translate( 'xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.ruleExecutionPercentileTooltip', { - defaultMessage: `{percentileOrdinal} percentile of this rule's past {sampleLimit} execution durations (mm:ss).`, + defaultMessage: `{percentileOrdinal} percentile of this rule's past {sampleLimit} run durations (mm:ss).`, values: { percentileOrdinal: percentileOrdinals[selectedPercentile!], sampleLimit: MONITORING_HISTORY_LIMIT, @@ -588,42 +624,14 @@ export const RulesList: React.FunctionComponent = () => { sortable: false, width: '50px', 'data-test-subj': 'rulesTableCell-tagsPopover', - render: (tags: string[], item: RuleTableItem) => { - return tags.length > 0 ? ( - setTagPopoverOpenIndex(item.index)} - onClickAriaLabel="Tags" - iconOnClick={() => setTagPopoverOpenIndex(item.index)} - iconOnClickAriaLabel="Tags" - > - {tags.length} - - } - anchorPosition="upCenter" + render: (ruleTags: string[], item: RuleTableItem) => { + return ruleTags.length > 0 ? ( + setTagPopoverOpenIndex(-1)} - > - Tags -
- {tags.map((tag: string, index: number) => ( - - {tag} - - ))} - + tags={ruleTags} + onClick={() => setTagPopoverOpenIndex(item.index)} + onClose={() => setTagPopoverOpenIndex(-1)} + /> ) : null; }, }, @@ -635,7 +643,7 @@ export const RulesList: React.FunctionComponent = () => { content={i18n.translate( 'xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.lastExecutionDateTitle', { - defaultMessage: 'Start time of the last execution.', + defaultMessage: 'Start time of the last run.', } )} > @@ -791,7 +799,7 @@ export const RulesList: React.FunctionComponent = () => { content={i18n.translate( 'xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.successRatioTitle', { - defaultMessage: 'How often this rule executes successfully.', + defaultMessage: 'How often this rule runs successfully.', } )} > @@ -960,6 +968,22 @@ export const RulesList: React.FunctionComponent = () => { ); }; + const getRuleTagFilter = () => { + if (isRuleTagFilterEnabled) { + return []; + } + return []; + }; + + const getRuleStatusFilter = () => { + if (isRuleStatusFilterEnabled) { + return [ + , + ]; + } + return []; + }; + const toolsRight = [ { }) )} />, + ...getRuleTagFilter(), + ...getRuleStatusFilter(), setActionTypesFilter(ids)} />, - setRuleStatusesFilter(ids)} + selectedStatuses={ruleExecutionStatusesFilter} + onChange={(ids: string[]) => setRuleExecutionStatusesFilter(ids)} />, { }} />   - setRuleStatusesFilter(['error'])}> + setRuleExecutionStatusesFilter(['error'])}> { rulesListDatagrid: true, internalAlertsTable: true, rulesDetailLogs: true, + ruleTagFilter: true, + ruleStatusFilter: true, internalShareableComponentsSandbox: true, }, }); @@ -38,6 +40,14 @@ describe('getIsExperimentalFeatureEnabled', () => { expect(result).toEqual(true); + result = getIsExperimentalFeatureEnabled('ruleTagFilter'); + + expect(result).toEqual(true); + + result = getIsExperimentalFeatureEnabled('ruleStatusFilter'); + + expect(result).toEqual(true); + expect(() => getIsExperimentalFeatureEnabled('doesNotExist' as any)).toThrowError( `Invalid enable value doesNotExist. Allowed values are: ${allowedExperimentalValueKeys.join( ', ' diff --git a/x-pack/plugins/triggers_actions_ui/public/common/get_rule_status_filter.tsx b/x-pack/plugins/triggers_actions_ui/public/common/get_rule_status_filter.tsx new file mode 100644 index 0000000000000..77ac3fc51d703 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/common/get_rule_status_filter.tsx @@ -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 React from 'react'; +import { RuleStatusFilter } from '../application/sections'; +import type { RuleStatusFilterProps } from '../application/sections/rules_list/components/rule_status_filter'; + +export const getRuleStatusFilterLazy = (props: RuleStatusFilterProps) => { + return ; +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/get_rule_tag_badge.tsx b/x-pack/plugins/triggers_actions_ui/public/common/get_rule_tag_badge.tsx new file mode 100644 index 0000000000000..dc889402ab3c5 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/common/get_rule_tag_badge.tsx @@ -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 React from 'react'; +import { RuleTagBadge } from '../application/sections'; +import type { RuleTagBadgeProps } from '../application/sections/rules_list/components/rule_tag_badge'; + +export const getRuleTagBadgeLazy = (props: RuleTagBadgeProps) => { + return ; +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/get_rule_tag_filter.tsx b/x-pack/plugins/triggers_actions_ui/public/common/get_rule_tag_filter.tsx new file mode 100644 index 0000000000000..ccca277ef10ba --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/common/get_rule_tag_filter.tsx @@ -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 React from 'react'; +import { RuleTagFilter } from '../application/sections'; +import type { RuleTagFilterProps } from '../application/sections/rules_list/components/rule_tag_filter'; + +export const getRuleTagFilterLazy = (props: RuleTagFilterProps) => { + return ; +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.test.ts b/x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.test.ts index 9b0d122e24d4e..178c891dc3a34 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.test.ts @@ -7,7 +7,7 @@ import { loadIndexPatterns, - setSavedObjectsClient, + setDataViewsService, getMatchingIndices, getESIndexFields, } from './data_apis'; @@ -19,10 +19,8 @@ const http = httpServiceMock.createStartContract(); const pattern = 'test-pattern'; const indexes = ['test-index']; -const generateIndexPattern = (title: string) => ({ - attributes: { - title, - }, +const generateDataView = (title: string) => ({ + title, }); const mockIndices = { indices: ['indices1', 'indices2'] }; @@ -67,7 +65,7 @@ describe('Data API', () => { describe('index patterns', () => { beforeEach(() => { - setSavedObjectsClient({ + setDataViewsService({ find: mockFind, }); }); @@ -76,68 +74,15 @@ describe('Data API', () => { }); test('fetches the index patterns', async () => { - mockFind.mockResolvedValueOnce({ - savedObjects: [generateIndexPattern('index-1'), generateIndexPattern('index-2')], - total: 2, - }); + mockFind.mockResolvedValueOnce([generateDataView('index-1'), generateDataView('index-2')]); const results = await loadIndexPatterns(mockPattern); expect(mockFind).toBeCalledTimes(1); - expect(mockFind).toBeCalledWith({ - fields: ['title'], - page: 1, - perPage, - search: '*test-pattern*', - type: 'index-pattern', - }); + expect(mockFind).toBeCalledWith('*test-pattern*', perPage); expect(results).toEqual(['index-1', 'index-2']); }); - test(`fetches the index patterns as chunks and merges them, if the total number of index patterns more than ${perPage}`, async () => { - mockFind.mockResolvedValueOnce({ - savedObjects: [generateIndexPattern('index-1'), generateIndexPattern('index-2')], - total: 2010, - }); - mockFind.mockResolvedValueOnce({ - savedObjects: [generateIndexPattern('index-3'), generateIndexPattern('index-4')], - total: 2010, - }); - mockFind.mockResolvedValueOnce({ - savedObjects: [generateIndexPattern('index-5'), generateIndexPattern('index-6')], - total: 2010, - }); - const results = await loadIndexPatterns(mockPattern); - - expect(mockFind).toBeCalledTimes(3); - expect(mockFind).toHaveBeenNthCalledWith(1, { - fields: ['title'], - page: 1, - perPage, - search: '*test-pattern*', - type: 'index-pattern', - }); - expect(mockFind).toHaveBeenNthCalledWith(2, { - fields: ['title'], - page: 2, - perPage, - search: '*test-pattern*', - type: 'index-pattern', - }); - expect(mockFind).toHaveBeenNthCalledWith(3, { - fields: ['title'], - page: 3, - perPage, - search: '*test-pattern*', - type: 'index-pattern', - }); - expect(results).toEqual(['index-1', 'index-2', 'index-3', 'index-4', 'index-5', 'index-6']); - }); - - test('returns an empty array if one of the requests fails', async () => { - mockFind.mockResolvedValueOnce({ - savedObjects: [generateIndexPattern('index-1'), generateIndexPattern('index-2')], - total: 1010, - }); + test('returns an empty array if find requests fails', async () => { mockFind.mockRejectedValueOnce(500); const results = await loadIndexPatterns(mockPattern); diff --git a/x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts b/x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts index 55b1ef4be2c74..90f80dd3dc2f0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts @@ -6,6 +6,7 @@ */ import { HttpSetup } from '@kbn/core/public'; +import { DataViewsContract, DataView } from '@kbn/data-views-plugin/public'; const DATA_API_ROOT = '/api/triggers_actions_ui/data'; @@ -62,57 +63,25 @@ export async function getESIndexFields({ return fields; } -let savedObjectsClient: any; +type DataViewsService = Pick; +let dataViewsService: DataViewsService; -export const setSavedObjectsClient = (aSavedObjectsClient: any) => { - savedObjectsClient = aSavedObjectsClient; +export const setDataViewsService = (aDataViewsService: DataViewsService) => { + dataViewsService = aDataViewsService; }; -export const getSavedObjectsClient = () => { - return savedObjectsClient; +export const getDataViewsService = () => { + return dataViewsService; }; export const loadIndexPatterns = async (pattern: string) => { - let allSavedObjects = []; const formattedPattern = formatPattern(pattern); const perPage = 1000; try { - const { savedObjects, total } = await getSavedObjectsClient().find({ - type: 'index-pattern', - fields: ['title'], - page: 1, - search: formattedPattern, - perPage, - }); + const dataViews: DataView[] = await getDataViewsService().find(formattedPattern, perPage); - allSavedObjects = savedObjects; - - if (total > perPage) { - let currentPage = 2; - const numberOfPages = Math.ceil(total / perPage); - const promises = []; - - while (currentPage <= numberOfPages) { - promises.push( - getSavedObjectsClient().find({ - type: 'index-pattern', - page: currentPage, - fields: ['title'], - search: formattedPattern, - perPage, - }) - ); - currentPage++; - } - - const paginatedResults = await Promise.all(promises); - - allSavedObjects = paginatedResults.reduce((oldResult, result) => { - return oldResult.concat(result.savedObjects); - }, allSavedObjects); - } - return allSavedObjects.map((indexPattern: any) => indexPattern.attributes.title); + return dataViews.map((dataView: DataView) => dataView.title); } catch (e) { return []; } diff --git a/x-pack/plugins/triggers_actions_ui/public/common/types.ts b/x-pack/plugins/triggers_actions_ui/public/common/types.ts index 4aca07ad5482e..610962706661a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/types.ts @@ -24,3 +24,5 @@ export interface GroupByType { value: string; validNormalizedTypes: string[]; } + +export type { RuleStatus } from '../types'; diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index 94fe718363e2b..82923205666f6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -18,6 +18,7 @@ export type { RuleType, RuleTypeIndex, RuleTypeModel, + RuleStatus, ActionType, ActionTypeRegistryContract, RuleTypeRegistryContract, @@ -30,6 +31,7 @@ export type { RuleTypeParams, AsApiContract, RuleTableItem, + AlertsTableConfigurationRegistryContract, } from './types'; export { diff --git a/x-pack/plugins/triggers_actions_ui/public/mocks.ts b/x-pack/plugins/triggers_actions_ui/public/mocks.ts index 007b906e8747b..003748f7d421e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/mocks.ts +++ b/x-pack/plugins/triggers_actions_ui/public/mocks.ts @@ -26,6 +26,9 @@ import { } from './types'; import { getAlertsTableLazy } from './common/get_alerts_table'; import { getRuleStatusDropdownLazy } from './common/get_rule_status_dropdown'; +import { getRuleTagFilterLazy } from './common/get_rule_tag_filter'; +import { getRuleStatusFilterLazy } from './common/get_rule_status_filter'; +import { getRuleTagBadgeLazy } from './common/get_rule_tag_badge'; function createStartMock(): TriggersAndActionsUIPublicPluginStart { const actionTypeRegistry = new TypeRegistry(); @@ -64,6 +67,15 @@ function createStartMock(): TriggersAndActionsUIPublicPluginStart { getRuleStatusDropdown: (props) => { return getRuleStatusDropdownLazy(props); }, + getRuleTagFilter: (props) => { + return getRuleTagFilterLazy(props); + }, + getRuleStatusFilter: (props) => { + return getRuleStatusFilterLazy(props); + }, + getRuleTagBadge: (props) => { + return getRuleTagBadgeLazy(props); + }, }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index 7997552a81023..c95dd73102fd9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -31,6 +31,9 @@ import { getAddAlertFlyoutLazy } from './common/get_add_alert_flyout'; import { getEditAlertFlyoutLazy } from './common/get_edit_alert_flyout'; import { getAlertsTableLazy } from './common/get_alerts_table'; import { getRuleStatusDropdownLazy } from './common/get_rule_status_dropdown'; +import { getRuleTagFilterLazy } from './common/get_rule_tag_filter'; +import { getRuleStatusFilterLazy } from './common/get_rule_status_filter'; +import { getRuleTagBadgeLazy } from './common/get_rule_tag_badge'; import { ExperimentalFeaturesService } from './common/experimental_features_service'; import { ExperimentalFeatures, @@ -46,6 +49,9 @@ import type { ConnectorEditFlyoutProps, AlertsTableProps, RuleStatusDropdownProps, + RuleTagFilterProps, + RuleStatusFilterProps, + RuleTagBadgeProps, AlertsTableConfigurationRegistry, } from './types'; import { TriggersActionsUiConfigType } from '../common/types'; @@ -76,6 +82,9 @@ export interface TriggersAndActionsUIPublicPluginStart { ) => ReactElement; getAlertsTable: (props: AlertsTableProps) => ReactElement; getRuleStatusDropdown: (props: RuleStatusDropdownProps) => ReactElement; + getRuleTagFilter: (props: RuleTagFilterProps) => ReactElement; + getRuleStatusFilter: (props: RuleStatusFilterProps) => ReactElement; + getRuleTagBadge: (props: RuleTagBadgeProps) => ReactElement; } interface PluginsSetup { @@ -249,6 +258,15 @@ export class Plugin getRuleStatusDropdown: (props: RuleStatusDropdownProps) => { return getRuleStatusDropdownLazy(props); }, + getRuleTagFilter: (props: RuleTagFilterProps) => { + return getRuleTagFilterLazy(props); + }, + getRuleStatusFilter: (props: RuleStatusFilterProps) => { + return getRuleStatusFilterLazy(props); + }, + getRuleTagBadge: (props: RuleTagBadgeProps) => { + return getRuleTagBadgeLazy(props); + }, }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index cb68aab1899be..ef7ea7096961b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -48,6 +48,9 @@ import { import { RuleRegistrySearchRequestPagination } from '@kbn/rule-registry-plugin/common'; import { TypeRegistry } from './application/type_registry'; import type { ComponentOpts as RuleStatusDropdownProps } from './application/sections/rules_list/components/rule_status_dropdown'; +import type { RuleTagFilterProps } from './application/sections/rules_list/components/rule_tag_filter'; +import type { RuleStatusFilterProps } from './application/sections/rules_list/components/rule_status_filter'; +import type { RuleTagBadgeProps } from './application/sections/rules_list/components/rule_tag_badge'; // In Triggers and Actions we treat all `Alert`s as `SanitizedRule` // so the `Params` is a black-box of Record @@ -80,6 +83,9 @@ export type { ResolvedRule, SanitizedRule, RuleStatusDropdownProps, + RuleTagFilterProps, + RuleStatusFilterProps, + RuleTagBadgeProps, }; export type { ActionType, AsApiContract }; export { @@ -304,6 +310,7 @@ export interface RuleTypeModel { | React.LazyExoticComponent>>; requiresAppContext: boolean; defaultActionMessage?: string; + defaultRecoveryMessage?: string; } export interface IErrorObject { @@ -373,7 +380,12 @@ export interface TriggersActionsUiConfig { }; } -export type AlertsData = Record; +export enum AlertsField { + name = 'kibana.alert.rule.name', + reason = 'kibana.alert.reason', +} + +export type AlertsData = Record; export interface FetchAlertData { activePage: number; @@ -404,14 +416,22 @@ export interface AlertsTableProps { pageSize: number; pageSizeOptions: number[]; leadingControlColumns: EuiDataGridControlColumn[]; - renderCellValue: (props: EuiDataGridCellValueElementProps) => React.ReactNode; + renderCellValue: (props: RenderCellValueProps) => React.ReactNode; showCheckboxes: boolean; trailingControlColumns: EuiDataGridControlColumn[]; useFetchAlertsData: () => FetchAlertData; + alerts: AlertsData[]; 'data-test-subj': string; } +export type RenderCellValueProps = EuiDataGridCellValueElementProps & { + alert: AlertsData; + field: AlertsField; +}; + export interface AlertsTableConfigurationRegistry { id: string; columns: EuiDataGridColumn[]; } + +export type RuleStatus = 'enabled' | 'disabled' | 'snoozed'; diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_definition.ts b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_definition.ts index 4d64f02d2c14b..4928b368a96b4 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_definition.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_definition.ts @@ -95,6 +95,12 @@ export interface DrilldownDefinition< */ isConfigValid: ActionFactoryDefinition['isConfigValid']; + /** + * Compatibility check during drilldown creation. + * Could be used to filter out a drilldown if it's not compatible with the current context. + */ + isConfigurable?(context: FactoryContext): boolean; + /** * Name of EUI icon to display when showing this drilldown to user. */ diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_manager/containers/action_factory_picker/action_factory_picker.tsx b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_manager/containers/action_factory_picker/action_factory_picker.tsx index db9951f235dfc..f52ac6e161577 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_manager/containers/action_factory_picker/action_factory_picker.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_manager/containers/action_factory_picker/action_factory_picker.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; import { ActionFactoryPicker as ActionFactoryPickerUi } from '../../../../components/action_factory_picker'; import { useDrilldownManager } from '../context'; import { ActionFactoryView } from '../action_factory_view'; @@ -14,14 +15,19 @@ export const ActionFactoryPicker: React.FC = ({}) => { const drilldowns = useDrilldownManager(); const factory = drilldowns.useActionFactory(); const context = React.useMemo(() => drilldowns.getActionFactoryContext(), [drilldowns]); + const compatibleFactories = drilldowns.useCompatibleActionFactories(context); if (!!factory) { return ; } + if (!compatibleFactories) { + return ; + } + return ( { drilldowns.setActionFactory(actionFactory); diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_manager/state/drilldown_manager_state.ts b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_manager/state/drilldown_manager_state.ts index 15997355a2ae2..231057a50ee1f 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_manager/state/drilldown_manager_state.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_manager/state/drilldown_manager_state.ts @@ -6,9 +6,10 @@ */ import useObservable from 'react-use/lib/useObservable'; -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import type { SerializableRecord } from '@kbn/utility-types'; +import { useMemo } from 'react'; import { PublicDrilldownManagerProps, DrilldownManagerDependencies, @@ -255,6 +256,24 @@ export class DrilldownManagerState { return context; } + public getCompatibleActionFactories( + context: BaseActionFactoryContext + ): Observable { + const compatibleActionFactories$ = new BehaviorSubject(undefined); + Promise.allSettled( + this.deps.actionFactories.map((factory) => factory.isCompatible(context)) + ).then((factoryCompatibility) => { + compatibleActionFactories$.next( + this.deps.actionFactories.filter((_factory, i) => { + const result = factoryCompatibility[i]; + // treat failed isCompatible checks as non-compatible + return result.status === 'fulfilled' && result.value; + }) + ); + }); + return compatibleActionFactories$.asObservable(); + } + /** * Get state object of the drilldown which is currently being created. */ @@ -478,4 +497,9 @@ export class DrilldownManagerState { public readonly useActionFactory = () => useObservable(this.actionFactory$, this.actionFactory$.getValue()); public readonly useEvents = () => useObservable(this.events$, this.events$.getValue()); + public readonly useCompatibleActionFactories = (context: BaseActionFactoryContext) => + useObservable( + useMemo(() => this.getCompatibleActionFactories(context), [context]), + undefined + ); } diff --git a/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.ts b/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.ts index 63f90d5a55a1f..fb2dc3ea5bd03 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.ts @@ -116,6 +116,7 @@ export class UiActionsServiceEnhancements licenseFeatureName, supportedTriggers, isCompatible, + isConfigurable, telemetry, extract, inject, @@ -135,7 +136,7 @@ export class UiActionsServiceEnhancements extract, inject, getIconType: () => euiIcon, - isCompatible: async () => true, + isCompatible: async (context) => !isConfigurable || isConfigurable(context), create: (serializedAction) => ({ id: '', type: factoryId, diff --git a/x-pack/plugins/ux/public/services/rest/create_call_apm_api.ts b/x-pack/plugins/ux/public/services/rest/create_call_apm_api.ts index eddd5fe07694e..f7e9b6799e17d 100644 --- a/x-pack/plugins/ux/public/services/rest/create_call_apm_api.ts +++ b/x-pack/plugins/ux/public/services/rest/create_call_apm_api.ts @@ -8,16 +8,11 @@ import { CoreSetup, CoreStart } from '@kbn/core/public'; import type { ClientRequestParamsOf, - formatRequest as formatRequestType, ReturnOf, RouteRepositoryClient, ServerRouteRepository, } from '@kbn/server-route-repository'; -// @ts-expect-error cannot find module or correspondent type declarations -// The code and types are at separated folders on @kbn/server-route-repository -// so in order to do targeted imports they must me imported separately, and -// an error is expected here -import { formatRequest } from '@kbn/server-route-repository/target_node/format_request'; +import { formatRequest } from '@kbn/server-route-repository'; import type { APMServerRouteRepository, APIEndpoint, @@ -73,10 +68,7 @@ export function createCallApmApi(core: CoreStart | CoreSetup) { params?: Partial>; }; - const { method, pathname } = formatRequest( - endpoint, - params?.path - ) as ReturnType; + const { method, pathname } = formatRequest(endpoint, params?.path); return callApi(core, { ...options, diff --git a/x-pack/scripts/functional_tests.js b/x-pack/scripts/functional_tests.js deleted file mode 100644 index ee99785aa8fad..0000000000000 --- a/x-pack/scripts/functional_tests.js +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -require('../../src/setup_node_env'); -require('@kbn/test').runTestsCli([ - require.resolve('../test/functional/config.ccs.ts'), - require.resolve('../test/functional/config.js'), - require.resolve('../test/functional_basic/config.ts'), - require.resolve('../test/security_solution_endpoint/config.ts'), - require.resolve('../test/plugin_functional/config.ts'), - require.resolve('../test/functional_with_es_ssl/config.ts'), - require.resolve('../test/functional/config_security_basic.ts'), - require.resolve('../test/reporting_functional/reporting_and_security.config.ts'), - require.resolve('../test/reporting_functional/reporting_without_security.config.ts'), - require.resolve('../test/reporting_functional/reporting_and_deprecated_security.config.ts'), - require.resolve('../test/security_functional/login_selector.config.ts'), - require.resolve('../test/security_functional/oidc.config.ts'), - require.resolve('../test/security_functional/saml.config.ts'), - require.resolve('../test/functional_embedded/config.ts'), - require.resolve('../test/functional_cors/config.ts'), - require.resolve('../test/functional_enterprise_search/without_host_configured.config.ts'), - require.resolve('../test/saved_object_tagging/functional/config.ts'), - require.resolve('../test/usage_collection/config.ts'), - require.resolve('../test/fleet_functional/config.ts'), - require.resolve('../test/functional_synthetics/config.js'), - require.resolve('../test/api_integration/config_security_basic.ts'), - require.resolve('../test/api_integration/config_security_trial.ts'), - require.resolve('../test/api_integration/config.ts'), - require.resolve('../test/api_integration_basic/config.ts'), - require.resolve('../test/alerting_api_integration/basic/config.ts'), - require.resolve('../test/alerting_api_integration/spaces_only/config.ts'), - require.resolve('../test/alerting_api_integration/security_and_spaces/config.ts'), - require.resolve('../test/cases_api_integration/security_and_spaces/config_basic.ts'), - require.resolve('../test/cases_api_integration/security_and_spaces/config_trial.ts'), - require.resolve('../test/cases_api_integration/spaces_only/config.ts'), - require.resolve('../test/apm_api_integration/basic/config.ts'), - require.resolve('../test/apm_api_integration/trial/config.ts'), - require.resolve('../test/apm_api_integration/rules/config.ts'), - require.resolve('../test/detection_engine_api_integration/security_and_spaces/config.ts'), - require.resolve('../test/detection_engine_api_integration/basic/config.ts'), - require.resolve('../test/lists_api_integration/security_and_spaces/config.ts'), - require.resolve('../test/plugin_api_integration/config.ts'), - require.resolve('../test/rule_registry/security_and_spaces/config_basic.ts'), - require.resolve('../test/rule_registry/security_and_spaces/config_trial.ts'), - require.resolve('../test/rule_registry/spaces_only/config_basic.ts'), - require.resolve('../test/rule_registry/spaces_only/config_trial.ts'), - require.resolve('../test/security_api_integration/saml.config.ts'), - require.resolve('../test/security_api_integration/session_idle.config.ts'), - require.resolve('../test/security_api_integration/session_invalidate.config.ts'), - require.resolve('../test/security_api_integration/session_lifespan.config.ts'), - require.resolve('../test/security_api_integration/login_selector.config.ts'), - require.resolve('../test/security_api_integration/audit.config.ts'), - require.resolve('../test/security_api_integration/http_bearer.config.ts'), - require.resolve('../test/security_api_integration/http_no_auth_providers.config.ts'), - require.resolve('../test/security_api_integration/kerberos.config.ts'), - require.resolve('../test/security_api_integration/kerberos_anonymous_access.config.ts'), - require.resolve('../test/security_api_integration/pki.config.ts'), - require.resolve('../test/security_api_integration/oidc.config.ts'), - require.resolve('../test/security_api_integration/oidc_implicit_flow.config.ts'), - require.resolve('../test/security_api_integration/token.config.ts'), - require.resolve('../test/security_api_integration/anonymous.config.ts'), - require.resolve('../test/security_api_integration/anonymous_es_anonymous.config.ts'), - require.resolve('../test/observability_api_integration/basic/config.ts'), - require.resolve('../test/observability_api_integration/trial/config.ts'), - require.resolve('../test/observability_functional/with_rac_write.config.ts'), - require.resolve('../test/encrypted_saved_objects_api_integration/config.ts'), - require.resolve('../test/spaces_api_integration/spaces_only/config.ts'), - require.resolve('../test/spaces_api_integration/security_and_spaces/config_trial.ts'), - require.resolve('../test/spaces_api_integration/security_and_spaces/config_basic.ts'), - require.resolve('../test/saved_object_api_integration/security_and_spaces/config_trial.ts'), - require.resolve('../test/saved_object_api_integration/security_and_spaces/config_basic.ts'), - require.resolve('../test/saved_object_api_integration/spaces_only/config.ts'), - // TODO: Enable once RBAC timeline search strategy - // tests updated - // require.resolve('../test/timeline/security_and_spaces/config_basic.ts'), - require.resolve('../test/timeline/security_and_spaces/config_trial.ts'), - require.resolve('../test/ui_capabilities/security_and_spaces/config.ts'), - require.resolve('../test/ui_capabilities/spaces_only/config.ts'), - require.resolve('../test/upgrade_assistant_integration/config.js'), - require.resolve('../test/licensing_plugin/config.ts'), - require.resolve('../test/licensing_plugin/config.public.ts'), - require.resolve('../test/endpoint_api_integration_no_ingest/config.ts'), - require.resolve('../test/reporting_api_integration/reporting_and_security.config.ts'), - require.resolve('../test/reporting_api_integration/reporting_without_security.config.ts'), - require.resolve('../test/security_solution_endpoint_api_int/config.ts'), - require.resolve('../test/fleet_api_integration/config.ts'), - require.resolve('../test/search_sessions_integration/config.ts'), - require.resolve('../test/saved_object_tagging/api_integration/security_and_spaces/config.ts'), - require.resolve('../test/saved_object_tagging/api_integration/tagging_api/config.ts'), - require.resolve('../test/examples/config.ts'), - require.resolve('../test/functional_execution_context/config.ts'), -]); diff --git a/x-pack/scripts/functional_tests_server.js b/x-pack/scripts/functional_tests_server.js index 946f7ea3836a6..329fea019221b 100755 --- a/x-pack/scripts/functional_tests_server.js +++ b/x-pack/scripts/functional_tests_server.js @@ -8,4 +8,4 @@ process.env.ALLOW_PERFORMANCE_HOOKS_IN_TASK_MANAGER = true; require('../../src/setup_node_env'); -require('@kbn/test').startServersCli(require.resolve('../test/functional/config.js')); +require('@kbn/test').startServersCli(require.resolve('../test/functional/config.base.js')); diff --git a/x-pack/tasks/build.ts b/x-pack/tasks/build.ts index 4680464976d79..dacb6b6447aff 100644 --- a/x-pack/tasks/build.ts +++ b/x-pack/tasks/build.ts @@ -11,6 +11,7 @@ import { writeFileSync } from 'fs'; import { promisify } from 'util'; import { pipeline } from 'stream'; +import { discoverBazelPackages } from '@kbn/bazel-packages'; import { REPO_ROOT } from '@kbn/utils'; import { transformFileStream, transformFileWithBabel } from '@kbn/dev-utils'; import { ToolingLog } from '@kbn/tooling-log'; @@ -49,6 +50,11 @@ async function reportTask() { } async function copySourceAndBabelify() { + // get bazel packages inside x-pack + const xpackBazelPackages = (await discoverBazelPackages()) + .filter((pkg) => pkg.normalizedRepoRelativeDir.startsWith('x-pack/')) + .map((pkg) => `${pkg.normalizedRepoRelativeDir.replace('x-pack/', '')}/**`); + // copy source files and apply some babel transformations in the process await asyncPipeline( vfs.src( @@ -87,6 +93,7 @@ async function copySourceAndBabelify() { 'plugins/apm/ftr_e2e/**', 'plugins/apm/scripts/**', 'plugins/lists/server/scripts/**', + ...xpackBazelPackages, ], allowEmpty: true, } diff --git a/x-pack/test/accessibility/apps/advanced_settings.ts b/x-pack/test/accessibility/apps/advanced_settings.ts index 6f2dc78a7b35b..6c931f0a0e5a1 100644 --- a/x-pack/test/accessibility/apps/advanced_settings.ts +++ b/x-pack/test/accessibility/apps/advanced_settings.ts @@ -13,7 +13,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const toasts = getService('toasts'); - describe('Stack Management -Advanced Settings', () => { + describe('Stack Management -Advanced Settings Accessibility', () => { // click on Management > Advanced settings it('click on advanced settings ', async () => { await PageObjects.common.navigateToUrl('management', 'kibana/settings', { diff --git a/x-pack/test/accessibility/apps/canvas.ts b/x-pack/test/accessibility/apps/canvas.ts index 609c8bf5bb1ae..d9508e75bdf27 100644 --- a/x-pack/test/accessibility/apps/canvas.ts +++ b/x-pack/test/accessibility/apps/canvas.ts @@ -14,7 +14,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const { common } = getPageObjects(['common']); - describe('Canvas', () => { + describe('Canvas Accessibility', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/canvas/default'); await common.navigateToApp('canvas'); diff --git a/x-pack/test/accessibility/apps/dashboard_edit_panel.ts b/x-pack/test/accessibility/apps/dashboard_edit_panel.ts index 5624a5f25db2f..20b72e142f5c7 100644 --- a/x-pack/test/accessibility/apps/dashboard_edit_panel.ts +++ b/x-pack/test/accessibility/apps/dashboard_edit_panel.ts @@ -20,7 +20,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PANEL_TITLE = 'Visualization PieChart'; - describe('Dashboard Edit Panel', () => { + describe('Dashboard Edit Panel Accessibility', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/dashboard/drilldowns'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); diff --git a/x-pack/test/accessibility/apps/enterprise_search.ts b/x-pack/test/accessibility/apps/enterprise_search.ts index aa6910842b5eb..0a1a5d68d9621 100644 --- a/x-pack/test/accessibility/apps/enterprise_search.ts +++ b/x-pack/test/accessibility/apps/enterprise_search.ts @@ -14,7 +14,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const { common } = getPageObjects(['common']); - describe('Enterprise Search', () => { + describe('Enterprise Search Accessibility', () => { // NOTE: These accessibility tests currently only run against Enterprise Search in Kibana // without a sidecar Enterprise Search service/host configured, and as such only test // the basic setup guides and not the full application(s) diff --git a/x-pack/test/accessibility/apps/grok_debugger.ts b/x-pack/test/accessibility/apps/grok_debugger.ts index ecb62ffd53177..8ee9114c7da0a 100644 --- a/x-pack/test/accessibility/apps/grok_debugger.ts +++ b/x-pack/test/accessibility/apps/grok_debugger.ts @@ -12,8 +12,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); const grokDebugger = getService('grokDebugger'); - // this test is failing as there is a violation https://github.com/elastic/kibana/issues/62102 - describe.skip('Dev tools grok debugger', () => { + // Fixes:https://github.com/elastic/kibana/issues/62102 + describe('Dev tools grok debugger', () => { before(async () => { await PageObjects.common.navigateToApp('grokDebugger'); await grokDebugger.assertExists(); diff --git a/x-pack/test/accessibility/apps/home.ts b/x-pack/test/accessibility/apps/home.ts index 61297859c29f8..544a32843f7f3 100644 --- a/x-pack/test/accessibility/apps/home.ts +++ b/x-pack/test/accessibility/apps/home.ts @@ -13,7 +13,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const find = getService('find'); - describe('Kibana Home', () => { + describe('Kibana Home Accessibility', () => { before(async () => { await common.navigateToApp('home'); }); diff --git a/x-pack/test/accessibility/apps/index_lifecycle_management.ts b/x-pack/test/accessibility/apps/index_lifecycle_management.ts index 6cec8d1cb891a..fc3ec1ff5cf81 100644 --- a/x-pack/test/accessibility/apps/index_lifecycle_management.ts +++ b/x-pack/test/accessibility/apps/index_lifecycle_management.ts @@ -63,14 +63,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { throw new Error(`Could not find ${policyName} in policy table`); }; - describe('Index Lifecycle Management', async () => { + describe('Index Lifecycle Management Accessibility', async () => { before(async () => { await esClient.snapshot.createRepository({ name: REPO_NAME, body: { type: 'fs', settings: { - // use one of the values defined in path.repo in test/functional/config.js + // use one of the values defined in path.repo in test/functional/config.base.js location: '/tmp/', }, }, diff --git a/x-pack/test/accessibility/apps/ingest_node_pipelines.ts b/x-pack/test/accessibility/apps/ingest_node_pipelines.ts index a09a07bb01267..4bbd9cde06d2d 100644 --- a/x-pack/test/accessibility/apps/ingest_node_pipelines.ts +++ b/x-pack/test/accessibility/apps/ingest_node_pipelines.ts @@ -14,7 +14,7 @@ export default function ({ getService, getPageObjects }: any) { const log = getService('log'); const a11y = getService('a11y'); /* this is the wrapping service around axe */ - describe('Ingest Pipelines', async () => { + describe('Ingest Pipelines Accessibility', async () => { before(async () => { await putSamplePipeline(esClient); await common.navigateToApp('ingestPipelines'); diff --git a/x-pack/test/accessibility/apps/kibana_overview.ts b/x-pack/test/accessibility/apps/kibana_overview.ts index 9d21f08a900cc..19af9c2828d35 100644 --- a/x-pack/test/accessibility/apps/kibana_overview.ts +++ b/x-pack/test/accessibility/apps/kibana_overview.ts @@ -11,7 +11,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'home']); const a11y = getService('a11y'); - describe('Kibana overview', () => { + describe('Kibana overview Accessibility', () => { const esArchiver = getService('esArchiver'); before(async () => { diff --git a/x-pack/test/accessibility/apps/lens.ts b/x-pack/test/accessibility/apps/lens.ts index 8a46d662a61cf..18459b56c0542 100644 --- a/x-pack/test/accessibility/apps/lens.ts +++ b/x-pack/test/accessibility/apps/lens.ts @@ -15,7 +15,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const listingTable = getService('listingTable'); const kibanaServer = getService('kibanaServer'); - describe('Lens', () => { + describe('Lens Accessibility', () => { const lensChartName = 'MyLensChart'; before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/logstash_functional'); diff --git a/x-pack/test/accessibility/apps/license_management.ts b/x-pack/test/accessibility/apps/license_management.ts index 891a682e653ba..7693ebb197ff1 100644 --- a/x-pack/test/accessibility/apps/license_management.ts +++ b/x-pack/test/accessibility/apps/license_management.ts @@ -12,7 +12,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); const testSubjects = getService('testSubjects'); - describe('License Management page a11y tests', () => { + describe('License Management page Accessibility', () => { before(async () => { await PageObjects.common.navigateToApp('licenseManagement'); }); diff --git a/x-pack/test/accessibility/apps/login_page.ts b/x-pack/test/accessibility/apps/login_page.ts index 154517d09502e..6463e63fb2e49 100644 --- a/x-pack/test/accessibility/apps/login_page.ts +++ b/x-pack/test/accessibility/apps/login_page.ts @@ -14,8 +14,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const PageObjects = getPageObjects(['common', 'security']); - // Failing: See https://github.com/elastic/kibana/issues/96372 - describe('Security', () => { + describe('Security Accessibility', () => { describe('Login Page', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/empty_kibana'); diff --git a/x-pack/test/accessibility/apps/maps.ts b/x-pack/test/accessibility/apps/maps.ts index c5b824c330829..0e4142e6ade60 100644 --- a/x-pack/test/accessibility/apps/maps.ts +++ b/x-pack/test/accessibility/apps/maps.ts @@ -13,7 +13,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const PageObjects = getPageObjects(['common', 'settings', 'header', 'home', 'maps']); - describe('Maps app meets ally validations', () => { + describe('Maps app Accessibility', () => { before(async () => { await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', { useActualUrl: true, diff --git a/x-pack/test/accessibility/apps/ml.ts b/x-pack/test/accessibility/apps/ml.ts index fd05d2af07747..2b99b665daced 100644 --- a/x-pack/test/accessibility/apps/ml.ts +++ b/x-pack/test/accessibility/apps/ml.ts @@ -5,15 +5,13 @@ * 2.0. */ -import path from 'path'; - import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const a11y = getService('a11y'); const ml = getService('ml'); - describe('ml', () => { + describe('ml Accessibility', () => { const esArchiver = getService('esArchiver'); before(async () => { @@ -79,16 +77,8 @@ export default function ({ getService }: FtrProviderContext) { const dfaJobType = 'outlier_detection'; const dfaJobId = `ihp_ally_${Date.now()}`; - const uploadFilePath = path.join( - __dirname, - '..', - '..', - 'functional', - 'apps', - 'ml', - 'data_visualizer', - 'files_to_import', - 'artificial_server_log' + const uploadFilePath = require.resolve( + '../../functional/apps/ml/data_visualizer/files_to_import/artificial_server_log' ); before(async () => { @@ -261,8 +251,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsCreation.selectJobType(dfaJobType); await ml.testExecution.logTestStep('displays the source data preview'); await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewExists(); - await ml.testExecution.logTestStep('enables the source data preview histogram charts'); - await ml.dataFrameAnalyticsCreation.enableSourceDataPreviewHistogramCharts(true); + await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewHistogramChartEnabled(true); await ml.testExecution.logTestStep('displays the include fields selection'); await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists(); await a11y.testAppSnapshot(); diff --git a/x-pack/test/accessibility/apps/ml_embeddables_in_dashboard.ts b/x-pack/test/accessibility/apps/ml_embeddables_in_dashboard.ts index 9532e8e365655..8f8bc67304c0d 100644 --- a/x-pack/test/accessibility/apps/ml_embeddables_in_dashboard.ts +++ b/x-pack/test/accessibility/apps/ml_embeddables_in_dashboard.ts @@ -60,7 +60,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'timePicker', 'dashboard']); const a11y = getService('a11y'); /* this is the wrapping service around axe */ - describe('machine learning embeddables anomaly charts', function () { + describe('machine learning embeddables anomaly charts Accessibility', function () { before(async () => { await ml.securityCommon.createMlRoles(); await ml.securityCommon.createMlUsers(); diff --git a/x-pack/test/accessibility/apps/painless_lab.ts b/x-pack/test/accessibility/apps/painless_lab.ts index c25930941b5eb..a0a4712dbe4e3 100644 --- a/x-pack/test/accessibility/apps/painless_lab.ts +++ b/x-pack/test/accessibility/apps/painless_lab.ts @@ -14,7 +14,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); const retry = getService('retry'); - describe('Accessibility Painless Lab Editor', () => { + describe('Accessibility Painless Lab Editor Accessibility', () => { before(async () => { await PageObjects.common.navigateToApp('painlessLab'); }); diff --git a/x-pack/test/accessibility/apps/remote_clusters.ts b/x-pack/test/accessibility/apps/remote_clusters.ts index 67c85eda60a4a..deb0e4a090b8c 100644 --- a/x-pack/test/accessibility/apps/remote_clusters.ts +++ b/x-pack/test/accessibility/apps/remote_clusters.ts @@ -79,7 +79,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); const retry = getService('retry'); - describe('Remote Clusters', () => { + describe('Remote Clusters Accessibility', () => { beforeEach(async () => { await PageObjects.common.navigateToApp('remoteClusters'); }); diff --git a/x-pack/test/accessibility/apps/reporting.ts b/x-pack/test/accessibility/apps/reporting.ts index c6a6571cc0ff6..f1ac0770c9587 100644 --- a/x-pack/test/accessibility/apps/reporting.ts +++ b/x-pack/test/accessibility/apps/reporting.ts @@ -16,7 +16,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const security = getService('security'); const log = getService('log'); - describe('Reporting', () => { + describe('Reporting Accessibility', () => { const createReportingUser = async () => { await security.user.create(reporting.REPORTING_USER_USERNAME, { password: reporting.REPORTING_USER_PASSWORD, diff --git a/x-pack/test/accessibility/apps/roles.ts b/x-pack/test/accessibility/apps/roles.ts index 3c40e664d7da2..5369dced427fa 100644 --- a/x-pack/test/accessibility/apps/roles.ts +++ b/x-pack/test/accessibility/apps/roles.ts @@ -17,7 +17,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const kibanaServer = getService('kibanaServer'); - describe('Kibana roles page a11y tests', () => { + describe('Kibana roles page Accessibility', () => { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); await kibanaServer.uiSettings.update({ diff --git a/x-pack/test/accessibility/apps/search_profiler.ts b/x-pack/test/accessibility/apps/search_profiler.ts index 47909662fb132..30043f8f4157f 100644 --- a/x-pack/test/accessibility/apps/search_profiler.ts +++ b/x-pack/test/accessibility/apps/search_profiler.ts @@ -15,7 +15,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); const esArchiver = getService('esArchiver'); - describe('Accessibility Search Profiler Editor', () => { + describe('Search Profiler Editor Accessibility', () => { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); await PageObjects.common.navigateToApp('searchProfiler'); diff --git a/x-pack/test/accessibility/apps/search_sessions.ts b/x-pack/test/accessibility/apps/search_sessions.ts index 30bef9086a4b6..42a2f387612ac 100644 --- a/x-pack/test/accessibility/apps/search_sessions.ts +++ b/x-pack/test/accessibility/apps/search_sessions.ts @@ -15,7 +15,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const find = getService('find'); const esArchiver = getService('esArchiver'); - describe('Search sessions a11y tests', () => { + describe('Search sessions Accessibility', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/data/search_sessions'); await PageObjects.searchSessionsManagement.goTo(); diff --git a/x-pack/test/accessibility/apps/security_solution.ts b/x-pack/test/accessibility/apps/security_solution.ts index 8014e03152b17..ef930f093eb4a 100644 --- a/x-pack/test/accessibility/apps/security_solution.ts +++ b/x-pack/test/accessibility/apps/security_solution.ts @@ -15,7 +15,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); // FLAKY: https://github.com/elastic/kibana/issues/95707 - describe.skip('Security Solution', () => { + describe.skip('Security Solution Accessibility', () => { before(async () => { await security.testUser.setRoles(['superuser'], { skipBrowserRefresh: true }); await common.navigateToApp('security'); diff --git a/x-pack/test/accessibility/apps/spaces.ts b/x-pack/test/accessibility/apps/spaces.ts index 567f958f5f8a4..38b34054911f6 100644 --- a/x-pack/test/accessibility/apps/spaces.ts +++ b/x-pack/test/accessibility/apps/spaces.ts @@ -18,7 +18,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const toasts = getService('toasts'); - describe('Kibana spaces page meets a11y validations', () => { + describe('Kibana Spaces Accessibility', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/empty_kibana'); await PageObjects.common.navigateToApp('home'); diff --git a/x-pack/test/accessibility/apps/tags.ts b/x-pack/test/accessibility/apps/tags.ts index 8174c8fa8c06b..da51f2f0535e2 100644 --- a/x-pack/test/accessibility/apps/tags.ts +++ b/x-pack/test/accessibility/apps/tags.ts @@ -16,7 +16,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const toasts = getService('toasts'); - describe('Kibana tags page meets a11y validations', () => { + describe('Kibana Tags Page Accessibility', () => { before(async () => { await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', { useActualUrl: true, diff --git a/x-pack/test/accessibility/apps/transform.ts b/x-pack/test/accessibility/apps/transform.ts index 59f19471490b8..fa54ea4ad6766 100644 --- a/x-pack/test/accessibility/apps/transform.ts +++ b/x-pack/test/accessibility/apps/transform.ts @@ -11,7 +11,7 @@ export default function ({ getService }: FtrProviderContext) { const a11y = getService('a11y'); const transform = getService('transform'); - describe('transform', () => { + describe('transform Accessibility', () => { const esArchiver = getService('esArchiver'); before(async () => { diff --git a/x-pack/test/accessibility/apps/upgrade_assistant.ts b/x-pack/test/accessibility/apps/upgrade_assistant.ts index 1f7fd2a654bca..fffb6e684ba4a 100644 --- a/x-pack/test/accessibility/apps/upgrade_assistant.ts +++ b/x-pack/test/accessibility/apps/upgrade_assistant.ts @@ -53,7 +53,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const es = getService('es'); const log = getService('log'); - describe.skip('Upgrade Assistant', () => { + describe.skip('Upgrade Assistant Accessibility', () => { before(async () => { await PageObjects.upgradeAssistant.navigateToPage(); diff --git a/x-pack/test/accessibility/apps/uptime.ts b/x-pack/test/accessibility/apps/uptime.ts index 41664c5920b82..49243c37fe730 100644 --- a/x-pack/test/accessibility/apps/uptime.ts +++ b/x-pack/test/accessibility/apps/uptime.ts @@ -19,7 +19,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const es = getService('es'); const toasts = getService('toasts'); - describe('uptime', () => { + describe('uptime Accessibility', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/uptime/blank'); await makeChecks(es, A11Y_TEST_MONITOR_ID, 150, 1, 1000, { diff --git a/x-pack/test/accessibility/apps/users.ts b/x-pack/test/accessibility/apps/users.ts index 8682cc8f0a884..5833a19580c24 100644 --- a/x-pack/test/accessibility/apps/users.ts +++ b/x-pack/test/accessibility/apps/users.ts @@ -17,7 +17,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const find = getService('find'); const retry = getService('retry'); - describe('Kibana users page a11y tests', () => { + describe('Kibana users Accessibility', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/empty_kibana'); await PageObjects.security.clickElasticsearchUsers(); diff --git a/x-pack/test/accessibility/config.ts b/x-pack/test/accessibility/config.ts index e85b8a9ef17d8..30c130df23a15 100644 --- a/x-pack/test/accessibility/config.ts +++ b/x-pack/test/accessibility/config.ts @@ -10,7 +10,7 @@ import { services } from './services'; import { pageObjects } from './page_objects'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); return { ...functionalConfig.getAll(), diff --git a/x-pack/test/alerting_api_integration/basic/tests/index.ts b/x-pack/test/alerting_api_integration/basic/tests/index.ts index 6ddb09b1c666e..ba6ebbe6a944e 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/index.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/index.ts @@ -13,8 +13,6 @@ export default function alertingApiIntegrationTests({ getService, }: FtrProviderContext) { describe('alerting api integration basic license', function () { - this.tags('ciGroup13'); - loadTestFile(require.resolve('./actions')); loadTestFile(require.resolve('./alerts')); }); diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index 14039ad3360a0..ffdf0c09ad216 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -25,6 +25,7 @@ interface CreateTestConfigOptions { customizeLocalHostSsl?: boolean; rejectUnauthorized?: boolean; // legacy emailDomainsAllowed?: string[]; + testFiles?: string[]; } // test.not-enabled is specifically not enabled @@ -64,6 +65,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) customizeLocalHostSsl = false, rejectUnauthorized = true, // legacy emailDomainsAllowed = undefined, + testFiles = undefined, } = options; return async ({ readConfigFile }: FtrConfigProviderContext) => { @@ -139,7 +141,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) : []; return { - testFiles: [require.resolve(`../${name}/tests/`)], + testFiles: testFiles ? testFiles : [require.resolve(`../${name}/tests/`)], servers, services, junit: { diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/plugin.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/plugin.ts index 4525768a0fb42..82516bf4a417d 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/plugin.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/plugin.ts @@ -15,6 +15,7 @@ import { ActionType } from '@kbn/actions-plugin/server'; import { initPlugin as initPagerduty } from './pagerduty_simulation'; import { initPlugin as initSwimlane } from './swimlane_simulation'; import { initPlugin as initServiceNow } from './servicenow_simulation'; +import { initPlugin as initServiceNowOAuth } from './servicenow_oauth_simulation'; import { initPlugin as initJira } from './jira_simulation'; import { initPlugin as initResilient } from './resilient_simulation'; import { initPlugin as initSlack } from './slack_simulation'; @@ -49,6 +50,7 @@ export function getAllExternalServiceSimulatorPaths(): string[] { allPaths.push(`/api/_${NAME}/${ExternalServiceSimulator.RESILIENT}/rest/orgs/201/incidents`); allPaths.push(`/api/_${NAME}/${ExternalServiceSimulator.MS_EXCHANGE}/users/test@/sendMail`); allPaths.push(`/api/_${NAME}/${ExternalServiceSimulator.MS_EXCHANGE}/1234567/oauth2/v2.0/token`); + allPaths.push(`/api/_${NAME}/${ExternalServiceSimulator.SERVICENOW}/oauth_token.do`); return allPaths; } @@ -129,6 +131,10 @@ export class FixturePlugin implements Plugin, + res: KibanaResponseFactory + ): Promise> { + return jsonResponse(res, 200, { + access_token: 'tokentokentoken', + expires_in: 3660, + token_type: 'Bearer', + }); + } + ); +} + +function jsonResponse( + res: KibanaResponseFactory, + code: number, + object: Record = {} +) { + return res.custom>({ body: object, statusCode: code }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/config.ts b/x-pack/test/alerting_api_integration/security_and_spaces/config.ts deleted file mode 100644 index 314f65c167048..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/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 { createTestConfig } from '../common/config'; - -// eslint-disable-next-line import/no-default-export -export default createTestConfig('security_and_spaces', { - disabledPlugins: [], - license: 'trial', - ssl: true, - enableActionsProxy: true, - publicBaseUrl: true, -}); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/config.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/config.ts new file mode 100644 index 0000000000000..9b90a4c18fdf0 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/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 { createTestConfig } from '../../common/config'; + +// eslint-disable-next-line import/no-default-export +export default createTestConfig('security_and_spaces', { + disabledPlugins: [], + license: 'trial', + ssl: true, + enableActionsProxy: true, + publicBaseUrl: true, + testFiles: [require.resolve('./tests')], +}); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/create.ts new file mode 100644 index 0000000000000..e601c6ee15ec7 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/create.ts @@ -0,0 +1,555 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { UserAtSpaceScenarios } from '../../../scenarios'; +import { + checkAAD, + getTestRuleData, + getConsumerUnauthorizedErrorMessage, + getUrlPrefix, + ObjectRemover, + getProducerUnauthorizedErrorMessage, + TaskManagerDoc, +} from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function createAlertTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const es = getService('es'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('create', () => { + const objectRemover = new ObjectRemover(supertest); + + after(() => objectRemover.removeAll()); + + async function getScheduledTask(id: string): Promise { + const scheduledTask = await es.get({ + id: `task:${id}`, + index: '.kibana_task_manager', + }); + return scheduledTask._source!; + } + + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + describe(scenario.id, () => { + it('should handle create alert request appropriately', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send( + getTestRuleData({ + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + ], + }) + ); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.noop', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to get actions`, + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); + expect(response.body).to.eql({ + id: response.body.id, + name: 'abc', + tags: ['foo'], + actions: [ + { + id: createdAction.id, + connector_type_id: createdAction.connector_type_id, + group: 'default', + params: {}, + }, + ], + enabled: true, + rule_type_id: 'test.noop', + consumer: 'alertsFixture', + params: {}, + created_by: user.username, + schedule: { interval: '1m' }, + scheduled_task_id: response.body.scheduled_task_id, + created_at: response.body.created_at, + updated_at: response.body.updated_at, + throttle: '1m', + notify_when: 'onThrottleInterval', + updated_by: user.username, + api_key_owner: user.username, + mute_all: false, + muted_alert_ids: [], + execution_status: response.body.execution_status, + }); + expect(typeof response.body.scheduled_task_id).to.be('string'); + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); + + const taskRecord = await getScheduledTask(response.body.scheduled_task_id); + expect(taskRecord.type).to.eql('task'); + expect(taskRecord.task.taskType).to.eql('alerting:test.noop'); + expect(JSON.parse(taskRecord.task.params)).to.eql({ + alertId: response.body.id, + spaceId: space.id, + consumer: 'alertsFixture', + }); + // Ensure AAD isn't broken + await checkAAD({ + supertest, + spaceId: space.id, + type: 'alert', + id: response.body.id, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle create alert request appropriately when consumer is the same as producer', async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send( + getTestRuleData({ + rule_type_id: 'test.restricted-noop', + consumer: 'alertsRestrictedFixture', + }) + ); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all at space2': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.restricted-noop', + 'alertsRestrictedFixture' + ), + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle create alert request appropriately when consumer is not the producer', async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send( + getTestRuleData({ + rule_type_id: 'test.unrestricted-noop', + consumer: 'alertsFixture', + }) + ); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.unrestricted-noop', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getProducerUnauthorizedErrorMessage( + 'create', + 'test.unrestricted-noop', + 'alertsRestrictedFixture' + ), + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle create alert request appropriately when consumer is "alerts"', async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send( + getTestRuleData({ + rule_type_id: 'test.noop', + consumer: 'alerts', + }) + ); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage('create', 'test.noop', 'alerts'), + statusCode: 403, + }); + break; + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getProducerUnauthorizedErrorMessage( + 'create', + 'test.noop', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'superuser at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle create alert request appropriately when consumer is unknown', async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send( + getTestRuleData({ + rule_type_id: 'test.noop', + consumer: 'some consumer patrick invented', + }) + ); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all at space2': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + case 'superuser at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.noop', + 'some consumer patrick invented' + ), + statusCode: 403, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle create alert request appropriately when an alert is disabled ', async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send(getTestRuleData({ enabled: false })); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.noop', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); + expect(response.body.scheduled_task_id).to.eql(undefined); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle create alert request appropriately when alert name has leading and trailing whitespaces', async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send( + getTestRuleData({ + name: ' leading and trailing whitespace ', + }) + ); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.noop', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body.name).to.eql(' leading and trailing whitespace '); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle create alert request appropriately when alert type is unregistered', async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send( + getTestRuleData({ + rule_type_id: 'test.unregistered-alert-type', + }) + ); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all at space2': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + case 'superuser at space1': + expect(response.statusCode).to.eql(400); + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: 'Rule type "test.unregistered-alert-type" is not registered.', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle create alert request appropriately when payload is empty and invalid', async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({}); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all at space2': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(400); + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: '[request body.name]: expected value of type [string] but got [undefined]', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`should handle create alert request appropriately when params isn't valid`, async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send( + getTestRuleData({ + rule_type_id: 'test.validation', + }) + ); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.validation', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(400); + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'params invalid: [param1]: expected value of type [string] but got [undefined]', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle create alert request appropriately when interval schedule is wrong syntax', async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send(getTestRuleData({ schedule: { interval: '10x' } })); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all at space2': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(400); + expect(response.body).to.eql({ + error: 'Bad Request', + message: '[request body.schedule.interval]: string is not a valid duration: 10x', + statusCode: 400, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle create alert request appropriately when interval schedule is 0', async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send(getTestRuleData({ schedule: { interval: '0s' } })); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all at space2': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(400); + expect(response.body).to.eql({ + error: 'Bad Request', + message: '[request body.schedule.interval]: string is not a valid duration: 0s', + statusCode: 400, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + }); + } + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/delete.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/delete.ts new file mode 100644 index 0000000000000..2b6086cf38d92 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/delete.ts @@ -0,0 +1,367 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { UserAtSpaceScenarios } from '../../../scenarios'; +import { + getUrlPrefix, + getTestRuleData, + getConsumerUnauthorizedErrorMessage, + getProducerUnauthorizedErrorMessage, + ObjectRemover, +} from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function createDeleteTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const es = getService('es'); + const retry = getService('retry'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('delete', () => { + const objectRemover = new ObjectRemover(supertest); + + after(() => objectRemover.removeAll()); + + async function getScheduledTask(id: string) { + return await es.get({ + id: `task:${id}`, + index: '.kibana_task_manager', + }); + } + + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + describe(scenario.id, () => { + it('should handle delete alert request appropriately', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + const response = await supertestWithoutAuth + .delete(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'delete', + 'test.noop', + 'alertsFixture' + ), + statusCode: 403, + }); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + // Ensure task still exists + await getScheduledTask(createdAlert.scheduled_task_id); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(204); + expect(response.body).to.eql(''); + try { + await getScheduledTask(createdAlert.scheduled_task_id); + throw new Error('Should have removed scheduled task'); + } catch (e) { + expect(e.meta.statusCode).to.eql(404); + } + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle delete alert request appropriately when consumer is the same as producer', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.restricted-noop', + consumer: 'alertsRestrictedFixture', + }) + ) + .expect(200); + + const response = await supertestWithoutAuth + .delete(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'delete', + 'test.restricted-noop', + 'alertsRestrictedFixture' + ), + statusCode: 403, + }); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + // Ensure task still exists + await getScheduledTask(createdAlert.scheduled_task_id); + break; + case 'superuser at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(204); + expect(response.body).to.eql(''); + try { + await getScheduledTask(createdAlert.scheduled_task_id); + throw new Error('Should have removed scheduled task'); + } catch (e) { + expect(e.meta.statusCode).to.eql(404); + } + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle delete alert request appropriately when consumer is not the producer', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.unrestricted-noop', + consumer: 'alertsFixture', + }) + ) + .expect(200); + + const response = await supertestWithoutAuth + .delete(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'delete', + 'test.unrestricted-noop', + 'alertsFixture' + ), + statusCode: 403, + }); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + // Ensure task still exists + await getScheduledTask(createdAlert.scheduled_task_id); + break; + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getProducerUnauthorizedErrorMessage( + 'delete', + 'test.unrestricted-noop', + 'alertsRestrictedFixture' + ), + statusCode: 403, + }); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + // Ensure task still exists + await getScheduledTask(createdAlert.scheduled_task_id); + break; + case 'superuser at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(204); + expect(response.body).to.eql(''); + try { + await getScheduledTask(createdAlert.scheduled_task_id); + throw new Error('Should have removed scheduled task'); + } catch (e) { + expect(e.meta.statusCode).to.eql(404); + } + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle delete alert request appropriately when consumer is "alerts"', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.noop', + consumer: 'alerts', + }) + ) + .expect(200); + + const response = await supertestWithoutAuth + .delete(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage('delete', 'test.noop', 'alerts'), + statusCode: 403, + }); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + break; + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getProducerUnauthorizedErrorMessage( + 'delete', + 'test.noop', + 'alertsFixture' + ), + statusCode: 403, + }); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + // Ensure task still exists + await getScheduledTask(createdAlert.scheduled_task_id); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(204); + expect(response.body).to.eql(''); + try { + await getScheduledTask(createdAlert.scheduled_task_id); + throw new Error('Should have removed scheduled task'); + } catch (e) { + expect(e.meta.statusCode).to.eql(404); + } + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`shouldn't delete alert from another space`, async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .delete(`${getUrlPrefix('other')}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password); + + expect(response.statusCode).to.eql(404); + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + case 'superuser at space1': + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: `Saved object [alert/${createdAlert.id}] not found`, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should still be able to delete alert when AAD is broken', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + + await retry.try(async () => { + await supertest + .put( + `${getUrlPrefix(space.id)}/api/alerts_fixture/saved_object/alert/${createdAlert.id}` + ) + .set('kbn-xsrf', 'foo') + .send({ + attributes: { + name: 'bar', + }, + }) + .expect(200); + }); + + const response = await supertestWithoutAuth + .delete(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'delete', + 'test.noop', + 'alertsFixture' + ), + statusCode: 403, + }); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + // Ensure task still exists + await getScheduledTask(createdAlert.scheduled_task_id); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(204); + expect(response.body).to.eql(''); + try { + await getScheduledTask(createdAlert.scheduled_task_id); + throw new Error('Should have removed scheduled task'); + } catch (e) { + expect(e.meta.statusCode).to.eql(404); + } + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + }); + } + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/disable.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/disable.ts index 8a4266eb8dc8a..864de743ea343 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/disable.ts @@ -6,8 +6,8 @@ */ import expect from '@kbn/expect'; -import { UserAtSpaceScenarios } from '../../scenarios'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { AlertUtils, checkAAD, @@ -16,7 +16,7 @@ import { ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, -} from '../../../common/lib'; +} from '../../../../common/lib'; // eslint-disable-next-line import/no-default-export export default function createDisableAlertTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/enable.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/enable.ts index 6d667eff24072..0aba468174cff 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/enable.ts @@ -6,8 +6,8 @@ */ import expect from '@kbn/expect'; -import { UserAtSpaceScenarios } from '../../scenarios'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { AlertUtils, checkAAD, @@ -17,7 +17,7 @@ import { getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, TaskManagerDoc, -} from '../../../common/lib'; +} from '../../../../common/lib'; // eslint-disable-next-line import/no-default-export export default function createEnableAlertTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/execution_status.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/execution_status.ts similarity index 95% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/execution_status.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/execution_status.ts index 4e61fd6593113..122353f444536 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/execution_status.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/execution_status.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import { RuleExecutionStatusErrorReasons } from '@kbn/alerting-plugin/common'; -import { Spaces } from '../../scenarios'; -import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { Spaces } from '../../../scenarios'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function executionStatusAlertTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts similarity index 99% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts index 84f0d7709d01a..20a5e82d303fe 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts @@ -9,9 +9,9 @@ import expect from '@kbn/expect'; import { SuperTest, Test } from 'supertest'; import { chunk, omit } from 'lodash'; import uuid from 'uuid'; -import { UserAtSpaceScenarios } from '../../scenarios'; -import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; const findTestUtils = ( describeType: 'internal' | 'public', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get.ts new file mode 100644 index 0000000000000..48559aa35ac3c --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get.ts @@ -0,0 +1,339 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { SuperTest, Test } from 'supertest'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { + getUrlPrefix, + getTestRuleData, + ObjectRemover, + getConsumerUnauthorizedErrorMessage, + getProducerUnauthorizedErrorMessage, +} from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +const getTestUtils = ( + describeType: 'internal' | 'public', + objectRemover: ObjectRemover, + supertest: SuperTest, + supertestWithoutAuth: any +) => { + describe(describeType, () => { + afterEach(() => objectRemover.removeAll()); + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + describe(scenario.id, () => { + it('should handle get alert request appropriately', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .get( + `${getUrlPrefix(space.id)}/${ + describeType === 'public' ? 'api' : 'internal' + }/alerting/rule/${createdAlert.id}` + ) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage('get', 'test.noop', 'alertsFixture'), + statusCode: 403, + }); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body).to.eql({ + id: createdAlert.id, + name: 'abc', + tags: ['foo'], + rule_type_id: 'test.noop', + consumer: 'alertsFixture', + schedule: { interval: '1m' }, + enabled: true, + actions: [], + params: {}, + created_by: 'elastic', + scheduled_task_id: response.body.scheduled_task_id, + updated_at: response.body.updated_at, + created_at: response.body.created_at, + throttle: '1m', + notify_when: 'onThrottleInterval', + updated_by: 'elastic', + api_key_owner: 'elastic', + mute_all: false, + muted_alert_ids: [], + execution_status: response.body.execution_status, + ...(describeType === 'internal' + ? { + monitoring: response.body.monitoring, + snooze_end_time: response.body.snooze_end_time, + } + : {}), + }); + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle get alert request appropriately when consumer is the same as producer', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.restricted-noop', + consumer: 'alertsRestrictedFixture', + }) + ) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .get( + `${getUrlPrefix(space.id)}/${ + describeType === 'public' ? 'api' : 'internal' + }/alerting/rule/${createdAlert.id}` + ) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'get', + 'test.restricted-noop', + 'alertsRestrictedFixture' + ), + statusCode: 403, + }); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle get alert request appropriately when consumer is not the producer', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.unrestricted-noop', + consumer: 'alertsFixture', + }) + ) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .get( + `${getUrlPrefix(space.id)}/${ + describeType === 'public' ? 'api' : 'internal' + }/alerting/rule/${createdAlert.id}` + ) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'get', + 'test.unrestricted-noop', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getProducerUnauthorizedErrorMessage( + 'get', + 'test.unrestricted-noop', + 'alertsRestrictedFixture' + ), + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'global_read at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle get alert request appropriately when consumer is "alerts"', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.restricted-noop', + consumer: 'alerts', + }) + ) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .get( + `${getUrlPrefix(space.id)}/${ + describeType === 'public' ? 'api' : 'internal' + }/alerting/rule/${createdAlert.id}` + ) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'get', + 'test.restricted-noop', + 'alerts' + ), + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getProducerUnauthorizedErrorMessage( + 'get', + 'test.restricted-noop', + 'alertsRestrictedFixture' + ), + statusCode: 403, + }); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`shouldn't get alert from another space`, async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .get( + `${getUrlPrefix('other')}/${ + describeType === 'public' ? 'api' : 'internal' + }/alerting/rule/${createdAlert.id}` + ) + .auth(user.username, user.password); + + expect(response.statusCode).to.eql(404); + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + case 'global_read at space1': + case 'superuser at space1': + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: `Saved object [alert/${createdAlert.id}] not found`, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`should handle get alert request appropriately when alert doesn't exist`, async () => { + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/1`) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(404); + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: 'Saved object [alert/1] not found', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + }); + } + }); +}; + +// eslint-disable-next-line import/no-default-export +export default function createGetTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('get', () => { + const objectRemover = new ObjectRemover(supertest); + afterEach(() => objectRemover.removeAll()); + + getTestUtils('public', objectRemover, supertest, supertestWithoutAuth); + getTestUtils('internal', objectRemover, supertest, supertestWithoutAuth); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_state.ts similarity index 97% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_state.ts index 3bdfe49464fcf..e3da329c1cbaf 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_state.ts @@ -12,9 +12,9 @@ import { getTestRuleData, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, -} from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { UserAtSpaceScenarios } from '../../scenarios'; +} from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../../scenarios'; // eslint-disable-next-line import/no-default-export export default function createGetAlertStateTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_summary.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_summary.ts similarity index 97% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_summary.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_summary.ts index eb4e592a91d8a..f0ba9dc451937 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_summary.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_summary.ts @@ -13,9 +13,9 @@ import { getTestRuleData, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, -} from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { UserAtSpaceScenarios } from '../../scenarios'; +} from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../../scenarios'; // eslint-disable-next-line import/no-default-export export default function createGetAlertSummaryTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/index.ts new file mode 100644 index 0000000000000..6753b6383872d --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/index.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { setupSpacesAndUsers, tearDown } from '../../../setup'; + +// eslint-disable-next-line import/no-default-export +export default function alertingTests({ loadTestFile, getService }: FtrProviderContext) { + describe('Alerts - Group 1', () => { + describe('alerts', () => { + before(async () => { + await setupSpacesAndUsers(getService); + }); + + after(async () => { + await tearDown(getService); + }); + + loadTestFile(require.resolve('./find')); + loadTestFile(require.resolve('./create')); + loadTestFile(require.resolve('./delete')); + loadTestFile(require.resolve('./disable')); + loadTestFile(require.resolve('./enable')); + loadTestFile(require.resolve('./execution_status')); + loadTestFile(require.resolve('./get')); + loadTestFile(require.resolve('./get_alert_state')); + loadTestFile(require.resolve('./get_alert_summary')); + loadTestFile(require.resolve('./rule_types')); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rule_types.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/rule_types.ts similarity index 96% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rule_types.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/rule_types.ts index 0c527ac1449f8..e9d8175f9066e 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rule_types.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/rule_types.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import { omit } from 'lodash'; -import { UserAtSpaceScenarios } from '../../scenarios'; -import { getUrlPrefix } from '../../../common/lib/space_test_utils'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { getUrlPrefix } from '../../../../common/lib/space_test_utils'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function listAlertTypes({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/index.ts new file mode 100644 index 0000000000000..795af26627dfb --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function alertingApiIntegrationTests({ loadTestFile }: FtrProviderContext) { + describe('alerting api integration security and spaces enabled', function () { + describe('', function () { + loadTestFile(require.resolve('./alerting')); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/config.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/config.ts new file mode 100644 index 0000000000000..9b90a4c18fdf0 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/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 { createTestConfig } from '../../common/config'; + +// eslint-disable-next-line import/no-default-export +export default createTestConfig('security_and_spaces', { + disabledPlugins: [], + license: 'trial', + ssl: true, + enableActionsProxy: true, + publicBaseUrl: true, + testFiles: [require.resolve('./tests')], +}); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/email.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/email.ts similarity index 99% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/email.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/email.ts index 6fb2315956b69..4d9282d4fdeea 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/email.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/email.ts @@ -7,11 +7,11 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { ExternalServiceSimulator, getExternalServiceSimulatorPath, -} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function emailTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/es_index.ts similarity index 99% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/es_index.ts index edf352936e979..fed3acba1147e 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/es_index.ts @@ -7,7 +7,7 @@ import type { Client } from '@elastic/elasticsearch'; import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; const ES_TEST_INDEX_NAME = 'functional-test-actions-index'; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index_preconfigured.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/es_index_preconfigured.ts similarity index 96% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index_preconfigured.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/es_index_preconfigured.ts index caa7d57688037..a447921ac5041 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index_preconfigured.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/es_index_preconfigured.ts @@ -7,7 +7,7 @@ import type { Client } from '@elastic/elasticsearch'; import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; // from: x-pack/test/alerting_api_integration/common/config.ts const ACTION_ID = 'preconfigured-es-index-action'; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/jira.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/jira.ts index 33185a20c9249..5a6e0967736a2 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/jira.ts @@ -8,13 +8,13 @@ import httpProxy from 'http-proxy'; import expect from '@kbn/expect'; -import { getHttpProxyServer } from '../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function jiraTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/oauth_access_token.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/oauth_access_token.ts new file mode 100644 index 0000000000000..6053f78ea76a4 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/oauth_access_token.ts @@ -0,0 +1,170 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import fs from 'fs'; +import expect from '@kbn/expect'; +import { promisify } from 'util'; +import httpProxy from 'http-proxy'; +import { KBN_KEY_PATH } from '@kbn/dev-utils'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; +import { + ExternalServiceSimulator, + getExternalServiceSimulatorPath, +} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; + +// eslint-disable-next-line import/no-default-export +export default function oAuthAccessTokenTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const kibanaServer = getService('kibanaServer'); + + describe('get oauth access token', () => { + let servicenowSimulatorURL: string = ''; + let proxyServer: httpProxy | undefined; + let testPrivateKey: string; + const configService = getService('config'); + + // need to wait for kibanaServer to settle ... + before(async () => { + testPrivateKey = await promisify(fs.readFile)(KBN_KEY_PATH, 'utf8'); + servicenowSimulatorURL = kibanaServer.resolveUrl( + getExternalServiceSimulatorPath(ExternalServiceSimulator.SERVICENOW) + ); + proxyServer = await getHttpProxyServer( + kibanaServer.resolveUrl('/'), + configService.get('kbnTestServer.serverArgs'), + () => {} + ); + }); + + after(() => { + if (proxyServer) { + proxyServer.close(); + } + }); + + it('should return 200 when requesting a JWT access token with OAuth credentials', async () => { + const { body: accessToken } = await supertest + .post('/internal/actions/connector/_oauth_access_token') + .set('kbn-xsrf', 'foo') + .send({ + type: 'jwt', + options: { + tokenUrl: `${servicenowSimulatorURL}/oauth_token.do`, + config: { + clientId: 'abc', + userIdentifierValue: 'elastic', + jwtKeyId: 'def', + }, + secrets: { + clientSecret: 'xyz', + privateKey: testPrivateKey, + }, + }, + }) + .expect(200); + + expect(accessToken).to.eql({ accessToken: 'Bearer tokentokentoken' }); + }); + + it('should return 200 when requesting a Client Credentials access token with OAuth credentials', async () => { + const { body: accessToken } = await supertest + .post('/internal/actions/connector/_oauth_access_token') + .set('kbn-xsrf', 'foo') + .send({ + type: 'client', + options: { + tokenUrl: `${kibanaServer.resolveUrl( + getExternalServiceSimulatorPath(ExternalServiceSimulator.MS_EXCHANGE) + )}/1234567/oauth2/v2.0/token`, + scope: 'https://graph.microsoft.com/.default', + config: { + clientId: 'abc', + tenantId: '98765', + }, + secrets: { + clientSecret: 'xyz', + }, + }, + }) + .expect(200); + + expect(accessToken).to.eql({ accessToken: 'Bearer asdadasd' }); + }); + + it('should return 400 when given incorrect options for requesting Client Credentials access token with OAuth credentials', async () => { + await supertest + .post('/internal/actions/connector/_oauth_access_token') + .set('kbn-xsrf', 'foo') + .send({ + type: 'client', + options: { + tokenUrl: `${servicenowSimulatorURL}/oauth_token.do`, + config: { + clientId: 'abc', + userIdentifierValue: 'elastic', + jwtKeyId: 'def', + }, + secrets: { + clientSecret: 'xyz', + privateKey: testPrivateKey, + }, + }, + }) + .expect(400); + }); + + it('should return 400 when given incorrect options for requesting JWT access token with OAuth credentials', async () => { + await supertest + .post('/internal/actions/connector/_oauth_access_token') + .set('kbn-xsrf', 'foo') + .send({ + type: 'jwt', + options: { + tokenUrl: `${kibanaServer.resolveUrl( + getExternalServiceSimulatorPath(ExternalServiceSimulator.MS_EXCHANGE) + )}/1234567/oauth2/v2.0/token`, + scope: 'https://graph.microsoft.com/.default', + config: { + clientId: 'abc', + tenantId: '98765', + }, + secrets: { + clientSecret: 'xyz', + }, + }, + }) + .expect(400); + }); + + it('should return 400 when token url not included in allowlist', async () => { + const { body } = await supertest + .post('/internal/actions/connector/_oauth_access_token') + .set('kbn-xsrf', 'foo') + .send({ + type: 'jwt', + options: { + tokenUrl: `https://servicenow.nonexistent.com/oauth_token.do`, + config: { + clientId: 'abc', + userIdentifierValue: 'elastic', + jwtKeyId: 'def', + }, + secrets: { + clientSecret: 'xyz', + privateKey: testPrivateKey, + }, + }, + }); + + expect(body.statusCode).to.equal(400); + expect(body.message).to.equal( + `target url "https://servicenow.nonexistent.com/oauth_token.do" is not added to the Kibana config xpack.actions.allowedHosts` + ); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/pagerduty.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/pagerduty.ts similarity index 96% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/pagerduty.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/pagerduty.ts index 05dba49236197..731eab7dad0f3 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/pagerduty.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/pagerduty.ts @@ -8,13 +8,13 @@ import httpProxy from 'http-proxy'; import expect from '@kbn/expect'; -import { getHttpProxyServer } from '../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function pagerdutyTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/resilient.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/resilient.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/resilient.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/resilient.ts index e2a92701b62cb..fec22ab72e1ef 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/resilient.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/resilient.ts @@ -8,13 +8,13 @@ import httpProxy from 'http-proxy'; import expect from '@kbn/expect'; -import { getHttpProxyServer } from '../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function resilientTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/server_log.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/server_log.ts similarity index 96% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/server_log.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/server_log.ts index fb7bac7d81e9c..b8b12d6aac764 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/server_log.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/server_log.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function serverLogTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_itom.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_itom.ts new file mode 100644 index 0000000000000..0771e4e293726 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_itom.ts @@ -0,0 +1,542 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import httpProxy from 'http-proxy'; +import expect from '@kbn/expect'; +import { asyncForEach } from '@kbn/std'; +import getPort from 'get-port'; +import http from 'http'; + +import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { getServiceNowServer } from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; + +// eslint-disable-next-line import/no-default-export +export default function serviceNowITOMTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const configService = getService('config'); + + const mockServiceNowCommon = { + params: { + subAction: 'addEvent', + subActionParams: { + source: 'A source', + event_class: 'An event class', + resource: 'C:', + node: 'node.example.com', + metric_name: 'Percentage Logical Disk Free Space', + type: 'Disk space', + severity: '4', + description: 'desc', + additional_info: '{"alert": "test"}', + message_key: 'a key', + time_of_event: '2021-10-13T10:51:44.981Z', + }, + }, + }; + const mockServiceNowBasic = { + ...mockServiceNowCommon, + config: { + apiUrl: 'www.servicenowisinkibanaactions.com', + }, + secrets: { + password: 'elastic', + username: 'changeme', + }, + }; + const mockServiceNowOAuth = { + ...mockServiceNowCommon, + config: { + apiUrl: 'www.servicenowisinkibanaactions.com', + isOAuth: true, + clientId: 'abc', + userIdentifierValue: 'elastic', + jwtKeyId: 'def', + }, + secrets: { + clientSecret: 'xyz', + privateKey: '-----BEGIN RSA PRIVATE KEY-----\nddddddd\n-----END RSA PRIVATE KEY-----', + }, + }; + + describe('ServiceNow ITOM', () => { + let simulatedActionId = ''; + let serviceNowSimulatorURL: string = ''; + let serviceNowServer: http.Server; + let proxyServer: httpProxy | undefined; + let proxyHaveBeenCalled = false; + + before(async () => { + serviceNowServer = await getServiceNowServer(); + const availablePort = await getPort({ port: getPort.makeRange(9000, 9100) }); + if (!serviceNowServer.listening) { + serviceNowServer.listen(availablePort); + } + serviceNowSimulatorURL = `http://localhost:${availablePort}`; + proxyServer = await getHttpProxyServer( + serviceNowSimulatorURL, + configService.get('kbnTestServer.serverArgs'), + () => { + proxyHaveBeenCalled = true; + } + ); + }); + + after(() => { + serviceNowServer.close(); + if (proxyServer) { + proxyServer.close(); + } + }); + + describe('ServiceNow ITOM - Action Creation', () => { + it('should return 200 when creating a servicenow Basic Auth connector successfully', async () => { + const { body: createdAction } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + config: { + apiUrl: serviceNowSimulatorURL, + }, + secrets: mockServiceNowBasic.secrets, + }) + .expect(200); + + expect(createdAction).to.eql({ + id: createdAction.id, + is_preconfigured: false, + is_deprecated: false, + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + is_missing_secrets: false, + config: { + apiUrl: serviceNowSimulatorURL, + isOAuth: false, + clientId: null, + jwtKeyId: null, + userIdentifierValue: null, + }, + }); + + const { body: fetchedAction } = await supertest + .get(`/api/actions/connector/${createdAction.id}`) + .expect(200); + + expect(fetchedAction).to.eql({ + id: fetchedAction.id, + is_preconfigured: false, + is_deprecated: false, + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + is_missing_secrets: false, + config: { + apiUrl: serviceNowSimulatorURL, + isOAuth: false, + clientId: null, + jwtKeyId: null, + userIdentifierValue: null, + }, + }); + }); + + it('should return 200 when creating a servicenow OAuth connector successfully', async () => { + const { body: createdConnector } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + }, + secrets: mockServiceNowOAuth.secrets, + }) + .expect(200); + + expect(createdConnector).to.eql({ + id: createdConnector.id, + is_preconfigured: false, + is_deprecated: false, + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + is_missing_secrets: false, + config: { + apiUrl: serviceNowSimulatorURL, + isOAuth: true, + clientId: mockServiceNowOAuth.config.clientId, + jwtKeyId: mockServiceNowOAuth.config.jwtKeyId, + userIdentifierValue: mockServiceNowOAuth.config.userIdentifierValue, + }, + }); + + const { body: fetchedConnector } = await supertest + .get(`/api/actions/connector/${createdConnector.id}`) + .expect(200); + + expect(fetchedConnector).to.eql({ + id: fetchedConnector.id, + is_preconfigured: false, + is_deprecated: false, + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + is_missing_secrets: false, + config: { + apiUrl: serviceNowSimulatorURL, + isOAuth: true, + clientId: mockServiceNowOAuth.config.clientId, + jwtKeyId: mockServiceNowOAuth.config.jwtKeyId, + userIdentifierValue: mockServiceNowOAuth.config.userIdentifierValue, + }, + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow Basic Auth connector with no apiUrl', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + config: {}, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: [apiUrl]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow OAuth connector with no apiUrl', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + config: { + isOAuth: true, + }, + secrets: mockServiceNowOAuth.secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: [apiUrl]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow connector with a not present in allowedHosts apiUrl', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + config: { + apiUrl: 'http://servicenow.mynonexistent.com', + }, + secrets: mockServiceNowBasic.secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: error configuring connector action: target url "http://servicenow.mynonexistent.com" is not added to the Kibana config xpack.actions.allowedHosts', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow Basic Auth connector without secrets', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + config: { + apiUrl: serviceNowSimulatorURL, + }, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type secrets: Either basic auth or OAuth credentials must be specified', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow OAuth connector without secrets', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + }, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type secrets: Either basic auth or OAuth credentials must be specified', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow OAuth connector with missing fields', async () => { + const badConfigs = [ + { + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + clientId: null, + }, + secrets: mockServiceNowOAuth.secrets, + errorMessage: `error validating action type config: clientId must be provided when isOAuth = true`, + }, + { + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + userIdentifierValue: null, + }, + secrets: mockServiceNowOAuth.secrets, + errorMessage: `error validating action type config: userIdentifierValue must be provided when isOAuth = true`, + }, + { + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + jwtKeyId: null, + }, + secrets: mockServiceNowOAuth.secrets, + errorMessage: `error validating action type config: jwtKeyId must be provided when isOAuth = true`, + }, + { + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + }, + secrets: { + ...mockServiceNowOAuth.secrets, + clientSecret: null, + }, + errorMessage: `error validating action type secrets: clientSecret and privateKey must both be specified`, + }, + { + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + }, + secrets: { + ...mockServiceNowOAuth.secrets, + privateKey: null, + }, + errorMessage: `error validating action type secrets: clientSecret and privateKey must both be specified`, + }, + ]; + + await asyncForEach(badConfigs, async (badConfig) => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + config: badConfig.config, + secrets: badConfig.secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: badConfig.errorMessage, + }); + }); + }); + }); + }); + + describe('ServiceNow ITOM - Executor', () => { + before(async () => { + const { body } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow simulator', + connector_type_id: '.servicenow-itom', + config: { + apiUrl: serviceNowSimulatorURL, + }, + secrets: mockServiceNowBasic.secrets, + }); + simulatedActionId = body.id; + }); + + describe('Validation', () => { + it('should handle failing with a simulated success without action', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: {}, + }) + .then((resp: any) => { + expect(Object.keys(resp.body)).to.eql(['status', 'message', 'retry', 'connector_id']); + expect(resp.body.connector_id).to.eql(simulatedActionId); + expect(resp.body.status).to.eql('error'); + }); + }); + + it('should handle failing with a simulated success without unsupported action', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'non-supported' }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [addEvent]\n- [1.subAction]: expected value to equal [getChoices]', + }); + }); + }); + + it('should handle failing with a simulated success without subActionParams', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'pushToService' }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [addEvent]\n- [1.subAction]: expected value to equal [getChoices]', + }); + }); + }); + + describe('getChoices', () => { + it('should fail when field is not provided', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'getChoices', + subActionParams: {}, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [addEvent]\n- [1.subActionParams.fields]: expected value of type [array] but got [undefined]', + }); + }); + }); + }); + }); + + describe('Execution', () => { + // New connectors + describe('Add event', () => { + it('should add an event ', async () => { + const { body: result } = await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: mockServiceNowBasic.params, + }) + .expect(200); + expect(result.status).to.eql('ok'); + }); + }); + + describe('getChoices', () => { + it('should get choices', async () => { + const { body: result } = await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'getChoices', + subActionParams: { fields: ['priority'] }, + }, + }) + .expect(200); + + expect(proxyHaveBeenCalled).to.equal(true); + expect(result).to.eql({ + status: 'ok', + connector_id: simulatedActionId, + data: [ + { + dependent_value: '', + label: '1 - Critical', + value: '1', + }, + { + dependent_value: '', + label: '2 - High', + value: '2', + }, + { + dependent_value: '', + label: '3 - Moderate', + value: '3', + }, + { + dependent_value: '', + label: '4 - Low', + value: '4', + }, + { + dependent_value: '', + label: '5 - Planning', + value: '5', + }, + ], + }); + }); + }); + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_itsm.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_itsm.ts new file mode 100644 index 0000000000000..368e1b104a87e --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_itsm.ts @@ -0,0 +1,718 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import httpProxy from 'http-proxy'; +import expect from '@kbn/expect'; +import { asyncForEach } from '@kbn/std'; +import getPort from 'get-port'; +import http from 'http'; + +import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { getServiceNowServer } from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; + +// eslint-disable-next-line import/no-default-export +export default function serviceNowITSMTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const configService = getService('config'); + + const mockServiceNowCommon = { + params: { + subAction: 'pushToService', + subActionParams: { + incident: { + description: 'a description', + externalId: null, + impact: '1', + severity: '1', + short_description: 'a title', + urgency: '1', + category: 'software', + subcategory: 'os', + }, + comments: [ + { + comment: 'first comment', + commentId: '456', + }, + ], + }, + }, + }; + + const mockServiceNowBasic = { + ...mockServiceNowCommon, + config: { + apiUrl: 'www.servicenowisinkibanaactions.com', + usesTableApi: false, + }, + secrets: { + password: 'elastic', + username: 'changeme', + }, + }; + const mockServiceNowOAuth = { + ...mockServiceNowCommon, + config: { + apiUrl: 'www.servicenowisinkibanaactions.com', + usesTableApi: false, + isOAuth: true, + clientId: 'abc', + userIdentifierValue: 'elastic', + jwtKeyId: 'def', + }, + secrets: { + clientSecret: 'xyz', + privateKey: '-----BEGIN RSA PRIVATE KEY-----\nddddddd\n-----END RSA PRIVATE KEY-----', + }, + }; + + describe('ServiceNow ITSM', () => { + let simulatedActionId = ''; + let serviceNowSimulatorURL: string = ''; + let serviceNowServer: http.Server; + let proxyServer: httpProxy | undefined; + let proxyHaveBeenCalled = false; + + before(async () => { + serviceNowServer = await getServiceNowServer(); + const availablePort = await getPort({ port: getPort.makeRange(9000, 9100) }); + if (!serviceNowServer.listening) { + serviceNowServer.listen(availablePort); + } + serviceNowSimulatorURL = `http://localhost:${availablePort}`; + proxyServer = await getHttpProxyServer( + serviceNowSimulatorURL, + configService.get('kbnTestServer.serverArgs'), + () => { + proxyHaveBeenCalled = true; + } + ); + }); + + after(() => { + serviceNowServer.close(); + if (proxyServer) { + proxyServer.close(); + } + }); + + describe('ServiceNow ITSM - Action Creation', () => { + it('should return 200 when creating a servicenow Basic Auth connector successfully', async () => { + const { body: createdAction } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow', + config: { + apiUrl: serviceNowSimulatorURL, + usesTableApi: false, + }, + secrets: mockServiceNowBasic.secrets, + }) + .expect(200); + + expect(createdAction).to.eql({ + id: createdAction.id, + is_preconfigured: false, + is_deprecated: false, + name: 'A servicenow action', + connector_type_id: '.servicenow', + is_missing_secrets: false, + config: { + apiUrl: serviceNowSimulatorURL, + usesTableApi: false, + isOAuth: false, + clientId: null, + jwtKeyId: null, + userIdentifierValue: null, + }, + }); + + const { body: fetchedAction } = await supertest + .get(`/api/actions/connector/${createdAction.id}`) + .expect(200); + + expect(fetchedAction).to.eql({ + id: fetchedAction.id, + is_preconfigured: false, + is_deprecated: false, + name: 'A servicenow action', + connector_type_id: '.servicenow', + is_missing_secrets: false, + config: { + apiUrl: serviceNowSimulatorURL, + usesTableApi: false, + isOAuth: false, + clientId: null, + jwtKeyId: null, + userIdentifierValue: null, + }, + }); + }); + + it('should return 200 when creating a servicenow OAuth connector successfully', async () => { + const { body: createdConnector } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow', + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + }, + secrets: mockServiceNowOAuth.secrets, + }) + .expect(200); + + expect(createdConnector).to.eql({ + id: createdConnector.id, + is_preconfigured: false, + is_deprecated: false, + name: 'A servicenow action', + connector_type_id: '.servicenow', + is_missing_secrets: false, + config: { + apiUrl: serviceNowSimulatorURL, + usesTableApi: false, + isOAuth: true, + clientId: mockServiceNowOAuth.config.clientId, + jwtKeyId: mockServiceNowOAuth.config.jwtKeyId, + userIdentifierValue: mockServiceNowOAuth.config.userIdentifierValue, + }, + }); + + const { body: fetchedConnector } = await supertest + .get(`/api/actions/connector/${createdConnector.id}`) + .expect(200); + + expect(fetchedConnector).to.eql({ + id: fetchedConnector.id, + is_preconfigured: false, + is_deprecated: false, + name: 'A servicenow action', + connector_type_id: '.servicenow', + is_missing_secrets: false, + config: { + apiUrl: serviceNowSimulatorURL, + usesTableApi: false, + isOAuth: true, + clientId: mockServiceNowOAuth.config.clientId, + jwtKeyId: mockServiceNowOAuth.config.jwtKeyId, + userIdentifierValue: mockServiceNowOAuth.config.userIdentifierValue, + }, + }); + }); + + it('should set the usesTableApi to true when not provided', async () => { + const { body: createdAction } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow', + config: { + apiUrl: serviceNowSimulatorURL, + }, + secrets: mockServiceNowBasic.secrets, + }) + .expect(200); + + const { body: fetchedAction } = await supertest + .get(`/api/actions/connector/${createdAction.id}`) + .expect(200); + + expect(fetchedAction.config.usesTableApi).to.be(true); + }); + + it('should respond with a 400 Bad Request when creating a servicenow Basic Auth connector with no apiUrl', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow', + config: {}, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: [apiUrl]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow OAuth connector with no apiUrl', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow', + config: { + isOAuth: true, + }, + secrets: mockServiceNowOAuth.secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: [apiUrl]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow connector with a not present in allowedHosts apiUrl', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow', + config: { + apiUrl: 'http://servicenow.mynonexistent.com', + }, + secrets: mockServiceNowBasic.secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: error configuring connector action: target url "http://servicenow.mynonexistent.com" is not added to the Kibana config xpack.actions.allowedHosts', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow Basic Auth connector without secrets', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow', + config: { + apiUrl: serviceNowSimulatorURL, + }, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type secrets: Either basic auth or OAuth credentials must be specified', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow OAuth connector without secrets', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow', + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + }, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type secrets: Either basic auth or OAuth credentials must be specified', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow OAuth connector with missing fields', async () => { + const badConfigs = [ + { + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + clientId: null, + }, + secrets: mockServiceNowOAuth.secrets, + errorMessage: `error validating action type config: clientId must be provided when isOAuth = true`, + }, + { + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + userIdentifierValue: null, + }, + secrets: mockServiceNowOAuth.secrets, + errorMessage: `error validating action type config: userIdentifierValue must be provided when isOAuth = true`, + }, + { + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + jwtKeyId: null, + }, + secrets: mockServiceNowOAuth.secrets, + errorMessage: `error validating action type config: jwtKeyId must be provided when isOAuth = true`, + }, + { + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + }, + secrets: { + ...mockServiceNowOAuth.secrets, + clientSecret: null, + }, + errorMessage: `error validating action type secrets: clientSecret and privateKey must both be specified`, + }, + { + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + }, + secrets: { + ...mockServiceNowOAuth.secrets, + privateKey: null, + }, + errorMessage: `error validating action type secrets: clientSecret and privateKey must both be specified`, + }, + ]; + + await asyncForEach(badConfigs, async (badConfig) => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow', + config: badConfig.config, + secrets: badConfig.secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: badConfig.errorMessage, + }); + }); + }); + }); + }); + + describe('ServiceNow ITSM - Executor', () => { + before(async () => { + const { body } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow simulator', + connector_type_id: '.servicenow', + config: { + apiUrl: serviceNowSimulatorURL, + usesTableApi: false, + }, + secrets: mockServiceNowBasic.secrets, + }); + simulatedActionId = body.id; + }); + + describe('Validation', () => { + it('should handle failing with a simulated success without action', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: {}, + }) + .then((resp: any) => { + expect(Object.keys(resp.body)).to.eql(['status', 'message', 'retry', 'connector_id']); + expect(resp.body.connector_id).to.eql(simulatedActionId); + expect(resp.body.status).to.eql('error'); + }); + }); + + it('should handle failing with a simulated success without unsupported action', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'non-supported' }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subAction]: expected value to equal [pushToService]\n- [4.subAction]: expected value to equal [getChoices]', + }); + }); + }); + + it('should handle failing with a simulated success without subActionParams', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'pushToService' }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.incident.short_description]: expected value of type [string] but got [undefined]\n- [4.subAction]: expected value to equal [getChoices]', + }); + }); + }); + + it('should handle failing with a simulated success without title', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockServiceNowBasic.params, + subActionParams: { + savedObjectId: 'success', + }, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.incident.short_description]: expected value of type [string] but got [undefined]\n- [4.subAction]: expected value to equal [getChoices]', + }); + }); + }); + + it('should handle failing with a simulated success without commentId', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockServiceNowBasic.params, + subActionParams: { + incident: { + ...mockServiceNowBasic.params.subActionParams.incident, + short_description: 'success', + }, + comments: [{ comment: 'boo' }], + }, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.commentId]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [4.subAction]: expected value to equal [getChoices]', + }); + }); + }); + + it('should handle failing with a simulated success without comment message', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockServiceNowBasic.params, + subActionParams: { + incident: { + ...mockServiceNowBasic.params.subActionParams.incident, + short_description: 'success', + }, + comments: [{ commentId: 'success' }], + }, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.comment]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [4.subAction]: expected value to equal [getChoices]', + }); + }); + }); + + describe('getChoices', () => { + it('should fail when field is not provided', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'getChoices', + subActionParams: {}, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subAction]: expected value to equal [pushToService]\n- [4.subActionParams.fields]: expected value of type [array] but got [undefined]', + }); + }); + }); + }); + }); + + describe('Execution', () => { + // Connectors that use the Import set API + describe('Import set API', () => { + it('should handle creating an incident without comments', async () => { + const { body: result } = await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockServiceNowBasic.params, + subActionParams: { + incident: mockServiceNowBasic.params.subActionParams.incident, + comments: [], + }, + }, + }) + .expect(200); + + expect(proxyHaveBeenCalled).to.equal(true); + expect(result).to.eql({ + status: 'ok', + connector_id: simulatedActionId, + data: { + id: '123', + title: 'INC01', + pushedDate: '2020-03-10T12:24:20.000Z', + url: `${serviceNowSimulatorURL}/nav_to.do?uri=incident.do?sys_id=123`, + }, + }); + }); + }); + + // Connectors that use the Table API + describe('Table API', () => { + before(async () => { + const { body } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow simulator', + connector_type_id: '.servicenow', + config: { + apiUrl: serviceNowSimulatorURL, + usesTableApi: true, + }, + secrets: mockServiceNowBasic.secrets, + }); + simulatedActionId = body.id; + }); + + it('should handle creating an incident without comments', async () => { + const { body: result } = await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockServiceNowBasic.params, + subActionParams: { + incident: mockServiceNowBasic.params.subActionParams.incident, + comments: [], + }, + }, + }) + .expect(200); + + expect(proxyHaveBeenCalled).to.equal(true); + expect(result).to.eql({ + status: 'ok', + connector_id: simulatedActionId, + data: { + id: '123', + title: 'INC01', + pushedDate: '2020-03-10T12:24:20.000Z', + url: `${serviceNowSimulatorURL}/nav_to.do?uri=incident.do?sys_id=123`, + }, + }); + }); + }); + + describe('getChoices', () => { + it('should get choices', async () => { + const { body: result } = await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'getChoices', + subActionParams: { fields: ['priority'] }, + }, + }) + .expect(200); + + expect(proxyHaveBeenCalled).to.equal(true); + expect(result).to.eql({ + status: 'ok', + connector_id: simulatedActionId, + data: [ + { + dependent_value: '', + label: '1 - Critical', + value: '1', + }, + { + dependent_value: '', + label: '2 - High', + value: '2', + }, + { + dependent_value: '', + label: '3 - Moderate', + value: '3', + }, + { + dependent_value: '', + label: '4 - Low', + value: '4', + }, + { + dependent_value: '', + label: '5 - Planning', + value: '5', + }, + ], + }); + }); + }); + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_sir.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_sir.ts new file mode 100644 index 0000000000000..f08ca542e4617 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/servicenow_sir.ts @@ -0,0 +1,731 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import httpProxy from 'http-proxy'; +import expect from '@kbn/expect'; +import { asyncForEach } from '@kbn/std'; +import getPort from 'get-port'; +import http from 'http'; + +import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { getServiceNowServer } from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; + +// eslint-disable-next-line import/no-default-export +export default function serviceNowSIRTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const configService = getService('config'); + + const mockServiceNowCommon = { + config: { + apiUrl: 'www.servicenowisinkibanaactions.com', + usesTableApi: false, + }, + secrets: { + password: 'elastic', + username: 'changeme', + }, + params: { + subAction: 'pushToService', + subActionParams: { + incident: { + externalId: null, + short_description: 'Incident title', + description: 'Incident description', + dest_ip: ['192.168.1.1', '192.168.1.3'], + source_ip: ['192.168.1.2', '192.168.1.4'], + malware_hash: ['5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9'], + malware_url: ['https://example.com'], + category: 'software', + subcategory: 'os', + correlation_id: 'alertID', + correlation_display: 'Alerting', + priority: '1', + }, + comments: [ + { + comment: 'first comment', + commentId: '456', + }, + ], + }, + }, + }; + + const mockServiceNowBasic = { + ...mockServiceNowCommon, + config: { + apiUrl: 'www.servicenowisinkibanaactions.com', + usesTableApi: false, + }, + secrets: { + password: 'elastic', + username: 'changeme', + }, + }; + const mockServiceNowOAuth = { + ...mockServiceNowCommon, + config: { + apiUrl: 'www.servicenowisinkibanaactions.com', + usesTableApi: false, + isOAuth: true, + clientId: 'abc', + userIdentifierValue: 'elastic', + jwtKeyId: 'def', + }, + secrets: { + clientSecret: 'xyz', + privateKey: '-----BEGIN RSA PRIVATE KEY-----\nddddddd\n-----END RSA PRIVATE KEY-----', + }, + }; + + describe('ServiceNow SIR', () => { + let simulatedActionId = ''; + let serviceNowSimulatorURL: string = ''; + let serviceNowServer: http.Server; + let proxyServer: httpProxy | undefined; + let proxyHaveBeenCalled = false; + + before(async () => { + serviceNowServer = await getServiceNowServer(); + const availablePort = await getPort({ port: getPort.makeRange(9000, 9100) }); + if (!serviceNowServer.listening) { + serviceNowServer.listen(availablePort); + } + serviceNowSimulatorURL = `http://localhost:${availablePort}`; + proxyServer = await getHttpProxyServer( + serviceNowSimulatorURL, + configService.get('kbnTestServer.serverArgs'), + () => { + proxyHaveBeenCalled = true; + } + ); + }); + + after(() => { + serviceNowServer.close(); + if (proxyServer) { + proxyServer.close(); + } + }); + + describe('ServiceNow SIR - Action Creation', () => { + it('should return 200 when creating a servicenow Basic Auth connector successfully', async () => { + const { body: createdAction } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-sir', + config: { + apiUrl: serviceNowSimulatorURL, + usesTableApi: false, + }, + secrets: mockServiceNowBasic.secrets, + }) + .expect(200); + + expect(createdAction).to.eql({ + id: createdAction.id, + is_preconfigured: false, + is_deprecated: false, + name: 'A servicenow action', + connector_type_id: '.servicenow-sir', + is_missing_secrets: false, + config: { + apiUrl: serviceNowSimulatorURL, + usesTableApi: false, + isOAuth: false, + clientId: null, + jwtKeyId: null, + userIdentifierValue: null, + }, + }); + + const { body: fetchedAction } = await supertest + .get(`/api/actions/connector/${createdAction.id}`) + .expect(200); + + expect(fetchedAction).to.eql({ + id: fetchedAction.id, + is_preconfigured: false, + is_deprecated: false, + name: 'A servicenow action', + connector_type_id: '.servicenow-sir', + is_missing_secrets: false, + config: { + apiUrl: serviceNowSimulatorURL, + usesTableApi: false, + isOAuth: false, + clientId: null, + jwtKeyId: null, + userIdentifierValue: null, + }, + }); + }); + + it('should return 200 when creating a servicenow OAuth connector successfully', async () => { + const { body: createdConnector } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-sir', + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + }, + secrets: mockServiceNowOAuth.secrets, + }) + .expect(200); + + expect(createdConnector).to.eql({ + id: createdConnector.id, + is_preconfigured: false, + is_deprecated: false, + name: 'A servicenow action', + connector_type_id: '.servicenow-sir', + is_missing_secrets: false, + config: { + apiUrl: serviceNowSimulatorURL, + usesTableApi: false, + isOAuth: true, + clientId: mockServiceNowOAuth.config.clientId, + jwtKeyId: mockServiceNowOAuth.config.jwtKeyId, + userIdentifierValue: mockServiceNowOAuth.config.userIdentifierValue, + }, + }); + + const { body: fetchedConnector } = await supertest + .get(`/api/actions/connector/${createdConnector.id}`) + .expect(200); + + expect(fetchedConnector).to.eql({ + id: fetchedConnector.id, + is_preconfigured: false, + is_deprecated: false, + name: 'A servicenow action', + connector_type_id: '.servicenow-sir', + is_missing_secrets: false, + config: { + apiUrl: serviceNowSimulatorURL, + usesTableApi: false, + isOAuth: true, + clientId: mockServiceNowOAuth.config.clientId, + jwtKeyId: mockServiceNowOAuth.config.jwtKeyId, + userIdentifierValue: mockServiceNowOAuth.config.userIdentifierValue, + }, + }); + }); + + it('should set the usesTableApi to true when not provided', async () => { + const { body: createdAction } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-sir', + config: { + apiUrl: serviceNowSimulatorURL, + }, + secrets: mockServiceNowBasic.secrets, + }) + .expect(200); + + const { body: fetchedAction } = await supertest + .get(`/api/actions/connector/${createdAction.id}`) + .expect(200); + + expect(fetchedAction.config.usesTableApi).to.be(true); + }); + + it('should respond with a 400 Bad Request when creating a servicenow Basic Auth connector with no apiUrl', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-sir', + config: {}, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: [apiUrl]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow OAuth connector with no apiUrl', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-sir', + config: { + isOAuth: true, + }, + secrets: mockServiceNowOAuth.secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: [apiUrl]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow connector with a not present in allowedHosts apiUrl', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-sir', + config: { + apiUrl: 'http://servicenow.mynonexistent.com', + }, + secrets: mockServiceNowBasic.secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: error configuring connector action: target url "http://servicenow.mynonexistent.com" is not added to the Kibana config xpack.actions.allowedHosts', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow Basic Auth connector without secrets', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-sir', + config: { + apiUrl: serviceNowSimulatorURL, + }, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type secrets: Either basic auth or OAuth credentials must be specified', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow OAuth connector without secrets', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-sir', + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + }, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type secrets: Either basic auth or OAuth credentials must be specified', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow OAuth connector with missing fields', async () => { + const badConfigs = [ + { + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + clientId: null, + }, + secrets: mockServiceNowOAuth.secrets, + errorMessage: `error validating action type config: clientId must be provided when isOAuth = true`, + }, + { + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + userIdentifierValue: null, + }, + secrets: mockServiceNowOAuth.secrets, + errorMessage: `error validating action type config: userIdentifierValue must be provided when isOAuth = true`, + }, + { + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + jwtKeyId: null, + }, + secrets: mockServiceNowOAuth.secrets, + errorMessage: `error validating action type config: jwtKeyId must be provided when isOAuth = true`, + }, + { + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + }, + secrets: { + ...mockServiceNowOAuth.secrets, + clientSecret: null, + }, + errorMessage: `error validating action type secrets: clientSecret and privateKey must both be specified`, + }, + { + config: { + ...mockServiceNowOAuth.config, + apiUrl: serviceNowSimulatorURL, + }, + secrets: { + ...mockServiceNowOAuth.secrets, + privateKey: null, + }, + errorMessage: `error validating action type secrets: clientSecret and privateKey must both be specified`, + }, + ]; + + await asyncForEach(badConfigs, async (badConfig) => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-sir', + config: badConfig.config, + secrets: badConfig.secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: badConfig.errorMessage, + }); + }); + }); + }); + }); + + describe('ServiceNow SIR - Executor', () => { + before(async () => { + const { body } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow simulator', + connector_type_id: '.servicenow-sir', + config: { + apiUrl: serviceNowSimulatorURL, + usesTableApi: false, + isOAuth: false, + }, + secrets: mockServiceNowBasic.secrets, + }); + simulatedActionId = body.id; + }); + + describe('Validation', () => { + it('should handle failing with a simulated success without action', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: {}, + }) + .then((resp: any) => { + expect(Object.keys(resp.body)).to.eql(['status', 'message', 'retry', 'connector_id']); + expect(resp.body.connector_id).to.eql(simulatedActionId); + expect(resp.body.status).to.eql('error'); + }); + }); + + it('should handle failing with a simulated success without unsupported action', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'non-supported' }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subAction]: expected value to equal [pushToService]\n- [4.subAction]: expected value to equal [getChoices]', + }); + }); + }); + + it('should handle failing with a simulated success without subActionParams', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'pushToService' }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.incident.short_description]: expected value of type [string] but got [undefined]\n- [4.subAction]: expected value to equal [getChoices]', + }); + }); + }); + + it('should handle failing with a simulated success without title', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockServiceNowBasic.params, + subActionParams: { + savedObjectId: 'success', + }, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.incident.short_description]: expected value of type [string] but got [undefined]\n- [4.subAction]: expected value to equal [getChoices]', + }); + }); + }); + + it('should handle failing with a simulated success without commentId', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockServiceNowBasic.params, + subActionParams: { + incident: { + ...mockServiceNowBasic.params.subActionParams.incident, + short_description: 'success', + }, + comments: [{ comment: 'boo' }], + }, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.commentId]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [4.subAction]: expected value to equal [getChoices]', + }); + }); + }); + + it('should handle failing with a simulated success without comment message', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockServiceNowBasic.params, + subActionParams: { + incident: { + ...mockServiceNowBasic.params.subActionParams.incident, + short_description: 'success', + }, + comments: [{ commentId: 'success' }], + }, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.comment]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [4.subAction]: expected value to equal [getChoices]', + }); + }); + }); + + describe('getChoices', () => { + it('should fail when field is not provided', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'getChoices', + subActionParams: {}, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subAction]: expected value to equal [pushToService]\n- [4.subActionParams.fields]: expected value of type [array] but got [undefined]', + }); + }); + }); + }); + }); + + describe('Execution', () => { + // Connectors that use the Import set API + describe('Import set API', () => { + it('should handle creating an incident without comments', async () => { + const { body: result } = await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockServiceNowBasic.params, + subActionParams: { + incident: mockServiceNowBasic.params.subActionParams.incident, + comments: [], + }, + }, + }) + .expect(200); + + expect(proxyHaveBeenCalled).to.equal(true); + expect(result).to.eql({ + status: 'ok', + connector_id: simulatedActionId, + data: { + id: '123', + title: 'INC01', + pushedDate: '2020-03-10T12:24:20.000Z', + url: `${serviceNowSimulatorURL}/nav_to.do?uri=sn_si_incident.do?sys_id=123`, + }, + }); + }); + }); + + // Connectors that use the Table API + describe('Table API', () => { + before(async () => { + const { body } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow simulator', + connector_type_id: '.servicenow-sir', + config: { + apiUrl: serviceNowSimulatorURL, + usesTableApi: true, + }, + secrets: mockServiceNowBasic.secrets, + }); + simulatedActionId = body.id; + }); + + it('should handle creating an incident without comments', async () => { + const { body: result } = await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockServiceNowBasic.params, + subActionParams: { + incident: mockServiceNowBasic.params.subActionParams.incident, + comments: [], + }, + }, + }) + .expect(200); + + expect(proxyHaveBeenCalled).to.equal(true); + expect(result).to.eql({ + status: 'ok', + connector_id: simulatedActionId, + data: { + id: '123', + title: 'INC01', + pushedDate: '2020-03-10T12:24:20.000Z', + url: `${serviceNowSimulatorURL}/nav_to.do?uri=sn_si_incident.do?sys_id=123`, + }, + }); + }); + }); + + describe('getChoices', () => { + it('should get choices', async () => { + const { body: result } = await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'getChoices', + subActionParams: { fields: ['priority'] }, + }, + }) + .expect(200); + + expect(proxyHaveBeenCalled).to.equal(true); + expect(result).to.eql({ + status: 'ok', + connector_id: simulatedActionId, + data: [ + { + dependent_value: '', + label: '1 - Critical', + value: '1', + }, + { + dependent_value: '', + label: '2 - High', + value: '2', + }, + { + dependent_value: '', + label: '3 - Moderate', + value: '3', + }, + { + dependent_value: '', + label: '4 - Low', + value: '4', + }, + { + dependent_value: '', + label: '5 - Planning', + value: '5', + }, + ], + }); + }); + }); + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/slack.ts similarity index 96% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/slack.ts index 66b988fb9b4eb..a05c622d8a1cf 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/slack.ts @@ -9,10 +9,10 @@ import httpProxy from 'http-proxy'; import expect from '@kbn/expect'; import http from 'http'; import getPort from 'get-port'; -import { getHttpProxyServer } from '../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { getSlackServer } from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +import { getSlackServer } from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function slackTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/swimlane.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/swimlane.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/swimlane.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/swimlane.ts index a55e8e30d419a..4119a409d7a4b 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/swimlane.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/swimlane.ts @@ -10,9 +10,9 @@ import expect from '@kbn/expect'; import getPort from 'get-port'; import http from 'http'; -import { getHttpProxyServer } from '../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import { getSwimlaneServer } from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { getSwimlaneServer } from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function swimlaneTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/webhook.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/webhook.ts similarity index 97% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/webhook.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/webhook.ts index 44a74a7a31571..c484dfad69539 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/webhook.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/webhook.ts @@ -10,13 +10,13 @@ import http from 'http'; import expect from '@kbn/expect'; import { URL, format as formatUrl } from 'url'; import getPort from 'get-port'; -import { getHttpProxyServer } from '../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, getWebhookServer, -} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; const defaultValues: Record = { headers: null, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/xmatters.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/xmatters.ts similarity index 96% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/xmatters.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/xmatters.ts index 7ce357bc62e36..e33597057cfe1 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/xmatters.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/builtin_action_types/xmatters.ts @@ -8,13 +8,13 @@ import httpProxy from 'http-proxy'; import expect from '@kbn/expect'; -import { getHttpProxyServer } from '../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { getHttpProxyServer } from '../../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +} from '../../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function xmattersTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/config.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/config.ts new file mode 100644 index 0000000000000..506d0016af4f6 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/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 { createTestConfig } from '../../../../common/config'; + +// eslint-disable-next-line import/no-default-export +export default createTestConfig('security_and_spaces', { + disabledPlugins: [], + license: 'trial', + ssl: true, + enableActionsProxy: true, + publicBaseUrl: true, + testFiles: [require.resolve('.')], +}); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/connector_types.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types.ts similarity index 91% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/connector_types.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types.ts index ec23719880926..feacbaa48be42 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/connector_types.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types.ts @@ -6,9 +6,9 @@ */ import expect from '@kbn/expect'; -import { UserAtSpaceScenarios } from '../../scenarios'; -import { getUrlPrefix } from '../../../common/lib/space_test_utils'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { getUrlPrefix } from '../../../../common/lib/space_test_utils'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function listActionTypesTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts new file mode 100644 index 0000000000000..15d43f9782d94 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts @@ -0,0 +1,230 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { checkAAD, getUrlPrefix, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function createActionTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('create', () => { + const objectRemover = new ObjectRemover(supertest); + + after(() => objectRemover.removeAll()); + + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + describe(scenario.id, () => { + it('should handle create action request appropriately', async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to create a "test.index-record" action', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + objectRemover.add(space.id, response.body.id, 'action', 'actions'); + expect(response.body).to.eql({ + id: response.body.id, + is_preconfigured: false, + is_deprecated: false, + is_missing_secrets: false, + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + }); + expect(typeof response.body.id).to.be('string'); + // Ensure AAD isn't broken + await checkAAD({ + supertest, + spaceId: space.id, + type: 'action', + id: response.body.id, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`should handle create action request appropriately when action type isn't registered`, async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + name: 'My action', + connector_type_id: 'test.unregistered-action-type', + config: {}, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to create a "test.unregistered-action-type" action', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(400); + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: 'Action type "test.unregistered-action-type" is not registered.', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle create action request appropriately when payload is empty and invalid', async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({}); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(400); + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: '[request body.name]: expected value of type [string] but got [undefined]', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`should handle create action request appropriately when config isn't valid`, async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + name: 'my name', + connector_type_id: 'test.index-record', + config: { + unencrypted: 'my unencrypted text', + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to create a "test.index-record" action', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(400); + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type secrets: [encrypted]: expected value of type [string] but got [undefined]', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`should handle create action requests for action types that are not enabled`, async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + name: 'my name', + connector_type_id: 'test.not-enabled', + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to create a "test.not-enabled" action', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: + 'action type "test.not-enabled" is not enabled in the Kibana config xpack.actions.enabledActionTypes', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + }); + } + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/delete.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/delete.ts new file mode 100644 index 0000000000000..b0cfffdd2f464 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/delete.ts @@ -0,0 +1,183 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { getUrlPrefix, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function deleteActionTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('delete', () => { + const objectRemover = new ObjectRemover(supertest); + + after(() => objectRemover.removeAll()); + + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + describe(scenario.id, () => { + it('should handle delete action request appropriately', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + + const response = await supertestWithoutAuth + .delete(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo'); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'global_read at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to delete actions', + }); + objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(204); + expect(response.body).to.eql(''); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`shouldn't delete action from another space`, async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + + const response = await supertestWithoutAuth + .delete(`${getUrlPrefix('other')}/api/actions/connector/${createdAction.id}`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo'); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'global_read at space1': + case 'space_1_all at space2': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to delete actions', + }); + break; + case 'superuser at space1': + expect(response.statusCode).to.eql(404); + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: `Saved object [action/${createdAction.id}] not found`, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`should handle delete request appropriately when action doesn't exist`, async () => { + const response = await supertestWithoutAuth + .delete(`${getUrlPrefix(space.id)}/api/actions/connector/2`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'global_read at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to delete actions', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(404); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`shouldn't delete action from preconfigured list`, async () => { + const response = await supertestWithoutAuth + .delete(`${getUrlPrefix(space.id)}/api/actions/connector/my-slack1`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo'); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'global_read at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to delete actions', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: 'Preconfigured action my-slack1 is not allowed to delete.', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + }); + } + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/execute.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts index 321d14683ee2f..35361920cc729 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts @@ -6,18 +6,16 @@ */ import expect from '@kbn/expect'; -import { IValidatedEvent } from '@kbn/event-log-plugin/server'; -import { UserAtSpaceScenarios } from '../../scenarios'; +import { IValidatedEvent, nanosToMillis } from '@kbn/event-log-plugin/server'; +import { UserAtSpaceScenarios } from '../../../scenarios'; import { ESTestIndexTool, ES_TEST_INDEX_NAME, getUrlPrefix, ObjectRemover, getEventLog, -} from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -const NANOS_IN_MILLIS = 1000 * 1000; +} from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function ({ getService }: FtrProviderContext) { @@ -538,14 +536,12 @@ export default function ({ getService }: FtrProviderContext) { const executeEventEnd = Date.parse(executeEvent?.event?.end || 'undefined'); const dateNow = Date.now(); - expect(typeof duration).to.be('number'); + expect(typeof duration).to.be('string'); expect(executeEventStart).to.be.ok(); expect(startExecuteEventStart).to.equal(executeEventStart); expect(executeEventEnd).to.be.ok(); - const durationDiff = Math.abs( - Math.round(duration! / NANOS_IN_MILLIS) - (executeEventEnd - executeEventStart) - ); + const durationDiff = Math.abs(nanosToMillis(duration!) - (executeEventEnd - executeEventStart)); // account for rounding errors expect(durationDiff < 1).to.equal(true); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts new file mode 100644 index 0000000000000..f58481bb94412 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts @@ -0,0 +1,164 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { getUrlPrefix, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function getActionTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('get', () => { + const objectRemover = new ObjectRemover(supertest); + + afterEach(() => objectRemover.removeAll()); + + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + describe(scenario.id, () => { + it('should handle get action request appropriately', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}`) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to get actions', + }); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body).to.eql({ + id: createdAction.id, + is_preconfigured: false, + connector_type_id: 'test.index-record', + is_deprecated: false, + is_missing_secrets: false, + name: 'My action', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`action shouldn't be acessible from another space`, async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + + const response = await supertestWithoutAuth + .get(`${getUrlPrefix('other')}/api/actions/connector/${createdAction.id}`) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to get actions', + }); + break; + case 'global_read at space1': + case 'superuser at space1': + expect(response.statusCode).to.eql(404); + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: `Saved object [action/${createdAction.id}] not found`, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle get preconfigured action request appropriately', async () => { + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/api/actions/connector/my-slack1`) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to get actions', + }); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body).to.eql({ + id: 'my-slack1', + connector_type_id: '.slack', + name: 'Slack#xyz', + is_preconfigured: true, + is_deprecated: false, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + }); + } + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts index ab6181369755f..103ae5abd3071 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts @@ -6,9 +6,9 @@ */ import expect from '@kbn/expect'; -import { UserAtSpaceScenarios } from '../../scenarios'; -import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function getAllActionTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts new file mode 100644 index 0000000000000..9c1b6a4fd8299 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.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 { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { setupSpacesAndUsers, tearDown } from '../../../setup'; + +// eslint-disable-next-line import/no-default-export +export default function actionsTests({ loadTestFile, getService }: FtrProviderContext) { + describe('Actions', () => { + before(async () => { + await setupSpacesAndUsers(getService); + }); + + after(async () => { + await tearDown(getService); + }); + + loadTestFile(require.resolve('./builtin_action_types/email')); + loadTestFile(require.resolve('./builtin_action_types/es_index')); + loadTestFile(require.resolve('./builtin_action_types/es_index_preconfigured')); + loadTestFile(require.resolve('./builtin_action_types/pagerduty')); + loadTestFile(require.resolve('./builtin_action_types/swimlane')); + loadTestFile(require.resolve('./builtin_action_types/server_log')); + loadTestFile(require.resolve('./builtin_action_types/oauth_access_token')); + loadTestFile(require.resolve('./builtin_action_types/servicenow_itsm')); + loadTestFile(require.resolve('./builtin_action_types/servicenow_sir')); + loadTestFile(require.resolve('./builtin_action_types/servicenow_itom')); + loadTestFile(require.resolve('./builtin_action_types/jira')); + loadTestFile(require.resolve('./builtin_action_types/resilient')); + loadTestFile(require.resolve('./builtin_action_types/slack')); + loadTestFile(require.resolve('./builtin_action_types/webhook')); + loadTestFile(require.resolve('./builtin_action_types/xmatters')); + loadTestFile(require.resolve('./create')); + loadTestFile(require.resolve('./delete')); + loadTestFile(require.resolve('./execute')); + loadTestFile(require.resolve('./get_all')); + loadTestFile(require.resolve('./get')); + loadTestFile(require.resolve('./connector_types')); + loadTestFile(require.resolve('./update')); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/manual/pr_40694.js b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/manual/pr_40694.js similarity index 100% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/manual/pr_40694.js rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/manual/pr_40694.js diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/update.ts new file mode 100644 index 0000000000000..6a9181d5ba5dd --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/update.ts @@ -0,0 +1,357 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { UserAtSpaceScenarios } from '../../../scenarios'; +import { checkAAD, getUrlPrefix, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function updateActionTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('update', () => { + const objectRemover = new ObjectRemover(supertest); + + after(() => objectRemover.removeAll()); + + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + describe(scenario.id, () => { + it('should handle update action request appropriately', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action updated', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to update actions', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body).to.eql({ + id: createdAction.id, + is_preconfigured: false, + is_deprecated: false, + connector_type_id: 'test.index-record', + is_missing_secrets: false, + name: 'My action updated', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + }); + // Ensure AAD isn't broken + await checkAAD({ + supertest, + spaceId: space.id, + type: 'action', + id: createdAction.id, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`shouldn't update action from another space`, async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + + const response = await supertestWithoutAuth + .put(`${getUrlPrefix('other')}/api/actions/connector/${createdAction.id}`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action updated', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + case 'global_read at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to update actions', + }); + break; + case 'superuser at space1': + expect(response.statusCode).to.eql(404); + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: `Saved object [action/${createdAction.id}] not found`, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle update action request appropriately when passing a null config', async () => { + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/actions/connector/1`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + name: 'My action updated', + config: null, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(400); + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: '[request body.config]: expected value of type [object] but got [null]', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`should handle update action request appropriately when action doesn't exist`, async () => { + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/actions/connector/1`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + name: 'My action updated', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to update actions', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(404); + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: 'Saved object [action/1] not found', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle update action request appropriately when payload is empty and invalid', async () => { + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/actions/connector/1`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({}); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(400); + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: '[request body.name]: expected value of type [string] but got [undefined]', + // message: '[request body.config]: expected value of type [object] but got [null]', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle update action request appropriately when secrets are not valid', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + name: 'My action updated', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 42, + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to update actions', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(400); + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type secrets: [encrypted]: expected value of type [string] but got [number]', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`shouldn't update action from preconfigured list`, async () => { + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/actions/connector/custom-system-abc-connector`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action updated', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to update actions', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: `Preconfigured action custom-system-abc-connector is not allowed to update.`, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + }); + } + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts new file mode 100644 index 0000000000000..5dfbdfa9707c2 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts @@ -0,0 +1,1350 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { omit } from 'lodash'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { IValidatedEvent, nanosToMillis } from '@kbn/event-log-plugin/server'; +import { TaskRunning, TaskRunningStage } from '@kbn/task-manager-plugin/server/task_running'; +import { ConcreteTaskInstance } from '@kbn/task-manager-plugin/server'; +import { UserAtSpaceScenarios, Superuser } from '../../../scenarios'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { + ESTestIndexTool, + ES_TEST_INDEX_NAME, + getUrlPrefix, + getTestRuleData, + ObjectRemover, + AlertUtils, + getConsumerUnauthorizedErrorMessage, + TaskManagerUtils, + getEventLog, +} from '../../../../common/lib'; + +// eslint-disable-next-line import/no-default-export +export default function alertTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const es = getService('es'); + const retry = getService('retry'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const esTestIndexTool = new ESTestIndexTool(es, retry); + const taskManagerUtils = new TaskManagerUtils(es, retry); + + describe('alerts', () => { + const authorizationIndex = '.kibana-test-authorization'; + const objectRemover = new ObjectRemover(supertest); + + before(async () => { + await esTestIndexTool.destroy(); + await esTestIndexTool.setup(); + await es.indices.create({ index: authorizationIndex }); + }); + afterEach(() => objectRemover.removeAll()); + after(async () => { + await esTestIndexTool.destroy(); + await es.indices.delete({ index: authorizationIndex }); + }); + + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + + describe(scenario.id, () => { + let alertUtils: AlertUtils; + let indexRecordActionId: string; + + before(async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + indexRecordActionId = createdAction.id; + alertUtils = new AlertUtils({ + user, + space, + supertestWithoutAuth, + indexRecordActionId, + objectRemover, + }); + }); + after(() => objectRemover.add(space.id, indexRecordActionId, 'connector', 'actions')); + + it('should schedule task, run alert and schedule actions when appropriate', async () => { + const testStart = new Date(); + const reference = alertUtils.generateReference(); + const response = await alertUtils.createAlwaysFiringAction({ reference }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.always-firing', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to get actions`, + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + + // Wait for the action to index a document before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('action:test.index-record', reference); + + await taskManagerUtils.waitForAllTasksIdle(testStart); + + const alertId = response.body.id; + await alertUtils.disable(alertId); + await taskManagerUtils.waitForEmpty(testStart); + + // Ensure only 1 alert executed with proper params + const alertSearchResult = await esTestIndexTool.search( + 'alert:test.always-firing', + reference + ); + // @ts-expect-error doesnt handle total: number + expect(alertSearchResult.body.hits.total.value).to.eql(1); + const alertSearchResultWithoutDates = omit( + alertSearchResult.body.hits.hits[0]._source as object, + ['alertInfo.createdAt', 'alertInfo.updatedAt'] + ); + expect(alertSearchResultWithoutDates).to.eql({ + source: 'alert:test.always-firing', + reference, + state: {}, + params: { + index: ES_TEST_INDEX_NAME, + reference, + }, + alertInfo: { + alertId, + consumer: 'alertsFixture', + spaceId: space.id, + namespace: space.id, + name: 'abc', + enabled: true, + notifyWhen: 'onActiveAlert', + schedule: { + interval: '1m', + }, + tags: ['tag-A', 'tag-B'], + throttle: '1m', + createdBy: user.fullName, + updatedBy: user.fullName, + actions: response.body.actions.map((action: any) => { + /* eslint-disable @typescript-eslint/naming-convention */ + const { connector_type_id, group, id, params } = action; + return { + actionTypeId: connector_type_id, + group, + id, + params, + }; + }), + producer: 'alertsFixture', + ruleTypeId: 'test.always-firing', + ruleTypeName: 'Test: Always Firing', + }, + }); + // @ts-expect-error _source: unknown + expect(alertSearchResult.body.hits.hits[0]._source.alertInfo.createdAt).to.match( + /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/ + ); + + // @ts-expect-error _source: unknown + expect(alertSearchResult.body.hits.hits[0]._source.alertInfo.updatedAt).to.match( + /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/ + ); + + // Ensure only 1 action executed with proper params + const actionSearchResult = await esTestIndexTool.search( + 'action:test.index-record', + reference + ); + // @ts-expect-error doesnt handle total: number + expect(actionSearchResult.body.hits.total.value).to.eql(1); + expect(actionSearchResult.body.hits.hits[0]._source).to.eql({ + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + params: { + index: ES_TEST_INDEX_NAME, + reference, + message: ` +alertId: ${alertId}, +alertName: abc, +spaceId: ${space.id}, +tags: tag-A,tag-B, +alertInstanceId: 1, +alertActionGroup: default, +instanceContextValue: true, +instanceStateValue: true +`.trim(), + }, + reference, + source: 'action:test.index-record', + }); + + await taskManagerUtils.waitForActionTaskParamsToBeCleanedUp(testStart); + + await validateEventLog({ + spaceId: space.id, + alertId, + ruleTypeId: 'test.always-firing', + outcome: 'success', + message: `rule executed: test.always-firing:${alertId}: 'abc'`, + ruleObject: alertSearchResultWithoutDates, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should schedule task, run alert and schedule preconfigured actions when appropriate', async () => { + const testStart = new Date(); + const reference = alertUtils.generateReference(); + const response = await alertUtils.createAlwaysFiringAction({ + reference, + indexRecordActionId: 'preconfigured.test.index-record', + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.always-firing', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to get actions`, + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + + // Wait for the action to index a document before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('action:test.index-record', reference); + + await taskManagerUtils.waitForAllTasksIdle(testStart); + + const alertId = response.body.id; + await alertUtils.disable(alertId); + await taskManagerUtils.waitForEmpty(testStart); + + // Ensure only 1 alert executed with proper params + const alertSearchResult = await esTestIndexTool.search( + 'alert:test.always-firing', + reference + ); + // @ts-expect-error doesnt handle total: number + expect(alertSearchResult.body.hits.total.value).to.eql(1); + const alertSearchResultWithoutDates = omit( + alertSearchResult.body.hits.hits[0]._source as object, + ['alertInfo.createdAt', 'alertInfo.updatedAt'] + ); + expect(alertSearchResultWithoutDates).to.eql({ + source: 'alert:test.always-firing', + reference, + state: {}, + params: { + index: ES_TEST_INDEX_NAME, + reference, + }, + alertInfo: { + alertId, + consumer: 'alertsFixture', + spaceId: space.id, + namespace: space.id, + name: 'abc', + enabled: true, + notifyWhen: 'onActiveAlert', + schedule: { + interval: '1m', + }, + tags: ['tag-A', 'tag-B'], + throttle: '1m', + createdBy: user.fullName, + updatedBy: user.fullName, + actions: response.body.actions.map((action: any) => { + /* eslint-disable @typescript-eslint/naming-convention */ + const { connector_type_id, group, id, params } = action; + return { + actionTypeId: connector_type_id, + group, + id, + params, + }; + }), + producer: 'alertsFixture', + ruleTypeId: 'test.always-firing', + ruleTypeName: 'Test: Always Firing', + }, + }); + + // @ts-expect-error _source: unknown + expect(alertSearchResult.body.hits.hits[0]._source.alertInfo.createdAt).to.match( + /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/ + ); + // @ts-expect-error _source: unknown + expect(alertSearchResult.body.hits.hits[0]._source.alertInfo.updatedAt).to.match( + /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/ + ); + // Ensure only 1 action executed with proper params + const actionSearchResult = await esTestIndexTool.search( + 'action:test.index-record', + reference + ); + // @ts-expect-error doesnt handle total: number + expect(actionSearchResult.body.hits.total.value).to.eql(1); + expect(actionSearchResult.body.hits.hits[0]._source).to.eql({ + config: { + unencrypted: 'ignored-but-required', + }, + secrets: { + encrypted: 'this-is-also-ignored-and-also-required', + }, + params: { + index: ES_TEST_INDEX_NAME, + reference, + message: ` +alertId: ${alertId}, +alertName: abc, +spaceId: ${space.id}, +tags: tag-A,tag-B, +alertInstanceId: 1, +alertActionGroup: default, +instanceContextValue: true, +instanceStateValue: true +`.trim(), + }, + reference, + source: 'action:test.index-record', + }); + + await taskManagerUtils.waitForActionTaskParamsToBeCleanedUp(testStart); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should pass updated alert params to executor', async () => { + const testStart = new Date(); + // create an alert + const reference = alertUtils.generateReference(); + const response = await alertUtils.createAlwaysFiringAction({ + reference, + overwrites: { throttle: null }, + }); + + // only need to test creation success paths + if (response.statusCode !== 200) return; + + // Wait for the action to index a document before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('action:test.index-record', reference); + + // Avoid invalidating an API key while the alert is executing + await taskManagerUtils.waitForAllTasksIdle(testStart); + + // update the alert with super user + const alertId = response.body.id; + const reference2 = alertUtils.generateReference(); + const response2 = await alertUtils.updateAlwaysFiringAction({ + alertId, + actionId: indexRecordActionId, + user: Superuser, + reference: reference2, + overwrites: { + name: 'def', + tags: ['fee', 'fi', 'fo'], + // This will cause the task to re-run on update + schedule: { interval: '59s' }, + }, + }); + + expect(response2.statusCode).to.eql(200); + + // make sure alert info passed to executor is correct + await esTestIndexTool.waitForDocs('alert:test.always-firing', reference2); + + await taskManagerUtils.waitForAllTasksIdle(testStart); + + await alertUtils.disable(alertId); + const alertSearchResult = await esTestIndexTool.search( + 'alert:test.always-firing', + reference2 + ); + + // @ts-expect-error doesnt handle total: number + expect(alertSearchResult.body.hits.total.value).to.be.greaterThan(0); + const alertSearchResultInfoWithoutDates = omit( + // @ts-expect-error _source: unknown + alertSearchResult.body.hits.hits[0]._source.alertInfo, + ['createdAt', 'updatedAt'] + ); + expect(alertSearchResultInfoWithoutDates).to.eql({ + alertId, + consumer: 'alertsFixture', + spaceId: space.id, + namespace: space.id, + name: 'def', + enabled: true, + notifyWhen: 'onActiveAlert', + schedule: { + interval: '59s', + }, + tags: ['fee', 'fi', 'fo'], + throttle: '1m', + createdBy: user.fullName, + updatedBy: Superuser.fullName, + actions: response2.body.actions.map((action: any) => { + /* eslint-disable @typescript-eslint/naming-convention */ + const { connector_type_id, group, id, params } = action; + return { + actionTypeId: connector_type_id, + group, + id, + params, + }; + }), + producer: 'alertsFixture', + ruleTypeId: 'test.always-firing', + ruleTypeName: 'Test: Always Firing', + }); + + // @ts-expect-error _source: unknown + expect(alertSearchResult.body.hits.hits[0]._source.alertInfo.createdAt).to.match( + /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/ + ); + // @ts-expect-error _source: unknown + expect(alertSearchResult.body.hits.hits[0]._source.alertInfo.updatedAt).to.match( + /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/ + ); + }); + + it('should handle custom retry logic when appropriate', async () => { + const testStart = new Date().toISOString(); + // We have to provide the test.rate-limit the next runAt, for testing purposes + const retryDate = new Date(Date.now() + 60000); + + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'Test rate limit', + connector_type_id: 'test.rate-limit', + config: {}, + }) + .expect(200); + objectRemover.add(space.id, createdAction.id, 'connector', 'actions'); + + const reference = alertUtils.generateReference(); + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send( + getTestRuleData({ + rule_type_id: 'test.always-firing', + params: { + index: ES_TEST_INDEX_NAME, + reference: 'create-test-2', + }, + actions: [ + { + group: 'default', + id: createdAction.id, + params: { + reference, + index: ES_TEST_INDEX_NAME, + retryAt: retryDate.getTime(), + }, + }, + ], + }) + ); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.always-firing', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to get actions`, + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); + + // Wait for the task to be attempted once and idle + const scheduledActionTask: estypes.SearchHit< + TaskRunning + > = await retry.try(async () => { + const searchResult = await es.search< + TaskRunning + >({ + index: '.kibana_task_manager', + body: { + query: { + bool: { + must: [ + { + term: { + 'task.status': 'idle', + }, + }, + { + term: { + 'task.attempts': 1, + }, + }, + { + term: { + 'task.taskType': 'actions:test.rate-limit', + }, + }, + { + range: { + 'task.scheduledAt': { + gte: testStart, + }, + }, + }, + ], + }, + }, + }, + }); + expect((searchResult.hits.total as estypes.SearchTotalHits).value).to.eql(1); + return searchResult.hits.hits[0]; + }); + + // Ensure the next runAt is set to the retryDate by custom logic + expect(scheduledActionTask._source!.task.runAt).to.eql(retryDate.toISOString()); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should have proper callCluster and savedObjectsClient authorization for alert type executor when appropriate', async () => { + let searchResult: any; + const testStart = new Date(); + const reference = alertUtils.generateReference(); + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send( + getTestRuleData({ + rule_type_id: 'test.authorization', + params: { + callClusterAuthorizationIndex: authorizationIndex, + savedObjectsClientType: 'dashboard', + savedObjectsClientId: '1', + index: ES_TEST_INDEX_NAME, + reference, + }, + }) + ); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.authorization', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); + + // Wait for test.authorization to index a document before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('alert:test.authorization', reference); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForEmpty(testStart); + + // Ensure only 1 document exists with proper params + searchResult = await esTestIndexTool.search('alert:test.authorization', reference); + expect(searchResult.body.hits.total.value).to.eql(1); + expect(searchResult.body.hits.hits[0]._source.state).to.eql({ + callClusterSuccess: false, + callScopedClusterSuccess: false, + savedObjectsClientSuccess: false, + callClusterError: { + ...searchResult.body.hits.hits[0]._source.state.callClusterError, + }, + callScopedClusterError: { + ...searchResult.body.hits.hits[0]._source.state.callScopedClusterError, + }, + savedObjectsClientError: { + ...searchResult.body.hits.hits[0]._source.state.savedObjectsClientError, + output: { + ...searchResult.body.hits.hits[0]._source.state.savedObjectsClientError.output, + statusCode: 403, + }, + }, + }); + break; + case 'superuser at space1': + expect(response.statusCode).to.eql(200); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); + + // Wait for test.authorization to index a document before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('alert:test.authorization', reference); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForEmpty(testStart); + + // Ensure only 1 document exists with proper params + searchResult = await esTestIndexTool.search('alert:test.authorization', reference); + expect(searchResult.body.hits.total.value).to.eql(1); + expect(searchResult.body.hits.hits[0]._source.state).to.eql({ + callClusterSuccess: true, + callScopedClusterSuccess: true, + savedObjectsClientSuccess: false, + savedObjectsClientError: { + ...searchResult.body.hits.hits[0]._source.state.savedObjectsClientError, + output: { + ...searchResult.body.hits.hits[0]._source.state.savedObjectsClientError.output, + statusCode: 404, + }, + }, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should have proper callCluster and savedObjectsClient authorization for action type executor when appropriate', async () => { + let searchResult: any; + const testStart = new Date(); + const reference = alertUtils.generateReference(); + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.authorization', + }) + .expect(200); + objectRemover.add(space.id, createdAction.id, 'connector', 'actions'); + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send( + getTestRuleData({ + rule_type_id: 'test.always-firing', + params: { + index: ES_TEST_INDEX_NAME, + reference, + }, + actions: [ + { + group: 'default', + id: createdAction.id, + params: { + callClusterAuthorizationIndex: authorizationIndex, + savedObjectsClientType: 'dashboard', + savedObjectsClientId: '1', + index: ES_TEST_INDEX_NAME, + reference, + }, + }, + ], + }) + ); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.always-firing', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); + + // Ensure test.authorization indexed 1 document before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('action:test.authorization', reference); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForEmpty(testStart); + + // Ensure only 1 document with proper params exists + searchResult = await esTestIndexTool.search('action:test.authorization', reference); + expect(searchResult.body.hits.total.value).to.eql(1); + expect(searchResult.body.hits.hits[0]._source.state).to.eql({ + callClusterSuccess: false, + callScopedClusterSuccess: false, + savedObjectsClientSuccess: false, + callClusterError: { + ...searchResult.body.hits.hits[0]._source.state.callClusterError, + }, + callScopedClusterError: { + ...searchResult.body.hits.hits[0]._source.state.callScopedClusterError, + }, + savedObjectsClientError: { + ...searchResult.body.hits.hits[0]._source.state.savedObjectsClientError, + output: { + ...searchResult.body.hits.hits[0]._source.state.savedObjectsClientError.output, + statusCode: 403, + }, + }, + }); + break; + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to get actions`, + statusCode: 403, + }); + break; + case 'superuser at space1': + expect(response.statusCode).to.eql(200); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); + + // Ensure test.authorization indexed 1 document before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('action:test.authorization', reference); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForEmpty(testStart); + + // Ensure only 1 document with proper params exists + searchResult = await esTestIndexTool.search('action:test.authorization', reference); + expect(searchResult.body.hits.total.value).to.eql(1); + expect(searchResult.body.hits.hits[0]._source.state).to.eql({ + callClusterSuccess: true, + callScopedClusterSuccess: true, + savedObjectsClientSuccess: false, + savedObjectsClientError: { + ...searchResult.body.hits.hits[0]._source.state.savedObjectsClientError, + output: { + ...searchResult.body.hits.hits[0]._source.state.savedObjectsClientError.output, + statusCode: 404, + }, + }, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should throttle alerts when appropriate', async () => { + const testStart = new Date(); + const reference = alertUtils.generateReference(); + const response = await alertUtils.createAlwaysFiringAction({ + reference, + overwrites: { + schedule: { interval: '1s' }, + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.always-firing', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to get actions`, + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + case 'superuser at space1': + expect(response.statusCode).to.eql(200); + // Wait until alerts scheduled actions 3 times before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('alert:test.always-firing', reference, 3); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForEmpty(testStart); + + // Ensure actions only executed once + const searchResult = await esTestIndexTool.search( + 'action:test.index-record', + reference + ); + // @ts-expect-error doesnt handle total: number + expect(searchResult.body.hits.total.value).to.eql(1); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should not throttle when changing groups', async () => { + const testStart = new Date(); + const reference = alertUtils.generateReference(); + const response = await alertUtils.createAlwaysFiringAction({ + reference, + overwrites: { + schedule: { interval: '1s' }, + params: { + index: ES_TEST_INDEX_NAME, + reference, + groupsToScheduleActionsInSeries: ['default', 'other'], + }, + actions: [ + { + group: 'default', + id: indexRecordActionId, + params: { + index: ES_TEST_INDEX_NAME, + reference, + message: 'from:default', + }, + }, + { + group: 'other', + id: indexRecordActionId, + params: { + index: ES_TEST_INDEX_NAME, + reference, + message: 'from:other', + }, + }, + ], + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.always-firing', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to get actions`, + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + case 'superuser at space1': + expect(response.statusCode).to.eql(200); + // Wait for actions to execute twice before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('action:test.index-record', reference, 2); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForEmpty(testStart); + + // Ensure only 2 actions with proper params exists + const searchResult = await esTestIndexTool.search( + 'action:test.index-record', + reference + ); + // @ts-expect-error doesnt handle total: number + expect(searchResult.body.hits.total.value).to.eql(2); + const messages: string[] = searchResult.body.hits.hits.map( + // @ts-expect-error _search: unknown + (hit: { _source: { params: { message: string } } }) => hit._source.params.message + ); + expect(messages.sort()).to.eql(['from:default', 'from:other']); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should not throttle when changing subgroups', async () => { + const testStart = new Date(); + const reference = alertUtils.generateReference(); + const response = await alertUtils.createAlwaysFiringAction({ + reference, + overwrites: { + schedule: { interval: '1s' }, + params: { + index: ES_TEST_INDEX_NAME, + reference, + groupsToScheduleActionsInSeries: ['default:prev', 'default:next'], + }, + actions: [ + { + group: 'default', + id: indexRecordActionId, + params: { + index: ES_TEST_INDEX_NAME, + reference, + message: 'from:{{alertActionGroup}}:{{alertActionSubgroup}}', + }, + }, + ], + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.always-firing', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to get actions`, + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + case 'superuser at space1': + expect(response.statusCode).to.eql(200); + // Wait for actions to execute twice before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('action:test.index-record', reference, 2); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForEmpty(testStart); + + // Ensure only 2 actions with proper params exists + const searchResult = await esTestIndexTool.search( + 'action:test.index-record', + reference + ); + // @ts-expect-error doesnt handle total: number + expect(searchResult.body.hits.total.value).to.eql(2); + const messages: string[] = searchResult.body.hits.hits.map( + // @ts-expect-error _source: unknown + (hit: { _source: { params: { message: string } } }) => hit._source.params.message + ); + expect(messages.sort()).to.eql(['from:default:next', 'from:default:prev']); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should reset throttle window when not firing', async () => { + const testStart = new Date(); + const reference = alertUtils.generateReference(); + const response = await alertUtils.createAlwaysFiringAction({ + reference, + overwrites: { + schedule: { interval: '1s' }, + params: { + index: ES_TEST_INDEX_NAME, + reference, + groupsToScheduleActionsInSeries: ['default', null, 'default'], + }, + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.always-firing', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to get actions`, + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + case 'superuser at space1': + expect(response.statusCode).to.eql(200); + // Actions should execute twice before widning things down + await esTestIndexTool.waitForDocs('action:test.index-record', reference, 2); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForEmpty(testStart); + + // Ensure only 2 actions are executed + const searchResult = await esTestIndexTool.search( + 'action:test.index-record', + reference + ); + // @ts-expect-error doesnt handle total: number + expect(searchResult.body.hits.total.value).to.eql(2); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`shouldn't schedule actions when alert is muted`, async () => { + const testStart = new Date(); + const reference = alertUtils.generateReference(); + const response = await alertUtils.createAlwaysFiringAction({ + reference, + overwrites: { + enabled: false, + schedule: { interval: '1s' }, + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.always-firing', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to get actions`, + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + case 'superuser at space1': + await alertUtils.muteAll(response.body.id); + await alertUtils.enable(response.body.id); + + // Wait until alerts schedule actions twice to ensure actions had a chance to skip + // execution once before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('alert:test.always-firing', reference, 2); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForEmpty(testStart); + + // Should not have executed any action + const executedActionsResult = await esTestIndexTool.search( + 'action:test.index-record', + reference + ); + // @ts-expect-error doesnt handle total: number + expect(executedActionsResult.body.hits.total.value).to.eql(0); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`shouldn't schedule actions when alert instance is muted`, async () => { + const testStart = new Date(); + const reference = alertUtils.generateReference(); + const response = await alertUtils.createAlwaysFiringAction({ + reference, + overwrites: { + enabled: false, + schedule: { interval: '1s' }, + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.always-firing', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to get actions`, + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + case 'superuser at space1': + await alertUtils.muteInstance(response.body.id, '1'); + await alertUtils.enable(response.body.id); + + // Wait until alerts scheduled actions twice to ensure actions had a chance to execute + // once before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('alert:test.always-firing', reference, 2); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForEmpty(testStart); + + // Should not have executed any action + const executedActionsResult = await esTestIndexTool.search( + 'action:test.index-record', + reference + ); + // @ts-expect-error doesnt handle total: number + expect(executedActionsResult.body.hits.total.value).to.eql(0); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`should unmute all instances when unmuting an alert`, async () => { + const testStart = new Date(); + const reference = alertUtils.generateReference(); + const response = await alertUtils.createAlwaysFiringAction({ + reference, + overwrites: { + enabled: false, + schedule: { interval: '1s' }, + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'create', + 'test.always-firing', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to get actions`, + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + case 'superuser at space1': + await alertUtils.muteInstance(response.body.id, '1'); + await alertUtils.muteAll(response.body.id); + await alertUtils.unmuteAll(response.body.id); + await alertUtils.enable(response.body.id); + + // Ensure actions are executed once before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('action:test.index-record', reference, 1); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForEmpty(testStart); + + // Should have one document indexed by the action + const searchResult = await esTestIndexTool.search( + 'action:test.index-record', + reference + ); + // @ts-expect-error doesnt handle total: number + expect(searchResult.body.hits.total.value).to.eql(1); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + }); + } + }); + + interface ValidateEventLogParams { + spaceId: string; + alertId: string; + ruleTypeId: string; + outcome: string; + message: string; + errorMessage?: string; + ruleObject: any; + } + + async function validateEventLog(params: ValidateEventLogParams): Promise { + const { spaceId, alertId, outcome, message, errorMessage, ruleObject } = params; + + const events: IValidatedEvent[] = await retry.try(async () => { + return await getEventLog({ + getService, + spaceId, + type: 'alert', + id: alertId, + provider: 'alerting', + actions: new Map([['execute', { gte: 1 }]]), + }); + }); + + const event = events[0]; + + const duration = event?.event?.duration; + const eventStart = Date.parse(event?.event?.start || 'undefined'); + const eventEnd = Date.parse(event?.event?.end || 'undefined'); + const dateNow = Date.now(); + + expect(typeof duration).to.be('string'); + expect(eventStart).to.be.ok(); + expect(eventEnd).to.be.ok(); + + const durationDiff = Math.abs(nanosToMillis(duration!) - (eventEnd - eventStart)); + + // account for rounding errors + expect(durationDiff < 1).to.equal(true); + expect(eventStart <= eventEnd).to.equal(true); + expect(eventEnd <= dateNow).to.equal(true); + + expect(event?.event?.outcome).to.equal(outcome); + + expect(event?.kibana?.saved_objects).to.eql([ + { + rel: 'primary', + type: 'alert', + id: alertId, + namespace: spaceId, + type_id: ruleObject.alertInfo.ruleTypeId, + }, + ]); + + expect(event?.kibana?.alert?.rule?.execution?.metrics?.number_of_triggered_actions).to.be(1); + expect(event?.kibana?.alert?.rule?.execution?.metrics?.number_of_searches).to.be(0); + expect(event?.kibana?.alert?.rule?.execution?.metrics?.es_search_duration_ms).to.be(0); + expect(event?.kibana?.alert?.rule?.execution?.metrics?.total_search_duration_ms).to.be(0); + expect(event?.kibana?.alert?.rule?.execution?.metrics?.number_of_active_alerts).to.be(1); + expect(event?.kibana?.alert?.rule?.execution?.metrics?.number_of_new_alerts).to.be(1); + expect(event?.kibana?.alert?.rule?.execution?.metrics?.number_of_recovered_alerts).to.be(0); + expect(event?.kibana?.alert?.rule?.execution?.metrics?.total_number_of_alerts).to.be(1); + + expect(event?.rule).to.eql({ + id: alertId, + license: 'basic', + category: ruleObject.alertInfo.ruleTypeId, + ruleset: ruleObject.alertInfo.producer, + name: ruleObject.alertInfo.name, + }); + + expect(event?.message).to.eql(message); + + if (errorMessage) { + expect(event?.error?.message).to.eql(errorMessage); + } + } +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/event_log.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/event_log.ts similarity index 92% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/event_log.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/event_log.ts index 4a572002a4366..424c734b5c6a2 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/event_log.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/event_log.ts @@ -6,10 +6,10 @@ */ import expect from '@kbn/expect'; -import { Spaces } from '../../scenarios'; -import { getUrlPrefix, getTestRuleData, ObjectRemover, getEventLog } from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { validateEvent } from '../../../spaces_only/tests/alerting/event_log'; +import { Spaces } from '../../../scenarios'; +import { getUrlPrefix, getTestRuleData, ObjectRemover, getEventLog } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { validateEvent } from '../../../../spaces_only/tests/alerting/event_log'; // eslint-disable-next-line import/no-default-export export default function eventLogTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/excluded.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/excluded.ts similarity index 94% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/excluded.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/excluded.ts index eae80da85dc59..d09edae045e6f 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/excluded.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/excluded.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { UserAtSpaceScenarios } from '../../scenarios'; +import { UserAtSpaceScenarios } from '../../../scenarios'; import { getTestRuleData, getUrlPrefix, @@ -14,8 +14,8 @@ import { getEventLog, AlertUtils, ES_TEST_INDEX_NAME, -} from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +} from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function createAlertTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/health.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/health.ts similarity index 97% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/health.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/health.ts index d51cf8cc96af9..0663696dbee14 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/health.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/health.ts @@ -6,8 +6,8 @@ */ import expect from '@kbn/expect'; -import { UserAtSpaceScenarios } from '../../scenarios'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getUrlPrefix, getTestRuleData, @@ -15,7 +15,7 @@ import { AlertUtils, ESTestIndexTool, ES_TEST_INDEX_NAME, -} from '../../../common/lib'; +} from '../../../../common/lib'; // eslint-disable-next-line import/no-default-export export default function createFindTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/index.ts new file mode 100644 index 0000000000000..72890c2bbd90a --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/index.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { setupSpacesAndUsers, tearDown } from '../../../setup'; + +// eslint-disable-next-line import/no-default-export +export default function alertingTests({ loadTestFile, getService }: FtrProviderContext) { + describe('Alerts', () => { + describe('legacy alerts', function () { + before(async () => { + await setupSpacesAndUsers(getService); + }); + + after(async () => { + await tearDown(getService); + }); + + loadTestFile(require.resolve('./rbac_legacy')); + }); + + describe('alerts', () => { + before(async () => { + await setupSpacesAndUsers(getService); + }); + + after(async () => { + await tearDown(getService); + }); + + loadTestFile(require.resolve('./mute_all')); + loadTestFile(require.resolve('./mute_instance')); + loadTestFile(require.resolve('./unmute_all')); + loadTestFile(require.resolve('./unmute_instance')); + loadTestFile(require.resolve('./update')); + loadTestFile(require.resolve('./update_api_key')); + loadTestFile(require.resolve('./alerts')); + loadTestFile(require.resolve('./event_log')); + loadTestFile(require.resolve('./mustache_templates')); + loadTestFile(require.resolve('./health')); + loadTestFile(require.resolve('./excluded')); + loadTestFile(require.resolve('./snooze')); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mustache_templates.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/mustache_templates.ts similarity index 92% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mustache_templates.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/mustache_templates.ts index 7e3a7599a73e0..2426c154aa443 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mustache_templates.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/mustache_templates.ts @@ -18,11 +18,11 @@ import axios from 'axios'; import httpProxy from 'http-proxy'; import expect from '@kbn/expect'; -import { Spaces } from '../../scenarios'; -import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { getSlackServer } from '../../../common/fixtures/plugins/actions_simulators/server/plugin'; -import { getHttpProxyServer } from '../../../common/lib/get_proxy_server'; +import { Spaces } from '../../../scenarios'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { getSlackServer } from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +import { getHttpProxyServer } from '../../../../common/lib/get_proxy_server'; // eslint-disable-next-line import/no-default-export export default function executionStatusAlertTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/mute_all.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_all.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/mute_all.ts index 1cac93cb52b78..5a4c792463b62 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/mute_all.ts @@ -6,8 +6,8 @@ */ import expect from '@kbn/expect'; -import { UserAtSpaceScenarios } from '../../scenarios'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { AlertUtils, checkAAD, @@ -16,7 +16,7 @@ import { ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, -} from '../../../common/lib'; +} from '../../../../common/lib'; // eslint-disable-next-line import/no-default-export export default function createMuteAlertTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_instance.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/mute_instance.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_instance.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/mute_instance.ts index 3948f910423a9..63a285e0f4cb8 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_instance.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/mute_instance.ts @@ -6,8 +6,8 @@ */ import expect from '@kbn/expect'; -import { UserAtSpaceScenarios } from '../../scenarios'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { AlertUtils, checkAAD, @@ -16,7 +16,7 @@ import { ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, -} from '../../../common/lib'; +} from '../../../../common/lib'; // eslint-disable-next-line import/no-default-export export default function createMuteAlertInstanceTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/rbac_legacy.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/rbac_legacy.ts index 4f324cae689cd..4f0f53383e206 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/rbac_legacy.ts @@ -7,10 +7,10 @@ import expect from '@kbn/expect'; import { SavedObjectsUtils } from '@kbn/core/server/saved_objects'; -import { UserAtSpaceScenarios, Superuser } from '../../scenarios'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { ESTestIndexTool, getUrlPrefix, ObjectRemover, AlertUtils } from '../../../common/lib'; -import { setupSpacesAndUsers } from '..'; +import { UserAtSpaceScenarios, Superuser } from '../../../scenarios'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { ESTestIndexTool, getUrlPrefix, ObjectRemover, AlertUtils } from '../../../../common/lib'; +import { setupSpacesAndUsers } from '../../../setup'; // eslint-disable-next-line import/no-default-export export default function alertTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/snooze.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/snooze.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/snooze.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/snooze.ts index 929b95535e195..553e090498f00 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/snooze.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/snooze.ts @@ -6,8 +6,8 @@ */ import expect from '@kbn/expect'; -import { UserAtSpaceScenarios } from '../../scenarios'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { AlertUtils, checkAAD, @@ -16,7 +16,7 @@ import { ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, -} from '../../../common/lib'; +} from '../../../../common/lib'; const FUTURE_SNOOZE_TIME = '9999-12-31T06:00:00.000Z'; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/unmute_all.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_all.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/unmute_all.ts index e97e7e73abe44..dde198f54f771 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/unmute_all.ts @@ -6,8 +6,8 @@ */ import expect from '@kbn/expect'; -import { UserAtSpaceScenarios } from '../../scenarios'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { AlertUtils, checkAAD, @@ -16,7 +16,7 @@ import { ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, -} from '../../../common/lib'; +} from '../../../../common/lib'; // eslint-disable-next-line import/no-default-export export default function createUnmuteAlertTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_instance.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/unmute_instance.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_instance.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/unmute_instance.ts index 17ee25e822a6d..1aa84f64a7e79 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_instance.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/unmute_instance.ts @@ -6,8 +6,8 @@ */ import expect from '@kbn/expect'; -import { UserAtSpaceScenarios } from '../../scenarios'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { AlertUtils, checkAAD, @@ -16,7 +16,7 @@ import { ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, -} from '../../../common/lib'; +} from '../../../../common/lib'; // eslint-disable-next-line import/no-default-export export default function createMuteAlertInstanceTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unsnooze.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/unsnooze.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unsnooze.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/unsnooze.ts index ed37a19d80707..c868654235c21 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unsnooze.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/unsnooze.ts @@ -6,8 +6,8 @@ */ import expect from '@kbn/expect'; -import { UserAtSpaceScenarios } from '../../scenarios'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { AlertUtils, checkAAD, @@ -16,7 +16,7 @@ import { ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, -} from '../../../common/lib'; +} from '../../../../common/lib'; // eslint-disable-next-line import/no-default-export export default function createUnsnoozeRuleTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/update.ts new file mode 100644 index 0000000000000..c49fa62c606b6 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/update.ts @@ -0,0 +1,998 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { Response as SupertestResponse } from 'supertest'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { + checkAAD, + getUrlPrefix, + getTestRuleData, + ObjectRemover, + ensureDatetimeIsWithinRange, + getConsumerUnauthorizedErrorMessage, + getProducerUnauthorizedErrorMessage, +} from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function createUpdateTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const retry = getService('retry'); + + function getAlertingTaskById(taskId: string) { + return supertest + .get(`/api/alerting_tasks/${taskId}`) + .expect(200) + .then((response: SupertestResponse) => response.body); + } + + describe('update', () => { + const objectRemover = new ObjectRemover(supertest); + + after(() => objectRemover.removeAll()); + + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + describe(scenario.id, () => { + it('should handle update alert request appropriately', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const updatedData = { + name: 'bcd', + tags: ['bar'], + params: { + foo: true, + }, + schedule: { interval: '12s' }, + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + ], + throttle: '1m', + notify_when: 'onThrottleInterval', + }; + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send(updatedData); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'update', + 'test.noop', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to get actions`, + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body).to.eql({ + ...updatedData, + id: createdAlert.id, + rule_type_id: 'test.noop', + consumer: 'alertsFixture', + created_by: 'elastic', + enabled: true, + updated_by: user.username, + api_key_owner: user.username, + mute_all: false, + muted_alert_ids: [], + actions: [ + { + id: createdAction.id, + connector_type_id: 'test.noop', + group: 'default', + params: {}, + }, + ], + scheduled_task_id: createdAlert.scheduled_task_id, + created_at: response.body.created_at, + updated_at: response.body.updated_at, + execution_status: response.body.execution_status, + }); + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan( + Date.parse(response.body.created_at) + ); + // Ensure AAD isn't broken + await checkAAD({ + supertest, + spaceId: space.id, + type: 'alert', + id: createdAlert.id, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle update alert request appropriately when consumer is the same as producer', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.restricted-noop', + consumer: 'alertsRestrictedFixture', + }) + ) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const updatedData = { + name: 'bcd', + tags: ['bar'], + params: { + foo: true, + }, + schedule: { interval: '12s' }, + actions: [], + throttle: '1m', + notify_when: 'onThrottleInterval', + }; + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send(updatedData); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'update', + 'test.restricted-noop', + 'alertsRestrictedFixture' + ), + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body).to.eql({ + ...updatedData, + id: createdAlert.id, + rule_type_id: 'test.restricted-noop', + consumer: 'alertsRestrictedFixture', + created_by: 'elastic', + enabled: true, + updated_by: user.username, + api_key_owner: user.username, + mute_all: false, + muted_alert_ids: [], + scheduled_task_id: createdAlert.scheduled_task_id, + created_at: response.body.created_at, + updated_at: response.body.updated_at, + execution_status: response.body.execution_status, + }); + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan( + Date.parse(response.body.created_at) + ); + // Ensure AAD isn't broken + await checkAAD({ + supertest, + spaceId: space.id, + type: 'alert', + id: createdAlert.id, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle update alert request appropriately when consumer is not the producer', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.unrestricted-noop', + consumer: 'alertsFixture', + }) + ) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const updatedData = { + name: 'bcd', + tags: ['bar'], + params: { + foo: true, + }, + schedule: { interval: '12s' }, + actions: [], + throttle: '1m', + notify_when: 'onThrottleInterval', + }; + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send(updatedData); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'update', + 'test.unrestricted-noop', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getProducerUnauthorizedErrorMessage( + 'update', + 'test.unrestricted-noop', + 'alertsRestrictedFixture' + ), + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body).to.eql({ + ...updatedData, + id: createdAlert.id, + rule_type_id: 'test.unrestricted-noop', + consumer: 'alertsFixture', + created_by: 'elastic', + enabled: true, + updated_by: user.username, + api_key_owner: user.username, + mute_all: false, + muted_alert_ids: [], + scheduled_task_id: createdAlert.scheduled_task_id, + created_at: response.body.created_at, + updated_at: response.body.updated_at, + execution_status: response.body.execution_status, + }); + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan( + Date.parse(response.body.created_at) + ); + // Ensure AAD isn't broken + await checkAAD({ + supertest, + spaceId: space.id, + type: 'alert', + id: createdAlert.id, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle update alert request appropriately when consumer is "alerts"', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.restricted-noop', + consumer: 'alerts', + }) + ) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const updatedData = { + name: 'bcd', + tags: ['bar'], + params: { + foo: true, + }, + schedule: { interval: '12s' }, + actions: [], + throttle: '1m', + notify_when: 'onThrottleInterval', + }; + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send(updatedData); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'update', + 'test.restricted-noop', + 'alerts' + ), + statusCode: 403, + }); + break; + case 'global_read at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getProducerUnauthorizedErrorMessage( + 'update', + 'test.restricted-noop', + 'alertsRestrictedFixture' + ), + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body).to.eql({ + ...updatedData, + id: createdAlert.id, + rule_type_id: 'test.restricted-noop', + consumer: 'alerts', + created_by: 'elastic', + enabled: true, + updated_by: user.username, + api_key_owner: user.username, + mute_all: false, + muted_alert_ids: [], + scheduled_task_id: createdAlert.scheduled_task_id, + created_at: response.body.created_at, + updated_at: response.body.updated_at, + execution_status: response.body.execution_status, + }); + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan( + Date.parse(response.body.created_at) + ); + // Ensure AAD isn't broken + await checkAAD({ + supertest, + spaceId: space.id, + type: 'alert', + id: createdAlert.id, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should still be able to update when AAD is broken', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + await retry.try(async () => { + await supertest + .put( + `${getUrlPrefix(space.id)}/api/alerts_fixture/saved_object/alert/${createdAlert.id}` + ) + .set('kbn-xsrf', 'foo') + .send({ + attributes: { + name: 'bar', + }, + }) + .expect(200); + }); + + const updatedData = { + name: 'bcd', + tags: ['bar'], + params: { + foo: true, + }, + schedule: { interval: '12s' }, + actions: [], + throttle: '1m', + notify_when: 'onThrottleInterval', + }; + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send(updatedData); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'update', + 'test.noop', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body).to.eql({ + ...updatedData, + id: createdAlert.id, + rule_type_id: 'test.noop', + consumer: 'alertsFixture', + created_by: 'elastic', + enabled: true, + updated_by: user.username, + api_key_owner: user.username, + mute_all: false, + muted_alert_ids: [], + scheduled_task_id: createdAlert.scheduled_task_id, + created_at: response.body.created_at, + updated_at: response.body.updated_at, + execution_status: response.body.execution_status, + }); + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan( + Date.parse(response.body.created_at) + ); + // Ensure AAD isn't broken + await checkAAD({ + supertest, + spaceId: space.id, + type: 'alert', + id: createdAlert.id, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle update alert request appropriately when alert name has leading and trailing whitespaces', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const updatedData = { + name: ' leading and trailing whitespace ', + tags: ['bar'], + params: { + foo: true, + }, + schedule: { interval: '12s' }, + actions: [], + throttle: '1m', + notify_when: 'onActiveAlert', + }; + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send(updatedData); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'update', + 'test.noop', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body.name).to.eql(' leading and trailing whitespace '); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`shouldn't update alert from another space`, async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .put(`${getUrlPrefix('other')}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + name: 'bcd', + tags: ['bar'], + params: { + foo: true, + }, + schedule: { interval: '12s' }, + throttle: '1m', + actions: [], + notify_when: 'onActiveAlert', + }); + + expect(response.statusCode).to.eql(404); + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + case 'superuser at space1': + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: `Saved object [alert/${createdAlert.id}] not found`, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle update alert request appropriately when attempting to change alert type', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + name: 'bcd', + tags: ['bar'], + throttle: '1m', + rule_type_id: '1', + params: { + foo: true, + }, + schedule: { interval: '12s' }, + actions: [], + notify_when: 'onActiveAlert', + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(400); + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: '[request body.rule_type_id]: definition for this key is missing', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle update alert request appropriately when payload is empty and invalid', async () => { + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/1`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({}); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(400); + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: '[request body.name]: expected value of type [string] but got [undefined]', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`should handle update alert request appropriately when alertTypeConfig isn't valid`, async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.validation', + params: { + param1: 'test', + }, + }) + ) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({ + name: 'bcd', + tags: ['bar'], + schedule: { interval: '1m' }, + throttle: '1m', + params: {}, + actions: [], + notify_when: 'onActiveAlert', + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'update', + 'test.validation', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(400); + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'params invalid: [param1]: expected value of type [string] but got [undefined]', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle update alert request appropriately when interval schedule is wrong syntax', async () => { + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/1`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send( + getTestRuleData({ + schedule: { interval: '10x' }, + enabled: undefined, + consumer: undefined, + }) + ); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(400); + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: '[request body.schedule.interval]: string is not a valid duration: 10x', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle updates to an alert schedule by rescheduling the underlying task', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + schedule: { interval: '30m' }, + }) + ) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + await retry.try(async () => { + const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)).docs[0]; + expect(alertTask.status).to.eql('idle'); + // ensure the alert inital run has completed and it's been rescheduled to half an hour from now + ensureDatetimeIsWithinRange(Date.parse(alertTask.runAt), 30 * 60 * 1000); + }); + + const updatedData = { + name: 'bcd', + tags: ['bar'], + params: { + foo: true, + }, + schedule: { interval: '1m' }, + actions: [], + throttle: '1m', + notify_when: 'onThrottleInterval', + }; + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send(updatedData); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'update', + 'test.noop', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + await retry.try(async () => { + const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)) + .docs[0]; + expect(alertTask.status).to.eql('idle'); + // ensure the alert is rescheduled to a minute from now + ensureDatetimeIsWithinRange(Date.parse(alertTask.runAt), 60 * 1000); + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle updates for a long running alert type without failing the underlying tasks due to invalidated ApiKey', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send({ + enabled: true, + name: 'abc', + tags: ['foo'], + rule_type_id: 'test.longRunning', + consumer: 'alertsFixture', + schedule: { interval: '1s' }, + throttle: '1m', + actions: [], + params: {}, + notify_when: 'onThrottleInterval', + }) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + const updatedData = { + name: 'bcd', + tags: ['bar'], + params: { + foo: true, + }, + schedule: { interval: '1m' }, + actions: [], + throttle: '1m', + notify_when: 'onThrottleInterval', + }; + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send(updatedData); + + const statusUpdates: string[] = []; + await retry.try(async () => { + const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)).docs[0]; + statusUpdates.push(alertTask.status); + expect(alertTask.status).to.eql('idle'); + }); + + expect(statusUpdates.find((status) => status === 'failed')).to.be(undefined); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'update', + 'test.longRunning', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + await retry.try(async () => { + const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)) + .docs[0]; + expect(alertTask.status).to.eql('idle'); + // ensure the alert is rescheduled to a minute from now + ensureDatetimeIsWithinRange(Date.parse(alertTask.runAt), 60 * 1000); + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it('should handle updates to an alert schedule by setting the new schedule for the underlying task', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + schedule: { interval: '1m' }, + }) + ) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); + + await retry.try(async () => { + const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)).docs[0]; + expect(alertTask.status).to.eql('idle'); + expect(alertTask.schedule).to.eql({ interval: '1m' }); + }); + + const updatedData = { + name: 'bcd', + tags: ['bar'], + params: { + foo: true, + }, + schedule: { interval: '1s' }, + actions: [], + throttle: '1m', + notify_when: 'onThrottleInterval', + }; + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send(updatedData); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'update', + 'test.noop', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + await retry.try(async () => { + const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)) + .docs[0]; + expect(alertTask.status).to.eql('idle'); + expect(alertTask.schedule).to.eql({ interval: '1s' }); + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + }); + } + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update_api_key.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/update_api_key.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update_api_key.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/update_api_key.ts index 1c25ec550c41e..a0d1eb4dd0756 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update_api_key.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/update_api_key.ts @@ -6,8 +6,8 @@ */ import expect from '@kbn/expect'; -import { UserAtSpaceScenarios } from '../../scenarios'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { AlertUtils, checkAAD, @@ -16,7 +16,7 @@ import { ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, -} from '../../../common/lib'; +} from '../../../../common/lib'; // eslint-disable-next-line import/no-default-export export default function createUpdateApiKeyTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/index.ts new file mode 100644 index 0000000000000..c4b5ab80c3416 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function alertingApiIntegrationTests({ loadTestFile }: FtrProviderContext) { + describe('alerting api integration security and spaces enabled - Group 2', function () { + loadTestFile(require.resolve('./telemetry')); + loadTestFile(require.resolve('./actions')); + loadTestFile(require.resolve('./alerting')); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/actions_telemetry.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/actions_telemetry.ts similarity index 98% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/actions_telemetry.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/actions_telemetry.ts index 1d8d0091b67de..b187b9e9f9759 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/actions_telemetry.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/actions_telemetry.ts @@ -6,15 +6,15 @@ */ import expect from '@kbn/expect'; -import { Spaces, Superuser } from '../../scenarios'; +import { Spaces, Superuser } from '../../../scenarios'; import { getUrlPrefix, getEventLog, getTestRuleData, ObjectRemover, TaskManagerDoc, -} from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +} from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function createActionsTelemetryTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/alerting_telemetry.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/alerting_telemetry.ts similarity index 99% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/alerting_telemetry.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/alerting_telemetry.ts index afc39bf1c6b74..811eeecfc0375 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/alerting_telemetry.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/alerting_telemetry.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { Spaces, Superuser } from '../../scenarios'; +import { Spaces, Superuser } from '../../../scenarios'; import { getUrlPrefix, getEventLog, @@ -14,8 +14,8 @@ import { ObjectRemover, TaskManagerDoc, ESTestIndexTool, -} from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +} from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function createAlertingTelemetryTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/config.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/config.ts new file mode 100644 index 0000000000000..506d0016af4f6 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/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 { createTestConfig } from '../../../../common/config'; + +// eslint-disable-next-line import/no-default-export +export default createTestConfig('security_and_spaces', { + disabledPlugins: [], + license: 'trial', + ssl: true, + enableActionsProxy: true, + publicBaseUrl: true, + testFiles: [require.resolve('.')], +}); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/index.ts new file mode 100644 index 0000000000000..6077997b661ae --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/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 { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { setupSpacesAndUsers, tearDown } from '../../../setup'; + +// eslint-disable-next-line import/no-default-export +export default function actionsTests({ loadTestFile, getService }: FtrProviderContext) { + describe('Alerting and Actions Telemetry', () => { + before(async () => { + await setupSpacesAndUsers(getService); + }); + + after(async () => { + await tearDown(getService); + }); + + // run telemetry tests before anything else + loadTestFile(require.resolve('./actions_telemetry')); + loadTestFile(require.resolve('./alerting_telemetry')); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/setup.ts b/x-pack/test/alerting_api_integration/security_and_spaces/setup.ts new file mode 100644 index 0000000000000..69ca58e1edc12 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/setup.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { isCustomRoleSpecification } from '../common/types'; +import { Spaces, Users } from './scenarios'; + +export async function setupSpacesAndUsers(getService: FtrProviderContext['getService']) { + const securityService = getService('security'); + const spacesService = getService('spaces'); + + for (const space of Spaces) { + await spacesService.create(space); + } + + for (const user of Users) { + const roles = [...(user.role ? [user.role] : []), ...(user.roles ? user.roles : [])]; + + await securityService.user.create(user.username, { + password: user.password, + full_name: user.fullName, + roles: roles.map((role) => role.name), + }); + + for (const role of roles) { + if (isCustomRoleSpecification(role)) { + await securityService.role.create(role.name, { + kibana: role.kibana, + elasticsearch: role.elasticsearch, + }); + } + } + } +} + +export async function tearDown(getService: FtrProviderContext['getService']) { + const securityService = getService('security'); + const esArchiver = getService('esArchiver'); + + for (const user of Users) { + await securityService.user.delete(user.username); + + const roles = [...(user.role ? [user.role] : []), ...(user.roles ? user.roles : [])]; + for (const role of roles) { + if (isCustomRoleSpecification(role)) { + await securityService.role.delete(role.name); + } + } + } + + await esArchiver.unload('x-pack/test/functional/es_archives/empty_kibana'); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itom.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itom.ts deleted file mode 100644 index 9dcc3ef05266e..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itom.ts +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import httpProxy from 'http-proxy'; -import expect from '@kbn/expect'; -import getPort from 'get-port'; -import http from 'http'; - -import { getHttpProxyServer } from '../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import { getServiceNowServer } from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; - -// eslint-disable-next-line import/no-default-export -export default function serviceNowITOMTest({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const configService = getService('config'); - - const mockServiceNow = { - config: { - apiUrl: 'www.servicenowisinkibanaactions.com', - }, - secrets: { - password: 'elastic', - username: 'changeme', - }, - params: { - subAction: 'addEvent', - subActionParams: { - source: 'A source', - event_class: 'An event class', - resource: 'C:', - node: 'node.example.com', - metric_name: 'Percentage Logical Disk Free Space', - type: 'Disk space', - severity: '4', - description: 'desc', - additional_info: '{"alert": "test"}', - message_key: 'a key', - time_of_event: '2021-10-13T10:51:44.981Z', - }, - }, - }; - - describe('ServiceNow ITOM', () => { - let simulatedActionId = ''; - let serviceNowSimulatorURL: string = ''; - let serviceNowServer: http.Server; - let proxyServer: httpProxy | undefined; - let proxyHaveBeenCalled = false; - - before(async () => { - serviceNowServer = await getServiceNowServer(); - const availablePort = await getPort({ port: getPort.makeRange(9000, 9100) }); - if (!serviceNowServer.listening) { - serviceNowServer.listen(availablePort); - } - serviceNowSimulatorURL = `http://localhost:${availablePort}`; - proxyServer = await getHttpProxyServer( - serviceNowSimulatorURL, - configService.get('kbnTestServer.serverArgs'), - () => { - proxyHaveBeenCalled = true; - } - ); - }); - - after(() => { - serviceNowServer.close(); - if (proxyServer) { - proxyServer.close(); - } - }); - - describe('ServiceNow ITOM - Action Creation', () => { - it('should return 200 when creating a servicenow action successfully', async () => { - const { body: createdAction } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - connector_type_id: '.servicenow-itom', - config: { - apiUrl: serviceNowSimulatorURL, - }, - secrets: mockServiceNow.secrets, - }) - .expect(200); - - expect(createdAction).to.eql({ - id: createdAction.id, - is_preconfigured: false, - is_deprecated: false, - name: 'A servicenow action', - connector_type_id: '.servicenow-itom', - is_missing_secrets: false, - config: { - apiUrl: serviceNowSimulatorURL, - }, - }); - - const { body: fetchedAction } = await supertest - .get(`/api/actions/connector/${createdAction.id}`) - .expect(200); - - expect(fetchedAction).to.eql({ - id: fetchedAction.id, - is_preconfigured: false, - is_deprecated: false, - name: 'A servicenow action', - connector_type_id: '.servicenow-itom', - is_missing_secrets: false, - config: { - apiUrl: serviceNowSimulatorURL, - }, - }); - }); - - it('should respond with a 400 Bad Request when creating a servicenow action with no apiUrl', async () => { - await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - connector_type_id: '.servicenow-itom', - config: {}, - }) - .expect(400) - .then((resp: any) => { - expect(resp.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type config: [apiUrl]: expected value of type [string] but got [undefined]', - }); - }); - }); - - it('should respond with a 400 Bad Request when creating a servicenow action with a not present in allowedHosts apiUrl', async () => { - await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - connector_type_id: '.servicenow-itom', - config: { - apiUrl: 'http://servicenow.mynonexistent.com', - }, - secrets: mockServiceNow.secrets, - }) - .expect(400) - .then((resp: any) => { - expect(resp.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type config: error configuring connector action: target url "http://servicenow.mynonexistent.com" is not added to the Kibana config xpack.actions.allowedHosts', - }); - }); - }); - - it('should respond with a 400 Bad Request when creating a servicenow action without secrets', async () => { - await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - connector_type_id: '.servicenow-itom', - config: { - apiUrl: serviceNowSimulatorURL, - }, - }) - .expect(400) - .then((resp: any) => { - expect(resp.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type secrets: [password]: expected value of type [string] but got [undefined]', - }); - }); - }); - }); - - describe('ServiceNow ITOM - Executor', () => { - before(async () => { - const { body } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow simulator', - connector_type_id: '.servicenow-itom', - config: { - apiUrl: serviceNowSimulatorURL, - }, - secrets: mockServiceNow.secrets, - }); - simulatedActionId = body.id; - }); - - describe('Validation', () => { - it('should handle failing with a simulated success without action', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: {}, - }) - .then((resp: any) => { - expect(Object.keys(resp.body)).to.eql(['status', 'message', 'retry', 'connector_id']); - expect(resp.body.connector_id).to.eql(simulatedActionId); - expect(resp.body.status).to.eql('error'); - }); - }); - - it('should handle failing with a simulated success without unsupported action', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { subAction: 'non-supported' }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - connector_id: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [addEvent]\n- [1.subAction]: expected value to equal [getChoices]', - }); - }); - }); - - it('should handle failing with a simulated success without subActionParams', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { subAction: 'pushToService' }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - connector_id: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [addEvent]\n- [1.subAction]: expected value to equal [getChoices]', - }); - }); - }); - - describe('getChoices', () => { - it('should fail when field is not provided', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - subAction: 'getChoices', - subActionParams: {}, - }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - connector_id: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [addEvent]\n- [1.subActionParams.fields]: expected value of type [array] but got [undefined]', - }); - }); - }); - }); - }); - - describe('Execution', () => { - // New connectors - describe('Add event', () => { - it('should add an event ', async () => { - const { body: result } = await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: mockServiceNow.params, - }) - .expect(200); - expect(result.status).to.eql('ok'); - }); - }); - - describe('getChoices', () => { - it('should get choices', async () => { - const { body: result } = await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - subAction: 'getChoices', - subActionParams: { fields: ['priority'] }, - }, - }) - .expect(200); - - expect(proxyHaveBeenCalled).to.equal(true); - expect(result).to.eql({ - status: 'ok', - connector_id: simulatedActionId, - data: [ - { - dependent_value: '', - label: '1 - Critical', - value: '1', - }, - { - dependent_value: '', - label: '2 - High', - value: '2', - }, - { - dependent_value: '', - label: '3 - Moderate', - value: '3', - }, - { - dependent_value: '', - label: '4 - Low', - value: '4', - }, - { - dependent_value: '', - label: '5 - Planning', - value: '5', - }, - ], - }); - }); - }); - }); - }); - }); -} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itsm.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itsm.ts deleted file mode 100644 index 4cc65d7103a58..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itsm.ts +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import httpProxy from 'http-proxy'; -import expect from '@kbn/expect'; -import getPort from 'get-port'; -import http from 'http'; - -import { getHttpProxyServer } from '../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import { getServiceNowServer } from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; - -// eslint-disable-next-line import/no-default-export -export default function serviceNowITSMTest({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const configService = getService('config'); - - const mockServiceNow = { - config: { - apiUrl: 'www.servicenowisinkibanaactions.com', - usesTableApi: false, - }, - secrets: { - password: 'elastic', - username: 'changeme', - }, - params: { - subAction: 'pushToService', - subActionParams: { - incident: { - description: 'a description', - externalId: null, - impact: '1', - severity: '1', - short_description: 'a title', - urgency: '1', - category: 'software', - subcategory: 'os', - }, - comments: [ - { - comment: 'first comment', - commentId: '456', - }, - ], - }, - }, - }; - - describe('ServiceNow ITSM', () => { - let simulatedActionId = ''; - let serviceNowSimulatorURL: string = ''; - let serviceNowServer: http.Server; - let proxyServer: httpProxy | undefined; - let proxyHaveBeenCalled = false; - - before(async () => { - serviceNowServer = await getServiceNowServer(); - const availablePort = await getPort({ port: getPort.makeRange(9000, 9100) }); - if (!serviceNowServer.listening) { - serviceNowServer.listen(availablePort); - } - serviceNowSimulatorURL = `http://localhost:${availablePort}`; - proxyServer = await getHttpProxyServer( - serviceNowSimulatorURL, - configService.get('kbnTestServer.serverArgs'), - () => { - proxyHaveBeenCalled = true; - } - ); - }); - - after(() => { - serviceNowServer.close(); - if (proxyServer) { - proxyServer.close(); - } - }); - - describe('ServiceNow ITSM - Action Creation', () => { - it('should return 200 when creating a servicenow action successfully', async () => { - const { body: createdAction } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - connector_type_id: '.servicenow', - config: { - apiUrl: serviceNowSimulatorURL, - usesTableApi: false, - }, - secrets: mockServiceNow.secrets, - }) - .expect(200); - - expect(createdAction).to.eql({ - id: createdAction.id, - is_preconfigured: false, - is_deprecated: false, - name: 'A servicenow action', - connector_type_id: '.servicenow', - is_missing_secrets: false, - config: { - apiUrl: serviceNowSimulatorURL, - usesTableApi: false, - }, - }); - - const { body: fetchedAction } = await supertest - .get(`/api/actions/connector/${createdAction.id}`) - .expect(200); - - expect(fetchedAction).to.eql({ - id: fetchedAction.id, - is_preconfigured: false, - is_deprecated: false, - name: 'A servicenow action', - connector_type_id: '.servicenow', - is_missing_secrets: false, - config: { - apiUrl: serviceNowSimulatorURL, - usesTableApi: false, - }, - }); - }); - - it('should set the usesTableApi to true when not provided', async () => { - const { body: createdAction } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - connector_type_id: '.servicenow', - config: { - apiUrl: serviceNowSimulatorURL, - }, - secrets: mockServiceNow.secrets, - }) - .expect(200); - - const { body: fetchedAction } = await supertest - .get(`/api/actions/connector/${createdAction.id}`) - .expect(200); - - expect(fetchedAction.config.usesTableApi).to.be(true); - }); - - it('should respond with a 400 Bad Request when creating a servicenow action with no apiUrl', async () => { - await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - connector_type_id: '.servicenow', - config: {}, - }) - .expect(400) - .then((resp: any) => { - expect(resp.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type config: [apiUrl]: expected value of type [string] but got [undefined]', - }); - }); - }); - - it('should respond with a 400 Bad Request when creating a servicenow action with a not present in allowedHosts apiUrl', async () => { - await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - connector_type_id: '.servicenow', - config: { - apiUrl: 'http://servicenow.mynonexistent.com', - }, - secrets: mockServiceNow.secrets, - }) - .expect(400) - .then((resp: any) => { - expect(resp.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type config: error configuring connector action: target url "http://servicenow.mynonexistent.com" is not added to the Kibana config xpack.actions.allowedHosts', - }); - }); - }); - - it('should respond with a 400 Bad Request when creating a servicenow action without secrets', async () => { - await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - connector_type_id: '.servicenow', - config: { - apiUrl: serviceNowSimulatorURL, - }, - }) - .expect(400) - .then((resp: any) => { - expect(resp.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type secrets: [password]: expected value of type [string] but got [undefined]', - }); - }); - }); - }); - - describe('ServiceNow ITSM - Executor', () => { - before(async () => { - const { body } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow simulator', - connector_type_id: '.servicenow', - config: { - apiUrl: serviceNowSimulatorURL, - usesTableApi: false, - }, - secrets: mockServiceNow.secrets, - }); - simulatedActionId = body.id; - }); - - describe('Validation', () => { - it('should handle failing with a simulated success without action', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: {}, - }) - .then((resp: any) => { - expect(Object.keys(resp.body)).to.eql(['status', 'message', 'retry', 'connector_id']); - expect(resp.body.connector_id).to.eql(simulatedActionId); - expect(resp.body.status).to.eql('error'); - }); - }); - - it('should handle failing with a simulated success without unsupported action', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { subAction: 'non-supported' }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - connector_id: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subAction]: expected value to equal [pushToService]\n- [4.subAction]: expected value to equal [getChoices]', - }); - }); - }); - - it('should handle failing with a simulated success without subActionParams', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { subAction: 'pushToService' }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - connector_id: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.incident.short_description]: expected value of type [string] but got [undefined]\n- [4.subAction]: expected value to equal [getChoices]', - }); - }); - }); - - it('should handle failing with a simulated success without title', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - ...mockServiceNow.params, - subActionParams: { - savedObjectId: 'success', - }, - }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - connector_id: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.incident.short_description]: expected value of type [string] but got [undefined]\n- [4.subAction]: expected value to equal [getChoices]', - }); - }); - }); - - it('should handle failing with a simulated success without commentId', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - ...mockServiceNow.params, - subActionParams: { - incident: { - ...mockServiceNow.params.subActionParams.incident, - short_description: 'success', - }, - comments: [{ comment: 'boo' }], - }, - }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - connector_id: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.commentId]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [4.subAction]: expected value to equal [getChoices]', - }); - }); - }); - - it('should handle failing with a simulated success without comment message', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - ...mockServiceNow.params, - subActionParams: { - incident: { - ...mockServiceNow.params.subActionParams.incident, - short_description: 'success', - }, - comments: [{ commentId: 'success' }], - }, - }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - connector_id: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.comment]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [4.subAction]: expected value to equal [getChoices]', - }); - }); - }); - - describe('getChoices', () => { - it('should fail when field is not provided', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - subAction: 'getChoices', - subActionParams: {}, - }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - connector_id: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subAction]: expected value to equal [pushToService]\n- [4.subActionParams.fields]: expected value of type [array] but got [undefined]', - }); - }); - }); - }); - }); - - describe('Execution', () => { - // Connectors that use the Import set API - describe('Import set API', () => { - it('should handle creating an incident without comments', async () => { - const { body: result } = await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - ...mockServiceNow.params, - subActionParams: { - incident: mockServiceNow.params.subActionParams.incident, - comments: [], - }, - }, - }) - .expect(200); - - expect(proxyHaveBeenCalled).to.equal(true); - expect(result).to.eql({ - status: 'ok', - connector_id: simulatedActionId, - data: { - id: '123', - title: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: `${serviceNowSimulatorURL}/nav_to.do?uri=incident.do?sys_id=123`, - }, - }); - }); - }); - - // Connectors that use the Table API - describe('Table API', () => { - before(async () => { - const { body } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow simulator', - connector_type_id: '.servicenow', - config: { - apiUrl: serviceNowSimulatorURL, - usesTableApi: true, - }, - secrets: mockServiceNow.secrets, - }); - simulatedActionId = body.id; - }); - - it('should handle creating an incident without comments', async () => { - const { body: result } = await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - ...mockServiceNow.params, - subActionParams: { - incident: mockServiceNow.params.subActionParams.incident, - comments: [], - }, - }, - }) - .expect(200); - - expect(proxyHaveBeenCalled).to.equal(true); - expect(result).to.eql({ - status: 'ok', - connector_id: simulatedActionId, - data: { - id: '123', - title: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: `${serviceNowSimulatorURL}/nav_to.do?uri=incident.do?sys_id=123`, - }, - }); - }); - }); - - describe('getChoices', () => { - it('should get choices', async () => { - const { body: result } = await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - subAction: 'getChoices', - subActionParams: { fields: ['priority'] }, - }, - }) - .expect(200); - - expect(proxyHaveBeenCalled).to.equal(true); - expect(result).to.eql({ - status: 'ok', - connector_id: simulatedActionId, - data: [ - { - dependent_value: '', - label: '1 - Critical', - value: '1', - }, - { - dependent_value: '', - label: '2 - High', - value: '2', - }, - { - dependent_value: '', - label: '3 - Moderate', - value: '3', - }, - { - dependent_value: '', - label: '4 - Low', - value: '4', - }, - { - dependent_value: '', - label: '5 - Planning', - value: '5', - }, - ], - }); - }); - }); - }); - }); - }); -} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_sir.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_sir.ts deleted file mode 100644 index 305bbef7cf70a..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_sir.ts +++ /dev/null @@ -1,520 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import httpProxy from 'http-proxy'; -import expect from '@kbn/expect'; -import getPort from 'get-port'; -import http from 'http'; - -import { getHttpProxyServer } from '../../../../common/lib/get_proxy_server'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import { getServiceNowServer } from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; - -// eslint-disable-next-line import/no-default-export -export default function serviceNowSIRTest({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const configService = getService('config'); - - const mockServiceNow = { - config: { - apiUrl: 'www.servicenowisinkibanaactions.com', - usesTableApi: false, - }, - secrets: { - password: 'elastic', - username: 'changeme', - }, - params: { - subAction: 'pushToService', - subActionParams: { - incident: { - externalId: null, - short_description: 'Incident title', - description: 'Incident description', - dest_ip: ['192.168.1.1', '192.168.1.3'], - source_ip: ['192.168.1.2', '192.168.1.4'], - malware_hash: ['5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9'], - malware_url: ['https://example.com'], - category: 'software', - subcategory: 'os', - correlation_id: 'alertID', - correlation_display: 'Alerting', - priority: '1', - }, - comments: [ - { - comment: 'first comment', - commentId: '456', - }, - ], - }, - }, - }; - - describe('ServiceNow SIR', () => { - let simulatedActionId = ''; - let serviceNowSimulatorURL: string = ''; - let serviceNowServer: http.Server; - let proxyServer: httpProxy | undefined; - let proxyHaveBeenCalled = false; - - before(async () => { - serviceNowServer = await getServiceNowServer(); - const availablePort = await getPort({ port: getPort.makeRange(9000, 9100) }); - if (!serviceNowServer.listening) { - serviceNowServer.listen(availablePort); - } - serviceNowSimulatorURL = `http://localhost:${availablePort}`; - proxyServer = await getHttpProxyServer( - serviceNowSimulatorURL, - configService.get('kbnTestServer.serverArgs'), - () => { - proxyHaveBeenCalled = true; - } - ); - }); - - after(() => { - serviceNowServer.close(); - if (proxyServer) { - proxyServer.close(); - } - }); - - describe('ServiceNow SIR - Action Creation', () => { - it('should return 200 when creating a servicenow action successfully', async () => { - const { body: createdAction } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - connector_type_id: '.servicenow-sir', - config: { - apiUrl: serviceNowSimulatorURL, - usesTableApi: false, - }, - secrets: mockServiceNow.secrets, - }) - .expect(200); - - expect(createdAction).to.eql({ - id: createdAction.id, - is_preconfigured: false, - is_deprecated: false, - name: 'A servicenow action', - connector_type_id: '.servicenow-sir', - is_missing_secrets: false, - config: { - apiUrl: serviceNowSimulatorURL, - usesTableApi: false, - }, - }); - - const { body: fetchedAction } = await supertest - .get(`/api/actions/connector/${createdAction.id}`) - .expect(200); - - expect(fetchedAction).to.eql({ - id: fetchedAction.id, - is_preconfigured: false, - is_deprecated: false, - name: 'A servicenow action', - connector_type_id: '.servicenow-sir', - is_missing_secrets: false, - config: { - apiUrl: serviceNowSimulatorURL, - usesTableApi: false, - }, - }); - }); - - it('should set the usesTableApi to true when not provided', async () => { - const { body: createdAction } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - connector_type_id: '.servicenow-sir', - config: { - apiUrl: serviceNowSimulatorURL, - }, - secrets: mockServiceNow.secrets, - }) - .expect(200); - - const { body: fetchedAction } = await supertest - .get(`/api/actions/connector/${createdAction.id}`) - .expect(200); - - expect(fetchedAction.config.usesTableApi).to.be(true); - }); - - it('should respond with a 400 Bad Request when creating a servicenow action with no apiUrl', async () => { - await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - connector_type_id: '.servicenow-sir', - config: {}, - }) - .expect(400) - .then((resp: any) => { - expect(resp.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type config: [apiUrl]: expected value of type [string] but got [undefined]', - }); - }); - }); - - it('should respond with a 400 Bad Request when creating a servicenow action with a not present in allowedHosts apiUrl', async () => { - await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - connector_type_id: '.servicenow-sir', - config: { - apiUrl: 'http://servicenow.mynonexistent.com', - }, - secrets: mockServiceNow.secrets, - }) - .expect(400) - .then((resp: any) => { - expect(resp.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type config: error configuring connector action: target url "http://servicenow.mynonexistent.com" is not added to the Kibana config xpack.actions.allowedHosts', - }); - }); - }); - - it('should respond with a 400 Bad Request when creating a servicenow action without secrets', async () => { - await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - connector_type_id: '.servicenow-sir', - config: { - apiUrl: serviceNowSimulatorURL, - }, - }) - .expect(400) - .then((resp: any) => { - expect(resp.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type secrets: [password]: expected value of type [string] but got [undefined]', - }); - }); - }); - }); - - describe('ServiceNow SIR - Executor', () => { - before(async () => { - const { body } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow simulator', - connector_type_id: '.servicenow-sir', - config: { - apiUrl: serviceNowSimulatorURL, - usesTableApi: false, - }, - secrets: mockServiceNow.secrets, - }); - simulatedActionId = body.id; - }); - - describe('Validation', () => { - it('should handle failing with a simulated success without action', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: {}, - }) - .then((resp: any) => { - expect(Object.keys(resp.body)).to.eql(['status', 'message', 'retry', 'connector_id']); - expect(resp.body.connector_id).to.eql(simulatedActionId); - expect(resp.body.status).to.eql('error'); - }); - }); - - it('should handle failing with a simulated success without unsupported action', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { subAction: 'non-supported' }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - connector_id: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subAction]: expected value to equal [pushToService]\n- [4.subAction]: expected value to equal [getChoices]', - }); - }); - }); - - it('should handle failing with a simulated success without subActionParams', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { subAction: 'pushToService' }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - connector_id: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.incident.short_description]: expected value of type [string] but got [undefined]\n- [4.subAction]: expected value to equal [getChoices]', - }); - }); - }); - - it('should handle failing with a simulated success without title', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - ...mockServiceNow.params, - subActionParams: { - savedObjectId: 'success', - }, - }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - connector_id: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.incident.short_description]: expected value of type [string] but got [undefined]\n- [4.subAction]: expected value to equal [getChoices]', - }); - }); - }); - - it('should handle failing with a simulated success without commentId', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - ...mockServiceNow.params, - subActionParams: { - incident: { - ...mockServiceNow.params.subActionParams.incident, - short_description: 'success', - }, - comments: [{ comment: 'boo' }], - }, - }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - connector_id: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.commentId]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [4.subAction]: expected value to equal [getChoices]', - }); - }); - }); - - it('should handle failing with a simulated success without comment message', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - ...mockServiceNow.params, - subActionParams: { - incident: { - ...mockServiceNow.params.subActionParams.incident, - short_description: 'success', - }, - comments: [{ commentId: 'success' }], - }, - }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - connector_id: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.comment]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [4.subAction]: expected value to equal [getChoices]', - }); - }); - }); - - describe('getChoices', () => { - it('should fail when field is not provided', async () => { - await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - subAction: 'getChoices', - subActionParams: {}, - }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - connector_id: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getFields]\n- [1.subAction]: expected value to equal [getIncident]\n- [2.subAction]: expected value to equal [handshake]\n- [3.subAction]: expected value to equal [pushToService]\n- [4.subActionParams.fields]: expected value of type [array] but got [undefined]', - }); - }); - }); - }); - }); - - describe('Execution', () => { - // Connectors that use the Import set API - describe('Import set API', () => { - it('should handle creating an incident without comments', async () => { - const { body: result } = await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - ...mockServiceNow.params, - subActionParams: { - incident: mockServiceNow.params.subActionParams.incident, - comments: [], - }, - }, - }) - .expect(200); - - expect(proxyHaveBeenCalled).to.equal(true); - expect(result).to.eql({ - status: 'ok', - connector_id: simulatedActionId, - data: { - id: '123', - title: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: `${serviceNowSimulatorURL}/nav_to.do?uri=sn_si_incident.do?sys_id=123`, - }, - }); - }); - }); - - // Connectors that use the Table API - describe('Table API', () => { - before(async () => { - const { body } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow simulator', - connector_type_id: '.servicenow-sir', - config: { - apiUrl: serviceNowSimulatorURL, - usesTableApi: true, - }, - secrets: mockServiceNow.secrets, - }); - simulatedActionId = body.id; - }); - - it('should handle creating an incident without comments', async () => { - const { body: result } = await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - ...mockServiceNow.params, - subActionParams: { - incident: mockServiceNow.params.subActionParams.incident, - comments: [], - }, - }, - }) - .expect(200); - - expect(proxyHaveBeenCalled).to.equal(true); - expect(result).to.eql({ - status: 'ok', - connector_id: simulatedActionId, - data: { - id: '123', - title: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: `${serviceNowSimulatorURL}/nav_to.do?uri=sn_si_incident.do?sys_id=123`, - }, - }); - }); - }); - - describe('getChoices', () => { - it('should get choices', async () => { - const { body: result } = await supertest - .post(`/api/actions/connector/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - subAction: 'getChoices', - subActionParams: { fields: ['priority'] }, - }, - }) - .expect(200); - - expect(proxyHaveBeenCalled).to.equal(true); - expect(result).to.eql({ - status: 'ok', - connector_id: simulatedActionId, - data: [ - { - dependent_value: '', - label: '1 - Critical', - value: '1', - }, - { - dependent_value: '', - label: '2 - High', - value: '2', - }, - { - dependent_value: '', - label: '3 - Moderate', - value: '3', - }, - { - dependent_value: '', - label: '4 - Low', - value: '4', - }, - { - dependent_value: '', - label: '5 - Planning', - value: '5', - }, - ], - }); - }); - }); - }); - }); - }); -} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/create.ts deleted file mode 100644 index 06e8017177138..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/create.ts +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { UserAtSpaceScenarios } from '../../scenarios'; -import { checkAAD, getUrlPrefix, ObjectRemover } from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default function createActionTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - - describe('create', () => { - const objectRemover = new ObjectRemover(supertest); - - after(() => objectRemover.removeAll()); - - for (const scenario of UserAtSpaceScenarios) { - const { user, space } = scenario; - describe(scenario.id, () => { - it('should handle create action request appropriately', async () => { - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .auth(user.username, user.password) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action', - connector_type_id: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to create a "test.index-record" action', - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'action', 'actions'); - expect(response.body).to.eql({ - id: response.body.id, - is_preconfigured: false, - is_deprecated: false, - is_missing_secrets: false, - name: 'My action', - connector_type_id: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - }); - expect(typeof response.body.id).to.be('string'); - // Ensure AAD isn't broken - await checkAAD({ - supertest, - spaceId: space.id, - type: 'action', - id: response.body.id, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`should handle create action request appropriately when action type isn't registered`, async () => { - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send({ - name: 'My action', - connector_type_id: 'test.unregistered-action-type', - config: {}, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to create a "test.unregistered-action-type" action', - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(400); - expect(response.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: 'Action type "test.unregistered-action-type" is not registered.', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle create action request appropriately when payload is empty and invalid', async () => { - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send({}); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all at space2': - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(400); - expect(response.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: '[request body.name]: expected value of type [string] but got [undefined]', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`should handle create action request appropriately when config isn't valid`, async () => { - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send({ - name: 'my name', - connector_type_id: 'test.index-record', - config: { - unencrypted: 'my unencrypted text', - }, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to create a "test.index-record" action', - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(400); - expect(response.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type secrets: [encrypted]: expected value of type [string] but got [undefined]', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`should handle create action requests for action types that are not enabled`, async () => { - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send({ - name: 'my name', - connector_type_id: 'test.not-enabled', - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to create a "test.not-enabled" action', - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: - 'action type "test.not-enabled" is not enabled in the Kibana config xpack.actions.enabledActionTypes', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - }); - } - }); -} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/delete.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/delete.ts deleted file mode 100644 index a0aae0a1bd64d..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/delete.ts +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { UserAtSpaceScenarios } from '../../scenarios'; -import { getUrlPrefix, ObjectRemover } from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default function deleteActionTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - - describe('delete', () => { - const objectRemover = new ObjectRemover(supertest); - - after(() => objectRemover.removeAll()); - - for (const scenario of UserAtSpaceScenarios) { - const { user, space } = scenario; - describe(scenario.id, () => { - it('should handle delete action request appropriately', async () => { - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action', - connector_type_id: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }) - .expect(200); - - const response = await supertestWithoutAuth - .delete(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}`) - .auth(user.username, user.password) - .set('kbn-xsrf', 'foo'); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'global_read at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to delete actions', - }); - objectRemover.add(space.id, createdAction.id, 'action', 'actions'); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(204); - expect(response.body).to.eql(''); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`shouldn't delete action from another space`, async () => { - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action', - connector_type_id: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }) - .expect(200); - objectRemover.add(space.id, createdAction.id, 'action', 'actions'); - - const response = await supertestWithoutAuth - .delete(`${getUrlPrefix('other')}/api/actions/connector/${createdAction.id}`) - .auth(user.username, user.password) - .set('kbn-xsrf', 'foo'); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'global_read at space1': - case 'space_1_all at space2': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to delete actions', - }); - break; - case 'superuser at space1': - expect(response.statusCode).to.eql(404); - expect(response.body).to.eql({ - statusCode: 404, - error: 'Not Found', - message: `Saved object [action/${createdAction.id}] not found`, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`should handle delete request appropriately when action doesn't exist`, async () => { - const response = await supertestWithoutAuth - .delete(`${getUrlPrefix(space.id)}/api/actions/connector/2`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'global_read at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to delete actions', - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(404); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`shouldn't delete action from preconfigured list`, async () => { - const response = await supertestWithoutAuth - .delete(`${getUrlPrefix(space.id)}/api/actions/connector/my-slack1`) - .auth(user.username, user.password) - .set('kbn-xsrf', 'foo'); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'global_read at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to delete actions', - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: 'Preconfigured action my-slack1 is not allowed to delete.', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - }); - } - }); -} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get.ts deleted file mode 100644 index 9842b13a9745d..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get.ts +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { UserAtSpaceScenarios } from '../../scenarios'; -import { getUrlPrefix, ObjectRemover } from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default function getActionTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - - describe('get', () => { - const objectRemover = new ObjectRemover(supertest); - - afterEach(() => objectRemover.removeAll()); - - for (const scenario of UserAtSpaceScenarios) { - const { user, space } = scenario; - describe(scenario.id, () => { - it('should handle get action request appropriately', async () => { - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action', - connector_type_id: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }) - .expect(200); - objectRemover.add(space.id, createdAction.id, 'action', 'actions'); - - const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}`) - .auth(user.username, user.password); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to get actions', - }); - break; - case 'global_read at space1': - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - expect(response.body).to.eql({ - id: createdAction.id, - is_preconfigured: false, - connector_type_id: 'test.index-record', - is_deprecated: false, - is_missing_secrets: false, - name: 'My action', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`action shouldn't be acessible from another space`, async () => { - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action', - connector_type_id: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }) - .expect(200); - objectRemover.add(space.id, createdAction.id, 'action', 'actions'); - - const response = await supertestWithoutAuth - .get(`${getUrlPrefix('other')}/api/actions/connector/${createdAction.id}`) - .auth(user.username, user.password); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all at space2': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to get actions', - }); - break; - case 'global_read at space1': - case 'superuser at space1': - expect(response.statusCode).to.eql(404); - expect(response.body).to.eql({ - statusCode: 404, - error: 'Not Found', - message: `Saved object [action/${createdAction.id}] not found`, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle get preconfigured action request appropriately', async () => { - const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/actions/connector/my-slack1`) - .auth(user.username, user.password); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to get actions', - }); - break; - case 'global_read at space1': - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - expect(response.body).to.eql({ - id: 'my-slack1', - connector_type_id: '.slack', - name: 'Slack#xyz', - is_preconfigured: true, - is_deprecated: false, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - }); - } - }); -} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/index.ts deleted file mode 100644 index 93d4bbb4065ed..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { setupSpacesAndUsers, tearDown } from '..'; - -// eslint-disable-next-line import/no-default-export -export default function actionsTests({ loadTestFile, getService }: FtrProviderContext) { - describe('Actions', () => { - before(async () => { - await setupSpacesAndUsers(getService); - }); - - after(async () => { - await tearDown(getService); - }); - - loadTestFile(require.resolve('./builtin_action_types/email')); - loadTestFile(require.resolve('./builtin_action_types/es_index')); - loadTestFile(require.resolve('./builtin_action_types/es_index_preconfigured')); - loadTestFile(require.resolve('./builtin_action_types/pagerduty')); - loadTestFile(require.resolve('./builtin_action_types/swimlane')); - loadTestFile(require.resolve('./builtin_action_types/server_log')); - loadTestFile(require.resolve('./builtin_action_types/servicenow_itsm')); - loadTestFile(require.resolve('./builtin_action_types/servicenow_sir')); - loadTestFile(require.resolve('./builtin_action_types/servicenow_itom')); - loadTestFile(require.resolve('./builtin_action_types/jira')); - loadTestFile(require.resolve('./builtin_action_types/resilient')); - loadTestFile(require.resolve('./builtin_action_types/slack')); - loadTestFile(require.resolve('./builtin_action_types/webhook')); - loadTestFile(require.resolve('./builtin_action_types/xmatters')); - loadTestFile(require.resolve('./create')); - loadTestFile(require.resolve('./delete')); - loadTestFile(require.resolve('./execute')); - loadTestFile(require.resolve('./get_all')); - loadTestFile(require.resolve('./get')); - loadTestFile(require.resolve('./connector_types')); - loadTestFile(require.resolve('./update')); - }); -} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/update.ts deleted file mode 100644 index 92bb7084ec534..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/update.ts +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { UserAtSpaceScenarios } from '../../scenarios'; -import { checkAAD, getUrlPrefix, ObjectRemover } from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default function updateActionTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - - describe('update', () => { - const objectRemover = new ObjectRemover(supertest); - - after(() => objectRemover.removeAll()); - - for (const scenario of UserAtSpaceScenarios) { - const { user, space } = scenario; - describe(scenario.id, () => { - it('should handle update action request appropriately', async () => { - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action', - connector_type_id: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }) - .expect(200); - objectRemover.add(space.id, createdAction.id, 'action', 'actions'); - - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}`) - .auth(user.username, user.password) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action updated', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to update actions', - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - expect(response.body).to.eql({ - id: createdAction.id, - is_preconfigured: false, - is_deprecated: false, - connector_type_id: 'test.index-record', - is_missing_secrets: false, - name: 'My action updated', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - }); - // Ensure AAD isn't broken - await checkAAD({ - supertest, - spaceId: space.id, - type: 'action', - id: createdAction.id, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`shouldn't update action from another space`, async () => { - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action', - connector_type_id: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }) - .expect(200); - objectRemover.add(space.id, createdAction.id, 'action', 'actions'); - - const response = await supertestWithoutAuth - .put(`${getUrlPrefix('other')}/api/actions/connector/${createdAction.id}`) - .auth(user.username, user.password) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action updated', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all at space2': - case 'global_read at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to update actions', - }); - break; - case 'superuser at space1': - expect(response.statusCode).to.eql(404); - expect(response.body).to.eql({ - statusCode: 404, - error: 'Not Found', - message: `Saved object [action/${createdAction.id}] not found`, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle update action request appropriately when passing a null config', async () => { - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/actions/connector/1`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send({ - name: 'My action updated', - config: null, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all at space2': - case 'global_read at space1': - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(400); - expect(response.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: '[request body.config]: expected value of type [object] but got [null]', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`should handle update action request appropriately when action doesn't exist`, async () => { - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/actions/connector/1`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send({ - name: 'My action updated', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to update actions', - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(404); - expect(response.body).to.eql({ - statusCode: 404, - error: 'Not Found', - message: 'Saved object [action/1] not found', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle update action request appropriately when payload is empty and invalid', async () => { - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/actions/connector/1`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send({}); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all at space2': - case 'global_read at space1': - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(400); - expect(response.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: '[request body.name]: expected value of type [string] but got [undefined]', - // message: '[request body.config]: expected value of type [object] but got [null]', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle update action request appropriately when secrets are not valid', async () => { - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action', - connector_type_id: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }) - .expect(200); - objectRemover.add(space.id, createdAction.id, 'action', 'actions'); - - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/actions/connector/${createdAction.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send({ - name: 'My action updated', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 42, - }, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to update actions', - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(400); - expect(response.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type secrets: [encrypted]: expected value of type [string] but got [number]', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`shouldn't update action from preconfigured list`, async () => { - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/actions/connector/custom-system-abc-connector`) - .auth(user.username, user.password) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action updated', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: 'Unauthorized to update actions', - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: `Preconfigured action custom-system-abc-connector is not allowed to update.`, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - }); - } - }); -} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts deleted file mode 100644 index 601efa34341cb..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts +++ /dev/null @@ -1,1354 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { omit } from 'lodash'; -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { IValidatedEvent } from '@kbn/event-log-plugin/server'; -import { TaskRunning, TaskRunningStage } from '@kbn/task-manager-plugin/server/task_running'; -import { ConcreteTaskInstance } from '@kbn/task-manager-plugin/server'; -import { UserAtSpaceScenarios, Superuser } from '../../scenarios'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { - ESTestIndexTool, - ES_TEST_INDEX_NAME, - getUrlPrefix, - getTestRuleData, - ObjectRemover, - AlertUtils, - getConsumerUnauthorizedErrorMessage, - TaskManagerUtils, - getEventLog, -} from '../../../common/lib'; - -const NANOS_IN_MILLIS = 1000 * 1000; - -// eslint-disable-next-line import/no-default-export -export default function alertTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const es = getService('es'); - const retry = getService('retry'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - const esTestIndexTool = new ESTestIndexTool(es, retry); - const taskManagerUtils = new TaskManagerUtils(es, retry); - - describe('alerts', () => { - const authorizationIndex = '.kibana-test-authorization'; - const objectRemover = new ObjectRemover(supertest); - - before(async () => { - await esTestIndexTool.destroy(); - await esTestIndexTool.setup(); - await es.indices.create({ index: authorizationIndex }); - }); - afterEach(() => objectRemover.removeAll()); - after(async () => { - await esTestIndexTool.destroy(); - await es.indices.delete({ index: authorizationIndex }); - }); - - for (const scenario of UserAtSpaceScenarios) { - const { user, space } = scenario; - - describe(scenario.id, () => { - let alertUtils: AlertUtils; - let indexRecordActionId: string; - - before(async () => { - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action', - connector_type_id: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }) - .expect(200); - indexRecordActionId = createdAction.id; - alertUtils = new AlertUtils({ - user, - space, - supertestWithoutAuth, - indexRecordActionId, - objectRemover, - }); - }); - after(() => objectRemover.add(space.id, indexRecordActionId, 'connector', 'actions')); - - it('should schedule task, run alert and schedule actions when appropriate', async () => { - const testStart = new Date(); - const reference = alertUtils.generateReference(); - const response = await alertUtils.createAlwaysFiringAction({ reference }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.always-firing', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: `Unauthorized to get actions`, - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - - // Wait for the action to index a document before disabling the alert and waiting for tasks to finish - await esTestIndexTool.waitForDocs('action:test.index-record', reference); - - await taskManagerUtils.waitForAllTasksIdle(testStart); - - const alertId = response.body.id; - await alertUtils.disable(alertId); - await taskManagerUtils.waitForEmpty(testStart); - - // Ensure only 1 alert executed with proper params - const alertSearchResult = await esTestIndexTool.search( - 'alert:test.always-firing', - reference - ); - // @ts-expect-error doesnt handle total: number - expect(alertSearchResult.body.hits.total.value).to.eql(1); - const alertSearchResultWithoutDates = omit( - alertSearchResult.body.hits.hits[0]._source as object, - ['alertInfo.createdAt', 'alertInfo.updatedAt'] - ); - expect(alertSearchResultWithoutDates).to.eql({ - source: 'alert:test.always-firing', - reference, - state: {}, - params: { - index: ES_TEST_INDEX_NAME, - reference, - }, - alertInfo: { - alertId, - consumer: 'alertsFixture', - spaceId: space.id, - namespace: space.id, - name: 'abc', - enabled: true, - notifyWhen: 'onActiveAlert', - schedule: { - interval: '1m', - }, - tags: ['tag-A', 'tag-B'], - throttle: '1m', - createdBy: user.fullName, - updatedBy: user.fullName, - actions: response.body.actions.map((action: any) => { - /* eslint-disable @typescript-eslint/naming-convention */ - const { connector_type_id, group, id, params } = action; - return { - actionTypeId: connector_type_id, - group, - id, - params, - }; - }), - producer: 'alertsFixture', - ruleTypeId: 'test.always-firing', - ruleTypeName: 'Test: Always Firing', - }, - }); - // @ts-expect-error _source: unknown - expect(alertSearchResult.body.hits.hits[0]._source.alertInfo.createdAt).to.match( - /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/ - ); - - // @ts-expect-error _source: unknown - expect(alertSearchResult.body.hits.hits[0]._source.alertInfo.updatedAt).to.match( - /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/ - ); - - // Ensure only 1 action executed with proper params - const actionSearchResult = await esTestIndexTool.search( - 'action:test.index-record', - reference - ); - // @ts-expect-error doesnt handle total: number - expect(actionSearchResult.body.hits.total.value).to.eql(1); - expect(actionSearchResult.body.hits.hits[0]._source).to.eql({ - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - params: { - index: ES_TEST_INDEX_NAME, - reference, - message: ` -alertId: ${alertId}, -alertName: abc, -spaceId: ${space.id}, -tags: tag-A,tag-B, -alertInstanceId: 1, -alertActionGroup: default, -instanceContextValue: true, -instanceStateValue: true -`.trim(), - }, - reference, - source: 'action:test.index-record', - }); - - await taskManagerUtils.waitForActionTaskParamsToBeCleanedUp(testStart); - - await validateEventLog({ - spaceId: space.id, - alertId, - ruleTypeId: 'test.always-firing', - outcome: 'success', - message: `rule executed: test.always-firing:${alertId}: 'abc'`, - ruleObject: alertSearchResultWithoutDates, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should schedule task, run alert and schedule preconfigured actions when appropriate', async () => { - const testStart = new Date(); - const reference = alertUtils.generateReference(); - const response = await alertUtils.createAlwaysFiringAction({ - reference, - indexRecordActionId: 'preconfigured.test.index-record', - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.always-firing', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: `Unauthorized to get actions`, - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - - // Wait for the action to index a document before disabling the alert and waiting for tasks to finish - await esTestIndexTool.waitForDocs('action:test.index-record', reference); - - await taskManagerUtils.waitForAllTasksIdle(testStart); - - const alertId = response.body.id; - await alertUtils.disable(alertId); - await taskManagerUtils.waitForEmpty(testStart); - - // Ensure only 1 alert executed with proper params - const alertSearchResult = await esTestIndexTool.search( - 'alert:test.always-firing', - reference - ); - // @ts-expect-error doesnt handle total: number - expect(alertSearchResult.body.hits.total.value).to.eql(1); - const alertSearchResultWithoutDates = omit( - alertSearchResult.body.hits.hits[0]._source as object, - ['alertInfo.createdAt', 'alertInfo.updatedAt'] - ); - expect(alertSearchResultWithoutDates).to.eql({ - source: 'alert:test.always-firing', - reference, - state: {}, - params: { - index: ES_TEST_INDEX_NAME, - reference, - }, - alertInfo: { - alertId, - consumer: 'alertsFixture', - spaceId: space.id, - namespace: space.id, - name: 'abc', - enabled: true, - notifyWhen: 'onActiveAlert', - schedule: { - interval: '1m', - }, - tags: ['tag-A', 'tag-B'], - throttle: '1m', - createdBy: user.fullName, - updatedBy: user.fullName, - actions: response.body.actions.map((action: any) => { - /* eslint-disable @typescript-eslint/naming-convention */ - const { connector_type_id, group, id, params } = action; - return { - actionTypeId: connector_type_id, - group, - id, - params, - }; - }), - producer: 'alertsFixture', - ruleTypeId: 'test.always-firing', - ruleTypeName: 'Test: Always Firing', - }, - }); - - // @ts-expect-error _source: unknown - expect(alertSearchResult.body.hits.hits[0]._source.alertInfo.createdAt).to.match( - /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/ - ); - // @ts-expect-error _source: unknown - expect(alertSearchResult.body.hits.hits[0]._source.alertInfo.updatedAt).to.match( - /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/ - ); - // Ensure only 1 action executed with proper params - const actionSearchResult = await esTestIndexTool.search( - 'action:test.index-record', - reference - ); - // @ts-expect-error doesnt handle total: number - expect(actionSearchResult.body.hits.total.value).to.eql(1); - expect(actionSearchResult.body.hits.hits[0]._source).to.eql({ - config: { - unencrypted: 'ignored-but-required', - }, - secrets: { - encrypted: 'this-is-also-ignored-and-also-required', - }, - params: { - index: ES_TEST_INDEX_NAME, - reference, - message: ` -alertId: ${alertId}, -alertName: abc, -spaceId: ${space.id}, -tags: tag-A,tag-B, -alertInstanceId: 1, -alertActionGroup: default, -instanceContextValue: true, -instanceStateValue: true -`.trim(), - }, - reference, - source: 'action:test.index-record', - }); - - await taskManagerUtils.waitForActionTaskParamsToBeCleanedUp(testStart); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should pass updated alert params to executor', async () => { - const testStart = new Date(); - // create an alert - const reference = alertUtils.generateReference(); - const response = await alertUtils.createAlwaysFiringAction({ - reference, - overwrites: { throttle: null }, - }); - - // only need to test creation success paths - if (response.statusCode !== 200) return; - - // Wait for the action to index a document before disabling the alert and waiting for tasks to finish - await esTestIndexTool.waitForDocs('action:test.index-record', reference); - - // Avoid invalidating an API key while the alert is executing - await taskManagerUtils.waitForAllTasksIdle(testStart); - - // update the alert with super user - const alertId = response.body.id; - const reference2 = alertUtils.generateReference(); - const response2 = await alertUtils.updateAlwaysFiringAction({ - alertId, - actionId: indexRecordActionId, - user: Superuser, - reference: reference2, - overwrites: { - name: 'def', - tags: ['fee', 'fi', 'fo'], - // This will cause the task to re-run on update - schedule: { interval: '59s' }, - }, - }); - - expect(response2.statusCode).to.eql(200); - - // make sure alert info passed to executor is correct - await esTestIndexTool.waitForDocs('alert:test.always-firing', reference2); - - await taskManagerUtils.waitForAllTasksIdle(testStart); - - await alertUtils.disable(alertId); - const alertSearchResult = await esTestIndexTool.search( - 'alert:test.always-firing', - reference2 - ); - - // @ts-expect-error doesnt handle total: number - expect(alertSearchResult.body.hits.total.value).to.be.greaterThan(0); - const alertSearchResultInfoWithoutDates = omit( - // @ts-expect-error _source: unknown - alertSearchResult.body.hits.hits[0]._source.alertInfo, - ['createdAt', 'updatedAt'] - ); - expect(alertSearchResultInfoWithoutDates).to.eql({ - alertId, - consumer: 'alertsFixture', - spaceId: space.id, - namespace: space.id, - name: 'def', - enabled: true, - notifyWhen: 'onActiveAlert', - schedule: { - interval: '59s', - }, - tags: ['fee', 'fi', 'fo'], - throttle: '1m', - createdBy: user.fullName, - updatedBy: Superuser.fullName, - actions: response2.body.actions.map((action: any) => { - /* eslint-disable @typescript-eslint/naming-convention */ - const { connector_type_id, group, id, params } = action; - return { - actionTypeId: connector_type_id, - group, - id, - params, - }; - }), - producer: 'alertsFixture', - ruleTypeId: 'test.always-firing', - ruleTypeName: 'Test: Always Firing', - }); - - // @ts-expect-error _source: unknown - expect(alertSearchResult.body.hits.hits[0]._source.alertInfo.createdAt).to.match( - /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/ - ); - // @ts-expect-error _source: unknown - expect(alertSearchResult.body.hits.hits[0]._source.alertInfo.updatedAt).to.match( - /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/ - ); - }); - - it('should handle custom retry logic when appropriate', async () => { - const testStart = new Date().toISOString(); - // We have to provide the test.rate-limit the next runAt, for testing purposes - const retryDate = new Date(Date.now() + 60000); - - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'Test rate limit', - connector_type_id: 'test.rate-limit', - config: {}, - }) - .expect(200); - objectRemover.add(space.id, createdAction.id, 'connector', 'actions'); - - const reference = alertUtils.generateReference(); - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send( - getTestRuleData({ - rule_type_id: 'test.always-firing', - params: { - index: ES_TEST_INDEX_NAME, - reference: 'create-test-2', - }, - actions: [ - { - group: 'default', - id: createdAction.id, - params: { - reference, - index: ES_TEST_INDEX_NAME, - retryAt: retryDate.getTime(), - }, - }, - ], - }) - ); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.always-firing', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: `Unauthorized to get actions`, - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); - - // Wait for the task to be attempted once and idle - const scheduledActionTask: estypes.SearchHit< - TaskRunning - > = await retry.try(async () => { - const searchResult = await es.search< - TaskRunning - >({ - index: '.kibana_task_manager', - body: { - query: { - bool: { - must: [ - { - term: { - 'task.status': 'idle', - }, - }, - { - term: { - 'task.attempts': 1, - }, - }, - { - term: { - 'task.taskType': 'actions:test.rate-limit', - }, - }, - { - range: { - 'task.scheduledAt': { - gte: testStart, - }, - }, - }, - ], - }, - }, - }, - }); - expect((searchResult.hits.total as estypes.SearchTotalHits).value).to.eql(1); - return searchResult.hits.hits[0]; - }); - - // Ensure the next runAt is set to the retryDate by custom logic - expect(scheduledActionTask._source!.task.runAt).to.eql(retryDate.toISOString()); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should have proper callCluster and savedObjectsClient authorization for alert type executor when appropriate', async () => { - let searchResult: any; - const testStart = new Date(); - const reference = alertUtils.generateReference(); - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send( - getTestRuleData({ - rule_type_id: 'test.authorization', - params: { - callClusterAuthorizationIndex: authorizationIndex, - savedObjectsClientType: 'dashboard', - savedObjectsClientId: '1', - index: ES_TEST_INDEX_NAME, - reference, - }, - }) - ); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.authorization', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); - - // Wait for test.authorization to index a document before disabling the alert and waiting for tasks to finish - await esTestIndexTool.waitForDocs('alert:test.authorization', reference); - await alertUtils.disable(response.body.id); - await taskManagerUtils.waitForEmpty(testStart); - - // Ensure only 1 document exists with proper params - searchResult = await esTestIndexTool.search('alert:test.authorization', reference); - expect(searchResult.body.hits.total.value).to.eql(1); - expect(searchResult.body.hits.hits[0]._source.state).to.eql({ - callClusterSuccess: false, - callScopedClusterSuccess: false, - savedObjectsClientSuccess: false, - callClusterError: { - ...searchResult.body.hits.hits[0]._source.state.callClusterError, - }, - callScopedClusterError: { - ...searchResult.body.hits.hits[0]._source.state.callScopedClusterError, - }, - savedObjectsClientError: { - ...searchResult.body.hits.hits[0]._source.state.savedObjectsClientError, - output: { - ...searchResult.body.hits.hits[0]._source.state.savedObjectsClientError.output, - statusCode: 403, - }, - }, - }); - break; - case 'superuser at space1': - expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); - - // Wait for test.authorization to index a document before disabling the alert and waiting for tasks to finish - await esTestIndexTool.waitForDocs('alert:test.authorization', reference); - await alertUtils.disable(response.body.id); - await taskManagerUtils.waitForEmpty(testStart); - - // Ensure only 1 document exists with proper params - searchResult = await esTestIndexTool.search('alert:test.authorization', reference); - expect(searchResult.body.hits.total.value).to.eql(1); - expect(searchResult.body.hits.hits[0]._source.state).to.eql({ - callClusterSuccess: true, - callScopedClusterSuccess: true, - savedObjectsClientSuccess: false, - savedObjectsClientError: { - ...searchResult.body.hits.hits[0]._source.state.savedObjectsClientError, - output: { - ...searchResult.body.hits.hits[0]._source.state.savedObjectsClientError.output, - statusCode: 404, - }, - }, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should have proper callCluster and savedObjectsClient authorization for action type executor when appropriate', async () => { - let searchResult: any; - const testStart = new Date(); - const reference = alertUtils.generateReference(); - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action', - connector_type_id: 'test.authorization', - }) - .expect(200); - objectRemover.add(space.id, createdAction.id, 'connector', 'actions'); - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send( - getTestRuleData({ - rule_type_id: 'test.always-firing', - params: { - index: ES_TEST_INDEX_NAME, - reference, - }, - actions: [ - { - group: 'default', - id: createdAction.id, - params: { - callClusterAuthorizationIndex: authorizationIndex, - savedObjectsClientType: 'dashboard', - savedObjectsClientId: '1', - index: ES_TEST_INDEX_NAME, - reference, - }, - }, - ], - }) - ); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.always-firing', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); - - // Ensure test.authorization indexed 1 document before disabling the alert and waiting for tasks to finish - await esTestIndexTool.waitForDocs('action:test.authorization', reference); - await alertUtils.disable(response.body.id); - await taskManagerUtils.waitForEmpty(testStart); - - // Ensure only 1 document with proper params exists - searchResult = await esTestIndexTool.search('action:test.authorization', reference); - expect(searchResult.body.hits.total.value).to.eql(1); - expect(searchResult.body.hits.hits[0]._source.state).to.eql({ - callClusterSuccess: false, - callScopedClusterSuccess: false, - savedObjectsClientSuccess: false, - callClusterError: { - ...searchResult.body.hits.hits[0]._source.state.callClusterError, - }, - callScopedClusterError: { - ...searchResult.body.hits.hits[0]._source.state.callScopedClusterError, - }, - savedObjectsClientError: { - ...searchResult.body.hits.hits[0]._source.state.savedObjectsClientError, - output: { - ...searchResult.body.hits.hits[0]._source.state.savedObjectsClientError.output, - statusCode: 403, - }, - }, - }); - break; - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: `Unauthorized to get actions`, - statusCode: 403, - }); - break; - case 'superuser at space1': - expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); - - // Ensure test.authorization indexed 1 document before disabling the alert and waiting for tasks to finish - await esTestIndexTool.waitForDocs('action:test.authorization', reference); - await alertUtils.disable(response.body.id); - await taskManagerUtils.waitForEmpty(testStart); - - // Ensure only 1 document with proper params exists - searchResult = await esTestIndexTool.search('action:test.authorization', reference); - expect(searchResult.body.hits.total.value).to.eql(1); - expect(searchResult.body.hits.hits[0]._source.state).to.eql({ - callClusterSuccess: true, - callScopedClusterSuccess: true, - savedObjectsClientSuccess: false, - savedObjectsClientError: { - ...searchResult.body.hits.hits[0]._source.state.savedObjectsClientError, - output: { - ...searchResult.body.hits.hits[0]._source.state.savedObjectsClientError.output, - statusCode: 404, - }, - }, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should throttle alerts when appropriate', async () => { - const testStart = new Date(); - const reference = alertUtils.generateReference(); - const response = await alertUtils.createAlwaysFiringAction({ - reference, - overwrites: { - schedule: { interval: '1s' }, - }, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.always-firing', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: `Unauthorized to get actions`, - statusCode: 403, - }); - break; - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - case 'superuser at space1': - expect(response.statusCode).to.eql(200); - // Wait until alerts scheduled actions 3 times before disabling the alert and waiting for tasks to finish - await esTestIndexTool.waitForDocs('alert:test.always-firing', reference, 3); - await alertUtils.disable(response.body.id); - await taskManagerUtils.waitForEmpty(testStart); - - // Ensure actions only executed once - const searchResult = await esTestIndexTool.search( - 'action:test.index-record', - reference - ); - // @ts-expect-error doesnt handle total: number - expect(searchResult.body.hits.total.value).to.eql(1); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should not throttle when changing groups', async () => { - const testStart = new Date(); - const reference = alertUtils.generateReference(); - const response = await alertUtils.createAlwaysFiringAction({ - reference, - overwrites: { - schedule: { interval: '1s' }, - params: { - index: ES_TEST_INDEX_NAME, - reference, - groupsToScheduleActionsInSeries: ['default', 'other'], - }, - actions: [ - { - group: 'default', - id: indexRecordActionId, - params: { - index: ES_TEST_INDEX_NAME, - reference, - message: 'from:default', - }, - }, - { - group: 'other', - id: indexRecordActionId, - params: { - index: ES_TEST_INDEX_NAME, - reference, - message: 'from:other', - }, - }, - ], - }, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.always-firing', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: `Unauthorized to get actions`, - statusCode: 403, - }); - break; - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - case 'superuser at space1': - expect(response.statusCode).to.eql(200); - // Wait for actions to execute twice before disabling the alert and waiting for tasks to finish - await esTestIndexTool.waitForDocs('action:test.index-record', reference, 2); - await alertUtils.disable(response.body.id); - await taskManagerUtils.waitForEmpty(testStart); - - // Ensure only 2 actions with proper params exists - const searchResult = await esTestIndexTool.search( - 'action:test.index-record', - reference - ); - // @ts-expect-error doesnt handle total: number - expect(searchResult.body.hits.total.value).to.eql(2); - const messages: string[] = searchResult.body.hits.hits.map( - // @ts-expect-error _search: unknown - (hit: { _source: { params: { message: string } } }) => hit._source.params.message - ); - expect(messages.sort()).to.eql(['from:default', 'from:other']); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should not throttle when changing subgroups', async () => { - const testStart = new Date(); - const reference = alertUtils.generateReference(); - const response = await alertUtils.createAlwaysFiringAction({ - reference, - overwrites: { - schedule: { interval: '1s' }, - params: { - index: ES_TEST_INDEX_NAME, - reference, - groupsToScheduleActionsInSeries: ['default:prev', 'default:next'], - }, - actions: [ - { - group: 'default', - id: indexRecordActionId, - params: { - index: ES_TEST_INDEX_NAME, - reference, - message: 'from:{{alertActionGroup}}:{{alertActionSubgroup}}', - }, - }, - ], - }, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.always-firing', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: `Unauthorized to get actions`, - statusCode: 403, - }); - break; - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - case 'superuser at space1': - expect(response.statusCode).to.eql(200); - // Wait for actions to execute twice before disabling the alert and waiting for tasks to finish - await esTestIndexTool.waitForDocs('action:test.index-record', reference, 2); - await alertUtils.disable(response.body.id); - await taskManagerUtils.waitForEmpty(testStart); - - // Ensure only 2 actions with proper params exists - const searchResult = await esTestIndexTool.search( - 'action:test.index-record', - reference - ); - // @ts-expect-error doesnt handle total: number - expect(searchResult.body.hits.total.value).to.eql(2); - const messages: string[] = searchResult.body.hits.hits.map( - // @ts-expect-error _source: unknown - (hit: { _source: { params: { message: string } } }) => hit._source.params.message - ); - expect(messages.sort()).to.eql(['from:default:next', 'from:default:prev']); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should reset throttle window when not firing', async () => { - const testStart = new Date(); - const reference = alertUtils.generateReference(); - const response = await alertUtils.createAlwaysFiringAction({ - reference, - overwrites: { - schedule: { interval: '1s' }, - params: { - index: ES_TEST_INDEX_NAME, - reference, - groupsToScheduleActionsInSeries: ['default', null, 'default'], - }, - }, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.always-firing', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: `Unauthorized to get actions`, - statusCode: 403, - }); - break; - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - case 'superuser at space1': - expect(response.statusCode).to.eql(200); - // Actions should execute twice before widning things down - await esTestIndexTool.waitForDocs('action:test.index-record', reference, 2); - await alertUtils.disable(response.body.id); - await taskManagerUtils.waitForEmpty(testStart); - - // Ensure only 2 actions are executed - const searchResult = await esTestIndexTool.search( - 'action:test.index-record', - reference - ); - // @ts-expect-error doesnt handle total: number - expect(searchResult.body.hits.total.value).to.eql(2); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`shouldn't schedule actions when alert is muted`, async () => { - const testStart = new Date(); - const reference = alertUtils.generateReference(); - const response = await alertUtils.createAlwaysFiringAction({ - reference, - overwrites: { - enabled: false, - schedule: { interval: '1s' }, - }, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.always-firing', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: `Unauthorized to get actions`, - statusCode: 403, - }); - break; - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - case 'superuser at space1': - await alertUtils.muteAll(response.body.id); - await alertUtils.enable(response.body.id); - - // Wait until alerts schedule actions twice to ensure actions had a chance to skip - // execution once before disabling the alert and waiting for tasks to finish - await esTestIndexTool.waitForDocs('alert:test.always-firing', reference, 2); - await alertUtils.disable(response.body.id); - await taskManagerUtils.waitForEmpty(testStart); - - // Should not have executed any action - const executedActionsResult = await esTestIndexTool.search( - 'action:test.index-record', - reference - ); - // @ts-expect-error doesnt handle total: number - expect(executedActionsResult.body.hits.total.value).to.eql(0); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`shouldn't schedule actions when alert instance is muted`, async () => { - const testStart = new Date(); - const reference = alertUtils.generateReference(); - const response = await alertUtils.createAlwaysFiringAction({ - reference, - overwrites: { - enabled: false, - schedule: { interval: '1s' }, - }, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.always-firing', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: `Unauthorized to get actions`, - statusCode: 403, - }); - break; - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - case 'superuser at space1': - await alertUtils.muteInstance(response.body.id, '1'); - await alertUtils.enable(response.body.id); - - // Wait until alerts scheduled actions twice to ensure actions had a chance to execute - // once before disabling the alert and waiting for tasks to finish - await esTestIndexTool.waitForDocs('alert:test.always-firing', reference, 2); - await alertUtils.disable(response.body.id); - await taskManagerUtils.waitForEmpty(testStart); - - // Should not have executed any action - const executedActionsResult = await esTestIndexTool.search( - 'action:test.index-record', - reference - ); - // @ts-expect-error doesnt handle total: number - expect(executedActionsResult.body.hits.total.value).to.eql(0); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`should unmute all instances when unmuting an alert`, async () => { - const testStart = new Date(); - const reference = alertUtils.generateReference(); - const response = await alertUtils.createAlwaysFiringAction({ - reference, - overwrites: { - enabled: false, - schedule: { interval: '1s' }, - }, - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.always-firing', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: `Unauthorized to get actions`, - statusCode: 403, - }); - break; - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - case 'superuser at space1': - await alertUtils.muteInstance(response.body.id, '1'); - await alertUtils.muteAll(response.body.id); - await alertUtils.unmuteAll(response.body.id); - await alertUtils.enable(response.body.id); - - // Ensure actions are executed once before disabling the alert and waiting for tasks to finish - await esTestIndexTool.waitForDocs('action:test.index-record', reference, 1); - await alertUtils.disable(response.body.id); - await taskManagerUtils.waitForEmpty(testStart); - - // Should have one document indexed by the action - const searchResult = await esTestIndexTool.search( - 'action:test.index-record', - reference - ); - // @ts-expect-error doesnt handle total: number - expect(searchResult.body.hits.total.value).to.eql(1); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - }); - } - }); - - interface ValidateEventLogParams { - spaceId: string; - alertId: string; - ruleTypeId: string; - outcome: string; - message: string; - errorMessage?: string; - ruleObject: any; - } - - async function validateEventLog(params: ValidateEventLogParams): Promise { - const { spaceId, alertId, outcome, message, errorMessage, ruleObject } = params; - - const events: IValidatedEvent[] = await retry.try(async () => { - return await getEventLog({ - getService, - spaceId, - type: 'alert', - id: alertId, - provider: 'alerting', - actions: new Map([['execute', { gte: 1 }]]), - }); - }); - - const event = events[0]; - - const duration = event?.event?.duration; - const eventStart = Date.parse(event?.event?.start || 'undefined'); - const eventEnd = Date.parse(event?.event?.end || 'undefined'); - const dateNow = Date.now(); - - expect(typeof duration).to.be('number'); - expect(eventStart).to.be.ok(); - expect(eventEnd).to.be.ok(); - - const durationDiff = Math.abs( - Math.round(duration! / NANOS_IN_MILLIS) - (eventEnd - eventStart) - ); - - // account for rounding errors - expect(durationDiff < 1).to.equal(true); - expect(eventStart <= eventEnd).to.equal(true); - expect(eventEnd <= dateNow).to.equal(true); - - expect(event?.event?.outcome).to.equal(outcome); - - expect(event?.kibana?.saved_objects).to.eql([ - { - rel: 'primary', - type: 'alert', - id: alertId, - namespace: spaceId, - type_id: ruleObject.alertInfo.ruleTypeId, - }, - ]); - - expect(event?.kibana?.alert?.rule?.execution?.metrics?.number_of_triggered_actions).to.be(1); - expect(event?.kibana?.alert?.rule?.execution?.metrics?.number_of_searches).to.be(0); - expect(event?.kibana?.alert?.rule?.execution?.metrics?.es_search_duration_ms).to.be(0); - expect(event?.kibana?.alert?.rule?.execution?.metrics?.total_search_duration_ms).to.be(0); - expect(event?.kibana?.alert?.rule?.execution?.metrics?.number_of_active_alerts).to.be(1); - expect(event?.kibana?.alert?.rule?.execution?.metrics?.number_of_new_alerts).to.be(1); - expect(event?.kibana?.alert?.rule?.execution?.metrics?.number_of_recovered_alerts).to.be(0); - expect(event?.kibana?.alert?.rule?.execution?.metrics?.total_number_of_alerts).to.be(1); - - expect(event?.rule).to.eql({ - id: alertId, - license: 'basic', - category: ruleObject.alertInfo.ruleTypeId, - ruleset: ruleObject.alertInfo.producer, - name: ruleObject.alertInfo.name, - }); - - expect(event?.message).to.eql(message); - - if (errorMessage) { - expect(event?.error?.message).to.eql(errorMessage); - } - } -} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts deleted file mode 100644 index 57ba6e3863576..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts +++ /dev/null @@ -1,555 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { UserAtSpaceScenarios } from '../../scenarios'; -import { - checkAAD, - getTestRuleData, - getConsumerUnauthorizedErrorMessage, - getUrlPrefix, - ObjectRemover, - getProducerUnauthorizedErrorMessage, - TaskManagerDoc, -} from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default function createAlertTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const es = getService('es'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - - describe('create', () => { - const objectRemover = new ObjectRemover(supertest); - - after(() => objectRemover.removeAll()); - - async function getScheduledTask(id: string): Promise { - const scheduledTask = await es.get({ - id: `task:${id}`, - index: '.kibana_task_manager', - }); - return scheduledTask._source!; - } - - for (const scenario of UserAtSpaceScenarios) { - const { user, space } = scenario; - describe(scenario.id, () => { - it('should handle create alert request appropriately', async () => { - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'MY action', - connector_type_id: 'test.noop', - config: {}, - secrets: {}, - }) - .expect(200); - - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send( - getTestRuleData({ - actions: [ - { - id: createdAction.id, - group: 'default', - params: {}, - }, - ], - }) - ); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.noop', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: `Unauthorized to get actions`, - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); - expect(response.body).to.eql({ - id: response.body.id, - name: 'abc', - tags: ['foo'], - actions: [ - { - id: createdAction.id, - connector_type_id: createdAction.connector_type_id, - group: 'default', - params: {}, - }, - ], - enabled: true, - rule_type_id: 'test.noop', - consumer: 'alertsFixture', - params: {}, - created_by: user.username, - schedule: { interval: '1m' }, - scheduled_task_id: response.body.scheduled_task_id, - created_at: response.body.created_at, - updated_at: response.body.updated_at, - throttle: '1m', - notify_when: 'onThrottleInterval', - updated_by: user.username, - api_key_owner: user.username, - mute_all: false, - muted_alert_ids: [], - execution_status: response.body.execution_status, - }); - expect(typeof response.body.scheduled_task_id).to.be('string'); - expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); - expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); - - const taskRecord = await getScheduledTask(response.body.scheduled_task_id); - expect(taskRecord.type).to.eql('task'); - expect(taskRecord.task.taskType).to.eql('alerting:test.noop'); - expect(JSON.parse(taskRecord.task.params)).to.eql({ - alertId: response.body.id, - spaceId: space.id, - consumer: 'alertsFixture', - }); - // Ensure AAD isn't broken - await checkAAD({ - supertest, - spaceId: space.id, - type: 'alert', - id: response.body.id, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle create alert request appropriately when consumer is the same as producer', async () => { - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send( - getTestRuleData({ - rule_type_id: 'test.restricted-noop', - consumer: 'alertsRestrictedFixture', - }) - ); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all at space2': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.restricted-noop', - 'alertsRestrictedFixture' - ), - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle create alert request appropriately when consumer is not the producer', async () => { - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send( - getTestRuleData({ - rule_type_id: 'test.unrestricted-noop', - consumer: 'alertsFixture', - }) - ); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.unrestricted-noop', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getProducerUnauthorizedErrorMessage( - 'create', - 'test.unrestricted-noop', - 'alertsRestrictedFixture' - ), - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle create alert request appropriately when consumer is "alerts"', async () => { - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send( - getTestRuleData({ - rule_type_id: 'test.noop', - consumer: 'alerts', - }) - ); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage('create', 'test.noop', 'alerts'), - statusCode: 403, - }); - break; - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getProducerUnauthorizedErrorMessage( - 'create', - 'test.noop', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'superuser at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle create alert request appropriately when consumer is unknown', async () => { - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send( - getTestRuleData({ - rule_type_id: 'test.noop', - consumer: 'some consumer patrick invented', - }) - ); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all at space2': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - case 'superuser at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.noop', - 'some consumer patrick invented' - ), - statusCode: 403, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle create alert request appropriately when an alert is disabled ', async () => { - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send(getTestRuleData({ enabled: false })); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.noop', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); - expect(response.body.scheduled_task_id).to.eql(undefined); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle create alert request appropriately when alert name has leading and trailing whitespaces', async () => { - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send( - getTestRuleData({ - name: ' leading and trailing whitespace ', - }) - ); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.noop', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - expect(response.body.name).to.eql(' leading and trailing whitespace '); - objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle create alert request appropriately when alert type is unregistered', async () => { - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send( - getTestRuleData({ - rule_type_id: 'test.unregistered-alert-type', - }) - ); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all at space2': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - case 'superuser at space1': - expect(response.statusCode).to.eql(400); - expect(response.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: 'Rule type "test.unregistered-alert-type" is not registered.', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle create alert request appropriately when payload is empty and invalid', async () => { - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send({}); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all at space2': - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(400); - expect(response.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: '[request body.name]: expected value of type [string] but got [undefined]', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`should handle create alert request appropriately when params isn't valid`, async () => { - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send( - getTestRuleData({ - rule_type_id: 'test.validation', - }) - ); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'create', - 'test.validation', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(400); - expect(response.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'params invalid: [param1]: expected value of type [string] but got [undefined]', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle create alert request appropriately when interval schedule is wrong syntax', async () => { - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send(getTestRuleData({ schedule: { interval: '10x' } })); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all at space2': - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(400); - expect(response.body).to.eql({ - error: 'Bad Request', - message: '[request body.schedule.interval]: string is not a valid duration: 10x', - statusCode: 400, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle create alert request appropriately when interval schedule is 0', async () => { - const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send(getTestRuleData({ schedule: { interval: '0s' } })); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'global_read at space1': - case 'space_1_all at space2': - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(400); - expect(response.body).to.eql({ - error: 'Bad Request', - message: '[request body.schedule.interval]: string is not a valid duration: 0s', - statusCode: 400, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - }); - } - }); -} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts deleted file mode 100644 index 56c50f035b10e..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { UserAtSpaceScenarios } from '../../scenarios'; -import { - getUrlPrefix, - getTestRuleData, - getConsumerUnauthorizedErrorMessage, - getProducerUnauthorizedErrorMessage, - ObjectRemover, -} from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default function createDeleteTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const es = getService('es'); - const retry = getService('retry'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - - describe('delete', () => { - const objectRemover = new ObjectRemover(supertest); - - after(() => objectRemover.removeAll()); - - async function getScheduledTask(id: string) { - return await es.get({ - id: `task:${id}`, - index: '.kibana_task_manager', - }); - } - - for (const scenario of UserAtSpaceScenarios) { - const { user, space } = scenario; - describe(scenario.id, () => { - it('should handle delete alert request appropriately', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send(getTestRuleData()) - .expect(200); - - const response = await supertestWithoutAuth - .delete(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'delete', - 'test.noop', - 'alertsFixture' - ), - statusCode: 403, - }); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - // Ensure task still exists - await getScheduledTask(createdAlert.scheduled_task_id); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(204); - expect(response.body).to.eql(''); - try { - await getScheduledTask(createdAlert.scheduled_task_id); - throw new Error('Should have removed scheduled task'); - } catch (e) { - expect(e.meta.statusCode).to.eql(404); - } - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle delete alert request appropriately when consumer is the same as producer', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - rule_type_id: 'test.restricted-noop', - consumer: 'alertsRestrictedFixture', - }) - ) - .expect(200); - - const response = await supertestWithoutAuth - .delete(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'delete', - 'test.restricted-noop', - 'alertsRestrictedFixture' - ), - statusCode: 403, - }); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - // Ensure task still exists - await getScheduledTask(createdAlert.scheduled_task_id); - break; - case 'superuser at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(204); - expect(response.body).to.eql(''); - try { - await getScheduledTask(createdAlert.scheduled_task_id); - throw new Error('Should have removed scheduled task'); - } catch (e) { - expect(e.meta.statusCode).to.eql(404); - } - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle delete alert request appropriately when consumer is not the producer', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - rule_type_id: 'test.unrestricted-noop', - consumer: 'alertsFixture', - }) - ) - .expect(200); - - const response = await supertestWithoutAuth - .delete(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'delete', - 'test.unrestricted-noop', - 'alertsFixture' - ), - statusCode: 403, - }); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - // Ensure task still exists - await getScheduledTask(createdAlert.scheduled_task_id); - break; - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getProducerUnauthorizedErrorMessage( - 'delete', - 'test.unrestricted-noop', - 'alertsRestrictedFixture' - ), - statusCode: 403, - }); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - // Ensure task still exists - await getScheduledTask(createdAlert.scheduled_task_id); - break; - case 'superuser at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(204); - expect(response.body).to.eql(''); - try { - await getScheduledTask(createdAlert.scheduled_task_id); - throw new Error('Should have removed scheduled task'); - } catch (e) { - expect(e.meta.statusCode).to.eql(404); - } - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle delete alert request appropriately when consumer is "alerts"', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - rule_type_id: 'test.noop', - consumer: 'alerts', - }) - ) - .expect(200); - - const response = await supertestWithoutAuth - .delete(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage('delete', 'test.noop', 'alerts'), - statusCode: 403, - }); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - break; - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getProducerUnauthorizedErrorMessage( - 'delete', - 'test.noop', - 'alertsFixture' - ), - statusCode: 403, - }); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - // Ensure task still exists - await getScheduledTask(createdAlert.scheduled_task_id); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(204); - expect(response.body).to.eql(''); - try { - await getScheduledTask(createdAlert.scheduled_task_id); - throw new Error('Should have removed scheduled task'); - } catch (e) { - expect(e.meta.statusCode).to.eql(404); - } - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`shouldn't delete alert from another space`, async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send(getTestRuleData()) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - const response = await supertestWithoutAuth - .delete(`${getUrlPrefix('other')}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password); - - expect(response.statusCode).to.eql(404); - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - case 'superuser at space1': - expect(response.body).to.eql({ - statusCode: 404, - error: 'Not Found', - message: `Saved object [alert/${createdAlert.id}] not found`, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should still be able to delete alert when AAD is broken', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send(getTestRuleData()) - .expect(200); - - await retry.try(async () => { - await supertest - .put( - `${getUrlPrefix(space.id)}/api/alerts_fixture/saved_object/alert/${createdAlert.id}` - ) - .set('kbn-xsrf', 'foo') - .send({ - attributes: { - name: 'bar', - }, - }) - .expect(200); - }); - - const response = await supertestWithoutAuth - .delete(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'delete', - 'test.noop', - 'alertsFixture' - ), - statusCode: 403, - }); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - // Ensure task still exists - await getScheduledTask(createdAlert.scheduled_task_id); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(204); - expect(response.body).to.eql(''); - try { - await getScheduledTask(createdAlert.scheduled_task_id); - throw new Error('Should have removed scheduled task'); - } catch (e) { - expect(e.meta.statusCode).to.eql(404); - } - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - }); - } - }); -} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts deleted file mode 100644 index 180a3cf36e27f..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { SuperTest, Test } from 'supertest'; -import { UserAtSpaceScenarios } from '../../scenarios'; -import { - getUrlPrefix, - getTestRuleData, - ObjectRemover, - getConsumerUnauthorizedErrorMessage, - getProducerUnauthorizedErrorMessage, -} from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -const getTestUtils = ( - describeType: 'internal' | 'public', - objectRemover: ObjectRemover, - supertest: SuperTest, - supertestWithoutAuth: any -) => { - describe(describeType, () => { - afterEach(() => objectRemover.removeAll()); - for (const scenario of UserAtSpaceScenarios) { - const { user, space } = scenario; - describe(scenario.id, () => { - it('should handle get alert request appropriately', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send(getTestRuleData()) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - const response = await supertestWithoutAuth - .get( - `${getUrlPrefix(space.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rule/${createdAlert.id}` - ) - .auth(user.username, user.password); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage('get', 'test.noop', 'alertsFixture'), - statusCode: 403, - }); - break; - case 'global_read at space1': - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - expect(response.body).to.eql({ - id: createdAlert.id, - name: 'abc', - tags: ['foo'], - rule_type_id: 'test.noop', - consumer: 'alertsFixture', - schedule: { interval: '1m' }, - enabled: true, - actions: [], - params: {}, - created_by: 'elastic', - scheduled_task_id: response.body.scheduled_task_id, - updated_at: response.body.updated_at, - created_at: response.body.created_at, - throttle: '1m', - notify_when: 'onThrottleInterval', - updated_by: 'elastic', - api_key_owner: 'elastic', - mute_all: false, - muted_alert_ids: [], - execution_status: response.body.execution_status, - ...(describeType === 'internal' - ? { - monitoring: response.body.monitoring, - snooze_end_time: response.body.snooze_end_time, - } - : {}), - }); - expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); - expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle get alert request appropriately when consumer is the same as producer', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - rule_type_id: 'test.restricted-noop', - consumer: 'alertsRestrictedFixture', - }) - ) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - const response = await supertestWithoutAuth - .get( - `${getUrlPrefix(space.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rule/${createdAlert.id}` - ) - .auth(user.username, user.password); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'get', - 'test.restricted-noop', - 'alertsRestrictedFixture' - ), - statusCode: 403, - }); - break; - case 'global_read at space1': - case 'superuser at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle get alert request appropriately when consumer is not the producer', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - rule_type_id: 'test.unrestricted-noop', - consumer: 'alertsFixture', - }) - ) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - const response = await supertestWithoutAuth - .get( - `${getUrlPrefix(space.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rule/${createdAlert.id}` - ) - .auth(user.username, user.password); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'get', - 'test.unrestricted-noop', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getProducerUnauthorizedErrorMessage( - 'get', - 'test.unrestricted-noop', - 'alertsRestrictedFixture' - ), - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'global_read at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle get alert request appropriately when consumer is "alerts"', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - rule_type_id: 'test.restricted-noop', - consumer: 'alerts', - }) - ) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - const response = await supertestWithoutAuth - .get( - `${getUrlPrefix(space.id)}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rule/${createdAlert.id}` - ) - .auth(user.username, user.password); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'get', - 'test.restricted-noop', - 'alerts' - ), - statusCode: 403, - }); - break; - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getProducerUnauthorizedErrorMessage( - 'get', - 'test.restricted-noop', - 'alertsRestrictedFixture' - ), - statusCode: 403, - }); - break; - case 'global_read at space1': - case 'superuser at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`shouldn't get alert from another space`, async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send(getTestRuleData()) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - const response = await supertestWithoutAuth - .get( - `${getUrlPrefix('other')}/${ - describeType === 'public' ? 'api' : 'internal' - }/alerting/rule/${createdAlert.id}` - ) - .auth(user.username, user.password); - - expect(response.statusCode).to.eql(404); - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - case 'global_read at space1': - case 'superuser at space1': - expect(response.body).to.eql({ - statusCode: 404, - error: 'Not Found', - message: `Saved object [alert/${createdAlert.id}] not found`, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`should handle get alert request appropriately when alert doesn't exist`, async () => { - const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerting/rule/1`) - .auth(user.username, user.password); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(404); - expect(response.body).to.eql({ - statusCode: 404, - error: 'Not Found', - message: 'Saved object [alert/1] not found', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - }); - } - }); -}; - -// eslint-disable-next-line import/no-default-export -export default function createGetTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - - describe('get', () => { - const objectRemover = new ObjectRemover(supertest); - afterEach(() => objectRemover.removeAll()); - - getTestUtils('public', objectRemover, supertest, supertestWithoutAuth); - getTestUtils('internal', objectRemover, supertest, supertestWithoutAuth); - }); -} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts deleted file mode 100644 index a1e23db7944f1..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { setupSpacesAndUsers, tearDown } from '..'; - -// eslint-disable-next-line import/no-default-export -export default function alertingTests({ loadTestFile, getService }: FtrProviderContext) { - describe('Alerts', () => { - describe('legacy alerts', () => { - before(async () => { - await setupSpacesAndUsers(getService); - }); - - after(async () => { - await tearDown(getService); - }); - - loadTestFile(require.resolve('./rbac_legacy')); - }); - - describe('alerts', () => { - before(async () => { - await setupSpacesAndUsers(getService); - }); - - after(async () => { - await tearDown(getService); - }); - - describe('', function () { - this.tags('ciGroup17'); - loadTestFile(require.resolve('./find')); - loadTestFile(require.resolve('./create')); - loadTestFile(require.resolve('./delete')); - loadTestFile(require.resolve('./disable')); - loadTestFile(require.resolve('./enable')); - loadTestFile(require.resolve('./execution_status')); - loadTestFile(require.resolve('./get')); - loadTestFile(require.resolve('./get_alert_state')); - loadTestFile(require.resolve('./get_alert_summary')); - loadTestFile(require.resolve('./rule_types')); - }); - - describe('', function () { - this.tags('ciGroup30'); - loadTestFile(require.resolve('./mute_all')); - loadTestFile(require.resolve('./mute_instance')); - loadTestFile(require.resolve('./unmute_all')); - loadTestFile(require.resolve('./unmute_instance')); - loadTestFile(require.resolve('./update')); - loadTestFile(require.resolve('./update_api_key')); - loadTestFile(require.resolve('./alerts')); - loadTestFile(require.resolve('./event_log')); - loadTestFile(require.resolve('./mustache_templates')); - loadTestFile(require.resolve('./health')); - loadTestFile(require.resolve('./excluded')); - loadTestFile(require.resolve('./snooze')); - }); - }); - }); -} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts deleted file mode 100644 index b2a1ae223f62c..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts +++ /dev/null @@ -1,998 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license 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 { Response as SupertestResponse } from 'supertest'; -import { UserAtSpaceScenarios } from '../../scenarios'; -import { - checkAAD, - getUrlPrefix, - getTestRuleData, - ObjectRemover, - ensureDatetimeIsWithinRange, - getConsumerUnauthorizedErrorMessage, - getProducerUnauthorizedErrorMessage, -} from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default function createUpdateTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - const retry = getService('retry'); - - function getAlertingTaskById(taskId: string) { - return supertest - .get(`/api/alerting_tasks/${taskId}`) - .expect(200) - .then((response: SupertestResponse) => response.body); - } - - describe('update', () => { - const objectRemover = new ObjectRemover(supertest); - - after(() => objectRemover.removeAll()); - - for (const scenario of UserAtSpaceScenarios) { - const { user, space } = scenario; - describe(scenario.id, () => { - it('should handle update alert request appropriately', async () => { - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'MY action', - connector_type_id: 'test.noop', - config: {}, - secrets: {}, - }) - .expect(200); - - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send(getTestRuleData()) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - const updatedData = { - name: 'bcd', - tags: ['bar'], - params: { - foo: true, - }, - schedule: { interval: '12s' }, - actions: [ - { - id: createdAction.id, - group: 'default', - params: {}, - }, - ], - throttle: '1m', - notify_when: 'onThrottleInterval', - }; - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send(updatedData); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'update', - 'test.noop', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: `Unauthorized to get actions`, - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - expect(response.body).to.eql({ - ...updatedData, - id: createdAlert.id, - rule_type_id: 'test.noop', - consumer: 'alertsFixture', - created_by: 'elastic', - enabled: true, - updated_by: user.username, - api_key_owner: user.username, - mute_all: false, - muted_alert_ids: [], - actions: [ - { - id: createdAction.id, - connector_type_id: 'test.noop', - group: 'default', - params: {}, - }, - ], - scheduled_task_id: createdAlert.scheduled_task_id, - created_at: response.body.created_at, - updated_at: response.body.updated_at, - execution_status: response.body.execution_status, - }); - expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); - expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); - expect(Date.parse(response.body.updated_at)).to.be.greaterThan( - Date.parse(response.body.created_at) - ); - // Ensure AAD isn't broken - await checkAAD({ - supertest, - spaceId: space.id, - type: 'alert', - id: createdAlert.id, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle update alert request appropriately when consumer is the same as producer', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - rule_type_id: 'test.restricted-noop', - consumer: 'alertsRestrictedFixture', - }) - ) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - const updatedData = { - name: 'bcd', - tags: ['bar'], - params: { - foo: true, - }, - schedule: { interval: '12s' }, - actions: [], - throttle: '1m', - notify_when: 'onThrottleInterval', - }; - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send(updatedData); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'update', - 'test.restricted-noop', - 'alertsRestrictedFixture' - ), - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - expect(response.body).to.eql({ - ...updatedData, - id: createdAlert.id, - rule_type_id: 'test.restricted-noop', - consumer: 'alertsRestrictedFixture', - created_by: 'elastic', - enabled: true, - updated_by: user.username, - api_key_owner: user.username, - mute_all: false, - muted_alert_ids: [], - scheduled_task_id: createdAlert.scheduled_task_id, - created_at: response.body.created_at, - updated_at: response.body.updated_at, - execution_status: response.body.execution_status, - }); - expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); - expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); - expect(Date.parse(response.body.updated_at)).to.be.greaterThan( - Date.parse(response.body.created_at) - ); - // Ensure AAD isn't broken - await checkAAD({ - supertest, - spaceId: space.id, - type: 'alert', - id: createdAlert.id, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle update alert request appropriately when consumer is not the producer', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - rule_type_id: 'test.unrestricted-noop', - consumer: 'alertsFixture', - }) - ) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - const updatedData = { - name: 'bcd', - tags: ['bar'], - params: { - foo: true, - }, - schedule: { interval: '12s' }, - actions: [], - throttle: '1m', - notify_when: 'onThrottleInterval', - }; - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send(updatedData); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'update', - 'test.unrestricted-noop', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getProducerUnauthorizedErrorMessage( - 'update', - 'test.unrestricted-noop', - 'alertsRestrictedFixture' - ), - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - expect(response.body).to.eql({ - ...updatedData, - id: createdAlert.id, - rule_type_id: 'test.unrestricted-noop', - consumer: 'alertsFixture', - created_by: 'elastic', - enabled: true, - updated_by: user.username, - api_key_owner: user.username, - mute_all: false, - muted_alert_ids: [], - scheduled_task_id: createdAlert.scheduled_task_id, - created_at: response.body.created_at, - updated_at: response.body.updated_at, - execution_status: response.body.execution_status, - }); - expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); - expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); - expect(Date.parse(response.body.updated_at)).to.be.greaterThan( - Date.parse(response.body.created_at) - ); - // Ensure AAD isn't broken - await checkAAD({ - supertest, - spaceId: space.id, - type: 'alert', - id: createdAlert.id, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle update alert request appropriately when consumer is "alerts"', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - rule_type_id: 'test.restricted-noop', - consumer: 'alerts', - }) - ) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - const updatedData = { - name: 'bcd', - tags: ['bar'], - params: { - foo: true, - }, - schedule: { interval: '12s' }, - actions: [], - throttle: '1m', - notify_when: 'onThrottleInterval', - }; - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send(updatedData); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'update', - 'test.restricted-noop', - 'alerts' - ), - statusCode: 403, - }); - break; - case 'global_read at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getProducerUnauthorizedErrorMessage( - 'update', - 'test.restricted-noop', - 'alertsRestrictedFixture' - ), - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - expect(response.body).to.eql({ - ...updatedData, - id: createdAlert.id, - rule_type_id: 'test.restricted-noop', - consumer: 'alerts', - created_by: 'elastic', - enabled: true, - updated_by: user.username, - api_key_owner: user.username, - mute_all: false, - muted_alert_ids: [], - scheduled_task_id: createdAlert.scheduled_task_id, - created_at: response.body.created_at, - updated_at: response.body.updated_at, - execution_status: response.body.execution_status, - }); - expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); - expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); - expect(Date.parse(response.body.updated_at)).to.be.greaterThan( - Date.parse(response.body.created_at) - ); - // Ensure AAD isn't broken - await checkAAD({ - supertest, - spaceId: space.id, - type: 'alert', - id: createdAlert.id, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should still be able to update when AAD is broken', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send(getTestRuleData()) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - await retry.try(async () => { - await supertest - .put( - `${getUrlPrefix(space.id)}/api/alerts_fixture/saved_object/alert/${createdAlert.id}` - ) - .set('kbn-xsrf', 'foo') - .send({ - attributes: { - name: 'bar', - }, - }) - .expect(200); - }); - - const updatedData = { - name: 'bcd', - tags: ['bar'], - params: { - foo: true, - }, - schedule: { interval: '12s' }, - actions: [], - throttle: '1m', - notify_when: 'onThrottleInterval', - }; - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send(updatedData); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'update', - 'test.noop', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - expect(response.body).to.eql({ - ...updatedData, - id: createdAlert.id, - rule_type_id: 'test.noop', - consumer: 'alertsFixture', - created_by: 'elastic', - enabled: true, - updated_by: user.username, - api_key_owner: user.username, - mute_all: false, - muted_alert_ids: [], - scheduled_task_id: createdAlert.scheduled_task_id, - created_at: response.body.created_at, - updated_at: response.body.updated_at, - execution_status: response.body.execution_status, - }); - expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); - expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); - expect(Date.parse(response.body.updated_at)).to.be.greaterThan( - Date.parse(response.body.created_at) - ); - // Ensure AAD isn't broken - await checkAAD({ - supertest, - spaceId: space.id, - type: 'alert', - id: createdAlert.id, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle update alert request appropriately when alert name has leading and trailing whitespaces', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send(getTestRuleData()) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - const updatedData = { - name: ' leading and trailing whitespace ', - tags: ['bar'], - params: { - foo: true, - }, - schedule: { interval: '12s' }, - actions: [], - throttle: '1m', - notify_when: 'onActiveAlert', - }; - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send(updatedData); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'update', - 'test.noop', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - expect(response.body.name).to.eql(' leading and trailing whitespace '); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`shouldn't update alert from another space`, async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send(getTestRuleData()) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - const response = await supertestWithoutAuth - .put(`${getUrlPrefix('other')}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send({ - name: 'bcd', - tags: ['bar'], - params: { - foo: true, - }, - schedule: { interval: '12s' }, - throttle: '1m', - actions: [], - notify_when: 'onActiveAlert', - }); - - expect(response.statusCode).to.eql(404); - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - case 'superuser at space1': - expect(response.body).to.eql({ - statusCode: 404, - error: 'Not Found', - message: `Saved object [alert/${createdAlert.id}] not found`, - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle update alert request appropriately when attempting to change alert type', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send(getTestRuleData()) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send({ - name: 'bcd', - tags: ['bar'], - throttle: '1m', - rule_type_id: '1', - params: { - foo: true, - }, - schedule: { interval: '12s' }, - actions: [], - notify_when: 'onActiveAlert', - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(400); - expect(response.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: '[request body.rule_type_id]: definition for this key is missing', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle update alert request appropriately when payload is empty and invalid', async () => { - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerting/rule/1`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send({}); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(400); - expect(response.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: '[request body.name]: expected value of type [string] but got [undefined]', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it(`should handle update alert request appropriately when alertTypeConfig isn't valid`, async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - rule_type_id: 'test.validation', - params: { - param1: 'test', - }, - }) - ) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send({ - name: 'bcd', - tags: ['bar'], - schedule: { interval: '1m' }, - throttle: '1m', - params: {}, - actions: [], - notify_when: 'onActiveAlert', - }); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'update', - 'test.validation', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(400); - expect(response.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'params invalid: [param1]: expected value of type [string] but got [undefined]', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle update alert request appropriately when interval schedule is wrong syntax', async () => { - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerting/rule/1`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send( - getTestRuleData({ - schedule: { interval: '10x' }, - enabled: undefined, - consumer: undefined, - }) - ); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(400); - expect(response.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: '[request body.schedule.interval]: string is not a valid duration: 10x', - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle updates to an alert schedule by rescheduling the underlying task', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - schedule: { interval: '30m' }, - }) - ) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - await retry.try(async () => { - const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)).docs[0]; - expect(alertTask.status).to.eql('idle'); - // ensure the alert inital run has completed and it's been rescheduled to half an hour from now - ensureDatetimeIsWithinRange(Date.parse(alertTask.runAt), 30 * 60 * 1000); - }); - - const updatedData = { - name: 'bcd', - tags: ['bar'], - params: { - foo: true, - }, - schedule: { interval: '1m' }, - actions: [], - throttle: '1m', - notify_when: 'onThrottleInterval', - }; - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send(updatedData); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'update', - 'test.noop', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - await retry.try(async () => { - const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)) - .docs[0]; - expect(alertTask.status).to.eql('idle'); - // ensure the alert is rescheduled to a minute from now - ensureDatetimeIsWithinRange(Date.parse(alertTask.runAt), 60 * 1000); - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle updates for a long running alert type without failing the underlying tasks due to invalidated ApiKey', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send({ - enabled: true, - name: 'abc', - tags: ['foo'], - rule_type_id: 'test.longRunning', - consumer: 'alertsFixture', - schedule: { interval: '1s' }, - throttle: '1m', - actions: [], - params: {}, - notify_when: 'onThrottleInterval', - }) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - const updatedData = { - name: 'bcd', - tags: ['bar'], - params: { - foo: true, - }, - schedule: { interval: '1m' }, - actions: [], - throttle: '1m', - notify_when: 'onThrottleInterval', - }; - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send(updatedData); - - const statusUpdates: string[] = []; - await retry.try(async () => { - const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)).docs[0]; - statusUpdates.push(alertTask.status); - expect(alertTask.status).to.eql('idle'); - }); - - expect(statusUpdates.find((status) => status === 'failed')).to.be(undefined); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'update', - 'test.longRunning', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - await retry.try(async () => { - const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)) - .docs[0]; - expect(alertTask.status).to.eql('idle'); - // ensure the alert is rescheduled to a minute from now - ensureDatetimeIsWithinRange(Date.parse(alertTask.runAt), 60 * 1000); - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle updates to an alert schedule by setting the new schedule for the underlying task', async () => { - const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - schedule: { interval: '1m' }, - }) - ) - .expect(200); - objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); - - await retry.try(async () => { - const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)).docs[0]; - expect(alertTask.status).to.eql('idle'); - expect(alertTask.schedule).to.eql({ interval: '1m' }); - }); - - const updatedData = { - name: 'bcd', - tags: ['bar'], - params: { - foo: true, - }, - schedule: { interval: '1s' }, - actions: [], - throttle: '1m', - notify_when: 'onThrottleInterval', - }; - const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) - .set('kbn-xsrf', 'foo') - .auth(user.username, user.password) - .send(updatedData); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - case 'global_read at space1': - expect(response.statusCode).to.eql(403); - expect(response.body).to.eql({ - error: 'Forbidden', - message: getConsumerUnauthorizedErrorMessage( - 'update', - 'test.noop', - 'alertsFixture' - ), - statusCode: 403, - }); - break; - case 'superuser at space1': - case 'space_1_all at space1': - case 'space_1_all_alerts_none_actions at space1': - case 'space_1_all_with_restricted_fixture at space1': - expect(response.statusCode).to.eql(200); - await retry.try(async () => { - const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)) - .docs[0]; - expect(alertTask.status).to.eql('idle'); - expect(alertTask.schedule).to.eql({ interval: '1s' }); - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - }); - } - }); -} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts deleted file mode 100644 index 1196e3716347d..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { isCustomRoleSpecification } from '../../common/types'; -import { Spaces, Users } from '../scenarios'; - -export async function setupSpacesAndUsers(getService: FtrProviderContext['getService']) { - const securityService = getService('security'); - const spacesService = getService('spaces'); - - for (const space of Spaces) { - await spacesService.create(space); - } - - for (const user of Users) { - const roles = [...(user.role ? [user.role] : []), ...(user.roles ? user.roles : [])]; - - await securityService.user.create(user.username, { - password: user.password, - full_name: user.fullName, - roles: roles.map((role) => role.name), - }); - - for (const role of roles) { - if (isCustomRoleSpecification(role)) { - await securityService.role.create(role.name, { - kibana: role.kibana, - elasticsearch: role.elasticsearch, - }); - } - } - } -} - -export async function tearDown(getService: FtrProviderContext['getService']) { - const securityService = getService('security'); - const esArchiver = getService('esArchiver'); - - for (const user of Users) { - await securityService.user.delete(user.username); - - const roles = [...(user.role ? [user.role] : []), ...(user.roles ? user.roles : [])]; - for (const role of roles) { - if (isCustomRoleSpecification(role)) { - await securityService.role.delete(role.name); - } - } - } - - await esArchiver.unload('x-pack/test/functional/es_archives/empty_kibana'); -} - -// eslint-disable-next-line import/no-default-export -export default function alertingApiIntegrationTests({ loadTestFile }: FtrProviderContext) { - describe('alerting api integration security and spaces enabled', function () { - describe('', function () { - this.tags('ciGroup17'); - loadTestFile(require.resolve('./telemetry')); - loadTestFile(require.resolve('./actions')); - }); - - describe('', function () { - loadTestFile(require.resolve('./alerting')); - }); - }); -} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/index.ts deleted file mode 100644 index 9e73fafc9f7bd..0000000000000 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/index.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { setupSpacesAndUsers, tearDown } from '..'; - -// eslint-disable-next-line import/no-default-export -export default function actionsTests({ loadTestFile, getService }: FtrProviderContext) { - describe('Alerting and Actions Telemetry', () => { - before(async () => { - await setupSpacesAndUsers(getService); - }); - - after(async () => { - await tearDown(getService); - }); - - // run telemetry tests before anything else - loadTestFile(require.resolve('./actions_telemetry')); - loadTestFile(require.resolve('./alerting_telemetry')); - }); -} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts index 06e077f4b9de4..c6330e660aa24 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts @@ -6,7 +6,7 @@ */ import type { Client } from '@elastic/elasticsearch'; import expect from '@kbn/expect'; -import { IValidatedEvent } from '@kbn/event-log-plugin/server'; +import { IValidatedEvent, nanosToMillis } from '@kbn/event-log-plugin/server'; import { Spaces } from '../../scenarios'; import { ESTestIndexTool, @@ -17,8 +17,6 @@ import { } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; -const NANOS_IN_MILLIS = 1000 * 1000; - // eslint-disable-next-line import/no-default-export export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -133,7 +131,7 @@ export default function ({ getService }: FtrProviderContext) { expect(response.body).to.eql({ connector_id: createdAction.id, status: 'error', - message: 'an error occurred while running the action executor', + message: 'an error occurred while running the action', service_message: `expected failure for ${ES_TEST_INDEX_NAME} ${reference}`, retry: false, }); @@ -144,7 +142,7 @@ export default function ({ getService }: FtrProviderContext) { actionTypeId: 'test.failing', outcome: 'failure', message: `action execution failure: test.failing:${createdAction.id}: failing action`, - errorMessage: `an error occurred while running the action executor: expected failure for .kibana-alerting-test-data actions-failure-1:space1`, + errorMessage: `an error occurred while running the action: expected failure for .kibana-alerting-test-data actions-failure-1:space1`, }); }); @@ -327,7 +325,7 @@ export default function ({ getService }: FtrProviderContext) { expect(response.body).to.eql({ actionId: createdAction.id, status: 'error', - message: 'an error occurred while running the action executor', + message: 'an error occurred while running the action', serviceMessage: `expected failure for ${ES_TEST_INDEX_NAME} ${reference}`, retry: false, }); @@ -372,14 +370,12 @@ export default function ({ getService }: FtrProviderContext) { const executeEventEnd = Date.parse(executeEvent?.event?.end || 'undefined'); const dateNow = Date.now(); - expect(typeof duration).to.be('number'); + expect(typeof duration).to.be('string'); expect(executeEventStart).to.be.ok(); expect(startExecuteEventStart).to.equal(executeEventStart); expect(executeEventEnd).to.be.ok(); - const durationDiff = Math.abs( - Math.round(duration! / NANOS_IN_MILLIS) - (executeEventEnd - executeEventStart) - ); + const durationDiff = Math.abs(nanosToMillis(duration!) - (executeEventEnd - executeEventStart)); // account for rounding errors expect(durationDiff < 1).to.equal(true); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/migrations.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/migrations.ts index 7b28161c18238..4f23a5ff3a727 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/migrations.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/migrations.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { asyncForEach } from '@kbn/std'; import { getUrlPrefix } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; @@ -83,6 +84,23 @@ export default function createGetTests({ getService }: FtrProviderContext) { expect(connectorWithoutService.body.config.service).to.eql('other'); }); + it('8.3.0 migrates service now connectors to have `isOAuth` property', async () => { + const serviceNowConnectorIds = [ + '7d04bc30-c4c0-11ec-ae29-917aa31a5b75', + '8a9331b0-c4c0-11ec-ae29-917aa31a5b75', + '6d3a1250-c4c0-11ec-ae29-917aa31a5b75', + ]; + + await asyncForEach(serviceNowConnectorIds, async (serviceNowConnectorId) => { + const connectorResponse = await supertest.get( + `${getUrlPrefix(``)}/api/actions/action/${serviceNowConnectorId}` + ); + + expect(connectorResponse.status).to.eql(200); + expect(connectorResponse.body.config.isOAuth).to.eql(false); + }); + }); + it('decryption error during migration', async () => { const badEmailConnector = await supertest.get( `${getUrlPrefix(``)}/api/actions/connector/0f8f2810-0a59-11ec-9a7c-fd0c2b83ff7d` diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/aggregate.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/aggregate.ts index 588e7132f268c..4424175e36953 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/aggregate.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/aggregate.ts @@ -44,6 +44,7 @@ export default function createAggregateTests({ getService }: FtrProviderContext) rule_snoozed_status: { snoozed: 0, }, + rule_tags: [], }); }); @@ -122,6 +123,7 @@ export default function createAggregateTests({ getService }: FtrProviderContext) rule_snoozed_status: { snoozed: 0, }, + rule_tags: ['foo'], }); }); @@ -137,6 +139,7 @@ export default function createAggregateTests({ getService }: FtrProviderContext) { rule_type_id: 'test.noop', schedule: { interval: '1s' }, + tags: ['a', 'b'], }, 'ok' ); @@ -153,6 +156,7 @@ export default function createAggregateTests({ getService }: FtrProviderContext) params: { pattern: { instance: new Array(100).fill(true) }, }, + tags: ['a', 'c', 'f'], }, 'active' ); @@ -166,6 +170,7 @@ export default function createAggregateTests({ getService }: FtrProviderContext) { rule_type_id: 'test.throw', schedule: { interval: '1s' }, + tags: ['b', 'c', 'd'], }, 'error' ); @@ -202,6 +207,7 @@ export default function createAggregateTests({ getService }: FtrProviderContext) ruleSnoozedStatus: { snoozed: 0, }, + ruleTags: ['a', 'b', 'c', 'd', 'f'], }); }); }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts index 5777b4978894f..529e04c184740 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import uuid from 'uuid'; -import { IValidatedEvent } from '@kbn/event-log-plugin/server'; +import { IValidatedEvent, nanosToMillis } from '@kbn/event-log-plugin/server'; import { Spaces } from '../../scenarios'; import { getUrlPrefix, @@ -18,8 +18,6 @@ import { } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; -const NANOS_IN_MILLIS = 1000 * 1000; - // eslint-disable-next-line import/no-default-export export default function eventLogTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -403,10 +401,12 @@ export default function eventLogTests({ getService }: FtrProviderContext) { expect(event?.kibana?.alert?.rule?.execution?.metrics?.number_of_searches).to.be( numSearches ); - const esSearchDuration = - event?.kibana?.alert?.rule?.execution?.metrics?.es_search_duration_ms; - const totalSearchDuration = - event?.kibana?.alert?.rule?.execution?.metrics?.total_search_duration_ms; + const esSearchDuration = Number( + event?.kibana?.alert?.rule?.execution?.metrics?.es_search_duration_ms + ); + const totalSearchDuration = Number( + event?.kibana?.alert?.rule?.execution?.metrics?.total_search_duration_ms + ); expect(esSearchDuration).not.to.be(undefined); expect(totalSearchDuration).not.to.be(undefined); @@ -727,6 +727,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { category: response.body.rule_type_id, license: 'basic', ruleset: 'alertsFixture', + name: 'abc', }, consumer: 'alertsFixture', numActiveAlerts: 0, @@ -861,15 +862,13 @@ export function validateEvent(event: IValidatedEvent, params: ValidateEventLogPa const dateNow = Date.now(); if (duration !== undefined) { - expect(typeof duration).to.be('number'); + expect(typeof duration).to.be('string'); expect(eventStart).to.be.ok(); if (shouldHaveEventEnd !== false) { expect(eventEnd).to.be.ok(); - const durationDiff = Math.abs( - Math.round(duration! / NANOS_IN_MILLIS) - (eventEnd - eventStart) - ); + const durationDiff = Math.abs(nanosToMillis(duration!) - (eventEnd - eventStart)); // account for rounding errors expect(durationDiff < 1).to.equal(true); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log_alerts.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log_alerts.ts index c65ffb1bef5ac..46da0e597e66a 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log_alerts.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log_alerts.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { IValidatedEvent } from '@kbn/event-log-plugin/server'; +import { IValidatedEvent, nanosToMillis } from '@kbn/event-log-plugin/server'; import { Spaces } from '../../scenarios'; import { getUrlPrefix, getTestRuleData, ObjectRemover, getEventLog } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; @@ -91,7 +91,7 @@ export default function eventLogAlertTests({ getService }: FtrProviderContext) { const currentAlertSpan: { alertId?: string; start?: string; - durationToDate?: number; + durationToDate?: string; } = {}; for (let i = 0; i < instanceEvents.length; ++i) { switch (instanceEvents[i]?.event?.action) { @@ -102,7 +102,7 @@ export default function eventLogAlertTests({ getService }: FtrProviderContext) { currentAlertSpan.alertId = instanceEvents[i]?.kibana?.alerting?.instance_id; currentAlertSpan.start = instanceEvents[i]?.event?.start; - currentAlertSpan.durationToDate = instanceEvents[i]?.event?.duration; + currentAlertSpan.durationToDate = `${instanceEvents[i]?.event?.duration}`; break; case 'active-instance': @@ -110,12 +110,13 @@ export default function eventLogAlertTests({ getService }: FtrProviderContext) { expect(instanceEvents[i]?.event?.start).to.equal(currentAlertSpan.start); expect(instanceEvents[i]?.event?.end).to.be(undefined); - if (instanceEvents[i]?.event?.duration! !== 0) { - expect(instanceEvents[i]?.event?.duration! > currentAlertSpan.durationToDate!).to.be( - true - ); + if (instanceEvents[i]?.event?.duration! !== '0') { + expect( + BigInt(instanceEvents[i]?.event?.duration!) > + BigInt(currentAlertSpan.durationToDate!) + ).to.be(true); } - currentAlertSpan.durationToDate = instanceEvents[i]?.event?.duration; + currentAlertSpan.durationToDate = `${instanceEvents[i]?.event?.duration}`; break; case 'recovered-instance': @@ -125,7 +126,7 @@ export default function eventLogAlertTests({ getService }: FtrProviderContext) { expect( new Date(instanceEvents[i]?.event?.end!).valueOf() - new Date(instanceEvents[i]?.event?.start!).valueOf() - ).to.equal(instanceEvents[i]?.event?.duration! / 1000 / 1000); + ).to.equal(nanosToMillis(instanceEvents[i]?.event?.duration!)); break; } } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_execution_log.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_execution_log.ts index 5e9387ad4f0f9..6ae75c71d3bcf 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_execution_log.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_execution_log.ts @@ -419,7 +419,7 @@ export default function createGetExecutionLogTests({ getService }: FtrProviderCo for (const errors of response.body.errors) { expect(errors.type).to.equal('actions'); expect(errors.message).to.equal( - `action execution failure: test.throw:${createdConnector.id}: connector that throws - an error occurred while running the action executor: this action is intended to fail` + `action execution failure: test.throw:${createdConnector.id}: connector that throws - an error occurred while running the action: this action is intended to fail` ); } }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts index 4622e84081506..999b0a5f4c4f5 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts @@ -419,6 +419,18 @@ export default function createGetTests({ getService }: FtrProviderContext) { }); }); + it('8.2.0 migrates existing esQuery alerts to contain searchType param', async () => { + const response = await es.get<{ alert: RawRule }>( + { + index: '.kibana', + id: 'alert:776cb5c0-ad1e-11ec-ab9e-5f5932f4fad8', + }, + { meta: true } + ); + expect(response.statusCode).to.equal(200); + expect(response.body._source?.alert?.params.searchType).to.eql('esQuery'); + }); + it('8.3.0 removes internal tags in Security Solution rule', async () => { const response = await es.get<{ alert: RawRule }>( { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/index.ts index 88e5e0740789f..424deaaf83815 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/index.ts @@ -11,8 +11,6 @@ import { Spaces } from '../scenarios'; // eslint-disable-next-line import/no-default-export export default function alertingApiIntegrationTests({ loadTestFile }: FtrProviderContext) { describe('alerting api integration spaces only', function () { - this.tags('ciGroup12'); - loadTestFile(require.resolve('./actions')); loadTestFile(require.resolve('./alerting')); loadTestFile(require.resolve('./action_task_params')); diff --git a/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/index.ts b/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/index.ts index bb581a7f61cdb..757f2793b239f 100644 --- a/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/index.ts @@ -11,8 +11,6 @@ import { Spaces } from '../scenarios'; // eslint-disable-next-line import/no-default-export export default function alertingApiIntegrationTests({ loadTestFile }: FtrProviderContext) { describe('alerting api integration spaces only legacy configuration', function () { - this.tags('ciGroup12'); - loadTestFile(require.resolve('./actions/builtin_action_types/webhook')); }); } diff --git a/x-pack/test/api_integration/apis/index.ts b/x-pack/test/api_integration/apis/index.ts index ec964f97922ad..645cc81560682 100644 --- a/x-pack/test/api_integration/apis/index.ts +++ b/x-pack/test/api_integration/apis/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('apis', function () { - this.tags('ciGroup18'); - loadTestFile(require.resolve('./search')); loadTestFile(require.resolve('./es')); loadTestFile(require.resolve('./security')); diff --git a/x-pack/test/api_integration/apis/ml/index.ts b/x-pack/test/api_integration/apis/ml/index.ts index 6a36bf756cf19..c504811fcd0ce 100644 --- a/x-pack/test/api_integration/apis/ml/index.ts +++ b/x-pack/test/api_integration/apis/ml/index.ts @@ -12,7 +12,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { const ml = getService('ml'); describe('Machine Learning', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await ml.securityCommon.createMlRoles(); diff --git a/x-pack/test/api_integration/apis/security/index.ts b/x-pack/test/api_integration/apis/security/index.ts index eb81d8245dbff..b595894f157fc 100644 --- a/x-pack/test/api_integration/apis/security/index.ts +++ b/x-pack/test/api_integration/apis/security/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security', function () { - this.tags('ciGroup18'); - // Updates here should be mirrored in `./security_basic.ts` if tests // should also run under a basic license. 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 3dcb347126a97..1e7d8ed1ad4b2 100644 --- a/x-pack/test/api_integration/apis/security/security_basic.ts +++ b/x-pack/test/api_integration/apis/security/security_basic.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security (basic license)', function () { - this.tags('ciGroup6'); - // Updates here should be mirrored in `./index.js` if tests // should also run under a trial/platinum license. diff --git a/x-pack/test/api_integration/apis/security/security_trial.ts b/x-pack/test/api_integration/apis/security/security_trial.ts index be4ad252d408f..3786240b4e433 100644 --- a/x-pack/test/api_integration/apis/security/security_trial.ts +++ b/x-pack/test/api_integration/apis/security/security_trial.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security (trial license)', function () { - this.tags('ciGroup6'); - // THIS TEST NEEDS TO BE LAST. IT IS DESTRUCTIVE! IT REMOVES TRIAL LICENSE!!! loadTestFile(require.resolve('./license_downgrade')); }); diff --git a/x-pack/test/api_integration/apis/spaces/index.ts b/x-pack/test/api_integration/apis/spaces/index.ts index 3ca0040e39ec9..1ec1faac535ce 100644 --- a/x-pack/test/api_integration/apis/spaces/index.ts +++ b/x-pack/test/api_integration/apis/spaces/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('spaces', function () { - this.tags('ciGroup18'); - loadTestFile(require.resolve('./get_active_space')); loadTestFile(require.resolve('./saved_objects')); loadTestFile(require.resolve('./space_attributes')); diff --git a/x-pack/test/api_integration/config.ts b/x-pack/test/api_integration/config.ts index 4e47947aa29dc..8cc5fb6f57d42 100644 --- a/x-pack/test/api_integration/config.ts +++ b/x-pack/test/api_integration/config.ts @@ -10,7 +10,7 @@ import { services } from './services'; export async function getApiIntegrationConfig({ readConfigFile }: FtrConfigProviderContext) { const xPackFunctionalTestsConfig = await readConfigFile( - require.resolve('../functional/config.js') + require.resolve('../functional/config.base.js') ); return { diff --git a/x-pack/test/api_integration_basic/apis/index.ts b/x-pack/test/api_integration_basic/apis/index.ts index 9490d4c277675..31176c55ac3ca 100644 --- a/x-pack/test/api_integration_basic/apis/index.ts +++ b/x-pack/test/api_integration_basic/apis/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('apis', function () { - this.tags('ciGroup11'); - loadTestFile(require.resolve('./transform')); loadTestFile(require.resolve('./security_solution')); }); diff --git a/x-pack/test/apm_api_integration/tests/index.ts b/x-pack/test/apm_api_integration/tests/index.ts index 59764cf021c4a..e1312f589862c 100644 --- a/x-pack/test/apm_api_integration/tests/index.ts +++ b/x-pack/test/apm_api_integration/tests/index.ts @@ -14,8 +14,6 @@ export default function apmApiIntegrationTests({ getService, loadTestFile }: Ftr const registry = getService('registry'); describe('APM API tests', function () { - this.tags('ciGroup1'); - const tests = glob.sync('**/*.spec.ts', { cwd }); tests.forEach((test) => { describe(test, function () { diff --git a/x-pack/test/apm_api_integration/tests/suggestions/suggestions.spec.ts b/x-pack/test/apm_api_integration/tests/suggestions/suggestions.spec.ts index 74bdc860bfba3..692cd1c0cf7f1 100644 --- a/x-pack/test/apm_api_integration/tests/suggestions/suggestions.spec.ts +++ b/x-pack/test/apm_api_integration/tests/suggestions/suggestions.spec.ts @@ -9,12 +9,14 @@ import { SERVICE_NAME, TRANSACTION_TYPE, } from '@kbn/apm-plugin/common/elasticsearch_fieldnames'; +import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; import { FtrProviderContext } from '../../common/ftr_provider_context'; export default function suggestionsTests({ getService }: FtrProviderContext) { const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); const archiveName = 'apm_8.0.0'; + const { start, end } = archives_metadata[archiveName]; registry.when( 'suggestions when data is loaded', @@ -25,7 +27,7 @@ export default function suggestionsTests({ getService }: FtrProviderContext) { it('returns all environments', async () => { const { body } = await apmApiClient.readUser({ endpoint: 'GET /internal/apm/suggestions', - params: { query: { field: SERVICE_ENVIRONMENT, string: '' } }, + params: { query: { fieldName: SERVICE_ENVIRONMENT, fieldValue: '', start, end } }, }); expectSnapshot(body).toMatchInline(` @@ -43,7 +45,7 @@ export default function suggestionsTests({ getService }: FtrProviderContext) { it('returns items matching the string parameter', async () => { const { body } = await apmApiClient.readUser({ endpoint: 'GET /internal/apm/suggestions', - params: { query: { field: SERVICE_ENVIRONMENT, string: 'pr' } }, + params: { query: { fieldName: SERVICE_ENVIRONMENT, fieldValue: 'pr', start, end } }, }); expectSnapshot(body).toMatchInline(` @@ -62,7 +64,7 @@ export default function suggestionsTests({ getService }: FtrProviderContext) { it('returns all services', async () => { const { body } = await apmApiClient.readUser({ endpoint: 'GET /internal/apm/suggestions', - params: { query: { field: SERVICE_NAME, string: '' } }, + params: { query: { fieldName: SERVICE_NAME, fieldValue: '', start, end } }, }); expectSnapshot(body).toMatchInline(` @@ -86,7 +88,7 @@ export default function suggestionsTests({ getService }: FtrProviderContext) { it('returns items matching the string parameter', async () => { const { body } = await apmApiClient.readUser({ endpoint: 'GET /internal/apm/suggestions', - params: { query: { field: SERVICE_NAME, string: 'aud' } }, + params: { query: { fieldName: SERVICE_NAME, fieldValue: 'aud', start, end } }, }); expectSnapshot(body).toMatchInline(` @@ -105,7 +107,7 @@ export default function suggestionsTests({ getService }: FtrProviderContext) { it('returns all transaction types', async () => { const { body } = await apmApiClient.readUser({ endpoint: 'GET /internal/apm/suggestions', - params: { query: { field: TRANSACTION_TYPE, string: '' } }, + params: { query: { fieldName: TRANSACTION_TYPE, fieldValue: '', start, end } }, }); expectSnapshot(body).toMatchInline(` @@ -125,7 +127,7 @@ export default function suggestionsTests({ getService }: FtrProviderContext) { it('returns items matching the string parameter', async () => { const { body } = await apmApiClient.readUser({ endpoint: 'GET /internal/apm/suggestions', - params: { query: { field: TRANSACTION_TYPE, string: 'w' } }, + params: { query: { fieldName: TRANSACTION_TYPE, fieldValue: 'w', start, end } }, }); expectSnapshot(body).toMatchInline(` diff --git a/x-pack/test/banners_functional/config.ts b/x-pack/test/banners_functional/config.ts index 03f91dfbc34e2..83d0c4656572c 100644 --- a/x-pack/test/banners_functional/config.ts +++ b/x-pack/test/banners_functional/config.ts @@ -9,7 +9,9 @@ import { FtrConfigProviderContext } from '@kbn/test'; import { services, pageObjects } from './ftr_provider_context'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const kibanaFunctionalConfig = await readConfigFile(require.resolve('../functional/config.js')); + const kibanaFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); return { testFiles: [require.resolve('./tests')], diff --git a/x-pack/test/banners_functional/tests/index.ts b/x-pack/test/banners_functional/tests/index.ts index 301c872c746e1..8a26cb66ad569 100644 --- a/x-pack/test/banners_functional/tests/index.ts +++ b/x-pack/test/banners_functional/tests/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('banners - functional tests', function () { - this.tags('ciGroup2'); - loadTestFile(require.resolve('./global')); loadTestFile(require.resolve('./spaces')); }); diff --git a/x-pack/test/cases_api_integration/common/lib/mock.ts b/x-pack/test/cases_api_integration/common/lib/mock.ts index ce025a5681b4f..c77c4ff8d6451 100644 --- a/x-pack/test/cases_api_integration/common/lib/mock.ts +++ b/x-pack/test/cases_api_integration/common/lib/mock.ts @@ -17,6 +17,7 @@ import { CaseStatuses, CommentRequest, CommentRequestActionsType, + CaseSeverity, } from '@kbn/cases-plugin/common/api'; export const defaultUser = { email: null, full_name: null, username: 'elastic' }; @@ -29,6 +30,7 @@ export const postCaseReq: CasePostRequest = { description: 'This is a brand new case of a bad meanie defacing data', title: 'Super Bad Security Issue', tags: ['defacement'], + severity: CaseSeverity.LOW, connector: { id: 'none', name: 'none', @@ -85,6 +87,8 @@ export const postCaseResp = ( ...req, ...(id != null ? { id } : {}), comments: [], + duration: null, + severity: req.severity ?? CaseSeverity.LOW, totalAlerts: 0, totalComment: 0, closed_by: null, diff --git a/x-pack/test/cases_api_integration/common/lib/utils.ts b/x-pack/test/cases_api_integration/common/lib/utils.ts index 322bc3567211d..825f50b792cc6 100644 --- a/x-pack/test/cases_api_integration/common/lib/utils.ts +++ b/x-pack/test/cases_api_integration/common/lib/utils.ts @@ -48,9 +48,10 @@ import { ConnectorMappings, CasesByAlertId, CaseResolveResponse, - CaseMetricsResponse, + SingleCaseMetricsResponse, BulkCreateCommentRequest, CommentType, + CasesMetricsResponse, } from '@kbn/cases-plugin/common/api'; import { getCaseUserActionUrl } from '@kbn/cases-plugin/common/api/helpers'; import { SignalHit } from '@kbn/security-solution-plugin/server/lib/detection_engine/signals/types'; @@ -217,6 +218,23 @@ export const getServiceNowConnector = () => ({ }, }); +export const getServiceNowOAuthConnector = () => ({ + name: 'ServiceNow OAuth Connector', + connector_type_id: '.servicenow', + secrets: { + clientSecret: 'xyz', + privateKey: '-----BEGIN RSA PRIVATE KEY-----\nddddddd\n-----END RSA PRIVATE KEY-----', + }, + config: { + apiUrl: 'http://some.non.existent.com', + usesTableApi: false, + isOAuth: true, + clientId: 'abc', + userIdentifierValue: 'elastic', + jwtKeyId: 'def', + }, +}); + export const getJiraConnector = () => ({ name: 'Jira Connector', connector_type_id: '.jira', @@ -262,7 +280,7 @@ export const getResilientConnector = () => ({ }); export const getServiceNowSIRConnector = () => ({ - name: 'ServiceNow Connector', + name: 'ServiceNow SIR Connector', connector_type_id: '.servicenow-sir', secrets: { username: 'admin', @@ -992,13 +1010,13 @@ export const getCaseMetrics = async ({ }: { supertest: SuperTest.SuperTest; caseId: string; - features: string[]; + features: string[] | string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; -}): Promise => { +}): Promise => { const { body: metricsResponse } = await supertest .get(`${getSpaceUrlPrefix(auth?.space)}${CASES_URL}/metrics/${caseId}`) - .query({ features: JSON.stringify(features) }) + .query({ features }) .auth(auth.user.username, auth.user.password) .expect(expectedHttpCode); @@ -1229,3 +1247,46 @@ export const createCaseAndBulkCreateAttachments = async ({ return { theCase: patchedCase, attachments }; }; + +export const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +export const calculateDuration = (closedAt: string | null, createdAt: string | null): number => { + if (closedAt == null || createdAt == null) { + throw new Error('Dates are null'); + } + + const createdAtMillis = new Date(createdAt).getTime(); + const closedAtMillis = new Date(closedAt).getTime(); + + if (isNaN(createdAtMillis) || isNaN(closedAtMillis)) { + throw new Error('Dates are invalid'); + } + + if (closedAtMillis < createdAtMillis) { + throw new Error('Closed date is earlier than created date'); + } + + return Math.floor(Math.abs((closedAtMillis - createdAtMillis) / 1000)); +}; + +export const getCasesMetrics = async ({ + supertest, + features, + query = {}, + expectedHttpCode = 200, + auth = { user: superUser, space: null }, +}: { + supertest: SuperTest.SuperTest; + features: string[] | string; + query?: Record; + expectedHttpCode?: number; + auth?: { user: User; space: string | null }; +}): Promise => { + const { body: metricsResponse } = await supertest + .get(`${getSpaceUrlPrefix(auth?.space)}${CASES_URL}/metrics`) + .query({ features, ...query }) + .auth(auth.user.username, auth.user.password) + .expect(expectedHttpCode); + + return metricsResponse; +}; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/index.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/index.ts index b618cf5b4df68..714d7e0c6b219 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/index.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/index.ts @@ -11,9 +11,6 @@ import { createSpacesAndUsers, deleteSpacesAndUsers } from '../../../common/lib/ // eslint-disable-next-line import/no-default-export export default ({ loadTestFile, getService }: FtrProviderContext): void => { describe('cases security and spaces enabled: basic', function () { - // Fastest ciGroup for the moment. - this.tags('ciGroup27'); - before(async () => { await createSpacesAndUsers(getService); }); diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/find_cases.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/find_cases.ts index 657424e644be5..0381c46214669 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/find_cases.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/find_cases.ts @@ -7,7 +7,12 @@ import expect from '@kbn/expect'; import { CASES_URL } from '@kbn/cases-plugin/common/constants'; -import { CaseResponse, CaseStatuses, CommentType } from '@kbn/cases-plugin/common/api'; +import { + CaseResponse, + CaseSeverity, + CaseStatuses, + CommentType, +} from '@kbn/cases-plugin/common/api'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { @@ -117,6 +122,45 @@ export default ({ getService }: FtrProviderContext): void => { }); }); + it('filters by severity', async () => { + await createCase(supertest, postCaseReq); + const theCase = await createCase(supertest, postCaseReq); + const patchedCase = await updateCase({ + supertest, + params: { + cases: [ + { + id: theCase.id, + version: theCase.version, + severity: CaseSeverity.HIGH, + }, + ], + }, + }); + + const cases = await findCases({ supertest, query: { severity: CaseSeverity.HIGH } }); + + expect(cases).to.eql({ + ...findCasesResp, + total: 1, + cases: [patchedCase[0]], + count_open_cases: 1, + }); + }); + + it('filters by severity (none found)', async () => { + await createCase(supertest, postCaseReq); + await createCase(supertest, postCaseReq); + + const cases = await findCases({ supertest, query: { severity: CaseSeverity.CRITICAL } }); + + expect(cases).to.eql({ + ...findCasesResp, + total: 0, + cases: [], + }); + }); + it('filters by reporters', async () => { const postedCase = await createCase(supertest, postCaseReq); const cases = await findCases({ supertest, query: { reporters: 'elastic' } }); @@ -517,8 +561,15 @@ export default ({ getService }: FtrProviderContext): void => { } }); - it('returns a bad request on malformed parameter', async () => { - await findCases({ supertest, query: { from: '<' }, expectedHttpCode: 400 }); + it('escapes correctly', async () => { + const cases = await findCases({ + supertest, + query: { from: '2022-03-15T10:16:56.252Z', to: '2022-03-20T10:16:56.252' }, + }); + + expect(cases.total).to.be(2); + expect(cases.count_open_cases).to.be(2); + expect(cases.cases.length).to.be(2); }); }); @@ -795,6 +846,55 @@ export default ({ getService }: FtrProviderContext): void => { ensureSavedObjectIsAuthorized(res.cases, 1, ['securitySolutionFixture']); }); }); + + describe('RBAC query filter', () => { + it('should return the correct cases when trying to query filter by severity', async () => { + await Promise.all([ + createCase( + supertestWithoutAuth, + getPostCaseRequest({ owner: 'securitySolutionFixture', severity: CaseSeverity.HIGH }), + 200, + { + user: obsSec, + space: 'space1', + } + ), + createCase( + supertestWithoutAuth, + getPostCaseRequest({ owner: 'securitySolutionFixture', severity: CaseSeverity.HIGH }), + 200, + { + user: obsSec, + space: 'space1', + } + ), + createCase( + supertestWithoutAuth, + getPostCaseRequest({ owner: 'observabilityFixture', severity: CaseSeverity.HIGH }), + 200, + { + user: obsOnly, + space: 'space1', + } + ), + ]); + + // User with permissions only to security solution should get only the security solution cases + const res = await findCases({ + supertest: supertestWithoutAuth, + query: { + severity: CaseSeverity.HIGH, + }, + auth: { + user: secOnly, + space: 'space1', + }, + }); + + // Only security solution cases are being returned + ensureSavedObjectIsAuthorized(res.cases, 2, ['securitySolutionFixture']); + }); + }); }); }); }; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/import_export.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/import_export.ts index 44da07a845ff7..2ce441c37e687 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/import_export.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/import_export.ts @@ -27,6 +27,7 @@ import { CommentUserAction, CreateCaseUserAction, CaseStatuses, + CaseSeverity, } from '@kbn/cases-plugin/common/api'; import { ObjectRemover as ActionsRemover } from '../../../../../alerting_api_integration/common/lib'; import { @@ -204,6 +205,10 @@ const expectExportToHaveCaseSavedObject = ( expect(createdCaseSO.attributes.connector.name).to.eql(caseRequest.connector.name); expect(createdCaseSO.attributes.connector.fields).to.eql([]); expect(createdCaseSO.attributes.settings).to.eql(caseRequest.settings); + expect(createdCaseSO.attributes.status).to.eql(CaseStatuses.open); + expect(createdCaseSO.attributes.severity).to.eql(CaseSeverity.LOW); + expect(createdCaseSO.attributes.duration).to.eql(null); + expect(createdCaseSO.attributes.tags).to.eql(caseRequest.tags); }; const expectExportToHaveUserActions = (objects: SavedObject[], caseRequest: CasePostRequest) => { @@ -239,6 +244,7 @@ const expectCaseCreateUserAction = ( expect(restParsedCreateCase).to.eql({ ...restCreateCase, status: CaseStatuses.open, + severity: CaseSeverity.LOW, }); expect(restParsedConnector).to.eql(restConnector); }; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/migrations.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/migrations.ts index d844cbae1d79e..4d4b9d45b6717 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/migrations.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/migrations.ts @@ -368,5 +368,64 @@ export default function createGetTests({ getService }: FtrProviderContext) { expect(caseInfo).not.to.have.property('type'); }); }); + + describe('8.3.0', () => { + before(async () => { + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_duration.json' + ); + }); + + after(async () => { + await kibanaServer.importExport.unload( + 'x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_duration.json' + ); + await deleteAllCaseItems(es); + }); + + describe('adding duration', () => { + it('calculates the correct duration for closed cases', async () => { + const caseInfo = await getCase({ + supertest, + caseId: '4537b380-a512-11ec-b92f-859b9e89e434', + }); + + expect(caseInfo).to.have.property('duration'); + expect(caseInfo.duration).to.be(120); + }); + + it('sets the duration to null to open cases', async () => { + const caseInfo = await getCase({ + supertest, + caseId: '7537b580-a512-11ec-b94f-85979e89e434', + }); + + expect(caseInfo).to.have.property('duration'); + expect(caseInfo.duration).to.be(null); + }); + + it('sets the duration to null to in-progress cases', async () => { + const caseInfo = await getCase({ + supertest, + caseId: '1537b580-a512-11ec-b94f-85979e89e434', + }); + + expect(caseInfo).to.have.property('duration'); + expect(caseInfo.duration).to.be(null); + }); + }); + + describe('add severity', () => { + it('adds the severity field for existing documents', async () => { + const caseInfo = await getCase({ + supertest, + caseId: '4537b380-a512-11ec-b92f-859b9e89e434', + }); + + expect(caseInfo).to.have.property('severity'); + expect(caseInfo.severity).to.be('low'); + }); + }); + }); }); } diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts index 8e25ee9225493..80dffef7cd3ee 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts @@ -10,6 +10,7 @@ import { ALERT_WORKFLOW_STATUS } from '@kbn/rule-data-utils'; import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '@kbn/security-solution-plugin/common/constants'; import { + CaseSeverity, CasesResponse, CaseStatuses, CommentType, @@ -34,6 +35,8 @@ import { removeServerGeneratedPropertiesFromUserAction, findCases, superUserSpace1Auth, + delay, + calculateDuration, } from '../../../../common/lib/utils'; import { createSignalsIndex, @@ -111,9 +114,11 @@ export default ({ getService }: FtrProviderContext): void => { const userActions = await getCaseUserActions({ supertest, caseID: postedCase.id }); const statusUserAction = removeServerGeneratedPropertiesFromUserAction(userActions[1]); const data = removeServerGeneratedPropertiesFromCase(patchedCases[0]); + const { duration, ...dataWithoutDuration } = data; + const { duration: resDuration, ...resWithoutDuration } = postCaseResp(); - expect(data).to.eql({ - ...postCaseResp(), + expect(dataWithoutDuration).to.eql({ + ...resWithoutDuration, status: CaseStatuses.closed, closed_by: defaultUser, updated_by: defaultUser, @@ -166,6 +171,34 @@ export default ({ getService }: FtrProviderContext): void => { }); }); + it('should patch the severity of a case correctly', async () => { + const postedCase = await createCase(supertest, postCaseReq); + + // the default severity + expect(postedCase.severity).equal(CaseSeverity.LOW); + + const patchedCases = await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + severity: CaseSeverity.MEDIUM, + }, + ], + }, + }); + + const data = removeServerGeneratedPropertiesFromCase(patchedCases[0]); + + expect(data).to.eql({ + ...postCaseResp(), + severity: CaseSeverity.MEDIUM, + updated_by: defaultUser, + }); + }); + it('should patch a case with new connector', async () => { const postedCase = await createCase(supertest, postCaseReq); const patchedCases = await updateCase({ @@ -198,6 +231,65 @@ export default ({ getService }: FtrProviderContext): void => { updated_by: defaultUser, }); }); + + describe('duration', () => { + it('updates the duration correctly when the case closes', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await delay(1000); + + const patchedCases = await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + status: CaseStatuses.closed, + }, + ], + }, + }); + + const duration = calculateDuration(patchedCases[0].closed_at, postedCase.created_at); + expect(duration).to.be(patchedCases[0].duration); + }); + + for (const status of [CaseStatuses.open, CaseStatuses['in-progress']]) { + it(`sets the duration to null when the case status changes to ${status}`, async () => { + const postedCase = await createCase(supertest, postCaseReq); + + const closedCases = await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + status: CaseStatuses.closed, + }, + ], + }, + }); + + expect(closedCases[0].duration).to.not.be(null); + + const openCases = await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: closedCases[0].version, + status, + }, + ], + }, + }); + + expect(openCases[0].duration).to.be(null); + }); + } + }); }); describe('unhappy path', () => { @@ -234,6 +326,22 @@ export default ({ getService }: FtrProviderContext): void => { }); }); + it('400s when a wrong severity value is passed', async () => { + await updateCase({ + supertest, + params: { + cases: [ + { + version: 'version', + // @ts-expect-error + severity: 'wont-do', + }, + ], + }, + expectedHttpCode: 400, + }); + }); + it('400s when id is missing', async () => { await updateCase({ supertest, diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/post_case.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/post_case.ts index 9cd986a032b24..d4b52ff6f3394 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/post_case.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/post_case.ts @@ -12,6 +12,7 @@ import { ConnectorTypes, ConnectorJiraTypeFields, CaseStatuses, + CaseSeverity, } from '@kbn/cases-plugin/common/api'; import { getPostCaseRequest, postCaseResp, defaultUser } from '../../../../common/lib/mock'; import { @@ -102,6 +103,32 @@ export default ({ getService }: FtrProviderContext): void => { ); }); + it('should post a case without severity', async () => { + const postedCase = await createCase(supertest, getPostCaseRequest()); + const data = removeServerGeneratedPropertiesFromCase(postedCase); + + expect(data).to.eql(postCaseResp(null, getPostCaseRequest())); + }); + + it('should post a case with severity', async () => { + const postedCase = await createCase( + supertest, + getPostCaseRequest({ + severity: CaseSeverity.HIGH, + }) + ); + const data = removeServerGeneratedPropertiesFromCase(postedCase); + + expect(data).to.eql( + postCaseResp( + null, + getPostCaseRequest({ + severity: CaseSeverity.HIGH, + }) + ) + ); + }); + it('should create a user action when creating a case', async () => { const postedCase = await createCase(supertest, getPostCaseRequest()); const userActions = await getCaseUserActions({ supertest, caseID: postedCase.id }); @@ -122,6 +149,7 @@ export default ({ getService }: FtrProviderContext): void => { settings: postedCase.settings, owner: postedCase.owner, status: CaseStatuses.open, + severity: CaseSeverity.LOW, }, }); }); @@ -207,6 +235,11 @@ export default ({ getService }: FtrProviderContext): void => { await supertest.post(CASES_URL).set('kbn-xsrf', 'true').send(caseWithoutTags).expect(400); }); + it('400s when passing a wrong severity value', async () => { + // @ts-expect-error + await createCase(supertest, { ...getPostCaseRequest(), severity: 'very-severe' }, 400); + }); + it('400s if you passing status for a new case', async () => { const req = getPostCaseRequest(); diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/status/get_status.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/status/get_status.ts index 1a9671385a36c..4b7a11166ed16 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/status/get_status.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/status/get_status.ts @@ -113,8 +113,17 @@ export default ({ getService }: FtrProviderContext): void => { } }); - it('returns a bad request on malformed parameter', async () => { - await getAllCasesStatuses({ supertest, query: { from: '<' }, expectedHttpCode: 400 }); + it('escapes correctly', async () => { + const statuses = await getAllCasesStatuses({ + supertest, + query: { from: '2022-03-15T10:16:56.252Z', to: '2022-03-20T10:16:56.252' }, + }); + + expect(statuses).to.eql({ + count_open_cases: 2, + count_closed_cases: 0, + count_in_progress_cases: 0, + }); }); }); diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/index.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/index.ts index 25f39164f7c28..93bb948265ba0 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/index.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/index.ts @@ -37,6 +37,7 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./metrics/get_case_metrics_alerts')); loadTestFile(require.resolve('./metrics/get_case_metrics_actions')); loadTestFile(require.resolve('./metrics/get_case_metrics_connectors')); + loadTestFile(require.resolve('./metrics/get_cases_metrics')); /** * Internal routes diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/metrics/get_case_metrics.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/metrics/get_case_metrics.ts index cf1b842db0fe7..d08a00ab4f088 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/metrics/get_case_metrics.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/metrics/get_case_metrics.ts @@ -30,6 +30,24 @@ export default ({ getService }: FtrProviderContext): void => { const supertestWithoutAuth = getService('supertestWithoutAuth'); describe('case metrics', () => { + it('accepts the features as string', async () => { + const newCase = await createCase(supertest, getPostCaseRequest()); + + const metrics = await getCaseMetrics({ + supertest, + caseId: newCase.id, + features: 'connectors', + }); + + expect(metrics).to.eql({ + connectors: { + total: 0, + }, + }); + + await deleteAllCaseItems(es); + }); + describe('closed case from kbn archive', () => { const closedCaseId = 'e49ad6e0-cf9d-11eb-a603-13e7747d215z'; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/metrics/get_cases_metrics.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/metrics/get_cases_metrics.ts new file mode 100644 index 0000000000000..e3e0c2cb20570 --- /dev/null +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/metrics/get_cases_metrics.ts @@ -0,0 +1,244 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { CaseStatuses } from '@kbn/cases-plugin/common/api'; +import { + secOnly, + obsOnlyRead, + secOnlyRead, + noKibanaPrivileges, + superUser, + globalRead, + obsSecRead, + obsSec, +} from '../../../../common/lib/authentication/users'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { + createCase, + deleteAllCaseItems, + getCasesMetrics, + updateCase, +} from '../../../../common/lib/utils'; +import { getPostCaseRequest } from '../../../../common/lib/mock'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext): void => { + const es = getService('es'); + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const kibanaServer = getService('kibanaServer'); + + describe('all cases metrics', () => { + it('accepts the features as string', async () => { + const metrics = await getCasesMetrics({ + supertest, + features: 'mttr', + }); + + expect(metrics).to.eql({ mttr: 0 }); + + await deleteAllCaseItems(es); + }); + + describe('MTTR', () => { + it('responses with zero if there are no cases', async () => { + const metrics = await getCasesMetrics({ + supertest, + features: ['mttr'], + }); + + expect(metrics).to.eql({ mttr: 0 }); + }); + + it('responses with zero if there are only open case and in-progress cases', async () => { + await createCase(supertest, getPostCaseRequest()); + const theCase = await createCase(supertest, getPostCaseRequest()); + + await updateCase({ + supertest, + params: { + cases: [ + { + id: theCase.id, + version: theCase.version, + status: CaseStatuses['in-progress'], + }, + ], + }, + }); + + const metrics = await getCasesMetrics({ + supertest, + features: ['mttr'], + }); + + expect(metrics).to.eql({ mttr: 0 }); + }); + + describe('closed and open cases from kbn archive', () => { + before(async () => { + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/cases/8.3.0/all_cases_metrics.json' + ); + }); + + after(async () => { + await kibanaServer.importExport.unload( + 'x-pack/test/functional/fixtures/kbn_archiver/cases/8.3.0/all_cases_metrics.json' + ); + await deleteAllCaseItems(es); + }); + + it('should calculate the mttr correctly across all cases', async () => { + const metrics = await getCasesMetrics({ + supertest, + features: ['mttr'], + }); + + expect(metrics).to.eql({ mttr: 220 }); + }); + + it('should respects the range parameters', async () => { + const metrics = await getCasesMetrics({ + supertest, + features: ['mttr'], + query: { + from: '2022-04-28', + to: '2022-04-29', + }, + }); + + expect(metrics).to.eql({ mttr: 90 }); + }); + }); + }); + + describe('rbac', () => { + before(async () => { + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/cases/8.3.0/all_cases_metrics.json', + { space: 'space1' } + ); + }); + + after(async () => { + await kibanaServer.importExport.unload( + 'x-pack/test/functional/fixtures/kbn_archiver/cases/8.3.0/all_cases_metrics.json', + { space: 'space1' } + ); + await deleteAllCaseItems(es); + }); + + it('should calculate the mttr correctly only for the cases the user has access to', async () => { + for (const scenario of [ + { + user: globalRead, + expectedMetrics: { mttr: 220 }, + owners: ['securitySolutionFixture', 'observabilityFixture'], + }, + { + user: superUser, + expectedMetrics: { mttr: 220 }, + owners: ['securitySolutionFixture', 'observabilityFixture'], + }, + { + user: secOnlyRead, + expectedMetrics: { mttr: 250 }, + owners: ['securitySolutionFixture'], + }, + { user: obsOnlyRead, expectedMetrics: { mttr: 160 }, owners: ['observabilityFixture'] }, + { + user: obsSecRead, + expectedMetrics: { mttr: 220 }, + owners: ['securitySolutionFixture', 'observabilityFixture'], + }, + ]) { + const metrics = await getCasesMetrics({ + supertest: supertestWithoutAuth, + features: ['mttr'], + auth: { + user: scenario.user, + space: 'space1', + }, + }); + + expect(metrics).to.eql(scenario.expectedMetrics); + } + }); + + for (const scenario of [ + { user: noKibanaPrivileges, space: 'space1' }, + { user: secOnly, space: 'space2' }, + ]) { + it(`User ${scenario.user.username} with role(s) ${scenario.user.roles.join()} and space ${ + scenario.space + } - should NOT read a case`, async () => { + // user should not be able to read cases at the appropriate space + await getCasesMetrics({ + supertest: supertestWithoutAuth, + features: ['mttr'], + auth: { + user: scenario.user, + space: scenario.space, + }, + expectedHttpCode: 403, + }); + }); + } + + it('should respect the owner filter when having permissions', async () => { + const metrics = await getCasesMetrics({ + supertest: supertestWithoutAuth, + features: ['mttr'], + query: { + owner: 'securitySolutionFixture', + }, + auth: { + user: obsSec, + space: 'space1', + }, + }); + + expect(metrics).to.eql({ mttr: 250 }); + }); + + it('should return the correct cases when trying to exploit RBAC through the owner query parameter', async () => { + const metrics = await getCasesMetrics({ + supertest: supertestWithoutAuth, + features: ['mttr'], + query: { + owner: ['securitySolutionFixture', 'observabilityFixture'], + }, + auth: { + user: secOnly, + space: 'space1', + }, + }); + + expect(metrics).to.eql({ mttr: 250 }); + }); + + it('should respect the owner filter when using range queries', async () => { + const metrics = await getCasesMetrics({ + supertest: supertestWithoutAuth, + features: ['mttr'], + query: { + from: '2022-04-20', + to: '2022-04-30', + }, + auth: { + user: secOnly, + space: 'space1', + }, + }); + + expect(metrics).to.eql({ mttr: 250 }); + }); + }); + }); +}; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts index 283e6b0c5301b..aacb5f6c8ae17 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts @@ -9,6 +9,7 @@ import expect from '@kbn/expect'; import { CaseResponse, + CaseSeverity, CaseStatuses, CommentType, ConnectorTypes, @@ -106,6 +107,30 @@ export default ({ getService }: FtrProviderContext): void => { expect(statusUserAction.payload).to.eql({ status: 'closed' }); }); + it('creates a severity update user action when changing the severity', async () => { + const theCase = await createCase(supertest, postCaseReq); + await updateCase({ + supertest, + params: { + cases: [ + { + id: theCase.id, + version: theCase.version, + severity: CaseSeverity.HIGH, + }, + ], + }, + }); + + const userActions = await getCaseUserActions({ supertest, caseID: theCase.id }); + const statusUserAction = userActions[1]; + + expect(userActions.length).to.eql(2); + expect(statusUserAction.type).to.eql('severity'); + expect(statusUserAction.action).to.eql('update'); + expect(statusUserAction.payload).to.eql({ severity: 'high' }); + }); + it('creates a connector update user action', async () => { const newConnector = { id: '123', diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts index 26df77bfbc924..de72e0f343026 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts @@ -11,6 +11,7 @@ import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { ObjectRemover as ActionsRemover } from '../../../../../alerting_api_integration/common/lib'; import { getServiceNowConnector, + getServiceNowOAuthConnector, getJiraConnector, getResilientConnector, createConnector, @@ -31,6 +32,10 @@ export default ({ getService }: FtrProviderContext): void => { it('should return the correct connectors', async () => { const snConnector = await createConnector({ supertest, req: getServiceNowConnector() }); + const snOAuthConnector = await createConnector({ + supertest, + req: getServiceNowOAuthConnector(), + }); const emailConnector = await createConnector({ supertest, req: getEmailConnector() }); const jiraConnector = await createConnector({ supertest, req: getJiraConnector() }); const resilientConnector = await createConnector({ supertest, req: getResilientConnector() }); @@ -38,13 +43,15 @@ export default ({ getService }: FtrProviderContext): void => { actionsRemover.add('default', sir.id, 'action', 'actions'); actionsRemover.add('default', snConnector.id, 'action', 'actions'); + actionsRemover.add('default', snOAuthConnector.id, 'action', 'actions'); actionsRemover.add('default', emailConnector.id, 'action', 'actions'); actionsRemover.add('default', jiraConnector.id, 'action', 'actions'); actionsRemover.add('default', resilientConnector.id, 'action', 'actions'); const connectors = await getCaseConnectors({ supertest }); + const sortedConnectors = connectors.sort((a, b) => a.name.localeCompare(b.name)); - expect(connectors).to.eql([ + expect(sortedConnectors).to.eql([ { id: jiraConnector.id, actionTypeId: '.jira', @@ -90,6 +97,27 @@ export default ({ getService }: FtrProviderContext): void => { config: { apiUrl: 'http://some.non.existent.com', usesTableApi: false, + isOAuth: false, + clientId: null, + jwtKeyId: null, + userIdentifierValue: null, + }, + isPreconfigured: false, + isDeprecated: false, + isMissingSecrets: false, + referencedByCount: 0, + }, + { + id: snOAuthConnector.id, + actionTypeId: '.servicenow', + name: 'ServiceNow OAuth Connector', + config: { + apiUrl: 'http://some.non.existent.com', + usesTableApi: false, + isOAuth: true, + clientId: 'abc', + userIdentifierValue: 'elastic', + jwtKeyId: 'def', }, isPreconfigured: false, isDeprecated: false, @@ -99,10 +127,14 @@ export default ({ getService }: FtrProviderContext): void => { { id: sir.id, actionTypeId: '.servicenow-sir', - name: 'ServiceNow Connector', + name: 'ServiceNow SIR Connector', config: { apiUrl: 'http://some.non.existent.com', usesTableApi: false, + isOAuth: false, + clientId: null, + jwtKeyId: null, + userIdentifierValue: null, }, isPreconfigured: false, isDeprecated: false, diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/index.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/index.ts index 3c1ee84296270..e768c92d8c8af 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/index.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/index.ts @@ -19,23 +19,15 @@ export default ({ loadTestFile, getService }: FtrProviderContext): void => { await deleteSpacesAndUsers(getService); }); - describe('', function () { - this.tags('ciGroup13'); + // Trial + loadTestFile(require.resolve('./cases/push_case')); + loadTestFile(require.resolve('./cases/user_actions/get_all_user_actions')); + loadTestFile(require.resolve('./configure')); - // Trial - loadTestFile(require.resolve('./cases/push_case')); - loadTestFile(require.resolve('./cases/user_actions/get_all_user_actions')); - loadTestFile(require.resolve('./configure')); - }); - - describe('', function () { - this.tags('ciGroup25'); + // Common + loadTestFile(require.resolve('../common')); - // Common - loadTestFile(require.resolve('../common')); - - // NOTE: These need to be at the end because they could delete the .kibana index and inadvertently remove the users and spaces - loadTestFile(require.resolve('../common/migrations')); - }); + // NOTE: These need to be at the end because they could delete the .kibana index and inadvertently remove the users and spaces + loadTestFile(require.resolve('../common/migrations')); }); }; diff --git a/x-pack/test/cases_api_integration/spaces_only/tests/common/index.ts b/x-pack/test/cases_api_integration/spaces_only/tests/common/index.ts index 0b18a56bdcd11..a180d46d45edb 100644 --- a/x-pack/test/cases_api_integration/spaces_only/tests/common/index.ts +++ b/x-pack/test/cases_api_integration/spaces_only/tests/common/index.ts @@ -29,6 +29,8 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./configure/get_configure')); loadTestFile(require.resolve('./configure/patch_configure')); loadTestFile(require.resolve('./configure/post_configure')); + loadTestFile(require.resolve('./configure/post_configure')); + loadTestFile(require.resolve('./metrics/get_cases_metrics')); /** * Internal routes diff --git a/x-pack/test/cases_api_integration/spaces_only/tests/common/metrics/get_cases_metrics.ts b/x-pack/test/cases_api_integration/spaces_only/tests/common/metrics/get_cases_metrics.ts new file mode 100644 index 0000000000000..66fb3f4343e58 --- /dev/null +++ b/x-pack/test/cases_api_integration/spaces_only/tests/common/metrics/get_cases_metrics.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { + deleteAllCaseItems, + getAuthWithSuperUser, + getCasesMetrics, +} from '../../../../common/lib/utils'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext): void => { + const es = getService('es'); + const supertest = getService('supertest'); + const kibanaServer = getService('kibanaServer'); + const authSpace1 = getAuthWithSuperUser(); + + describe('all cases metrics', () => { + before(async () => { + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/cases/8.3.0/all_cases_metrics.json', + { space: 'space1' } + ); + + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/cases/8.3.0/all_cases_metrics.json', + { space: 'space2' } + ); + }); + + after(async () => { + await kibanaServer.importExport.unload( + 'x-pack/test/functional/fixtures/kbn_archiver/cases/8.3.0/all_cases_metrics.json', + { space: 'space1' } + ); + + await kibanaServer.importExport.unload( + 'x-pack/test/functional/fixtures/kbn_archiver/cases/8.3.0/all_cases_metrics.json', + { space: 'space2' } + ); + await deleteAllCaseItems(es); + }); + + describe('MTTR', () => { + it('should calculate the mttr correctly on space 1', async () => { + const metrics = await getCasesMetrics({ + supertest, + features: ['mttr'], + auth: authSpace1, + }); + + expect(metrics).to.eql({ mttr: 220 }); + }); + + it('should calculate the mttr correctly on space 2', async () => { + const authSpace2 = getAuthWithSuperUser('space2'); + const metrics = await getCasesMetrics({ + supertest, + features: ['mttr'], + auth: authSpace2, + }); + + expect(metrics).to.eql({ mttr: 220 }); + }); + }); + }); +}; diff --git a/x-pack/test/cases_api_integration/spaces_only/tests/trial/configure/get_connectors.ts b/x-pack/test/cases_api_integration/spaces_only/tests/trial/configure/get_connectors.ts index c4115b5c4902d..0ca47597e7b6b 100644 --- a/x-pack/test/cases_api_integration/spaces_only/tests/trial/configure/get_connectors.ts +++ b/x-pack/test/cases_api_integration/spaces_only/tests/trial/configure/get_connectors.ts @@ -11,6 +11,7 @@ import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { ObjectRemover as ActionsRemover } from '../../../../../alerting_api_integration/common/lib'; import { getServiceNowConnector, + getServiceNowOAuthConnector, getJiraConnector, getResilientConnector, createConnector, @@ -39,7 +40,11 @@ export default ({ getService }: FtrProviderContext): void => { req: getServiceNowConnector(), auth: authSpace1, }); - + const snOAuthConnector = await createConnector({ + supertest, + req: getServiceNowOAuthConnector(), + auth: authSpace1, + }); const emailConnector = await createConnector({ supertest, req: getEmailConnector(), @@ -66,13 +71,15 @@ export default ({ getService }: FtrProviderContext): void => { actionsRemover.add(space, sir.id, 'action', 'actions'); actionsRemover.add(space, snConnector.id, 'action', 'actions'); + actionsRemover.add(space, snOAuthConnector.id, 'action', 'actions'); actionsRemover.add(space, emailConnector.id, 'action', 'actions'); actionsRemover.add(space, jiraConnector.id, 'action', 'actions'); actionsRemover.add(space, resilientConnector.id, 'action', 'actions'); const connectors = await getCaseConnectors({ supertest, auth: authSpace1 }); + const sortedConnectors = connectors.sort((a, b) => a.name.localeCompare(b.name)); - expect(connectors).to.eql([ + expect(sortedConnectors).to.eql([ { id: jiraConnector.id, actionTypeId: '.jira', @@ -118,6 +125,27 @@ export default ({ getService }: FtrProviderContext): void => { config: { apiUrl: 'http://some.non.existent.com', usesTableApi: false, + isOAuth: false, + clientId: null, + jwtKeyId: null, + userIdentifierValue: null, + }, + isPreconfigured: false, + isDeprecated: false, + isMissingSecrets: false, + referencedByCount: 0, + }, + { + id: snOAuthConnector.id, + actionTypeId: '.servicenow', + name: 'ServiceNow OAuth Connector', + config: { + apiUrl: 'http://some.non.existent.com', + usesTableApi: false, + isOAuth: true, + clientId: 'abc', + userIdentifierValue: 'elastic', + jwtKeyId: 'def', }, isPreconfigured: false, isDeprecated: false, @@ -127,10 +155,14 @@ export default ({ getService }: FtrProviderContext): void => { { id: sir.id, actionTypeId: '.servicenow-sir', - name: 'ServiceNow Connector', + name: 'ServiceNow SIR Connector', config: { apiUrl: 'http://some.non.existent.com', usesTableApi: false, + isOAuth: false, + clientId: null, + jwtKeyId: null, + userIdentifierValue: null, }, isPreconfigured: false, isDeprecated: false, @@ -147,6 +179,12 @@ export default ({ getService }: FtrProviderContext): void => { auth: authSpace1, }); + const snOAuthConnector = await createConnector({ + supertest, + req: getServiceNowOAuthConnector(), + auth: authSpace1, + }); + const emailConnector = await createConnector({ supertest, req: getEmailConnector(), @@ -173,6 +211,7 @@ export default ({ getService }: FtrProviderContext): void => { actionsRemover.add(space, sir.id, 'action', 'actions'); actionsRemover.add(space, snConnector.id, 'action', 'actions'); + actionsRemover.add(space, snOAuthConnector.id, 'action', 'actions'); actionsRemover.add(space, emailConnector.id, 'action', 'actions'); actionsRemover.add(space, jiraConnector.id, 'action', 'actions'); actionsRemover.add(space, resilientConnector.id, 'action', 'actions'); diff --git a/x-pack/test/cases_api_integration/spaces_only/tests/trial/index.ts b/x-pack/test/cases_api_integration/spaces_only/tests/trial/index.ts index 346640aa6b9de..cbf0f010048ef 100644 --- a/x-pack/test/cases_api_integration/spaces_only/tests/trial/index.ts +++ b/x-pack/test/cases_api_integration/spaces_only/tests/trial/index.ts @@ -11,9 +11,6 @@ import { createSpaces, deleteSpaces } from '../../../common/lib/authentication'; // eslint-disable-next-line import/no-default-export export default ({ loadTestFile, getService }: FtrProviderContext): void => { describe('cases spaces only enabled: trial', function () { - // Fastest ciGroup for the moment. - this.tags('ciGroup5'); - before(async () => { await createSpaces(getService); }); diff --git a/x-pack/test/cloud_integration/config.ts b/x-pack/test/cloud_integration/config.ts index 102d276b34584..0074565875845 100644 --- a/x-pack/test/cloud_integration/config.ts +++ b/x-pack/test/cloud_integration/config.ts @@ -25,7 +25,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { require.resolve('../../../test/common/config.js') ); const kibanaFunctionalConfig = await readConfigFile( - require.resolve('../../../test/functional/config.js') + require.resolve('../../../test/functional/config.base.js') ); const kibanaPort = kibanaFunctionalConfig.get('servers.kibana.port'); diff --git a/x-pack/test/common/services/bsearch_secure.ts b/x-pack/test/common/services/bsearch_secure.ts index 458e0878e187f..f60dc6aa4c3cd 100644 --- a/x-pack/test/common/services/bsearch_secure.ts +++ b/x-pack/test/common/services/bsearch_secure.ts @@ -79,12 +79,11 @@ export const BSecureSearchFactory = (retry: RetryService) => ({ .set('kbn-xsrf', 'true') .send(options); } - if (result.status === 500 || result.status === 200) { + if ((result.status === 500 || result.status === 200) && result.body) { return result; } throw new Error('try again'); }); - if (body.isRunning) { const result = await retry.try(async () => { const resp = await supertestWithoutAuth diff --git a/x-pack/test/detection_engine_api_integration/basic/config.ts b/x-pack/test/detection_engine_api_integration/basic/config.ts index 0c5c7d1649f84..26fdc62e0ec52 100644 --- a/x-pack/test/detection_engine_api_integration/basic/config.ts +++ b/x-pack/test/detection_engine_api_integration/basic/config.ts @@ -8,7 +8,10 @@ import { createTestConfig } from '../common/config'; // eslint-disable-next-line import/no-default-export -export default createTestConfig('basic', { - license: 'basic', - ssl: true, -}); +export default createTestConfig( + { + license: 'basic', + ssl: true, + }, + [require.resolve('./tests')] +); diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/index.ts b/x-pack/test/detection_engine_api_integration/basic/tests/index.ts index 1a5ea8de935b4..349d33c89fdf3 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/index.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/index.ts @@ -10,8 +10,6 @@ import { FtrProviderContext } from '../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default ({ loadTestFile }: FtrProviderContext): void => { describe('detection engine api basic license', function () { - this.tags('ciGroup1'); - loadTestFile(require.resolve('./add_prepackaged_rules')); loadTestFile(require.resolve('./create_rules')); loadTestFile(require.resolve('./create_rules_bulk')); diff --git a/x-pack/test/detection_engine_api_integration/common/config.ts b/x-pack/test/detection_engine_api_integration/common/config.ts index 96a83d2c3a427..a95cb937d4cd9 100644 --- a/x-pack/test/detection_engine_api_integration/common/config.ts +++ b/x-pack/test/detection_engine_api_integration/common/config.ts @@ -31,7 +31,7 @@ const enabledActionTypes = [ 'test.rate-limit', ]; -export function createTestConfig(name: string, options: CreateTestConfigOptions) { +export function createTestConfig(options: CreateTestConfigOptions, testFiles?: string[]) { const { license = 'trial', ssl = false } = options; return async ({ readConfigFile }: FtrConfigProviderContext) => { @@ -47,7 +47,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) }; return { - testFiles: [require.resolve(`../${name}/tests/`)], + testFiles, servers, services, junit: { diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/README.md b/x-pack/test/detection_engine_api_integration/security_and_spaces/README.md new file mode 100644 index 0000000000000..ca10827803c65 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/README.md @@ -0,0 +1,33 @@ +# What are all these groups? + +These tests take a while so they have been broken up into groups with their own `config.ts` and `index.ts` file, causing each of these groups to be independent bundles of tests which can be run on some worker in CI without taking an incredible amount of time. + +Want to change the groups to something more logical? Have fun! Just make sure that each group executes on CI in less than 10 minutes or so. We don't currently have any mechanism for validating this right now, you just need to look at the times in the log output on CI, but we'll be working on tooling for making this information more accessible soon. + +- Kibana Operations + +# Rule Exception List Tests + +These tests are currently in group7-9. + +These are tests for rule exception lists where we test each data type + +- date +- double +- float +- integer +- ip +- keyword +- long +- text + +Against the operator types of: + +- "is" +- "is not" +- "is one of" +- "is not one of" +- "exists" +- "does not exist" +- "is in list" +- "is not in list" diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/config.base.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/config.base.ts new file mode 100644 index 0000000000000..ed72e1faa682c --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/config.base.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 { createTestConfig } from '../common/config'; + +// eslint-disable-next-line import/no-default-export +export default createTestConfig({ + license: 'trial', + ssl: true, +}); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/config.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/config.ts deleted file mode 100644 index 78203525b887a..0000000000000 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/config.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { createTestConfig } from '../common/config'; - -// eslint-disable-next-line import/no-default-export -export default createTestConfig('security_and_spaces', { - license: 'trial', - ssl: true, -}); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/add_actions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/add_actions.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/add_actions.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/add_actions.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/add_prepackaged_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/add_prepackaged_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/add_prepackaged_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/add_prepackaged_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/aliases.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/aliases.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/aliases.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/aliases.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/check_privileges.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/check_privileges.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/check_privileges.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/check_privileges.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/config.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/config.ts new file mode 100644 index 0000000000000..2430b8f2148d9 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/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 { FtrConfigProviderContext } from '@kbn/test'; + +// eslint-disable-next-line import/no-default-export +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../config.base.ts')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_index.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_index.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_index.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_ml.ts similarity index 98% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_ml.ts index 20c914e17ce70..94d65933bc9dc 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_ml.ts @@ -92,7 +92,8 @@ export default ({ getService }: FtrProviderContext) => { return body; } - describe('Generating signals from ml anomalies', () => { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/125033 + describe.skip('Generating signals from ml anomalies', () => { before(async () => { // Order is critical here: auditbeat data must be loaded before attempting to start the ML job, // as the job looks for certain indices on start diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_signals_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_signals_migrations.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_signals_migrations.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_signals_migrations.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_threat_matching.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_threat_matching.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules.ts similarity index 99% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules.ts index 69cbd9fd4d4c7..f138bad4b9d2c 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules.ts @@ -20,6 +20,7 @@ import { getSimpleRuleOutput, getSimpleRuleOutputWithoutRuleId, getSimpleRuleWithoutRuleId, + getSlackAction, getWebHookAction, removeServerGeneratedProperties, removeServerGeneratedPropertiesIncludingRuleId, @@ -148,7 +149,7 @@ export default ({ getService }: FtrProviderContext): void => { const { body: hookAction } = await supertest .post('/api/actions/action') .set('kbn-xsrf', 'true') - .send(getWebHookAction()) + .send(getSlackAction()) .expect(200); // create a rule without actions diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules_bulk.ts similarity index 99% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules_bulk.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules_bulk.ts index 29f5d5edf49d9..d05dcb690994c 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules_bulk.ts @@ -20,6 +20,7 @@ import { getSimpleRuleOutput, getSimpleRuleOutputWithoutRuleId, getSimpleRuleWithoutRuleId, + getSlackAction, getWebHookAction, removeServerGeneratedProperties, removeServerGeneratedPropertiesIncludingRuleId, @@ -277,7 +278,7 @@ export default ({ getService }: FtrProviderContext): void => { const { body: hookAction } = await supertest .post('/api/actions/action') .set('kbn-xsrf', 'true') - .send(getWebHookAction()) + .send(getSlackAction()) .expect(200); // create a rule without actions @@ -318,12 +319,12 @@ export default ({ getService }: FtrProviderContext): void => { const { body: hookAction1 } = await supertest .post('/api/actions/action') .set('kbn-xsrf', 'true') - .send(getWebHookAction()) + .send(getSlackAction()) .expect(200); const { body: hookAction2 } = await supertest .post('/api/actions/action') .set('kbn-xsrf', 'true') - .send(getWebHookAction()) + .send(getSlackAction()) .expect(200); // create 2 rules without actions diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_signals_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_signals_migrations.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_signals_migrations.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_signals_migrations.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/export_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/export_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/finalize_signals_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/finalize_signals_migrations.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/finalize_signals_migrations.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/finalize_signals_migrations.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/find_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/find_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/generating_signals.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/generating_signals.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/get_prepackaged_rules_status.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/get_prepackaged_rules_status.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/get_prepackaged_rules_status.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/get_prepackaged_rules_status.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/get_rule_execution_events.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/get_rule_execution_events.ts new file mode 100644 index 0000000000000..9fadbd39f7704 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/get_rule_execution_events.ts @@ -0,0 +1,168 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import dateMath from '@kbn/datemath'; +import expect from '@kbn/expect'; +import moment from 'moment'; +import { set } from '@elastic/safer-lodash-set'; +import uuid from 'uuid'; +import { detectionEngineRuleExecutionEventsUrl } from '@kbn/security-solution-plugin/common/constants'; +import { RuleExecutionStatus } from '@kbn/security-solution-plugin/common/detection_engine/schemas/common'; + +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { + createRule, + createSignalsIndex, + deleteAllAlerts, + deleteAllEventLogExecutionEvents, + deleteSignalsIndex, + getRuleForSignalTesting, + indexEventLogExecutionEvents, + waitForEventLogExecuteComplete, + waitForRuleSuccessOrStatus, +} from '../../utils'; +import { failedGapExecution } from './template_data/execution_events'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const es = getService('es'); + const log = getService('log'); + + describe('Get Rule Execution Log Events', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); + await esArchiver.load('x-pack/test/functional/es_archives/security_solution/alias'); + await createSignalsIndex(supertest, log); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts'); + await esArchiver.unload('x-pack/test/functional/es_archives/security_solution/alias'); + await deleteSignalsIndex(supertest, log); + }); + + beforeEach(async () => { + await deleteAllAlerts(supertest, log); + await deleteAllEventLogExecutionEvents(es, log); + }); + + it('should return an error if rule does not exist', async () => { + const start = dateMath.parse('now-24h')?.utc().toISOString(); + const end = dateMath.parse('now', { roundUp: true })?.utc().toISOString(); + const response = await supertest + .get(detectionEngineRuleExecutionEventsUrl('1')) + .set('kbn-xsrf', 'true') + .query({ start, end }); + + expect(response.status).to.eql(404); + expect(response.text).to.eql( + '{"message":"Saved object [alert/1] not found","status_code":404}' + ); + }); + + it('should return execution events for a rule that has executed successfully', async () => { + const rule = getRuleForSignalTesting(['auditbeat-*']); + const { id } = await createRule(supertest, log, rule); + await waitForRuleSuccessOrStatus(supertest, log, id); + await waitForEventLogExecuteComplete(es, log, id); + + const start = dateMath.parse('now-24h')?.utc().toISOString(); + const end = dateMath.parse('now', { roundUp: true })?.utc().toISOString(); + const response = await supertest + .get(detectionEngineRuleExecutionEventsUrl(id)) + .set('kbn-xsrf', 'true') + .query({ start, end }); + + expect(response.status).to.eql(200); + expect(response.body.total).to.eql(1); + expect(response.body.events[0].duration_ms).to.greaterThan(0); + expect(response.body.events[0].search_duration_ms).to.greaterThan(0); + expect(response.body.events[0].schedule_delay_ms).to.greaterThan(0); + expect(response.body.events[0].indexing_duration_ms).to.greaterThan(0); + expect(response.body.events[0].gap_duration_s).to.eql(0); + expect(response.body.events[0].security_status).to.eql('succeeded'); + expect(response.body.events[0].security_message).to.eql('succeeded'); + }); + + it('should return execution events for a rule that has executed in a warning state', async () => { + const rule = getRuleForSignalTesting(['auditbeat-*', 'no-name-index']); + const { id } = await createRule(supertest, log, rule); + await waitForRuleSuccessOrStatus(supertest, log, id, RuleExecutionStatus['partial failure']); + await waitForEventLogExecuteComplete(es, log, id); + + const start = dateMath.parse('now-24h')?.utc().toISOString(); + const end = dateMath.parse('now', { roundUp: true })?.utc().toISOString(); + const response = await supertest + .get(detectionEngineRuleExecutionEventsUrl(id)) + .set('kbn-xsrf', 'true') + .query({ start, end }); + + expect(response.status).to.eql(200); + expect(response.body.total).to.eql(1); + expect(response.body.events[0].duration_ms).to.greaterThan(0); + expect(response.body.events[0].search_duration_ms).to.eql(0); + expect(response.body.events[0].schedule_delay_ms).to.greaterThan(0); + expect(response.body.events[0].indexing_duration_ms).to.eql(0); + expect(response.body.events[0].gap_duration_s).to.eql(0); + expect(response.body.events[0].security_status).to.eql('partial failure'); + expect( + response.body.events[0].security_message.startsWith( + 'Check privileges failed to execute ResponseError: index_not_found_exception: [index_not_found_exception] Reason: no such index [no-name-index]' + ) + ).to.eql(true); + }); + + it('should return execution events for a rule that has executed in a failure state with a gap', async () => { + const rule = getRuleForSignalTesting(['auditbeat-*'], uuid.v4(), false); + const { id } = await createRule(supertest, log, rule); + + const start = dateMath.parse('now')?.utc().toISOString(); + const end = dateMath.parse('now+24h', { roundUp: true })?.utc().toISOString(); + + // Create 5 timestamps (failedGapExecution.length) a minute apart to use in the templated data + const dateTimes = [...Array(failedGapExecution.length).keys()].map((i) => + moment(start) + .add(i + 1, 'm') + .toDate() + .toISOString() + ); + + const events = failedGapExecution.map((e, i) => { + set(e, '@timestamp', dateTimes[i]); + set(e, 'event.start', dateTimes[i]); + set(e, 'event.end', dateTimes[i]); + set(e, 'rule.id', id); + set(e, 'kibana.saved_objects[0].id', id); + return e; + }); + + await indexEventLogExecutionEvents(es, log, events); + await waitForEventLogExecuteComplete(es, log, id); + + const response = await supertest + .get(detectionEngineRuleExecutionEventsUrl(id)) + .set('kbn-xsrf', 'true') + .query({ start, end }); + + expect(response.status).to.eql(200); + expect(response.body.total).to.eql(1); + expect(response.body.events[0].duration_ms).to.eql(1545); + expect(response.body.events[0].search_duration_ms).to.eql(0); + expect(response.body.events[0].schedule_delay_ms).to.eql(544808); + expect(response.body.events[0].indexing_duration_ms).to.eql(0); + expect(response.body.events[0].gap_duration_s).to.eql(245); + expect(response.body.events[0].security_status).to.eql('failed'); + expect( + response.body.events[0].security_message.startsWith( + '4 minutes (244689ms) were not queried between this rule execution and the last execution, so signals may have been missed. Consider increasing your look behind time or adding more Kibana instances.' + ) + ).to.eql(true); + }); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/get_signals_migration_status.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/get_signals_migration_status.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/get_signals_migration_status.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/get_signals_migration_status.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/ignore_fields.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/ignore_fields.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/ignore_fields.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/ignore_fields.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_export_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/import_export_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_export_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/import_export_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/import_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/import_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/index.ts new file mode 100644 index 0000000000000..f7a96c2f496d8 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/index.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('detection engine api security and spaces enabled - Group 1', function () { + // !!NOTE: For new routes that do any updates on a rule, please ensure that you are including the legacy + // action migration code. We are monitoring legacy action telemetry to clean up once we see their + // existence being near 0. + + loadTestFile(require.resolve('./aliases')); + loadTestFile(require.resolve('./add_actions')); + loadTestFile(require.resolve('./update_actions')); + loadTestFile(require.resolve('./add_prepackaged_rules')); + loadTestFile(require.resolve('./check_privileges')); + loadTestFile(require.resolve('./create_index')); + loadTestFile(require.resolve('./create_rules')); + loadTestFile(require.resolve('./preview_rules')); + loadTestFile(require.resolve('./create_rules_bulk')); + loadTestFile(require.resolve('./create_ml')); + loadTestFile(require.resolve('./create_threat_matching')); + loadTestFile(require.resolve('./delete_rules')); + loadTestFile(require.resolve('./delete_rules_bulk')); + loadTestFile(require.resolve('./export_rules')); + loadTestFile(require.resolve('./find_rules')); + loadTestFile(require.resolve('./generating_signals')); + loadTestFile(require.resolve('./get_prepackaged_rules_status')); + loadTestFile(require.resolve('./get_rule_execution_events')); + loadTestFile(require.resolve('./import_rules')); + loadTestFile(require.resolve('./import_export_rules')); + loadTestFile(require.resolve('./legacy_actions_migrations')); + loadTestFile(require.resolve('./read_rules')); + loadTestFile(require.resolve('./resolve_read_rules')); + loadTestFile(require.resolve('./update_rules')); + loadTestFile(require.resolve('./update_rules_bulk')); + loadTestFile(require.resolve('./patch_rules_bulk')); + loadTestFile(require.resolve('./perform_bulk_action')); + loadTestFile(require.resolve('./patch_rules')); + loadTestFile(require.resolve('./read_privileges')); + loadTestFile(require.resolve('./open_close_signals')); + loadTestFile(require.resolve('./get_signals_migration_status')); + loadTestFile(require.resolve('./create_signals_migrations')); + loadTestFile(require.resolve('./finalize_signals_migrations')); + loadTestFile(require.resolve('./delete_signals_migrations')); + loadTestFile(require.resolve('./timestamps')); + loadTestFile(require.resolve('./runtime')); + loadTestFile(require.resolve('./throttle')); + loadTestFile(require.resolve('./ignore_fields')); + loadTestFile(require.resolve('./migrations')); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/legacy_actions_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/legacy_actions_migrations.ts new file mode 100644 index 0000000000000..ad6e45954e06a --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/legacy_actions_migrations.ts @@ -0,0 +1,322 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { + getLegacyActionSOById, + getLegacyActionNotificationSOById, + getRuleSOById, +} from '../../utils'; + +/** + * @deprecated Once the legacy notification system is removed, remove this test too. + */ +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const es = getService('es'); + const esArchiver = getService('esArchiver'); + + // This test suite is not meant to test a specific route, but to test the legacy action migration + // code that lives in multiple routes. This code is also tested in each of the routes it lives in + // but not in as much detail and relying on mocks. This test loads an es_archive containing rules + // created in 7.15 with legacy actions. + // For new routes that do any updates on a rule, please ensure that you are including the legacy + // action migration code. We are monitoring legacy action telemetry to clean up once we see their + // existence being near 0. + describe('migrate_legacy_actions', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/security_solution/legacy_actions'); + }); + + after(async () => { + await esArchiver.unload( + 'x-pack/test/functional/es_archives/security_solution/legacy_actions' + ); + }); + + it('migrates legacy actions for rule with no actions', async () => { + const soId = '9095ee90-b075-11ec-bb3f-1f063f8e06cf'; + const ruleId = '2297be91-894c-4831-830f-b424a0ec84f0'; + const legacySidecarId = '926668d0-b075-11ec-bb3f-1f063f8e06cf'; + + // check for legacy sidecar action + const sidecarActionSO = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionSO.hits.hits.length).to.eql(1); + + // check for legacy notification SO + // should not have been created for a rule with no actions + const legacyNotificationSO = await getLegacyActionNotificationSOById(es, soId); + expect(legacyNotificationSO.hits.hits.length).to.eql(0); + + // patch enable the rule + // any route that edits the rule should trigger the migration + await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send({ rule_id: ruleId, enabled: false }) + .expect(200); + + const { + hits: { + hits: [{ _source: ruleSO }], + }, + } = await getRuleSOById(es, soId); + + // Sidecar should be removed + const sidecarActionsSOAfterMigration = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); + + expect(ruleSO?.alert.actions).to.eql([]); + expect(ruleSO?.alert.throttle).to.eql(null); + expect(ruleSO?.alert.notifyWhen).to.eql('onActiveAlert'); + }); + + it('migrates legacy actions for rule with action run on every run', async () => { + const soId = 'dc6595f0-b075-11ec-bb3f-1f063f8e06cf'; + const ruleId = '72a0d429-363b-4f70-905e-c6019a224d40'; + const legacySidecarId = 'dde13970-b075-11ec-bb3f-1f063f8e06cf'; + + // check for legacy sidecar action + const sidecarActionSO = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionSO.hits.hits.length).to.eql(1); + + // check for legacy notification SO + // should not have been created for a rule that runs on every rule run + const legacyNotificationSO = await getLegacyActionNotificationSOById(es, soId); + expect(legacyNotificationSO.hits.hits.length).to.eql(0); + + // patch enable the rule + // any route that edits the rule should trigger the migration + await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send({ rule_id: ruleId, enabled: false }) + .expect(200); + + const { + hits: { + hits: [{ _source: ruleSO }], + }, + } = await getRuleSOById(es, soId); + + // Sidecar should be removed + const sidecarActionsSOAfterMigration = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); + + expect(ruleSO?.alert.actions).to.eql([ + { + actionRef: 'action_0', + actionTypeId: '.email', + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + subject: 'Test Actions', + to: ['test@test.com'], + }, + }, + ]); + expect(ruleSO?.alert.throttle).to.eql(null); + expect(ruleSO?.alert.notifyWhen).to.eql('onActiveAlert'); + expect(ruleSO?.references).to.eql([ + { + id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', + name: 'action_0', + type: 'action', + }, + ]); + }); + + it('migrates legacy actions for rule with action run hourly', async () => { + const soId = '064e3160-b076-11ec-bb3f-1f063f8e06cf'; + const ruleId = '4c056b05-75ac-4209-be32-82100f771eb4'; + const legacySidecarId = '07aa8d10-b076-11ec-bb3f-1f063f8e06cf'; + + // check for legacy sidecar action + const sidecarActionSO = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionSO.hits.hits.length).to.eql(1); + + // check for legacy notification SO + const legacyNotificationSO = await getLegacyActionNotificationSOById(es, soId); + expect(legacyNotificationSO.hits.hits.length).to.eql(1); + + // patch enable the rule + // any route that edits the rule should trigger the migration + await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send({ rule_id: ruleId, enabled: false }) + .expect(200); + + const { + hits: { + hits: [{ _source: ruleSO }], + }, + } = await getRuleSOById(es, soId); + + // Sidecar should be removed + const sidecarActionsSOAfterMigration = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); + + // Legacy notification should be removed + const legacyNotificationSOAfterMigration = await getLegacyActionNotificationSOById(es, soId); + expect(legacyNotificationSOAfterMigration.hits.hits.length).to.eql(0); + + expect(ruleSO?.alert.actions).to.eql([ + { + actionTypeId: '.email', + params: { + subject: 'Rule email', + to: ['test@test.com'], + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + }, + actionRef: 'action_0', + group: 'default', + }, + { + actionTypeId: '.slack', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + }, + actionRef: 'action_1', + group: 'default', + }, + ]); + expect(ruleSO?.alert.throttle).to.eql('1h'); + expect(ruleSO?.alert.notifyWhen).to.eql('onThrottleInterval'); + expect(ruleSO?.references).to.eql([ + { + id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', + name: 'action_0', + type: 'action', + }, + { + id: '207fa0e0-c04e-11ec-8a52-4fb92379525a', + name: 'action_1', + type: 'action', + }, + ]); + }); + + it('migrates legacy actions for rule with action run daily', async () => { + const soId = '27639570-b076-11ec-bb3f-1f063f8e06cf'; + const ruleId = '8e2c8550-f13f-4e21-be0c-92148d71a5f1'; + const legacySidecarId = '291ae260-b076-11ec-bb3f-1f063f8e06cf'; + + // check for legacy sidecar action + const sidecarActionSO = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionSO.hits.hits.length).to.eql(1); + + // check for legacy notification SO + const legacyNotificationSO = await getLegacyActionNotificationSOById(es, soId); + expect(legacyNotificationSO.hits.hits.length).to.eql(1); + + // patch enable the rule + await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send({ rule_id: ruleId, enabled: false }) + .expect(200); + + const { + hits: { + hits: [{ _source: ruleSO }], + }, + } = await getRuleSOById(es, soId); + + // Sidecar should be removed + const sidecarActionsSOAfterMigration = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); + + // Legacy notification should be removed + const legacyNotificationSOAfterMigration = await getLegacyActionNotificationSOById(es, soId); + expect(legacyNotificationSOAfterMigration.hits.hits.length).to.eql(0); + + expect(ruleSO?.alert.actions).to.eql([ + { + actionRef: 'action_0', + actionTypeId: '.email', + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + subject: 'Test Actions', + to: ['test@test.com'], + }, + }, + ]); + expect(ruleSO?.alert.throttle).to.eql('1d'); + expect(ruleSO?.alert.notifyWhen).to.eql('onThrottleInterval'); + expect(ruleSO?.references).to.eql([ + { + id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', + name: 'action_0', + type: 'action', + }, + ]); + }); + + it('migrates legacy actions for rule with action run weekly', async () => { + const soId = '61ec7a40-b076-11ec-bb3f-1f063f8e06cf'; + const ruleId = '05fbdd2a-e802-420b-bdc3-95ae0acca454'; + const legacySidecarId = '63aa2fd0-b076-11ec-bb3f-1f063f8e06cf'; + + // check for legacy sidecar action + const sidecarActionSO = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionSO.hits.hits.length).to.eql(1); + + // check for legacy notification SO + const legacyNotificationSO = await getLegacyActionNotificationSOById(es, soId); + expect(legacyNotificationSO.hits.hits.length).to.eql(1); + + // patch enable the rule + await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send({ rule_id: ruleId, enabled: false }) + .expect(200); + + const { + hits: { + hits: [{ _source: ruleSO }], + }, + } = await getRuleSOById(es, soId); + + // Sidecar should be removed + const sidecarActionsSOAfterMigration = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); + + // Legacy notification should be removed + const legacyNotificationSOAfterMigration = await getLegacyActionNotificationSOById(es, soId); + expect(legacyNotificationSOAfterMigration.hits.hits.length).to.eql(0); + + expect(ruleSO?.alert.actions).to.eql([ + { + actionRef: 'action_0', + actionTypeId: '.email', + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + subject: 'Test Actions', + to: ['test@test.com'], + }, + }, + ]); + expect(ruleSO?.alert.throttle).to.eql('7d'); + expect(ruleSO?.alert.notifyWhen).to.eql('onThrottleInterval'); + expect(ruleSO?.references).to.eql([ + { + id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', + name: 'action_0', + type: 'action', + }, + ]); + }); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/migrations.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/migrations.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/migrations.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/open_close_signals.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/open_close_signals.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/patch_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/patch_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/patch_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/patch_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/patch_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/patch_rules_bulk.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/patch_rules_bulk.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/patch_rules_bulk.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/perform_bulk_action.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/perform_bulk_action.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/preview_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/preview_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/preview_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/preview_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/read_privileges.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/read_privileges.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/read_privileges.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/read_privileges.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/read_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/read_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/read_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/read_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/resolve_read_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/resolve_read_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/resolve_read_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/resolve_read_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/runtime.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/runtime.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/runtime.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/runtime.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/template_data/execution_events.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/template_data/execution_events.ts similarity index 83% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/template_data/execution_events.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/template_data/execution_events.ts index c4767bbcc5632..4a674c52fa1ed 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/template_data/execution_events.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/template_data/execution_events.ts @@ -5,6 +5,20 @@ * 2.0. */ +/** + * When using these execution events as templates be sure to replace all the following fields with their updated values + * + * E.g. + * set(e, '@timestamp', dateTimes[i]); + * set(e, 'event.start', dateTimes[i]); + * set(e, 'event.end', dateTimes[i]); + * set(e, 'rule.id', id); + * set(e, 'kibana.saved_objects[0].id', id); + */ + +/** + * Rule executed without issue + */ export const successfulExecution = [ { '@timestamp': '2022-03-17T22:59:31.360Z', @@ -226,30 +240,24 @@ export const successfulExecution = [ }, ]; +/** + * Rule execution identified gap since last execution + */ export const failedGapExecution = [ { - '@timestamp': '2022-03-17T12:36:16.413Z', + '@timestamp': '2022-03-17T12:36:14.868Z', event: { provider: 'alerting', - action: 'execute', + action: 'execute-start', kind: 'alert', category: ['siem'], start: '2022-03-17T12:36:14.868Z', - outcome: 'success', - end: '2022-03-17T12:36:16.413Z', - duration: 1545000000, }, kibana: { alert: { rule: { execution: { uuid: '38fa2d4a-94d3-4ea3-80d6-d1284eb98357', - metrics: { - number_of_triggered_actions: 0, - number_of_searches: 6, - es_search_duration_ms: 2, - total_search_duration_ms: 15, - }, }, }, }, @@ -265,9 +273,6 @@ export const failedGapExecution = [ scheduled: '2022-03-17T12:27:10.060Z', schedule_delay: 544808000000, }, - alerting: { - status: 'ok', - }, server_uuid: '5b2de169-2785-441b-ae8c-186a1936b17d', version: '8.2.0', }, @@ -276,22 +281,21 @@ export const failedGapExecution = [ license: 'basic', category: 'siem.queryRule', ruleset: 'siem', - name: 'Lots of Execution Events', }, - message: - "rule executed: siem.queryRule:fb1fc150-a292-11ec-a2cf-c1b28b0392b0: 'Lots of Execution Events'", + message: 'rule execution start: "fb1fc150-a292-11ec-a2cf-c1b28b0392b0"', ecs: { version: '1.8.0', }, }, { - '@timestamp': '2022-03-17T12:36:15.382Z', + '@timestamp': '2022-03-17T12:36:14.888Z', event: { provider: 'securitySolution.ruleExecution', - kind: 'metric', - action: 'execution-metrics', - sequence: 1, + kind: 'event', + action: 'status-change', + sequence: 0, }, + message: '', rule: { id: 'fb1fc150-a292-11ec-a2cf-c1b28b0392b0', name: 'Lots of Execution Events', @@ -301,9 +305,8 @@ export const failedGapExecution = [ alert: { rule: { execution: { - metrics: { - execution_gap_duration_s: 245, - }, + status: 'running', + status_order: 15, uuid: '38fa2d4a-94d3-4ea3-80d6-d1284eb98357', }, }, @@ -364,14 +367,13 @@ export const failedGapExecution = [ }, }, { - '@timestamp': '2022-03-17T12:36:14.888Z', + '@timestamp': '2022-03-17T12:36:15.382Z', event: { provider: 'securitySolution.ruleExecution', - kind: 'event', - action: 'status-change', - sequence: 0, + kind: 'metric', + action: 'execution-metrics', + sequence: 1, }, - message: '', rule: { id: 'fb1fc150-a292-11ec-a2cf-c1b28b0392b0', name: 'Lots of Execution Events', @@ -381,8 +383,9 @@ export const failedGapExecution = [ alert: { rule: { execution: { - status: 'running', - status_order: 15, + metrics: { + execution_gap_duration_s: 245, + }, uuid: '38fa2d4a-94d3-4ea3-80d6-d1284eb98357', }, }, @@ -403,19 +406,28 @@ export const failedGapExecution = [ }, }, { - '@timestamp': '2022-03-17T12:36:14.868Z', + '@timestamp': '2022-03-17T12:36:16.413Z', event: { provider: 'alerting', - action: 'execute-start', + action: 'execute', kind: 'alert', category: ['siem'], start: '2022-03-17T12:36:14.868Z', + outcome: 'success', + end: '2022-03-17T12:36:16.413Z', + duration: 1545000000, }, kibana: { alert: { rule: { execution: { uuid: '38fa2d4a-94d3-4ea3-80d6-d1284eb98357', + metrics: { + number_of_triggered_actions: 0, + number_of_searches: 6, + es_search_duration_ms: 2, + total_search_duration_ms: 15, + }, }, }, }, @@ -431,6 +443,9 @@ export const failedGapExecution = [ scheduled: '2022-03-17T12:27:10.060Z', schedule_delay: 544808000000, }, + alerting: { + status: 'ok', + }, server_uuid: '5b2de169-2785-441b-ae8c-186a1936b17d', version: '8.2.0', }, @@ -439,14 +454,19 @@ export const failedGapExecution = [ license: 'basic', category: 'siem.queryRule', ruleset: 'siem', + name: 'Lots of Execution Events', }, - message: 'rule execution start: "fb1fc150-a292-11ec-a2cf-c1b28b0392b0"', + message: + "rule executed: siem.queryRule:fb1fc150-a292-11ec-a2cf-c1b28b0392b0: 'Lots of Execution Events'", ecs: { version: '1.8.0', }, }, ]; +/** + * Rule execution resulted in partial warning, e.g. missing index pattern + */ export const partialWarningExecution = [ { '@timestamp': '2022-03-16T23:28:36.012Z', @@ -628,3 +648,112 @@ export const partialWarningExecution = [ }, }, ]; + +/** + * Rule execution failed because rule is disabled (configure 1s interval/lookback then rule + * is disabled while running) + */ +export const failedRanAfterDisabled = [ + { + '@timestamp': '2022-04-21T02:00:55.400Z', + event: { + provider: 'alerting', + action: 'execute', + kind: 'alert', + category: ['siem'], + start: '2022-04-21T02:00:55.397Z', + end: '2022-04-21T02:00:55.400Z', + duration: 3000000, + reason: 'disabled', + outcome: 'failure', + }, + kibana: { + alert: { + rule: { + rule_type_id: 'siem.queryRule', + consumer: 'siem', + execution: { + uuid: '50eb8b2e-8334-4387-b77f-d47fdb7fbe2d', + }, + }, + }, + saved_objects: [ + { + rel: 'primary', + type: 'alert', + id: 'a890e240-b9fb-11ec-8598-338317271cf4', + type_id: 'siem.queryRule', + }, + ], + space_ids: ['default'], + task: { + scheduled: '2022-04-21T02:00:53.325Z', + schedule_delay: 2072000000, + }, + alerting: { + status: 'error', + }, + server_uuid: '5b2de169-2785-441b-ae8c-186a1936b17d', + version: '8.3.0', + }, + rule: { + id: 'a890e240-b9fb-11ec-8598-338317271cf4', + license: 'basic', + category: 'siem.queryRule', + ruleset: 'siem', + }, + error: { + message: 'Rule failed to execute because rule ran after it was disabled.', + }, + message: 'siem.queryRule:a890e240-b9fb-11ec-8598-338317271cf4: execution failed', + ecs: { + version: '1.8.0', + }, + }, + { + '@timestamp': '2022-04-21T02:00:55.397Z', + event: { + provider: 'alerting', + action: 'execute-start', + kind: 'alert', + category: ['siem'], + start: '2022-04-21T02:00:55.397Z', + }, + kibana: { + alert: { + rule: { + rule_type_id: 'siem.queryRule', + consumer: 'siem', + execution: { + uuid: '50eb8b2e-8334-4387-b77f-d47fdb7fbe2d', + }, + }, + }, + saved_objects: [ + { + rel: 'primary', + type: 'alert', + id: 'a890e240-b9fb-11ec-8598-338317271cf4', + type_id: 'siem.queryRule', + }, + ], + space_ids: ['default'], + task: { + scheduled: '2022-04-21T02:00:53.325Z', + schedule_delay: 2072000000, + }, + server_uuid: '5b2de169-2785-441b-ae8c-186a1936b17d', + version: '8.3.0', + }, + rule: { + id: 'a890e240-b9fb-11ec-8598-338317271cf4', + license: 'basic', + category: 'siem.queryRule', + ruleset: 'siem', + }, + message: 'rule execution start: "a890e240-b9fb-11ec-8598-338317271cf4"', + ecs: { + version: '1.8.0', + }, + }, +]; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/throttle.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/throttle.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/throttle.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/throttle.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/timestamps.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/timestamps.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/timestamps.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/timestamps.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_actions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/update_actions.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_actions.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/update_actions.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/update_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/update_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/update_rules_bulk.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_rules_bulk.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group1/update_rules_bulk.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group2/config.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group2/config.ts new file mode 100644 index 0000000000000..2430b8f2148d9 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group2/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 { FtrConfigProviderContext } from '@kbn/test'; + +// eslint-disable-next-line import/no-default-export +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../config.base.ts')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group2/create_endpoint_exceptions.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group2/create_endpoint_exceptions.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group2/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group2/index.ts new file mode 100644 index 0000000000000..f477b23e801f3 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group2/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('detection engine api security and spaces enabled - Group 2', function () { + loadTestFile(require.resolve('./create_endpoint_exceptions')); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group3/config.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group3/config.ts new file mode 100644 index 0000000000000..2430b8f2148d9 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group3/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 { FtrConfigProviderContext } from '@kbn/test'; + +// eslint-disable-next-line import/no-default-export +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../config.base.ts')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group3/create_exceptions.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group3/create_exceptions.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group3/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group3/index.ts new file mode 100644 index 0000000000000..10d4e9c2d6635 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group3/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('detection engine api security and spaces enabled - Group 3', function () { + loadTestFile(require.resolve('./create_exceptions')); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/config.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/config.ts new file mode 100644 index 0000000000000..2430b8f2148d9 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/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 { FtrConfigProviderContext } from '@kbn/test'; + +// eslint-disable-next-line import/no-default-export +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../config.base.ts')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/index.ts new file mode 100644 index 0000000000000..9394e81017aba --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('detection engine api security and spaces enabled - Group 4', function () { + loadTestFile(require.resolve('./telemetry')); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/README.md b/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/README.md similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/README.md rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/README.md diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/index.ts new file mode 100644 index 0000000000000..4e180f73dc742 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/index.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 { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('Detection rule type telemetry', function () { + loadTestFile(require.resolve('./usage_collector/all_types')); + loadTestFile(require.resolve('./usage_collector/detection_rules')); + loadTestFile(require.resolve('./usage_collector/detection_rule_status')); + + loadTestFile(require.resolve('./task_based/all_types')); + loadTestFile(require.resolve('./task_based/detection_rules')); + loadTestFile(require.resolve('./task_based/security_lists')); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/task_based/all_types.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/task_based/all_types.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/task_based/all_types.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/task_based/all_types.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/task_based/detection_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/task_based/detection_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/task_based/detection_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/task_based/detection_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/task_based/security_lists.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/task_based/security_lists.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/task_based/security_lists.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/task_based/security_lists.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/usage_collector/all_types.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/usage_collector/all_types.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/usage_collector/all_types.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/usage_collector/all_types.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/usage_collector/detection_rule_status.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/usage_collector/detection_rule_status.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/usage_collector/detection_rule_status.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/usage_collector/detection_rule_status.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/usage_collector/detection_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/usage_collector/detection_rules.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/usage_collector/detection_rules.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/usage_collector/detection_rules.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group5/config.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group5/config.ts new file mode 100644 index 0000000000000..2430b8f2148d9 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group5/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 { FtrConfigProviderContext } from '@kbn/test'; + +// eslint-disable-next-line import/no-default-export +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../config.base.ts')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group5/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group5/index.ts new file mode 100644 index 0000000000000..ac107392d4b5c --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group5/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('detection engine api security and spaces enabled - Group 5', function () { + loadTestFile(require.resolve('./keyword_family')); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/README.md b/x-pack/test/detection_engine_api_integration/security_and_spaces/group5/keyword_family/README.md similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/README.md rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group5/keyword_family/README.md diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/const_keyword.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group5/keyword_family/const_keyword.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/const_keyword.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group5/keyword_family/const_keyword.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group5/keyword_family/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group5/keyword_family/index.ts new file mode 100644 index 0000000000000..1ecb06fbed4e5 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group5/keyword_family/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('Detection keyword family data types', function () { + loadTestFile(require.resolve('./keyword')); + loadTestFile(require.resolve('./const_keyword')); + loadTestFile(require.resolve('./keyword_mixed_with_const')); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group5/keyword_family/keyword.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group5/keyword_family/keyword.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword_mixed_with_const.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group5/keyword_family/keyword_mixed_with_const.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/keyword_mixed_with_const.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group5/keyword_family/keyword_mixed_with_const.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/alerts/alerts_compatibility.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/alerts/alerts_compatibility.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/alerts/alerts_compatibility.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group6/alerts/alerts_compatibility.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/alerts/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/alerts/index.ts new file mode 100644 index 0000000000000..320650a4c79e3 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/alerts/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('Detection engine signals/alerts compatibility', function () { + loadTestFile(require.resolve('./alerts_compatibility')); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/config.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/config.ts new file mode 100644 index 0000000000000..2430b8f2148d9 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/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 { FtrConfigProviderContext } from '@kbn/test'; + +// eslint-disable-next-line import/no-default-export +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../config.base.ts')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/index.ts new file mode 100644 index 0000000000000..92c235e95c0e4 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('detection engine api security and spaces enabled - Group 6', function () { + loadTestFile(require.resolve('./alerts')); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group7/config.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group7/config.ts new file mode 100644 index 0000000000000..2430b8f2148d9 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group7/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 { FtrConfigProviderContext } from '@kbn/test'; + +// eslint-disable-next-line import/no-default-export +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../config.base.ts')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/date.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group7/exception_operators_data_types/date.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/date.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group7/exception_operators_data_types/date.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/double.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group7/exception_operators_data_types/double.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/double.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group7/exception_operators_data_types/double.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group7/exception_operators_data_types/float.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group7/exception_operators_data_types/float.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group7/exception_operators_data_types/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group7/exception_operators_data_types/index.ts new file mode 100644 index 0000000000000..6b2cf915cc51b --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group7/exception_operators_data_types/index.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 { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('Detection exceptions data types and operators', function () { + loadTestFile(require.resolve('./date')); + loadTestFile(require.resolve('./double')); + loadTestFile(require.resolve('./float')); + loadTestFile(require.resolve('./integer')); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group7/exception_operators_data_types/integer.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group7/exception_operators_data_types/integer.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group7/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group7/index.ts new file mode 100644 index 0000000000000..96f53c47441ed --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group7/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('detection engine api security and spaces enabled - Group 7', function () { + loadTestFile(require.resolve('./exception_operators_data_types')); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group8/config.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group8/config.ts new file mode 100644 index 0000000000000..2430b8f2148d9 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group8/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 { FtrConfigProviderContext } from '@kbn/test'; + +// eslint-disable-next-line import/no-default-export +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../config.base.ts')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group8/exception_operators_data_types/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group8/exception_operators_data_types/index.ts new file mode 100644 index 0000000000000..cf87276aac3ae --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group8/exception_operators_data_types/index.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 { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('Detection exceptions data types and operators', function () { + loadTestFile(require.resolve('./keyword')); + loadTestFile(require.resolve('./keyword_array')); + loadTestFile(require.resolve('./long')); + loadTestFile(require.resolve('./text')); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group8/exception_operators_data_types/keyword.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group8/exception_operators_data_types/keyword.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group8/exception_operators_data_types/keyword_array.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group8/exception_operators_data_types/keyword_array.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group8/exception_operators_data_types/long.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group8/exception_operators_data_types/long.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group8/exception_operators_data_types/text.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group8/exception_operators_data_types/text.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group8/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group8/index.ts new file mode 100644 index 0000000000000..7182e411a1332 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group8/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('detection engine api security and spaces enabled - Group 8', function () { + loadTestFile(require.resolve('./exception_operators_data_types')); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group9/config.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group9/config.ts new file mode 100644 index 0000000000000..2430b8f2148d9 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group9/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 { FtrConfigProviderContext } from '@kbn/test'; + +// eslint-disable-next-line import/no-default-export +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../config.base.ts')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group9/exception_operators_data_types/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group9/exception_operators_data_types/index.ts new file mode 100644 index 0000000000000..22864980e2653 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group9/exception_operators_data_types/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('Detection exceptions data types and operators', function () { + loadTestFile(require.resolve('./text_array')); + loadTestFile(require.resolve('./ip')); + loadTestFile(require.resolve('./ip_array')); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group9/exception_operators_data_types/ip.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group9/exception_operators_data_types/ip.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group9/exception_operators_data_types/ip_array.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group9/exception_operators_data_types/ip_array.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group9/exception_operators_data_types/text_array.ts similarity index 100% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts rename to x-pack/test/detection_engine_api_integration/security_and_spaces/group9/exception_operators_data_types/text_array.ts diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group9/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group9/index.ts new file mode 100644 index 0000000000000..ad5e427c69df8 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group9/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('detection engine api security and spaces enabled - Group 9', function () { + loadTestFile(require.resolve('./exception_operators_data_types')); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/alerts/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/alerts/index.ts deleted file mode 100644 index b06d6cb26e33b..0000000000000 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/alerts/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default ({ loadTestFile }: FtrProviderContext): void => { - describe('Detection engine signals/alerts compatibility', function () { - describe('', function () { - this.tags('ciGroup11'); - - loadTestFile(require.resolve('./alerts_compatibility')); - }); - }); -}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/README.md b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/README.md deleted file mode 100644 index d6beb912d7007..0000000000000 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/README.md +++ /dev/null @@ -1,21 +0,0 @@ -These are tests for rule exception lists where we test each data type -* date -* double -* float -* integer -* ip -* keyword -* long -* text - -Against the operator types of: -* "is" -* "is not" -* "is one of" -* "is not one of" -* "exists" -* "does not exist" -* "is in list" -* "is not in list" - -If you add a test here, ensure you add it to the ./index.ts" file as well \ No newline at end of file diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/index.ts deleted file mode 100644 index ded3df8b6716c..0000000000000 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default ({ loadTestFile }: FtrProviderContext): void => { - describe('Detection exceptions data types and operators', function () { - describe('', function () { - this.tags('ciGroup23'); - - loadTestFile(require.resolve('./date')); - loadTestFile(require.resolve('./double')); - loadTestFile(require.resolve('./float')); - loadTestFile(require.resolve('./integer')); - }); - - describe('', function () { - this.tags('ciGroup24'); - - loadTestFile(require.resolve('./keyword')); - loadTestFile(require.resolve('./keyword_array')); - loadTestFile(require.resolve('./long')); - loadTestFile(require.resolve('./text')); - loadTestFile(require.resolve('./text_array')); - }); - - describe('', function () { - this.tags('ciGroup16'); - - loadTestFile(require.resolve('./ip')); - }); - - describe('', function () { - this.tags('ciGroup21'); - - loadTestFile(require.resolve('./ip_array')); - }); - }); -}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/get_rule_execution_events.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/get_rule_execution_events.ts deleted file mode 100644 index d3bddc8b0d46c..0000000000000 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/get_rule_execution_events.ts +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import dateMath from '@kbn/datemath'; -import expect from '@kbn/expect'; -import moment from 'moment'; -import { set } from '@elastic/safer-lodash-set'; -import uuid from 'uuid'; -import { detectionEngineRuleExecutionEventsUrl } from '@kbn/security-solution-plugin/common/constants'; -import { RuleExecutionStatus } from '@kbn/security-solution-plugin/common/detection_engine/schemas/common'; - -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { - createRule, - createSignalsIndex, - deleteAllAlerts, - deleteAllEventLogExecutionEvents, - deleteSignalsIndex, - getRuleForSignalTesting, - indexEventLogExecutionEvents, - waitForEventLogExecuteComplete, - waitForRuleSuccessOrStatus, -} from '../../utils'; -import { failedGapExecution } from './template_data/execution_events'; - -// eslint-disable-next-line import/no-default-export -export default ({ getService }: FtrProviderContext) => { - const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - const es = getService('es'); - const log = getService('log'); - - describe('Get Rule Execution Log Events', () => { - before(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); - await esArchiver.load('x-pack/test/functional/es_archives/security_solution/alias'); - await createSignalsIndex(supertest, log); - }); - - after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts'); - await esArchiver.unload('x-pack/test/functional/es_archives/security_solution/alias'); - await deleteSignalsIndex(supertest, log); - }); - - beforeEach(async () => { - await deleteAllAlerts(supertest, log); - await deleteAllEventLogExecutionEvents(es, log); - }); - - afterEach(async () => { - await deleteAllAlerts(supertest, log); - await deleteAllEventLogExecutionEvents(es, log); - }); - - it('should return an error if rule does not exist', async () => { - const start = dateMath.parse('now-24h')?.utc().toISOString(); - const end = dateMath.parse('now', { roundUp: true })?.utc().toISOString(); - const response = await supertest - .get(detectionEngineRuleExecutionEventsUrl('1')) - .set('kbn-xsrf', 'true') - .query({ start, end }); - - expect(response.status).to.eql(404); - expect(response.text).to.eql( - '{"message":"Saved object [alert/1] not found","status_code":404}' - ); - }); - - it('should return execution events for a rule that has executed successfully', async () => { - const rule = getRuleForSignalTesting(['auditbeat-*']); - const { id } = await createRule(supertest, log, rule); - await waitForRuleSuccessOrStatus(supertest, log, id); - await waitForEventLogExecuteComplete(es, log, id); - - const start = dateMath.parse('now-24h')?.utc().toISOString(); - const end = dateMath.parse('now', { roundUp: true })?.utc().toISOString(); - const response = await supertest - .get(detectionEngineRuleExecutionEventsUrl(id)) - .set('kbn-xsrf', 'true') - .query({ start, end }); - - expect(response.status).to.eql(200); - expect(response.body.total).to.eql(1); - expect(response.body.events[0].duration_ms).to.greaterThan(0); - expect(response.body.events[0].search_duration_ms).to.greaterThan(0); - expect(response.body.events[0].schedule_delay_ms).to.greaterThan(0); - expect(response.body.events[0].indexing_duration_ms).to.greaterThan(0); - expect(response.body.events[0].gap_duration_ms).to.eql(0); - expect(response.body.events[0].security_status).to.eql('succeeded'); - expect(response.body.events[0].security_message).to.eql('succeeded'); - }); - - it('should return execution events for a rule that has executed in a warning state', async () => { - const rule = getRuleForSignalTesting(['auditbeat-*', 'no-name-index']); - const { id } = await createRule(supertest, log, rule); - await waitForRuleSuccessOrStatus(supertest, log, id, RuleExecutionStatus['partial failure']); - await waitForEventLogExecuteComplete(es, log, id); - - const start = dateMath.parse('now-24h')?.utc().toISOString(); - const end = dateMath.parse('now', { roundUp: true })?.utc().toISOString(); - const response = await supertest - .get(detectionEngineRuleExecutionEventsUrl(id)) - .set('kbn-xsrf', 'true') - .query({ start, end }); - - expect(response.status).to.eql(200); - expect(response.body.total).to.eql(1); - expect(response.body.events[0].duration_ms).to.greaterThan(0); - expect(response.body.events[0].search_duration_ms).to.eql(0); - expect(response.body.events[0].schedule_delay_ms).to.greaterThan(0); - expect(response.body.events[0].indexing_duration_ms).to.eql(0); - expect(response.body.events[0].gap_duration_ms).to.eql(0); - expect(response.body.events[0].security_status).to.eql('partial failure'); - expect( - response.body.events[0].security_message.startsWith( - 'Check privileges failed to execute ResponseError: index_not_found_exception: [index_not_found_exception] Reason: no such index [no-name-index]' - ) - ).to.eql(true); - }); - - // TODO: Debug indexing - it.skip('should return execution events for a rule that has executed in a failure state with a gap', async () => { - const rule = getRuleForSignalTesting(['auditbeat-*'], uuid.v4(), false); - const { id } = await createRule(supertest, log, rule); - - const start = dateMath.parse('now')?.utc().toISOString(); - const end = dateMath.parse('now+24h', { roundUp: true })?.utc().toISOString(); - - // Create 5 timestamps a minute apart to use in the templated data - const dateTimes = [...Array(5).keys()].map((i) => - moment(start) - .add(i + 1, 'm') - .toDate() - .toISOString() - ); - - const events = failedGapExecution.map((e, i) => { - set(e, '@timestamp', dateTimes[i]); - set(e, 'event.start', dateTimes[i]); - set(e, 'event.end', dateTimes[i]); - set(e, 'rule.id', id); - return e; - }); - - await indexEventLogExecutionEvents(es, log, events); - await waitForEventLogExecuteComplete(es, log, id); - - const response = await supertest - .get(detectionEngineRuleExecutionEventsUrl(id)) - .set('kbn-xsrf', 'true') - .query({ start, end }); - - // console.log(JSON.stringify(response)); - - expect(response.status).to.eql(200); - expect(response.body.total).to.eql(1); - expect(response.body.events[0].duration_ms).to.eql(4236); - expect(response.body.events[0].search_duration_ms).to.eql(0); - expect(response.body.events[0].schedule_delay_ms).to.greaterThan(0); - expect(response.body.events[0].indexing_duration_ms).to.eql(0); - expect(response.body.events[0].gap_duration_ms).to.greaterThan(0); - expect(response.body.events[0].security_status).to.eql('failed'); - expect( - response.body.events[0].security_message.startsWith( - 'Check privileges failed to execute ResponseError: index_not_found_exception: [index_not_found_exception] Reason: no such index [no-name-index]' - ) - ).to.eql(true); - }); - - // it('should return execution events when providing a status filter', async () => { - // const rule = getRuleForSignalTesting(['auditbeat-*', 'no-name-index']); - // const { id } = await createRule(supertest, log, rule); - // await waitForRuleSuccessOrStatus(supertest, log, id, RuleExecutionStatus.failed); - // await waitForSignalsToBePresent(supertest, log, 1, [id]); - // - // const start = dateMath.parse('now-24h')?.utc().toISOString(); - // const end = dateMath.parse('now', { roundUp: true })?.utc().toISOString(); - // const response = await supertest - // .get(detectionEngineRuleExecutionEventsUrl(id)) - // .set('kbn-xsrf', 'true') - // .query({ start, end }); - // - // expect(response.status).to.eql(200); - // expect(response.body.total).to.eql(1); - // expect(response.body.events[0].duration_ms).to.greaterThan(0); - // expect(response.body.events[0].search_duration_ms).to.eql(0); - // expect(response.body.events[0].schedule_delay_ms).to.greaterThan(0); - // expect(response.body.events[0].indexing_duration_ms).to.eql(0); - // expect(response.body.events[0].gap_duration_ms).to.eql(0); - // expect(response.body.events[0].security_status).to.eql('failed'); - // expect(response.body.events[0].security_message).to.include( - // 'were not queried between this rule execution and the last execution, so signals may have been missed. ' - // ); - // }); - - // it('should return execution events when providing a status filter and sortField', async () => { - // const rule = getRuleForSignalTesting(['auditbeat-*', 'no-name-index']); - // const { id } = await createRule(supertest, log, rule); - // await waitForRuleSuccessOrStatus(supertest, log, id, RuleExecutionStatus.failed); - // await waitForSignalsToBePresent(supertest, log, 1, [id]); - // - // const start = dateMath.parse('now-24h')?.utc().toISOString(); - // const end = dateMath.parse('now', { roundUp: true })?.utc().toISOString(); - // const response = await supertest - // .get(detectionEngineRuleExecutionEventsUrl(id)) - // .set('kbn-xsrf', 'true') - // .query({ start, end }); - // - // expect(response.status).to.eql(200); - // expect(response.body.total).to.eql(1); - // expect(response.body.events[0].duration_ms).to.greaterThan(0); - // expect(response.body.events[0].search_duration_ms).to.eql(0); - // expect(response.body.events[0].schedule_delay_ms).to.greaterThan(0); - // expect(response.body.events[0].indexing_duration_ms).to.eql(0); - // expect(response.body.events[0].gap_duration_ms).to.eql(0); - // expect(response.body.events[0].security_status).to.eql('failed'); - // expect(response.body.events[0].security_message).to.include( - // 'were not queried between this rule execution and the last execution, so signals may have been missed. ' - // ); - // }); - }); -}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts deleted file mode 100644 index a3c4dd8ed3be1..0000000000000 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../common/ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default ({ loadTestFile }: FtrProviderContext): void => { - describe('detection engine api security and spaces enabled', function () { - describe('', function () { - this.tags('ciGroup11'); - - loadTestFile(require.resolve('./aliases')); - loadTestFile(require.resolve('./add_actions')); - loadTestFile(require.resolve('./update_actions')); - loadTestFile(require.resolve('./add_prepackaged_rules')); - loadTestFile(require.resolve('./check_privileges')); - loadTestFile(require.resolve('./create_index')); - loadTestFile(require.resolve('./create_rules')); - loadTestFile(require.resolve('./preview_rules')); - loadTestFile(require.resolve('./create_rules_bulk')); - loadTestFile(require.resolve('./create_ml')); - loadTestFile(require.resolve('./create_threat_matching')); - loadTestFile(require.resolve('./delete_rules')); - loadTestFile(require.resolve('./delete_rules_bulk')); - loadTestFile(require.resolve('./export_rules')); - loadTestFile(require.resolve('./find_rules')); - loadTestFile(require.resolve('./generating_signals')); - loadTestFile(require.resolve('./get_prepackaged_rules_status')); - loadTestFile(require.resolve('./get_rule_execution_events')); - loadTestFile(require.resolve('./import_rules')); - loadTestFile(require.resolve('./import_export_rules')); - loadTestFile(require.resolve('./read_rules')); - loadTestFile(require.resolve('./resolve_read_rules')); - loadTestFile(require.resolve('./update_rules')); - loadTestFile(require.resolve('./update_rules_bulk')); - loadTestFile(require.resolve('./patch_rules_bulk')); - loadTestFile(require.resolve('./perform_bulk_action')); - loadTestFile(require.resolve('./patch_rules')); - loadTestFile(require.resolve('./read_privileges')); - loadTestFile(require.resolve('./open_close_signals')); - loadTestFile(require.resolve('./get_signals_migration_status')); - loadTestFile(require.resolve('./create_signals_migrations')); - loadTestFile(require.resolve('./finalize_signals_migrations')); - loadTestFile(require.resolve('./delete_signals_migrations')); - loadTestFile(require.resolve('./timestamps')); - loadTestFile(require.resolve('./runtime')); - loadTestFile(require.resolve('./throttle')); - loadTestFile(require.resolve('./ignore_fields')); - loadTestFile(require.resolve('./migrations')); - }); - - describe('', function () { - this.tags('ciGroup26'); - - loadTestFile(require.resolve('./create_endpoint_exceptions')); - }); - - describe('', function () { - this.tags('ciGroup14'); - - loadTestFile(require.resolve('./create_exceptions')); - }); - - // That split here enable us on using a different ciGroup to run the tests - // listed on ./exception_operators_data_types/index - describe('', function () { - loadTestFile(require.resolve('./exception_operators_data_types')); - }); - - // That split here enable us on using a different ciGroup to run the tests - // listed on ./keyword_family/index - describe('', function () { - loadTestFile(require.resolve('./keyword_family')); - }); - - describe('', function () { - loadTestFile(require.resolve('./alerts')); - }); - - describe('', function () { - loadTestFile(require.resolve('./telemetry')); - }); - }); -}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/index.ts deleted file mode 100644 index 4855524d650ef..0000000000000 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/keyword_family/index.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 { FtrProviderContext } from '../../../common/ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default ({ loadTestFile }: FtrProviderContext): void => { - describe('Detection keyword family data types', function () { - describe('', function () { - this.tags('ciGroup11'); - - loadTestFile(require.resolve('./keyword')); - loadTestFile(require.resolve('./const_keyword')); - loadTestFile(require.resolve('./keyword_mixed_with_const')); - }); - }); -}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/index.ts deleted file mode 100644 index d2050179abd0e..0000000000000 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/telemetry/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default ({ loadTestFile }: FtrProviderContext): void => { - describe('Detection rule type telemetry', function () { - describe('', function () { - this.tags('ciGroup28'); - loadTestFile(require.resolve('./usage_collector/all_types')); - loadTestFile(require.resolve('./usage_collector/detection_rules')); - loadTestFile(require.resolve('./usage_collector/detection_rule_status')); - - loadTestFile(require.resolve('./task_based/all_types')); - loadTestFile(require.resolve('./task_based/detection_rules')); - loadTestFile(require.resolve('./task_based/security_lists')); - }); - }); -}; diff --git a/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_notification_so.ts b/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_notification_so.ts new file mode 100644 index 0000000000000..9a084d800a2d8 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_notification_so.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Client } from '@elastic/elasticsearch'; +import { SavedObjectReference } from '@kbn/core/server'; +import type { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { LegacyRuleNotificationAlertTypeParams } from '@kbn/security-solution-plugin/server/lib/detection_engine/notifications/legacy_types'; + +interface LegacyActionNotificationSO extends LegacyRuleNotificationAlertTypeParams { + references: SavedObjectReference[]; +} + +/** + * Fetch all legacy action sidecar notification SOs from the .kibana index + * @param es The ElasticSearch service + */ +export const getLegacyActionNotificationSO = async ( + es: Client +): Promise> => + es.search({ + index: '.kibana', + q: 'alert.alertTypeId:siem.notifications', + }); diff --git a/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_notifications_so_by_id.ts b/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_notifications_so_by_id.ts new file mode 100644 index 0000000000000..3120e85e899bf --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_notifications_so_by_id.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Client } from '@elastic/elasticsearch'; +import { SavedObjectReference } from '@kbn/core/server'; +import type { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { LegacyRuleNotificationAlertTypeParams } from '@kbn/security-solution-plugin/server/lib/detection_engine/notifications/legacy_types'; + +interface LegacyActionNotificationSO extends LegacyRuleNotificationAlertTypeParams { + references: SavedObjectReference[]; +} + +/** + * Fetch legacy action sidecar notification SOs from the .kibana index + * @param es The ElasticSearch service + * @param id SO id + */ +export const getLegacyActionNotificationSOById = async ( + es: Client, + id: string +): Promise> => + es.search({ + index: '.kibana', + q: `alert.alertTypeId:siem.notifications AND alert.params.ruleAlertId:"${id}"`, + }); diff --git a/x-pack/test/detection_engine_api_integration/utils/get_legacy_actions_so_by_id.ts b/x-pack/test/detection_engine_api_integration/utils/get_legacy_actions_so_by_id.ts new file mode 100644 index 0000000000000..48ea142aa28ca --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/utils/get_legacy_actions_so_by_id.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Client } from '@elastic/elasticsearch'; +import { SavedObjectReference } from '@kbn/core/server'; +import type { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { LegacyRuleActions } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_actions/legacy_types'; + +interface LegacyActionSO extends LegacyRuleActions { + references: SavedObjectReference[]; +} + +/** + * Fetch legacy action sidecar SOs from the .kibana index + * @param es The ElasticSearch service + * @param id SO id + */ +export const getLegacyActionSOById = async ( + es: Client, + id: string +): Promise> => + es.search({ + index: '.kibana', + q: `type:siem-detection-engine-rule-actions AND _id:"siem-detection-engine-rule-actions:${id}"`, + }); diff --git a/x-pack/test/detection_engine_api_integration/utils/get_rule_so_by_id.ts b/x-pack/test/detection_engine_api_integration/utils/get_rule_so_by_id.ts new file mode 100644 index 0000000000000..5a46f96cc400a --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/utils/get_rule_so_by_id.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Client } from '@elastic/elasticsearch'; +import { SavedObjectReference } from '@kbn/core/server'; +import type { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { Rule } from '@kbn/alerting-plugin/common'; + +interface RuleSO { + alert: Rule; + references: SavedObjectReference[]; +} + +/** + * Fetch legacy action sidecar SOs from the .kibana index + * @param es The ElasticSearch service + * @param id SO id + */ +export const getRuleSOById = async (es: Client, id: string): Promise> => + es.search({ + index: '.kibana', + q: `type:alert AND _id:"alert:${id}"`, + }); diff --git a/x-pack/test/detection_engine_api_integration/utils/get_slack_action.ts b/x-pack/test/detection_engine_api_integration/utils/get_slack_action.ts new file mode 100644 index 0000000000000..1d88f2cdbce73 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/utils/get_slack_action.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 const getSlackAction = () => ({ + actionTypeId: '.slack', + secrets: { + webhookUrl: 'http://localhost:123', + }, + name: 'Slack connector', +}); diff --git a/x-pack/test/detection_engine_api_integration/utils/index.ts b/x-pack/test/detection_engine_api_integration/utils/index.ts index 3fb9c8ebafc02..81bad1b1b583b 100644 --- a/x-pack/test/detection_engine_api_integration/utils/index.ts +++ b/x-pack/test/detection_engine_api_integration/utils/index.ts @@ -35,7 +35,10 @@ export * from './get_detection_metrics_from_body'; export * from './get_eql_rule_for_signal_testing'; export * from './get_event_log_execute_complete_by_id'; export * from './get_index_name_from_load'; +export * from './get_legacy_action_notification_so'; +export * from './get_legacy_action_notifications_so_by_id'; export * from './get_legacy_action_so'; +export * from './get_legacy_actions_so_by_id'; export * from './get_open_signals'; export * from './get_prepackaged_rule_status'; export * from './get_query_all_signals'; @@ -44,6 +47,7 @@ export * from './get_query_signals_ids'; export * from './get_query_signals_rule_id'; export * from './get_rule'; export * from './get_rule_for_signal_testing'; +export * from './get_rule_so_by_id'; export * from './get_rule_for_signal_testing_with_timestamp_override'; export * from './get_rule_with_web_hook_action'; export * from './get_saved_query_rule_for_signal_testing'; @@ -70,6 +74,7 @@ export * from './get_stats'; export * from './get_stats_url'; export * from './get_threat_match_rule_for_signal_testing'; export * from './get_threshold_rule_for_signal_testing'; +export * from './get_slack_action'; export * from './get_web_hook_action'; export * from './index_event_log_execution_events'; export * from './install_prepackaged_rules'; diff --git a/x-pack/test/detection_engine_api_integration/utils/index_event_log_execution_events.ts b/x-pack/test/detection_engine_api_integration/utils/index_event_log_execution_events.ts index 63f8c9f2c8e1b..8e354607d4002 100644 --- a/x-pack/test/detection_engine_api_integration/utils/index_event_log_execution_events.ts +++ b/x-pack/test/detection_engine_api_integration/utils/index_event_log_execution_events.ts @@ -19,8 +19,9 @@ export const indexEventLogExecutionEvents = async ( log: ToolingLog, events: object[] ): Promise => { + const aliases = await es.cat.aliases({ format: 'json', name: '.kibana-event-log-*' }); const operations = events.flatMap((doc: object) => [ - { index: { _index: '.kibana-event-log-*' } }, + { index: { _index: aliases[0].index } }, doc, ]); diff --git a/x-pack/test/encrypted_saved_objects_api_integration/tests/index.ts b/x-pack/test/encrypted_saved_objects_api_integration/tests/index.ts index de87d627ac486..0313187ac09a7 100644 --- a/x-pack/test/encrypted_saved_objects_api_integration/tests/index.ts +++ b/x-pack/test/encrypted_saved_objects_api_integration/tests/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('encryptedSavedObjects', function encryptedSavedObjectsSuite() { - this.tags('ciGroup13'); loadTestFile(require.resolve('./encrypted_saved_objects_api')); loadTestFile(require.resolve('./encrypted_saved_objects_decryption')); }); diff --git a/x-pack/test/endpoint_api_integration_no_ingest/apis/index.ts b/x-pack/test/endpoint_api_integration_no_ingest/apis/index.ts index 1ca035df18fd5..10a4e18f8c133 100644 --- a/x-pack/test/endpoint_api_integration_no_ingest/apis/index.ts +++ b/x-pack/test/endpoint_api_integration_no_ingest/apis/index.ts @@ -10,7 +10,6 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function endpointAPIIntegrationTests({ loadTestFile }: FtrProviderContext) { // Failing ES snapshot promotion: https://github.com/elastic/kibana/issues/70535 describe.skip('Endpoint plugin', function () { - this.tags('ciGroup7'); loadTestFile(require.resolve('./metadata')); }); } diff --git a/x-pack/test/examples/config.ts b/x-pack/test/examples/config.ts index 6eb1e86dd0826..16db620e76598 100644 --- a/x-pack/test/examples/config.ts +++ b/x-pack/test/examples/config.ts @@ -12,7 +12,9 @@ import fs from 'fs'; import { KIBANA_ROOT } from '@kbn/test'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xpackFunctionalConfig = await readConfigFile(require.resolve('../functional/config')); + const xpackFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); // Find all folders in /examples and /x-pack/examples since we treat all them as plugin folder const examplesFiles = fs.readdirSync(resolve(KIBANA_ROOT, 'examples')); diff --git a/x-pack/test/examples/embedded_lens/index.ts b/x-pack/test/examples/embedded_lens/index.ts index debab4773f9eb..b418e69584a9a 100644 --- a/x-pack/test/examples/embedded_lens/index.ts +++ b/x-pack/test/examples/embedded_lens/index.ts @@ -13,6 +13,8 @@ export default function ({ getService, loadTestFile }: PluginFunctionalProviderC const kibanaServer = getService('kibanaServer'); describe('embedded Lens examples', function () { + this.tags('skipFirefox'); + before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/logstash_functional'); await kibanaServer.importExport.load( @@ -30,10 +32,6 @@ export default function ({ getService, loadTestFile }: PluginFunctionalProviderC ); }); - describe('', function () { - this.tags(['ciGroup4', 'skipFirefox']); - - loadTestFile(require.resolve('./embedded_example')); - }); + loadTestFile(require.resolve('./embedded_example')); }); } diff --git a/x-pack/test/examples/reporting_examples/index.ts b/x-pack/test/examples/reporting_examples/index.ts index e4e5c93e5eee2..2f1b00597a9d8 100644 --- a/x-pack/test/examples/reporting_examples/index.ts +++ b/x-pack/test/examples/reporting_examples/index.ts @@ -10,8 +10,6 @@ import { PluginFunctionalProviderContext } from '../../../../test/plugin_functio // eslint-disable-next-line import/no-default-export export default function ({ loadTestFile }: PluginFunctionalProviderContext) { describe('reporting examples', function () { - this.tags('ciGroup13'); - loadTestFile(require.resolve('./capture_test')); }); } diff --git a/x-pack/test/examples/screenshotting/index.ts b/x-pack/test/examples/screenshotting/index.ts index b59663766389f..c64d84c7fcf3d 100644 --- a/x-pack/test/examples/screenshotting/index.ts +++ b/x-pack/test/examples/screenshotting/index.ts @@ -20,9 +20,9 @@ export default function ({ const testSubjects = getService('testSubjects'); const PageObjects = getPageObjects(['common']); - describe('Screenshotting Example', function () { + // FAILING: https://github.com/elastic/kibana/issues/131190 + describe.skip('Screenshotting Example', function () { before(async () => { - this.tags('ciGroup13'); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/visualize.json'); await PageObjects.common.navigateToApp('screenshottingExample'); diff --git a/x-pack/test/examples/search_examples/index.ts b/x-pack/test/examples/search_examples/index.ts index c0f8ab22c8f8f..cee873dfed53a 100644 --- a/x-pack/test/examples/search_examples/index.ts +++ b/x-pack/test/examples/search_examples/index.ts @@ -13,7 +13,6 @@ export default function ({ getService, loadTestFile }: PluginFunctionalProviderC const kibanaServer = getService('kibanaServer'); describe('search examples', function () { - this.tags('ciGroup13'); before(async () => { await esArchiver.emptyKibanaIndex(); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts index 8f2b3effd94ed..16f8fc04aa92f 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts @@ -569,6 +569,10 @@ const expectAssetsInstalled = ({ id: 'metrics-all_assets.test_metrics-all_assets', type: 'data_stream_ilm_policy', }, + { + id: 'all_assets', + type: 'ilm_policy', + }, { id: 'logs-all_assets.test_logs', type: 'index_template', diff --git a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts index b73ca9537990c..9758107cee83d 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts @@ -403,13 +403,17 @@ export default function (providerContext: FtrProviderContext) { ], installed_es: [ { - id: 'logs-all_assets.test_logs-all_assets', - type: 'data_stream_ilm_policy', + id: 'all_assets', + type: 'ilm_policy', }, { id: 'default', type: 'ml_model', }, + { + id: 'logs-all_assets.test_logs-all_assets', + type: 'data_stream_ilm_policy', + }, { id: 'logs-all_assets.test_logs-0.2.0', type: 'ingest_pipeline', diff --git a/x-pack/test/fleet_api_integration/apis/index.js b/x-pack/test/fleet_api_integration/apis/index.js index 9f68eb1c7f81f..1c528e719e2e8 100644 --- a/x-pack/test/fleet_api_integration/apis/index.js +++ b/x-pack/test/fleet_api_integration/apis/index.js @@ -9,8 +9,6 @@ import { setupTestUsers } from './test_users'; export default function ({ loadTestFile, getService }) { describe('Fleet Endpoints', function () { - this.tags('ciGroup29'); - before(async () => { await setupTestUsers(getService('security')); }); diff --git a/x-pack/test/fleet_api_integration/apis/mock_http_server.d.ts b/x-pack/test/fleet_api_integration/apis/mock_http_server.d.ts deleted file mode 100644 index ac8e88b6fefe3..0000000000000 --- a/x-pack/test/fleet_api_integration/apis/mock_http_server.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -// No types for mock-http-server available, but we don't need them. - -declare module 'mock-http-server'; diff --git a/x-pack/test/fleet_cypress/config.ts b/x-pack/test/fleet_cypress/config.ts index d2076fa940412..52198f4f035e0 100644 --- a/x-pack/test/fleet_cypress/config.ts +++ b/x-pack/test/fleet_cypress/config.ts @@ -14,7 +14,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { require.resolve('../../../test/common/config.js') ); const xpackFunctionalTestsConfig = await readConfigFile( - require.resolve('../functional/config.js') + require.resolve('../functional/config.base.js') ); return { diff --git a/x-pack/test/fleet_functional/apps/fleet/agents_page.ts b/x-pack/test/fleet_functional/apps/fleet/agents_page.ts index 515eaa65f5310..cff1273e0b901 100644 --- a/x-pack/test/fleet_functional/apps/fleet/agents_page.ts +++ b/x-pack/test/fleet_functional/apps/fleet/agents_page.ts @@ -11,8 +11,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const { agentsPage } = getPageObjects(['agentsPage']); describe('When in the Fleet application', function () { - this.tags(['ciGroup7']); - describe('and on the agents page', () => { before(async () => { await agentsPage.navigateToAgentsPage(); diff --git a/x-pack/test/fleet_functional/apps/fleet/index.ts b/x-pack/test/fleet_functional/apps/fleet/index.ts index ec16e2d857183..965d4c7776197 100644 --- a/x-pack/test/fleet_functional/apps/fleet/index.ts +++ b/x-pack/test/fleet_functional/apps/fleet/index.ts @@ -11,7 +11,6 @@ export default function (providerContext: FtrProviderContext) { const { loadTestFile } = providerContext; describe('endpoint', function () { - this.tags('ciGroup7'); loadTestFile(require.resolve('./agents_page')); }); } diff --git a/x-pack/test/fleet_functional/apps/home/index.ts b/x-pack/test/fleet_functional/apps/home/index.ts index cd14bfdff557d..727213b96349e 100644 --- a/x-pack/test/fleet_functional/apps/home/index.ts +++ b/x-pack/test/fleet_functional/apps/home/index.ts @@ -11,7 +11,6 @@ export default function (providerContext: FtrProviderContext) { const { loadTestFile } = providerContext; describe('home onboarding', function () { - this.tags('ciGroup7'); loadTestFile(require.resolve('./welcome')); }); } diff --git a/x-pack/test/fleet_functional/config.ts b/x-pack/test/fleet_functional/config.ts index 60db783280aec..5efc39b02acd6 100644 --- a/x-pack/test/fleet_functional/config.ts +++ b/x-pack/test/fleet_functional/config.ts @@ -11,7 +11,9 @@ import { pageObjects } from './page_objects'; import { services } from './services'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xpackFunctionalConfig = await readConfigFile(require.resolve('../functional/config.js')); + const xpackFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); return { ...xpackFunctionalConfig.getAll(), diff --git a/x-pack/test/functional/apps/advanced_settings/config.ts b/x-pack/test/functional/apps/advanced_settings/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/advanced_settings/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/advanced_settings/index.ts b/x-pack/test/functional/apps/advanced_settings/index.ts index d962f648db395..f121b031466ea 100644 --- a/x-pack/test/functional/apps/advanced_settings/index.ts +++ b/x-pack/test/functional/apps/advanced_settings/index.ts @@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function advancedSettingsApp({ loadTestFile }: FtrProviderContext) { describe('Advanced Settings', function canvasAppTestSuite() { - this.tags(['ciGroup2', 'skipFirefox']); // CI requires tags ヽ(゜Q。)ノ? + this.tags(['skipFirefox']); // CI requires tags ヽ(゜Q。)ノ? loadTestFile(require.resolve('./feature_controls/advanced_settings_security')); loadTestFile(require.resolve('./feature_controls/advanced_settings_spaces')); }); diff --git a/x-pack/test/functional/apps/api_keys/config.ts b/x-pack/test/functional/apps/api_keys/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/api_keys/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/api_keys/index.ts b/x-pack/test/functional/apps/api_keys/index.ts index 7b9afd201e3d9..2f9d7206d374a 100644 --- a/x-pack/test/functional/apps/api_keys/index.ts +++ b/x-pack/test/functional/apps/api_keys/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('API Keys app', function () { - this.tags(['ciGroup7']); loadTestFile(require.resolve('./home_page')); loadTestFile(require.resolve('./feature_controls')); }); diff --git a/x-pack/test/functional/apps/apm/config.ts b/x-pack/test/functional/apps/apm/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/apm/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/apm/correlations/latency_correlations.ts b/x-pack/test/functional/apps/apm/correlations/latency_correlations.ts index 22b553d006303..5cba074eedbec 100644 --- a/x-pack/test/functional/apps/apm/correlations/latency_correlations.ts +++ b/x-pack/test/functional/apps/apm/correlations/latency_correlations.ts @@ -70,7 +70,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await retry.try(async () => { const apmMainContainerText = await testSubjects.getVisibleTextAll('apmMainContainer'); const apmMainContainerTextItems = apmMainContainerText[0].split('\n'); - expect(apmMainContainerTextItems).to.not.contain('No services found'); expect(apmMainContainerTextItems).to.contain('opbeans-go'); diff --git a/x-pack/test/functional/apps/apm/index.ts b/x-pack/test/functional/apps/apm/index.ts index 20c2bc264a74f..61aca7ca3f9de 100644 --- a/x-pack/test/functional/apps/apm/index.ts +++ b/x-pack/test/functional/apps/apm/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('APM specs', function () { - this.tags('ciGroup10'); loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./correlations')); }); diff --git a/x-pack/test/functional/apps/canvas/config.ts b/x-pack/test/functional/apps/canvas/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/canvas/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/canvas/embeddables/lens.ts b/x-pack/test/functional/apps/canvas/embeddables/lens.ts index 5ecd3a3156909..748f17c720b53 100644 --- a/x-pack/test/functional/apps/canvas/embeddables/lens.ts +++ b/x-pack/test/functional/apps/canvas/embeddables/lens.ts @@ -10,7 +10,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function canvasLensTest({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); - const PageObjects = getPageObjects(['canvas', 'common', 'header', 'lens']); + const PageObjects = getPageObjects(['canvas', 'common', 'header', 'lens', 'unifiedSearch']); const esArchiver = getService('esArchiver'); const dashboardAddPanel = getService('dashboardAddPanel'); const dashboardPanelActions = getService('dashboardPanelActions'); @@ -68,6 +68,7 @@ export default function canvasLensTest({ getService, getPageObjects }: FtrProvid await PageObjects.canvas.deleteSelectedElement(); const originalEmbeddableCount = await PageObjects.canvas.getEmbeddableCount(); await PageObjects.canvas.createNewVis('lens'); + await PageObjects.unifiedSearch.closeTourPopoverByLocalStorage(); await PageObjects.lens.goToTimeRange(); await PageObjects.lens.configureDimension({ dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', diff --git a/x-pack/test/functional/apps/canvas/exports/8.2.workpad.ndjson b/x-pack/test/functional/apps/canvas/exports/8.2.workpad.ndjson new file mode 100644 index 0000000000000..b8a2c0f06827f --- /dev/null +++ b/x-pack/test/functional/apps/canvas/exports/8.2.workpad.ndjson @@ -0,0 +1,2 @@ +{"attributes":{"@created":"2022-05-09T15:35:33.151Z","@timestamp":"2022-05-09T15:35:57.797Z","assets":{},"colors":["#37988d","#c19628","#b83c6f","#3f9939","#1785b0","#ca5f35","#45bdb0","#f2bc33","#e74b8b","#4fbf48","#1ea6dc","#fd7643","#72cec3","#f5cc5d","#ec77a8","#7acf74","#4cbce4","#fd986f","#a1ded7","#f8dd91","#f2a4c5","#a6dfa2","#86d2ed","#fdba9f","#000000","#444444","#777777","#BBBBBB","#FFFFFF","rgba(255,255,255,0)"],"css":".canvasPage {\n\n}","height":720,"isWriteable":true,"name":"Test Canvas Workpad","page":0,"pages":[{"elements":[{"expression":"kibana\n| selectFilter\n| demodata\n| pointseries x=\"project\" y=\"sum(price)\" color=\"state\" size=\"size(username)\"\n| plot defaultStyle={seriesStyle points=5 fill=1}\n| render","filter":null,"id":"element-02d3f58d-b35a-42a1-8a91-ee6a6af99b8a","position":{"angle":0,"height":300,"left":229,"parent":null,"top":211,"width":700}}],"groups":[],"id":"page-55e10c30-e5ff-443e-bb9d-47264b477412","style":{"background":"#FFF"},"transition":{}}],"variables":[],"width":1080},"coreMigrationVersion":"8.2.0","id":"workpad-c25be373-fb42-49b5-9515-d13cbc041d46","migrationVersion":{"canvas-workpad":"8.2.0"},"references":[],"type":"canvas-workpad","updated_at":"2022-05-09T15:35:57.822Z","version":"WzEyOSwxXQ=="} +{"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":1,"missingRefCount":0,"missingReferences":[]} \ No newline at end of file diff --git a/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts b/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts index 1497c85b91bad..d1a500be98ff8 100644 --- a/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts +++ b/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts @@ -94,7 +94,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it(`allows a workpad to be edited`, async () => { await PageObjects.common.navigateToActualUrl( 'canvas', - 'workpad/workpad-1705f884-6224-47de-ba49-ca224fe6ec31', + '/workpad/workpad-1705f884-6224-47de-ba49-ca224fe6ec31', { ensureCurrentUrl: true, shouldLoginIfPrompted: false, @@ -171,7 +171,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it(`does not allow a workpad to be edited`, async () => { await PageObjects.common.navigateToActualUrl( 'canvas', - 'workpad/workpad-1705f884-6224-47de-ba49-ca224fe6ec31', + '/workpad/workpad-1705f884-6224-47de-ba49-ca224fe6ec31', { ensureCurrentUrl: true, shouldLoginIfPrompted: false, diff --git a/x-pack/test/functional/apps/canvas/feature_controls/canvas_spaces.ts b/x-pack/test/functional/apps/canvas/feature_controls/canvas_spaces.ts index 5060ac60eceae..e030cdbf5f624 100644 --- a/x-pack/test/functional/apps/canvas/feature_controls/canvas_spaces.ts +++ b/x-pack/test/functional/apps/canvas/feature_controls/canvas_spaces.ts @@ -80,7 +80,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it(`allows a workpad to be edited`, async () => { await PageObjects.common.navigateToActualUrl( 'canvas', - 'workpad/workpad-1705f884-6224-47de-ba49-ca224fe6ec31', + '/workpad/workpad-1705f884-6224-47de-ba49-ca224fe6ec31', { ensureCurrentUrl: true, shouldLoginIfPrompted: false, diff --git a/x-pack/test/functional/apps/canvas/index.js b/x-pack/test/functional/apps/canvas/index.js index 784aeb6655768..b572642f14df6 100644 --- a/x-pack/test/functional/apps/canvas/index.js +++ b/x-pack/test/functional/apps/canvas/index.js @@ -9,36 +9,41 @@ export default function canvasApp({ loadTestFile, getService }) { const security = getService('security'); const esArchiver = getService('esArchiver'); - describe('Canvas app', function canvasAppTestSuite() { - before(async () => { - // init data - await security.testUser.setRoles([ - 'test_logstash_reader', - 'global_canvas_all', - 'global_discover_all', - 'global_maps_all', - // TODO: Fix permission check, save and return button is disabled when dashboard is disabled - 'global_dashboard_all', - ]); - await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); - }); + describe('Canvas', function canvasAppTestSuite() { + describe('Canvas app', () => { + before(async () => { + // init data + await security.testUser.setRoles([ + 'test_logstash_reader', + 'global_canvas_all', + 'global_discover_all', + 'global_maps_all', + // TODO: Fix permission check, save and return button is disabled when dashboard is disabled + 'global_dashboard_all', + ]); + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); + }); + + after(async () => { + await security.testUser.restoreDefaults(); + }); - after(async () => { - await security.testUser.restoreDefaults(); + loadTestFile(require.resolve('./smoke_test')); + loadTestFile(require.resolve('./expression')); + loadTestFile(require.resolve('./filters')); + loadTestFile(require.resolve('./custom_elements')); + loadTestFile(require.resolve('./feature_controls/canvas_security')); + loadTestFile(require.resolve('./feature_controls/canvas_spaces')); + loadTestFile(require.resolve('./embeddables/lens')); + loadTestFile(require.resolve('./embeddables/maps')); + loadTestFile(require.resolve('./embeddables/saved_search')); + loadTestFile(require.resolve('./embeddables/visualization')); + loadTestFile(require.resolve('./reports')); + loadTestFile(require.resolve('./saved_object_resolve')); }); - this.tags('ciGroup2'); - loadTestFile(require.resolve('./smoke_test')); - loadTestFile(require.resolve('./expression')); - loadTestFile(require.resolve('./filters')); - loadTestFile(require.resolve('./custom_elements')); - loadTestFile(require.resolve('./feature_controls/canvas_security')); - loadTestFile(require.resolve('./feature_controls/canvas_spaces')); - loadTestFile(require.resolve('./embeddables/lens')); - loadTestFile(require.resolve('./embeddables/maps')); - loadTestFile(require.resolve('./embeddables/saved_search')); - loadTestFile(require.resolve('./embeddables/visualization')); - loadTestFile(require.resolve('./reports')); - loadTestFile(require.resolve('./saved_object_resolve')); + describe('Canvas management', () => { + loadTestFile(require.resolve('./migrations_smoke_test')); + }); }); } diff --git a/x-pack/test/functional/apps/canvas/migrations_smoke_test.ts b/x-pack/test/functional/apps/canvas/migrations_smoke_test.ts new file mode 100644 index 0000000000000..12a75e5a2ba8b --- /dev/null +++ b/x-pack/test/functional/apps/canvas/migrations_smoke_test.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 path from 'path'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const PageObjects = getPageObjects(['common', 'settings', 'savedObjects']); + + describe('migration smoke test', function () { + it('imports an 8.2 workpad', async function () { + /* + In 8.1 Canvas introduced by value embeddables, which requires expressions to know about embeddable migrations + Starting in 8.3, we were seeing an error during migration where it would appear that an 8.2 workpad was + from a future version. This was because there were missing embeddable migrations on the expression because + the Canvas plugin was adding the embeddable expression with all of it's migrations before other embeddables had + registered their own migrations. + + This smoke test is intended to import an 8.2 workpad to ensure that we don't hit a similar scenario in the future + */ + await PageObjects.settings.navigateTo(); + await PageObjects.settings.clickKibanaSavedObjects(); + await PageObjects.savedObjects.waitTableIsLoaded(); + await PageObjects.savedObjects.importFile( + path.join(__dirname, 'exports', '8.2.workpad.ndjson') + ); + await PageObjects.savedObjects.checkImportSucceeded(); + await PageObjects.savedObjects.clickImportDone(); + }); + }); +} diff --git a/x-pack/test/functional/apps/canvas/smoke_test.js b/x-pack/test/functional/apps/canvas/smoke_test.js index 053c58e88cde1..dff3cb7ce30a6 100644 --- a/x-pack/test/functional/apps/canvas/smoke_test.js +++ b/x-pack/test/functional/apps/canvas/smoke_test.js @@ -48,9 +48,9 @@ export default function canvasSmokeTest({ getService, getPageObjects }) { await retry.try(async () => { const url = await browser.getCurrentUrl(); - // remove all the search params, just compare the route - const hashRoute = new URL(url).hash.split('?')[0]; - expect(hashRoute).to.equal(`#/workpad/${testWorkpadId}/page/1`); + const path = new URL(url).pathname; + + expect(path).to.equal(`/app/canvas/workpad/${testWorkpadId}/page/1`); }); }); diff --git a/x-pack/test/functional/apps/cross_cluster_replication/config.ts b/x-pack/test/functional/apps/cross_cluster_replication/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/cross_cluster_replication/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/cross_cluster_replication/index.ts b/x-pack/test/functional/apps/cross_cluster_replication/index.ts index 1ab1ab7183394..5c6539b5e73f7 100644 --- a/x-pack/test/functional/apps/cross_cluster_replication/index.ts +++ b/x-pack/test/functional/apps/cross_cluster_replication/index.ts @@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('Cross Cluster Replication app', function () { - this.tags(['ciGroup4', 'skipCloud']); + this.tags('skipCloud'); loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./home_page')); }); diff --git a/x-pack/test/functional/apps/dashboard/README.md b/x-pack/test/functional/apps/dashboard/README.md new file mode 100644 index 0000000000000..5e87a8b210bdd --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/README.md @@ -0,0 +1,7 @@ +# What are all these groups? + +These tests take a while so they have been broken up into groups with their own `config.ts` and `index.ts` file, causing each of these groups to be independent bundles of tests which can be run on some worker in CI without taking an incredible amount of time. + +Want to change the groups to something more logical? Have fun! Just make sure that each group executes on CI in less than 10 minutes or so. We don't currently have any mechanism for validating this right now, you just need to look at the times in the log output on CI, but we'll be working on tooling for making this information more accessible soon. + +- Kibana Operations \ No newline at end of file diff --git a/x-pack/test/functional/apps/dashboard/drilldowns/index.ts b/x-pack/test/functional/apps/dashboard/drilldowns/index.ts deleted file mode 100644 index fac0c355ce4d0..0000000000000 --- a/x-pack/test/functional/apps/dashboard/drilldowns/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ loadTestFile, getService }: FtrProviderContext) { - const kibanaServer = getService('kibanaServer'); - const esArchiver = getService('esArchiver'); - - describe('drilldowns', function () { - this.tags(['skipFirefox']); - - before(async () => { - await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); - await esArchiver.load('x-pack/test/functional/es_archives/dashboard/drilldowns'); - await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' }); - }); - - after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/dashboard/drilldowns'); - }); - - loadTestFile(require.resolve('./dashboard_to_dashboard_drilldown')); - loadTestFile(require.resolve('./dashboard_to_url_drilldown')); - // Requires xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled - // setting set in kibana.yml to work (not enabled by default) - loadTestFile(require.resolve('./explore_data_panel_action')); - - // Disabled for now as it requires xpack.discoverEnhanced.actions.exploreDataInChart.enabled - // setting set in kibana.yml to work. Once that is enabled by default, we can re-enable this test suite. - // loadTestFile(require.resolve('./explore_data_chart_action')); - }); -} diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/index.ts b/x-pack/test/functional/apps/dashboard/feature_controls/index.ts deleted file mode 100644 index 3b32ea031f6e2..0000000000000 --- a/x-pack/test/functional/apps/dashboard/feature_controls/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ loadTestFile }: FtrProviderContext) { - describe('feature controls', function () { - this.tags(['skipFirefox']); - loadTestFile(require.resolve('./dashboard_security')); - loadTestFile(require.resolve('./time_to_visualize_security')); - loadTestFile(require.resolve('./dashboard_spaces')); - }); -} diff --git a/x-pack/test/functional/apps/dashboard/group1/config.ts b/x-pack/test/functional/apps/dashboard/group1/config.ts new file mode 100644 index 0000000000000..d927f93adeffd --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/group1/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_dashboard_drilldown.ts b/x-pack/test/functional/apps/dashboard/group1/drilldowns/dashboard_to_dashboard_drilldown.ts similarity index 99% rename from x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_dashboard_drilldown.ts rename to x-pack/test/functional/apps/dashboard/group1/drilldowns/dashboard_to_dashboard_drilldown.ts index 9fd6beeb8ff22..c540d5673d89e 100644 --- a/x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_dashboard_drilldown.ts +++ b/x-pack/test/functional/apps/dashboard/group1/drilldowns/dashboard_to_dashboard_drilldown.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; const DRILLDOWN_TO_PIE_CHART_NAME = 'Go to pie chart dashboard'; const DRILLDOWN_TO_AREA_CHART_NAME = 'Go to area chart dashboard'; diff --git a/x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_url_drilldown.ts b/x-pack/test/functional/apps/dashboard/group1/drilldowns/dashboard_to_url_drilldown.ts similarity index 98% rename from x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_url_drilldown.ts rename to x-pack/test/functional/apps/dashboard/group1/drilldowns/dashboard_to_url_drilldown.ts index 5ed118c9b753a..ca057b7421b7c 100644 --- a/x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_url_drilldown.ts +++ b/x-pack/test/functional/apps/dashboard/group1/drilldowns/dashboard_to_url_drilldown.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; const DRILLDOWN_TO_DISCOVER_URL = 'Go to discover'; diff --git a/x-pack/test/functional/apps/dashboard/drilldowns/explore_data_chart_action.ts b/x-pack/test/functional/apps/dashboard/group1/drilldowns/explore_data_chart_action.ts similarity index 98% rename from x-pack/test/functional/apps/dashboard/drilldowns/explore_data_chart_action.ts rename to x-pack/test/functional/apps/dashboard/group1/drilldowns/explore_data_chart_action.ts index b9272a0a6c3bd..2e2c1f7ecca1d 100644 --- a/x-pack/test/functional/apps/dashboard/drilldowns/explore_data_chart_action.ts +++ b/x-pack/test/functional/apps/dashboard/group1/drilldowns/explore_data_chart_action.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; const ACTION_ID = 'ACTION_EXPLORE_DATA_CHART'; const ACTION_TEST_SUBJ = `embeddablePanelAction-${ACTION_ID}`; diff --git a/x-pack/test/functional/apps/dashboard/drilldowns/explore_data_panel_action.ts b/x-pack/test/functional/apps/dashboard/group1/drilldowns/explore_data_panel_action.ts similarity index 96% rename from x-pack/test/functional/apps/dashboard/drilldowns/explore_data_panel_action.ts rename to x-pack/test/functional/apps/dashboard/group1/drilldowns/explore_data_panel_action.ts index c78c716364c4b..1dafddbb8567b 100644 --- a/x-pack/test/functional/apps/dashboard/drilldowns/explore_data_panel_action.ts +++ b/x-pack/test/functional/apps/dashboard/group1/drilldowns/explore_data_panel_action.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; const ACTION_ID = 'ACTION_EXPLORE_DATA'; const ACTION_TEST_SUBJ = `embeddablePanelAction-${ACTION_ID}`; @@ -67,7 +67,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.clickWhenNotDisabled(ACTION_TEST_SUBJ); await discover.waitForDiscoverAppOnScreen(); - const el = await testSubjects.find('indexPattern-switch-link'); + const el = await testSubjects.find('discover-dataView-switch-link'); const text = await el.getVisibleText(); expect(text).to.be('logstash-*'); diff --git a/x-pack/test/functional/apps/dashboard/group1/drilldowns/index.ts b/x-pack/test/functional/apps/dashboard/group1/drilldowns/index.ts new file mode 100644 index 0000000000000..eaa1189ab1007 --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/group1/drilldowns/index.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile, getService }: FtrProviderContext) { + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + + describe('drilldowns', function () { + this.tags(['skipFirefox']); + + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); + await esArchiver.load('x-pack/test/functional/es_archives/dashboard/drilldowns'); + await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' }); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/dashboard/drilldowns'); + }); + + loadTestFile(require.resolve('./dashboard_to_dashboard_drilldown')); + loadTestFile(require.resolve('./dashboard_to_url_drilldown')); + // Requires xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled + // setting set in kibana.yml to work (not enabled by default) + loadTestFile(require.resolve('./explore_data_panel_action')); + + // Disabled for now as it requires xpack.discoverEnhanced.actions.exploreDataInChart.enabled + // setting set in kibana.yml to work. Once that is enabled by default, we can re-enable this test suite. + // loadTestFile(require.resolve('./explore_data_chart_action')); + }); +} diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts b/x-pack/test/functional/apps/dashboard/group1/feature_controls/dashboard_security.ts similarity index 98% rename from x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts rename to x-pack/test/functional/apps/dashboard/group1/feature_controls/dashboard_security.ts index efac6d6739fcb..6b08a9455b644 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts +++ b/x-pack/test/functional/apps/dashboard/group1/feature_controls/dashboard_security.ts @@ -10,7 +10,7 @@ import { createDashboardEditUrl, DashboardConstants, } from '@kbn/dashboard-plugin/public/dashboard_constants'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); @@ -228,6 +228,12 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { true, false ); + const contextMenuPanelTitleButton = await testSubjects.exists( + 'contextMenuPanelTitleButton' + ); + if (contextMenuPanelTitleButton) { + await testSubjects.click('contextMenuPanelTitleButton'); + } await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); const queryString = await queryBar.getQueryString(); @@ -244,6 +250,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('allow saving currently loaded query as a copy', async () => { await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); + await queryBar.setQuery('response:404'); await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( 'ok2', 'description', diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts b/x-pack/test/functional/apps/dashboard/group1/feature_controls/dashboard_spaces.ts similarity index 98% rename from x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts rename to x-pack/test/functional/apps/dashboard/group1/feature_controls/dashboard_spaces.ts index 0d177a0f3c65b..24a90b883315e 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts +++ b/x-pack/test/functional/apps/dashboard/group1/feature_controls/dashboard_spaces.ts @@ -10,7 +10,7 @@ import { createDashboardEditUrl, DashboardConstants, } from '@kbn/dashboard-plugin/public/dashboard_constants'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); diff --git a/x-pack/test/functional/apps/dashboard/group1/feature_controls/index.ts b/x-pack/test/functional/apps/dashboard/group1/feature_controls/index.ts new file mode 100644 index 0000000000000..2ea15966f3740 --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/group1/feature_controls/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('feature controls', function () { + this.tags(['skipFirefox']); + loadTestFile(require.resolve('./dashboard_security')); + loadTestFile(require.resolve('./time_to_visualize_security')); + loadTestFile(require.resolve('./dashboard_spaces')); + }); +} diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts b/x-pack/test/functional/apps/dashboard/group1/feature_controls/time_to_visualize_security.ts similarity index 99% rename from x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts rename to x-pack/test/functional/apps/dashboard/group1/feature_controls/time_to_visualize_security.ts index 9eeb49f5eb0d2..57ddc76835213 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts +++ b/x-pack/test/functional/apps/dashboard/group1/feature_controls/time_to_visualize_security.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const PageObjects = getPageObjects([ diff --git a/x-pack/test/functional/apps/dashboard/group1/index.ts b/x-pack/test/functional/apps/dashboard/group1/index.ts new file mode 100644 index 0000000000000..f829002448f33 --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/group1/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('dashboard', function () { + loadTestFile(require.resolve('./feature_controls')); + loadTestFile(require.resolve('./preserve_url')); + loadTestFile(require.resolve('./reporting')); + loadTestFile(require.resolve('./drilldowns')); + }); +} diff --git a/x-pack/test/functional/apps/dashboard/preserve_url.ts b/x-pack/test/functional/apps/dashboard/group1/preserve_url.ts similarity index 97% rename from x-pack/test/functional/apps/dashboard/preserve_url.ts rename to x-pack/test/functional/apps/dashboard/group1/preserve_url.ts index e391a8b346f6f..88ad055d34c20 100644 --- a/x-pack/test/functional/apps/dashboard/preserve_url.ts +++ b/x-pack/test/functional/apps/dashboard/group1/preserve_url.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); diff --git a/x-pack/test/functional/apps/dashboard/group1/reporting/README.md b/x-pack/test/functional/apps/dashboard/group1/reporting/README.md new file mode 100644 index 0000000000000..149b691dc3115 --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/group1/reporting/README.md @@ -0,0 +1,22 @@ +## The Dashboard Reporting Tests + +### Baseline snapshots + +The reporting tests create a few PNG reports and do a snapshot comparison against stored baselines. The baseline images are stored in `./reports/baseline`. + +### Updating the baselines + +Every now and then visual changes will be made that will require the snapshots to be updated. This is how you go about updating it. + +1. **Load the ES Archive containing the dashboard and data.** + This will load the test data into an Elasticsearch instance running via the functional test server: + ``` + node scripts/es_archiver load reporting/ecommerce --config=x-pack/test/functional/apps/dashboard/config.ts + node scripts/es_archiver load reporting/ecommerce_kibana --config=x-pack/test/functional/apps/dashboard/config.ts + ``` +2. **Generate the reports of the E-commerce dashboard in the Kibana UI.** + Navigate to `http://localhost:5620`, find the archived dashboard, and generate all the types of reports for which there are stored baseline images. +3. **Download the reports, and save them into the `reports/baseline` folder.** + Change the names of the PNG/PDF files to overwrite the stored baselines. + +The next time functional tests run, the generated reports will be compared to the latest image that you have saved :bowtie: \ No newline at end of file diff --git a/x-pack/test/functional/apps/dashboard/reporting/__snapshots__/download_csv.snap b/x-pack/test/functional/apps/dashboard/group1/reporting/__snapshots__/download_csv.snap similarity index 99% rename from x-pack/test/functional/apps/dashboard/reporting/__snapshots__/download_csv.snap rename to x-pack/test/functional/apps/dashboard/group1/reporting/__snapshots__/download_csv.snap index d524543183a3f..e6b31be13861d 100644 --- a/x-pack/test/functional/apps/dashboard/reporting/__snapshots__/download_csv.snap +++ b/x-pack/test/functional/apps/dashboard/group1/reporting/__snapshots__/download_csv.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`dashboard Reporting Download CSV Default Saved Search Data Download CSV export of a saved search panel 1`] = ` +exports[`dashboard Reporting Download CSV Default Saved Search Data Download CSV export of a saved search panel 1`] = ` "\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"products.created_on\\",sku \\"Jun 22, 2019 @ 00:00:00.000\\",\\"Men's Clothing, Men's Shoes\\",EUR,23,564670,6,\\"Dec 11, 2016 @ 00:00:00.000, Dec 11, 2016 @ 00:00:00.000\\",\\"ZO0531205312, ZO0684706847\\" \\"Jun 22, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,44,564710,6,\\"Dec 11, 2016 @ 00:00:00.000, Dec 11, 2016 @ 00:00:00.000\\",\\"ZO0263402634, ZO0499404994\\" @@ -460,7 +460,7 @@ exports[`dashboard Reporting Download CSV Default Saved Search Data Download CS " `; -exports[`dashboard Reporting Download CSV Default Saved Search Data Downloads a filtered CSV export of a saved search panel 1`] = ` +exports[`dashboard Reporting Download CSV Default Saved Search Data Downloads a filtered CSV export of a saved search panel 1`] = ` "\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"products.created_on\\",sku \\"Jun 22, 2019 @ 00:00:00.000\\",\\"Men's Clothing, Men's Shoes\\",EUR,23,564670,6,\\"Dec 11, 2016 @ 00:00:00.000, Dec 11, 2016 @ 00:00:00.000\\",\\"ZO0531205312, ZO0684706847\\" \\"Jun 22, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,52,564513,6,\\"Dec 11, 2016 @ 00:00:00.000, Dec 11, 2016 @ 00:00:00.000\\",\\"ZO0390003900, ZO0287902879\\" @@ -562,13 +562,13 @@ exports[`dashboard Reporting Download CSV Default Saved Search Data Downloads a " `; -exports[`dashboard Reporting Download CSV Field Formatters and Scripted Fields Download CSV export of a saved search panel 1`] = ` +exports[`dashboard Reporting Download CSV Field Formatters and Scripted Fields Download CSV export of a saved search panel 1`] = ` "date,\\"_id\\",name,gender,value,year,\\"years_ago\\",\\"date_informal\\" \\"Jan 1, 1982 @ 00:00:00.000\\",\\"1982-Fethany-F\\",Fethany,F,780,1982,\\"37.00000000000000000000\\",\\"Jan 1st 82\\" " `; -exports[`dashboard Reporting Download CSV Filtered Saved Search Downloads filtered Discover saved search report 1`] = ` +exports[`dashboard Reporting Download CSV Filtered Saved Search Downloads filtered Discover saved search report 1`] = ` "\\"order_date\\",category,\\"customer_full_name\\",\\"taxful_total_price\\",currency \\"Jun 25, 2019 @ 00:00:00.000\\",\\"Women's Accessories\\",\\"Betty Reese\\",\\"22.984\\",EUR \\"Jun 25, 2019 @ 00:00:00.000\\",\\"Women's Accessories, Women's Clothing\\",\\"Betty Brewer\\",\\"28.984\\",EUR diff --git a/x-pack/test/functional/apps/dashboard/reporting/download_csv.ts b/x-pack/test/functional/apps/dashboard/group1/reporting/download_csv.ts similarity index 99% rename from x-pack/test/functional/apps/dashboard/reporting/download_csv.ts rename to x-pack/test/functional/apps/dashboard/group1/reporting/download_csv.ts index 1e2d465fee66b..bdcf95955b451 100644 --- a/x-pack/test/functional/apps/dashboard/reporting/download_csv.ts +++ b/x-pack/test/functional/apps/dashboard/group1/reporting/download_csv.ts @@ -9,7 +9,7 @@ import { REPO_ROOT } from '@kbn/utils'; import expect from '@kbn/expect'; import fs from 'fs'; import path from 'path'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); diff --git a/x-pack/test/functional/apps/dashboard/group1/reporting/index.ts b/x-pack/test/functional/apps/dashboard/group1/reporting/index.ts new file mode 100644 index 0000000000000..ef9821a544b3a --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/group1/reporting/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Reporting', function () { + loadTestFile(require.resolve('./screenshots')); + loadTestFile(require.resolve('./download_csv')); + }); +} diff --git a/x-pack/test/functional/apps/dashboard/reporting/reports/baseline/large_dashboard_preserve_layout.png b/x-pack/test/functional/apps/dashboard/group1/reporting/reports/baseline/large_dashboard_preserve_layout.png similarity index 100% rename from x-pack/test/functional/apps/dashboard/reporting/reports/baseline/large_dashboard_preserve_layout.png rename to x-pack/test/functional/apps/dashboard/group1/reporting/reports/baseline/large_dashboard_preserve_layout.png diff --git a/x-pack/test/functional/apps/dashboard/reporting/reports/baseline/small_dashboard_preserve_layout.png b/x-pack/test/functional/apps/dashboard/group1/reporting/reports/baseline/small_dashboard_preserve_layout.png similarity index 100% rename from x-pack/test/functional/apps/dashboard/reporting/reports/baseline/small_dashboard_preserve_layout.png rename to x-pack/test/functional/apps/dashboard/group1/reporting/reports/baseline/small_dashboard_preserve_layout.png diff --git a/x-pack/test/functional/apps/dashboard/reporting/screenshots.ts b/x-pack/test/functional/apps/dashboard/group1/reporting/screenshots.ts similarity index 99% rename from x-pack/test/functional/apps/dashboard/reporting/screenshots.ts rename to x-pack/test/functional/apps/dashboard/group1/reporting/screenshots.ts index d42d23b7578a5..5a69262801d2e 100644 --- a/x-pack/test/functional/apps/dashboard/reporting/screenshots.ts +++ b/x-pack/test/functional/apps/dashboard/group1/reporting/screenshots.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import path from 'path'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; const REPORTS_FOLDER = path.resolve(__dirname, 'reports'); diff --git a/x-pack/test/functional/apps/dashboard/_async_dashboard.ts b/x-pack/test/functional/apps/dashboard/group2/_async_dashboard.ts similarity index 98% rename from x-pack/test/functional/apps/dashboard/_async_dashboard.ts rename to x-pack/test/functional/apps/dashboard/group2/_async_dashboard.ts index 89c58645b64b0..ae79c1893ac8b 100644 --- a/x-pack/test/functional/apps/dashboard/_async_dashboard.ts +++ b/x-pack/test/functional/apps/dashboard/group2/_async_dashboard.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); diff --git a/x-pack/test/functional/apps/dashboard/group2/config.ts b/x-pack/test/functional/apps/dashboard/group2/config.ts new file mode 100644 index 0000000000000..d927f93adeffd --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/group2/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/dashboard/dashboard_lens_by_value.ts b/x-pack/test/functional/apps/dashboard/group2/dashboard_lens_by_value.ts similarity index 98% rename from x-pack/test/functional/apps/dashboard/dashboard_lens_by_value.ts rename to x-pack/test/functional/apps/dashboard/group2/dashboard_lens_by_value.ts index 9e4c2554100b9..2604b942f7313 100644 --- a/x-pack/test/functional/apps/dashboard/dashboard_lens_by_value.ts +++ b/x-pack/test/functional/apps/dashboard/group2/dashboard_lens_by_value.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'dashboard', 'visualize', 'lens', 'timePicker']); diff --git a/x-pack/test/functional/apps/dashboard/dashboard_maps_by_value.ts b/x-pack/test/functional/apps/dashboard/group2/dashboard_maps_by_value.ts similarity index 98% rename from x-pack/test/functional/apps/dashboard/dashboard_maps_by_value.ts rename to x-pack/test/functional/apps/dashboard/group2/dashboard_maps_by_value.ts index e885d1c0d3f73..c1224b604364d 100644 --- a/x-pack/test/functional/apps/dashboard/dashboard_maps_by_value.ts +++ b/x-pack/test/functional/apps/dashboard/group2/dashboard_maps_by_value.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const PageObjects = getPageObjects([ diff --git a/x-pack/test/functional/apps/dashboard/dashboard_tagging.ts b/x-pack/test/functional/apps/dashboard/group2/dashboard_tagging.ts similarity index 98% rename from x-pack/test/functional/apps/dashboard/dashboard_tagging.ts rename to x-pack/test/functional/apps/dashboard/group2/dashboard_tagging.ts index 784d1f3678415..ca0093135b7da 100644 --- a/x-pack/test/functional/apps/dashboard/dashboard_tagging.ts +++ b/x-pack/test/functional/apps/dashboard/group2/dashboard_tagging.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const listingTable = getService('listingTable'); diff --git a/x-pack/test/functional/apps/dashboard/group2/index.ts b/x-pack/test/functional/apps/dashboard/group2/index.ts new file mode 100644 index 0000000000000..666756735e80f --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/group2/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('dashboard', function () { + loadTestFile(require.resolve('./sync_colors')); + loadTestFile(require.resolve('./_async_dashboard')); + loadTestFile(require.resolve('./dashboard_tagging')); + loadTestFile(require.resolve('./dashboard_lens_by_value')); + loadTestFile(require.resolve('./dashboard_maps_by_value')); + loadTestFile(require.resolve('./panel_titles')); + + loadTestFile(require.resolve('./migration_smoke_tests/lens_migration_smoke_test')); + loadTestFile(require.resolve('./migration_smoke_tests/controls_migration_smoke_test')); + loadTestFile(require.resolve('./migration_smoke_tests/visualize_migration_smoke_test')); + loadTestFile(require.resolve('./migration_smoke_tests/tsvb_migration_smoke_test')); + }); +} diff --git a/x-pack/test/functional/apps/dashboard/migration_smoke_tests/controls_migration_smoke_test.ts b/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/controls_migration_smoke_test.ts similarity index 98% rename from x-pack/test/functional/apps/dashboard/migration_smoke_tests/controls_migration_smoke_test.ts rename to x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/controls_migration_smoke_test.ts index b87ee15910d23..20fe8a9413723 100644 --- a/x-pack/test/functional/apps/dashboard/migration_smoke_tests/controls_migration_smoke_test.ts +++ b/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/controls_migration_smoke_test.ts @@ -12,7 +12,7 @@ import expect from '@kbn/expect'; import path from 'path'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); diff --git a/x-pack/test/functional/apps/dashboard/migration_smoke_tests/exports/controls_dashboard_migration_test_8_0_0.ndjson b/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/exports/controls_dashboard_migration_test_8_0_0.ndjson similarity index 100% rename from x-pack/test/functional/apps/dashboard/migration_smoke_tests/exports/controls_dashboard_migration_test_8_0_0.ndjson rename to x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/exports/controls_dashboard_migration_test_8_0_0.ndjson diff --git a/x-pack/test/functional/apps/dashboard/migration_smoke_tests/exports/lens_dashboard_migration_test_7_12_1.ndjson b/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/exports/lens_dashboard_migration_test_7_12_1.ndjson similarity index 100% rename from x-pack/test/functional/apps/dashboard/migration_smoke_tests/exports/lens_dashboard_migration_test_7_12_1.ndjson rename to x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/exports/lens_dashboard_migration_test_7_12_1.ndjson diff --git a/x-pack/test/functional/apps/dashboard/migration_smoke_tests/exports/tsvb_dashboard_migration_test_7_12_1.ndjson b/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/exports/tsvb_dashboard_migration_test_7_12_1.ndjson similarity index 100% rename from x-pack/test/functional/apps/dashboard/migration_smoke_tests/exports/tsvb_dashboard_migration_test_7_12_1.ndjson rename to x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/exports/tsvb_dashboard_migration_test_7_12_1.ndjson diff --git a/x-pack/test/functional/apps/dashboard/migration_smoke_tests/exports/tsvb_dashboard_migration_test_7_13_3.ndjson b/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/exports/tsvb_dashboard_migration_test_7_13_3.ndjson similarity index 100% rename from x-pack/test/functional/apps/dashboard/migration_smoke_tests/exports/tsvb_dashboard_migration_test_7_13_3.ndjson rename to x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/exports/tsvb_dashboard_migration_test_7_13_3.ndjson diff --git a/x-pack/test/functional/apps/dashboard/migration_smoke_tests/exports/visualize_dashboard_migration_test_7_12_1.ndjson b/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/exports/visualize_dashboard_migration_test_7_12_1.ndjson similarity index 100% rename from x-pack/test/functional/apps/dashboard/migration_smoke_tests/exports/visualize_dashboard_migration_test_7_12_1.ndjson rename to x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/exports/visualize_dashboard_migration_test_7_12_1.ndjson diff --git a/x-pack/test/functional/apps/dashboard/migration_smoke_tests/lens_migration_smoke_test.ts b/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/lens_migration_smoke_test.ts similarity index 97% rename from x-pack/test/functional/apps/dashboard/migration_smoke_tests/lens_migration_smoke_test.ts rename to x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/lens_migration_smoke_test.ts index 78b7ccfe7df08..32c6449ad97b5 100644 --- a/x-pack/test/functional/apps/dashboard/migration_smoke_tests/lens_migration_smoke_test.ts +++ b/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/lens_migration_smoke_test.ts @@ -11,7 +11,7 @@ import expect from '@kbn/expect'; import path from 'path'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); diff --git a/x-pack/test/functional/apps/dashboard/migration_smoke_tests/tsvb_migration_smoke_test.ts b/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/tsvb_migration_smoke_test.ts similarity index 98% rename from x-pack/test/functional/apps/dashboard/migration_smoke_tests/tsvb_migration_smoke_test.ts rename to x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/tsvb_migration_smoke_test.ts index 22606cf6d2862..0c3f9c652a6ec 100644 --- a/x-pack/test/functional/apps/dashboard/migration_smoke_tests/tsvb_migration_smoke_test.ts +++ b/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/tsvb_migration_smoke_test.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import path from 'path'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); diff --git a/x-pack/test/functional/apps/dashboard/migration_smoke_tests/visualize_migration_smoke_test.ts b/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/visualize_migration_smoke_test.ts similarity index 97% rename from x-pack/test/functional/apps/dashboard/migration_smoke_tests/visualize_migration_smoke_test.ts rename to x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/visualize_migration_smoke_test.ts index d3d6ca46cd227..f4e86217a4d75 100644 --- a/x-pack/test/functional/apps/dashboard/migration_smoke_tests/visualize_migration_smoke_test.ts +++ b/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/visualize_migration_smoke_test.ts @@ -11,7 +11,7 @@ import expect from '@kbn/expect'; import path from 'path'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); diff --git a/x-pack/test/functional/apps/dashboard/panel_titles.ts b/x-pack/test/functional/apps/dashboard/group2/panel_titles.ts similarity index 99% rename from x-pack/test/functional/apps/dashboard/panel_titles.ts rename to x-pack/test/functional/apps/dashboard/group2/panel_titles.ts index 3db72ba8d0a90..34b7e14d26a1a 100644 --- a/x-pack/test/functional/apps/dashboard/panel_titles.ts +++ b/x-pack/test/functional/apps/dashboard/group2/panel_titles.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); diff --git a/x-pack/test/functional/apps/dashboard/sync_colors.ts b/x-pack/test/functional/apps/dashboard/group2/sync_colors.ts similarity index 98% rename from x-pack/test/functional/apps/dashboard/sync_colors.ts rename to x-pack/test/functional/apps/dashboard/group2/sync_colors.ts index a3628635dfa2f..093318bb8b5cd 100644 --- a/x-pack/test/functional/apps/dashboard/sync_colors.ts +++ b/x-pack/test/functional/apps/dashboard/group2/sync_colors.ts @@ -7,7 +7,7 @@ import { DebugState } from '@elastic/charts'; import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); diff --git a/x-pack/test/functional/apps/dashboard/index.ts b/x-pack/test/functional/apps/dashboard/index.ts deleted file mode 100644 index baf37ff56bd80..0000000000000 --- a/x-pack/test/functional/apps/dashboard/index.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 { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ loadTestFile }: FtrProviderContext) { - describe('dashboard', function () { - describe('', function () { - this.tags('ciGroup19'); - loadTestFile(require.resolve('./feature_controls')); - loadTestFile(require.resolve('./preserve_url')); - loadTestFile(require.resolve('./reporting')); - loadTestFile(require.resolve('./drilldowns')); - }); - - describe('', function () { - this.tags('ciGroup31'); - loadTestFile(require.resolve('./sync_colors')); - loadTestFile(require.resolve('./_async_dashboard')); - loadTestFile(require.resolve('./dashboard_tagging')); - loadTestFile(require.resolve('./dashboard_lens_by_value')); - loadTestFile(require.resolve('./dashboard_maps_by_value')); - loadTestFile(require.resolve('./panel_titles')); - - loadTestFile(require.resolve('./migration_smoke_tests/lens_migration_smoke_test')); - loadTestFile(require.resolve('./migration_smoke_tests/controls_migration_smoke_test')); - loadTestFile(require.resolve('./migration_smoke_tests/visualize_migration_smoke_test')); - loadTestFile(require.resolve('./migration_smoke_tests/tsvb_migration_smoke_test')); - }); - }); -} diff --git a/x-pack/test/functional/apps/dashboard/reporting/README.md b/x-pack/test/functional/apps/dashboard/reporting/README.md deleted file mode 100644 index 3a2b8f5cc783f..0000000000000 --- a/x-pack/test/functional/apps/dashboard/reporting/README.md +++ /dev/null @@ -1,22 +0,0 @@ -## The Dashboard Reporting Tests - -### Baseline snapshots - -The reporting tests create a few PNG reports and do a snapshot comparison against stored baselines. The baseline images are stored in `./reports/baseline`. - -### Updating the baselines - -Every now and then visual changes will be made that will require the snapshots to be updated. This is how you go about updating it. - -1. **Load the ES Archive containing the dashboard and data.** - This will load the test data into an Elasticsearch instance running via the functional test server: - ``` - node scripts/es_archiver load reporting/ecommerce --config=x-pack/test/functional/config.js - node scripts/es_archiver load reporting/ecommerce_kibana --config=x-pack/test/functional/config.js - ``` -2. **Generate the reports of the E-commerce dashboard in the Kibana UI.** - Navigate to `http://localhost:5620`, find the archived dashboard, and generate all the types of reports for which there are stored baseline images. -3. **Download the reports, and save them into the `reports/baseline` folder.** - Change the names of the PNG/PDF files to overwrite the stored baselines. - -The next time functional tests run, the generated reports will be compared to the latest image that you have saved :bowtie: \ No newline at end of file diff --git a/x-pack/test/functional/apps/dashboard/reporting/index.ts b/x-pack/test/functional/apps/dashboard/reporting/index.ts deleted file mode 100644 index 088e54d534b6a..0000000000000 --- a/x-pack/test/functional/apps/dashboard/reporting/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ loadTestFile }: FtrProviderContext) { - describe('Reporting', function () { - loadTestFile(require.resolve('./screenshots')); - loadTestFile(require.resolve('./download_csv')); - }); -} diff --git a/x-pack/test/functional/apps/data_views/config.ts b/x-pack/test/functional/apps/data_views/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/data_views/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/data_views/index.ts b/x-pack/test/functional/apps/data_views/index.ts index 3b3f7b3608113..3284dc901c25a 100644 --- a/x-pack/test/functional/apps/data_views/index.ts +++ b/x-pack/test/functional/apps/data_views/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function advancedSettingsApp({ loadTestFile }: FtrProviderContext) { describe('Data Views', function indexPatternsTestSuite() { - this.tags('ciGroup2'); loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./spaces')); }); diff --git a/x-pack/test/functional/apps/dev_tools/config.ts b/x-pack/test/functional/apps/dev_tools/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/dev_tools/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/dev_tools/index.ts b/x-pack/test/functional/apps/dev_tools/index.ts index 4f0e9290cc25e..6ef1688bb4c4e 100644 --- a/x-pack/test/functional/apps/dev_tools/index.ts +++ b/x-pack/test/functional/apps/dev_tools/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('Dev Tools', function () { - this.tags('ciGroup13'); - loadTestFile(require.resolve('./breadcrumbs')); loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./searchprofiler_editor')); diff --git a/x-pack/test/functional/apps/discover/config.ts b/x-pack/test/functional/apps/discover/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/discover/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index 0a12de3fb44d6..1f4cfa15fa892 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -20,6 +20,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'security', 'share', 'spaceSelector', + 'header', ]); const testSubjects = getService('testSubjects'); const appsMenu = getService('appsMenu'); @@ -152,13 +153,17 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('allow saving currently loaded query as a copy', async () => { await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); + await queryBar.setQuery('response:404'); await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( 'ok2', 'description', true, false ); + await PageObjects.header.waitUntilLoadingHasFinished(); await savedQueryManagementComponent.savedQueryExistOrFail('ok2'); + await savedQueryManagementComponent.closeSavedQueryManagementComponent(); + await testSubjects.click('showQueryBarMenu'); await savedQueryManagementComponent.deleteSavedQuery('ok2'); }); }); diff --git a/x-pack/test/functional/apps/discover/index.ts b/x-pack/test/functional/apps/discover/index.ts index 9eda11bc6e6fb..bb5ac2f8ea9d4 100644 --- a/x-pack/test/functional/apps/discover/index.ts +++ b/x-pack/test/functional/apps/discover/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('discover', function () { - this.tags('ciGroup25'); - loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./preserve_url')); loadTestFile(require.resolve('./async_scripted_fields')); diff --git a/x-pack/test/functional/apps/discover/reporting.ts b/x-pack/test/functional/apps/discover/reporting.ts index 55282dd143b7f..d1eb2e7e03c27 100644 --- a/x-pack/test/functional/apps/discover/reporting.ts +++ b/x-pack/test/functional/apps/discover/reporting.ts @@ -270,6 +270,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await setupPage(); }); + afterEach(async () => { + await PageObjects.reporting.checkForReportingToasts(); + }); + it('generates a report with data', async () => { await PageObjects.discover.loadSavedSearch('Ecommerce Data'); await retry.try(async () => { diff --git a/x-pack/test/functional/apps/graph/config.ts b/x-pack/test/functional/apps/graph/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/graph/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts index 9179373cf610c..a1f0e3db2c187 100644 --- a/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts +++ b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts @@ -16,8 +16,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const appsMenu = getService('appsMenu'); const globalNav = getService('globalNav'); - // FLAKY https://github.com/elastic/kibana/issues/109564 - describe.skip('security', () => { + describe('security', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/empty_kibana'); // ensure we're logged out so we can login as the appropriate users diff --git a/x-pack/test/functional/apps/graph/index.ts b/x-pack/test/functional/apps/graph/index.ts index 561cf8e5833d4..ca0b02e8b0f7d 100644 --- a/x-pack/test/functional/apps/graph/index.ts +++ b/x-pack/test/functional/apps/graph/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('graph app', function () { - this.tags('ciGroup12'); - loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./graph')); }); diff --git a/x-pack/test/functional/apps/grok_debugger/config.ts b/x-pack/test/functional/apps/grok_debugger/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/grok_debugger/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/grok_debugger/index.ts b/x-pack/test/functional/apps/grok_debugger/index.ts index 1fed41f6e3e36..7b0bd70508b6f 100644 --- a/x-pack/test/functional/apps/grok_debugger/index.ts +++ b/x-pack/test/functional/apps/grok_debugger/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('Grok Debugger App', function () { - this.tags('ciGroup13'); loadTestFile(require.resolve('./home_page')); }); }; diff --git a/x-pack/test/functional/apps/home/config.ts b/x-pack/test/functional/apps/home/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/home/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/home/feature_controls/home_security.ts b/x-pack/test/functional/apps/home/feature_controls/home_security.ts index f306b9f553d64..96a3ccbe8ea5d 100644 --- a/x-pack/test/functional/apps/home/feature_controls/home_security.ts +++ b/x-pack/test/functional/apps/home/feature_controls/home_security.ts @@ -78,7 +78,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it('shows the "Manage" action item', async () => { - await testSubjects.existOrFail('homManagementActionItem', { + await testSubjects.existOrFail('homeManage', { timeout: 2000, }); }); @@ -128,7 +128,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it('does not show the "Manage" action item', async () => { - await testSubjects.missingOrFail('homManagementActionItem', { + await testSubjects.missingOrFail('homeManage', { timeout: 2000, }); }); diff --git a/x-pack/test/functional/apps/home/index.ts b/x-pack/test/functional/apps/home/index.ts index c7579efff03a4..fd2c5b3b752c8 100644 --- a/x-pack/test/functional/apps/home/index.ts +++ b/x-pack/test/functional/apps/home/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('Home page', function () { - this.tags('ciGroup7'); loadTestFile(require.resolve('./feature_controls')); }); }; diff --git a/x-pack/test/functional/apps/index_lifecycle_management/config.ts b/x-pack/test/functional/apps/index_lifecycle_management/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/index_lifecycle_management/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/index_lifecycle_management/home_page.ts b/x-pack/test/functional/apps/index_lifecycle_management/home_page.ts index d362b84ee479a..71056c2d836fc 100644 --- a/x-pack/test/functional/apps/index_lifecycle_management/home_page.ts +++ b/x-pack/test/functional/apps/index_lifecycle_management/home_page.ts @@ -29,7 +29,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { body: { type: 'fs', settings: { - // use one of the values defined in path.repo in test/functional/config.js + // use one of the values defined in path.repo in test/functional/config.base.js location: '/tmp/', }, }, diff --git a/x-pack/test/functional/apps/index_lifecycle_management/index.ts b/x-pack/test/functional/apps/index_lifecycle_management/index.ts index cf83939c942d9..38b5803bd77ef 100644 --- a/x-pack/test/functional/apps/index_lifecycle_management/index.ts +++ b/x-pack/test/functional/apps/index_lifecycle_management/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('Index Lifecycle Management app', function () { - this.tags('ciGroup7'); loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./home_page')); }); diff --git a/x-pack/test/functional/apps/index_management/config.ts b/x-pack/test/functional/apps/index_management/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/index_management/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/index_management/index.ts b/x-pack/test/functional/apps/index_management/index.ts index 81bd94769db62..83da8cc4bba89 100644 --- a/x-pack/test/functional/apps/index_management/index.ts +++ b/x-pack/test/functional/apps/index_management/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('Index Management app', function () { - this.tags('ciGroup13'); loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./home_page')); }); diff --git a/x-pack/test/functional/apps/infra/config.ts b/x-pack/test/functional/apps/infra/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/infra/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/infra/index.ts b/x-pack/test/functional/apps/infra/index.ts index 15f92b8f37fd4..d574c747bf041 100644 --- a/x-pack/test/functional/apps/infra/index.ts +++ b/x-pack/test/functional/apps/infra/index.ts @@ -9,14 +9,15 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('InfraOps App', function () { - this.tags('ciGroup7'); loadTestFile(require.resolve('./feature_controls')); + describe('Metrics UI', function () { loadTestFile(require.resolve('./home_page')); loadTestFile(require.resolve('./metrics_source_configuration')); loadTestFile(require.resolve('./metrics_anomalies')); loadTestFile(require.resolve('./metrics_explorer')); }); + describe('Logs UI', function () { loadTestFile(require.resolve('./log_entry_categories_tab')); loadTestFile(require.resolve('./log_entry_rate_tab')); diff --git a/x-pack/test/functional/apps/ingest_pipelines/config.ts b/x-pack/test/functional/apps/ingest_pipelines/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/ingest_pipelines/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/ingest_pipelines/index.ts b/x-pack/test/functional/apps/ingest_pipelines/index.ts index 655fccaf35a95..3c585319cfe13 100644 --- a/x-pack/test/functional/apps/ingest_pipelines/index.ts +++ b/x-pack/test/functional/apps/ingest_pipelines/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('Ingest pipelines app', function () { - this.tags('ciGroup13'); loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./ingest_pipelines')); }); diff --git a/x-pack/test/functional/apps/lens/README.md b/x-pack/test/functional/apps/lens/README.md new file mode 100644 index 0000000000000..5e87a8b210bdd --- /dev/null +++ b/x-pack/test/functional/apps/lens/README.md @@ -0,0 +1,7 @@ +# What are all these groups? + +These tests take a while so they have been broken up into groups with their own `config.ts` and `index.ts` file, causing each of these groups to be independent bundles of tests which can be run on some worker in CI without taking an incredible amount of time. + +Want to change the groups to something more logical? Have fun! Just make sure that each group executes on CI in less than 10 minutes or so. We don't currently have any mechanism for validating this right now, you just need to look at the times in the log output on CI, but we'll be working on tooling for making this information more accessible soon. + +- Kibana Operations \ No newline at end of file diff --git a/x-pack/test/functional/apps/lens/group1/config.ts b/x-pack/test/functional/apps/lens/group1/config.ts new file mode 100644 index 0000000000000..d927f93adeffd --- /dev/null +++ b/x-pack/test/functional/apps/lens/group1/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/lens/group1/index.ts b/x-pack/test/functional/apps/lens/group1/index.ts new file mode 100644 index 0000000000000..35030e3636c96 --- /dev/null +++ b/x-pack/test/functional/apps/lens/group1/index.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EsArchiver } from '@kbn/es-archiver'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default ({ getService, loadTestFile, getPageObjects }: FtrProviderContext) => { + const browser = getService('browser'); + const log = getService('log'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['timePicker']); + const config = getService('config'); + let remoteEsArchiver; + + describe('lens app - group 1', () => { + const esArchive = 'x-pack/test/functional/es_archives/logstash_functional'; + const localIndexPatternString = 'logstash-*'; + const remoteIndexPatternString = 'ftr-remote:logstash-*'; + const localFixtures = { + lensBasic: 'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json', + lensDefault: 'x-pack/test/functional/fixtures/kbn_archiver/lens/default', + }; + + const remoteFixtures = { + lensBasic: 'x-pack/test/functional/fixtures/kbn_archiver/lens/ccs/lens_basic.json', + lensDefault: 'x-pack/test/functional/fixtures/kbn_archiver/lens/ccs/default', + }; + let esNode: EsArchiver; + let fixtureDirs: { + lensBasic: string; + lensDefault: string; + }; + let indexPatternString: string; + before(async () => { + await log.debug('Starting lens before method'); + await browser.setWindowSize(1280, 1200); + try { + config.get('esTestCluster.ccs'); + remoteEsArchiver = getService('remoteEsArchiver' as 'esArchiver'); + esNode = remoteEsArchiver; + fixtureDirs = remoteFixtures; + indexPatternString = remoteIndexPatternString; + } catch (error) { + esNode = esArchiver; + fixtureDirs = localFixtures; + indexPatternString = localIndexPatternString; + } + + await esNode.load(esArchive); + // changing the timepicker default here saves us from having to set it in Discover (~8s) + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await kibanaServer.uiSettings.update({ + defaultIndex: indexPatternString, + 'dateFormat:tz': 'UTC', + }); + await kibanaServer.importExport.load(fixtureDirs.lensBasic); + await kibanaServer.importExport.load(fixtureDirs.lensDefault); + }); + + after(async () => { + await esArchiver.unload(esArchive); + await PageObjects.timePicker.resetDefaultAbsoluteRangeViaUiSettings(); + await kibanaServer.importExport.unload(fixtureDirs.lensBasic); + await kibanaServer.importExport.unload(fixtureDirs.lensDefault); + }); + + if (config.get('esTestCluster.ccs')) { + loadTestFile(require.resolve('./smokescreen')); + } else { + loadTestFile(require.resolve('./smokescreen')); + loadTestFile(require.resolve('./persistent_context')); + loadTestFile(require.resolve('./table_dashboard')); + loadTestFile(require.resolve('./table')); + } + }); +}; diff --git a/x-pack/test/functional/apps/lens/persistent_context.ts b/x-pack/test/functional/apps/lens/group1/persistent_context.ts similarity index 99% rename from x-pack/test/functional/apps/lens/persistent_context.ts rename to x-pack/test/functional/apps/lens/group1/persistent_context.ts index 445caa1abbec2..b6c37f8842329 100644 --- a/x-pack/test/functional/apps/lens/persistent_context.ts +++ b/x-pack/test/functional/apps/lens/group1/persistent_context.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects([ diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/group1/smokescreen.ts similarity index 97% rename from x-pack/test/functional/apps/lens/smokescreen.ts rename to x-pack/test/functional/apps/lens/group1/smokescreen.ts index 721ff1e210326..70887b337114f 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/group1/smokescreen.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { range } from 'lodash'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); @@ -240,14 +240,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); await PageObjects.lens.changeAxisSide('right'); - await PageObjects.lens.waitForVisualization('xyVisChart'); - let data = await PageObjects.lens.getCurrentChartDebugState(); + let data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); expect(data?.axes?.y.length).to.eql(2); expect(data?.axes?.y.some(({ position }) => position === 'right')).to.eql(true); await PageObjects.lens.changeAxisSide('left'); - await PageObjects.lens.waitForVisualization('xyVisChart'); - data = await PageObjects.lens.getCurrentChartDebugState(); + data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); expect(data?.axes?.y.length).to.eql(1); expect(data?.axes?.y.some(({ position }) => position === 'right')).to.eql(false); @@ -262,18 +260,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.openVisualOptions(); await testSubjects.click('lns_valueLabels_inside'); - await PageObjects.lens.waitForVisualization('xyVisChart'); - // check for value labels - let data = await PageObjects.lens.getCurrentChartDebugState(); + let data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); expect(data?.bars?.[0].labels).not.to.eql(0); // switch to stacked bar chart await PageObjects.lens.switchToVisualization('bar_stacked'); - await PageObjects.lens.waitForVisualization('xyVisChart'); // check for value labels - data = await PageObjects.lens.getCurrentChartDebugState(); + data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); expect(data?.bars?.[0].labels.length).to.eql(0); }); @@ -283,16 +278,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.setValue('lnsyLeftAxisTitle', axisTitle, { clearWithKeyboard: true, }); - await PageObjects.lens.waitForVisualization('xyVisChart'); - let data = await PageObjects.lens.getCurrentChartDebugState(); + let data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); expect(data?.axes?.y?.[0].title).to.eql(axisTitle); // hide the gridlines await testSubjects.click('lnsshowyLeftAxisGridlines'); - await PageObjects.lens.waitForVisualization('xyVisChart'); - data = await PageObjects.lens.getCurrentChartDebugState(); + data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); expect(data?.axes?.y?.[0].gridlines.length).to.eql(0); }); diff --git a/x-pack/test/functional/apps/lens/table.ts b/x-pack/test/functional/apps/lens/group1/table.ts similarity index 99% rename from x-pack/test/functional/apps/lens/table.ts rename to x-pack/test/functional/apps/lens/group1/table.ts index 2070eb047ef61..18ecc2e90cfe4 100644 --- a/x-pack/test/functional/apps/lens/table.ts +++ b/x-pack/test/functional/apps/lens/group1/table.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); diff --git a/x-pack/test/functional/apps/lens/table_dashboard.ts b/x-pack/test/functional/apps/lens/group1/table_dashboard.ts similarity index 97% rename from x-pack/test/functional/apps/lens/table_dashboard.ts rename to x-pack/test/functional/apps/lens/group1/table_dashboard.ts index 6e76d816fa6a6..136f1903420de 100644 --- a/x-pack/test/functional/apps/lens/table_dashboard.ts +++ b/x-pack/test/functional/apps/lens/group1/table_dashboard.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['lens', 'visualize', 'dashboard']); diff --git a/x-pack/test/functional/apps/lens/add_to_dashboard.ts b/x-pack/test/functional/apps/lens/group2/add_to_dashboard.ts similarity index 99% rename from x-pack/test/functional/apps/lens/add_to_dashboard.ts rename to x-pack/test/functional/apps/lens/group2/add_to_dashboard.ts index 5fbfdd0a5806e..8ad5cd41e0bec 100644 --- a/x-pack/test/functional/apps/lens/add_to_dashboard.ts +++ b/x-pack/test/functional/apps/lens/group2/add_to_dashboard.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects([ diff --git a/x-pack/test/functional/apps/lens/group2/config.ts b/x-pack/test/functional/apps/lens/group2/config.ts new file mode 100644 index 0000000000000..d927f93adeffd --- /dev/null +++ b/x-pack/test/functional/apps/lens/group2/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/lens/dashboard.ts b/x-pack/test/functional/apps/lens/group2/dashboard.ts similarity index 98% rename from x-pack/test/functional/apps/lens/dashboard.ts rename to x-pack/test/functional/apps/lens/group2/dashboard.ts index 97dc29280761f..787a0a6a6d99a 100644 --- a/x-pack/test/functional/apps/lens/dashboard.ts +++ b/x-pack/test/functional/apps/lens/group2/dashboard.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects([ @@ -16,6 +16,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'timePicker', 'lens', 'discover', + 'unifiedSearch', ]); const find = getService('find'); @@ -163,6 +164,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.clickNewDashboard(); await dashboardAddPanel.clickCreateNewLink(); await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.unifiedSearch.closeTourPopoverByLocalStorage(); await PageObjects.lens.goToTimeRange(); await PageObjects.lens.configureDimension({ dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', diff --git a/x-pack/test/functional/apps/lens/epoch_millis.ts b/x-pack/test/functional/apps/lens/group2/epoch_millis.ts similarity index 97% rename from x-pack/test/functional/apps/lens/epoch_millis.ts rename to x-pack/test/functional/apps/lens/group2/epoch_millis.ts index d882d69ddd1fd..ce773baf27ad9 100644 --- a/x-pack/test/functional/apps/lens/epoch_millis.ts +++ b/x-pack/test/functional/apps/lens/group2/epoch_millis.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); diff --git a/x-pack/test/functional/apps/lens/group2/index.ts b/x-pack/test/functional/apps/lens/group2/index.ts new file mode 100644 index 0000000000000..0e1c732dff41c --- /dev/null +++ b/x-pack/test/functional/apps/lens/group2/index.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EsArchiver } from '@kbn/es-archiver'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default ({ getService, loadTestFile, getPageObjects }: FtrProviderContext) => { + const browser = getService('browser'); + const log = getService('log'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['timePicker']); + const config = getService('config'); + let remoteEsArchiver; + + describe('lens app - group 2', () => { + const esArchive = 'x-pack/test/functional/es_archives/logstash_functional'; + const localIndexPatternString = 'logstash-*'; + const remoteIndexPatternString = 'ftr-remote:logstash-*'; + const localFixtures = { + lensBasic: 'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json', + lensDefault: 'x-pack/test/functional/fixtures/kbn_archiver/lens/default', + }; + + const remoteFixtures = { + lensBasic: 'x-pack/test/functional/fixtures/kbn_archiver/lens/ccs/lens_basic.json', + lensDefault: 'x-pack/test/functional/fixtures/kbn_archiver/lens/ccs/default', + }; + let esNode: EsArchiver; + let fixtureDirs: { + lensBasic: string; + lensDefault: string; + }; + let indexPatternString: string; + before(async () => { + await log.debug('Starting lens before method'); + await browser.setWindowSize(1280, 1200); + try { + config.get('esTestCluster.ccs'); + remoteEsArchiver = getService('remoteEsArchiver' as 'esArchiver'); + esNode = remoteEsArchiver; + fixtureDirs = remoteFixtures; + indexPatternString = remoteIndexPatternString; + } catch (error) { + esNode = esArchiver; + fixtureDirs = localFixtures; + indexPatternString = localIndexPatternString; + } + + await esNode.load(esArchive); + // changing the timepicker default here saves us from having to set it in Discover (~8s) + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await kibanaServer.uiSettings.update({ + defaultIndex: indexPatternString, + 'dateFormat:tz': 'UTC', + }); + await kibanaServer.importExport.load(fixtureDirs.lensBasic); + await kibanaServer.importExport.load(fixtureDirs.lensDefault); + }); + + after(async () => { + await esArchiver.unload(esArchive); + await PageObjects.timePicker.resetDefaultAbsoluteRangeViaUiSettings(); + await kibanaServer.importExport.unload(fixtureDirs.lensBasic); + await kibanaServer.importExport.unload(fixtureDirs.lensDefault); + }); + + loadTestFile(require.resolve('./add_to_dashboard')); + loadTestFile(require.resolve('./runtime_fields')); + loadTestFile(require.resolve('./dashboard')); + loadTestFile(require.resolve('./multi_terms')); + loadTestFile(require.resolve('./epoch_millis')); + loadTestFile(require.resolve('./show_underlying_data')); + loadTestFile(require.resolve('./show_underlying_data_dashboard')); + }); +}; diff --git a/x-pack/test/functional/apps/lens/multi_terms.ts b/x-pack/test/functional/apps/lens/group2/multi_terms.ts similarity index 96% rename from x-pack/test/functional/apps/lens/multi_terms.ts rename to x-pack/test/functional/apps/lens/group2/multi_terms.ts index 18936f09925f5..58fa172378964 100644 --- a/x-pack/test/functional/apps/lens/multi_terms.ts +++ b/x-pack/test/functional/apps/lens/group2/multi_terms.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); @@ -50,7 +50,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'Top values of geo.src + 2 others' ); - const data = await PageObjects.lens.getCurrentChartDebugState(); + const data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); expect(data!.bars![0].bars[0].x).to.eql('PE › US › 19,986'); }); @@ -75,7 +75,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.closeDimensionEditor(); - const data = await PageObjects.lens.getCurrentChartDebugState(); + const data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); expect(data?.bars?.[0]?.name).to.eql('PE › US › 19,986'); }); diff --git a/x-pack/test/functional/apps/lens/runtime_fields.ts b/x-pack/test/functional/apps/lens/group2/runtime_fields.ts similarity index 97% rename from x-pack/test/functional/apps/lens/runtime_fields.ts rename to x-pack/test/functional/apps/lens/group2/runtime_fields.ts index 252951cba4bd0..868432ce1ae4d 100644 --- a/x-pack/test/functional/apps/lens/runtime_fields.ts +++ b/x-pack/test/functional/apps/lens/group2/runtime_fields.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); diff --git a/x-pack/test/functional/apps/lens/show_underlying_data.ts b/x-pack/test/functional/apps/lens/group2/show_underlying_data.ts similarity index 98% rename from x-pack/test/functional/apps/lens/show_underlying_data.ts rename to x-pack/test/functional/apps/lens/group2/show_underlying_data.ts index 4bc8be22eb8f4..bd8f02c723102 100644 --- a/x-pack/test/functional/apps/lens/show_underlying_data.ts +++ b/x-pack/test/functional/apps/lens/group2/show_underlying_data.ts @@ -5,7 +5,7 @@ * 2.0. */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header', 'discover']); @@ -87,7 +87,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.enableFilter(); // turn off the KQL switch to change the language to lucene await testSubjects.click('indexPattern-filter-by-input > switchQueryLanguageButton'); - await testSubjects.click('languageToggle'); + await testSubjects.click('luceneLanguageMenuItem'); await testSubjects.click('indexPattern-filter-by-input > switchQueryLanguageButton'); // apparently setting a filter requires some time before and after typing to work properly await PageObjects.common.sleep(1000); diff --git a/x-pack/test/functional/apps/lens/show_underlying_data_dashboard.ts b/x-pack/test/functional/apps/lens/group2/show_underlying_data_dashboard.ts similarity index 90% rename from x-pack/test/functional/apps/lens/show_underlying_data_dashboard.ts rename to x-pack/test/functional/apps/lens/group2/show_underlying_data_dashboard.ts index dc25e5f77f412..92e9b6fcdb58e 100644 --- a/x-pack/test/functional/apps/lens/show_underlying_data_dashboard.ts +++ b/x-pack/test/functional/apps/lens/group2/show_underlying_data_dashboard.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; import uuid from 'uuid'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects([ @@ -23,6 +23,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const dashboardPanelActions = getService('dashboardPanelActions'); const filterBarService = getService('filterBar'); const queryBar = getService('queryBar'); + const savedQueryManagementComponent = getService('savedQueryManagementComponent'); const browser = getService('browser'); const retry = getService('retry'); @@ -58,8 +59,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should bring both dashboard context and visualization context to discover', async () => { await PageObjects.dashboard.switchToEditMode(); await dashboardPanelActions.clickEdit(); - + await savedQueryManagementComponent.openSavedQueryManagementComponent(); await queryBar.switchQueryLanguage('lucene'); + await savedQueryManagementComponent.closeSavedQueryManagementComponent(); await queryBar.setQuery('host.keyword www.elastic.co'); await queryBar.submitQuery(); await filterBarService.addFilter('geo.src', 'is', 'AF'); @@ -67,8 +69,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.common.sleep(1000); await PageObjects.lens.saveAndReturn(); - + await savedQueryManagementComponent.openSavedQueryManagementComponent(); await queryBar.switchQueryLanguage('kql'); + await savedQueryManagementComponent.closeSavedQueryManagementComponent(); await queryBar.setQuery('request.keyword : "/apm"'); await queryBar.submitQuery(); await filterBarService.addFilter( diff --git a/x-pack/test/functional/apps/lens/annotations.ts b/x-pack/test/functional/apps/lens/group3/annotations.ts similarity index 97% rename from x-pack/test/functional/apps/lens/annotations.ts rename to x-pack/test/functional/apps/lens/group3/annotations.ts index c54b3081f7418..2b641c6c161d4 100644 --- a/x-pack/test/functional/apps/lens/annotations.ts +++ b/x-pack/test/functional/apps/lens/group3/annotations.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); diff --git a/x-pack/test/functional/apps/lens/chart_data.ts b/x-pack/test/functional/apps/lens/group3/chart_data.ts similarity index 92% rename from x-pack/test/functional/apps/lens/chart_data.ts rename to x-pack/test/functional/apps/lens/group3/chart_data.ts index ff2f5b339fe53..6ef40c11407e8 100644 --- a/x-pack/test/functional/apps/lens/chart_data.ts +++ b/x-pack/test/functional/apps/lens/group3/chart_data.ts @@ -8,7 +8,7 @@ import { DebugState } from '@elastic/charts'; import expect from '@kbn/expect'; import { range } from 'lodash'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); @@ -73,37 +73,31 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { } it('should render xy chart', async () => { - await PageObjects.lens.waitForVisualization('xyVisChart'); - - const data = await PageObjects.lens.getCurrentChartDebugState(); + const data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); assertMatchesExpectedData(data!); }); it('should render pie chart', async () => { await PageObjects.lens.switchToVisualization('pie'); - await PageObjects.lens.waitForVisualization('partitionVisChart'); - const data = await PageObjects.lens.getCurrentChartDebugState(); + const data = await PageObjects.lens.getCurrentChartDebugState('partitionVisChart'); assertMatchesExpectedPieData(data!); }); it('should render donut chart', async () => { await PageObjects.lens.switchToVisualization('donut'); - await PageObjects.lens.waitForVisualization('partitionVisChart'); - const data = await PageObjects.lens.getCurrentChartDebugState(); + const data = await PageObjects.lens.getCurrentChartDebugState('partitionVisChart'); assertMatchesExpectedPieData(data!); }); it('should render treemap chart', async () => { await PageObjects.lens.switchToVisualization('treemap', 'treemap'); - await PageObjects.lens.waitForVisualization('partitionVisChart'); - const data = await PageObjects.lens.getCurrentChartDebugState(); + const data = await PageObjects.lens.getCurrentChartDebugState('partitionVisChart'); assertMatchesExpectedPieData(data!); }); it('should render heatmap chart', async () => { await PageObjects.lens.switchToVisualization('heatmap', 'heat'); - await PageObjects.lens.waitForVisualization('heatmapChart'); - const debugState = await PageObjects.lens.getCurrentChartDebugState(); + const debugState = await PageObjects.lens.getCurrentChartDebugState('heatmapChart'); if (!debugState) { throw new Error('Debug state is not available'); diff --git a/x-pack/test/functional/apps/lens/colors.ts b/x-pack/test/functional/apps/lens/group3/colors.ts similarity index 96% rename from x-pack/test/functional/apps/lens/colors.ts rename to x-pack/test/functional/apps/lens/group3/colors.ts index 638da79b5c5cb..4078b0663d7ae 100644 --- a/x-pack/test/functional/apps/lens/colors.ts +++ b/x-pack/test/functional/apps/lens/group3/colors.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common']); diff --git a/x-pack/test/functional/apps/lens/group3/config.ts b/x-pack/test/functional/apps/lens/group3/config.ts new file mode 100644 index 0000000000000..d927f93adeffd --- /dev/null +++ b/x-pack/test/functional/apps/lens/group3/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/lens/disable_auto_apply.ts b/x-pack/test/functional/apps/lens/group3/disable_auto_apply.ts similarity index 96% rename from x-pack/test/functional/apps/lens/disable_auto_apply.ts rename to x-pack/test/functional/apps/lens/group3/disable_auto_apply.ts index f73ce56bae694..4ccc642dd9929 100644 --- a/x-pack/test/functional/apps/lens/disable_auto_apply.ts +++ b/x-pack/test/functional/apps/lens/group3/disable_auto_apply.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['lens', 'visualize']); @@ -50,6 +50,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.goToTimeRange(); await PageObjects.lens.disableAutoApply(); + await PageObjects.lens.closeSettingsMenu(); await PageObjects.lens.configureDimension({ dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', diff --git a/x-pack/test/functional/apps/lens/drag_and_drop.ts b/x-pack/test/functional/apps/lens/group3/drag_and_drop.ts similarity index 98% rename from x-pack/test/functional/apps/lens/drag_and_drop.ts rename to x-pack/test/functional/apps/lens/group3/drag_and_drop.ts index d5b929481e6dc..6b772c8d13c05 100644 --- a/x-pack/test/functional/apps/lens/drag_and_drop.ts +++ b/x-pack/test/functional/apps/lens/group3/drag_and_drop.ts @@ -6,14 +6,15 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); const xyChartContainer = 'xyVisChart'; describe('lens drag and drop tests', () => { - describe('basic drag and drop', () => { + // FLAKY: https://github.com/elastic/kibana/issues/108352 + describe.skip('basic drag and drop', () => { it('should construct the basic split xy chart', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); diff --git a/x-pack/test/functional/apps/lens/error_handling.ts b/x-pack/test/functional/apps/lens/group3/error_handling.ts similarity index 97% rename from x-pack/test/functional/apps/lens/error_handling.ts rename to x-pack/test/functional/apps/lens/group3/error_handling.ts index 87f62abf88e4f..8f6659bda1562 100644 --- a/x-pack/test/functional/apps/lens/error_handling.ts +++ b/x-pack/test/functional/apps/lens/group3/error_handling.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects([ diff --git a/x-pack/test/functional/apps/lens/formula.ts b/x-pack/test/functional/apps/lens/group3/formula.ts similarity index 99% rename from x-pack/test/functional/apps/lens/formula.ts rename to x-pack/test/functional/apps/lens/group3/formula.ts index 02d4fda0e96a6..33a24d3aefb1c 100644 --- a/x-pack/test/functional/apps/lens/formula.ts +++ b/x-pack/test/functional/apps/lens/group3/formula.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common']); diff --git a/x-pack/test/functional/apps/lens/gauge.ts b/x-pack/test/functional/apps/lens/group3/gauge.ts similarity index 96% rename from x-pack/test/functional/apps/lens/gauge.ts rename to x-pack/test/functional/apps/lens/group3/gauge.ts index c21ddf5b70791..ea029793ddfc0 100644 --- a/x-pack/test/functional/apps/lens/gauge.ts +++ b/x-pack/test/functional/apps/lens/group3/gauge.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); @@ -48,6 +48,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should reflect edits for gauge', async () => { + await PageObjects.lens.switchToVisualization('horizontalBullet', 'gauge'); + await PageObjects.lens.waitForVisualization('gaugeChart'); await PageObjects.lens.configureDimension({ dimension: 'lnsGauge_metricDimensionPanel > lns-dimensionTrigger', operation: 'count', diff --git a/x-pack/test/functional/apps/lens/geo_field.ts b/x-pack/test/functional/apps/lens/group3/geo_field.ts similarity index 95% rename from x-pack/test/functional/apps/lens/geo_field.ts rename to x-pack/test/functional/apps/lens/group3/geo_field.ts index f9b8277a22731..bb8012ba50d23 100644 --- a/x-pack/test/functional/apps/lens/geo_field.ts +++ b/x-pack/test/functional/apps/lens/group3/geo_field.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'header', 'maps', 'timePicker']); diff --git a/x-pack/test/functional/apps/lens/heatmap.ts b/x-pack/test/functional/apps/lens/group3/heatmap.ts similarity index 92% rename from x-pack/test/functional/apps/lens/heatmap.ts rename to x-pack/test/functional/apps/lens/group3/heatmap.ts index 1386e1beea899..aa1e10a715547 100644 --- a/x-pack/test/functional/apps/lens/heatmap.ts +++ b/x-pack/test/functional/apps/lens/group3/heatmap.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common']); @@ -38,8 +38,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render heatmap chart with the temperature palette', async () => { await PageObjects.lens.switchToVisualization('heatmap', 'heat'); - await PageObjects.lens.waitForVisualization('heatmapChart'); - const debugState = await PageObjects.lens.getCurrentChartDebugState(); + const debugState = await PageObjects.lens.getCurrentChartDebugState('heatmapChart'); if (!debugState) { throw new Error('Debug state is not available'); @@ -78,9 +77,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { typeCharByChar: true, }); }); - await PageObjects.lens.waitForVisualization('heatmapChart'); - - const debugState = await PageObjects.lens.getCurrentChartDebugState(); + const debugState = await PageObjects.lens.getCurrentChartDebugState('heatmapChart'); if (!debugState) { throw new Error('Debug state is not available'); @@ -98,9 +95,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should not change when passing from percentage to number', async () => { await testSubjects.click('lnsPalettePanel_dynamicColoring_rangeType_groups_number'); - await PageObjects.lens.waitForVisualization('heatmapChart'); - - const debugState = await PageObjects.lens.getCurrentChartDebugState(); + const debugState = await PageObjects.lens.getCurrentChartDebugState('heatmapChart'); if (!debugState) { throw new Error('Debug state is not available'); @@ -124,9 +119,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.setValue('lnsPalettePanel_dynamicColoring_range_value_0', '0', { clearWithKeyboard: true, }); - await PageObjects.lens.waitForVisualization('heatmapChart'); - const debugState = await PageObjects.lens.getCurrentChartDebugState(); + const debugState = await PageObjects.lens.getCurrentChartDebugState('heatmapChart'); if (!debugState) { throw new Error('Debug state is not available'); @@ -144,9 +138,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should reset stop numbers when changing palette', async () => { await PageObjects.lens.changePaletteTo('status'); - await PageObjects.lens.waitForVisualization('heatmapChart'); - const debugState = await PageObjects.lens.getCurrentChartDebugState(); + const debugState = await PageObjects.lens.getCurrentChartDebugState('heatmapChart'); if (!debugState) { throw new Error('Debug state is not available'); @@ -164,9 +157,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should not change when passing from number to percent', async () => { await testSubjects.click('lnsPalettePanel_dynamicColoring_rangeType_groups_percent'); - await PageObjects.lens.waitForVisualization('heatmapChart'); - const debugState = await PageObjects.lens.getCurrentChartDebugState(); + const debugState = await PageObjects.lens.getCurrentChartDebugState('heatmapChart'); if (!debugState) { throw new Error('Debug state is not available'); diff --git a/x-pack/test/functional/apps/lens/group3/index.ts b/x-pack/test/functional/apps/lens/group3/index.ts new file mode 100644 index 0000000000000..03c42e4c70ebf --- /dev/null +++ b/x-pack/test/functional/apps/lens/group3/index.ts @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EsArchiver } from '@kbn/es-archiver'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default ({ getService, loadTestFile, getPageObjects }: FtrProviderContext) => { + const browser = getService('browser'); + const log = getService('log'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['timePicker']); + const config = getService('config'); + let remoteEsArchiver; + + describe('lens app - group 3', () => { + const esArchive = 'x-pack/test/functional/es_archives/logstash_functional'; + const localIndexPatternString = 'logstash-*'; + const remoteIndexPatternString = 'ftr-remote:logstash-*'; + const localFixtures = { + lensBasic: 'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json', + lensDefault: 'x-pack/test/functional/fixtures/kbn_archiver/lens/default', + }; + + const remoteFixtures = { + lensBasic: 'x-pack/test/functional/fixtures/kbn_archiver/lens/ccs/lens_basic.json', + lensDefault: 'x-pack/test/functional/fixtures/kbn_archiver/lens/ccs/default', + }; + let esNode: EsArchiver; + let fixtureDirs: { + lensBasic: string; + lensDefault: string; + }; + let indexPatternString: string; + before(async () => { + await log.debug('Starting lens before method'); + await browser.setWindowSize(1280, 1200); + try { + config.get('esTestCluster.ccs'); + remoteEsArchiver = getService('remoteEsArchiver' as 'esArchiver'); + esNode = remoteEsArchiver; + fixtureDirs = remoteFixtures; + indexPatternString = remoteIndexPatternString; + } catch (error) { + esNode = esArchiver; + fixtureDirs = localFixtures; + indexPatternString = localIndexPatternString; + } + + await esNode.load(esArchive); + // changing the timepicker default here saves us from having to set it in Discover (~8s) + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await kibanaServer.uiSettings.update({ + defaultIndex: indexPatternString, + 'dateFormat:tz': 'UTC', + }); + await kibanaServer.importExport.load(fixtureDirs.lensBasic); + await kibanaServer.importExport.load(fixtureDirs.lensDefault); + }); + + after(async () => { + await esArchiver.unload(esArchive); + await PageObjects.timePicker.resetDefaultAbsoluteRangeViaUiSettings(); + await kibanaServer.importExport.unload(fixtureDirs.lensBasic); + await kibanaServer.importExport.unload(fixtureDirs.lensDefault); + }); + + loadTestFile(require.resolve('./colors')); + loadTestFile(require.resolve('./chart_data')); + loadTestFile(require.resolve('./time_shift')); + loadTestFile(require.resolve('./drag_and_drop')); + loadTestFile(require.resolve('./disable_auto_apply')); + loadTestFile(require.resolve('./geo_field')); + loadTestFile(require.resolve('./formula')); + loadTestFile(require.resolve('./heatmap')); + loadTestFile(require.resolve('./gauge')); + loadTestFile(require.resolve('./metrics')); + loadTestFile(require.resolve('./reference_lines')); + loadTestFile(require.resolve('./annotations')); + loadTestFile(require.resolve('./inspector')); + loadTestFile(require.resolve('./error_handling')); + loadTestFile(require.resolve('./lens_tagging')); + loadTestFile(require.resolve('./lens_reporting')); + loadTestFile(require.resolve('./tsvb_open_in_lens')); + // has to be last one in the suite because it overrides saved objects + loadTestFile(require.resolve('./rollup')); + }); +}; diff --git a/x-pack/test/functional/apps/lens/inspector.ts b/x-pack/test/functional/apps/lens/group3/inspector.ts similarity index 96% rename from x-pack/test/functional/apps/lens/inspector.ts rename to x-pack/test/functional/apps/lens/group3/inspector.ts index d94d3413c07b0..9f52d783011c4 100644 --- a/x-pack/test/functional/apps/lens/inspector.ts +++ b/x-pack/test/functional/apps/lens/group3/inspector.ts @@ -5,7 +5,7 @@ * 2.0. */ import expect from '@kbn/expect'; -import type { FtrProviderContext } from '../../ftr_provider_context'; +import type { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); diff --git a/x-pack/test/functional/apps/lens/lens_reporting.ts b/x-pack/test/functional/apps/lens/group3/lens_reporting.ts similarity index 96% rename from x-pack/test/functional/apps/lens/lens_reporting.ts rename to x-pack/test/functional/apps/lens/group3/lens_reporting.ts index 6dfb1dd923b3e..2cbb55ae03d97 100644 --- a/x-pack/test/functional/apps/lens/lens_reporting.ts +++ b/x-pack/test/functional/apps/lens/group3/lens_reporting.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'dashboard', 'reporting', 'timePicker']); diff --git a/x-pack/test/functional/apps/lens/lens_tagging.ts b/x-pack/test/functional/apps/lens/group3/lens_tagging.ts similarity index 96% rename from x-pack/test/functional/apps/lens/lens_tagging.ts rename to x-pack/test/functional/apps/lens/group3/lens_tagging.ts index 3852fdb0456ac..b246f84bb43ce 100644 --- a/x-pack/test/functional/apps/lens/lens_tagging.ts +++ b/x-pack/test/functional/apps/lens/group3/lens_tagging.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const listingTable = getService('listingTable'); @@ -24,6 +24,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'visualize', 'lens', 'timePicker', + 'unifiedSearch', ]); const lensTag = 'extreme-lens-tag'; @@ -36,6 +37,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.preserveCrossAppState(); await PageObjects.dashboard.clickNewDashboard(); + await PageObjects.unifiedSearch.closeTourPopoverByLocalStorage(); }); after(async () => { diff --git a/x-pack/test/functional/apps/lens/metrics.ts b/x-pack/test/functional/apps/lens/group3/metrics.ts similarity index 97% rename from x-pack/test/functional/apps/lens/metrics.ts rename to x-pack/test/functional/apps/lens/group3/metrics.ts index 62a8b69141a58..bf651f7a12a1b 100644 --- a/x-pack/test/functional/apps/lens/metrics.ts +++ b/x-pack/test/functional/apps/lens/group3/metrics.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); diff --git a/x-pack/test/functional/apps/lens/reference_lines.ts b/x-pack/test/functional/apps/lens/group3/reference_lines.ts similarity index 98% rename from x-pack/test/functional/apps/lens/reference_lines.ts rename to x-pack/test/functional/apps/lens/group3/reference_lines.ts index 97c6ebf5b138f..f022a6cef6e7a 100644 --- a/x-pack/test/functional/apps/lens/reference_lines.ts +++ b/x-pack/test/functional/apps/lens/group3/reference_lines.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); diff --git a/x-pack/test/functional/apps/lens/rollup.ts b/x-pack/test/functional/apps/lens/group3/rollup.ts similarity index 98% rename from x-pack/test/functional/apps/lens/rollup.ts rename to x-pack/test/functional/apps/lens/group3/rollup.ts index 25ab766d04bbd..d42cc67ad673c 100644 --- a/x-pack/test/functional/apps/lens/rollup.ts +++ b/x-pack/test/functional/apps/lens/group3/rollup.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'header', 'timePicker']); diff --git a/x-pack/test/functional/apps/lens/time_shift.ts b/x-pack/test/functional/apps/lens/group3/time_shift.ts similarity index 97% rename from x-pack/test/functional/apps/lens/time_shift.ts rename to x-pack/test/functional/apps/lens/group3/time_shift.ts index e3363bee419aa..4dd22ea719ec7 100644 --- a/x-pack/test/functional/apps/lens/time_shift.ts +++ b/x-pack/test/functional/apps/lens/group3/time_shift.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); diff --git a/x-pack/test/functional/apps/lens/tsvb_open_in_lens.ts b/x-pack/test/functional/apps/lens/group3/tsvb_open_in_lens.ts similarity index 99% rename from x-pack/test/functional/apps/lens/tsvb_open_in_lens.ts rename to x-pack/test/functional/apps/lens/group3/tsvb_open_in_lens.ts index 0315d20e5fc91..f9d21f80462e6 100644 --- a/x-pack/test/functional/apps/lens/tsvb_open_in_lens.ts +++ b/x-pack/test/functional/apps/lens/group3/tsvb_open_in_lens.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const { visualize, visualBuilder, header, lens, timeToVisualize, dashboard, canvas } = diff --git a/x-pack/test/functional/apps/lens/index.ts b/x-pack/test/functional/apps/lens/index.ts deleted file mode 100644 index 372d17b473e4d..0000000000000 --- a/x-pack/test/functional/apps/lens/index.ts +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EsArchiver } from '@kbn/es-archiver'; -import { FtrProviderContext } from '../../ftr_provider_context'; - -export default ({ getService, loadTestFile, getPageObjects }: FtrProviderContext) => { - const browser = getService('browser'); - const log = getService('log'); - const esArchiver = getService('esArchiver'); - const kibanaServer = getService('kibanaServer'); - const PageObjects = getPageObjects(['timePicker']); - const config = getService('config'); - let remoteEsArchiver; - - describe('lens app', () => { - const esArchive = 'x-pack/test/functional/es_archives/logstash_functional'; - const localIndexPatternString = 'logstash-*'; - const remoteIndexPatternString = 'ftr-remote:logstash-*'; - const localFixtures = { - lensBasic: 'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json', - lensDefault: 'x-pack/test/functional/fixtures/kbn_archiver/lens/default', - }; - - const remoteFixtures = { - lensBasic: 'x-pack/test/functional/fixtures/kbn_archiver/lens/ccs/lens_basic.json', - lensDefault: 'x-pack/test/functional/fixtures/kbn_archiver/lens/ccs/default', - }; - let esNode: EsArchiver; - let fixtureDirs: { - lensBasic: string; - lensDefault: string; - }; - let indexPatternString: string; - before(async () => { - await log.debug('Starting lens before method'); - await browser.setWindowSize(1280, 1200); - try { - config.get('esTestCluster.ccs'); - remoteEsArchiver = getService('remoteEsArchiver' as 'esArchiver'); - esNode = remoteEsArchiver; - fixtureDirs = remoteFixtures; - indexPatternString = remoteIndexPatternString; - } catch (error) { - esNode = esArchiver; - fixtureDirs = localFixtures; - indexPatternString = localIndexPatternString; - } - - await esNode.load(esArchive); - // changing the timepicker default here saves us from having to set it in Discover (~8s) - await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); - await kibanaServer.uiSettings.update({ - defaultIndex: indexPatternString, - 'dateFormat:tz': 'UTC', - }); - await kibanaServer.importExport.load(fixtureDirs.lensBasic); - await kibanaServer.importExport.load(fixtureDirs.lensDefault); - }); - - after(async () => { - await esArchiver.unload(esArchive); - await PageObjects.timePicker.resetDefaultAbsoluteRangeViaUiSettings(); - await kibanaServer.importExport.unload(fixtureDirs.lensBasic); - await kibanaServer.importExport.unload(fixtureDirs.lensDefault); - }); - - if (config.get('esTestCluster.ccs')) { - describe('', function () { - this.tags(['ciGroup3', 'skipFirefox']); - loadTestFile(require.resolve('./smokescreen')); - }); - } else { - describe('', function () { - this.tags(['ciGroup3', 'skipFirefox']); - loadTestFile(require.resolve('./smokescreen')); - loadTestFile(require.resolve('./persistent_context')); - }); - - describe('', function () { - this.tags(['ciGroup16', 'skipFirefox']); - - loadTestFile(require.resolve('./add_to_dashboard')); - loadTestFile(require.resolve('./table_dashboard')); - loadTestFile(require.resolve('./table')); - loadTestFile(require.resolve('./runtime_fields')); - loadTestFile(require.resolve('./dashboard')); - loadTestFile(require.resolve('./multi_terms')); - loadTestFile(require.resolve('./epoch_millis')); - loadTestFile(require.resolve('./show_underlying_data')); - loadTestFile(require.resolve('./show_underlying_data_dashboard')); - }); - - describe('', function () { - this.tags(['ciGroup4', 'skipFirefox']); - - loadTestFile(require.resolve('./colors')); - loadTestFile(require.resolve('./chart_data')); - loadTestFile(require.resolve('./time_shift')); - loadTestFile(require.resolve('./drag_and_drop')); - loadTestFile(require.resolve('./disable_auto_apply')); - loadTestFile(require.resolve('./geo_field')); - loadTestFile(require.resolve('./formula')); - loadTestFile(require.resolve('./heatmap')); - loadTestFile(require.resolve('./gauge')); - loadTestFile(require.resolve('./metrics')); - loadTestFile(require.resolve('./reference_lines')); - loadTestFile(require.resolve('./annotations')); - loadTestFile(require.resolve('./inspector')); - loadTestFile(require.resolve('./error_handling')); - loadTestFile(require.resolve('./lens_tagging')); - loadTestFile(require.resolve('./lens_reporting')); - loadTestFile(require.resolve('./tsvb_open_in_lens')); - // has to be last one in the suite because it overrides saved objects - loadTestFile(require.resolve('./rollup')); - }); - } - }); -}; diff --git a/x-pack/test/functional/apps/license_management/config.ts b/x-pack/test/functional/apps/license_management/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/license_management/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/license_management/index.ts b/x-pack/test/functional/apps/license_management/index.ts index a209a4370ced9..d4256588667ec 100644 --- a/x-pack/test/functional/apps/license_management/index.ts +++ b/x-pack/test/functional/apps/license_management/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('License app', function () { - this.tags('ciGroup7'); loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./home_page')); }); diff --git a/x-pack/test/functional/apps/logstash/config.ts b/x-pack/test/functional/apps/logstash/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/logstash/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/logstash/feature_controls/index.ts b/x-pack/test/functional/apps/logstash/feature_controls/index.ts index eb4c681531cbc..c50611d7fa7c4 100644 --- a/x-pack/test/functional/apps/logstash/feature_controls/index.ts +++ b/x-pack/test/functional/apps/logstash/feature_controls/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('feature controls', function () { - this.tags(['ciGroup2']); - loadTestFile(require.resolve('./logstash_security')); }); } diff --git a/x-pack/test/functional/apps/logstash/index.js b/x-pack/test/functional/apps/logstash/index.js index 8c1ac233e9c57..deaf4041b1b59 100644 --- a/x-pack/test/functional/apps/logstash/index.js +++ b/x-pack/test/functional/apps/logstash/index.js @@ -7,8 +7,6 @@ export default function ({ loadTestFile }) { describe('logstash', function () { - this.tags(['ciGroup2']); - loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./pipeline_list')); loadTestFile(require.resolve('./pipeline_create')); diff --git a/x-pack/test/functional/apps/management/config.ts b/x-pack/test/functional/apps/management/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/management/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/management/feature_controls/index.ts b/x-pack/test/functional/apps/management/feature_controls/index.ts index 66eb2e4937620..17e71ef190856 100644 --- a/x-pack/test/functional/apps/management/feature_controls/index.ts +++ b/x-pack/test/functional/apps/management/feature_controls/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('feature controls', function () { - this.tags(['ciGroup2']); - loadTestFile(require.resolve('./management_security')); }); } diff --git a/x-pack/test/functional/apps/management/index.ts b/x-pack/test/functional/apps/management/index.ts index 03fec9fffe4fb..72da3e0fd739a 100644 --- a/x-pack/test/functional/apps/management/index.ts +++ b/x-pack/test/functional/apps/management/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('management', function () { - this.tags(['ciGroup2']); - loadTestFile(require.resolve('./create_index_pattern_wizard')); loadTestFile(require.resolve('./feature_controls')); }); diff --git a/x-pack/test/functional/apps/maps/README.md b/x-pack/test/functional/apps/maps/README.md new file mode 100644 index 0000000000000..5e87a8b210bdd --- /dev/null +++ b/x-pack/test/functional/apps/maps/README.md @@ -0,0 +1,7 @@ +# What are all these groups? + +These tests take a while so they have been broken up into groups with their own `config.ts` and `index.ts` file, causing each of these groups to be independent bundles of tests which can be run on some worker in CI without taking an incredible amount of time. + +Want to change the groups to something more logical? Have fun! Just make sure that each group executes on CI in less than 10 minutes or so. We don't currently have any mechanism for validating this right now, you just need to look at the times in the log output on CI, but we'll be working on tooling for making this information more accessible soon. + +- Kibana Operations \ No newline at end of file diff --git a/x-pack/test/functional/apps/maps/auto_fit_to_bounds.js b/x-pack/test/functional/apps/maps/group1/auto_fit_to_bounds.js similarity index 100% rename from x-pack/test/functional/apps/maps/auto_fit_to_bounds.js rename to x-pack/test/functional/apps/maps/group1/auto_fit_to_bounds.js diff --git a/x-pack/test/functional/apps/maps/blended_vector_layer.js b/x-pack/test/functional/apps/maps/group1/blended_vector_layer.js similarity index 100% rename from x-pack/test/functional/apps/maps/blended_vector_layer.js rename to x-pack/test/functional/apps/maps/group1/blended_vector_layer.js diff --git a/x-pack/test/functional/apps/maps/group1/config.ts b/x-pack/test/functional/apps/maps/group1/config.ts new file mode 100644 index 0000000000000..d927f93adeffd --- /dev/null +++ b/x-pack/test/functional/apps/maps/group1/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js b/x-pack/test/functional/apps/maps/group1/documents_source/docvalue_fields.js similarity index 100% rename from x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js rename to x-pack/test/functional/apps/maps/group1/documents_source/docvalue_fields.js diff --git a/x-pack/test/functional/apps/maps/documents_source/index.js b/x-pack/test/functional/apps/maps/group1/documents_source/index.js similarity index 100% rename from x-pack/test/functional/apps/maps/documents_source/index.js rename to x-pack/test/functional/apps/maps/group1/documents_source/index.js diff --git a/x-pack/test/functional/apps/maps/documents_source/search_hits.js b/x-pack/test/functional/apps/maps/group1/documents_source/search_hits.js similarity index 100% rename from x-pack/test/functional/apps/maps/documents_source/search_hits.js rename to x-pack/test/functional/apps/maps/group1/documents_source/search_hits.js diff --git a/x-pack/test/functional/apps/maps/documents_source/top_hits.js b/x-pack/test/functional/apps/maps/group1/documents_source/top_hits.js similarity index 100% rename from x-pack/test/functional/apps/maps/documents_source/top_hits.js rename to x-pack/test/functional/apps/maps/group1/documents_source/top_hits.js diff --git a/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts b/x-pack/test/functional/apps/maps/group1/feature_controls/maps_security.ts similarity index 94% rename from x-pack/test/functional/apps/maps/feature_controls/maps_security.ts rename to x-pack/test/functional/apps/maps/group1/feature_controls/maps_security.ts index db6bfb642ebbb..94f46763acd31 100644 --- a/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts +++ b/x-pack/test/functional/apps/maps/group1/feature_controls/maps_security.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const security = getService('security'); @@ -113,18 +113,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { false ); }); - - it('allow saving currently loaded query as a copy', async () => { - await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); - await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( - 'ok2', - 'description', - true, - false - ); - await savedQueryManagementComponent.savedQueryExistOrFail('ok2'); - await savedQueryManagementComponent.deleteSavedQuery('ok2'); - }); }); describe('global maps read-only privileges', () => { diff --git a/x-pack/test/functional/apps/maps/feature_controls/maps_spaces.ts b/x-pack/test/functional/apps/maps/group1/feature_controls/maps_spaces.ts similarity index 98% rename from x-pack/test/functional/apps/maps/feature_controls/maps_spaces.ts rename to x-pack/test/functional/apps/maps/group1/feature_controls/maps_spaces.ts index 1beddd72d3fde..a306c15bfbdb2 100644 --- a/x-pack/test/functional/apps/maps/feature_controls/maps_spaces.ts +++ b/x-pack/test/functional/apps/maps/group1/feature_controls/maps_spaces.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { APP_ID } from '@kbn/maps-plugin/common/constants'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const spacesService = getService('spaces'); diff --git a/x-pack/test/functional/apps/maps/full_screen_mode.js b/x-pack/test/functional/apps/maps/group1/full_screen_mode.js similarity index 100% rename from x-pack/test/functional/apps/maps/full_screen_mode.js rename to x-pack/test/functional/apps/maps/group1/full_screen_mode.js diff --git a/x-pack/test/functional/apps/maps/group1/index.js b/x-pack/test/functional/apps/maps/group1/index.js new file mode 100644 index 0000000000000..be59d43012626 --- /dev/null +++ b/x-pack/test/functional/apps/maps/group1/index.js @@ -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. + */ + +export default function ({ loadTestFile, getService }) { + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + const browser = getService('browser'); + const log = getService('log'); + const supertest = getService('supertest'); + + describe('maps app', function () { + this.tags(['skipFirefox']); + + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/maps.json' + ); + // Functional tests verify behavior when referenced index pattern saved objects can not be found. + // However, saved object import fails when reference saved objects can not be found. + // To prevent import errors, index pattern saved object references exist during import + // but are then deleted afterwards to enable testing of missing reference index pattern saved objects. + + log.info('Delete index pattern'); + log.debug('id: ' + 'idThatDoesNotExitForESGeoGridSource'); + log.debug('id: ' + 'idThatDoesNotExitForESSearchSource'); + log.debug('id: ' + 'idThatDoesNotExitForESJoinSource'); + await supertest + .delete('/api/index_patterns/index_pattern/' + 'idThatDoesNotExitForESGeoGridSource') + .set('kbn-xsrf', 'true') + .expect(200); + + await supertest + .delete('/api/index_patterns/index_pattern/' + 'idThatDoesNotExitForESSearchSource') + .set('kbn-xsrf', 'true') + .expect(200); + + await supertest + .delete('/api/index_patterns/index_pattern/' + 'idThatDoesNotExitForESJoinSource') + .set('kbn-xsrf', 'true') + .expect(200); + + await esArchiver.load('x-pack/test/functional/es_archives/maps/data'); + await kibanaServer.uiSettings.replace({ + defaultIndex: 'c698b940-e149-11e8-a35a-370a8516603a', + }); + await browser.setWindowSize(1600, 1000); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/maps/data'); + await kibanaServer.importExport.unload( + 'x-pack/test/functional/fixtures/kbn_archiver/maps.json' + ); + }); + + loadTestFile(require.resolve('./documents_source')); + loadTestFile(require.resolve('./blended_vector_layer')); + loadTestFile(require.resolve('./vector_styling')); + loadTestFile(require.resolve('./saved_object_management')); + loadTestFile(require.resolve('./sample_data')); + loadTestFile(require.resolve('./auto_fit_to_bounds')); + loadTestFile(require.resolve('./layer_visibility')); + loadTestFile(require.resolve('./feature_controls/maps_security')); + loadTestFile(require.resolve('./feature_controls/maps_spaces')); + loadTestFile(require.resolve('./full_screen_mode')); + }); +} diff --git a/x-pack/test/functional/apps/maps/layer_visibility.js b/x-pack/test/functional/apps/maps/group1/layer_visibility.js similarity index 100% rename from x-pack/test/functional/apps/maps/layer_visibility.js rename to x-pack/test/functional/apps/maps/group1/layer_visibility.js diff --git a/x-pack/test/functional/apps/maps/sample_data.js b/x-pack/test/functional/apps/maps/group1/sample_data.js similarity index 100% rename from x-pack/test/functional/apps/maps/sample_data.js rename to x-pack/test/functional/apps/maps/group1/sample_data.js diff --git a/x-pack/test/functional/apps/maps/saved_object_management.js b/x-pack/test/functional/apps/maps/group1/saved_object_management.js similarity index 100% rename from x-pack/test/functional/apps/maps/saved_object_management.js rename to x-pack/test/functional/apps/maps/group1/saved_object_management.js diff --git a/x-pack/test/functional/apps/maps/vector_styling.js b/x-pack/test/functional/apps/maps/group1/vector_styling.js similarity index 100% rename from x-pack/test/functional/apps/maps/vector_styling.js rename to x-pack/test/functional/apps/maps/group1/vector_styling.js diff --git a/x-pack/test/functional/apps/maps/group2/config.ts b/x-pack/test/functional/apps/maps/group2/config.ts new file mode 100644 index 0000000000000..d927f93adeffd --- /dev/null +++ b/x-pack/test/functional/apps/maps/group2/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/maps/embeddable/add_to_dashboard.js b/x-pack/test/functional/apps/maps/group2/embeddable/add_to_dashboard.js similarity index 100% rename from x-pack/test/functional/apps/maps/embeddable/add_to_dashboard.js rename to x-pack/test/functional/apps/maps/group2/embeddable/add_to_dashboard.js diff --git a/x-pack/test/functional/apps/maps/embeddable/dashboard.js b/x-pack/test/functional/apps/maps/group2/embeddable/dashboard.js similarity index 100% rename from x-pack/test/functional/apps/maps/embeddable/dashboard.js rename to x-pack/test/functional/apps/maps/group2/embeddable/dashboard.js diff --git a/x-pack/test/functional/apps/maps/embeddable/embeddable_library.js b/x-pack/test/functional/apps/maps/group2/embeddable/embeddable_library.js similarity index 100% rename from x-pack/test/functional/apps/maps/embeddable/embeddable_library.js rename to x-pack/test/functional/apps/maps/group2/embeddable/embeddable_library.js diff --git a/x-pack/test/functional/apps/maps/embeddable/embeddable_state.js b/x-pack/test/functional/apps/maps/group2/embeddable/embeddable_state.js similarity index 100% rename from x-pack/test/functional/apps/maps/embeddable/embeddable_state.js rename to x-pack/test/functional/apps/maps/group2/embeddable/embeddable_state.js diff --git a/x-pack/test/functional/apps/maps/embeddable/filter_by_map_extent.js b/x-pack/test/functional/apps/maps/group2/embeddable/filter_by_map_extent.js similarity index 100% rename from x-pack/test/functional/apps/maps/embeddable/filter_by_map_extent.js rename to x-pack/test/functional/apps/maps/group2/embeddable/filter_by_map_extent.js diff --git a/x-pack/test/functional/apps/maps/embeddable/index.js b/x-pack/test/functional/apps/maps/group2/embeddable/index.js similarity index 100% rename from x-pack/test/functional/apps/maps/embeddable/index.js rename to x-pack/test/functional/apps/maps/group2/embeddable/index.js diff --git a/x-pack/test/functional/apps/maps/embeddable/save_and_return.js b/x-pack/test/functional/apps/maps/group2/embeddable/save_and_return.js similarity index 100% rename from x-pack/test/functional/apps/maps/embeddable/save_and_return.js rename to x-pack/test/functional/apps/maps/group2/embeddable/save_and_return.js diff --git a/x-pack/test/functional/apps/maps/embeddable/tooltip_filter_actions.js b/x-pack/test/functional/apps/maps/group2/embeddable/tooltip_filter_actions.js similarity index 100% rename from x-pack/test/functional/apps/maps/embeddable/tooltip_filter_actions.js rename to x-pack/test/functional/apps/maps/group2/embeddable/tooltip_filter_actions.js diff --git a/x-pack/test/functional/apps/maps/es_geo_grid_source.js b/x-pack/test/functional/apps/maps/group2/es_geo_grid_source.js similarity index 100% rename from x-pack/test/functional/apps/maps/es_geo_grid_source.js rename to x-pack/test/functional/apps/maps/group2/es_geo_grid_source.js diff --git a/x-pack/test/functional/apps/maps/group2/index.js b/x-pack/test/functional/apps/maps/group2/index.js new file mode 100644 index 0000000000000..b8b3a15a10ed5 --- /dev/null +++ b/x-pack/test/functional/apps/maps/group2/index.js @@ -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. + */ + +export default function ({ loadTestFile, getService }) { + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + const browser = getService('browser'); + const log = getService('log'); + const supertest = getService('supertest'); + + describe('maps app', function () { + this.tags(['skipFirefox']); + + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/maps.json' + ); + // Functional tests verify behavior when referenced index pattern saved objects can not be found. + // However, saved object import fails when reference saved objects can not be found. + // To prevent import errors, index pattern saved object references exist during import + // but are then deleted afterwards to enable testing of missing reference index pattern saved objects. + + log.info('Delete index pattern'); + log.debug('id: ' + 'idThatDoesNotExitForESGeoGridSource'); + log.debug('id: ' + 'idThatDoesNotExitForESSearchSource'); + log.debug('id: ' + 'idThatDoesNotExitForESJoinSource'); + await supertest + .delete('/api/index_patterns/index_pattern/' + 'idThatDoesNotExitForESGeoGridSource') + .set('kbn-xsrf', 'true') + .expect(200); + + await supertest + .delete('/api/index_patterns/index_pattern/' + 'idThatDoesNotExitForESSearchSource') + .set('kbn-xsrf', 'true') + .expect(200); + + await supertest + .delete('/api/index_patterns/index_pattern/' + 'idThatDoesNotExitForESJoinSource') + .set('kbn-xsrf', 'true') + .expect(200); + + await esArchiver.load('x-pack/test/functional/es_archives/maps/data'); + await kibanaServer.uiSettings.replace({ + defaultIndex: 'c698b940-e149-11e8-a35a-370a8516603a', + }); + await browser.setWindowSize(1600, 1000); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/maps/data'); + await kibanaServer.importExport.unload( + 'x-pack/test/functional/fixtures/kbn_archiver/maps.json' + ); + }); + + loadTestFile(require.resolve('./es_geo_grid_source')); + loadTestFile(require.resolve('./embeddable')); + }); +} diff --git a/x-pack/test/functional/apps/maps/group3/config.ts b/x-pack/test/functional/apps/maps/group3/config.ts new file mode 100644 index 0000000000000..d927f93adeffd --- /dev/null +++ b/x-pack/test/functional/apps/maps/group3/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/maps/group3/index.js b/x-pack/test/functional/apps/maps/group3/index.js new file mode 100644 index 0000000000000..fda116cecc307 --- /dev/null +++ b/x-pack/test/functional/apps/maps/group3/index.js @@ -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. + */ + +export default function ({ loadTestFile, getService }) { + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + const browser = getService('browser'); + const log = getService('log'); + const supertest = getService('supertest'); + + describe('maps app', function () { + this.tags(['skipFirefox']); + + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/maps.json' + ); + // Functional tests verify behavior when referenced index pattern saved objects can not be found. + // However, saved object import fails when reference saved objects can not be found. + // To prevent import errors, index pattern saved object references exist during import + // but are then deleted afterwards to enable testing of missing reference index pattern saved objects. + + log.info('Delete index pattern'); + log.debug('id: ' + 'idThatDoesNotExitForESGeoGridSource'); + log.debug('id: ' + 'idThatDoesNotExitForESSearchSource'); + log.debug('id: ' + 'idThatDoesNotExitForESJoinSource'); + await supertest + .delete('/api/index_patterns/index_pattern/' + 'idThatDoesNotExitForESGeoGridSource') + .set('kbn-xsrf', 'true') + .expect(200); + + await supertest + .delete('/api/index_patterns/index_pattern/' + 'idThatDoesNotExitForESSearchSource') + .set('kbn-xsrf', 'true') + .expect(200); + + await supertest + .delete('/api/index_patterns/index_pattern/' + 'idThatDoesNotExitForESJoinSource') + .set('kbn-xsrf', 'true') + .expect(200); + + await esArchiver.load('x-pack/test/functional/es_archives/maps/data'); + await kibanaServer.uiSettings.replace({ + defaultIndex: 'c698b940-e149-11e8-a35a-370a8516603a', + }); + await browser.setWindowSize(1600, 1000); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/maps/data'); + await kibanaServer.importExport.unload( + 'x-pack/test/functional/fixtures/kbn_archiver/maps.json' + ); + }); + + loadTestFile(require.resolve('./reports')); + }); +} diff --git a/x-pack/test/functional/apps/maps/reports/baseline/example_map_report.png b/x-pack/test/functional/apps/maps/group3/reports/baseline/example_map_report.png similarity index 100% rename from x-pack/test/functional/apps/maps/reports/baseline/example_map_report.png rename to x-pack/test/functional/apps/maps/group3/reports/baseline/example_map_report.png diff --git a/x-pack/test/functional/apps/maps/reports/baseline/geo_map_report.png b/x-pack/test/functional/apps/maps/group3/reports/baseline/geo_map_report.png similarity index 100% rename from x-pack/test/functional/apps/maps/reports/baseline/geo_map_report.png rename to x-pack/test/functional/apps/maps/group3/reports/baseline/geo_map_report.png diff --git a/x-pack/test/functional/apps/maps/group3/reports/index.ts b/x-pack/test/functional/apps/maps/group3/reports/index.ts new file mode 100644 index 0000000000000..c892842782aa6 --- /dev/null +++ b/x-pack/test/functional/apps/maps/group3/reports/index.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +const REPORTS_FOLDER = __dirname; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const PageObjects = getPageObjects(['reporting', 'common', 'dashboard']); + const config = getService('config'); + const log = getService('log'); + const reporting = getService('reporting'); + + describe('dashboard reporting', () => { + // helper function to check the difference between the new image and the baseline + const measurePngDifference = async (fileName: string) => { + const url = await PageObjects.reporting.getReportURL(60000); + const reportData = await PageObjects.reporting.getRawPdfReportData(url); + + const sessionReportPath = await PageObjects.reporting.writeSessionReport( + fileName, + 'png', + reportData, + REPORTS_FOLDER + ); + log.debug(`session report path: ${sessionReportPath}`); + + expect(sessionReportPath).not.to.be(null); + return await reporting.checkIfPngsMatch( + sessionReportPath, + PageObjects.reporting.getBaselineReportPath(fileName, 'png', REPORTS_FOLDER), + config.get('screenshots.directory'), + log + ); + }; + + after(async () => { + await reporting.deleteAllReports(); + }); + + it('creates a map report using sample geo data', async function () { + await reporting.initEcommerce(); + + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.loadSavedDashboard('Ecommerce Map'); + await PageObjects.reporting.openPngReportingPanel(); + await PageObjects.reporting.clickGenerateReportButton(); + + const percentDiff = await measurePngDifference('geo_map_report'); + expect(percentDiff).to.be.lessThan(0.09); + + await reporting.teardownEcommerce(); + }); + + it('creates a map report using embeddable example', async function () { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.loadSavedDashboard('map embeddable example'); + await PageObjects.reporting.openPngReportingPanel(); + await PageObjects.reporting.clickGenerateReportButton(); + + const percentDiff = await measurePngDifference('example_map_report'); + expect(percentDiff).to.be.lessThan(0.09); + }); + }); +} diff --git a/x-pack/test/functional/apps/maps/add_layer_panel.js b/x-pack/test/functional/apps/maps/group4/add_layer_panel.js similarity index 100% rename from x-pack/test/functional/apps/maps/add_layer_panel.js rename to x-pack/test/functional/apps/maps/group4/add_layer_panel.js diff --git a/x-pack/test/functional/apps/maps/group4/config.ts b/x-pack/test/functional/apps/maps/group4/config.ts new file mode 100644 index 0000000000000..d927f93adeffd --- /dev/null +++ b/x-pack/test/functional/apps/maps/group4/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/maps/discover.js b/x-pack/test/functional/apps/maps/group4/discover.js similarity index 100% rename from x-pack/test/functional/apps/maps/discover.js rename to x-pack/test/functional/apps/maps/group4/discover.js diff --git a/x-pack/test/functional/apps/maps/es_pew_pew_source.js b/x-pack/test/functional/apps/maps/group4/es_pew_pew_source.js similarity index 100% rename from x-pack/test/functional/apps/maps/es_pew_pew_source.js rename to x-pack/test/functional/apps/maps/group4/es_pew_pew_source.js diff --git a/x-pack/test/functional/apps/maps/file_upload/files/cb_2018_us_csa_500k.dbf b/x-pack/test/functional/apps/maps/group4/file_upload/files/cb_2018_us_csa_500k.dbf similarity index 100% rename from x-pack/test/functional/apps/maps/file_upload/files/cb_2018_us_csa_500k.dbf rename to x-pack/test/functional/apps/maps/group4/file_upload/files/cb_2018_us_csa_500k.dbf diff --git a/x-pack/test/functional/apps/maps/file_upload/files/cb_2018_us_csa_500k.prj b/x-pack/test/functional/apps/maps/group4/file_upload/files/cb_2018_us_csa_500k.prj similarity index 100% rename from x-pack/test/functional/apps/maps/file_upload/files/cb_2018_us_csa_500k.prj rename to x-pack/test/functional/apps/maps/group4/file_upload/files/cb_2018_us_csa_500k.prj diff --git a/x-pack/test/functional/apps/maps/file_upload/files/cb_2018_us_csa_500k.shp b/x-pack/test/functional/apps/maps/group4/file_upload/files/cb_2018_us_csa_500k.shp similarity index 100% rename from x-pack/test/functional/apps/maps/file_upload/files/cb_2018_us_csa_500k.shp rename to x-pack/test/functional/apps/maps/group4/file_upload/files/cb_2018_us_csa_500k.shp diff --git a/x-pack/test/functional/apps/maps/file_upload/files/cb_2018_us_csa_500k.shx b/x-pack/test/functional/apps/maps/group4/file_upload/files/cb_2018_us_csa_500k.shx similarity index 100% rename from x-pack/test/functional/apps/maps/file_upload/files/cb_2018_us_csa_500k.shx rename to x-pack/test/functional/apps/maps/group4/file_upload/files/cb_2018_us_csa_500k.shx diff --git a/x-pack/test/functional/apps/maps/file_upload/files/point.json b/x-pack/test/functional/apps/maps/group4/file_upload/files/point.json similarity index 100% rename from x-pack/test/functional/apps/maps/file_upload/files/point.json rename to x-pack/test/functional/apps/maps/group4/file_upload/files/point.json diff --git a/x-pack/test/functional/apps/maps/file_upload/files/polygon.json b/x-pack/test/functional/apps/maps/group4/file_upload/files/polygon.json similarity index 100% rename from x-pack/test/functional/apps/maps/file_upload/files/polygon.json rename to x-pack/test/functional/apps/maps/group4/file_upload/files/polygon.json diff --git a/x-pack/test/functional/apps/maps/file_upload/files/world_countries_v7.geo.json b/x-pack/test/functional/apps/maps/group4/file_upload/files/world_countries_v7.geo.json similarity index 100% rename from x-pack/test/functional/apps/maps/file_upload/files/world_countries_v7.geo.json rename to x-pack/test/functional/apps/maps/group4/file_upload/files/world_countries_v7.geo.json diff --git a/x-pack/test/functional/apps/maps/file_upload/geojson.js b/x-pack/test/functional/apps/maps/group4/file_upload/geojson.js similarity index 100% rename from x-pack/test/functional/apps/maps/file_upload/geojson.js rename to x-pack/test/functional/apps/maps/group4/file_upload/geojson.js diff --git a/x-pack/test/functional/apps/maps/file_upload/index.js b/x-pack/test/functional/apps/maps/group4/file_upload/index.js similarity index 100% rename from x-pack/test/functional/apps/maps/file_upload/index.js rename to x-pack/test/functional/apps/maps/group4/file_upload/index.js diff --git a/x-pack/test/functional/apps/maps/file_upload/shapefile.js b/x-pack/test/functional/apps/maps/group4/file_upload/shapefile.js similarity index 100% rename from x-pack/test/functional/apps/maps/file_upload/shapefile.js rename to x-pack/test/functional/apps/maps/group4/file_upload/shapefile.js diff --git a/x-pack/test/functional/apps/maps/file_upload/wizard.js b/x-pack/test/functional/apps/maps/group4/file_upload/wizard.js similarity index 100% rename from x-pack/test/functional/apps/maps/file_upload/wizard.js rename to x-pack/test/functional/apps/maps/group4/file_upload/wizard.js diff --git a/x-pack/test/functional/apps/maps/geofile_wizard_auto_open.ts b/x-pack/test/functional/apps/maps/group4/geofile_wizard_auto_open.ts similarity index 95% rename from x-pack/test/functional/apps/maps/geofile_wizard_auto_open.ts rename to x-pack/test/functional/apps/maps/group4/geofile_wizard_auto_open.ts index dd69cc7882fc7..ebe434b6afe6e 100644 --- a/x-pack/test/functional/apps/maps/geofile_wizard_auto_open.ts +++ b/x-pack/test/functional/apps/maps/group4/geofile_wizard_auto_open.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'maps']); diff --git a/x-pack/test/functional/apps/maps/group4/index.js b/x-pack/test/functional/apps/maps/group4/index.js new file mode 100644 index 0000000000000..70cb5ac660768 --- /dev/null +++ b/x-pack/test/functional/apps/maps/group4/index.js @@ -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 default function ({ loadTestFile, getService }) { + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + const browser = getService('browser'); + const log = getService('log'); + const supertest = getService('supertest'); + + describe('maps app', function () { + this.tags(['skipFirefox']); + + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/maps.json' + ); + // Functional tests verify behavior when referenced index pattern saved objects can not be found. + // However, saved object import fails when reference saved objects can not be found. + // To prevent import errors, index pattern saved object references exist during import + // but are then deleted afterwards to enable testing of missing reference index pattern saved objects. + + log.info('Delete index pattern'); + log.debug('id: ' + 'idThatDoesNotExitForESGeoGridSource'); + log.debug('id: ' + 'idThatDoesNotExitForESSearchSource'); + log.debug('id: ' + 'idThatDoesNotExitForESJoinSource'); + await supertest + .delete('/api/index_patterns/index_pattern/' + 'idThatDoesNotExitForESGeoGridSource') + .set('kbn-xsrf', 'true') + .expect(200); + + await supertest + .delete('/api/index_patterns/index_pattern/' + 'idThatDoesNotExitForESSearchSource') + .set('kbn-xsrf', 'true') + .expect(200); + + await supertest + .delete('/api/index_patterns/index_pattern/' + 'idThatDoesNotExitForESJoinSource') + .set('kbn-xsrf', 'true') + .expect(200); + + await esArchiver.load('x-pack/test/functional/es_archives/maps/data'); + await kibanaServer.uiSettings.replace({ + defaultIndex: 'c698b940-e149-11e8-a35a-370a8516603a', + }); + await browser.setWindowSize(1600, 1000); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/maps/data'); + await kibanaServer.importExport.unload( + 'x-pack/test/functional/fixtures/kbn_archiver/maps.json' + ); + }); + + loadTestFile(require.resolve('./es_pew_pew_source')); + loadTestFile(require.resolve('./joins')); + loadTestFile(require.resolve('./mvt_joins')); + loadTestFile(require.resolve('./mapbox_styles')); + loadTestFile(require.resolve('./mvt_scaling')); + loadTestFile(require.resolve('./mvt_geotile_grid')); + loadTestFile(require.resolve('./add_layer_panel')); + loadTestFile(require.resolve('./file_upload')); + loadTestFile(require.resolve('./layer_errors')); + loadTestFile(require.resolve('./visualize_create_menu')); + loadTestFile(require.resolve('./discover')); + loadTestFile(require.resolve('./geofile_wizard_auto_open')); + loadTestFile(require.resolve('./lens')); + }); +} diff --git a/x-pack/test/functional/apps/maps/joins.js b/x-pack/test/functional/apps/maps/group4/joins.js similarity index 100% rename from x-pack/test/functional/apps/maps/joins.js rename to x-pack/test/functional/apps/maps/group4/joins.js diff --git a/x-pack/test/functional/apps/maps/layer_errors.js b/x-pack/test/functional/apps/maps/group4/layer_errors.js similarity index 100% rename from x-pack/test/functional/apps/maps/layer_errors.js rename to x-pack/test/functional/apps/maps/group4/layer_errors.js diff --git a/x-pack/test/functional/apps/maps/lens/choropleth_chart.ts b/x-pack/test/functional/apps/maps/group4/lens/choropleth_chart.ts similarity index 97% rename from x-pack/test/functional/apps/maps/lens/choropleth_chart.ts rename to x-pack/test/functional/apps/maps/group4/lens/choropleth_chart.ts index 420f895fe6aa6..b026dd7444748 100644 --- a/x-pack/test/functional/apps/maps/lens/choropleth_chart.ts +++ b/x-pack/test/functional/apps/maps/group4/lens/choropleth_chart.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'maps']); diff --git a/x-pack/test/functional/apps/maps/group4/lens/index.ts b/x-pack/test/functional/apps/maps/group4/lens/index.ts new file mode 100644 index 0000000000000..484faf431be75 --- /dev/null +++ b/x-pack/test/functional/apps/maps/group4/lens/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('lens', function () { + loadTestFile(require.resolve('./choropleth_chart')); + }); +} diff --git a/x-pack/test/functional/apps/maps/mapbox_styles.js b/x-pack/test/functional/apps/maps/group4/mapbox_styles.js similarity index 100% rename from x-pack/test/functional/apps/maps/mapbox_styles.js rename to x-pack/test/functional/apps/maps/group4/mapbox_styles.js diff --git a/x-pack/test/functional/apps/maps/mvt_geotile_grid.js b/x-pack/test/functional/apps/maps/group4/mvt_geotile_grid.js similarity index 100% rename from x-pack/test/functional/apps/maps/mvt_geotile_grid.js rename to x-pack/test/functional/apps/maps/group4/mvt_geotile_grid.js diff --git a/x-pack/test/functional/apps/maps/mvt_joins.ts b/x-pack/test/functional/apps/maps/group4/mvt_joins.ts similarity index 98% rename from x-pack/test/functional/apps/maps/mvt_joins.ts rename to x-pack/test/functional/apps/maps/group4/mvt_joins.ts index 2ae8f7ea5943b..c6567c84b2165 100644 --- a/x-pack/test/functional/apps/maps/mvt_joins.ts +++ b/x-pack/test/functional/apps/maps/group4/mvt_joins.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const PageObjects = getPageObjects(['maps']); diff --git a/x-pack/test/functional/apps/maps/mvt_scaling.js b/x-pack/test/functional/apps/maps/group4/mvt_scaling.js similarity index 100% rename from x-pack/test/functional/apps/maps/mvt_scaling.js rename to x-pack/test/functional/apps/maps/group4/mvt_scaling.js diff --git a/x-pack/test/functional/apps/maps/visualize_create_menu.js b/x-pack/test/functional/apps/maps/group4/visualize_create_menu.js similarity index 100% rename from x-pack/test/functional/apps/maps/visualize_create_menu.js rename to x-pack/test/functional/apps/maps/group4/visualize_create_menu.js diff --git a/x-pack/test/functional/apps/maps/index.js b/x-pack/test/functional/apps/maps/index.js deleted file mode 100644 index 9d7e7a9cbddcc..0000000000000 --- a/x-pack/test/functional/apps/maps/index.js +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export default function ({ loadTestFile, getService }) { - const kibanaServer = getService('kibanaServer'); - const esArchiver = getService('esArchiver'); - const browser = getService('browser'); - const log = getService('log'); - const supertest = getService('supertest'); - - describe('maps app', function () { - this.tags(['skipFirefox']); - - before(async () => { - await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); - await kibanaServer.importExport.load( - 'x-pack/test/functional/fixtures/kbn_archiver/maps.json' - ); - // Functional tests verify behavior when referenced index pattern saved objects can not be found. - // However, saved object import fails when reference saved objects can not be found. - // To prevent import errors, index pattern saved object references exist during import - // but are then deleted afterwards to enable testing of missing reference index pattern saved objects. - - log.info('Delete index pattern'); - log.debug('id: ' + 'idThatDoesNotExitForESGeoGridSource'); - log.debug('id: ' + 'idThatDoesNotExitForESSearchSource'); - log.debug('id: ' + 'idThatDoesNotExitForESJoinSource'); - await supertest - .delete('/api/index_patterns/index_pattern/' + 'idThatDoesNotExitForESGeoGridSource') - .set('kbn-xsrf', 'true') - .expect(200); - - await supertest - .delete('/api/index_patterns/index_pattern/' + 'idThatDoesNotExitForESSearchSource') - .set('kbn-xsrf', 'true') - .expect(200); - - await supertest - .delete('/api/index_patterns/index_pattern/' + 'idThatDoesNotExitForESJoinSource') - .set('kbn-xsrf', 'true') - .expect(200); - - await esArchiver.load('x-pack/test/functional/es_archives/maps/data'); - await kibanaServer.uiSettings.replace({ - defaultIndex: 'c698b940-e149-11e8-a35a-370a8516603a', - }); - await browser.setWindowSize(1600, 1000); - }); - - after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/maps/data'); - await kibanaServer.importExport.unload( - 'x-pack/test/functional/fixtures/kbn_archiver/maps.json' - ); - }); - - describe('', async function () { - this.tags('ciGroup9'); - loadTestFile(require.resolve('./documents_source')); - loadTestFile(require.resolve('./blended_vector_layer')); - loadTestFile(require.resolve('./vector_styling')); - loadTestFile(require.resolve('./saved_object_management')); - loadTestFile(require.resolve('./sample_data')); - loadTestFile(require.resolve('./auto_fit_to_bounds')); - loadTestFile(require.resolve('./layer_visibility')); - loadTestFile(require.resolve('./feature_controls/maps_security')); - loadTestFile(require.resolve('./feature_controls/maps_spaces')); - loadTestFile(require.resolve('./full_screen_mode')); - }); - - describe('', function () { - this.tags('ciGroup22'); - loadTestFile(require.resolve('./es_geo_grid_source')); - loadTestFile(require.resolve('./embeddable')); - }); - - describe('', function () { - this.tags('ciGroup2'); // same group used in x-pack/test/reporting_functional - loadTestFile(require.resolve('./reports')); - }); - - describe('', function () { - this.tags('ciGroup10'); - loadTestFile(require.resolve('./es_pew_pew_source')); - loadTestFile(require.resolve('./joins')); - loadTestFile(require.resolve('./mvt_joins')); - loadTestFile(require.resolve('./mapbox_styles')); - loadTestFile(require.resolve('./mvt_scaling')); - loadTestFile(require.resolve('./mvt_geotile_grid')); - loadTestFile(require.resolve('./add_layer_panel')); - loadTestFile(require.resolve('./file_upload')); - loadTestFile(require.resolve('./layer_errors')); - loadTestFile(require.resolve('./visualize_create_menu')); - loadTestFile(require.resolve('./discover')); - loadTestFile(require.resolve('./geofile_wizard_auto_open')); - loadTestFile(require.resolve('./lens')); - }); - }); -} diff --git a/x-pack/test/functional/apps/maps/lens/index.ts b/x-pack/test/functional/apps/maps/lens/index.ts deleted file mode 100644 index 78086303166a1..0000000000000 --- a/x-pack/test/functional/apps/maps/lens/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ loadTestFile }: FtrProviderContext) { - describe('lens', function () { - loadTestFile(require.resolve('./choropleth_chart')); - }); -} diff --git a/x-pack/test/functional/apps/maps/reports/index.ts b/x-pack/test/functional/apps/maps/reports/index.ts deleted file mode 100644 index 4e942b1e150ef..0000000000000 --- a/x-pack/test/functional/apps/maps/reports/index.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../ftr_provider_context'; - -const REPORTS_FOLDER = __dirname; - -export default function ({ getPageObjects, getService }: FtrProviderContext) { - const PageObjects = getPageObjects(['reporting', 'common', 'dashboard']); - const config = getService('config'); - const log = getService('log'); - const reporting = getService('reporting'); - - describe('dashboard reporting', () => { - // helper function to check the difference between the new image and the baseline - const measurePngDifference = async (fileName: string) => { - const url = await PageObjects.reporting.getReportURL(60000); - const reportData = await PageObjects.reporting.getRawPdfReportData(url); - - const sessionReportPath = await PageObjects.reporting.writeSessionReport( - fileName, - 'png', - reportData, - REPORTS_FOLDER - ); - log.debug(`session report path: ${sessionReportPath}`); - - expect(sessionReportPath).not.to.be(null); - return await reporting.checkIfPngsMatch( - sessionReportPath, - PageObjects.reporting.getBaselineReportPath(fileName, 'png', REPORTS_FOLDER), - config.get('screenshots.directory'), - log - ); - }; - - after(async () => { - await reporting.deleteAllReports(); - }); - - it('creates a map report using sample geo data', async function () { - await reporting.initEcommerce(); - - await PageObjects.common.navigateToApp('dashboard'); - await PageObjects.dashboard.loadSavedDashboard('Ecommerce Map'); - await PageObjects.reporting.openPngReportingPanel(); - await PageObjects.reporting.clickGenerateReportButton(); - - const percentDiff = await measurePngDifference('geo_map_report'); - expect(percentDiff).to.be.lessThan(0.09); - - await reporting.teardownEcommerce(); - }); - - it('creates a map report using embeddable example', async function () { - await PageObjects.common.navigateToApp('dashboard'); - await PageObjects.dashboard.loadSavedDashboard('map embeddable example'); - await PageObjects.reporting.openPngReportingPanel(); - await PageObjects.reporting.clickGenerateReportButton(); - - const percentDiff = await measurePngDifference('example_map_report'); - expect(percentDiff).to.be.lessThan(0.09); - }); - }); -} diff --git a/x-pack/test/functional/apps/ml/README.md b/x-pack/test/functional/apps/ml/README.md new file mode 100644 index 0000000000000..5e87a8b210bdd --- /dev/null +++ b/x-pack/test/functional/apps/ml/README.md @@ -0,0 +1,7 @@ +# What are all these groups? + +These tests take a while so they have been broken up into groups with their own `config.ts` and `index.ts` file, causing each of these groups to be independent bundles of tests which can be run on some worker in CI without taking an incredible amount of time. + +Want to change the groups to something more logical? Have fun! Just make sure that each group executes on CI in less than 10 minutes or so. We don't currently have any mechanism for validating this right now, you just need to look at the times in the log output on CI, but we'll be working on tooling for making this information more accessible soon. + +- Kibana Operations \ No newline at end of file diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/advanced_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection/advanced_job.ts index 1fc4c87619f18..c809d0ee5c20d 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/advanced_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/advanced_job.ts @@ -220,7 +220,7 @@ export default function ({ getService }: FtrProviderContext) { const calendarId = `wizard-test-calendar_${Date.now()}`; describe('advanced job', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); await ml.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/aggregated_scripted_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection/aggregated_scripted_job.ts index c47171c1cd75a..0740c365f02e2 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/aggregated_scripted_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/aggregated_scripted_job.ts @@ -360,7 +360,7 @@ export default function ({ getService }: FtrProviderContext) { ]; describe('aggregated or scripted job', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/annotations.ts b/x-pack/test/functional/apps/ml/anomaly_detection/annotations.ts index 3b36701e651a7..05e38d565e969 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/annotations.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/annotations.ts @@ -13,7 +13,7 @@ export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); describe('annotations', function () { - this.tags(['mlqa']); + this.tags(['ml']); const jobId = `fq_single_1_smv_${Date.now()}`; const annotation = { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/anomaly_explorer.ts b/x-pack/test/functional/apps/ml/anomaly_detection/anomaly_explorer.ts index bf6fcd10a9152..6e3e98171b109 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/anomaly_explorer.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/anomaly_explorer.ts @@ -64,7 +64,7 @@ export default function ({ getService }: FtrProviderContext) { const elasticChart = getService('elasticChart'); describe('anomaly explorer', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/categorization_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection/categorization_job.ts index 555040ef8e59e..2ee9d226596d8 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/categorization_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/categorization_job.ts @@ -75,7 +75,7 @@ export default function ({ getService }: FtrProviderContext) { const calendarId = `wizard-test-calendar_${Date.now()}`; describe('categorization', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/categorization_small'); await ml.testResources.createIndexPatternIfNeeded('ft_categorization_small', '@timestamp'); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/config.ts b/x-pack/test/functional/apps/ml/anomaly_detection/config.ts new file mode 100644 index 0000000000000..9078782e36f0b --- /dev/null +++ b/x-pack/test/functional/apps/ml/anomaly_detection/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + junit: { + reportName: 'Chrome X-Pack UI Functional Tests - ML anomaly_detection', + }, + }; +} diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/custom_urls.ts b/x-pack/test/functional/apps/ml/anomaly_detection/custom_urls.ts index a83f5d814c028..7920cf9721d47 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/custom_urls.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/custom_urls.ts @@ -63,7 +63,7 @@ export default function ({ getService }: FtrProviderContext) { const browser = getService('browser'); describe('custom urls', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/date_nanos_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection/date_nanos_job.ts index 0401d13780403..ed9f63be66dd4 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/date_nanos_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/date_nanos_job.ts @@ -115,7 +115,7 @@ export default function ({ getService }: FtrProviderContext) { ]; describe('job on data set with date_nanos time field', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/event_rate_nanos'); await ml.testResources.createIndexPatternIfNeeded( diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/forecasts.ts b/x-pack/test/functional/apps/ml/anomaly_detection/forecasts.ts index 964e0762d9322..93ec331230a8a 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/forecasts.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/forecasts.ts @@ -40,7 +40,7 @@ export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); describe('forecasts', function () { - this.tags(['mlqa']); + this.tags(['ml']); describe('with single metric job', function () { before(async () => { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/index.ts b/x-pack/test/functional/apps/ml/anomaly_detection/index.ts index ed5f618f86644..0b206bfc450f3 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/index.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/index.ts @@ -7,10 +7,33 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; -export default function ({ loadTestFile }: FtrProviderContext) { - describe('anomaly detection', function () { +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + + describe('machine learning - anomaly detection', function () { this.tags(['skipFirefox']); + before(async () => { + await ml.securityCommon.createMlRoles(); + await ml.securityCommon.createMlUsers(); + }); + + after(async () => { + // NOTE: Logout needs to happen before anything else to avoid flaky behavior + await ml.securityUI.logout(); + + await ml.securityCommon.cleanMlUsers(); + await ml.securityCommon.cleanMlRoles(); + + await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote'); + await esArchiver.unload('x-pack/test/functional/es_archives/ml/ecommerce'); + await esArchiver.unload('x-pack/test/functional/es_archives/ml/categorization_small'); + await esArchiver.unload('x-pack/test/functional/es_archives/ml/event_rate_nanos'); + + await ml.testResources.resetKibanaTimeZone(); + }); + loadTestFile(require.resolve('./single_metric_job')); loadTestFile(require.resolve('./single_metric_job_without_datafeed_start')); loadTestFile(require.resolve('./multi_metric_job')); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/multi_metric_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection/multi_metric_job.ts index 4786f51bdc414..dcb47b205bb1b 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/multi_metric_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/multi_metric_job.ts @@ -72,7 +72,7 @@ export default function ({ getService }: FtrProviderContext) { const calendarId = `wizard-test-calendar_${Date.now()}`; describe('multi metric', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/population_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection/population_job.ts index f522f5ebefd9a..0d04bb2ff7064 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/population_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/population_job.ts @@ -86,7 +86,7 @@ export default function ({ getService }: FtrProviderContext) { const calendarId = `wizard-test-calendar_${Date.now()}`; describe('population', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); await ml.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/saved_search_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection/saved_search_job.ts index f314052035ff1..7d9c528d763d7 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/saved_search_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/saved_search_job.ts @@ -266,7 +266,7 @@ export default function ({ getService }: FtrProviderContext) { ]; describe('saved search', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_job.ts index 8ec2e4a48e228..cb21f8de77bd2 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_job.ts @@ -72,7 +72,7 @@ export default function ({ getService }: FtrProviderContext) { const calendarId = `wizard-test-calendar_${Date.now()}`; describe('single metric', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_job_without_datafeed_start.ts b/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_job_without_datafeed_start.ts index 8d08fb84a8f55..4cdea1a726fe9 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_job_without_datafeed_start.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_job_without_datafeed_start.ts @@ -57,7 +57,7 @@ export default function ({ getService }: FtrProviderContext) { } describe('single metric without datafeed start', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_viewer.ts b/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_viewer.ts index c0946713a2564..809ebf204e2a7 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_viewer.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_viewer.ts @@ -41,7 +41,7 @@ export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); describe('single metric viewer', function () { - this.tags(['mlqa']); + this.tags(['ml']); describe('with single metric job', function () { before(async () => { diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/config.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/config.ts new file mode 100644 index 0000000000000..e82782f89973e --- /dev/null +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + junit: { + reportName: 'Chrome X-Pack UI Functional Tests - ML data_frame_analytics', + }, + }; +} diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/index.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/index.ts index bc11a44148546..19844632cc411 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/index.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/index.ts @@ -7,9 +7,32 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; -export default function ({ loadTestFile }: FtrProviderContext) { - describe('data frame analytics', function () { - this.tags(['mlqa', 'skipFirefox']); +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + + describe('machine learning - data frame analytics', function () { + this.tags(['ml', 'skipFirefox']); + + before(async () => { + await ml.securityCommon.createMlRoles(); + await ml.securityCommon.createMlUsers(); + }); + + after(async () => { + // NOTE: Logout needs to happen before anything else to avoid flaky behavior + await ml.securityUI.logout(); + + await ml.securityCommon.cleanMlUsers(); + await ml.securityCommon.cleanMlRoles(); + + await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote_small'); + await esArchiver.unload('x-pack/test/functional/es_archives/ml/bm_classification'); + await esArchiver.unload('x-pack/test/functional/es_archives/ml/ihp_outlier'); + await esArchiver.unload('x-pack/test/functional/es_archives/ml/egs_regression'); + + await ml.testResources.resetKibanaTimeZone(); + }); loadTestFile(require.resolve('./outlier_detection_creation')); loadTestFile(require.resolve('./regression_creation')); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts index 1dacd8a7e80b4..947cd82cdd342 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts @@ -160,12 +160,10 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('displays the source data preview'); await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewExists(); - - await ml.testExecution.logTestStep('enables the source data preview histogram charts'); - await ml.dataFrameAnalyticsCreation.enableSourceDataPreviewHistogramCharts(true); + await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewHistogramChartEnabled(true); await ml.testExecution.logTestStep('displays the source data preview histogram charts'); - await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewHistogramCharts( + await ml.dataFrameAnalyticsCreation.enableAndAssertSourceDataPreviewHistogramCharts( testData.expected.histogramCharts ); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation_saved_search.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation_saved_search.ts index 75ad646a26ac9..1dc431c74a97d 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation_saved_search.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation_saved_search.ts @@ -163,8 +163,7 @@ export default function ({ getService }: FtrProviderContext) { ]; for (const testData of testDataList) { - // FLAKY: https://github.com/elastic/kibana/issues/126112 - describe.skip(`${testData.suiteTitle}`, function () { + describe(`${testData.suiteTitle}`, function () { after(async () => { await ml.api.deleteIndices(testData.destinationIndex); await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex); @@ -218,12 +217,10 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('displays the source data preview'); await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewExists(); - - await ml.testExecution.logTestStep('enables the source data preview histogram charts'); - await ml.dataFrameAnalyticsCreation.enableSourceDataPreviewHistogramCharts(true); + await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewHistogramChartEnabled(true); await ml.testExecution.logTestStep('displays the source data preview histogram charts'); - await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewHistogramCharts( + await ml.dataFrameAnalyticsCreation.enableAndAssertSourceDataPreviewHistogramCharts( testData.expected.histogramCharts ); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/results_view_content.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/results_view_content.ts index 57d3922b043a3..2bddf0a7d9512 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/results_view_content.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/results_view_content.ts @@ -14,8 +14,7 @@ export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const ml = getService('ml'); - // FLAKY: https://github.com/elastic/kibana/issues/126422 - describe.skip('results view content and total feature importance', function () { + describe('results view content and total feature importance', function () { const testDataList: Array<{ suiteTitle: string; archive: string; @@ -284,6 +283,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('should display the total feature importance in the results view', async () => { + await ml.dataFrameAnalyticsResults.expandFeatureImportanceSection(true); await ml.dataFrameAnalyticsResults.assertTotalFeatureImportanceEvaluatePanelExists(); }); diff --git a/x-pack/test/functional/apps/ml/data_visualizer/config.ts b/x-pack/test/functional/apps/ml/data_visualizer/config.ts new file mode 100644 index 0000000000000..daad4e85a1f8b --- /dev/null +++ b/x-pack/test/functional/apps/ml/data_visualizer/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + junit: { + reportName: 'Chrome X-Pack UI Functional Tests - ML data_visualizer', + }, + }; +} diff --git a/x-pack/test/functional/apps/ml/data_visualizer/file_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/file_data_visualizer.ts index f5610e8b607da..5e529a3430606 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/file_data_visualizer.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/file_data_visualizer.ts @@ -5,8 +5,6 @@ * 2.0. */ -import path from 'path'; - import { ML_JOB_FIELD_TYPES } from '@kbn/ml-plugin/common/constants/field_types'; import { FtrProviderContext } from '../../../ftr_provider_context'; @@ -16,7 +14,7 @@ export default function ({ getService }: FtrProviderContext) { const testDataListPositive = [ { suiteSuffix: 'with an artificial server log', - filePath: path.join(__dirname, 'files_to_import', 'artificial_server_log'), + filePath: require.resolve('./files_to_import/artificial_server_log'), indexName: 'user-import_1', createIndexPattern: false, fieldTypeFilters: [ML_JOB_FIELD_TYPES.NUMBER, ML_JOB_FIELD_TYPES.DATE], @@ -116,7 +114,7 @@ export default function ({ getService }: FtrProviderContext) { }, { suiteSuffix: 'with a file containing geo field', - filePath: path.join(__dirname, 'files_to_import', 'geo_file.csv'), + filePath: require.resolve('./files_to_import/geo_file.csv'), indexName: 'user-import_2', createIndexPattern: false, fieldTypeFilters: [ML_JOB_FIELD_TYPES.GEO_POINT], @@ -158,7 +156,7 @@ export default function ({ getService }: FtrProviderContext) { }, { suiteSuffix: 'with a file with a missing new line char at the end', - filePath: path.join(__dirname, 'files_to_import', 'missing_end_of_file_newline.csv'), + filePath: require.resolve('./files_to_import/missing_end_of_file_newline.csv'), indexName: 'user-import_3', createIndexPattern: false, fieldTypeFilters: [], @@ -205,12 +203,12 @@ export default function ({ getService }: FtrProviderContext) { const testDataListNegative = [ { suiteSuffix: 'with a non-log file', - filePath: path.join(__dirname, 'files_to_import', 'not_a_log_file'), + filePath: require.resolve('./files_to_import/not_a_log_file'), }, ]; describe('file based', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await ml.testResources.setKibanaTimeZoneToUTC(); diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index.ts b/x-pack/test/functional/apps/ml/data_visualizer/index.ts index 3bb8e3d728318..a75fc8d0bf794 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index.ts @@ -7,9 +7,30 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; -export default function ({ loadTestFile }: FtrProviderContext) { - describe('data visualizer', function () { - this.tags(['skipFirefox', 'mlqa']); +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + + describe('machine learning - data visualizer', function () { + this.tags(['skipFirefox', 'ml']); + + before(async () => { + await ml.securityCommon.createMlRoles(); + await ml.securityCommon.createMlUsers(); + }); + + after(async () => { + // NOTE: Logout needs to happen before anything else to avoid flaky behavior + await ml.securityUI.logout(); + + await ml.securityCommon.cleanMlUsers(); + await ml.securityCommon.cleanMlRoles(); + + await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote'); + await esArchiver.unload('x-pack/test/functional/es_archives/ml/module_sample_logs'); + + await ml.testResources.resetKibanaTimeZone(); + }); loadTestFile(require.resolve('./index_data_visualizer')); loadTestFile(require.resolve('./index_data_visualizer_grid_in_discover')); diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts index 4334e72e9a16e..1f4c20ea6faa5 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts @@ -154,7 +154,7 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { } describe('index based', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/module_sample_logs'); diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_actions_panel.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_actions_panel.ts index b3f0e9e175d7a..c7e00f8ed5b54 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_actions_panel.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_actions_panel.ts @@ -12,7 +12,7 @@ export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); describe('index based actions panel on trial license', function () { - this.tags(['mlqa']); + this.tags(['ml']); const indexPatternName = 'ft_farequote'; diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_index_pattern_management.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_index_pattern_management.ts index 6ddf3bba3a81f..0017a71a086fe 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_index_pattern_management.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_index_pattern_management.ts @@ -173,7 +173,7 @@ export default function ({ getService }: FtrProviderContext) { } describe('data view management', function () { - this.tags(['mlqa']); + this.tags(['ml']); const indexPatternTitle = 'ft_farequote'; before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); diff --git a/x-pack/test/functional/apps/ml/embeddables/index.ts b/x-pack/test/functional/apps/ml/embeddables/index.ts deleted file mode 100644 index 31074a59866a6..0000000000000 --- a/x-pack/test/functional/apps/ml/embeddables/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ loadTestFile }: FtrProviderContext) { - describe('embeddables', function () { - this.tags(['skipFirefox']); - loadTestFile(require.resolve('./anomaly_charts_dashboard_embeddables')); - loadTestFile(require.resolve('./anomaly_embeddables_migration')); - }); -} diff --git a/x-pack/test/functional/apps/ml/feature_controls/index.ts b/x-pack/test/functional/apps/ml/feature_controls/index.ts deleted file mode 100644 index ffe419b506fd6..0000000000000 --- a/x-pack/test/functional/apps/ml/feature_controls/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ loadTestFile }: FtrProviderContext) { - describe('feature controls', function () { - this.tags(['skipFirefox', 'mlqa']); - loadTestFile(require.resolve('./ml_security')); - loadTestFile(require.resolve('./ml_spaces')); - }); -} diff --git a/x-pack/test/functional/apps/ml/index.ts b/x-pack/test/functional/apps/ml/index.ts deleted file mode 100644 index c58b20e1c374b..0000000000000 --- a/x-pack/test/functional/apps/ml/index.ts +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ getService, loadTestFile }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const ml = getService('ml'); - - describe('machine learning', function () { - before(async () => { - await ml.securityCommon.createMlRoles(); - await ml.securityCommon.createMlUsers(); - }); - - after(async () => { - // NOTE: Logout needs to happen before anything else to avoid flaky behavior - await ml.securityUI.logout(); - - await ml.securityCommon.cleanMlUsers(); - await ml.securityCommon.cleanMlRoles(); - - await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote'); - await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote_small'); - await esArchiver.unload('x-pack/test/functional/es_archives/ml/ecommerce'); - await esArchiver.unload('x-pack/test/functional/es_archives/ml/categorization_small'); - await esArchiver.unload('x-pack/test/functional/es_archives/ml/event_rate_nanos'); - await esArchiver.unload('x-pack/test/functional/es_archives/ml/bm_classification'); - await esArchiver.unload('x-pack/test/functional/es_archives/ml/ihp_outlier'); - await esArchiver.unload('x-pack/test/functional/es_archives/ml/egs_regression'); - await esArchiver.unload('x-pack/test/functional/es_archives/ml/module_sample_ecommerce'); - - await ml.testResources.resetKibanaTimeZone(); - }); - - describe('', function () { - this.tags('ciGroup15'); - loadTestFile(require.resolve('./permissions')); - loadTestFile(require.resolve('./pages')); - loadTestFile(require.resolve('./data_visualizer')); - loadTestFile(require.resolve('./data_frame_analytics')); - loadTestFile(require.resolve('./model_management')); - }); - - describe('', function () { - this.tags('ciGroup26'); - loadTestFile(require.resolve('./anomaly_detection')); - }); - - describe('', function () { - this.tags('ciGroup8'); - loadTestFile(require.resolve('./feature_controls')); - loadTestFile(require.resolve('./settings')); - loadTestFile(require.resolve('./embeddables')); - loadTestFile(require.resolve('./stack_management_jobs')); - }); - }); -} diff --git a/x-pack/test/functional/apps/ml/model_management/index.ts b/x-pack/test/functional/apps/ml/model_management/index.ts deleted file mode 100644 index e958392d9ba74..0000000000000 --- a/x-pack/test/functional/apps/ml/model_management/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ loadTestFile }: FtrProviderContext) { - describe('model management', function () { - this.tags(['mlqa', 'skipFirefox']); - - loadTestFile(require.resolve('./model_list')); - }); -} diff --git a/x-pack/test/functional/apps/ml/permissions/config.ts b/x-pack/test/functional/apps/ml/permissions/config.ts new file mode 100644 index 0000000000000..cc9fffd2c93f5 --- /dev/null +++ b/x-pack/test/functional/apps/ml/permissions/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + junit: { + reportName: 'Chrome X-Pack UI Functional Tests - ML permission', + }, + }; +} diff --git a/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts b/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts index 467bfc370e8e2..18a6e130daed0 100644 --- a/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts +++ b/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts @@ -5,8 +5,6 @@ * 2.0. */ -import path from 'path'; - import { FtrProviderContext } from '../../../ftr_provider_context'; import { USER } from '../../../services/ml/security_common'; @@ -22,7 +20,7 @@ export default function ({ getService }: FtrProviderContext) { ]; describe('for user with full ML access', function () { - this.tags(['skipFirefox', 'mlqa']); + this.tags(['skipFirefox', 'ml']); describe('with no data loaded', function () { for (const testUser of testUsers) { @@ -123,12 +121,8 @@ export default function ({ getService }: FtrProviderContext) { const ecIndexPattern = 'ft_module_sample_ecommerce'; const ecExpectedTotalCount = '287'; - const uploadFilePath = path.join( - __dirname, - '..', - 'data_visualizer', - 'files_to_import', - 'artificial_server_log' + const uploadFilePath = require.resolve( + '../data_visualizer/files_to_import/artificial_server_log' ); const expectedUploadFileTitle = 'artificial_server_log'; diff --git a/x-pack/test/functional/apps/ml/permissions/index.ts b/x-pack/test/functional/apps/ml/permissions/index.ts index e777f241eaf85..8b28c9e6ccda4 100644 --- a/x-pack/test/functional/apps/ml/permissions/index.ts +++ b/x-pack/test/functional/apps/ml/permissions/index.ts @@ -7,9 +7,31 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; -export default function ({ loadTestFile }: FtrProviderContext) { - describe('permissions', function () { - this.tags(['skipFirefox']); +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + + describe('machine learning - permissions', function () { + this.tags(['ml', 'skipFirefox']); + + before(async () => { + await ml.securityCommon.createMlRoles(); + await ml.securityCommon.createMlUsers(); + }); + + after(async () => { + // NOTE: Logout needs to happen before anything else to avoid flaky behavior + await ml.securityUI.logout(); + + await ml.securityCommon.cleanMlUsers(); + await ml.securityCommon.cleanMlRoles(); + + await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote'); + await esArchiver.unload('x-pack/test/functional/es_archives/ml/ihp_outlier'); + await esArchiver.unload('x-pack/test/functional/es_archives/ml/module_sample_ecommerce'); + + await ml.testResources.resetKibanaTimeZone(); + }); loadTestFile(require.resolve('./full_ml_access')); loadTestFile(require.resolve('./read_ml_access')); diff --git a/x-pack/test/functional/apps/ml/permissions/no_ml_access.ts b/x-pack/test/functional/apps/ml/permissions/no_ml_access.ts index 6132e6e63b1b0..1974a48e77841 100644 --- a/x-pack/test/functional/apps/ml/permissions/no_ml_access.ts +++ b/x-pack/test/functional/apps/ml/permissions/no_ml_access.ts @@ -16,7 +16,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const testUsers = [{ user: USER.ML_UNAUTHORIZED, discoverAvailable: true }]; describe('for user with no ML access', function () { - this.tags(['skipFirefox', 'mlqa']); + this.tags(['skipFirefox', 'ml']); for (const testUser of testUsers) { describe(`(${testUser.user})`, function () { diff --git a/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts b/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts index fd9cb2cb4c79e..301fc5102a94f 100644 --- a/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts +++ b/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts @@ -5,8 +5,6 @@ * 2.0. */ -import path from 'path'; - import { FtrProviderContext } from '../../../ftr_provider_context'; import { USER } from '../../../services/ml/security_common'; @@ -21,7 +19,7 @@ export default function ({ getService }: FtrProviderContext) { ]; describe('for user with read ML access', function () { - this.tags(['skipFirefox', 'mlqa']); + this.tags(['skipFirefox', 'ml']); describe('with no data loaded', function () { for (const testUser of testUsers) { @@ -117,12 +115,8 @@ export default function ({ getService }: FtrProviderContext) { const ecIndexPattern = 'ft_module_sample_ecommerce'; const ecExpectedTotalCount = '287'; - const uploadFilePath = path.join( - __dirname, - '..', - 'data_visualizer', - 'files_to_import', - 'artificial_server_log' + const uploadFilePath = require.resolve( + '../data_visualizer/files_to_import/artificial_server_log' ); const expectedUploadFileTitle = 'artificial_server_log'; diff --git a/x-pack/test/functional/apps/ml/settings/common.ts b/x-pack/test/functional/apps/ml/settings/common.ts deleted file mode 100644 index f161ae637e3ad..0000000000000 --- a/x-pack/test/functional/apps/ml/settings/common.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. - */ - -export async function asyncForEach(array: T[], callback: (item: T, index: number) => void) { - for (let index = 0; index < array.length; index++) { - await callback(array[index], index); - } -} - -export const createJobConfig = (jobId: string) => ({ - job_id: jobId, - description: - 'mean/min/max(responsetime) partition=airline on farequote dataset with 1h bucket span', - groups: ['farequote', 'automated', 'multi-metric'], - analysis_config: { - bucket_span: '1h', - influencers: ['airline'], - detectors: [{ function: 'mean', field_name: 'responsetime', partition_field_name: 'airline' }], - }, - data_description: { time_field: '@timestamp' }, - analysis_limits: { model_memory_limit: '20mb' }, - model_plot_config: { enabled: false }, -}); diff --git a/x-pack/test/functional/apps/ml/settings/index.ts b/x-pack/test/functional/apps/ml/settings/index.ts deleted file mode 100644 index e904eaedb8db0..0000000000000 --- a/x-pack/test/functional/apps/ml/settings/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ loadTestFile }: FtrProviderContext) { - describe('settings', function () { - this.tags(['mlqa', 'skipFirefox']); - - loadTestFile(require.resolve('./calendar_creation')); - loadTestFile(require.resolve('./calendar_edit')); - loadTestFile(require.resolve('./calendar_delete')); - - loadTestFile(require.resolve('./filter_list_creation')); - loadTestFile(require.resolve('./filter_list_edit')); - loadTestFile(require.resolve('./filter_list_delete')); - }); -} diff --git a/x-pack/test/functional/apps/ml/short_tests/config.ts b/x-pack/test/functional/apps/ml/short_tests/config.ts new file mode 100644 index 0000000000000..33d37ecd71457 --- /dev/null +++ b/x-pack/test/functional/apps/ml/short_tests/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + junit: { + reportName: 'Chrome X-Pack UI Functional Tests - ML short_tests', + }, + }; +} diff --git a/x-pack/test/functional/apps/ml/embeddables/anomaly_charts_dashboard_embeddables.ts b/x-pack/test/functional/apps/ml/short_tests/embeddables/anomaly_charts_dashboard_embeddables.ts similarity index 97% rename from x-pack/test/functional/apps/ml/embeddables/anomaly_charts_dashboard_embeddables.ts rename to x-pack/test/functional/apps/ml/short_tests/embeddables/anomaly_charts_dashboard_embeddables.ts index 89b733c21498f..ef674c1744a51 100644 --- a/x-pack/test/functional/apps/ml/embeddables/anomaly_charts_dashboard_embeddables.ts +++ b/x-pack/test/functional/apps/ml/short_tests/embeddables/anomaly_charts_dashboard_embeddables.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; import { JOB_CONFIG, DATAFEED_CONFIG, ML_EMBEDDABLE_TYPES } from './constants'; const testDataList = [ @@ -34,7 +34,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'timePicker', 'dashboard']); describe('anomaly charts in dashboard', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); diff --git a/x-pack/test/functional/apps/ml/embeddables/anomaly_embeddables_migration.ts b/x-pack/test/functional/apps/ml/short_tests/embeddables/anomaly_embeddables_migration.ts similarity index 98% rename from x-pack/test/functional/apps/ml/embeddables/anomaly_embeddables_migration.ts rename to x-pack/test/functional/apps/ml/short_tests/embeddables/anomaly_embeddables_migration.ts index ed38ff7021a92..8f3c30a15e543 100644 --- a/x-pack/test/functional/apps/ml/embeddables/anomaly_embeddables_migration.ts +++ b/x-pack/test/functional/apps/ml/short_tests/embeddables/anomaly_embeddables_migration.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; import { JOB_CONFIG, DATAFEED_CONFIG, ML_EMBEDDABLE_TYPES } from './constants'; const testDataList = [ @@ -66,7 +66,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'timePicker', 'dashboard']); describe('anomaly embeddables migration in Dashboard', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); diff --git a/x-pack/test/functional/apps/ml/embeddables/constants.ts b/x-pack/test/functional/apps/ml/short_tests/embeddables/constants.ts similarity index 100% rename from x-pack/test/functional/apps/ml/embeddables/constants.ts rename to x-pack/test/functional/apps/ml/short_tests/embeddables/constants.ts diff --git a/x-pack/test/functional/apps/ml/short_tests/embeddables/index.ts b/x-pack/test/functional/apps/ml/short_tests/embeddables/index.ts new file mode 100644 index 0000000000000..d786491e55a4e --- /dev/null +++ b/x-pack/test/functional/apps/ml/short_tests/embeddables/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 { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('embeddables', function () { + this.tags(['skipFirefox']); + loadTestFile(require.resolve('./anomaly_charts_dashboard_embeddables')); + loadTestFile(require.resolve('./anomaly_embeddables_migration')); + }); +} diff --git a/x-pack/test/functional/apps/ml/short_tests/feature_controls/index.ts b/x-pack/test/functional/apps/ml/short_tests/feature_controls/index.ts new file mode 100644 index 0000000000000..657eb86e20c19 --- /dev/null +++ b/x-pack/test/functional/apps/ml/short_tests/feature_controls/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 { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('feature controls', function () { + this.tags(['skipFirefox', 'ml']); + loadTestFile(require.resolve('./ml_security')); + loadTestFile(require.resolve('./ml_spaces')); + }); +} diff --git a/x-pack/test/functional/apps/ml/feature_controls/ml_security.ts b/x-pack/test/functional/apps/ml/short_tests/feature_controls/ml_security.ts similarity index 98% rename from x-pack/test/functional/apps/ml/feature_controls/ml_security.ts rename to x-pack/test/functional/apps/ml/short_tests/feature_controls/ml_security.ts index 58af3abbf6a47..fd498f00a8262 100644 --- a/x-pack/test/functional/apps/ml/feature_controls/ml_security.ts +++ b/x-pack/test/functional/apps/ml/short_tests/feature_controls/ml_security.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const security = getService('security'); diff --git a/x-pack/test/functional/apps/ml/feature_controls/ml_spaces.ts b/x-pack/test/functional/apps/ml/short_tests/feature_controls/ml_spaces.ts similarity index 97% rename from x-pack/test/functional/apps/ml/feature_controls/ml_spaces.ts rename to x-pack/test/functional/apps/ml/short_tests/feature_controls/ml_spaces.ts index 1ea0e1e717e5f..0352a0059ba55 100644 --- a/x-pack/test/functional/apps/ml/feature_controls/ml_spaces.ts +++ b/x-pack/test/functional/apps/ml/short_tests/feature_controls/ml_spaces.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const spacesService = getService('spaces'); diff --git a/x-pack/test/functional/apps/ml/short_tests/index.ts b/x-pack/test/functional/apps/ml/short_tests/index.ts new file mode 100644 index 0000000000000..3c4cbbc0677be --- /dev/null +++ b/x-pack/test/functional/apps/ml/short_tests/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + + describe('machine learning - short tests', function () { + before(async () => { + await ml.securityCommon.createMlRoles(); + await ml.securityCommon.createMlUsers(); + }); + + after(async () => { + // NOTE: Logout needs to happen before anything else to avoid flaky behavior + await ml.securityUI.logout(); + + await ml.securityCommon.cleanMlUsers(); + await ml.securityCommon.cleanMlRoles(); + + await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote'); + + await ml.testResources.resetKibanaTimeZone(); + }); + + loadTestFile(require.resolve('./pages')); + loadTestFile(require.resolve('./model_management')); + loadTestFile(require.resolve('./feature_controls')); + loadTestFile(require.resolve('./settings')); + loadTestFile(require.resolve('./embeddables')); + }); +} diff --git a/x-pack/test/functional/apps/ml/short_tests/model_management/index.ts b/x-pack/test/functional/apps/ml/short_tests/model_management/index.ts new file mode 100644 index 0000000000000..c20957beb1ea5 --- /dev/null +++ b/x-pack/test/functional/apps/ml/short_tests/model_management/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 { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('model management', function () { + this.tags(['ml', 'skipFirefox']); + + loadTestFile(require.resolve('./model_list')); + }); +} diff --git a/x-pack/test/functional/apps/ml/model_management/model_list.ts b/x-pack/test/functional/apps/ml/short_tests/model_management/model_list.ts similarity index 97% rename from x-pack/test/functional/apps/ml/model_management/model_list.ts rename to x-pack/test/functional/apps/ml/short_tests/model_management/model_list.ts index 811ae280e0780..ca360130b89f9 100644 --- a/x-pack/test/functional/apps/ml/model_management/model_list.ts +++ b/x-pack/test/functional/apps/ml/short_tests/model_management/model_list.ts @@ -5,13 +5,12 @@ * 2.0. */ -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); - // Failing: See https://github.com/elastic/kibana/issues/125455 - describe.skip('trained models', function () { + describe('trained models', function () { before(async () => { await ml.trainedModels.createTestTrainedModels('classification', 15, true); await ml.trainedModels.createTestTrainedModels('regression', 15); diff --git a/x-pack/test/functional/apps/ml/pages.ts b/x-pack/test/functional/apps/ml/short_tests/pages.ts similarity index 93% rename from x-pack/test/functional/apps/ml/pages.ts rename to x-pack/test/functional/apps/ml/short_tests/pages.ts index 6c8687d213cee..d81b5933d77df 100644 --- a/x-pack/test/functional/apps/ml/pages.ts +++ b/x-pack/test/functional/apps/ml/short_tests/pages.ts @@ -5,13 +5,13 @@ * 2.0. */ -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); describe('page navigation', function () { - this.tags(['skipFirefox', 'mlqa']); + this.tags(['skipFirefox', 'ml']); before(async () => { await ml.api.cleanMlIndices(); await ml.securityUI.loginAsMlPowerUser(); @@ -21,7 +21,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('loads the ML home page'); await ml.navigation.navigateToMl(); - await ml.testExecution.logTestStep('loads the overview page'); + await ml.testExecution.logTestStep('loads the overview page'); await ml.navigation.navigateToOverview(); await ml.testExecution.logTestStep('loads the anomaly detection area'); diff --git a/x-pack/test/functional/apps/ml/settings/calendar_creation.ts b/x-pack/test/functional/apps/ml/short_tests/settings/calendar_creation.ts similarity index 98% rename from x-pack/test/functional/apps/ml/settings/calendar_creation.ts rename to x-pack/test/functional/apps/ml/short_tests/settings/calendar_creation.ts index f0f5cd71cafe7..91121528d477a 100644 --- a/x-pack/test/functional/apps/ml/settings/calendar_creation.ts +++ b/x-pack/test/functional/apps/ml/short_tests/settings/calendar_creation.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; import { asyncForEach, createJobConfig } from './common'; export default function ({ getService }: FtrProviderContext) { diff --git a/x-pack/test/functional/apps/ml/settings/calendar_delete.ts b/x-pack/test/functional/apps/ml/short_tests/settings/calendar_delete.ts similarity index 96% rename from x-pack/test/functional/apps/ml/settings/calendar_delete.ts rename to x-pack/test/functional/apps/ml/short_tests/settings/calendar_delete.ts index ff417a32c1d7c..28b526147c96e 100644 --- a/x-pack/test/functional/apps/ml/settings/calendar_delete.ts +++ b/x-pack/test/functional/apps/ml/short_tests/settings/calendar_delete.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; import { asyncForEach } from './common'; export default function ({ getService }: FtrProviderContext) { diff --git a/x-pack/test/functional/apps/ml/settings/calendar_edit.ts b/x-pack/test/functional/apps/ml/short_tests/settings/calendar_edit.ts similarity index 98% rename from x-pack/test/functional/apps/ml/settings/calendar_edit.ts rename to x-pack/test/functional/apps/ml/short_tests/settings/calendar_edit.ts index 70c3e50ec309b..9f68ccc8a1196 100644 --- a/x-pack/test/functional/apps/ml/settings/calendar_edit.ts +++ b/x-pack/test/functional/apps/ml/short_tests/settings/calendar_edit.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; import { asyncForEach, createJobConfig } from './common'; export default function ({ getService }: FtrProviderContext) { diff --git a/x-pack/test/functional/apps/ml/short_tests/settings/common.ts b/x-pack/test/functional/apps/ml/short_tests/settings/common.ts new file mode 100644 index 0000000000000..77d798ecf241b --- /dev/null +++ b/x-pack/test/functional/apps/ml/short_tests/settings/common.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export async function asyncForEach( + array: T[], + callback: (item: T, index: number) => Promise +) { + for (let index = 0; index < array.length; index++) { + await callback(array[index], index); + } +} + +export const createJobConfig = (jobId: string) => ({ + job_id: jobId, + description: + 'mean/min/max(responsetime) partition=airline on farequote dataset with 1h bucket span', + groups: ['farequote', 'automated', 'multi-metric'], + analysis_config: { + bucket_span: '1h', + influencers: ['airline'], + detectors: [{ function: 'mean', field_name: 'responsetime', partition_field_name: 'airline' }], + }, + data_description: { time_field: '@timestamp' }, + analysis_limits: { model_memory_limit: '20mb' }, + model_plot_config: { enabled: false }, +}); diff --git a/x-pack/test/functional/apps/ml/settings/filter_list_creation.ts b/x-pack/test/functional/apps/ml/short_tests/settings/filter_list_creation.ts similarity index 96% rename from x-pack/test/functional/apps/ml/settings/filter_list_creation.ts rename to x-pack/test/functional/apps/ml/short_tests/settings/filter_list_creation.ts index 3938b73a131f4..38ee8a3e6e4c2 100644 --- a/x-pack/test/functional/apps/ml/settings/filter_list_creation.ts +++ b/x-pack/test/functional/apps/ml/short_tests/settings/filter_list_creation.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); diff --git a/x-pack/test/functional/apps/ml/settings/filter_list_delete.ts b/x-pack/test/functional/apps/ml/short_tests/settings/filter_list_delete.ts similarity index 96% rename from x-pack/test/functional/apps/ml/settings/filter_list_delete.ts rename to x-pack/test/functional/apps/ml/short_tests/settings/filter_list_delete.ts index aad56ffe55606..cdbf26ea12a03 100644 --- a/x-pack/test/functional/apps/ml/settings/filter_list_delete.ts +++ b/x-pack/test/functional/apps/ml/short_tests/settings/filter_list_delete.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; import { asyncForEach } from './common'; export default function ({ getService }: FtrProviderContext) { diff --git a/x-pack/test/functional/apps/ml/settings/filter_list_edit.ts b/x-pack/test/functional/apps/ml/short_tests/settings/filter_list_edit.ts similarity index 97% rename from x-pack/test/functional/apps/ml/settings/filter_list_edit.ts rename to x-pack/test/functional/apps/ml/short_tests/settings/filter_list_edit.ts index 7acd1b2bbc123..0a4c4f63ee741 100644 --- a/x-pack/test/functional/apps/ml/settings/filter_list_edit.ts +++ b/x-pack/test/functional/apps/ml/short_tests/settings/filter_list_edit.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../../../ftr_provider_context'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; import { asyncForEach } from './common'; export default function ({ getService }: FtrProviderContext) { diff --git a/x-pack/test/functional/apps/ml/short_tests/settings/index.ts b/x-pack/test/functional/apps/ml/short_tests/settings/index.ts new file mode 100644 index 0000000000000..d3f7000918a8e --- /dev/null +++ b/x-pack/test/functional/apps/ml/short_tests/settings/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('settings', function () { + this.tags(['ml', 'skipFirefox']); + + loadTestFile(require.resolve('./calendar_creation')); + loadTestFile(require.resolve('./calendar_edit')); + loadTestFile(require.resolve('./calendar_delete')); + + loadTestFile(require.resolve('./filter_list_creation')); + loadTestFile(require.resolve('./filter_list_edit')); + loadTestFile(require.resolve('./filter_list_delete')); + }); +} diff --git a/x-pack/test/functional/apps/ml/stack_management_jobs/config.ts b/x-pack/test/functional/apps/ml/stack_management_jobs/config.ts new file mode 100644 index 0000000000000..9d0fe82b9158c --- /dev/null +++ b/x-pack/test/functional/apps/ml/stack_management_jobs/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + junit: { + reportName: 'Chrome X-Pack UI Functional Tests - ML stack_management_jobs', + }, + }; +} diff --git a/x-pack/test/functional/apps/ml/stack_management_jobs/export_jobs.ts b/x-pack/test/functional/apps/ml/stack_management_jobs/export_jobs.ts index 85e249861378f..c43cf74e3048c 100644 --- a/x-pack/test/functional/apps/ml/stack_management_jobs/export_jobs.ts +++ b/x-pack/test/functional/apps/ml/stack_management_jobs/export_jobs.ts @@ -255,7 +255,7 @@ export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); describe('export jobs', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await ml.api.cleanMlIndices(); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); diff --git a/x-pack/test/functional/apps/ml/stack_management_jobs/import_jobs.ts b/x-pack/test/functional/apps/ml/stack_management_jobs/import_jobs.ts index ef367797ef7e9..e2ba704f5e109 100644 --- a/x-pack/test/functional/apps/ml/stack_management_jobs/import_jobs.ts +++ b/x-pack/test/functional/apps/ml/stack_management_jobs/import_jobs.ts @@ -5,8 +5,6 @@ * 2.0. */ -import path from 'path'; - import { JobType } from '@kbn/ml-plugin/common/types/saved_objects'; import { FtrProviderContext } from '../../../ftr_provider_context'; @@ -15,7 +13,7 @@ export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); const testDataListPositive = [ { - filePath: path.join(__dirname, 'files_to_import', 'anomaly_detection_jobs_7.16.json'), + filePath: require.resolve('./files_to_import/anomaly_detection_jobs_7.16.json'), expected: { jobType: 'anomaly-detector' as JobType, jobIds: ['ad-test1', 'ad-test3'], @@ -23,7 +21,7 @@ export default function ({ getService }: FtrProviderContext) { }, }, { - filePath: path.join(__dirname, 'files_to_import', 'data_frame_analytics_jobs_7.16.json'), + filePath: require.resolve('./files_to_import/data_frame_analytics_jobs_7.16.json'), expected: { jobType: 'data-frame-analytics' as JobType, jobIds: ['dfa-test1'], @@ -33,7 +31,7 @@ export default function ({ getService }: FtrProviderContext) { ]; describe('import jobs', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await ml.api.cleanMlIndices(); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); @@ -107,7 +105,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('selects job import'); await ml.stackManagementJobs.openImportFlyout(); await ml.stackManagementJobs.selectFileToImport( - path.join(__dirname, 'files_to_import', 'bad_data.json'), + require.resolve('./files_to_import/bad_data.json'), true ); }); diff --git a/x-pack/test/functional/apps/ml/stack_management_jobs/index.ts b/x-pack/test/functional/apps/ml/stack_management_jobs/index.ts index c5e0728266bab..37f238dbeecc9 100644 --- a/x-pack/test/functional/apps/ml/stack_management_jobs/index.ts +++ b/x-pack/test/functional/apps/ml/stack_management_jobs/index.ts @@ -7,9 +7,31 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; -export default function ({ loadTestFile }: FtrProviderContext) { - describe('stack management jobs', function () { - this.tags(['mlqa', 'skipFirefox']); +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + + describe('machine learning - stack management jobs', function () { + this.tags(['ml', 'skipFirefox']); + before(async () => { + await ml.securityCommon.createMlRoles(); + await ml.securityCommon.createMlUsers(); + }); + + after(async () => { + // NOTE: Logout needs to happen before anything else to avoid flaky behavior + await ml.securityUI.logout(); + + await ml.securityCommon.cleanMlUsers(); + await ml.securityCommon.cleanMlRoles(); + + await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote'); + await esArchiver.unload('x-pack/test/functional/es_archives/ml/bm_classification'); + await esArchiver.unload('x-pack/test/functional/es_archives/ml/ihp_outlier'); + await esArchiver.unload('x-pack/test/functional/es_archives/ml/egs_regression'); + + await ml.testResources.resetKibanaTimeZone(); + }); loadTestFile(require.resolve('./synchronize')); loadTestFile(require.resolve('./manage_spaces')); diff --git a/x-pack/test/functional/apps/ml/stack_management_jobs/manage_spaces.ts b/x-pack/test/functional/apps/ml/stack_management_jobs/manage_spaces.ts index 4841fca9d74f7..e68502f4dab5a 100644 --- a/x-pack/test/functional/apps/ml/stack_management_jobs/manage_spaces.ts +++ b/x-pack/test/functional/apps/ml/stack_management_jobs/manage_spaces.ts @@ -107,7 +107,7 @@ export default function ({ getService }: FtrProviderContext) { } describe('manage spaces', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ihp_outlier'); diff --git a/x-pack/test/functional/apps/ml/stack_management_jobs/synchronize.ts b/x-pack/test/functional/apps/ml/stack_management_jobs/synchronize.ts index cf22f29bc277f..317a71ae79a0b 100644 --- a/x-pack/test/functional/apps/ml/stack_management_jobs/synchronize.ts +++ b/x-pack/test/functional/apps/ml/stack_management_jobs/synchronize.ts @@ -20,7 +20,7 @@ export default function ({ getService }: FtrProviderContext) { const dfaJobIdES = 'ihp_od_es'; describe('synchronize', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ihp_outlier'); diff --git a/x-pack/test/functional/apps/monitoring/config.ts b/x-pack/test/functional/apps/monitoring/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/monitoring/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/monitoring/index.js b/x-pack/test/functional/apps/monitoring/index.js index b8f6f223092f6..cfa29d2f784e6 100644 --- a/x-pack/test/functional/apps/monitoring/index.js +++ b/x-pack/test/functional/apps/monitoring/index.js @@ -7,7 +7,6 @@ export default function ({ loadTestFile }) { describe('Monitoring app', function () { - this.tags('ciGroup28'); loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./cluster/list')); diff --git a/x-pack/test/functional/apps/remote_clusters/config.ts b/x-pack/test/functional/apps/remote_clusters/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/remote_clusters/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/remote_clusters/index.ts b/x-pack/test/functional/apps/remote_clusters/index.ts index a1cc660b6426a..74c4ce6e68bfc 100644 --- a/x-pack/test/functional/apps/remote_clusters/index.ts +++ b/x-pack/test/functional/apps/remote_clusters/index.ts @@ -12,7 +12,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; // https://www.elastic.co/guide/en/kibana/7.9/working-remote-clusters.html export default ({ loadTestFile }: FtrProviderContext) => { describe('Remote Clusters app', function () { - this.tags(['ciGroup4', 'skipCloud']); + this.tags('skipCloud'); loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./home_page')); }); diff --git a/x-pack/test/functional/apps/reporting/README.md b/x-pack/test/functional/apps/reporting/README.md index ec9bba8b88341..5d47804fef284 100644 --- a/x-pack/test/functional/apps/reporting/README.md +++ b/x-pack/test/functional/apps/reporting/README.md @@ -5,7 +5,7 @@ Functional tests on report generation are under the applications that use report **PDF/PNG Report testing:** - `x-pack/test/functional/apps/canvas/reports.ts` - `x-pack/test/functional/apps/dashboard/reporting/screenshots.ts` - - `x-pack/test/functional/apps/lens/lens_reporting.ts` + - `x-pack/test/functional/apps/lens/group3/lens_reporting.ts` - `x-pack/test/functional/apps/visualize/reporting.ts` **CSV Report testing:** diff --git a/x-pack/test/functional/apps/reporting_management/config.ts b/x-pack/test/functional/apps/reporting_management/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/reporting_management/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/reporting_management/index.js b/x-pack/test/functional/apps/reporting_management/index.js index 4e6abe85a4041..dcf5583eeb92a 100644 --- a/x-pack/test/functional/apps/reporting_management/index.js +++ b/x-pack/test/functional/apps/reporting_management/index.js @@ -7,7 +7,6 @@ export default ({ loadTestFile }) => { describe('reporting management app', function () { - this.tags('ciGroup7'); loadTestFile(require.resolve('./report_listing')); }); }; diff --git a/x-pack/test/functional/apps/rollup_job/config.ts b/x-pack/test/functional/apps/rollup_job/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/rollup_job/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/rollup_job/index.js b/x-pack/test/functional/apps/rollup_job/index.js index 8fa9cd6f7aa72..943536539c5ad 100644 --- a/x-pack/test/functional/apps/rollup_job/index.js +++ b/x-pack/test/functional/apps/rollup_job/index.js @@ -7,8 +7,6 @@ export default function ({ loadTestFile }) { describe('rollup app', function () { - this.tags('ciGroup28'); - loadTestFile(require.resolve('./rollup_jobs')); loadTestFile(require.resolve('./hybrid_index_pattern')); loadTestFile(require.resolve('./tsvb')); diff --git a/x-pack/test/functional/apps/saved_objects_management/config.ts b/x-pack/test/functional/apps/saved_objects_management/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/saved_objects_management/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/saved_objects_management/exports/_7.13_import_saved_objects.ndjson b/x-pack/test/functional/apps/saved_objects_management/exports/_7.13_import_saved_objects.ndjson index 21be31349af0f..8f2aa535f305c 100644 --- a/x-pack/test/functional/apps/saved_objects_management/exports/_7.13_import_saved_objects.ndjson +++ b/x-pack/test/functional/apps/saved_objects_management/exports/_7.13_import_saved_objects.ndjson @@ -1,87 +1,83 @@ -{"attributes":{"accessCount":0,"accessDate":1621977234367,"createDate":1621977234367,"url":"/app/dashboards#/view/154944b0-6249-11eb-aebf-c306684b328d?embed=true&_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15y,to:now))&_a=(description:%27%27,filters:!(),fullScreenMode:!f,options:(darkTheme:!f,hidePanelTitles:!f,useMargins:!t),panels:!((embeddableConfig:(enhancements:()),gridData:(h:15,i:%271%27,w:24,x:0,y:0),id:%2736b91810-6239-11eb-aebf-c306684b328d%27,panelIndex:%271%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:(),vis:!n),gridData:(h:15,i:%272%27,w:24,x:24,y:0),id:%270a274320-61cc-11eb-aebf-c306684b328d%27,panelIndex:%272%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%273%27,w:24,x:0,y:15),id:e4aef350-623d-11eb-aebf-c306684b328d,panelIndex:%273%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%274%27,w:24,x:24,y:15),id:f92e5630-623e-11eb-aebf-c306684b328d,panelIndex:%274%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:(),vis:!n),gridData:(h:15,i:%275%27,w:24,x:0,y:30),id:%279853d4d0-623d-11eb-aebf-c306684b328d%27,panelIndex:%275%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:(),vis:!n),gridData:(h:15,i:%276%27,w:24,x:24,y:30),id:%276ecb33b0-623d-11eb-aebf-c306684b328d%27,panelIndex:%276%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:(),vis:!n),gridData:(h:15,i:%277%27,w:24,x:0,y:45),id:b8e35c80-623c-11eb-aebf-c306684b328d,panelIndex:%277%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%278%27,w:24,x:24,y:45),id:f1bc75d0-6239-11eb-aebf-c306684b328d,panelIndex:%278%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%279%27,w:24,x:0,y:60),id:%270d8a8860-623a-11eb-aebf-c306684b328d%27,panelIndex:%279%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2710%27,w:24,x:24,y:60),id:d79fe3d0-6239-11eb-aebf-c306684b328d,panelIndex:%2710%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2711%27,w:24,x:0,y:75),id:%27318375a0-6240-11eb-aebf-c306684b328d%27,panelIndex:%2711%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2712%27,w:24,x:24,y:75),id:e461eb20-6245-11eb-aebf-c306684b328d,panelIndex:%2712%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2713%27,w:24,x:0,y:90),id:%2725bdc750-6242-11eb-aebf-c306684b328d%27,panelIndex:%2713%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2714%27,w:24,x:24,y:90),id:%2771dd7bc0-6248-11eb-aebf-c306684b328d%27,panelIndex:%2714%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2715%27,w:24,x:0,y:105),id:%276aea48a0-6240-11eb-aebf-c306684b328d%27,panelIndex:%2715%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2716%27,w:24,x:24,y:105),id:%2732b681f0-6241-11eb-aebf-c306684b328d%27,panelIndex:%2716%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2717%27,w:24,x:0,y:120),id:ccca99e0-6244-11eb-aebf-c306684b328d,panelIndex:%2717%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2718%27,w:24,x:24,y:120),id:a4d7be80-6245-11eb-aebf-c306684b328d,panelIndex:%2718%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2719%27,w:24,x:0,y:135),id:c94d8440-6248-11eb-aebf-c306684b328d,panelIndex:%2719%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2720%27,w:24,x:24,y:135),id:db6226f0-61c0-11eb-aebf-c306684b328d,panelIndex:%2720%27,type:search,version:%277.13.1%27)),query:(language:lucene,query:%27%27),tags:!(),timeRestore:!f,title:logstash_dashboardwithtime,viewMode:view)"},"coreMigrationVersion":"7.13.2","id":"058bc10f0578013fc41ddedc9a1dcd1e","references":[],"sort":[1623693556928,448],"type":"url","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NDUsNF0="} -{"attributes":{"color":"#ba898f","description":"","name":"By value tag"},"coreMigrationVersion":"7.13.2","id":"07f48f70-ca29-11eb-bf5e-3de94e83d4f0","references":[],"sort":[1623415891791,116],"type":"tag","updated_at":"2021-06-11T12:51:31.791Z","version":"WzE0MzEsNF0="} -{"attributes":{"fieldAttrs":"{\"ip\":{\"count\":2},\"geo.dest\":{\"count\":1}}","fields":"[]","runtimeFieldMap":"{}","timeFieldName":"@timestamp","title":"logstash-*"},"coreMigrationVersion":"7.13.2","id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","migrationVersion":{"index-pattern":"7.11.0"},"references":[],"sort":[1623415891791,120],"type":"index-pattern","updated_at":"2021-06-11T12:51:31.791Z","version":"WzE0MzMsNF0="} -{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"60aaea59-d871-4e90-9ff3-78946d6bef90\"},\"panelIndex\":\"60aaea59-d871-4e90-9ff3-78946d6bef90\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsPie\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"2174c5fe-75d2-43ae-9c79-1a7cc7bbbaea\":{\"columns\":{\"65625f0d-e7f1-4370-b939-7db27af74de7\":{\"label\":\"Top values of geo.srcdest\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"geo.srcdest\",\"isBucketed\":true,\"params\":{\"size\":18,\"orderBy\":{\"type\":\"column\",\"columnId\":\"553a353f-dac5-4a52-a25d-52c6e1462597\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"553a353f-dac5-4a52-a25d-52c6e1462597\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"Records\"}},\"columnOrder\":[\"65625f0d-e7f1-4370-b939-7db27af74de7\",\"553a353f-dac5-4a52-a25d-52c6e1462597\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"shape\":\"donut\",\"layers\":[{\"layerId\":\"2174c5fe-75d2-43ae-9c79-1a7cc7bbbaea\",\"groups\":[\"65625f0d-e7f1-4370-b939-7db27af74de7\",\"65625f0d-e7f1-4370-b939-7db27af74de7\",\"65625f0d-e7f1-4370-b939-7db27af74de7\"],\"metric\":\"553a353f-dac5-4a52-a25d-52c6e1462597\",\"numberDisplay\":\"percent\",\"categoryDisplay\":\"default\",\"legendDisplay\":\"default\",\"nestedLegend\":false}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"43fcac20-ca27-11eb-bf5e-3de94e83d4f0\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"43fcac20-ca27-11eb-bf5e-3de94e83d4f0\",\"name\":\"indexpattern-datasource-layer-2174c5fe-75d2-43ae-9c79-1a7cc7bbbaea\"}]},\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"9ed45b8b-534b-4fac-9fee-436896b90039\",\"triggers\":[\"FILTER_TRIGGER\"],\"action\":{\"name\":\"circular drilldown\",\"config\":{\"useCurrentFilters\":true,\"useCurrentDateRange\":true},\"factoryId\":\"DASHBOARD_TO_DASHBOARD_DRILLDOWN\"}}]}},\"type\":\"lens\"}}]","timeRestore":false,"title":"lens_panel_drilldown","version":1},"coreMigrationVersion":"7.13.2","id":"35ce3b30-ca29-11eb-bf5e-3de94e83d4f0","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"indexpattern-datasource-layer-2174c5fe-75d2-43ae-9c79-1a7cc7bbbaea","type":"index-pattern"},{"id":"08dec860-ca29-11eb-bf5e-3de94e83d4f0","name":"drilldown:DASHBOARD_TO_DASHBOARD_DRILLDOWN:9ed45b8b-534b-4fac-9fee-436896b90039:dashboardId","type":"dashboard"},{"id":"07f48f70-ca29-11eb-bf5e-3de94e83d4f0","name":"tag-07f48f70-ca29-11eb-bf5e-3de94e83d4f0","type":"tag"}],"sort":[1623415891791,125],"type":"dashboard","updated_at":"2021-06-11T12:51:31.791Z","version":"WzE0MzQsNF0="} -{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"095e13b2-d0ac-47db-a62b-0aca28931402\"},\"panelIndex\":\"095e13b2-d0ac-47db-a62b-0aca28931402\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsXY\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"f61694eb-94ed-495d-9ce8-63592f040b0b\":{\"columns\":{\"75ddcdb4-3050-4545-b401-509384b0d532\":{\"label\":\"Top values of machine.os.raw\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"machine.os.raw\",\"isBucketed\":true,\"params\":{\"size\":3,\"orderBy\":{\"type\":\"column\",\"columnId\":\"2f97b0f5-f0ff-40f2-abb0-bb7d1081a126\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"273e31ef-7c2d-4d0e-9063-5528f4011a51\":{\"label\":\"@timestamp\",\"dataType\":\"date\",\"operationType\":\"date_histogram\",\"sourceField\":\"@timestamp\",\"isBucketed\":true,\"scale\":\"interval\",\"params\":{\"interval\":\"auto\"}},\"2f97b0f5-f0ff-40f2-abb0-bb7d1081a126\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"Records\"},\"2eb30654-0ead-40ac-92ab-d8d113e25ac5\":{\"label\":\"Average of bytes\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"bytes\",\"isBucketed\":false,\"scale\":\"ratio\"}},\"columnOrder\":[\"75ddcdb4-3050-4545-b401-509384b0d532\",\"273e31ef-7c2d-4d0e-9063-5528f4011a51\",\"2f97b0f5-f0ff-40f2-abb0-bb7d1081a126\",\"2eb30654-0ead-40ac-92ab-d8d113e25ac5\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"legend\":{\"isVisible\":true,\"position\":\"right\"},\"valueLabels\":\"hide\",\"fittingFunction\":\"None\",\"axisTitlesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"bar_stacked\",\"layers\":[{\"layerId\":\"f61694eb-94ed-495d-9ce8-63592f040b0b\",\"accessors\":[\"2f97b0f5-f0ff-40f2-abb0-bb7d1081a126\",\"2eb30654-0ead-40ac-92ab-d8d113e25ac5\"],\"position\":\"top\",\"seriesType\":\"bar_stacked\",\"showGridlines\":false,\"xAccessor\":\"273e31ef-7c2d-4d0e-9063-5528f4011a51\",\"splitAccessor\":\"75ddcdb4-3050-4545-b401-509384b0d532\"}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"43fcac20-ca27-11eb-bf5e-3de94e83d4f0\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"43fcac20-ca27-11eb-bf5e-3de94e83d4f0\",\"name\":\"indexpattern-datasource-layer-f61694eb-94ed-495d-9ce8-63592f040b0b\"}]},\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"da7ad2b6-1e4a-40b5-9123-d0ec2bde858d\",\"triggers\":[\"FILTER_TRIGGER\"],\"action\":{\"name\":\"lens_panel_drilldown\",\"config\":{\"useCurrentFilters\":true,\"useCurrentDateRange\":true},\"factoryId\":\"DASHBOARD_TO_DASHBOARD_DRILLDOWN\"}}]}},\"type\":\"lens\"}},{\"version\":\"7.13.1\",\"type\":\"map\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"153bb2dc-c4f5-4fcc-a45a-4e61cdaad8c2\"},\"panelIndex\":\"153bb2dc-c4f5-4fcc-a45a-4e61cdaad8c2\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"description\":\"\",\"layerListJSON\":\"[{\\\"sourceDescriptor\\\":{\\\"type\\\":\\\"EMS_TMS\\\",\\\"isAutoSelect\\\":true},\\\"id\\\":\\\"16b378f6-4b68-4d17-8be4-3da333440869\\\",\\\"label\\\":null,\\\"minZoom\\\":0,\\\"maxZoom\\\":24,\\\"alpha\\\":1,\\\"visible\\\":true,\\\"style\\\":{\\\"type\\\":\\\"TILE\\\"},\\\"type\\\":\\\"VECTOR_TILE\\\"},{\\\"sourceDescriptor\\\":{\\\"indexPatternId\\\":\\\"43fcac20-ca27-11eb-bf5e-3de94e83d4f0\\\",\\\"geoField\\\":\\\"geo.coordinates\\\",\\\"filterByMapBounds\\\":true,\\\"scalingType\\\":\\\"CLUSTERS\\\",\\\"topHitsSplitField\\\":\\\"\\\",\\\"topHitsSize\\\":1,\\\"id\\\":\\\"86ff9ac9-9ccf-44b0-8b90-a4b779f5bc38\\\",\\\"type\\\":\\\"ES_SEARCH\\\",\\\"applyGlobalQuery\\\":true,\\\"applyGlobalTime\\\":true,\\\"tooltipProperties\\\":[\\\"extension\\\",\\\"geo.dest\\\",\\\"response\\\"],\\\"sortField\\\":\\\"\\\",\\\"sortOrder\\\":\\\"desc\\\"},\\\"id\\\":\\\"3d062d54-73de-4be7-8ae2-11e9b385f7d9\\\",\\\"label\\\":\\\"\\\",\\\"minZoom\\\":0,\\\"maxZoom\\\":24,\\\"alpha\\\":0.75,\\\"visible\\\":true,\\\"style\\\":{\\\"type\\\":\\\"VECTOR\\\",\\\"properties\\\":{\\\"icon\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"value\\\":\\\"marker\\\"}},\\\"fillColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#54B399\\\"}},\\\"lineColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#41937c\\\"}},\\\"lineWidth\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"size\\\":1}},\\\"iconSize\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"size\\\":6}},\\\"iconOrientation\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"orientation\\\":0}},\\\"labelText\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"value\\\":\\\"\\\"}},\\\"labelColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#000000\\\"}},\\\"labelSize\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"size\\\":14}},\\\"labelBorderColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#FFFFFF\\\"}},\\\"symbolizeAs\\\":{\\\"options\\\":{\\\"value\\\":\\\"circle\\\"}},\\\"labelBorderSize\\\":{\\\"options\\\":{\\\"size\\\":\\\"SMALL\\\"}}},\\\"isTimeAware\\\":true},\\\"type\\\":\\\"BLENDED_VECTOR\\\",\\\"joins\\\":[]}]\",\"mapStateJSON\":\"{\\\"zoom\\\":1.38,\\\"center\\\":{\\\"lon\\\":0,\\\"lat\\\":19.94277},\\\"timeFilters\\\":{\\\"from\\\":\\\"now-15y\\\",\\\"to\\\":\\\"now\\\"},\\\"refreshConfig\\\":{\\\"isPaused\\\":true,\\\"interval\\\":0},\\\"query\\\":{\\\"query\\\":\\\"\\\",\\\"language\\\":\\\"kuery\\\"},\\\"filters\\\":[],\\\"settings\\\":{\\\"autoFitToDataBounds\\\":false,\\\"backgroundColor\\\":\\\"#ffffff\\\",\\\"disableInteractive\\\":false,\\\"disableTooltipControl\\\":false,\\\"hideToolbarOverlay\\\":false,\\\"hideLayerControl\\\":false,\\\"hideViewControl\\\":false,\\\"initialLocation\\\":\\\"LAST_SAVED_LOCATION\\\",\\\"fixedLocation\\\":{\\\"lat\\\":0,\\\"lon\\\":0,\\\"zoom\\\":2},\\\"browserLocation\\\":{\\\"zoom\\\":2},\\\"maxZoom\\\":24,\\\"minZoom\\\":0,\\\"showScaleControl\\\":false,\\\"showSpatialFilters\\\":true,\\\"spatialFiltersAlpa\\\":0.3,\\\"spatialFiltersFillColor\\\":\\\"#DA8B45\\\",\\\"spatialFiltersLineColor\\\":\\\"#DA8B45\\\"}}\",\"uiStateJSON\":\"{\\\"isLayerTOCOpen\\\":true,\\\"openTOCDetails\\\":[]}\"},\"mapCenter\":{\"lat\":19.94277,\"lon\":0,\"zoom\":1.38},\"mapBuffer\":{\"minLon\":-214.7723,\"minLat\":-74.644155,\"maxLon\":214.7723,\"maxLat\":102.864625},\"isLayerTOCOpen\":true,\"openTOCDetails\":[],\"hiddenLayers\":[],\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"b10e564b-7d42-45f5-8c26-a9220c405834\",\"triggers\":[\"CONTEXT_MENU_TRIGGER\"],\"action\":{\"name\":\"URL drilldown\",\"config\":{\"url\":{\"template\":\"http://localhost:5601/app/discover#/view/0abce1c0-ca2a-11eb-bf5e-3de94e83d4f0?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15y,to:now))&_a=(columns:!(),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'43fcac20-ca27-11eb-bf5e-3de94e83d4f0',key:geo.srcdest,negate:!f,params:!('IN:CN'),type:phrases,value:'IN:CN'),query:(bool:(minimum_should_match:1,should:!((match_phrase:(geo.srcdest:'IN:CN'))))))),index:'43fcac20-ca27-11eb-bf5e-3de94e83d4f0',interval:auto,query:(language:kuery,query:''),sort:!(!('@timestamp',desc)))\"},\"openInNewTab\":true,\"encodeUrl\":true},\"factoryId\":\"URL_DRILLDOWN\"}}]}},\"type\":\"map\"}},{\"version\":\"7.13.1\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"5de83d82-bbd1-4d30-be61-dd6724f32c07\"},\"panelIndex\":\"5de83d82-bbd1-4d30-be61-dd6724f32c07\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"\",\"description\":\"\",\"type\":\"metrics\",\"params\":{\"annotations\":[{\"fields\":\"response.raw\",\"template\":\"{{response.raw}}\",\"index_pattern\":\"logstash-*\",\"query_string\":{\"query\":\"response.raw :\\\"404\\\" \",\"language\":\"kuery\"},\"color\":\"#F00\",\"icon\":\"fa-bomb\",\"id\":\"37395960-ca28-11eb-9eac-2f3ccefcbeef\",\"ignore_global_filters\":1,\"ignore_panel_filters\":1,\"time_field\":\"@timestamp\"}],\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logstash-*\",\"interval\":\"\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"kibana\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"tooltip_mode\":\"show_all\",\"type\":\"timeseries\",\"use_kibana_indexes\":false},\"uiState\":{},\"data\":{\"aggs\":[],\"searchSource\":{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}}},\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"23d04651-266f-4f0a-8eef-6f190f0a84af\",\"triggers\":[\"FILTER_TRIGGER\"],\"action\":{\"name\":\"dashboard\",\"config\":{\"useCurrentFilters\":true,\"useCurrentDateRange\":true},\"factoryId\":\"DASHBOARD_TO_DASHBOARD_DRILLDOWN\"}}]}},\"type\":\"visualization\"}},{\"version\":\"7.13.1\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":15,\"w\":24,\"h\":15,\"i\":\"a254a623-a9af-4372-851b-572fa95b0902\"},\"panelIndex\":\"a254a623-a9af-4372-851b-572fa95b0902\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"\",\"description\":\"\",\"type\":\"vega\",\"params\":{\"spec\":\"{\\n/*\\n\\nWelcome to Vega visualizations. Here you can design your own dataviz from scratch using a declarative language called Vega, or its simpler form Vega-Lite. In Vega, you have the full control of what data is loaded, even from multiple sources, how that data is transformed, and what visual elements are used to show it. Use help icon to view Vega examples, tutorials, and other docs. Use the wrench icon to reformat this text, or to remove comments.\\n\\nThis example graph shows the document count in all indexes in the current time range. You might need to adjust the time filter in the upper right corner.\\n*/\\n\\n $schema: https://vega.github.io/schema/vega-lite/v4.json\\n title: Event counts from all indexes\\n\\n // Define the data source\\n data: {\\n url: {\\n/*\\nAn object instead of a string for the \\\"url\\\" param is treated as an Elasticsearch query. Anything inside this object is not part of the Vega language, but only understood by Kibana and Elasticsearch server. This query counts the number of documents per time interval, assuming you have a @timestamp field in your data.\\n\\nKibana has a special handling for the fields surrounded by \\\"%\\\". They are processed before the the query is sent to Elasticsearch. This way the query becomes context aware, and can use the time range and the dashboard filters.\\n*/\\n\\n // Apply dashboard context filters when set\\n %context%: true\\n // Filter the time picker (upper right corner) with this field\\n %timefield%: @timestamp\\n\\n/*\\nSee .search() documentation for : https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/api-reference.html#api-search\\n*/\\n\\n // Which index to search\\n index: _all\\n // Aggregate data by the time field into time buckets, counting the number of documents in each bucket.\\n body: {\\n aggs: {\\n time_buckets: {\\n date_histogram: {\\n // Use date histogram aggregation on @timestamp field\\n field: @timestamp\\n // The interval value will depend on the daterange picker (true), or use an integer to set an approximate bucket count\\n interval: {%autointerval%: true}\\n // Make sure we get an entire range, even if it has no data\\n extended_bounds: {\\n // Use the current time range's start and end\\n min: {%timefilter%: \\\"min\\\"}\\n max: {%timefilter%: \\\"max\\\"}\\n }\\n // Use this for linear (e.g. line, area) graphs. Without it, empty buckets will not show up\\n min_doc_count: 0\\n }\\n }\\n }\\n // Speed up the response by only including aggregation results\\n size: 0\\n }\\n }\\n/*\\nElasticsearch will return results in this format:\\n\\naggregations: {\\n time_buckets: {\\n buckets: [\\n {\\n key_as_string: 2015-11-30T22:00:00.000Z\\n key: 1448920800000\\n doc_count: 0\\n },\\n {\\n key_as_string: 2015-11-30T23:00:00.000Z\\n key: 1448924400000\\n doc_count: 0\\n }\\n ...\\n ]\\n }\\n}\\n\\nFor our graph, we only need the list of bucket values. Use the format.property to discard everything else.\\n*/\\n format: {property: \\\"aggregations.time_buckets.buckets\\\"}\\n }\\n\\n // \\\"mark\\\" is the graphics element used to show our data. Other mark values are: area, bar, circle, line, point, rect, rule, square, text, and tick. See https://vega.github.io/vega-lite/docs/mark.html\\n mark: line\\n\\n // \\\"encoding\\\" tells the \\\"mark\\\" what data to use and in what way. See https://vega.github.io/vega-lite/docs/encoding.html\\n encoding: {\\n x: {\\n // The \\\"key\\\" value is the timestamp in milliseconds. Use it for X axis.\\n field: key\\n type: temporal\\n axis: {title: false} // Customize X axis format\\n }\\n y: {\\n // The \\\"doc_count\\\" is the count per bucket. Use it for Y axis.\\n field: doc_count\\n type: quantitative\\n axis: {title: \\\"Document count\\\"}\\n }\\n }\\n}\\n\"},\"uiState\":{},\"data\":{\"aggs\":[],\"searchSource\":{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"43fcac20-ca27-11eb-bf5e-3de94e83d4f0\",\"key\":\"geo.srcdest\",\"negate\":false,\"params\":{\"query\":\"CN:CN\"},\"type\":\"phrase\"},\"query\":{\"match_phrase\":{\"geo.srcdest\":\"CN:CN\"}}}]}}},\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"cfd2521d-15a0-4c64-b0ab-d2dc18f396e3\",\"triggers\":[\"CONTEXT_MENU_TRIGGER\"],\"action\":{\"name\":\"URL\",\"config\":{\"url\":{\"template\":\"http://localhost:5601/app/discover#/view/4acce030-ca2a-11eb-bf5e-3de94e83d4f0?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15y,to:now))&_a=(columns:!(),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'43fcac20-ca27-11eb-bf5e-3de94e83d4f0',key:ip,negate:!f,params:(query:'57.237.11.219'),type:phrase),query:(match_phrase:(ip:'57.237.11.219')))),index:'43fcac20-ca27-11eb-bf5e-3de94e83d4f0',interval:auto,query:(language:kuery,query:''),sort:!(!('@timestamp',desc)))\"},\"openInNewTab\":true,\"encodeUrl\":true},\"factoryId\":\"URL_DRILLDOWN\"}}]}},\"type\":\"visualization\"}},{\"version\":\"7.13.1\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":30,\"w\":24,\"h\":15,\"i\":\"7f0506ed-1f30-410f-bcd7-3f70623aa5ba\"},\"panelIndex\":\"7f0506ed-1f30-410f-bcd7-3f70623aa5ba\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"\",\"description\":\"\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"linear\",\"orientation\":\"single\",\"minFontSize\":18,\"maxFontSize\":72,\"showLabel\":true},\"uiState\":{},\"data\":{\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"significant_terms\",\"params\":{\"field\":\"geo.srcdest\",\"size\":77},\"schema\":\"segment\"}],\"searchSource\":{\"index\":\"43fcac20-ca27-11eb-bf5e-3de94e83d4f0\",\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}}},\"enhancements\":{},\"type\":\"visualization\"}}]","timeRestore":false,"title":"logstash_by_value_dashboard","version":1},"coreMigrationVersion":"7.13.2","id":"08dec860-ca29-11eb-bf5e-3de94e83d4f0","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"indexpattern-datasource-layer-f61694eb-94ed-495d-9ce8-63592f040b0b","type":"index-pattern"},{"id":"35ce3b30-ca29-11eb-bf5e-3de94e83d4f0","name":"drilldown:DASHBOARD_TO_DASHBOARD_DRILLDOWN:da7ad2b6-1e4a-40b5-9123-d0ec2bde858d:dashboardId","type":"dashboard"},{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"layer_1_source_index_pattern","type":"index-pattern"},{"id":"35ce3b30-ca29-11eb-bf5e-3de94e83d4f0","name":"drilldown:DASHBOARD_TO_DASHBOARD_DRILLDOWN:23d04651-266f-4f0a-8eef-6f190f0a84af:dashboardId","type":"dashboard"},{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"07f48f70-ca29-11eb-bf5e-3de94e83d4f0","name":"tag-07f48f70-ca29-11eb-bf5e-3de94e83d4f0","type":"tag"}],"sort":[1623415891791,134],"type":"dashboard","updated_at":"2021-06-11T12:51:31.791Z","version":"WzE0MzUsNF0="} -{"attributes":{"fieldAttrs":"{\"machine.os\":{\"count\":1},\"spaces\":{\"count\":1},\"type\":{\"count\":1},\"bytes_scripted\":{\"count\":1}}","fields":"[{\"count\":1,\"script\":\"doc['bytes'].value*1024\",\"lang\":\"painless\",\"name\":\"bytes_scripted\",\"type\":\"number\",\"scripted\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false}]","runtimeFieldMap":"{}","timeFieldName":"@timestamp","title":"logstash-*"},"coreMigrationVersion":"7.13.2","id":"56b34100-619d-11eb-aebf-c306684b328d","migrationVersion":{"index-pattern":"7.11.0"},"references":[],"sort":[1623693556928,449],"type":"index-pattern","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NDYsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_scriptedfieldviz","uiStateJSON":"{\"vis\":{\"defaultColors\":{\"0 - 100\":\"rgb(0,104,55)\"}}}","version":1,"visState":"{\"title\":\"logstash_scriptedfieldviz\",\"type\":\"goal\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"isDisplayWarning\":false,\"type\":\"gauge\",\"gauge\":{\"verticalSplit\":false,\"autoExtend\":false,\"percentageMode\":true,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"gaugeColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":false,\"labels\":false,\"color\":\"#333\",\"width\":2},\"type\":\"meter\",\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"range\",\"schema\":\"group\",\"params\":{\"field\":\"bytes_scripted\",\"ranges\":[{\"from\":0,\"to\":40000},{\"from\":40001,\"to\":20000000}]}}]}"},"coreMigrationVersion":"7.13.2","id":"0a274320-61cc-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,451],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NDcsNF0="} -{"attributes":{"columns":[],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[{\"meta\":{\"type\":\"phrases\",\"key\":\"geo.srcdest\",\"value\":\"IN:CN\",\"params\":[\"IN:CN\"],\"alias\":null,\"negate\":false,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"bool\":{\"should\":[{\"match_phrase\":{\"geo.srcdest\":\"IN:CN\"}}],\"minimum_should_match\":1}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["@timestamp","desc"]],"title":"search_saved","version":1},"coreMigrationVersion":"7.13.2","id":"0abce1c0-ca2a-11eb-bf5e-3de94e83d4f0","migrationVersion":{"search":"7.9.3"},"references":[{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"}],"sort":[1623415891791,137],"type":"search","updated_at":"2021-06-11T12:51:31.791Z","version":"WzE0MzYsNF0="} -{"attributes":{"color":"#81a93f","description":"","name":"logstash_tag"},"coreMigrationVersion":"7.13.2","id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","references":[],"sort":[1623693556928,452],"type":"tag","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NDgsNF0="} -{"attributes":{"description":"","layerListJSON":"[{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"isAutoSelect\":true},\"id\":\"4c2394ca-a6a2-4f8d-9631-259eb3a9627f\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"style\":{\"type\":\"TILE\"},\"type\":\"VECTOR_TILE\"},{\"sourceDescriptor\":{\"geoField\":\"geo.coordinates\",\"filterByMapBounds\":true,\"scalingType\":\"CLUSTERS\",\"id\":\"7555324e-e793-4b7d-a9d2-cd63e6b7fe3d\",\"type\":\"ES_SEARCH\",\"applyGlobalQuery\":true,\"applyGlobalTime\":true,\"tooltipProperties\":[\"geo.srcdest\",\"machine.os\",\"type\"],\"sortField\":\"bytes_scripted\",\"sortOrder\":\"desc\",\"topHitsSplitField\":\"\",\"topHitsSize\":1,\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"id\":\"6a493d8b-a220-46bc-8906-a1a7569799e0\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}},\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"color\":\"Blues\",\"colorCategory\":\"palette_0\",\"field\":{\"name\":\"extension.raw\",\"origin\":\"source\"},\"fieldMetaOptions\":{\"isEnabled\":true,\"sigma\":3},\"type\":\"CATEGORICAL\"}},\"lineColor\":{\"type\":\"DYNAMIC\",\"options\":{\"color\":\"Blues\",\"colorCategory\":\"palette_0\",\"field\":{\"name\":\"machine.os.raw\",\"origin\":\"source\"},\"fieldMetaOptions\":{\"isEnabled\":true,\"sigma\":3},\"type\":\"CATEGORICAL\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":6}},\"iconOrientation\":{\"type\":\"STATIC\",\"options\":{\"orientation\":0}},\"labelText\":{\"type\":\"STATIC\",\"options\":{\"value\":\"\"}},\"labelColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#000000\"}},\"labelSize\":{\"type\":\"STATIC\",\"options\":{\"size\":14}},\"labelBorderColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"labelBorderSize\":{\"options\":{\"size\":\"SMALL\"}}},\"isTimeAware\":true},\"type\":\"BLENDED_VECTOR\",\"joins\":[]}]","mapStateJSON":"{\"zoom\":1.56,\"center\":{\"lon\":0,\"lat\":19.94277},\"timeFilters\":{\"from\":\"now-15y\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":0},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"settings\":{\"autoFitToDataBounds\":false,\"backgroundColor\":\"#ffffff\",\"disableInteractive\":false,\"disableTooltipControl\":false,\"hideToolbarOverlay\":false,\"hideLayerControl\":false,\"hideViewControl\":false,\"initialLocation\":\"LAST_SAVED_LOCATION\",\"fixedLocation\":{\"lat\":0,\"lon\":0,\"zoom\":2},\"browserLocation\":{\"zoom\":2},\"maxZoom\":24,\"minZoom\":0,\"showScaleControl\":false,\"showSpatialFilters\":true,\"spatialFiltersAlpa\":0.3,\"spatialFiltersFillColor\":\"#DA8B45\",\"spatialFiltersLineColor\":\"#DA8B45\"}}","title":"logstash_maps","uiStateJSON":"{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}"},"coreMigrationVersion":"7.13.2","id":"0c5974f0-be5c-11eb-9520-1b4c3ca6a781","migrationVersion":{"map":"7.12.0"},"references":[{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"layer_1_source_index_pattern","type":"index-pattern"}],"sort":[1623693556928,455],"type":"map","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NDksNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_datatable","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}","version":1,"visState":"{\"title\":\"logstash_datatable\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":true,\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"bucket\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"2015-07-24T08:58:14.175Z\",\"to\":\"2015-11-11T13:28:17.223Z\",\"mode\":\"absolute\"},\"useNormalizedEsInterval\":true,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.raw\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.2","id":"0d8a8860-623a-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,457],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NTAsNF0="} -{"attributes":{"description":null,"state":{"datasourceStates":{"indexpattern":{"layers":{"35fd070e-5bbc-4906-bf69-8548a213d7a0":{"columnOrder":["2bf7969f-0371-4df2-a398-0a191e428ce5","aab812d6-609b-444d-9990-1e67f85fd85d","e9829e8a-c484-4c9d-b489-f1eb3fb138d2","4fc9fb3b-29a5-4679-ab3c-90d5daaf0661"],"columns":{"2bf7969f-0371-4df2-a398-0a191e428ce5":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"},"4fc9fb3b-29a5-4679-ab3c-90d5daaf0661":{"dataType":"number","isBucketed":false,"label":"Moving average of Median of bytes","operationType":"moving_average","params":{"window":5},"references":["e9829e8a-c484-4c9d-b489-f1eb3fb138d2"],"scale":"ratio"},"aab812d6-609b-444d-9990-1e67f85fd85d":{"dataType":"number","isBucketed":false,"label":"Average of bytes","operationType":"average","scale":"ratio","sourceField":"bytes"},"e9829e8a-c484-4c9d-b489-f1eb3fb138d2":{"dataType":"number","isBucketed":false,"label":"Median of bytes","operationType":"median","scale":"ratio","sourceField":"bytes"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["aab812d6-609b-444d-9990-1e67f85fd85d","4fc9fb3b-29a5-4679-ab3c-90d5daaf0661"],"layerId":"35fd070e-5bbc-4906-bf69-8548a213d7a0","position":"top","seriesType":"bar_stacked","showGridlines":false,"xAccessor":"2bf7969f-0371-4df2-a398-0a191e428ce5"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_barvertical_stacked_average","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.2","id":"0dbbf8b0-be3c-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-35fd070e-5bbc-4906-bf69-8548a213d7a0","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,461],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NTEsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_area_chart","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_area_chart\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"2010-01-28T19:25:55.242Z\",\"to\":\"2021-01-28T19:40:55.242Z\",\"mode\":\"absolute\"},\"useNormalizedEsInterval\":true,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"machine.os.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"machine OS\"}}]}"},"coreMigrationVersion":"7.13.2","id":"36b91810-6239-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,463],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NTIsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_horizontal","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_horizontal\",\"type\":\"horizontal_bar\",\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":200},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":75,\"filter\":true,\"truncate\":100},\"title\":{\"text\":\"no of documents\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"normal\",\"data\":{\"label\":\"no of documents\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":true,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"no of documents\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"2015-07-24T08:58:14.175Z\",\"to\":\"2015-11-11T13:28:17.223Z\",\"mode\":\"absolute\"},\"useNormalizedEsInterval\":true,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"agent.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"extension.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.2","id":"e4aef350-623d-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,465],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NTMsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_linechart","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_linechart\",\"type\":\"line\",\"params\":{\"type\":\"line\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"line\",\"mode\":\"normal\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"radiusRatio\":51,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"2015-09-18T06:38:43.311Z\",\"to\":\"2015-09-26T04:02:51.104Z\",\"mode\":\"absolute\"},\"useNormalizedEsInterval\":true,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"sum\",\"schema\":\"radius\",\"params\":{\"field\":\"bytes\",\"customLabel\":\"bubbles\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"machine.os.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.2","id":"f92e5630-623e-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,467],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NTQsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_heatmap","uiStateJSON":"{\"vis\":{\"defaultColors\":{\"0% - 25%\":\"rgb(255,255,204)\",\"25% - 50%\":\"rgb(254,217,118)\",\"50% - 75%\":\"rgb(253,141,60)\",\"75% - 100%\":\"rgb(227,27,28)\"}}}","version":1,"visState":"{\"title\":\"logstash_heatmap\",\"type\":\"heatmap\",\"params\":{\"type\":\"heatmap\",\"addTooltip\":true,\"addLegend\":true,\"enableHover\":false,\"legendPosition\":\"right\",\"times\":[],\"colorsNumber\":4,\"colorSchema\":\"Yellow to Red\",\"setColorRange\":false,\"colorsRange\":[],\"invertColors\":false,\"percentageMode\":true,\"valueAxes\":[{\"show\":false,\"id\":\"ValueAxis-1\",\"type\":\"value\",\"scale\":{\"type\":\"linear\",\"defaultYExtents\":false},\"labels\":{\"show\":false,\"rotate\":0,\"overwriteColor\":false,\"color\":\"#555\"}}],\"row\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"2015-07-24T08:58:14.175Z\",\"to\":\"2015-11-11T13:28:17.223Z\",\"mode\":\"absolute\"},\"useNormalizedEsInterval\":true,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"machine.os.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"response.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.2","id":"9853d4d0-623d-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,469],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NTUsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_goalchart","uiStateJSON":"{\"vis\":{\"defaultColors\":{\"0 - 33\":\"rgb(0,104,55)\",\"33 - 67\":\"rgb(255,255,190)\",\"67 - 100\":\"rgb(165,0,38)\"}}}","version":1,"visState":"{\"title\":\"logstash_goalchart\",\"type\":\"goal\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"isDisplayWarning\":false,\"type\":\"gauge\",\"gauge\":{\"verticalSplit\":false,\"autoExtend\":false,\"percentageMode\":true,\"gaugeType\":\"Circle\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"gaugeColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000},{\"from\":10001,\"to\":20000},{\"from\":20001,\"to\":30000}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":false,\"labels\":false,\"color\":\"#333\",\"width\":2},\"type\":\"meter\",\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60},\"minAngle\":0,\"maxAngle\":6.283185307179586}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"group\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"2015-07-24T08:58:14.175Z\",\"to\":\"2015-11-11T13:28:17.223Z\",\"mode\":\"absolute\"},\"useNormalizedEsInterval\":true,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}}]}"},"coreMigrationVersion":"7.13.2","id":"6ecb33b0-623d-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,471],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NTYsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_gauge","uiStateJSON":"{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(0,104,55)\",\"50 - 75\":\"rgb(255,255,190)\",\"75 - 100\":\"rgb(165,0,38)\"}}}","version":1,"visState":"{\"title\":\"logstash_gauge\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"extendRange\":true,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Green to Red\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":50},{\"from\":50,\"to\":75},{\"from\":75,\"to\":100}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true},\"alignment\":\"horizontal\"}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"range\",\"schema\":\"group\",\"params\":{\"field\":\"bytes\",\"ranges\":[{\"from\":0,\"to\":10001},{\"from\":10002,\"to\":1000000}],\"json\":\"\"}}]}"},"coreMigrationVersion":"7.13.2","id":"b8e35c80-623c-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,473],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NTcsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_coordinatemaps","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_coordinatemaps\",\"type\":\"tile_map\",\"params\":{\"colorSchema\":\"Yellow to Red\",\"mapType\":\"Scaled Circle Markers\",\"isDesaturated\":false,\"addTooltip\":true,\"heatClusterSize\":1.5,\"legendPosition\":\"bottomright\",\"mapZoom\":2,\"mapCenter\":[0,0],\"wms\":{\"enabled\":false,\"options\":{\"format\":\"image/png\",\"transparent\":true},\"selectedTmsLayer\":{\"origin\":\"elastic_maps_service\",\"id\":\"road_map\",\"minZoom\":0,\"maxZoom\":18,\"attribution\":\"

© OpenStreetMap contributors|OpenMapTiles|Elastic Maps Service

\"}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"geohash_grid\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.coordinates\",\"autoPrecision\":true,\"isFilteredByCollar\":true,\"useGeocentroid\":true,\"mapZoom\":2,\"mapCenter\":[0,0],\"precision\":2,\"customLabel\":\"logstash src/dest\"}}]}"},"coreMigrationVersion":"7.13.2","id":"f1bc75d0-6239-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,475],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NTgsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"title":"logstash_inputcontrols","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_inputcontrols\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1611928563867\",\"fieldName\":\"machine.ram\",\"parent\":\"\",\"label\":\"Logstash RAM\",\"type\":\"range\",\"options\":{\"decimalPlaces\":0,\"step\":1024},\"indexPatternRefName\":\"control_0_index_pattern\"},{\"id\":\"1611928586274\",\"fieldName\":\"machine.os.raw\",\"parent\":\"\",\"label\":\"Logstash OS\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"dynamicOptions\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_1_index_pattern\"}],\"updateFiltersOnChange\":false,\"useTimeFilter\":false,\"pinFilters\":false},\"aggs\":[]}"},"coreMigrationVersion":"7.13.2","id":"d79fe3d0-6239-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"control_0_index_pattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"control_1_index_pattern","type":"index-pattern"}],"sort":[1623693556928,478],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NTksNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"title":"logstash_markdown","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_markdown\",\"type\":\"markdown\",\"params\":{\"fontSize\":12,\"openLinksInNewTab\":true,\"markdown\":\"Kibana is built with JS https://www.javascript.com/\"},\"aggs\":[]}"},"coreMigrationVersion":"7.13.2","id":"318375a0-6240-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[],"sort":[1623693556928,479],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NjAsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"title":"logstash_vegaviz","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_vegaviz\",\"type\":\"vega\",\"params\":{\"spec\":\"{\\n/*\\n\\nWelcome to Vega visualizations. Here you can design your own dataviz from scratch using a declarative language called Vega, or its simpler form Vega-Lite. In Vega, you have the full control of what data is loaded, even from multiple sources, how that data is transformed, and what visual elements are used to show it. Use help icon to view Vega examples, tutorials, and other docs. Use the wrench icon to reformat this text, or to remove comments.\\n\\nThis example graph shows the document count in all indexes in the current time range. You might need to adjust the time filter in the upper right corner.\\n*/\\n\\n $schema: https://vega.github.io/schema/vega-lite/v2.json\\n title: Event counts from all indexes\\n\\n // Define the data source\\n data: {\\n url: {\\n/*\\nAn object instead of a string for the \\\"url\\\" param is treated as an Elasticsearch query. Anything inside this object is not part of the Vega language, but only understood by Kibana and Elasticsearch server. This query counts the number of documents per time interval, assuming you have a @timestamp field in your data.\\n\\nKibana has a special handling for the fields surrounded by \\\"%\\\". They are processed before the the query is sent to Elasticsearch. This way the query becomes context aware, and can use the time range and the dashboard filters.\\n*/\\n\\n // Apply dashboard context filters when set\\n %context%: true\\n // Filter the time picker (upper right corner) with this field\\n %timefield%: @timestamp\\n\\n/*\\nSee .search() documentation for : https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/api-reference.html#api-search\\n*/\\n\\n // Which index to search\\n index: logstash-*\\n // Aggregate data by the time field into time buckets, counting the number of documents in each bucket.\\n body: {\\n aggs: {\\n time_buckets: {\\n date_histogram: {\\n // Use date histogram aggregation on @timestamp field\\n field: @timestamp\\n // The interval value will depend on the daterange picker (true), or use an integer to set an approximate bucket count\\n interval: {%autointerval%: true}\\n // Make sure we get an entire range, even if it has no data\\n extended_bounds: {\\n // Use the current time range's start and end\\n min: {%timefilter%: \\\"min\\\"}\\n max: {%timefilter%: \\\"max\\\"}\\n }\\n // Use this for linear (e.g. line, area) graphs. Without it, empty buckets will not show up\\n min_doc_count: 13\\n }\\n }\\n }\\n // Speed up the response by only including aggregation results\\n size: 0\\n }\\n }\\n/*\\nElasticsearch will return results in this format:\\n\\naggregations: {\\n time_buckets: {\\n buckets: [\\n {\\n key_as_string: 2015-11-30T22:00:00.000Z\\n key: 1448920800000\\n doc_count: 0\\n },\\n {\\n key_as_string: 2015-11-30T23:00:00.000Z\\n key: 1448924400000\\n doc_count: 0\\n }\\n ...\\n ]\\n }\\n}\\n\\nFor our graph, we only need the list of bucket values. Use the format.property to discard everything else.\\n*/\\n format: {property: \\\"aggregations.time_buckets.buckets\\\"}\\n }\\n\\n // \\\"mark\\\" is the graphics element used to show our data. Other mark values are: area, bar, circle, line, point, rect, rule, square, text, and tick. See https://vega.github.io/vega-lite/docs/mark.html\\n mark: line\\n\\n // \\\"encoding\\\" tells the \\\"mark\\\" what data to use and in what way. See https://vega.github.io/vega-lite/docs/encoding.html\\n encoding: {\\n x: {\\n // The \\\"key\\\" value is the timestamp in milliseconds. Use it for X axis.\\n field: key\\n type: temporal\\n axis: {title: false} // Customize X axis format\\n }\\n y: {\\n // The \\\"doc_count\\\" is the count per bucket. Use it for Y axis.\\n field: doc_count\\n type: quantitative\\n axis: {title: \\\"Document count\\\"}\\n }\\n }\\n}\\n\"},\"aggs\":[]}"},"coreMigrationVersion":"7.13.2","id":"e461eb20-6245-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[],"sort":[1623693556928,480],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NjEsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_regionmap","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_regionmap\",\"type\":\"region_map\",\"params\":{\"addTooltip\":true,\"colorSchema\":\"Yellow to Red\",\"emsHotLink\":\"https://maps.elastic.co/v6.7?locale=en#file/world_countries\",\"isDisplayWarning\":true,\"legendPosition\":\"bottomright\",\"mapCenter\":[0,0],\"mapZoom\":2,\"outlineWeight\":1,\"selectedJoinField\":{\"type\":\"id\",\"name\":\"iso2\",\"description\":\"ISO 3166-1 alpha-2 code\"},\"showAllShapes\":true,\"wms\":{\"enabled\":false,\"options\":{\"format\":\"image/png\",\"transparent\":true},\"selectedTmsLayer\":{\"origin\":\"elastic_maps_service\",\"id\":\"road_map\",\"minZoom\":0,\"maxZoom\":18,\"attribution\":\"

© OpenStreetMap contributors|OpenMapTiles|Elastic Maps Service

\"}},\"selectedLayer\":{\"name\":\"World Countries\",\"origin\":\"elastic_maps_service\",\"id\":\"world_countries\",\"created_at\":\"2017-04-26T17:12:15.978370\",\"attribution\":\"Made with NaturalEarth | Elastic Maps Service\",\"fields\":[{\"type\":\"id\",\"name\":\"iso2\",\"description\":\"ISO 3166-1 alpha-2 code\"},{\"type\":\"id\",\"name\":\"iso3\",\"description\":\"ISO 3166-1 alpha-3 code\"},{\"type\":\"property\",\"name\":\"name\",\"description\":\"name\"}],\"format\":{\"type\":\"geojson\"},\"layerId\":\"elastic_maps_service.World Countries\",\"isEMS\":true}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.dest\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.2","id":"25bdc750-6242-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,482],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NjIsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_verticalbarchart","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_verticalbarchart\",\"type\":\"histogram\",\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"defaultYExtents\":true},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":true,\"row\":true,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"2015-09-18T06:38:43.311Z\",\"to\":\"2015-09-26T04:02:51.104Z\",\"mode\":\"absolute\"},\"useNormalizedEsInterval\":true,\"interval\":\"h\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{},\"scaleMetricValues\":true}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"response.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Response code\"}}]}"},"coreMigrationVersion":"7.13.2","id":"71dd7bc0-6248-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,484],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NjMsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_metricviz","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_metricviz\",\"type\":\"metric\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"type\":\"metric\",\"metric\":{\"percentageMode\":false,\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"metricColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"labels\":{\"show\":true},\"invertColors\":false,\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"range\",\"schema\":\"group\",\"params\":{\"field\":\"bytes_scripted\",\"ranges\":[{\"from\":0,\"to\":10000},{\"from\":10001,\"to\":300000}]}}]}"},"coreMigrationVersion":"7.13.2","id":"6aea48a0-6240-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,486],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NjQsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_piechart","uiStateJSON":"{}","version":1,"visState":"{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"machine.os.raw\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"segment\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTooltip\":true,\"isDonut\":true,\"labels\":{\"last_level\":true,\"show\":false,\"truncate\":100,\"values\":true},\"legendPosition\":\"right\",\"type\":\"pie\"},\"title\":\"logstash_piechart\",\"type\":\"pie\"}"},"coreMigrationVersion":"7.13.2","id":"32b681f0-6241-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,488],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NjUsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_tagcloud","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_tagcloud\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"log\",\"orientation\":\"single\",\"minFontSize\":18,\"maxFontSize\":72,\"showLabel\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.srcdest\",\"size\":23,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.2","id":"ccca99e0-6244-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,490],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NjYsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"title":"logstash_timelion","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_timelion\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(q='machine.os.raw:win xp' , index=logstash-*)\",\"interval\":\"auto\"},\"aggs\":[]}"},"coreMigrationVersion":"7.13.2","id":"a4d7be80-6245-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[],"sort":[1623693556928,491],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NjcsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{}"},"title":"logstash_tsvb","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_tsvb\",\"type\":\"metrics\",\"params\":{\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"timeseries\",\"series\":[{\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"show_grid\":1,\"annotations\":[{\"fields\":\"machine.os.raw\",\"template\":\"{{machine.os.raw}}\",\"index_pattern\":\"logstash-*\",\"query_string\":{\"query\":\"machine.os.raw :\\\"win xp\\\" \",\"language\":\"lucene\"},\"id\":\"aa43ceb0-6248-11eb-9a82-ef1c6e6c0265\",\"color\":\"#F00\",\"time_field\":\"@timestamp\",\"icon\":\"fa-tag\",\"ignore_global_filters\":1,\"ignore_panel_filters\":1}],\"use_kibana_indexes\":false},\"aggs\":[]}"},"coreMigrationVersion":"7.13.2","id":"c94d8440-6248-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[],"sort":[1623693556928,492],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NjgsNF0="} -{"attributes":{"columns":["bytes_scripted"],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"highlightAll\":true,\"version\":true,\"query\":{\"query\":\"machine.os.raw :\\\"win xp\\\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["@timestamp","desc"]],"title":"logstash_scripted_saved_search","version":1},"coreMigrationVersion":"7.13.2","id":"db6226f0-61c0-11eb-aebf-c306684b328d","migrationVersion":{"search":"7.9.3"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,494],"type":"search","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NjksNF0="} -{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"1\",\"w\":24,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"2\",\"w\":24,\"x\":24,\"y\":0},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"3\",\"w\":24,\"x\":0,\"y\":15},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"4\",\"w\":24,\"x\":24,\"y\":15},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"5\",\"w\":24,\"x\":0,\"y\":30},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"6\",\"w\":24,\"x\":24,\"y\":30},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"7\",\"w\":24,\"x\":0,\"y\":45},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"8\",\"w\":24,\"x\":24,\"y\":45},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_8\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"9\",\"w\":24,\"x\":0,\"y\":60},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"10\",\"w\":24,\"x\":24,\"y\":60},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"11\",\"w\":24,\"x\":0,\"y\":75},\"panelIndex\":\"11\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_11\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"12\",\"w\":24,\"x\":24,\"y\":75},\"panelIndex\":\"12\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_12\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"13\",\"w\":24,\"x\":0,\"y\":90},\"panelIndex\":\"13\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"14\",\"w\":24,\"x\":24,\"y\":90},\"panelIndex\":\"14\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_14\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"15\",\"w\":24,\"x\":0,\"y\":105},\"panelIndex\":\"15\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_15\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"16\",\"w\":24,\"x\":24,\"y\":105},\"panelIndex\":\"16\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_16\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"17\",\"w\":24,\"x\":0,\"y\":120},\"panelIndex\":\"17\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_17\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"18\",\"w\":24,\"x\":24,\"y\":120},\"panelIndex\":\"18\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_18\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"19\",\"w\":24,\"x\":0,\"y\":135},\"panelIndex\":\"19\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_19\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":15,\"i\":\"20\",\"w\":24,\"x\":24,\"y\":135},\"panelIndex\":\"20\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_20\"}]","timeRestore":false,"title":"logstash_dashboardwithtime","version":1},"coreMigrationVersion":"7.13.2","id":"154944b0-6249-11eb-aebf-c306684b328d","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"36b91810-6239-11eb-aebf-c306684b328d","name":"1:panel_1","type":"visualization"},{"id":"0a274320-61cc-11eb-aebf-c306684b328d","name":"2:panel_2","type":"visualization"},{"id":"e4aef350-623d-11eb-aebf-c306684b328d","name":"3:panel_3","type":"visualization"},{"id":"f92e5630-623e-11eb-aebf-c306684b328d","name":"4:panel_4","type":"visualization"},{"id":"9853d4d0-623d-11eb-aebf-c306684b328d","name":"5:panel_5","type":"visualization"},{"id":"6ecb33b0-623d-11eb-aebf-c306684b328d","name":"6:panel_6","type":"visualization"},{"id":"b8e35c80-623c-11eb-aebf-c306684b328d","name":"7:panel_7","type":"visualization"},{"id":"f1bc75d0-6239-11eb-aebf-c306684b328d","name":"8:panel_8","type":"visualization"},{"id":"0d8a8860-623a-11eb-aebf-c306684b328d","name":"9:panel_9","type":"visualization"},{"id":"d79fe3d0-6239-11eb-aebf-c306684b328d","name":"10:panel_10","type":"visualization"},{"id":"318375a0-6240-11eb-aebf-c306684b328d","name":"11:panel_11","type":"visualization"},{"id":"e461eb20-6245-11eb-aebf-c306684b328d","name":"12:panel_12","type":"visualization"},{"id":"25bdc750-6242-11eb-aebf-c306684b328d","name":"13:panel_13","type":"visualization"},{"id":"71dd7bc0-6248-11eb-aebf-c306684b328d","name":"14:panel_14","type":"visualization"},{"id":"6aea48a0-6240-11eb-aebf-c306684b328d","name":"15:panel_15","type":"visualization"},{"id":"32b681f0-6241-11eb-aebf-c306684b328d","name":"16:panel_16","type":"visualization"},{"id":"ccca99e0-6244-11eb-aebf-c306684b328d","name":"17:panel_17","type":"visualization"},{"id":"a4d7be80-6245-11eb-aebf-c306684b328d","name":"18:panel_18","type":"visualization"},{"id":"c94d8440-6248-11eb-aebf-c306684b328d","name":"19:panel_19","type":"visualization"},{"id":"db6226f0-61c0-11eb-aebf-c306684b328d","name":"20:panel_20","type":"search"}],"sort":[1623693556928,515],"type":"dashboard","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NzAsNF0="} -{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"26e2cf99-d931-4320-9e15-9dbc148f3534":{"columnOrder":["6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e","beb72af1-239c-46d8-823b-b00d1e2ace43"],"columns":{"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e":{"dataType":"string","isBucketed":true,"label":"Top values of url.raw","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"beb72af1-239c-46d8-823b-b00d1e2ace43","type":"column"},"orderDirection":"desc","otherBucket":true,"size":20},"scale":"ordinal","sourceField":"url.raw"},"beb72af1-239c-46d8-823b-b00d1e2ace43":{"dataType":"number","isBucketed":false,"label":"Unique count of geo.srcdest","operationType":"unique_count","scale":"ratio","sourceField":"geo.srcdest"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"layers":[{"categoryDisplay":"default","groups":["6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e","6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e","6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e"],"layerId":"26e2cf99-d931-4320-9e15-9dbc148f3534","legendDisplay":"default","metric":"beb72af1-239c-46d8-823b-b00d1e2ace43","nestedLegend":false,"numberDisplay":"percent"}],"shape":"donut"}},"title":"lens_pie_chart","visualizationType":"lnsPie"},"coreMigrationVersion":"7.13.2","id":"21905950-bd9f-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-26e2cf99-d931-4320-9e15-9dbc148f3534","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,519],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NzEsNF0="} -{"attributes":{"description":null,"state":{"datasourceStates":{"indexpattern":{"layers":{"a3ac0e3d-63ec-49b2-882a-b34680a967ba":{"columnOrder":["352a2c02-aa6f-4a35-b776-45c3715a6c5e","8ef68cbb-e039-49d6-b15e-be81559f4b55","14fad6b1-6a7c-4ae8-ae4b-d9569e31e04a"],"columns":{"14fad6b1-6a7c-4ae8-ae4b-d9569e31e04a":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"},"352a2c02-aa6f-4a35-b776-45c3715a6c5e":{"dataType":"string","isBucketed":true,"label":"Top values of geo.srcdest","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"14fad6b1-6a7c-4ae8-ae4b-d9569e31e04a","type":"column"},"orderDirection":"desc","otherBucket":true,"size":67},"scale":"ordinal","sourceField":"geo.srcdest"},"8ef68cbb-e039-49d6-b15e-be81559f4b55":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["14fad6b1-6a7c-4ae8-ae4b-d9569e31e04a"],"layerId":"a3ac0e3d-63ec-49b2-882a-b34680a967ba","position":"top","seriesType":"bar_percentage_stacked","showGridlines":false,"splitAccessor":"352a2c02-aa6f-4a35-b776-45c3715a6c5e","xAccessor":"8ef68cbb-e039-49d6-b15e-be81559f4b55"}],"legend":{"isVisible":true,"position":"top","showSingleSeries":true},"preferredSeriesType":"bar_percentage_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_bar_verticalpercentage","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.2","id":"aa4b8da0-bd9f-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-a3ac0e3d-63ec-49b2-882a-b34680a967ba","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,523],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NzIsNF0="} -{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"037b7937-790b-4d2d-94a5-7f5837a6ef05":{"columnOrder":["b3d46616-75e0-419e-97ea-91148961ef94","025a0fb3-dc44-4f5c-b517-2d71d3f26f14","c476db14-0cc1-40ec-863e-d2779256a407"],"columns":{"025a0fb3-dc44-4f5c-b517-2d71d3f26f14":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"},"b3d46616-75e0-419e-97ea-91148961ef94":{"dataType":"string","isBucketed":true,"label":"Top values of geo.srcdest","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"c476db14-0cc1-40ec-863e-d2779256a407","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"geo.srcdest"},"c476db14-0cc1-40ec-863e-d2779256a407":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"lucene","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["c476db14-0cc1-40ec-863e-d2779256a407"],"layerId":"037b7937-790b-4d2d-94a5-7f5837a6ef05","position":"top","seriesType":"bar_stacked","showGridlines":false,"splitAccessor":"b3d46616-75e0-419e-97ea-91148961ef94","xAccessor":"025a0fb3-dc44-4f5c-b517-2d71d3f26f14"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_barchart_vertical","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.2","id":"2d3f1250-bd9f-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-037b7937-790b-4d2d-94a5-7f5837a6ef05","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,527],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NzMsNF0="} -{"attributes":{"description":null,"state":{"datasourceStates":{"indexpattern":{"layers":{"212688dc-e7d7-4875-a221-09e6191bdcf7":{"columnOrder":["05410186-83c4-460a-82bf-dd7e9d998c9f","e8659feb-1db4-4706-9147-ac1fd513a1ba","c9a32fd0-a465-44fb-8adc-b957fb72cad5"],"columns":{"05410186-83c4-460a-82bf-dd7e9d998c9f":{"dataType":"string","isBucketed":true,"label":"Top values of extension.raw","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"c9a32fd0-a465-44fb-8adc-b957fb72cad5","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"extension.raw"},"c9a32fd0-a465-44fb-8adc-b957fb72cad5":{"dataType":"number","isBucketed":false,"label":"Average of bytes","operationType":"average","scale":"ratio","sourceField":"bytes"},"e8659feb-1db4-4706-9147-ac1fd513a1ba":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["c9a32fd0-a465-44fb-8adc-b957fb72cad5"],"layerId":"212688dc-e7d7-4875-a221-09e6191bdcf7","position":"top","seriesType":"bar_horizontal_stacked","showGridlines":false,"splitAccessor":"05410186-83c4-460a-82bf-dd7e9d998c9f","xAccessor":"e8659feb-1db4-4706-9147-ac1fd513a1ba"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_horizontal_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_barhorizontal_stacked","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.2","id":"edd5a560-bda4-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-212688dc-e7d7-4875-a221-09e6191bdcf7","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,531],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NzQsNF0="} -{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"7ab04fd4-04da-4023-8899-d94620193607":{"columnOrder":["0ab2d5f8-11f0-4b25-b8bb-3127a3b8d4c7","9eb851dd-31f6-481a-84d1-9ecce53a6ad2","f6b271a7-509b-4c37-b7b6-ac5be4bcb49a"],"columns":{"0ab2d5f8-11f0-4b25-b8bb-3127a3b8d4c7":{"dataType":"string","isBucketed":true,"label":"Top values of request.raw","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"f6b271a7-509b-4c37-b7b6-ac5be4bcb49a","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"request.raw"},"9eb851dd-31f6-481a-84d1-9ecce53a6ad2":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"},"f6b271a7-509b-4c37-b7b6-ac5be4bcb49a":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["f6b271a7-509b-4c37-b7b6-ac5be4bcb49a"],"layerId":"7ab04fd4-04da-4023-8899-d94620193607","position":"top","seriesType":"bar_horizontal_percentage_stacked","showGridlines":false,"splitAccessor":"0ab2d5f8-11f0-4b25-b8bb-3127a3b8d4c7","xAccessor":"9eb851dd-31f6-481a-84d1-9ecce53a6ad2"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_horizontal_percentage_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_barhorizontalpercentage","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.2","id":"2c25a450-bda5-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-7ab04fd4-04da-4023-8899-d94620193607","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,535],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NzUsNF0="} -{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"037b7937-790b-4d2d-94a5-7f5837a6ef05":{"columnOrder":["b3d46616-75e0-419e-97ea-91148961ef94","025a0fb3-dc44-4f5c-b517-2d71d3f26f14","c476db14-0cc1-40ec-863e-d2779256a407"],"columns":{"025a0fb3-dc44-4f5c-b517-2d71d3f26f14":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"},"b3d46616-75e0-419e-97ea-91148961ef94":{"dataType":"string","isBucketed":true,"label":"Top values of geo.srcdest","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"c476db14-0cc1-40ec-863e-d2779256a407","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"geo.srcdest"},"c476db14-0cc1-40ec-863e-d2779256a407":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"lucene","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["c476db14-0cc1-40ec-863e-d2779256a407"],"layerId":"037b7937-790b-4d2d-94a5-7f5837a6ef05","position":"top","seriesType":"bar_stacked","showGridlines":false,"splitAccessor":"b3d46616-75e0-419e-97ea-91148961ef94","xAccessor":"025a0fb3-dc44-4f5c-b517-2d71d3f26f14"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_visualization","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.2","id":"e79116e0-bd9e-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-037b7937-790b-4d2d-94a5-7f5837a6ef05","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693677171,767],"type":"lens","updated_at":"2021-06-14T18:01:17.171Z","version":"WzE3NDEsNF0="} -{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"72783e5f-aa7b-4b8a-b26c-a3e4d051340e":{"columnOrder":["0f619652-9ff1-453b-ae1f-7371baa82f55"],"columns":{"0f619652-9ff1-453b-ae1f-7371baa82f55":{"dataType":"number","isBucketed":false,"label":"Average of phpmemory","operationType":"average","params":{"format":{"id":"percent","params":{"decimals":10}}},"scale":"ratio","sourceField":"phpmemory"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"accessor":"0f619652-9ff1-453b-ae1f-7371baa82f55","layerId":"72783e5f-aa7b-4b8a-b26c-a3e4d051340e"}},"title":"lens_metric","visualizationType":"lnsMetric"},"coreMigrationVersion":"7.13.2","id":"974fb950-bda5-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-72783e5f-aa7b-4b8a-b26c-a3e4d051340e","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,543],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NzcsNF0="} -{"attributes":{"description":null,"state":{"datasourceStates":{"indexpattern":{"layers":{"bb478774-f9e8-4380-bf3a-f4a89a4d79b5":{"columnOrder":["4573ae8f-8f9d-4918-b496-c08f7102c6e1","cebdc6c5-3587-4f57-879c-dd63ea99cf03"],"columns":{"4573ae8f-8f9d-4918-b496-c08f7102c6e1":{"dataType":"string","isBucketed":true,"label":"Top values of machine.os.raw","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"cebdc6c5-3587-4f57-879c-dd63ea99cf03","type":"column"},"orderDirection":"desc","otherBucket":true,"size":5},"scale":"ordinal","sourceField":"machine.os.raw"},"cebdc6c5-3587-4f57-879c-dd63ea99cf03":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"layers":[{"categoryDisplay":"default","groups":["4573ae8f-8f9d-4918-b496-c08f7102c6e1"],"layerId":"bb478774-f9e8-4380-bf3a-f4a89a4d79b5","legendDisplay":"default","metric":"cebdc6c5-3587-4f57-879c-dd63ea99cf03","nestedLegend":false,"numberDisplay":"percent"}],"shape":"pie"}},"title":"lens_piechart","visualizationType":"lnsPie"},"coreMigrationVersion":"7.13.2","id":"51b63040-bda5-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-bb478774-f9e8-4380-bf3a-f4a89a4d79b5","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,547],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NzgsNF0="} -{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"a1b85651-db29-441f-8f08-cf1b9b6f7bf1":{"columnOrder":["2b3bdc32-0be0-49dc-993d-4630b0bd1185","b85cc0a7-0b18-4b08-b7f0-c617f80cf903","03203126-8286-444d-b5b3-4f399eaf2c26","44305317-61e8-4600-9f3c-ac4070e0c529"],"columns":{"03203126-8286-444d-b5b3-4f399eaf2c26":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"},"2b3bdc32-0be0-49dc-993d-4630b0bd1185":{"dataType":"string","isBucketed":true,"label":"Top values of extension.raw","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"44305317-61e8-4600-9f3c-ac4070e0c529","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"extension.raw"},"44305317-61e8-4600-9f3c-ac4070e0c529":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"},"b85cc0a7-0b18-4b08-b7f0-c617f80cf903":{"dataType":"string","isBucketed":true,"label":"Top values of machine.os.raw","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"44305317-61e8-4600-9f3c-ac4070e0c529","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"machine.os.raw"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"columns":[{"columnId":"2b3bdc32-0be0-49dc-993d-4630b0bd1185","isTransposed":false},{"columnId":"b85cc0a7-0b18-4b08-b7f0-c617f80cf903","isTransposed":false},{"columnId":"03203126-8286-444d-b5b3-4f399eaf2c26","isTransposed":false},{"columnId":"44305317-61e8-4600-9f3c-ac4070e0c529","isTransposed":false}],"layerId":"a1b85651-db29-441f-8f08-cf1b9b6f7bf1"}},"title":"lens_table","visualizationType":"lnsDatatable"},"coreMigrationVersion":"7.13.2","id":"b00679c0-bda5-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-a1b85651-db29-441f-8f08-cf1b9b6f7bf1","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,551],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2NzksNF0="} -{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"4fbb51e9-1f99-4b5e-b59d-60fcb547b1d9":{"columnOrder":["08a1af05-743d-480e-9056-3405b1bdda7d","bae35990-75c2-487f-94eb-d8e03d2eda33"],"columns":{"08a1af05-743d-480e-9056-3405b1bdda7d":{"dataType":"string","isBucketed":true,"label":"Top values of geo.srcdest","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"bae35990-75c2-487f-94eb-d8e03d2eda33","type":"column"},"orderDirection":"desc","otherBucket":true,"size":25},"scale":"ordinal","sourceField":"geo.srcdest"},"bae35990-75c2-487f-94eb-d8e03d2eda33":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"layers":[{"categoryDisplay":"default","groups":["08a1af05-743d-480e-9056-3405b1bdda7d","08a1af05-743d-480e-9056-3405b1bdda7d","08a1af05-743d-480e-9056-3405b1bdda7d"],"layerId":"4fbb51e9-1f99-4b5e-b59d-60fcb547b1d9","legendDisplay":"default","metric":"bae35990-75c2-487f-94eb-d8e03d2eda33","nestedLegend":false,"numberDisplay":"percent"}],"shape":"treemap"}},"title":"lens_treemap","visualizationType":"lnsPie"},"coreMigrationVersion":"7.13.2","id":"652ade10-bd9f-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-4fbb51e9-1f99-4b5e-b59d-60fcb547b1d9","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,555],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2ODAsNF0="} -{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"e84503c1-4dbd-4ac6-9ac9-ad938654680f":{"columnOrder":["38c73fd4-6330-4162-8a7b-1a059f005da8","e8d4dad2-ac30-4741-aca0-904eb1fc8455","70433aa7-3c2c-4e6c-b8cf-4218c995cff5"],"columns":{"38c73fd4-6330-4162-8a7b-1a059f005da8":{"dataType":"string","isBucketed":true,"label":"Top values of url.raw","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"70433aa7-3c2c-4e6c-b8cf-4218c995cff5","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"url.raw"},"70433aa7-3c2c-4e6c-b8cf-4218c995cff5":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"},"e8d4dad2-ac30-4741-aca0-904eb1fc8455":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["70433aa7-3c2c-4e6c-b8cf-4218c995cff5"],"layerId":"e84503c1-4dbd-4ac6-9ac9-ad938654680f","position":"top","seriesType":"line","showGridlines":false,"splitAccessor":"38c73fd4-6330-4162-8a7b-1a059f005da8","xAccessor":"e8d4dad2-ac30-4741-aca0-904eb1fc8455"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"line","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_line_chart","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.2","id":"7f3b5fb0-be2f-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-e84503c1-4dbd-4ac6-9ac9-ad938654680f","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,559],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2ODEsNF0="} -{"attributes":{"fieldAttrs":"{\"speaker\":{\"count\":1},\"text_entry\":{\"count\":6},\"type\":{\"count\":3}}","fields":"[]","runtimeFieldMap":"{}","title":"shakespeare"},"coreMigrationVersion":"7.13.2","id":"4e937b20-619d-11eb-aebf-c306684b328d","migrationVersion":{"index-pattern":"7.11.0"},"references":[],"sort":[1623693556928,560],"type":"index-pattern","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2ODIsNF0="} -{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"d35680ce-c285-4fae-89d6-1245671bbc78":{"columnOrder":["2bcbffbe-c24d-4e74-8a03-9a6da7db70c0","6b00fde6-bfaa-4da1-beeb-bfd85a4cb2ff","8319857d-a03b-4158-bdf1-2a788e510445"],"columns":{"2bcbffbe-c24d-4e74-8a03-9a6da7db70c0":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"},"6b00fde6-bfaa-4da1-beeb-bfd85a4cb2ff":{"dataType":"number","isBucketed":false,"label":"Average of bytes","operationType":"average","scale":"ratio","sourceField":"bytes"},"8319857d-a03b-4158-bdf1-2a788e510445":{"dataType":"number","isBucketed":false,"label":"Sum of bytes_scripted","operationType":"sum","params":{"format":{"id":"number","params":{"decimals":2}}},"scale":"ratio","sourceField":"bytes_scripted"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["6b00fde6-bfaa-4da1-beeb-bfd85a4cb2ff","8319857d-a03b-4158-bdf1-2a788e510445"],"layerId":"d35680ce-c285-4fae-89d6-1245671bbc78","position":"top","seriesType":"area","showGridlines":false,"xAccessor":"2bcbffbe-c24d-4e74-8a03-9a6da7db70c0","yConfig":[{"axisMode":"auto","forAccessor":"8319857d-a03b-4158-bdf1-2a788e510445"}]}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"area","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_area_chart","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.2","id":"bb9e5bb0-be2f-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-d35680ce-c285-4fae-89d6-1245671bbc78","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,564],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2ODMsNF0="} -{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"70bd567e-8e67-4696-a406-313b06344fa9":{"columnOrder":["96ddedfb-043b-479e-a746-600e72ab546e","d325b7da-4266-4035-9b13-5f853615149a","2fc1391b-17d1-4c49-9ddc-06ff307e3520","1cc6f19c-cbcb-4abd-b56d-1a2f9deae5f3"],"columns":{"1cc6f19c-cbcb-4abd-b56d-1a2f9deae5f3":{"dataType":"number","isBucketed":false,"label":"Average of machine.ram","operationType":"average","scale":"ratio","sourceField":"machine.ram"},"2fc1391b-17d1-4c49-9ddc-06ff307e3520":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"},"96ddedfb-043b-479e-a746-600e72ab546e":{"dataType":"string","isBucketed":true,"label":"Top values of machine.os.raw","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"2fc1391b-17d1-4c49-9ddc-06ff307e3520","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"machine.os.raw"},"d325b7da-4266-4035-9b13-5f853615149a":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["2fc1391b-17d1-4c49-9ddc-06ff307e3520","1cc6f19c-cbcb-4abd-b56d-1a2f9deae5f3"],"layerId":"70bd567e-8e67-4696-a406-313b06344fa9","position":"top","seriesType":"area_stacked","showGridlines":false,"splitAccessor":"96ddedfb-043b-479e-a746-600e72ab546e","xAccessor":"d325b7da-4266-4035-9b13-5f853615149a"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"area_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_area_stacked","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.2","id":"dd315430-be2f-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-70bd567e-8e67-4696-a406-313b06344fa9","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,568],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2ODQsNF0="} -{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"2e80716f-c1b6-46f2-be2b-35db744b5031\"},\"panelIndex\":\"2e80716f-c1b6-46f2-be2b-35db744b5031\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsPie\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"26e2cf99-d931-4320-9e15-9dbc148f3534\":{\"columns\":{\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\":{\"label\":\"Top values of url.raw\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"url.raw\",\"isBucketed\":true,\"params\":{\"size\":20,\"orderBy\":{\"type\":\"column\",\"columnId\":\"beb72af1-239c-46d8-823b-b00d1e2ace43\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"beb72af1-239c-46d8-823b-b00d1e2ace43\":{\"label\":\"Unique count of geo.srcdest\",\"dataType\":\"number\",\"operationType\":\"unique_count\",\"scale\":\"ratio\",\"sourceField\":\"geo.srcdest\",\"isBucketed\":false}},\"columnOrder\":[\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"beb72af1-239c-46d8-823b-b00d1e2ace43\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"shape\":\"donut\",\"layers\":[{\"layerId\":\"26e2cf99-d931-4320-9e15-9dbc148f3534\",\"groups\":[\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\"],\"metric\":\"beb72af1-239c-46d8-823b-b00d1e2ace43\",\"numberDisplay\":\"percent\",\"categoryDisplay\":\"default\",\"legendDisplay\":\"default\",\"nestedLegend\":false}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-layer-26e2cf99-d931-4320-9e15-9dbc148f3534\"}]},\"enhancements\":{},\"type\":\"lens\"},\"panelRefName\":\"panel_2e80716f-c1b6-46f2-be2b-35db744b5031\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"da8843e0-6789-4aae-bcd0-81f270538719\"},\"panelIndex\":\"da8843e0-6789-4aae-bcd0-81f270538719\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_da8843e0-6789-4aae-bcd0-81f270538719\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":15,\"w\":24,\"h\":15,\"i\":\"adcd4418-7299-4efa-b369-5f71a7b4ebe0\"},\"panelIndex\":\"adcd4418-7299-4efa-b369-5f71a7b4ebe0\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_adcd4418-7299-4efa-b369-5f71a7b4ebe0\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"869754a7-edf0-478f-a7f1-80374f63108a\"},\"panelIndex\":\"869754a7-edf0-478f-a7f1-80374f63108a\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_869754a7-edf0-478f-a7f1-80374f63108a\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":30,\"w\":24,\"h\":15,\"i\":\"67111cf4-338e-453f-8621-e8dea64082d1\"},\"panelIndex\":\"67111cf4-338e-453f-8621-e8dea64082d1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_67111cf4-338e-453f-8621-e8dea64082d1\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":30,\"w\":24,\"h\":15,\"i\":\"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\"},\"panelIndex\":\"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":45,\"w\":24,\"h\":15,\"i\":\"88847944-ae1b-45fd-b102-3b45f9bea04b\"},\"panelIndex\":\"88847944-ae1b-45fd-b102-3b45f9bea04b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_88847944-ae1b-45fd-b102-3b45f9bea04b\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":45,\"w\":24,\"h\":15,\"i\":\"5a7924c7-eac0-4573-9199-fecec5b82e9e\"},\"panelIndex\":\"5a7924c7-eac0-4573-9199-fecec5b82e9e\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5a7924c7-eac0-4573-9199-fecec5b82e9e\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":60,\"w\":24,\"h\":15,\"i\":\"f8f49591-f071-4a96-b1ed-cd65daff5648\"},\"panelIndex\":\"f8f49591-f071-4a96-b1ed-cd65daff5648\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_f8f49591-f071-4a96-b1ed-cd65daff5648\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":60,\"w\":24,\"h\":15,\"i\":\"9f357f47-c2a0-421f-a456-9583c40837ab\"},\"panelIndex\":\"9f357f47-c2a0-421f-a456-9583c40837ab\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9f357f47-c2a0-421f-a456-9583c40837ab\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":75,\"w\":24,\"h\":15,\"i\":\"6cb383e9-1e80-44f9-80d5-7b8c585668db\"},\"panelIndex\":\"6cb383e9-1e80-44f9-80d5-7b8c585668db\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6cb383e9-1e80-44f9-80d5-7b8c585668db\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":75,\"w\":24,\"h\":15,\"i\":\"57f5f0bf-6610-4599-aad4-37484640b5e2\"},\"panelIndex\":\"57f5f0bf-6610-4599-aad4-37484640b5e2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_57f5f0bf-6610-4599-aad4-37484640b5e2\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":90,\"w\":24,\"h\":15,\"i\":\"32d3ab66-52e1-44e3-8c1f-1dccff3c5692\"},\"panelIndex\":\"32d3ab66-52e1-44e3-8c1f-1dccff3c5692\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_32d3ab66-52e1-44e3-8c1f-1dccff3c5692\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":90,\"w\":24,\"h\":15,\"i\":\"dd1718fd-74ee-4032-851b-db97e893825d\"},\"panelIndex\":\"dd1718fd-74ee-4032-851b-db97e893825d\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_dd1718fd-74ee-4032-851b-db97e893825d\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":105,\"w\":24,\"h\":15,\"i\":\"98a556ee-078b-4e03-93a8-29996133cdcb\"},\"panelIndex\":\"98a556ee-078b-4e03-93a8-29996133cdcb\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsXY\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"999a2d60-cb2a-451c-8d71-80d7e92e70fd\":{\"columns\":{\"ce9117a2-773c-474c-8fb1-18940cf58b38\":{\"label\":\"Top values of type\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"type\",\"isBucketed\":true,\"params\":{\"size\":5,\"orderBy\":{\"type\":\"column\",\"columnId\":\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\"},\"orderDirection\":\"asc\",\"otherBucket\":true,\"missingBucket\":false}},\"a3d10552-e352-40d0-a156-e86112c0501a\":{\"label\":\"Top values of _type\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"_type\",\"isBucketed\":true,\"params\":{\"size\":3,\"orderBy\":{\"type\":\"column\",\"columnId\":\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"Records\"},\"9c5db2f3-9eb0-4667-9a74-3318301de251\":{\"label\":\"Sum of bytes\",\"dataType\":\"number\",\"operationType\":\"sum\",\"sourceField\":\"bytes\",\"isBucketed\":false,\"scale\":\"ratio\"}},\"columnOrder\":[\"ce9117a2-773c-474c-8fb1-18940cf58b38\",\"a3d10552-e352-40d0-a156-e86112c0501a\",\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\",\"9c5db2f3-9eb0-4667-9a74-3318301de251\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"legend\":{\"isVisible\":true,\"position\":\"right\"},\"valueLabels\":\"hide\",\"fittingFunction\":\"None\",\"axisTitlesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"bar_stacked\",\"layers\":[{\"layerId\":\"999a2d60-cb2a-451c-8d71-80d7e92e70fd\",\"accessors\":[\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\",\"9c5db2f3-9eb0-4667-9a74-3318301de251\"],\"position\":\"top\",\"seriesType\":\"bar_stacked\",\"showGridlines\":false,\"xAccessor\":\"ce9117a2-773c-474c-8fb1-18940cf58b38\",\"splitAccessor\":\"a3d10552-e352-40d0-a156-e86112c0501a\"}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-layer-999a2d60-cb2a-451c-8d71-80d7e92e70fd\"}]},\"enhancements\":{},\"type\":\"lens\"}},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":105,\"w\":24,\"h\":15,\"i\":\"62a0f0b0-3589-4cef-807b-b1b4258b7a9b\"},\"panelIndex\":\"62a0f0b0-3589-4cef-807b-b1b4258b7a9b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_62a0f0b0-3589-4cef-807b-b1b4258b7a9b\"},{\"version\":\"7.13.1\",\"type\":\"map\",\"gridData\":{\"x\":0,\"y\":120,\"w\":24,\"h\":15,\"i\":\"dcc0defa-3376-465c-9b5b-2ba69528848c\"},\"panelIndex\":\"dcc0defa-3376-465c-9b5b-2ba69528848c\",\"embeddableConfig\":{\"mapCenter\":{\"lat\":19.94277,\"lon\":0,\"zoom\":1.56},\"mapBuffer\":{\"minLon\":-210.32666,\"minLat\":-64.8435,\"maxLon\":210.32666,\"maxLat\":95.13806},\"isLayerTOCOpen\":true,\"openTOCDetails\":[],\"hiddenLayers\":[],\"enhancements\":{}},\"panelRefName\":\"panel_dcc0defa-3376-465c-9b5b-2ba69528848c\"}]","refreshInterval":{"pause":true,"value":0},"timeFrom":"2015-09-20T01:56:56.132Z","timeRestore":true,"timeTo":"2015-09-21T11:18:20.471Z","title":"lens_maps_dashboard_logstash","version":1},"coreMigrationVersion":"7.13.2","id":"16d86080-be5c-11eb-9520-1b4c3ca6a781","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"21905950-bd9f-11eb-9520-1b4c3ca6a781","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:panel_2e80716f-c1b6-46f2-be2b-35db744b5031","type":"lens"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:indexpattern-datasource-layer-26e2cf99-d931-4320-9e15-9dbc148f3534","type":"index-pattern"},{"id":"aa4b8da0-bd9f-11eb-9520-1b4c3ca6a781","name":"da8843e0-6789-4aae-bcd0-81f270538719:panel_da8843e0-6789-4aae-bcd0-81f270538719","type":"lens"},{"id":"2d3f1250-bd9f-11eb-9520-1b4c3ca6a781","name":"adcd4418-7299-4efa-b369-5f71a7b4ebe0:panel_adcd4418-7299-4efa-b369-5f71a7b4ebe0","type":"lens"},{"id":"edd5a560-bda4-11eb-9520-1b4c3ca6a781","name":"869754a7-edf0-478f-a7f1-80374f63108a:panel_869754a7-edf0-478f-a7f1-80374f63108a","type":"lens"},{"id":"2c25a450-bda5-11eb-9520-1b4c3ca6a781","name":"67111cf4-338e-453f-8621-e8dea64082d1:panel_67111cf4-338e-453f-8621-e8dea64082d1","type":"lens"},{"id":"e79116e0-bd9e-11eb-9520-1b4c3ca6a781","name":"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d:panel_13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d","type":"lens"},{"id":"974fb950-bda5-11eb-9520-1b4c3ca6a781","name":"88847944-ae1b-45fd-b102-3b45f9bea04b:panel_88847944-ae1b-45fd-b102-3b45f9bea04b","type":"lens"},{"id":"21905950-bd9f-11eb-9520-1b4c3ca6a781","name":"5a7924c7-eac0-4573-9199-fecec5b82e9e:panel_5a7924c7-eac0-4573-9199-fecec5b82e9e","type":"lens"},{"id":"51b63040-bda5-11eb-9520-1b4c3ca6a781","name":"f8f49591-f071-4a96-b1ed-cd65daff5648:panel_f8f49591-f071-4a96-b1ed-cd65daff5648","type":"lens"},{"id":"b00679c0-bda5-11eb-9520-1b4c3ca6a781","name":"9f357f47-c2a0-421f-a456-9583c40837ab:panel_9f357f47-c2a0-421f-a456-9583c40837ab","type":"lens"},{"id":"652ade10-bd9f-11eb-9520-1b4c3ca6a781","name":"6cb383e9-1e80-44f9-80d5-7b8c585668db:panel_6cb383e9-1e80-44f9-80d5-7b8c585668db","type":"lens"},{"id":"7f3b5fb0-be2f-11eb-9520-1b4c3ca6a781","name":"57f5f0bf-6610-4599-aad4-37484640b5e2:panel_57f5f0bf-6610-4599-aad4-37484640b5e2","type":"lens"},{"id":"bb9e5bb0-be2f-11eb-9520-1b4c3ca6a781","name":"32d3ab66-52e1-44e3-8c1f-1dccff3c5692:panel_32d3ab66-52e1-44e3-8c1f-1dccff3c5692","type":"lens"},{"id":"dd315430-be2f-11eb-9520-1b4c3ca6a781","name":"dd1718fd-74ee-4032-851b-db97e893825d:panel_dd1718fd-74ee-4032-851b-db97e893825d","type":"lens"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"98a556ee-078b-4e03-93a8-29996133cdcb:indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"98a556ee-078b-4e03-93a8-29996133cdcb:indexpattern-datasource-layer-999a2d60-cb2a-451c-8d71-80d7e92e70fd","type":"index-pattern"},{"id":"0dbbf8b0-be3c-11eb-9520-1b4c3ca6a781","name":"62a0f0b0-3589-4cef-807b-b1b4258b7a9b:panel_62a0f0b0-3589-4cef-807b-b1b4258b7a9b","type":"lens"},{"id":"0c5974f0-be5c-11eb-9520-1b4c3ca6a781","name":"dcc0defa-3376-465c-9b5b-2ba69528848c:panel_dcc0defa-3376-465c-9b5b-2ba69528848c","type":"map"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,590],"type":"dashboard","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2ODUsNF0="} -{"attributes":{"fieldAttrs":"{}","fields":"[]","runtimeFieldMap":"{}","title":".kibana"},"coreMigrationVersion":"7.13.2","id":"1773aa90-be66-11eb-9520-1b4c3ca6a781","migrationVersion":{"index-pattern":"7.11.0"},"references":[],"sort":[1623693556928,591],"type":"index-pattern","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2ODYsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"shakespeare_areachart","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"shakespeare_areachart\",\"type\":\"histogram\",\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"mode\":\"stacked\",\"type\":\"histogram\",\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"data\":{\"id\":\"2\",\"label\":\"Count\"},\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"aggs\":[{\"id\":\"2\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"play_name\",\"size\":20,\"order\":\"desc\",\"orderBy\":\"2\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"play_name\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"2\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.2","id":"185283c0-619e-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,593],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2ODcsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"by_reference_logstash","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"by_reference_logstash\",\"type\":\"histogram\",\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{},\"style\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"},\"style\":{}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"radiusRatio\":0,\"addTooltip\":true,\"detailedTooltip\":true,\"palette\":{\"type\":\"palette\",\"name\":\"default\"},\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":false},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"2014-07-15T12:33:21.084Z\",\"to\":\"2019-01-28T03:18:12.440Z\"},\"useNormalizedEsInterval\":true,\"scaleMetricValues\":false,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}},\"schema\":\"segment\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"response.raw\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"group\"}]}"},"coreMigrationVersion":"7.13.2","id":"1885abb0-ca2b-11eb-bf5e-3de94e83d4f0","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623415891791,140],"type":"visualization","updated_at":"2021-06-11T12:51:31.791Z","version":"WzE0MzgsNF0="} -{"attributes":{"color":"#f44fcf","description":"","name":"shakespeare"},"coreMigrationVersion":"7.13.2","id":"42b4cec0-be32-11eb-9520-1b4c3ca6a781","references":[],"sort":[1623693556928,594],"type":"tag","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2ODgsNF0="} -{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"3338dd55-4007-4be5-908d-25722b6174cb":{"columnOrder":["6c83b0c2-5834-4619-888c-9e8a08e47d42","b25e7497-c188-4c25-b002-1fd5bd69e76d"],"columns":{"6c83b0c2-5834-4619-888c-9e8a08e47d42":{"dataType":"string","isBucketed":true,"label":"Top values of speaker","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"b25e7497-c188-4c25-b002-1fd5bd69e76d","type":"column"},"orderDirection":"desc","otherBucket":false,"size":90},"scale":"ordinal","sourceField":"speaker"},"b25e7497-c188-4c25-b002-1fd5bd69e76d":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"layers":[{"categoryDisplay":"default","groups":["6c83b0c2-5834-4619-888c-9e8a08e47d42","6c83b0c2-5834-4619-888c-9e8a08e47d42","6c83b0c2-5834-4619-888c-9e8a08e47d42"],"layerId":"3338dd55-4007-4be5-908d-25722b6174cb","legendDisplay":"default","metric":"b25e7497-c188-4c25-b002-1fd5bd69e76d","nestedLegend":false,"numberDisplay":"percent"}],"palette":{"name":"complimentary","type":"palette"},"shape":"treemap"}},"title":"lens_shakespeare_treemap","visualizationType":"lnsPie"},"coreMigrationVersion":"7.13.2","id":"31e9f2f0-be32-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-3338dd55-4007-4be5-908d-25722b6174cb","type":"index-pattern"},{"id":"42b4cec0-be32-11eb-9520-1b4c3ca6a781","name":"tag-ref-42b4cec0-be32-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,598],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2ODksNF0="} -{"attributes":{"accessCount":0,"accessDate":1622059178542,"createDate":1622059178542,"url":"/app/dashboards#/view/73398a90-619e-11eb-aebf-c306684b328d?embed=true&_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:%272015-09-20T01:56:56.132Z%27,to:%272015-09-21T11:18:20.471Z%27))&_a=(description:%27%27,filters:!(),fullScreenMode:!f,options:(darkTheme:!f,hidePanelTitles:!f,useMargins:!t),panels:!((embeddableConfig:(enhancements:()),gridData:(h:15,i:%271%27,w:24,x:0,y:0),id:%27185283c0-619e-11eb-aebf-c306684b328d%27,panelIndex:%271%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%272%27,w:24,x:24,y:0),id:%2733736660-619e-11eb-aebf-c306684b328d%27,panelIndex:%272%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%273%27,w:24,x:0,y:15),id:%27622ac7f0-619e-11eb-aebf-c306684b328d%27,panelIndex:%273%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%274%27,w:24,x:24,y:15),id:%27712ebbe0-619d-11eb-aebf-c306684b328d%27,panelIndex:%274%27,type:search,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%275%27,w:24,x:0,y:30),id:ddacc820-619d-11eb-aebf-c306684b328d,panelIndex:%275%27,type:search,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%276%27,w:24,x:24,y:30),id:f852d570-619d-11eb-aebf-c306684b328d,panelIndex:%276%27,type:search,version:%277.13.1%27)),query:(language:kuery,query:%27%27),tags:!(),timeRestore:!f,title:shakespeare_dashboard,viewMode:view)"},"coreMigrationVersion":"7.13.2","id":"32a03249ec3a048108d4b5a427a37fc8","references":[],"sort":[1623693556928,599],"type":"url","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2OTAsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"shakespeare_piechart","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"shakespeare_piechart\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":false,\"labels\":{\"show\":true,\"values\":true,\"last_level\":true,\"truncate\":100}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"play_name\",\"size\":15,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.2","id":"33736660-619e-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,601],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2OTEsNF0="} -{"attributes":{"color":"#7b01cf","description":"","name":"By reference"},"coreMigrationVersion":"7.13.2","id":"39d2c190-ca2b-11eb-bf5e-3de94e83d4f0","references":[],"sort":[1623415891791,147],"type":"tag","updated_at":"2021-06-11T12:51:31.791Z","version":"WzE0NDIsNF0="} -{"attributes":{"fieldAttrs":"{}","fields":"[]","runtimeFieldMap":"{}","title":"shakespeare"},"coreMigrationVersion":"7.13.2","id":"39d52f60-ca27-11eb-bf5e-3de94e83d4f0","migrationVersion":{"index-pattern":"7.11.0"},"references":[],"sort":[1623415891791,148],"type":"index-pattern","updated_at":"2021-06-11T12:51:31.791Z","version":"WzE0NDMsNF0="} -{"attributes":{"columns":[],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[{\"meta\":{\"alias\":null,\"negate\":false,\"disabled\":false,\"type\":\"phrase\",\"key\":\"ip\",\"params\":{\"query\":\"57.237.11.219\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match_phrase\":{\"ip\":\"57.237.11.219\"}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["@timestamp","desc"]],"title":"drilldown_saved_search","version":1},"coreMigrationVersion":"7.13.2","id":"4acce030-ca2a-11eb-bf5e-3de94e83d4f0","migrationVersion":{"search":"7.9.3"},"references":[{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"}],"sort":[1623415891791,151],"type":"search","updated_at":"2021-06-11T12:51:31.791Z","version":"WzE0NDQsNF0="} -{"attributes":{"description":"","layerListJSON":"[{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"isAutoSelect\":true},\"id\":\"e0d51731-2bb3-4fed-92af-65f93c3e7e58\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"style\":{\"type\":\"TILE\"},\"type\":\"VECTOR_TILE\"},{\"sourceDescriptor\":{\"geoField\":\"geo.coordinates\",\"filterByMapBounds\":true,\"scalingType\":\"CLUSTERS\",\"topHitsSplitField\":\"\",\"topHitsSize\":1,\"id\":\"142e0a6b-53c9-4f66-a65d-fced755318de\",\"type\":\"ES_SEARCH\",\"applyGlobalQuery\":true,\"applyGlobalTime\":true,\"tooltipProperties\":[],\"sortField\":\"\",\"sortOrder\":\"desc\",\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"id\":\"ca96ce4a-4e73-46a5-bcc8-99a39d227030\",\"label\":null,\"minZoom\":9,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}},\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#CA8EAE\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#934193\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":6}},\"iconOrientation\":{\"type\":\"STATIC\",\"options\":{\"orientation\":0}},\"labelText\":{\"type\":\"STATIC\",\"options\":{\"value\":\"\"}},\"labelColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#000000\"}},\"labelSize\":{\"type\":\"STATIC\",\"options\":{\"size\":14}},\"labelBorderColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"labelBorderSize\":{\"options\":{\"size\":\"SMALL\"}}},\"isTimeAware\":true},\"type\":\"BLENDED_VECTOR\",\"joins\":[]}]","mapStateJSON":"{\"zoom\":1.38,\"center\":{\"lon\":0,\"lat\":19.94277},\"timeFilters\":{\"from\":\"2014-07-15T12:33:21.084Z\",\"to\":\"2019-01-28T03:18:12.440Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":0},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"settings\":{\"autoFitToDataBounds\":false,\"backgroundColor\":\"#ffffff\",\"disableInteractive\":false,\"disableTooltipControl\":false,\"hideToolbarOverlay\":false,\"hideLayerControl\":false,\"hideViewControl\":false,\"initialLocation\":\"LAST_SAVED_LOCATION\",\"fixedLocation\":{\"lat\":0,\"lon\":0,\"zoom\":2},\"browserLocation\":{\"zoom\":2},\"maxZoom\":24,\"minZoom\":0,\"showScaleControl\":false,\"showSpatialFilters\":true,\"spatialFiltersAlpa\":0.3,\"spatialFiltersFillColor\":\"#DA8B45\",\"spatialFiltersLineColor\":\"#DA8B45\"}}","title":"Logstash_map_by_reference","uiStateJSON":"{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}"},"coreMigrationVersion":"7.13.2","id":"a53a2db0-ca2b-11eb-bf5e-3de94e83d4f0","migrationVersion":{"map":"7.12.0"},"references":[{"id":"39d2c190-ca2b-11eb-bf5e-3de94e83d4f0","name":"tag-ref-39d2c190-ca2b-11eb-bf5e-3de94e83d4f0","type":"tag"},{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"layer_1_source_index_pattern","type":"index-pattern"}],"sort":[1623415891791,154],"type":"map","updated_at":"2021-06-11T12:51:31.791Z","version":"WzE0NDUsNF0="} -{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.12.1\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"7c29a321-2a9a-412b-9ed1-1d0a1f66ea63\"},\"panelIndex\":\"7c29a321-2a9a-412b-9ed1-1d0a1f66ea63\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_0\"},{\"version\":\"7.12.1\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"f2d1feb1-d807-46b1-90ac-96d4a9edb6b1\"},\"panelIndex\":\"f2d1feb1-d807-46b1-90ac-96d4a9edb6b1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.12.1\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"a3530107-8b1c-4e94-8f99-e239fa40a09c\"},\"panelIndex\":\"a3530107-8b1c-4e94-8f99-e239fa40a09c\",\"embeddableConfig\":{\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"b14188e0-53d6-433e-874f-b1be7c97487c\",\"triggers\":[\"FILTER_TRIGGER\"],\"action\":{\"name\":\"by_reference_going_to_value\",\"config\":{\"useCurrentFilters\":true,\"useCurrentDateRange\":true},\"factoryId\":\"DASHBOARD_TO_DASHBOARD_DRILLDOWN\"}},{\"eventId\":\"60cba413-1793-4dd3-b072-9d53655d5522\",\"triggers\":[\"SELECT_RANGE_TRIGGER\"],\"action\":{\"name\":\"Goto_Discover\",\"config\":{\"url\":{\"template\":\"http://localhost:5601/app/discover#/view/b3288100-ca2c-11eb-bf5e-3de94e83d4f0?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15y,to:now))&_a=(columns:!(),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'43fcac20-ca27-11eb-bf5e-3de94e83d4f0',key:geo.dest,negate:!f,params:(query:US),type:phrase),query:(match_phrase:(geo.dest:US)))),index:'43fcac20-ca27-11eb-bf5e-3de94e83d4f0',interval:auto,query:(language:kuery,query:''),sort:!(!('@timestamp',desc)))\"},\"openInNewTab\":true,\"encodeUrl\":true},\"factoryId\":\"URL_DRILLDOWN\"}}]}}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.12.1\",\"gridData\":{\"x\":24,\"y\":15,\"w\":24,\"h\":15,\"i\":\"77245314-9495-4625-9f53-0946150e26d4\"},\"panelIndex\":\"77245314-9495-4625-9f53-0946150e26d4\",\"embeddableConfig\":{\"mapCenter\":{\"lat\":19.94277,\"lon\":0,\"zoom\":1.38},\"mapBuffer\":{\"minLon\":-214.7723,\"minLat\":-74.644155,\"maxLon\":214.7723,\"maxLat\":102.864625},\"isLayerTOCOpen\":true,\"openTOCDetails\":[],\"hiddenLayers\":[],\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"9b61b9d4-20a3-4bca-9697-1097c524a943\",\"triggers\":[\"FILTER_TRIGGER\"],\"action\":{\"name\":\"By_reference_to_value\",\"config\":{\"useCurrentFilters\":true,\"useCurrentDateRange\":true},\"factoryId\":\"DASHBOARD_TO_DASHBOARD_DRILLDOWN\"}}]}}},\"panelRefName\":\"panel_3\"}]","timeRestore":false,"title":"by_reference_drilldown","version":1},"coreMigrationVersion":"7.13.2","id":"3b844220-ca2b-11eb-bf5e-3de94e83d4f0","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"08dec860-ca29-11eb-bf5e-3de94e83d4f0","name":"drilldown:DASHBOARD_TO_DASHBOARD_DRILLDOWN:b14188e0-53d6-433e-874f-b1be7c97487c:dashboardId","type":"dashboard"},{"id":"35ce3b30-ca29-11eb-bf5e-3de94e83d4f0","name":"drilldown:DASHBOARD_TO_DASHBOARD_DRILLDOWN:9b61b9d4-20a3-4bca-9697-1097c524a943:dashboardId","type":"dashboard"},{"id":"4acce030-ca2a-11eb-bf5e-3de94e83d4f0","name":"panel_0","type":"search"},{"id":"0abce1c0-ca2a-11eb-bf5e-3de94e83d4f0","name":"panel_1","type":"search"},{"id":"1885abb0-ca2b-11eb-bf5e-3de94e83d4f0","name":"panel_2","type":"visualization"},{"id":"a53a2db0-ca2b-11eb-bf5e-3de94e83d4f0","name":"panel_3","type":"map"},{"id":"39d2c190-ca2b-11eb-bf5e-3de94e83d4f0","name":"tag-39d2c190-ca2b-11eb-bf5e-3de94e83d4f0","type":"tag"}],"sort":[1623415891791,162],"type":"dashboard","updated_at":"2021-06-11T12:51:31.791Z","version":"WzE0NDYsNF0="} -{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"a7a8f2fb-066e-4023-9755-821e84560b4a":{"columnOrder":["ee46f645-0af0-4b5d-8ed3-2557c98c9c12","91859a54-9b88-4478-8c80-0779fe165fba","62a4dea1-fab9-45ff-93e0-b99cfff719d5"],"columns":{"62a4dea1-fab9-45ff-93e0-b99cfff719d5":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"},"91859a54-9b88-4478-8c80-0779fe165fba":{"dataType":"string","isBucketed":true,"label":"Top values of play_name","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"62a4dea1-fab9-45ff-93e0-b99cfff719d5","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"play_name"},"ee46f645-0af0-4b5d-8ed3-2557c98c9c12":{"dataType":"string","isBucketed":true,"label":"Top values of speaker","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"62a4dea1-fab9-45ff-93e0-b99cfff719d5","type":"column"},"orderDirection":"desc","otherBucket":true,"size":25},"scale":"ordinal","sourceField":"speaker"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"layers":[{"categoryDisplay":"default","groups":["ee46f645-0af0-4b5d-8ed3-2557c98c9c12","ee46f645-0af0-4b5d-8ed3-2557c98c9c12","ee46f645-0af0-4b5d-8ed3-2557c98c9c12","ee46f645-0af0-4b5d-8ed3-2557c98c9c12","91859a54-9b88-4478-8c80-0779fe165fba"],"layerId":"a7a8f2fb-066e-4023-9755-821e84560b4a","legendDisplay":"default","metric":"62a4dea1-fab9-45ff-93e0-b99cfff719d5","nestedLegend":false,"numberDisplay":"percent"}],"palette":{"name":"kibana_palette","type":"palette"},"shape":"pie"}},"title":"lens_shakespeare_piechart","visualizationType":"lnsPie"},"coreMigrationVersion":"7.13.2","id":"b5bd5050-be31-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-a7a8f2fb-066e-4023-9755-821e84560b4a","type":"index-pattern"},{"id":"42b4cec0-be32-11eb-9520-1b4c3ca6a781","name":"tag-ref-42b4cec0-be32-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,605],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2OTIsNF0="} -{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"c4b1daae-a3af-4136-969e-8485d4ba53f9\"},\"panelIndex\":\"c4b1daae-a3af-4136-969e-8485d4ba53f9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_c4b1daae-a3af-4136-969e-8485d4ba53f9\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"f092b002-182e-49b8-bcc4-58f5233e041b\"},\"panelIndex\":\"f092b002-182e-49b8-bcc4-58f5233e041b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_f092b002-182e-49b8-bcc4-58f5233e041b\"}]","refreshInterval":{"pause":true,"value":0},"timeFrom":"2015-09-20T01:56:56.132Z","timeRestore":true,"timeTo":"2015-09-21T11:18:20.471Z","title":"lens_shakespeare_dashboard","version":1},"coreMigrationVersion":"7.13.2","id":"43fae350-be32-11eb-9520-1b4c3ca6a781","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"b5bd5050-be31-11eb-9520-1b4c3ca6a781","name":"c4b1daae-a3af-4136-969e-8485d4ba53f9:panel_c4b1daae-a3af-4136-969e-8485d4ba53f9","type":"lens"},{"id":"31e9f2f0-be32-11eb-9520-1b4c3ca6a781","name":"f092b002-182e-49b8-bcc4-58f5233e041b:panel_f092b002-182e-49b8-bcc4-58f5233e041b","type":"lens"},{"id":"42b4cec0-be32-11eb-9520-1b4c3ca6a781","name":"tag-42b4cec0-be32-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,609],"type":"dashboard","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2OTMsNF0="} -{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"1\",\"w\":24,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"2\",\"w\":24,\"x\":24,\"y\":0},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"3\",\"w\":24,\"x\":0,\"y\":15},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"4\",\"w\":24,\"x\":24,\"y\":15},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"5\",\"w\":24,\"x\":0,\"y\":30},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"6\",\"w\":24,\"x\":24,\"y\":30},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"7\",\"w\":24,\"x\":0,\"y\":45},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"8\",\"w\":24,\"x\":24,\"y\":45},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_8\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"9\",\"w\":24,\"x\":0,\"y\":60},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"10\",\"w\":24,\"x\":24,\"y\":60},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"11\",\"w\":24,\"x\":0,\"y\":75},\"panelIndex\":\"11\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_11\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"12\",\"w\":24,\"x\":24,\"y\":75},\"panelIndex\":\"12\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_12\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"13\",\"w\":24,\"x\":0,\"y\":90},\"panelIndex\":\"13\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"14\",\"w\":24,\"x\":24,\"y\":90},\"panelIndex\":\"14\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_14\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"15\",\"w\":24,\"x\":0,\"y\":105},\"panelIndex\":\"15\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_15\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"16\",\"w\":24,\"x\":24,\"y\":105},\"panelIndex\":\"16\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_16\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"17\",\"w\":24,\"x\":0,\"y\":120},\"panelIndex\":\"17\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_17\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"18\",\"w\":24,\"x\":24,\"y\":120},\"panelIndex\":\"18\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_18\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"19\",\"w\":24,\"x\":0,\"y\":135},\"panelIndex\":\"19\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_19\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":15,\"i\":\"20\",\"w\":24,\"x\":24,\"y\":135},\"panelIndex\":\"20\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_20\"}]","timeRestore":false,"title":"logstash_dashboard_withouttime","version":1},"coreMigrationVersion":"7.13.2","id":"5d3410c0-6249-11eb-aebf-c306684b328d","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"36b91810-6239-11eb-aebf-c306684b328d","name":"1:panel_1","type":"visualization"},{"id":"0a274320-61cc-11eb-aebf-c306684b328d","name":"2:panel_2","type":"visualization"},{"id":"e4aef350-623d-11eb-aebf-c306684b328d","name":"3:panel_3","type":"visualization"},{"id":"f92e5630-623e-11eb-aebf-c306684b328d","name":"4:panel_4","type":"visualization"},{"id":"9853d4d0-623d-11eb-aebf-c306684b328d","name":"5:panel_5","type":"visualization"},{"id":"6ecb33b0-623d-11eb-aebf-c306684b328d","name":"6:panel_6","type":"visualization"},{"id":"b8e35c80-623c-11eb-aebf-c306684b328d","name":"7:panel_7","type":"visualization"},{"id":"f1bc75d0-6239-11eb-aebf-c306684b328d","name":"8:panel_8","type":"visualization"},{"id":"0d8a8860-623a-11eb-aebf-c306684b328d","name":"9:panel_9","type":"visualization"},{"id":"d79fe3d0-6239-11eb-aebf-c306684b328d","name":"10:panel_10","type":"visualization"},{"id":"318375a0-6240-11eb-aebf-c306684b328d","name":"11:panel_11","type":"visualization"},{"id":"e461eb20-6245-11eb-aebf-c306684b328d","name":"12:panel_12","type":"visualization"},{"id":"25bdc750-6242-11eb-aebf-c306684b328d","name":"13:panel_13","type":"visualization"},{"id":"71dd7bc0-6248-11eb-aebf-c306684b328d","name":"14:panel_14","type":"visualization"},{"id":"6aea48a0-6240-11eb-aebf-c306684b328d","name":"15:panel_15","type":"visualization"},{"id":"32b681f0-6241-11eb-aebf-c306684b328d","name":"16:panel_16","type":"visualization"},{"id":"ccca99e0-6244-11eb-aebf-c306684b328d","name":"17:panel_17","type":"visualization"},{"id":"a4d7be80-6245-11eb-aebf-c306684b328d","name":"18:panel_18","type":"visualization"},{"id":"c94d8440-6248-11eb-aebf-c306684b328d","name":"19:panel_19","type":"visualization"},{"id":"db6226f0-61c0-11eb-aebf-c306684b328d","name":"20:panel_20","type":"search"}],"sort":[1623693556928,630],"type":"dashboard","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2OTQsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"shakespeare_tag_cloud","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"shakespeare_tag_cloud\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"linear\",\"orientation\":\"multiple\",\"minFontSize\":59,\"maxFontSize\":100,\"showLabel\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"type.keyword\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.2","id":"622ac7f0-619e-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,632],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2OTUsNF0="} -{"attributes":{"numLinks":4,"numVertices":5,"title":"logstash_graph","version":1,"wsState":"\"{\\\"selectedFields\\\":[{\\\"name\\\":\\\"machine.os.raw\\\",\\\"hopSize\\\":5,\\\"lastValidHopSize\\\":5,\\\"color\\\":\\\"#B9A888\\\",\\\"selected\\\":true,\\\"iconClass\\\":\\\"fa-folder-open-o\\\"},{\\\"name\\\":\\\"response.raw\\\",\\\"hopSize\\\":5,\\\"lastValidHopSize\\\":5,\\\"color\\\":\\\"#D6BF57\\\",\\\"selected\\\":true,\\\"iconClass\\\":\\\"fa-folder-open-o\\\"}],\\\"blocklist\\\":[],\\\"vertices\\\":[{\\\"x\\\":461.96184642905024,\\\"y\\\":284.02313214227325,\\\"label\\\":\\\"osx\\\",\\\"color\\\":\\\"#B9A888\\\",\\\"field\\\":\\\"machine.os.raw\\\",\\\"term\\\":\\\"osx\\\",\\\"parent\\\":null,\\\"size\\\":15},{\\\"x\\\":383.946159835112,\\\"y\\\":375.6063135315976,\\\"label\\\":\\\"503\\\",\\\"color\\\":\\\"#D6BF57\\\",\\\"field\\\":\\\"response.raw\\\",\\\"term\\\":\\\"503\\\",\\\"parent\\\":null,\\\"size\\\":15},{\\\"x\\\":287.104700756828,\\\"y\\\":324.1245253249895,\\\"label\\\":\\\"win 7\\\",\\\"color\\\":\\\"#B9A888\\\",\\\"field\\\":\\\"machine.os.raw\\\",\\\"term\\\":\\\"win 7\\\",\\\"parent\\\":null,\\\"size\\\":15},{\\\"x\\\":487.9986107998273,\\\"y\\\":407.07546535764254,\\\"label\\\":\\\"ios\\\",\\\"color\\\":\\\"#B9A888\\\",\\\"field\\\":\\\"machine.os.raw\\\",\\\"term\\\":\\\"ios\\\",\\\"parent\\\":null,\\\"size\\\":15},{\\\"x\\\":302.35059551806023,\\\"y\\\":211.66825720913607,\\\"label\\\":\\\"200\\\",\\\"color\\\":\\\"#D6BF57\\\",\\\"field\\\":\\\"response.raw\\\",\\\"term\\\":\\\"200\\\",\\\"parent\\\":null,\\\"size\\\":15}],\\\"links\\\":[{\\\"weight\\\":0.000881324009872165,\\\"width\\\":7.983523640193488,\\\"source\\\":4,\\\"target\\\":2},{\\\"weight\\\":0.000023386835221992895,\\\"width\\\":2,\\\"source\\\":1,\\\"target\\\":0},{\\\"weight\\\":0.0011039286029480653,\\\"width\\\":2,\\\"source\\\":1,\\\"target\\\":2},{\\\"weight\\\":0.000045596928960694605,\\\"width\\\":2,\\\"source\\\":1,\\\"target\\\":3}],\\\"urlTemplates\\\":[{\\\"url\\\":\\\"/app/discover#/?_a=(columns%3A!(_source)%2Cindex%3A%2756b34100-619d-11eb-aebf-c306684b328d%27%2Cinterval%3Aauto%2Cquery%3A(language%3Akuery%2Cquery%3A{{gquery}})%2Csort%3A!(_score%2Cdesc))\\\",\\\"description\\\":\\\"Machine OS win 7\\\",\\\"isDefault\\\":false,\\\"encoderID\\\":\\\"kql\\\",\\\"iconClass\\\":\\\"fa-share-alt\\\"}],\\\"exploreControls\\\":{\\\"useSignificance\\\":true,\\\"sampleSize\\\":2000,\\\"timeoutMillis\\\":5000,\\\"maxValuesPerDoc\\\":1,\\\"minDocCount\\\":3},\\\"indexPatternRefName\\\":\\\"indexPattern_0\\\"}\""},"coreMigrationVersion":"7.13.2","id":"6afc4b40-be5c-11eb-9520-1b4c3ca6a781","migrationVersion":{"graph-workspace":"7.11.0"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexPattern_0","type":"index-pattern"}],"sort":[1623693556928,634],"type":"graph-workspace","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2OTYsNF0="} -{"attributes":{"buildNum":39457,"defaultIndex":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0"},"coreMigrationVersion":"7.13.2","id":"7.12.1","migrationVersion":{"config":"7.13.0"},"references":[],"sort":[1623415891791,170],"type":"config","updated_at":"2021-06-11T12:51:31.791Z","version":"WzE0NTAsNF0="} -{"attributes":{"accessibility:disableAnimations":true,"buildNum":null,"dateFormat:tz":"UTC","defaultIndex":"56b34100-619d-11eb-aebf-c306684b328d","visualization:visualize:legacyChartsLibrary":true},"coreMigrationVersion":"7.13.2","id":"7.13.1","migrationVersion":{"config":"7.13.0"},"references":[],"sort":[1623693556928,635],"type":"config","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2OTcsNF0="} -{"attributes":{"buildNum":40943,"defaultIndex":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0"},"coreMigrationVersion":"7.13.2","id":"7.13.2","migrationVersion":{"config":"7.13.0"},"references":[],"sort":[1623693652730,748],"type":"config","updated_at":"2021-06-14T18:00:52.730Z","version":"WzE3MjQsNF0="} -{"attributes":{"columns":["_source"],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"highlightAll\":true,\"version\":true,\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"negate\":false,\"type\":\"phrase\",\"key\":\"text_entry\",\"value\":\"Christendom.\",\"params\":{\"query\":\"Christendom.\",\"type\":\"phrase\"},\"disabled\":false,\"alias\":null,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"text_entry\":{\"query\":\"Christendom.\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["_score","desc"]],"title":"shakespeare_saved_search","version":1},"coreMigrationVersion":"7.13.2","id":"712ebbe0-619d-11eb-aebf-c306684b328d","migrationVersion":{"search":"7.9.3"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"}],"sort":[1623693556928,638],"type":"search","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2OTgsNF0="} -{"attributes":{"columns":["play_name","speaker"],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"highlightAll\":true,\"version\":true,\"query\":{\"query\":\"speaker:\\\"GLOUCESTER\\\"\",\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["_score","desc"]],"title":"shakespeare_saved_lucene_search","version":1},"coreMigrationVersion":"7.13.2","id":"ddacc820-619d-11eb-aebf-c306684b328d","migrationVersion":{"search":"7.9.3"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,640],"type":"search","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE2OTksNF0="} -{"attributes":{"columns":["_source"],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"highlightAll\":true,\"version\":true,\"query\":{\"query\":\"text_entry :\\\"MORDAKE THE EARL OF FIFE, AND ELDEST SON\\\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["_score","desc"]],"title":"shakespeare_saved_kql_search","version":1},"coreMigrationVersion":"7.13.2","id":"f852d570-619d-11eb-aebf-c306684b328d","migrationVersion":{"search":"7.9.3"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623693556928,642],"type":"search","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MDAsNF0="} -{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"1\",\"w\":24,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"2\",\"w\":24,\"x\":24,\"y\":0},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"3\",\"w\":24,\"x\":0,\"y\":15},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":15,\"i\":\"4\",\"w\":24,\"x\":24,\"y\":15},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":15,\"i\":\"5\",\"w\":24,\"x\":0,\"y\":30},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":15,\"i\":\"6\",\"w\":24,\"x\":24,\"y\":30},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"}]","timeRestore":false,"title":"shakespeare_dashboard","version":1},"coreMigrationVersion":"7.13.2","id":"73398a90-619e-11eb-aebf-c306684b328d","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"185283c0-619e-11eb-aebf-c306684b328d","name":"1:panel_1","type":"visualization"},{"id":"33736660-619e-11eb-aebf-c306684b328d","name":"2:panel_2","type":"visualization"},{"id":"622ac7f0-619e-11eb-aebf-c306684b328d","name":"3:panel_3","type":"visualization"},{"id":"712ebbe0-619d-11eb-aebf-c306684b328d","name":"4:panel_4","type":"search"},{"id":"ddacc820-619d-11eb-aebf-c306684b328d","name":"5:panel_5","type":"search"},{"id":"f852d570-619d-11eb-aebf-c306684b328d","name":"6:panel_6","type":"search"}],"sort":[1623693556928,649],"type":"dashboard","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MDEsNF0="} -{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"geo.srcdest\",\"value\":\"IN:US\",\"params\":{\"query\":\"IN:US\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"geo.srcdest\":{\"query\":\"IN:US\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]}"},"optionsJSON":"{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"1\",\"w\":24,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"2\",\"w\":24,\"x\":24,\"y\":0},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"3\",\"w\":24,\"x\":0,\"y\":15},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"4\",\"w\":24,\"x\":24,\"y\":15},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"5\",\"w\":24,\"x\":0,\"y\":30},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"6\",\"w\":24,\"x\":24,\"y\":30},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"7\",\"w\":24,\"x\":0,\"y\":45},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"8\",\"w\":24,\"x\":24,\"y\":45},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_8\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"9\",\"w\":24,\"x\":0,\"y\":60},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"10\",\"w\":24,\"x\":24,\"y\":60},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"11\",\"w\":24,\"x\":0,\"y\":75},\"panelIndex\":\"11\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_11\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"12\",\"w\":24,\"x\":24,\"y\":75},\"panelIndex\":\"12\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_12\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"13\",\"w\":24,\"x\":0,\"y\":90},\"panelIndex\":\"13\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"14\",\"w\":24,\"x\":24,\"y\":90},\"panelIndex\":\"14\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_14\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"15\",\"w\":24,\"x\":0,\"y\":105},\"panelIndex\":\"15\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_15\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"16\",\"w\":24,\"x\":24,\"y\":105},\"panelIndex\":\"16\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_16\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"17\",\"w\":24,\"x\":0,\"y\":120},\"panelIndex\":\"17\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_17\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"18\",\"w\":24,\"x\":24,\"y\":120},\"panelIndex\":\"18\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_18\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"19\",\"w\":24,\"x\":0,\"y\":135},\"panelIndex\":\"19\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_19\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":15,\"i\":\"20\",\"w\":24,\"x\":24,\"y\":135},\"panelIndex\":\"20\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_20\"}]","timeRestore":false,"title":"logstash_dashboardwithfilters","version":1},"coreMigrationVersion":"7.13.2","id":"79794f20-6249-11eb-aebf-c306684b328d","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"36b91810-6239-11eb-aebf-c306684b328d","name":"1:panel_1","type":"visualization"},{"id":"0a274320-61cc-11eb-aebf-c306684b328d","name":"2:panel_2","type":"visualization"},{"id":"e4aef350-623d-11eb-aebf-c306684b328d","name":"3:panel_3","type":"visualization"},{"id":"f92e5630-623e-11eb-aebf-c306684b328d","name":"4:panel_4","type":"visualization"},{"id":"9853d4d0-623d-11eb-aebf-c306684b328d","name":"5:panel_5","type":"visualization"},{"id":"6ecb33b0-623d-11eb-aebf-c306684b328d","name":"6:panel_6","type":"visualization"},{"id":"b8e35c80-623c-11eb-aebf-c306684b328d","name":"7:panel_7","type":"visualization"},{"id":"f1bc75d0-6239-11eb-aebf-c306684b328d","name":"8:panel_8","type":"visualization"},{"id":"0d8a8860-623a-11eb-aebf-c306684b328d","name":"9:panel_9","type":"visualization"},{"id":"d79fe3d0-6239-11eb-aebf-c306684b328d","name":"10:panel_10","type":"visualization"},{"id":"318375a0-6240-11eb-aebf-c306684b328d","name":"11:panel_11","type":"visualization"},{"id":"e461eb20-6245-11eb-aebf-c306684b328d","name":"12:panel_12","type":"visualization"},{"id":"25bdc750-6242-11eb-aebf-c306684b328d","name":"13:panel_13","type":"visualization"},{"id":"71dd7bc0-6248-11eb-aebf-c306684b328d","name":"14:panel_14","type":"visualization"},{"id":"6aea48a0-6240-11eb-aebf-c306684b328d","name":"15:panel_15","type":"visualization"},{"id":"32b681f0-6241-11eb-aebf-c306684b328d","name":"16:panel_16","type":"visualization"},{"id":"ccca99e0-6244-11eb-aebf-c306684b328d","name":"17:panel_17","type":"visualization"},{"id":"a4d7be80-6245-11eb-aebf-c306684b328d","name":"18:panel_18","type":"visualization"},{"id":"c94d8440-6248-11eb-aebf-c306684b328d","name":"19:panel_19","type":"visualization"},{"id":"db6226f0-61c0-11eb-aebf-c306684b328d","name":"20:panel_20","type":"search"}],"sort":[1623693556928,671],"type":"dashboard","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MDIsNF0="} -{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"037b7937-790b-4d2d-94a5-7f5837a6ef05":{"columnOrder":["b3d46616-75e0-419e-97ea-91148961ef94","025a0fb3-dc44-4f5c-b517-2d71d3f26f14","c476db14-0cc1-40ec-863e-d2779256a407"],"columns":{"025a0fb3-dc44-4f5c-b517-2d71d3f26f14":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"},"b3d46616-75e0-419e-97ea-91148961ef94":{"dataType":"string","isBucketed":true,"label":"Top values of geo.srcdest","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"c476db14-0cc1-40ec-863e-d2779256a407","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"geo.srcdest"},"c476db14-0cc1-40ec-863e-d2779256a407":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"lucene","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["c476db14-0cc1-40ec-863e-d2779256a407"],"layerId":"037b7937-790b-4d2d-94a5-7f5837a6ef05","position":"top","seriesType":"bar_stacked","showGridlines":false,"splitAccessor":"b3d46616-75e0-419e-97ea-91148961ef94","xAccessor":"025a0fb3-dc44-4f5c-b517-2d71d3f26f14"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_verticalstacked","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.2","id":"8dc19b50-be32-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-037b7937-790b-4d2d-94a5-7f5837a6ef05","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,675],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MDMsNF0="} -{"attributes":{"columns":[],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[{\"meta\":{\"alias\":null,\"negate\":false,\"disabled\":false,\"type\":\"phrase\",\"key\":\"geo.dest\",\"params\":{\"query\":\"US\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match_phrase\":{\"geo.dest\":\"US\"}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["@timestamp","desc"]],"title":"drilldown_logstash","version":1},"coreMigrationVersion":"7.13.2","id":"b3288100-ca2c-11eb-bf5e-3de94e83d4f0","migrationVersion":{"search":"7.9.3"},"references":[{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"}],"sort":[1623415891791,216],"type":"search","updated_at":"2021-06-11T12:51:31.791Z","version":"WzE0NjMsNF0="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{}"},"title":"logstash_timelion_panel","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_timelion_panel\",\"type\":\"timelion\",\"aggs\":[],\"params\":{\"expression\":\".es(index=logstash-*, \\\"sum:bytes\\\")\",\"interval\":\"auto\"}}"},"coreMigrationVersion":"7.13.2","id":"b3a44cd0-be5c-11eb-9520-1b4c3ca6a781","migrationVersion":{"visualization":"7.13.1"},"references":[],"sort":[1623693556928,677],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MDUsNF0="} -{"attributes":{"color":"#9170B8","description":"","name":"alltogether"},"coreMigrationVersion":"7.13.2","id":"be808cb0-be32-11eb-9520-1b4c3ca6a781","references":[],"sort":[1623693556928,678],"type":"tag","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MDYsNF0="} -{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"4d9e9a01-cdb8-4aef-afcb-50db52247bb1\"},\"panelIndex\":\"4d9e9a01-cdb8-4aef-afcb-50db52247bb1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4d9e9a01-cdb8-4aef-afcb-50db52247bb1\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"d9cab9c8-667e-4d34-821b-cbb070891956\"},\"panelIndex\":\"d9cab9c8-667e-4d34-821b-cbb070891956\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_d9cab9c8-667e-4d34-821b-cbb070891956\"}]","refreshInterval":{"pause":true,"value":0},"timeFrom":"2015-09-20T01:56:56.132Z","timeRestore":true,"timeTo":"2015-09-21T11:18:20.471Z","title":"lens_combined_dashboard","version":1},"coreMigrationVersion":"7.13.2","id":"bfb3dc90-be32-11eb-9520-1b4c3ca6a781","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"8dc19b50-be32-11eb-9520-1b4c3ca6a781","name":"4d9e9a01-cdb8-4aef-afcb-50db52247bb1:panel_4d9e9a01-cdb8-4aef-afcb-50db52247bb1","type":"lens"},{"id":"b5bd5050-be31-11eb-9520-1b4c3ca6a781","name":"d9cab9c8-667e-4d34-821b-cbb070891956:panel_d9cab9c8-667e-4d34-821b-cbb070891956","type":"lens"},{"id":"be808cb0-be32-11eb-9520-1b4c3ca6a781","name":"tag-be808cb0-be32-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,682],"type":"dashboard","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MDcsNF0="} -{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"2e80716f-c1b6-46f2-be2b-35db744b5031\"},\"panelIndex\":\"2e80716f-c1b6-46f2-be2b-35db744b5031\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsPie\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"26e2cf99-d931-4320-9e15-9dbc148f3534\":{\"columns\":{\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\":{\"label\":\"Top values of url.raw\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"url.raw\",\"isBucketed\":true,\"params\":{\"size\":20,\"orderBy\":{\"type\":\"column\",\"columnId\":\"beb72af1-239c-46d8-823b-b00d1e2ace43\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"beb72af1-239c-46d8-823b-b00d1e2ace43\":{\"label\":\"Unique count of geo.srcdest\",\"dataType\":\"number\",\"operationType\":\"unique_count\",\"scale\":\"ratio\",\"sourceField\":\"geo.srcdest\",\"isBucketed\":false}},\"columnOrder\":[\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"beb72af1-239c-46d8-823b-b00d1e2ace43\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"shape\":\"donut\",\"layers\":[{\"layerId\":\"26e2cf99-d931-4320-9e15-9dbc148f3534\",\"groups\":[\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\"],\"metric\":\"beb72af1-239c-46d8-823b-b00d1e2ace43\",\"numberDisplay\":\"percent\",\"categoryDisplay\":\"default\",\"legendDisplay\":\"default\",\"nestedLegend\":false}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-layer-26e2cf99-d931-4320-9e15-9dbc148f3534\"}]},\"enhancements\":{},\"type\":\"lens\"},\"panelRefName\":\"panel_2e80716f-c1b6-46f2-be2b-35db744b5031\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"da8843e0-6789-4aae-bcd0-81f270538719\"},\"panelIndex\":\"da8843e0-6789-4aae-bcd0-81f270538719\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_da8843e0-6789-4aae-bcd0-81f270538719\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":15,\"w\":24,\"h\":15,\"i\":\"adcd4418-7299-4efa-b369-5f71a7b4ebe0\"},\"panelIndex\":\"adcd4418-7299-4efa-b369-5f71a7b4ebe0\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_adcd4418-7299-4efa-b369-5f71a7b4ebe0\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"869754a7-edf0-478f-a7f1-80374f63108a\"},\"panelIndex\":\"869754a7-edf0-478f-a7f1-80374f63108a\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_869754a7-edf0-478f-a7f1-80374f63108a\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":30,\"w\":24,\"h\":15,\"i\":\"67111cf4-338e-453f-8621-e8dea64082d1\"},\"panelIndex\":\"67111cf4-338e-453f-8621-e8dea64082d1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_67111cf4-338e-453f-8621-e8dea64082d1\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":30,\"w\":24,\"h\":15,\"i\":\"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\"},\"panelIndex\":\"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":45,\"w\":24,\"h\":15,\"i\":\"88847944-ae1b-45fd-b102-3b45f9bea04b\"},\"panelIndex\":\"88847944-ae1b-45fd-b102-3b45f9bea04b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_88847944-ae1b-45fd-b102-3b45f9bea04b\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":45,\"w\":24,\"h\":15,\"i\":\"5a7924c7-eac0-4573-9199-fecec5b82e9e\"},\"panelIndex\":\"5a7924c7-eac0-4573-9199-fecec5b82e9e\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5a7924c7-eac0-4573-9199-fecec5b82e9e\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":60,\"w\":24,\"h\":15,\"i\":\"f8f49591-f071-4a96-b1ed-cd65daff5648\"},\"panelIndex\":\"f8f49591-f071-4a96-b1ed-cd65daff5648\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_f8f49591-f071-4a96-b1ed-cd65daff5648\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":60,\"w\":24,\"h\":15,\"i\":\"9f357f47-c2a0-421f-a456-9583c40837ab\"},\"panelIndex\":\"9f357f47-c2a0-421f-a456-9583c40837ab\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9f357f47-c2a0-421f-a456-9583c40837ab\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":75,\"w\":24,\"h\":15,\"i\":\"6cb383e9-1e80-44f9-80d5-7b8c585668db\"},\"panelIndex\":\"6cb383e9-1e80-44f9-80d5-7b8c585668db\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6cb383e9-1e80-44f9-80d5-7b8c585668db\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":75,\"w\":24,\"h\":15,\"i\":\"57f5f0bf-6610-4599-aad4-37484640b5e2\"},\"panelIndex\":\"57f5f0bf-6610-4599-aad4-37484640b5e2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_57f5f0bf-6610-4599-aad4-37484640b5e2\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":90,\"w\":24,\"h\":15,\"i\":\"32d3ab66-52e1-44e3-8c1f-1dccff3c5692\"},\"panelIndex\":\"32d3ab66-52e1-44e3-8c1f-1dccff3c5692\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_32d3ab66-52e1-44e3-8c1f-1dccff3c5692\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":90,\"w\":24,\"h\":15,\"i\":\"dd1718fd-74ee-4032-851b-db97e893825d\"},\"panelIndex\":\"dd1718fd-74ee-4032-851b-db97e893825d\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_dd1718fd-74ee-4032-851b-db97e893825d\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":105,\"w\":24,\"h\":15,\"i\":\"98a556ee-078b-4e03-93a8-29996133cdcb\"},\"panelIndex\":\"98a556ee-078b-4e03-93a8-29996133cdcb\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsXY\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"999a2d60-cb2a-451c-8d71-80d7e92e70fd\":{\"columns\":{\"ce9117a2-773c-474c-8fb1-18940cf58b38\":{\"label\":\"Top values of type\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"type\",\"isBucketed\":true,\"params\":{\"size\":5,\"orderBy\":{\"type\":\"column\",\"columnId\":\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\"},\"orderDirection\":\"asc\",\"otherBucket\":true,\"missingBucket\":false}},\"a3d10552-e352-40d0-a156-e86112c0501a\":{\"label\":\"Top values of _type\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"_type\",\"isBucketed\":true,\"params\":{\"size\":3,\"orderBy\":{\"type\":\"column\",\"columnId\":\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"Records\"},\"9c5db2f3-9eb0-4667-9a74-3318301de251\":{\"label\":\"Sum of bytes\",\"dataType\":\"number\",\"operationType\":\"sum\",\"sourceField\":\"bytes\",\"isBucketed\":false,\"scale\":\"ratio\"}},\"columnOrder\":[\"ce9117a2-773c-474c-8fb1-18940cf58b38\",\"a3d10552-e352-40d0-a156-e86112c0501a\",\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\",\"9c5db2f3-9eb0-4667-9a74-3318301de251\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"legend\":{\"isVisible\":true,\"position\":\"right\"},\"valueLabels\":\"hide\",\"fittingFunction\":\"None\",\"axisTitlesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"bar_stacked\",\"layers\":[{\"layerId\":\"999a2d60-cb2a-451c-8d71-80d7e92e70fd\",\"accessors\":[\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\",\"9c5db2f3-9eb0-4667-9a74-3318301de251\"],\"position\":\"top\",\"seriesType\":\"bar_stacked\",\"showGridlines\":false,\"xAccessor\":\"ce9117a2-773c-474c-8fb1-18940cf58b38\",\"splitAccessor\":\"a3d10552-e352-40d0-a156-e86112c0501a\"}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-layer-999a2d60-cb2a-451c-8d71-80d7e92e70fd\"}]},\"enhancements\":{},\"type\":\"lens\"}},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":105,\"w\":24,\"h\":15,\"i\":\"62a0f0b0-3589-4cef-807b-b1b4258b7a9b\"},\"panelIndex\":\"62a0f0b0-3589-4cef-807b-b1b4258b7a9b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_62a0f0b0-3589-4cef-807b-b1b4258b7a9b\"},{\"version\":\"7.13.1\",\"type\":\"map\",\"gridData\":{\"x\":0,\"y\":120,\"w\":24,\"h\":15,\"i\":\"dcc0defa-3376-465c-9b5b-2ba69528848c\"},\"panelIndex\":\"dcc0defa-3376-465c-9b5b-2ba69528848c\",\"embeddableConfig\":{\"mapCenter\":{\"lat\":19.94277,\"lon\":0,\"zoom\":1.56},\"mapBuffer\":{\"minLon\":-210.32666,\"minLat\":-64.8435,\"maxLon\":210.32666,\"maxLat\":95.13806},\"isLayerTOCOpen\":true,\"openTOCDetails\":[],\"hiddenLayers\":[],\"enhancements\":{}},\"panelRefName\":\"panel_dcc0defa-3376-465c-9b5b-2ba69528848c\"},{\"version\":\"7.13.1\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":120,\"w\":24,\"h\":15,\"i\":\"dd21a674-ae3a-40f6-9d68-4e01361ea5e2\"},\"panelIndex\":\"dd21a674-ae3a-40f6-9d68-4e01361ea5e2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_dd21a674-ae3a-40f6-9d68-4e01361ea5e2\"}]","refreshInterval":{"pause":true,"value":0},"timeFrom":"2015-09-20T01:56:56.132Z","timeRestore":true,"timeTo":"2015-09-21T11:18:20.471Z","title":"timelion_lens_maps_dashboard_logstash","version":1},"coreMigrationVersion":"7.13.2","id":"c4ab2030-be5c-11eb-9520-1b4c3ca6a781","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"21905950-bd9f-11eb-9520-1b4c3ca6a781","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:panel_2e80716f-c1b6-46f2-be2b-35db744b5031","type":"lens"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:indexpattern-datasource-layer-26e2cf99-d931-4320-9e15-9dbc148f3534","type":"index-pattern"},{"id":"aa4b8da0-bd9f-11eb-9520-1b4c3ca6a781","name":"da8843e0-6789-4aae-bcd0-81f270538719:panel_da8843e0-6789-4aae-bcd0-81f270538719","type":"lens"},{"id":"2d3f1250-bd9f-11eb-9520-1b4c3ca6a781","name":"adcd4418-7299-4efa-b369-5f71a7b4ebe0:panel_adcd4418-7299-4efa-b369-5f71a7b4ebe0","type":"lens"},{"id":"edd5a560-bda4-11eb-9520-1b4c3ca6a781","name":"869754a7-edf0-478f-a7f1-80374f63108a:panel_869754a7-edf0-478f-a7f1-80374f63108a","type":"lens"},{"id":"2c25a450-bda5-11eb-9520-1b4c3ca6a781","name":"67111cf4-338e-453f-8621-e8dea64082d1:panel_67111cf4-338e-453f-8621-e8dea64082d1","type":"lens"},{"id":"e79116e0-bd9e-11eb-9520-1b4c3ca6a781","name":"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d:panel_13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d","type":"lens"},{"id":"974fb950-bda5-11eb-9520-1b4c3ca6a781","name":"88847944-ae1b-45fd-b102-3b45f9bea04b:panel_88847944-ae1b-45fd-b102-3b45f9bea04b","type":"lens"},{"id":"21905950-bd9f-11eb-9520-1b4c3ca6a781","name":"5a7924c7-eac0-4573-9199-fecec5b82e9e:panel_5a7924c7-eac0-4573-9199-fecec5b82e9e","type":"lens"},{"id":"51b63040-bda5-11eb-9520-1b4c3ca6a781","name":"f8f49591-f071-4a96-b1ed-cd65daff5648:panel_f8f49591-f071-4a96-b1ed-cd65daff5648","type":"lens"},{"id":"b00679c0-bda5-11eb-9520-1b4c3ca6a781","name":"9f357f47-c2a0-421f-a456-9583c40837ab:panel_9f357f47-c2a0-421f-a456-9583c40837ab","type":"lens"},{"id":"652ade10-bd9f-11eb-9520-1b4c3ca6a781","name":"6cb383e9-1e80-44f9-80d5-7b8c585668db:panel_6cb383e9-1e80-44f9-80d5-7b8c585668db","type":"lens"},{"id":"7f3b5fb0-be2f-11eb-9520-1b4c3ca6a781","name":"57f5f0bf-6610-4599-aad4-37484640b5e2:panel_57f5f0bf-6610-4599-aad4-37484640b5e2","type":"lens"},{"id":"bb9e5bb0-be2f-11eb-9520-1b4c3ca6a781","name":"32d3ab66-52e1-44e3-8c1f-1dccff3c5692:panel_32d3ab66-52e1-44e3-8c1f-1dccff3c5692","type":"lens"},{"id":"dd315430-be2f-11eb-9520-1b4c3ca6a781","name":"dd1718fd-74ee-4032-851b-db97e893825d:panel_dd1718fd-74ee-4032-851b-db97e893825d","type":"lens"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"98a556ee-078b-4e03-93a8-29996133cdcb:indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"98a556ee-078b-4e03-93a8-29996133cdcb:indexpattern-datasource-layer-999a2d60-cb2a-451c-8d71-80d7e92e70fd","type":"index-pattern"},{"id":"0dbbf8b0-be3c-11eb-9520-1b4c3ca6a781","name":"62a0f0b0-3589-4cef-807b-b1b4258b7a9b:panel_62a0f0b0-3589-4cef-807b-b1b4258b7a9b","type":"lens"},{"id":"0c5974f0-be5c-11eb-9520-1b4c3ca6a781","name":"dcc0defa-3376-465c-9b5b-2ba69528848c:panel_dcc0defa-3376-465c-9b5b-2ba69528848c","type":"map"},{"id":"a4d7be80-6245-11eb-aebf-c306684b328d","name":"dd21a674-ae3a-40f6-9d68-4e01361ea5e2:panel_dd21a674-ae3a-40f6-9d68-4e01361ea5e2","type":"visualization"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,705],"type":"dashboard","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MDgsNF0="} -{"attributes":{"columns":[],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"speaker :\\\"DUKE VINCENTIO\\\" \",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[],"title":"drilldown_shakes","version":1},"coreMigrationVersion":"7.13.2","id":"c4b9cc00-ca2a-11eb-bf5e-3de94e83d4f0","migrationVersion":{"search":"7.9.3"},"references":[{"id":"39d52f60-ca27-11eb-bf5e-3de94e83d4f0","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1623415891791,218],"type":"search","updated_at":"2021-06-11T12:51:31.791Z","version":"WzE0NjQsNF0="} -{"attributes":{"@created":"2021-05-27T19:45:29.712Z","@timestamp":"2021-05-27T19:45:29.712Z","content":"{\"selectedNodes\":[{\"id\":\"element-56d2ba72-f227-4d04-9478-a1d6f0c7e601\",\"position\":{\"left\":20,\"top\":20,\"width\":500,\"height\":300,\"angle\":0,\"parent\":\"group-499b5982-25f4-4894-9540-1874a27d78e7\",\"type\":\"element\"},\"expression\":\"savedLens id=\\\"bb9e5bb0-be2f-11eb-9520-1b4c3ca6a781\\\" timerange={timerange from=\\\"now-15y\\\" to=\\\"now\\\"}\\n| render\",\"filter\":null,\"ast\":{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"savedLens\",\"arguments\":{\"id\":[\"bb9e5bb0-be2f-11eb-9520-1b4c3ca6a781\"],\"timerange\":[{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"timerange\",\"arguments\":{\"from\":[\"now-15y\"],\"to\":[\"now\"]}}]}]}},{\"type\":\"function\",\"function\":\"render\",\"arguments\":{}}]}},{\"id\":\"element-afbaa26e-10e7-47d4-bb41-b061dfdced2b\",\"position\":{\"left\":527,\"top\":20,\"width\":500,\"height\":300,\"angle\":0,\"parent\":\"group-499b5982-25f4-4894-9540-1874a27d78e7\",\"type\":\"element\"},\"expression\":\"savedVisualization id=\\\"0d8a8860-623a-11eb-aebf-c306684b328d\\\" timerange={timerange from=\\\"now-15y\\\" to=\\\"now\\\"}\\n| render\",\"filter\":null,\"ast\":{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"savedVisualization\",\"arguments\":{\"id\":[\"0d8a8860-623a-11eb-aebf-c306684b328d\"],\"timerange\":[{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"timerange\",\"arguments\":{\"from\":[\"now-15y\"],\"to\":[\"now\"]}}]}]}},{\"type\":\"function\",\"function\":\"render\",\"arguments\":{}}]}}]}","displayName":"element_canvas","help":"","image":"","name":"elementCanvas"},"coreMigrationVersion":"7.13.2","id":"custom-element-3bc52277-ee01-4cdc-8d2d-f2db6ade1512","references":[],"sort":[1623693556928,706],"type":"canvas-element","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MDksNF0="} -{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"ced0a5ea-3ec2-4274-8431-6e76d85637f6\"},\"panelIndex\":\"ced0a5ea-3ec2-4274-8431-6e76d85637f6\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsXY\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"6a3b206a-4ac3-4d67-b2b9-c6b543a11ea3\":{\"columns\":{\"f70668f8-ae97-4b64-867f-b0c9b77914ef\":{\"label\":\"Top values of speaker\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"speaker\",\"isBucketed\":true,\"params\":{\"size\":39,\"orderBy\":{\"type\":\"column\",\"columnId\":\"fbf256d9-cae7-4244-8504-b73a5666e917\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"fbf256d9-cae7-4244-8504-b73a5666e917\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"Records\"}},\"columnOrder\":[\"f70668f8-ae97-4b64-867f-b0c9b77914ef\",\"fbf256d9-cae7-4244-8504-b73a5666e917\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"legend\":{\"isVisible\":true,\"position\":\"right\"},\"valueLabels\":\"hide\",\"fittingFunction\":\"None\",\"axisTitlesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"bar_horizontal_percentage_stacked\",\"layers\":[{\"layerId\":\"6a3b206a-4ac3-4d67-b2b9-c6b543a11ea3\",\"accessors\":[\"fbf256d9-cae7-4244-8504-b73a5666e917\"],\"position\":\"top\",\"seriesType\":\"bar_horizontal_percentage_stacked\",\"showGridlines\":false,\"splitAccessor\":\"f70668f8-ae97-4b64-867f-b0c9b77914ef\"}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"39d52f60-ca27-11eb-bf5e-3de94e83d4f0\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"39d52f60-ca27-11eb-bf5e-3de94e83d4f0\",\"name\":\"indexpattern-datasource-layer-6a3b206a-4ac3-4d67-b2b9-c6b543a11ea3\"}]},\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"2b9a2bad-d6aa-4d3b-a692-fd96c3fb0ac1\",\"triggers\":[\"FILTER_TRIGGER\"],\"action\":{\"name\":\"We_like_lens\",\"config\":{\"useCurrentFilters\":true,\"useCurrentDateRange\":true},\"factoryId\":\"DASHBOARD_TO_DASHBOARD_DRILLDOWN\"}}]}},\"type\":\"lens\"}},{\"version\":\"7.13.1\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"5e4cf03a-13bb-4aa7-8326-b47a19e88968\"},\"panelIndex\":\"5e4cf03a-13bb-4aa7-8326-b47a19e88968\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"\",\"description\":\"\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100}},\"uiState\":{},\"data\":{\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"play_name\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":788,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"segment\"}],\"searchSource\":{\"index\":\"39d52f60-ca27-11eb-bf5e-3de94e83d4f0\",\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}}},\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"c5e2a416-2985-4f44-a6b6-70bb95d3bcdd\",\"triggers\":[\"CONTEXT_MENU_TRIGGER\"],\"action\":{\"name\":\"shakespeare_discover\",\"config\":{\"url\":{\"template\":\"http://localhost:5601/app/discover#/view/c4b9cc00-ca2a-11eb-bf5e-3de94e83d4f0?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15y,to:now))&_a=(columns:!(),filters:!(),index:'39d52f60-ca27-11eb-bf5e-3de94e83d4f0',interval:auto,query:(language:kuery,query:'speaker%20:%22DUKE%20VINCENTIO%22%20'),sort:!())\"},\"openInNewTab\":true,\"encodeUrl\":true},\"factoryId\":\"URL_DRILLDOWN\"}},{\"eventId\":\"de71a757-6401-4b05-9d8d-475fedc0cd47\",\"triggers\":[\"VALUE_CLICK_TRIGGER\"],\"action\":{\"name\":\"drilldown_timebased\",\"config\":{\"url\":{\"template\":\"http://localhost:5601/app/discover#/view/b3288100-ca2c-11eb-bf5e-3de94e83d4f0?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15y,to:now))&_a=(columns:!(),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'43fcac20-ca27-11eb-bf5e-3de94e83d4f0',key:geo.dest,negate:!f,params:(query:US),type:phrase),query:(match_phrase:(geo.dest:US)))),index:'43fcac20-ca27-11eb-bf5e-3de94e83d4f0',interval:auto,query:(language:kuery,query:''),sort:!(!('@timestamp',desc)))\"},\"openInNewTab\":true,\"encodeUrl\":true},\"factoryId\":\"URL_DRILLDOWN\"}}]}},\"type\":\"visualization\"}}]","timeRestore":false,"title":"nontimebased_shakespeare_drilldown","version":1},"coreMigrationVersion":"7.13.2","id":"e9eb20f0-ca2a-11eb-bf5e-3de94e83d4f0","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"39d52f60-ca27-11eb-bf5e-3de94e83d4f0","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"39d52f60-ca27-11eb-bf5e-3de94e83d4f0","name":"indexpattern-datasource-layer-6a3b206a-4ac3-4d67-b2b9-c6b543a11ea3","type":"index-pattern"},{"id":"08dec860-ca29-11eb-bf5e-3de94e83d4f0","name":"drilldown:DASHBOARD_TO_DASHBOARD_DRILLDOWN:2b9a2bad-d6aa-4d3b-a692-fd96c3fb0ac1:dashboardId","type":"dashboard"},{"id":"39d52f60-ca27-11eb-bf5e-3de94e83d4f0","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"07f48f70-ca29-11eb-bf5e-3de94e83d4f0","name":"tag-07f48f70-ca29-11eb-bf5e-3de94e83d4f0","type":"tag"}],"sort":[1623415891791,224],"type":"dashboard","updated_at":"2021-06-11T12:51:31.791Z","version":"WzE0NjUsNF0="} -{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"2e80716f-c1b6-46f2-be2b-35db744b5031\"},\"panelIndex\":\"2e80716f-c1b6-46f2-be2b-35db744b5031\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsPie\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"26e2cf99-d931-4320-9e15-9dbc148f3534\":{\"columns\":{\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\":{\"label\":\"Top values of url.raw\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"url.raw\",\"isBucketed\":true,\"params\":{\"size\":20,\"orderBy\":{\"type\":\"column\",\"columnId\":\"beb72af1-239c-46d8-823b-b00d1e2ace43\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"beb72af1-239c-46d8-823b-b00d1e2ace43\":{\"label\":\"Unique count of geo.srcdest\",\"dataType\":\"number\",\"operationType\":\"unique_count\",\"scale\":\"ratio\",\"sourceField\":\"geo.srcdest\",\"isBucketed\":false}},\"columnOrder\":[\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"beb72af1-239c-46d8-823b-b00d1e2ace43\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"shape\":\"donut\",\"layers\":[{\"layerId\":\"26e2cf99-d931-4320-9e15-9dbc148f3534\",\"groups\":[\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\"],\"metric\":\"beb72af1-239c-46d8-823b-b00d1e2ace43\",\"numberDisplay\":\"percent\",\"categoryDisplay\":\"default\",\"legendDisplay\":\"default\",\"nestedLegend\":false}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-layer-26e2cf99-d931-4320-9e15-9dbc148f3534\"}]},\"enhancements\":{},\"type\":\"lens\"},\"panelRefName\":\"panel_2e80716f-c1b6-46f2-be2b-35db744b5031\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"da8843e0-6789-4aae-bcd0-81f270538719\"},\"panelIndex\":\"da8843e0-6789-4aae-bcd0-81f270538719\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_da8843e0-6789-4aae-bcd0-81f270538719\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":15,\"w\":24,\"h\":15,\"i\":\"adcd4418-7299-4efa-b369-5f71a7b4ebe0\"},\"panelIndex\":\"adcd4418-7299-4efa-b369-5f71a7b4ebe0\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_adcd4418-7299-4efa-b369-5f71a7b4ebe0\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"869754a7-edf0-478f-a7f1-80374f63108a\"},\"panelIndex\":\"869754a7-edf0-478f-a7f1-80374f63108a\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_869754a7-edf0-478f-a7f1-80374f63108a\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":30,\"w\":24,\"h\":15,\"i\":\"67111cf4-338e-453f-8621-e8dea64082d1\"},\"panelIndex\":\"67111cf4-338e-453f-8621-e8dea64082d1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_67111cf4-338e-453f-8621-e8dea64082d1\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":30,\"w\":24,\"h\":15,\"i\":\"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\"},\"panelIndex\":\"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":45,\"w\":24,\"h\":15,\"i\":\"88847944-ae1b-45fd-b102-3b45f9bea04b\"},\"panelIndex\":\"88847944-ae1b-45fd-b102-3b45f9bea04b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_88847944-ae1b-45fd-b102-3b45f9bea04b\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":45,\"w\":24,\"h\":15,\"i\":\"5a7924c7-eac0-4573-9199-fecec5b82e9e\"},\"panelIndex\":\"5a7924c7-eac0-4573-9199-fecec5b82e9e\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5a7924c7-eac0-4573-9199-fecec5b82e9e\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":60,\"w\":24,\"h\":15,\"i\":\"f8f49591-f071-4a96-b1ed-cd65daff5648\"},\"panelIndex\":\"f8f49591-f071-4a96-b1ed-cd65daff5648\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_f8f49591-f071-4a96-b1ed-cd65daff5648\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":60,\"w\":24,\"h\":15,\"i\":\"9f357f47-c2a0-421f-a456-9583c40837ab\"},\"panelIndex\":\"9f357f47-c2a0-421f-a456-9583c40837ab\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9f357f47-c2a0-421f-a456-9583c40837ab\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":75,\"w\":24,\"h\":15,\"i\":\"6cb383e9-1e80-44f9-80d5-7b8c585668db\"},\"panelIndex\":\"6cb383e9-1e80-44f9-80d5-7b8c585668db\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6cb383e9-1e80-44f9-80d5-7b8c585668db\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":75,\"w\":24,\"h\":15,\"i\":\"57f5f0bf-6610-4599-aad4-37484640b5e2\"},\"panelIndex\":\"57f5f0bf-6610-4599-aad4-37484640b5e2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_57f5f0bf-6610-4599-aad4-37484640b5e2\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":90,\"w\":24,\"h\":15,\"i\":\"32d3ab66-52e1-44e3-8c1f-1dccff3c5692\"},\"panelIndex\":\"32d3ab66-52e1-44e3-8c1f-1dccff3c5692\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_32d3ab66-52e1-44e3-8c1f-1dccff3c5692\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":90,\"w\":24,\"h\":15,\"i\":\"dd1718fd-74ee-4032-851b-db97e893825d\"},\"panelIndex\":\"dd1718fd-74ee-4032-851b-db97e893825d\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_dd1718fd-74ee-4032-851b-db97e893825d\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":105,\"w\":24,\"h\":15,\"i\":\"98a556ee-078b-4e03-93a8-29996133cdcb\"},\"panelIndex\":\"98a556ee-078b-4e03-93a8-29996133cdcb\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsXY\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"999a2d60-cb2a-451c-8d71-80d7e92e70fd\":{\"columns\":{\"ce9117a2-773c-474c-8fb1-18940cf58b38\":{\"label\":\"Top values of type\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"type\",\"isBucketed\":true,\"params\":{\"size\":5,\"orderBy\":{\"type\":\"column\",\"columnId\":\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\"},\"orderDirection\":\"asc\",\"otherBucket\":true,\"missingBucket\":false}},\"a3d10552-e352-40d0-a156-e86112c0501a\":{\"label\":\"Top values of _type\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"_type\",\"isBucketed\":true,\"params\":{\"size\":3,\"orderBy\":{\"type\":\"column\",\"columnId\":\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"Records\"},\"9c5db2f3-9eb0-4667-9a74-3318301de251\":{\"label\":\"Sum of bytes\",\"dataType\":\"number\",\"operationType\":\"sum\",\"sourceField\":\"bytes\",\"isBucketed\":false,\"scale\":\"ratio\"}},\"columnOrder\":[\"ce9117a2-773c-474c-8fb1-18940cf58b38\",\"a3d10552-e352-40d0-a156-e86112c0501a\",\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\",\"9c5db2f3-9eb0-4667-9a74-3318301de251\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"legend\":{\"isVisible\":true,\"position\":\"right\"},\"valueLabels\":\"hide\",\"fittingFunction\":\"None\",\"axisTitlesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"bar_stacked\",\"layers\":[{\"layerId\":\"999a2d60-cb2a-451c-8d71-80d7e92e70fd\",\"accessors\":[\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\",\"9c5db2f3-9eb0-4667-9a74-3318301de251\"],\"position\":\"top\",\"seriesType\":\"bar_stacked\",\"showGridlines\":false,\"xAccessor\":\"ce9117a2-773c-474c-8fb1-18940cf58b38\",\"splitAccessor\":\"a3d10552-e352-40d0-a156-e86112c0501a\"}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-layer-999a2d60-cb2a-451c-8d71-80d7e92e70fd\"}]},\"enhancements\":{},\"type\":\"lens\"}},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":105,\"w\":24,\"h\":15,\"i\":\"62a0f0b0-3589-4cef-807b-b1b4258b7a9b\"},\"panelIndex\":\"62a0f0b0-3589-4cef-807b-b1b4258b7a9b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_62a0f0b0-3589-4cef-807b-b1b4258b7a9b\"}]","refreshInterval":{"pause":true,"value":0},"timeFrom":"2015-09-20T01:56:56.132Z","timeRestore":true,"timeTo":"2015-09-21T11:18:20.471Z","title":"lens_dashboard_logstash","version":1},"coreMigrationVersion":"7.13.2","id":"f458b9f0-bd9e-11eb-9520-1b4c3ca6a781","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"21905950-bd9f-11eb-9520-1b4c3ca6a781","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:panel_2e80716f-c1b6-46f2-be2b-35db744b5031","type":"lens"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:indexpattern-datasource-layer-26e2cf99-d931-4320-9e15-9dbc148f3534","type":"index-pattern"},{"id":"aa4b8da0-bd9f-11eb-9520-1b4c3ca6a781","name":"da8843e0-6789-4aae-bcd0-81f270538719:panel_da8843e0-6789-4aae-bcd0-81f270538719","type":"lens"},{"id":"2d3f1250-bd9f-11eb-9520-1b4c3ca6a781","name":"adcd4418-7299-4efa-b369-5f71a7b4ebe0:panel_adcd4418-7299-4efa-b369-5f71a7b4ebe0","type":"lens"},{"id":"edd5a560-bda4-11eb-9520-1b4c3ca6a781","name":"869754a7-edf0-478f-a7f1-80374f63108a:panel_869754a7-edf0-478f-a7f1-80374f63108a","type":"lens"},{"id":"2c25a450-bda5-11eb-9520-1b4c3ca6a781","name":"67111cf4-338e-453f-8621-e8dea64082d1:panel_67111cf4-338e-453f-8621-e8dea64082d1","type":"lens"},{"id":"e79116e0-bd9e-11eb-9520-1b4c3ca6a781","name":"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d:panel_13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d","type":"lens"},{"id":"974fb950-bda5-11eb-9520-1b4c3ca6a781","name":"88847944-ae1b-45fd-b102-3b45f9bea04b:panel_88847944-ae1b-45fd-b102-3b45f9bea04b","type":"lens"},{"id":"21905950-bd9f-11eb-9520-1b4c3ca6a781","name":"5a7924c7-eac0-4573-9199-fecec5b82e9e:panel_5a7924c7-eac0-4573-9199-fecec5b82e9e","type":"lens"},{"id":"51b63040-bda5-11eb-9520-1b4c3ca6a781","name":"f8f49591-f071-4a96-b1ed-cd65daff5648:panel_f8f49591-f071-4a96-b1ed-cd65daff5648","type":"lens"},{"id":"b00679c0-bda5-11eb-9520-1b4c3ca6a781","name":"9f357f47-c2a0-421f-a456-9583c40837ab:panel_9f357f47-c2a0-421f-a456-9583c40837ab","type":"lens"},{"id":"652ade10-bd9f-11eb-9520-1b4c3ca6a781","name":"6cb383e9-1e80-44f9-80d5-7b8c585668db:panel_6cb383e9-1e80-44f9-80d5-7b8c585668db","type":"lens"},{"id":"7f3b5fb0-be2f-11eb-9520-1b4c3ca6a781","name":"57f5f0bf-6610-4599-aad4-37484640b5e2:panel_57f5f0bf-6610-4599-aad4-37484640b5e2","type":"lens"},{"id":"bb9e5bb0-be2f-11eb-9520-1b4c3ca6a781","name":"32d3ab66-52e1-44e3-8c1f-1dccff3c5692:panel_32d3ab66-52e1-44e3-8c1f-1dccff3c5692","type":"lens"},{"id":"dd315430-be2f-11eb-9520-1b4c3ca6a781","name":"dd1718fd-74ee-4032-851b-db97e893825d:panel_dd1718fd-74ee-4032-851b-db97e893825d","type":"lens"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"98a556ee-078b-4e03-93a8-29996133cdcb:indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"98a556ee-078b-4e03-93a8-29996133cdcb:indexpattern-datasource-layer-999a2d60-cb2a-451c-8d71-80d7e92e70fd","type":"index-pattern"},{"id":"0dbbf8b0-be3c-11eb-9520-1b4c3ca6a781","name":"62a0f0b0-3589-4cef-807b-b1b4258b7a9b:panel_62a0f0b0-3589-4cef-807b-b1b4258b7a9b","type":"lens"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,727],"type":"dashboard","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MTAsNF0="} -{"attributes":{"allowNoIndex":true,"fieldFormatMap":"{\"Target.process.parent.pgid\":{\"id\":\"string\"},\"Target.process.parent.pid\":{\"id\":\"string\"},\"Target.process.parent.ppid\":{\"id\":\"string\"},\"Target.process.parent.thread.id\":{\"id\":\"string\"},\"Target.process.pgid\":{\"id\":\"string\"},\"Target.process.pid\":{\"id\":\"string\"},\"Target.process.ppid\":{\"id\":\"string\"},\"Target.process.thread.id\":{\"id\":\"string\"},\"event.sequence\":{\"id\":\"string\"},\"event.severity\":{\"id\":\"string\"},\"process.parent.pgid\":{\"id\":\"string\"},\"process.parent.pid\":{\"id\":\"string\"},\"process.parent.ppid\":{\"id\":\"string\"},\"process.parent.thread.id\":{\"id\":\"string\"},\"process.pgid\":{\"id\":\"string\"},\"process.pid\":{\"id\":\"string\"},\"process.ppid\":{\"id\":\"string\"},\"process.thread.id\":{\"id\":\"string\"},\"destination.bytes\":{\"id\":\"bytes\"},\"destination.port\":{\"id\":\"string\"},\"http.request.body.bytes\":{\"id\":\"bytes\"},\"http.request.bytes\":{\"id\":\"bytes\"},\"http.response.body.bytes\":{\"id\":\"bytes\"},\"http.response.bytes\":{\"id\":\"bytes\"},\"http.response.status_code\":{\"id\":\"string\"},\"network.bytes\":{\"id\":\"bytes\"},\"source.bytes\":{\"id\":\"bytes\"},\"source.port\":{\"id\":\"string\"}}","fields":"[{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"Endpoint.policy.applied.artifacts.global\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.global.identifiers\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.global.identifiers.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.global.identifiers.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.global.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.user\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.user.identifiers\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.user.identifiers.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.user.identifiers.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.user.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Memory_protection.cross_session\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Memory_protection.feature\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Memory_protection.parent_to_child\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Memory_protection.self_injection\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Memory_protection.thread_count\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Memory_protection.unique_key_v1\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Ransomware.child_pids\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Ransomware.feature\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Ransomware.files\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Ransomware.files.data\",\"type\":\"binary\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"Ransomware.files.entropy\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Ransomware.files.extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Ransomware.files.metrics\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Ransomware.files.operation\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Ransomware.files.original.extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Ransomware.files.original.path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Ransomware.files.path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Ransomware.files.score\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Ransomware.score\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Ransomware.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.code_signature\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.code_signature.exists\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.code_signature.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.code_signature.subject_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.code_signature.trusted\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.code_signature.valid\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.compile_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.malware_classification.features\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"Target.dll.Ext.malware_classification.features.data.buffer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.malware_classification.features.data.decompressed_size\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.malware_classification.features.data.encoding\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.malware_classification.identifier\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.malware_classification.score\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.malware_classification.threshold\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.malware_classification.upx_packed\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.malware_classification.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.mapped_address\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.Ext.mapped_size\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.hash.md5\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.hash.sha1\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.hash.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.hash.sha512\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.pe.company\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.pe.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.pe.file_version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.pe.imphash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.pe.original_file_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.dll.pe.product\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.ancestry\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.architecture\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.authentication_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.code_signature\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.code_signature.exists\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.code_signature.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.code_signature.subject_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.code_signature.trusted\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.code_signature.valid\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.Ext.code_signature\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.Ext.code_signature.exists\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.Ext.code_signature.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.Ext.code_signature.subject_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.Ext.code_signature.trusted\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.Ext.code_signature.valid\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.Ext.compile_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.Ext.mapped_address\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.Ext.mapped_size\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.hash.md5\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.hash.sha1\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.hash.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.hash.sha512\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.pe.company\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.pe.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.pe.file_version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.pe.imphash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.pe.original_file_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.dll.pe.product\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.malware_classification.features\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"Target.process.Ext.malware_classification.features.data.buffer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.malware_classification.features.data.decompressed_size\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.malware_classification.features.data.encoding\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.malware_classification.identifier\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.malware_classification.score\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.malware_classification.threshold\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.malware_classification.upx_packed\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.malware_classification.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.services\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.session\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.token.domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.token.elevation\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.token.elevation_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.token.impersonation_level\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.token.integrity_level\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.token.integrity_level_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.token.is_appcontainer\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.token.privileges\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.token.privileges.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.token.privileges.enabled\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.token.privileges.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.token.sid\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.token.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.token.user\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.Ext.user\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.args\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.args_count\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.command_line\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.command_line.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.command_line.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"Target.process.entity_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.executable\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.executable.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.executable.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"Target.process.exit_code\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.hash.md5\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.hash.sha1\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.hash.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.hash.sha512\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.name.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.name.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.architecture\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.code_signature\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.code_signature.exists\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.code_signature.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.code_signature.subject_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.code_signature.trusted\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.code_signature.valid\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.Ext.code_signature\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.Ext.code_signature.exists\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.Ext.code_signature.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.Ext.code_signature.subject_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.Ext.code_signature.trusted\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.Ext.code_signature.valid\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.Ext.compile_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.Ext.mapped_address\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.Ext.mapped_size\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.hash.md5\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.hash.sha1\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.hash.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.hash.sha512\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.pe.company\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.pe.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.pe.file_version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.pe.imphash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.pe.original_file_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.dll.pe.product\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.real\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.real.pid\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.token.domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.token.elevation\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.token.elevation_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.token.impersonation_level\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.token.integrity_level\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.token.integrity_level_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.token.is_appcontainer\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.token.privileges\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.token.privileges.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.token.privileges.enabled\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.token.privileges.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.token.sid\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.token.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.token.user\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.Ext.user\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.args\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.args_count\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.command_line\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.command_line.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.command_line.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.entity_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.executable\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.executable.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.executable.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.exit_code\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.hash.md5\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.hash.sha1\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.hash.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.hash.sha512\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.name.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.name.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.pe.company\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.pe.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.pe.file_version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.pe.imphash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.pe.original_file_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.pe.product\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.pgid\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.pid\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.ppid\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.start\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.thread.id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.thread.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.title.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.uptime\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.working_directory\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.working_directory.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.parent.working_directory.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"Target.process.pe.company\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.pe.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.pe.file_version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.pe.imphash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.pe.original_file_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.pe.product\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.pgid\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.pid\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.ppid\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.start\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.call_stack.instruction_pointer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.call_stack.memory_section.address\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.call_stack.memory_section.protection\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.call_stack.memory_section.size\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.call_stack.module_path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.call_stack.rva\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.call_stack.symbol_info\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.parameter\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.parameter_bytes_compressed\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.parameter_bytes_compressed_present\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.service\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_allocation_offset\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_bytes\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_bytes_disasm\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_bytes_disasm_hash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.allocation_base\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.allocation_protection\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.allocation_size\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.allocation_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.bytes_address\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.bytes_allocation_offset\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.bytes_compressed\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.bytes_compressed_present\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.mapped_pe.company\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.mapped_pe.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.mapped_pe.file_version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.mapped_pe.imphash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.mapped_pe.original_file_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.mapped_pe.product\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.mapped_pe_detected\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.memory_pe.company\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.memory_pe.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.memory_pe.file_version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.memory_pe.imphash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.memory_pe.original_file_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.memory_pe.product\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.memory_pe_detected\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.region_base\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.region_protection\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.region_size\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.region_state\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_details.strings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.start_address_module\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.token.domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.token.elevation\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.token.elevation_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.token.impersonation_level\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.token.integrity_level\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.token.integrity_level_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.token.is_appcontainer\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.token.privileges\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.token.privileges.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.token.privileges.enabled\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.token.privileges.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.token.sid\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.token.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.token.user\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.Ext.uptime\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.thread.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.title.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"Target.process.uptime\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.working_directory\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.working_directory.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Target.process.working_directory.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"agent.ephemeral_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"data_stream.dataset\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"data_stream.namespace\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"data_stream.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.city_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.continent_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.country_iso_code\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.country_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.location\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.region_iso_code\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.region_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.code_signature\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.code_signature.exists\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.code_signature.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.code_signature.subject_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.code_signature.trusted\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.code_signature.valid\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.compile_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.malware_classification.features\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"dll.Ext.malware_classification.features.data.buffer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.malware_classification.features.data.decompressed_size\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.malware_classification.features.data.encoding\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.malware_classification.identifier\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.malware_classification.score\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.malware_classification.threshold\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.malware_classification.upx_packed\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.malware_classification.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.mapped_address\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.mapped_size\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.hash.md5\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.hash.sha1\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.hash.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.hash.sha512\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.pe.company\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.pe.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.pe.file_version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.pe.imphash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.pe.original_file_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.pe.product\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ecs.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elastic.agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elastic.agent.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.action\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.category\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.code\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.created\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.dataset\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.hash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.ingested\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.kind\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.module\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.outcome\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.provider\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.sequence\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.severity\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.code_signature\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.code_signature.exists\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.code_signature.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.code_signature.subject_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.code_signature.trusted\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.code_signature.valid\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.entry_modified\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.code_page\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.collection\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.collection.hash.md5\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.collection.hash.sha1\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.collection.hash.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.collection.hash.sha512\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.errors\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.errors.count\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.errors.error_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.file_extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.project_file\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.project_file.hash.md5\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.project_file.hash.sha1\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.project_file.hash.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.project_file.hash.sha512\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.stream\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.stream.hash.md5\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.stream.hash.sha1\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.stream.hash.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.stream.hash.sha512\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.stream.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.stream.raw_code\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.macro.stream.raw_code_size\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.malware_classification.features\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"file.Ext.malware_classification.features.data.buffer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.malware_classification.features.data.decompressed_size\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.malware_classification.features.data.encoding\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.malware_classification.identifier\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.malware_classification.score\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.malware_classification.threshold\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.malware_classification.upx_packed\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.malware_classification.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.original\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.original.gid\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.original.group\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.original.mode\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.original.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.original.owner\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.original.path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.original.uid\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.quarantine_path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.quarantine_result\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.temp_file_path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.windows\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.windows.zone_identifier\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.accessed\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.attributes\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.created\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.ctime\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.device\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.directory\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.drive_letter\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.gid\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.group\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.hash.md5\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.hash.sha1\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.hash.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.hash.sha512\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.inode\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.mime_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.mode\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.mtime\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.owner\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.path.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.path.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"file.pe.company\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.pe.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.pe.file_version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.pe.imphash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.pe.original_file_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.pe.product\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.size\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.target_path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.target_path.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.target_path.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"file.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.uid\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"group.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"group.Ext.real\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"group.Ext.real.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"group.Ext.real.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"group.domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"group.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"group.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.architecture\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.city_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.continent_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.country_iso_code\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.country_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.location\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.region_iso_code\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.region_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.hostname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.mac\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.Ext.variant\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.family\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.full\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.full.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.full.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"host.os.kernel\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.name.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.name.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"host.os.platform\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.uptime\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.Ext.real\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.Ext.real.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.Ext.real.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.email\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.full_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.full_name.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"host.user.group.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.group.Ext.real\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.group.Ext.real.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.group.Ext.real.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.group.domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.group.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.group.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.hash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.name.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"process.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.ancestry\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.architecture\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.authentication_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.code_signature\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.code_signature.exists\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.code_signature.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.code_signature.subject_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.code_signature.trusted\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.code_signature.valid\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.Ext.code_signature\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.Ext.code_signature.exists\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.Ext.code_signature.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.Ext.code_signature.subject_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.Ext.code_signature.trusted\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.Ext.code_signature.valid\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.Ext.compile_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.Ext.mapped_address\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.Ext.mapped_size\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.hash.md5\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.hash.sha1\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.hash.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.hash.sha512\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.pe.company\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.pe.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.pe.file_version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.pe.imphash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.pe.original_file_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.dll.pe.product\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.malware_classification.features\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"process.Ext.malware_classification.features.data.buffer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.malware_classification.features.data.decompressed_size\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.malware_classification.features.data.encoding\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.malware_classification.identifier\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.malware_classification.score\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.malware_classification.threshold\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.malware_classification.upx_packed\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.malware_classification.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.services\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.session\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.token.domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.token.elevation\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.token.elevation_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.token.impersonation_level\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.token.integrity_level\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.token.integrity_level_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.token.is_appcontainer\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.token.privileges\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.token.privileges.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.token.privileges.enabled\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.token.privileges.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.token.sid\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.token.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.token.user\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.user\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.args\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.args_count\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.command_line\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.command_line.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.command_line.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"process.entity_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.executable\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.executable.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.executable.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"process.exit_code\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.hash.md5\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.hash.sha1\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.hash.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.hash.sha512\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.name.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.name.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.architecture\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.code_signature\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.code_signature.exists\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.code_signature.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.code_signature.subject_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.code_signature.trusted\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.code_signature.valid\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.Ext.code_signature\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.Ext.code_signature.exists\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.Ext.code_signature.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.Ext.code_signature.subject_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.Ext.code_signature.trusted\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.Ext.code_signature.valid\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.Ext.compile_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.Ext.mapped_address\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.Ext.mapped_size\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.hash.md5\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.hash.sha1\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.hash.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.hash.sha512\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.pe.company\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.pe.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.pe.file_version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.pe.imphash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.pe.original_file_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.dll.pe.product\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.real\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.real.pid\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.token.domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.token.elevation\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.token.elevation_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.token.impersonation_level\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.token.integrity_level\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.token.integrity_level_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.token.is_appcontainer\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.token.privileges\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.token.privileges.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.token.privileges.enabled\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.token.privileges.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.token.sid\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.token.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.token.user\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.Ext.user\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.args\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.args_count\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.command_line\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.command_line.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.command_line.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"process.parent.entity_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.executable\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.executable.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.executable.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"process.parent.exit_code\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.hash.md5\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.hash.sha1\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.hash.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.hash.sha512\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.name.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.name.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"process.parent.pe.company\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.pe.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.pe.file_version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.pe.imphash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.pe.original_file_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.pe.product\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.pgid\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.pid\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.ppid\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.start\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.thread.id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.thread.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.title.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"process.parent.uptime\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.working_directory\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.working_directory.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.working_directory.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"process.pe.company\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pe.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pe.file_version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pe.imphash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pe.original_file_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pe.product\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pgid\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pid\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.ppid\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.start\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.call_stack.instruction_pointer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.call_stack.memory_section.address\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.call_stack.memory_section.protection\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.call_stack.memory_section.size\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.call_stack.module_path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.call_stack.rva\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.call_stack.symbol_info\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.parameter\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.parameter_bytes_compressed\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.parameter_bytes_compressed_present\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.service\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_allocation_offset\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_bytes\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_bytes_disasm\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_bytes_disasm_hash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.allocation_base\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.allocation_protection\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.allocation_size\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.allocation_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.bytes_address\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.bytes_allocation_offset\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.bytes_compressed\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.bytes_compressed_present\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.mapped_pe.company\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.mapped_pe.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.mapped_pe.file_version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.mapped_pe.imphash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.mapped_pe.original_file_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.mapped_pe.product\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.mapped_pe_detected\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.memory_pe.company\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.memory_pe.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.memory_pe.file_version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.memory_pe.imphash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.memory_pe.original_file_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.memory_pe.product\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.memory_pe_detected\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.region_base\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.region_protection\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.region_size\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.region_state\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_details.strings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.start_address_module\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.token.domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.token.elevation\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.token.elevation_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.token.impersonation_level\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.token.integrity_level\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.token.integrity_level_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.token.is_appcontainer\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.token.privileges\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.token.privileges.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.token.privileges.enabled\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.token.privileges.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.token.sid\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.token.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.token.user\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.Ext.uptime\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.title.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"process.uptime\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.working_directory\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.working_directory.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.working_directory.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"rule.author\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.category\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.license\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.reference\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.ruleset\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.uuid\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.city_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.continent_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.country_iso_code\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.country_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.location\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.region_iso_code\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.region_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.framework\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.tactic.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.tactic.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.tactic.reference\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.technique.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.technique.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.technique.name.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"threat.technique.reference\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.Ext.real\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.Ext.real.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.Ext.real.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.email\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.full_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.full_name.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"user.group.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.group.Ext.real\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.group.Ext.real.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.group.Ext.real.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.group.domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.group.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.group.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.hash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.name.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"event.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.Ext.correlation\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.Ext.correlation.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.entropy\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.Ext.header_data\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"file.Ext.monotonic_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.Ext.load_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.code_signature.exists\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.code_signature.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.code_signature.subject_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.code_signature.trusted\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.code_signature.valid\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.address\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.packets\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.port\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.registered_domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.top_level_domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.Ext.options\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.Ext.status\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.registered_domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.subdomain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.top_level_domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.resolved_ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.body.bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.body.content\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.body.content.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"http.request.bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.Ext.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.body.bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.body.content\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.body.content.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"http.response.bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.status_code\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.community_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.direction\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.iana_number\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.packets\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.protocol\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.transport\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.address\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.packets\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.port\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.registered_domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.top_level_domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"package.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.Ext.defense_evasions\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.code_signature.exists\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.code_signature.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.code_signature.subject_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.code_signature.trusted\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.code_signature.valid\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.code_signature.exists\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.code_signature.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.code_signature.subject_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.code_signature.trusted\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.code_signature.valid\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"registry.data.bytes\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"registry.data.strings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"registry.hive\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"registry.key\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"registry.path\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"registry.value\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]","timeFieldName":"@timestamp","title":"logs-*"},"coreMigrationVersion":"7.13.2","id":"logs-*","migrationVersion":{"index-pattern":"7.11.0"},"references":[],"sort":[1623693556928,728],"type":"index-pattern","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MTEsNF0="} -{"attributes":{"description":"this is a logstash saved query","filters":[],"query":{"language":"kuery","query":"extension.raw :\"gif\" and machine.os.raw :\"ios\" "},"timefilter":{"from":"2015-09-20T01:56:56.132Z","refreshInterval":{"pause":true,"value":0},"to":"2015-09-21T11:18:20.471Z"},"title":"logstash_saved_query"},"coreMigrationVersion":"7.13.2","id":"logstash_saved_query","references":[],"sort":[1623693556928,729],"type":"query","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MTIsNF0="} -{"attributes":{"allowNoIndex":true,"fieldFormatMap":"{\"event.sequence\":{\"id\":\"string\"},\"event.severity\":{\"id\":\"string\"}}","fields":"[{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"data_stream.dataset\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"data_stream.namespace\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"data_stream.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ecs.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elastic.agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elastic.agent.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.action\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.category\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.code\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.created\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.dataset\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.hash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.ingested\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.kind\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.module\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.outcome\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.provider\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.sequence\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.severity\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.architecture\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.domain\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.hostname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.mac\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.Ext\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.Ext.variant\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.family\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.full\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.full.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.full.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"host.os.kernel\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.name.caseless\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.name.text\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"host.os.platform\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.uptime\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.cpu\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.cpu.endpoint\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.cpu.endpoint.histogram\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.cpu.endpoint.latest\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.cpu.endpoint.mean\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.disks\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"Endpoint.metrics.disks.device\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.disks.endpoint_drive\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.disks.free\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.disks.fstype\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.disks.mount\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.disks.total\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.memory\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.memory.endpoint\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.memory.endpoint.private\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.memory.endpoint.private.latest\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.memory.endpoint.private.mean\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.threads\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"Endpoint.metrics.uptime\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.uptime.endpoint\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.metrics.uptime.system\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.end\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.start\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.actions\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.actions.message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.actions.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.actions.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"Endpoint.policy.applied.artifacts.global\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.global.identifiers\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.global.identifiers.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.global.identifiers.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.global.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.user\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.user.identifiers\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.user.identifiers.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.user.identifiers.sha256\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.artifacts.user.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.configurations\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"Endpoint.policy.applied.configurations.antivirus_registration\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"Endpoint.policy.applied.configurations.antivirus_registration.concerned_actions\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.configurations.antivirus_registration.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.configurations.events\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.configurations.events.concerned_actions\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.configurations.events.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.configurations.logging\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.configurations.logging.concerned_actions\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.configurations.logging.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.configurations.malware\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.configurations.malware.concerned_actions\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.configurations.malware.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.configurations.streaming\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.configurations.streaming.concerned_actions\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.configurations.streaming.status\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Endpoint.policy.applied.response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"Endpoint.policy.applied.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]","timeFieldName":"@timestamp","title":"metrics-*"},"coreMigrationVersion":"7.13.2","id":"metrics-*","migrationVersion":{"index-pattern":"7.11.0"},"references":[],"sort":[1623693556928,730],"type":"index-pattern","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MTMsNF0="} -{"attributes":{"description":"Shakespeare query","filters":[],"query":{"language":"kuery","query":"speaker : \"OTHELLO\" and play_name :\"Othello\" "},"title":"shakespeare_current_query"},"coreMigrationVersion":"7.13.2","id":"shakespeare_current_query","references":[],"sort":[1623693556928,731],"type":"query","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MTQsNF0="} -{"attributes":{"@created":"2021-05-27T18:53:18.432Z","@timestamp":"2021-05-27T19:46:12.539Z","assets":{},"colors":["#37988d","#c19628","#b83c6f","#3f9939","#1785b0","#ca5f35","#45bdb0","#f2bc33","#e74b8b","#4fbf48","#1ea6dc","#fd7643","#72cec3","#f5cc5d","#ec77a8","#7acf74","#4cbce4","#fd986f","#a1ded7","#f8dd91","#f2a4c5","#a6dfa2","#86d2ed","#fdba9f","#000000","#444444","#777777","#BBBBBB","#FFFFFF","rgba(255,255,255,0)"],"css":".canvasPage {\n\n}","height":720,"isWriteable":true,"name":"logstash-canvas-workpad","page":1,"pages":[{"elements":[{"expression":"savedLens id=\"bb9e5bb0-be2f-11eb-9520-1b4c3ca6a781\" timerange={timerange from=\"now-15y\" to=\"now\"}\n| render","filter":null,"id":"element-56d2ba72-f227-4d04-9478-a1d6f0c7e601","position":{"angle":0,"height":300,"left":20,"parent":null,"top":20,"width":500}},{"expression":"savedVisualization id=\"0d8a8860-623a-11eb-aebf-c306684b328d\" timerange={timerange from=\"now-15y\" to=\"now\"}\n| render","filter":null,"id":"element-afbaa26e-10e7-47d4-bb41-b061dfdced2b","position":{"angle":0,"height":300,"left":527,"parent":null,"top":20,"width":500}}],"groups":[],"id":"page-0f9ef2da-2868-4c0b-9223-fd3c9e53d6c9","style":{"background":"#FFF"},"transition":{}},{"elements":[{"expression":"image dataurl=null mode=\"contain\"\n| render","id":"element-c5534ef7-68c4-46bc-b35a-9e43a7f118c3","position":{"angle":0,"height":107,"left":20,"parent":null,"top":20,"width":132}},{"expression":"filters\n| essql query=\"SELECT machine.os.raw FROM \\\"logstash-*\\\"\"\n| pointseries x=\"machine.os.raw\" y=\"size(machine.os.raw)\" color=\"machine.os.raw\" size=\"sum(machine.os.raw)\"\n| plot defaultStyle={seriesStyle points=5 fill=1}\n| render","id":"element-5f7a3312-0e77-471c-9b8f-f98cb38075fb","position":{"angle":0,"height":192,"left":221,"parent":null,"top":56,"width":451}},{"expression":"timefilterControl compact=true column=@timestamp\n| render","filter":"timefilter from=\"now-29y\" to=now column=@timestamp","id":"element-6e00dcf4-06fe-4bd9-9315-d32d9d3fac5f","position":{"angle":0,"height":50,"left":221,"parent":null,"top":-1,"width":500}},{"expression":"filters\n| esdocs index=\"logstash-*\" fields=\"@timestamp, response.raw\"\n| pointseries x=\"size(response.raw)\" y=\"@timestamp\" color=\"response.raw\"\n| plot\n| render","id":"element-20281fac-1c3a-4ee3-9132-44379fb60b74","position":{"angle":0,"height":262,"left":51,"parent":null,"top":304,"width":590}},{"expression":"filters\n| timelion query=\".es(index=logstash-*, metric=sum:bytes)\"\n| pointseries x=\"@timestamp\" y=\"sum(value)\"\n| plot defaultStyle={seriesStyle lines=3}\n| render","id":"element-337b0548-5d6d-44cd-a324-eb50d63c7bd0","position":{"angle":0,"height":309,"left":648,"parent":null,"top":290,"width":369}},{"expression":"savedLens id=\"bb9e5bb0-be2f-11eb-9520-1b4c3ca6a781\" timerange={timerange from=\"now-15y\" to=\"now\"}\n| render","filter":null,"id":"element-353e5583-0dbb-4a6b-bac7-3b2a6b305397","position":{"angle":0,"height":181.99999999999997,"left":855,"parent":"group-d2618a19-3982-414e-93df-b2cb165b7c7e","top":15.000000000000014,"width":76.961271102284}},{"expression":"savedVisualization id=\"0d8a8860-623a-11eb-aebf-c306684b328d\" timerange={timerange from=\"now-15y\" to=\"now\"}\n| render","filter":null,"id":"element-0e5501a6-9e87-42bc-b539-1e697e62051b","position":{"angle":0,"height":181.99999999999997,"left":933.038728897716,"parent":"group-d2618a19-3982-414e-93df-b2cb165b7c7e","top":15.000000000000014,"width":76.961271102284}}],"groups":[],"id":"page-59c3cf09-1811-4324-995b-7336c1c11ab8","style":{"background":"#FFF"},"transition":{}}],"variables":[],"width":1080},"coreMigrationVersion":"7.13.2","id":"workpad-f2024ca3-e366-447a-b3af-7db4400646ef","migrationVersion":{"canvas-workpad":"7.0.0"},"references":[],"sort":[1623693556928,732],"type":"canvas-workpad","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MTUsNF0="} -{"exportedCount":87,"missingRefCount":0,"missingReferences":[]} \ No newline at end of file +{"attributes":{"accessCount":0,"accessDate":1621977234367,"createDate":1621977234367,"url":"/app/dashboards#/view/154944b0-6249-11eb-aebf-c306684b328d?embed=true&_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15y,to:now))&_a=(description:%27%27,filters:!(),fullScreenMode:!f,options:(darkTheme:!f,hidePanelTitles:!f,useMargins:!t),panels:!((embeddableConfig:(enhancements:()),gridData:(h:15,i:%271%27,w:24,x:0,y:0),id:%2736b91810-6239-11eb-aebf-c306684b328d%27,panelIndex:%271%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:(),vis:!n),gridData:(h:15,i:%272%27,w:24,x:24,y:0),id:%270a274320-61cc-11eb-aebf-c306684b328d%27,panelIndex:%272%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%273%27,w:24,x:0,y:15),id:e4aef350-623d-11eb-aebf-c306684b328d,panelIndex:%273%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%274%27,w:24,x:24,y:15),id:f92e5630-623e-11eb-aebf-c306684b328d,panelIndex:%274%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:(),vis:!n),gridData:(h:15,i:%275%27,w:24,x:0,y:30),id:%279853d4d0-623d-11eb-aebf-c306684b328d%27,panelIndex:%275%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:(),vis:!n),gridData:(h:15,i:%276%27,w:24,x:24,y:30),id:%276ecb33b0-623d-11eb-aebf-c306684b328d%27,panelIndex:%276%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:(),vis:!n),gridData:(h:15,i:%277%27,w:24,x:0,y:45),id:b8e35c80-623c-11eb-aebf-c306684b328d,panelIndex:%277%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%278%27,w:24,x:24,y:45),id:f1bc75d0-6239-11eb-aebf-c306684b328d,panelIndex:%278%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%279%27,w:24,x:0,y:60),id:%270d8a8860-623a-11eb-aebf-c306684b328d%27,panelIndex:%279%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2710%27,w:24,x:24,y:60),id:d79fe3d0-6239-11eb-aebf-c306684b328d,panelIndex:%2710%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2711%27,w:24,x:0,y:75),id:%27318375a0-6240-11eb-aebf-c306684b328d%27,panelIndex:%2711%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2712%27,w:24,x:24,y:75),id:e461eb20-6245-11eb-aebf-c306684b328d,panelIndex:%2712%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2713%27,w:24,x:0,y:90),id:%2725bdc750-6242-11eb-aebf-c306684b328d%27,panelIndex:%2713%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2714%27,w:24,x:24,y:90),id:%2771dd7bc0-6248-11eb-aebf-c306684b328d%27,panelIndex:%2714%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2715%27,w:24,x:0,y:105),id:%276aea48a0-6240-11eb-aebf-c306684b328d%27,panelIndex:%2715%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2716%27,w:24,x:24,y:105),id:%2732b681f0-6241-11eb-aebf-c306684b328d%27,panelIndex:%2716%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2717%27,w:24,x:0,y:120),id:ccca99e0-6244-11eb-aebf-c306684b328d,panelIndex:%2717%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2718%27,w:24,x:24,y:120),id:a4d7be80-6245-11eb-aebf-c306684b328d,panelIndex:%2718%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2719%27,w:24,x:0,y:135),id:c94d8440-6248-11eb-aebf-c306684b328d,panelIndex:%2719%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%2720%27,w:24,x:24,y:135),id:db6226f0-61c0-11eb-aebf-c306684b328d,panelIndex:%2720%27,type:search,version:%277.13.1%27)),query:(language:lucene,query:%27%27),tags:!(),timeRestore:!f,title:logstash_dashboardwithtime,viewMode:view)"},"coreMigrationVersion":"7.13.5","id":"058bc10f0578013fc41ddedc9a1dcd1e","references":[],"sort":[1651599812857,5],"type":"url","updated_at":"2022-05-03T17:43:32.857Z","version":"WzE3LDFd"} +{"attributes":{"color":"#ba898f","description":"","name":"By value tag"},"coreMigrationVersion":"7.13.5","id":"07f48f70-ca29-11eb-bf5e-3de94e83d4f0","references":[],"sort":[1651599812857,6],"type":"tag","updated_at":"2022-05-03T17:43:32.857Z","version":"WzE4LDFd"} +{"attributes":{"fieldAttrs":"{\"machine.os\":{\"count\":1},\"spaces\":{\"count\":1},\"type\":{\"count\":1},\"bytes_scripted\":{\"count\":1}}","fields":"[{\"count\":1,\"script\":\"doc['bytes'].value*1024\",\"lang\":\"painless\",\"name\":\"bytes_scripted\",\"type\":\"number\",\"scripted\":true,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false}]","runtimeFieldMap":"{}","timeFieldName":"@timestamp","title":"logstash-*"},"coreMigrationVersion":"7.13.5","id":"56b34100-619d-11eb-aebf-c306684b328d","migrationVersion":{"index-pattern":"7.11.0"},"references":[],"sort":[1651599812857,7],"type":"index-pattern","updated_at":"2022-05-03T17:43:32.857Z","version":"WzIyLDFd"} +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.5\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"60aaea59-d871-4e90-9ff3-78946d6bef90\"},\"panelIndex\":\"60aaea59-d871-4e90-9ff3-78946d6bef90\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsPie\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"2174c5fe-75d2-43ae-9c79-1a7cc7bbbaea\":{\"columns\":{\"65625f0d-e7f1-4370-b939-7db27af74de7\":{\"label\":\"Top values of geo.srcdest\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"geo.srcdest\",\"isBucketed\":true,\"params\":{\"size\":18,\"orderBy\":{\"type\":\"column\",\"columnId\":\"553a353f-dac5-4a52-a25d-52c6e1462597\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"553a353f-dac5-4a52-a25d-52c6e1462597\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"Records\"}},\"columnOrder\":[\"65625f0d-e7f1-4370-b939-7db27af74de7\",\"553a353f-dac5-4a52-a25d-52c6e1462597\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"shape\":\"donut\",\"layers\":[{\"layerId\":\"2174c5fe-75d2-43ae-9c79-1a7cc7bbbaea\",\"groups\":[\"65625f0d-e7f1-4370-b939-7db27af74de7\",\"65625f0d-e7f1-4370-b939-7db27af74de7\",\"65625f0d-e7f1-4370-b939-7db27af74de7\"],\"metric\":\"553a353f-dac5-4a52-a25d-52c6e1462597\",\"numberDisplay\":\"percent\",\"categoryDisplay\":\"default\",\"legendDisplay\":\"default\",\"nestedLegend\":false}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-layer-2174c5fe-75d2-43ae-9c79-1a7cc7bbbaea\"}]},\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"9ed45b8b-534b-4fac-9fee-436896b90039\",\"triggers\":[\"FILTER_TRIGGER\"],\"action\":{\"name\":\"circular drilldown\",\"config\":{\"useCurrentFilters\":true,\"useCurrentDateRange\":true},\"factoryId\":\"DASHBOARD_TO_DASHBOARD_DRILLDOWN\"}}]}}}}]","timeRestore":false,"title":"lens_panel_drilldown","version":1},"coreMigrationVersion":"7.13.5","id":"35ce3b30-ca29-11eb-bf5e-3de94e83d4f0","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"60aaea59-d871-4e90-9ff3-78946d6bef90:indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"60aaea59-d871-4e90-9ff3-78946d6bef90:indexpattern-datasource-layer-2174c5fe-75d2-43ae-9c79-1a7cc7bbbaea","type":"index-pattern"},{"id":"08dec860-ca29-11eb-bf5e-3de94e83d4f0","name":"60aaea59-d871-4e90-9ff3-78946d6bef90:drilldown:DASHBOARD_TO_DASHBOARD_DRILLDOWN:9ed45b8b-534b-4fac-9fee-436896b90039:dashboardId","type":"dashboard"},{"id":"07f48f70-ca29-11eb-bf5e-3de94e83d4f0","name":"tag-07f48f70-ca29-11eb-bf5e-3de94e83d4f0","type":"tag"}],"sort":[1651613174194,1151],"type":"dashboard","updated_at":"2022-05-03T21:26:14.194Z","version":"WzcxMywxXQ=="} +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[{\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"geo.srcdest\",\"negate\":true,\"params\":[\"CN:CN\",\"IN:CN\",\"CN:IN\",\"IN:IN\",\"CN:US\",\"IN:US\",\"US:IN\",\"US:CN\",\"ID:CN\",\"US:US\",\"CN:ID\",\"PK:CN\",\"BR:CN\",\"CN:PK\",\"BD:CN\",\"CN:BR\",\"IN:PK\",\"NG:CN\"],\"type\":\"phrases\",\"value\":\"CN:CN, IN:CN, CN:IN, IN:IN, CN:US, IN:US, US:IN, US:CN, ID:CN, US:US, CN:ID, PK:CN, BR:CN, CN:PK, BD:CN, CN:BR, IN:PK, NG:CN\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"geo.srcdest\":\"CN:CN\"}},{\"match_phrase\":{\"geo.srcdest\":\"IN:CN\"}},{\"match_phrase\":{\"geo.srcdest\":\"CN:IN\"}},{\"match_phrase\":{\"geo.srcdest\":\"IN:IN\"}},{\"match_phrase\":{\"geo.srcdest\":\"CN:US\"}},{\"match_phrase\":{\"geo.srcdest\":\"IN:US\"}},{\"match_phrase\":{\"geo.srcdest\":\"US:IN\"}},{\"match_phrase\":{\"geo.srcdest\":\"US:CN\"}},{\"match_phrase\":{\"geo.srcdest\":\"ID:CN\"}},{\"match_phrase\":{\"geo.srcdest\":\"US:US\"}},{\"match_phrase\":{\"geo.srcdest\":\"CN:ID\"}},{\"match_phrase\":{\"geo.srcdest\":\"PK:CN\"}},{\"match_phrase\":{\"geo.srcdest\":\"BR:CN\"}},{\"match_phrase\":{\"geo.srcdest\":\"CN:PK\"}},{\"match_phrase\":{\"geo.srcdest\":\"BD:CN\"}},{\"match_phrase\":{\"geo.srcdest\":\"CN:BR\"}},{\"match_phrase\":{\"geo.srcdest\":\"IN:PK\"}},{\"match_phrase\":{\"geo.srcdest\":\"NG:CN\"}}]}},\"$state\":{\"store\":\"appState\"}}]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.5\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"095e13b2-d0ac-47db-a62b-0aca28931402\"},\"panelIndex\":\"095e13b2-d0ac-47db-a62b-0aca28931402\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsXY\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"f61694eb-94ed-495d-9ce8-63592f040b0b\":{\"columns\":{\"75ddcdb4-3050-4545-b401-509384b0d532\":{\"label\":\"Top values of machine.os.raw\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"machine.os.raw\",\"isBucketed\":true,\"params\":{\"size\":3,\"orderBy\":{\"type\":\"column\",\"columnId\":\"2f97b0f5-f0ff-40f2-abb0-bb7d1081a126\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"273e31ef-7c2d-4d0e-9063-5528f4011a51\":{\"label\":\"@timestamp\",\"dataType\":\"date\",\"operationType\":\"date_histogram\",\"sourceField\":\"@timestamp\",\"isBucketed\":true,\"scale\":\"interval\",\"params\":{\"interval\":\"auto\"}},\"2f97b0f5-f0ff-40f2-abb0-bb7d1081a126\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"Records\"},\"2eb30654-0ead-40ac-92ab-d8d113e25ac5\":{\"label\":\"Average of bytes\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"bytes\",\"isBucketed\":false,\"scale\":\"ratio\"}},\"columnOrder\":[\"75ddcdb4-3050-4545-b401-509384b0d532\",\"273e31ef-7c2d-4d0e-9063-5528f4011a51\",\"2f97b0f5-f0ff-40f2-abb0-bb7d1081a126\",\"2eb30654-0ead-40ac-92ab-d8d113e25ac5\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"legend\":{\"isVisible\":true,\"position\":\"right\"},\"valueLabels\":\"hide\",\"fittingFunction\":\"None\",\"axisTitlesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"bar_stacked\",\"layers\":[{\"layerId\":\"f61694eb-94ed-495d-9ce8-63592f040b0b\",\"accessors\":[\"2f97b0f5-f0ff-40f2-abb0-bb7d1081a126\",\"2eb30654-0ead-40ac-92ab-d8d113e25ac5\"],\"position\":\"top\",\"seriesType\":\"bar_stacked\",\"showGridlines\":false,\"xAccessor\":\"273e31ef-7c2d-4d0e-9063-5528f4011a51\",\"splitAccessor\":\"75ddcdb4-3050-4545-b401-509384b0d532\"}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-layer-f61694eb-94ed-495d-9ce8-63592f040b0b\"}]},\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"da7ad2b6-1e4a-40b5-9123-d0ec2bde858d\",\"triggers\":[\"FILTER_TRIGGER\"],\"action\":{\"name\":\"lens_panel_drilldown\",\"config\":{\"useCurrentFilters\":true,\"useCurrentDateRange\":true},\"factoryId\":\"DASHBOARD_TO_DASHBOARD_DRILLDOWN\"}}]}}}},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":15,\"w\":24,\"h\":15,\"i\":\"a254a623-a9af-4372-851b-572fa95b0902\"},\"panelIndex\":\"a254a623-a9af-4372-851b-572fa95b0902\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"\",\"description\":\"\",\"type\":\"vega\",\"params\":{\"spec\":\"{\\n/*\\n\\nWelcome to Vega visualizations. Here you can design your own dataviz from scratch using a declarative language called Vega, or its simpler form Vega-Lite. In Vega, you have the full control of what data is loaded, even from multiple sources, how that data is transformed, and what visual elements are used to show it. Use help icon to view Vega examples, tutorials, and other docs. Use the wrench icon to reformat this text, or to remove comments.\\n\\nThis example graph shows the document count in all indexes in the current time range. You might need to adjust the time filter in the upper right corner.\\n*/\\n\\n $schema: https://vega.github.io/schema/vega-lite/v4.json\\n title: Event counts from all indexes\\n\\n // Define the data source\\n data: {\\n url: {\\n/*\\nAn object instead of a string for the \\\"url\\\" param is treated as an Elasticsearch query. Anything inside this object is not part of the Vega language, but only understood by Kibana and Elasticsearch server. This query counts the number of documents per time interval, assuming you have a @timestamp field in your data.\\n\\nKibana has a special handling for the fields surrounded by \\\"%\\\". They are processed before the the query is sent to Elasticsearch. This way the query becomes context aware, and can use the time range and the dashboard filters.\\n*/\\n\\n // Apply dashboard context filters when set\\n %context%: true\\n // Filter the time picker (upper right corner) with this field\\n %timefield%: @timestamp\\n\\n/*\\nSee .search() documentation for : https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/api-reference.html#api-search\\n*/\\n\\n // Which index to search\\n index: _all\\n // Aggregate data by the time field into time buckets, counting the number of documents in each bucket.\\n body: {\\n aggs: {\\n time_buckets: {\\n date_histogram: {\\n // Use date histogram aggregation on @timestamp field\\n field: @timestamp\\n // The interval value will depend on the daterange picker (true), or use an integer to set an approximate bucket count\\n interval: {%autointerval%: true}\\n // Make sure we get an entire range, even if it has no data\\n extended_bounds: {\\n // Use the current time range's start and end\\n min: {%timefilter%: \\\"min\\\"}\\n max: {%timefilter%: \\\"max\\\"}\\n }\\n // Use this for linear (e.g. line, area) graphs. Without it, empty buckets will not show up\\n min_doc_count: 0\\n }\\n }\\n }\\n // Speed up the response by only including aggregation results\\n size: 0\\n }\\n }\\n/*\\nElasticsearch will return results in this format:\\n\\naggregations: {\\n time_buckets: {\\n buckets: [\\n {\\n key_as_string: 2015-11-30T22:00:00.000Z\\n key: 1448920800000\\n doc_count: 0\\n },\\n {\\n key_as_string: 2015-11-30T23:00:00.000Z\\n key: 1448924400000\\n doc_count: 0\\n }\\n ...\\n ]\\n }\\n}\\n\\nFor our graph, we only need the list of bucket values. Use the format.property to discard everything else.\\n*/\\n format: {property: \\\"aggregations.time_buckets.buckets\\\"}\\n }\\n\\n // \\\"mark\\\" is the graphics element used to show our data. Other mark values are: area, bar, circle, line, point, rect, rule, square, text, and tick. See https://vega.github.io/vega-lite/docs/mark.html\\n mark: line\\n\\n // \\\"encoding\\\" tells the \\\"mark\\\" what data to use and in what way. See https://vega.github.io/vega-lite/docs/encoding.html\\n encoding: {\\n x: {\\n // The \\\"key\\\" value is the timestamp in milliseconds. Use it for X axis.\\n field: key\\n type: temporal\\n axis: {title: false} // Customize X axis format\\n }\\n y: {\\n // The \\\"doc_count\\\" is the count per bucket. Use it for Y axis.\\n field: doc_count\\n type: quantitative\\n axis: {title: \\\"Document count\\\"}\\n }\\n }\\n}\\n\"},\"uiState\":{},\"data\":{\"aggs\":[],\"searchSource\":{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"index\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"key\":\"geo.srcdest\",\"negate\":false,\"params\":{\"query\":\"CN:CN\"},\"type\":\"phrase\"},\"query\":{\"match_phrase\":{\"geo.srcdest\":\"CN:CN\"}}}]}}},\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"cfd2521d-15a0-4c64-b0ab-d2dc18f396e3\",\"triggers\":[\"CONTEXT_MENU_TRIGGER\"],\"action\":{\"name\":\"URL\",\"config\":{\"url\":{\"template\":\"http://localhost:5601/app/discover#/view/4acce030-ca2a-11eb-bf5e-3de94e83d4f0?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15y,to:now))&_a=(columns:!(),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'43fcac20-ca27-11eb-bf5e-3de94e83d4f0',key:ip,negate:!f,params:(query:'57.237.11.219'),type:phrase),query:(match_phrase:(ip:'57.237.11.219')))),index:'43fcac20-ca27-11eb-bf5e-3de94e83d4f0',interval:auto,query:(language:kuery,query:''),sort:!(!('@timestamp',desc)))\"},\"openInNewTab\":true,\"encodeUrl\":true},\"factoryId\":\"URL_DRILLDOWN\"}}]}}}},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"5de83d82-bbd1-4d30-be61-dd6724f32c07\"},\"panelIndex\":\"5de83d82-bbd1-4d30-be61-dd6724f32c07\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"\",\"description\":\"\",\"type\":\"metrics\",\"params\":{\"annotations\":[{\"fields\":\"response.raw\",\"template\":\"{{response.raw}}\",\"index_pattern\":\"logstash-*\",\"query_string\":{\"query\":\"response.raw :\\\"404\\\" \",\"language\":\"kuery\"},\"color\":\"#F00\",\"icon\":\"fa-bomb\",\"id\":\"37395960-ca28-11eb-9eac-2f3ccefcbeef\",\"ignore_global_filters\":1,\"ignore_panel_filters\":1,\"time_field\":\"@timestamp\"}],\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logstash-*\",\"interval\":\"\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"kibana\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"tooltip_mode\":\"show_all\",\"type\":\"timeseries\",\"use_kibana_indexes\":false},\"uiState\":{},\"data\":{\"aggs\":[],\"searchSource\":{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}}},\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"23d04651-266f-4f0a-8eef-6f190f0a84af\",\"triggers\":[\"FILTER_TRIGGER\"],\"action\":{\"name\":\"dashboard\",\"config\":{\"useCurrentFilters\":true,\"useCurrentDateRange\":true},\"factoryId\":\"DASHBOARD_TO_DASHBOARD_DRILLDOWN\"}}]}}}},{\"version\":\"7.13.5\",\"type\":\"map\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"ac0b1e52-de90-4c93-864b-32ce338fef51\"},\"panelIndex\":\"ac0b1e52-de90-4c93-864b-32ce338fef51\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"description\":\"\",\"layerListJSON\":\"[{\\\"sourceDescriptor\\\":{\\\"type\\\":\\\"EMS_TMS\\\",\\\"isAutoSelect\\\":true},\\\"id\\\":\\\"0965c5ce-6347-43f2-917e-b938ea3862f8\\\",\\\"label\\\":null,\\\"minZoom\\\":0,\\\"maxZoom\\\":24,\\\"alpha\\\":1,\\\"visible\\\":true,\\\"style\\\":{\\\"type\\\":\\\"TILE\\\"},\\\"type\\\":\\\"VECTOR_TILE\\\"},{\\\"sourceDescriptor\\\":{\\\"indexPatternId\\\":\\\"56b34100-619d-11eb-aebf-c306684b328d\\\",\\\"geoField\\\":\\\"geo.coordinates\\\",\\\"filterByMapBounds\\\":true,\\\"scalingType\\\":\\\"CLUSTERS\\\",\\\"id\\\":\\\"8fcc6cc4-919b-418c-9815-5a002cfca117\\\",\\\"type\\\":\\\"ES_SEARCH\\\",\\\"applyGlobalQuery\\\":true,\\\"applyGlobalTime\\\":true,\\\"tooltipProperties\\\":[],\\\"sortField\\\":\\\"\\\",\\\"sortOrder\\\":\\\"desc\\\",\\\"topHitsSplitField\\\":\\\"\\\",\\\"topHitsSize\\\":1},\\\"id\\\":\\\"0b28253c-ed9e-401f-9306-feb0e58d1c0e\\\",\\\"label\\\":\\\"Clustered logstash-*\\\",\\\"minZoom\\\":0,\\\"maxZoom\\\":24,\\\"alpha\\\":0.75,\\\"visible\\\":true,\\\"style\\\":{\\\"type\\\":\\\"VECTOR\\\",\\\"properties\\\":{\\\"icon\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"value\\\":\\\"marker\\\"}},\\\"fillColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#54B399\\\"}},\\\"lineColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#41937c\\\"}},\\\"lineWidth\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"size\\\":1}},\\\"iconSize\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"size\\\":6}},\\\"iconOrientation\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"orientation\\\":0}},\\\"labelText\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"value\\\":\\\"\\\"}},\\\"labelColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#000000\\\"}},\\\"labelSize\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"size\\\":14}},\\\"labelBorderColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#FFFFFF\\\"}},\\\"symbolizeAs\\\":{\\\"options\\\":{\\\"value\\\":\\\"circle\\\"}},\\\"labelBorderSize\\\":{\\\"options\\\":{\\\"size\\\":\\\"SMALL\\\"}}},\\\"isTimeAware\\\":true},\\\"type\\\":\\\"BLENDED_VECTOR\\\",\\\"joins\\\":[]}]\",\"mapStateJSON\":\"{\\\"zoom\\\":1.45,\\\"center\\\":{\\\"lon\\\":0,\\\"lat\\\":19.94277},\\\"timeFilters\\\":{\\\"from\\\":\\\"2015-09-20T01:56:56.132Z\\\",\\\"to\\\":\\\"2015-09-21T11:18:20.471Z\\\"},\\\"refreshConfig\\\":{\\\"isPaused\\\":true,\\\"interval\\\":0},\\\"query\\\":{\\\"query\\\":\\\"\\\",\\\"language\\\":\\\"kuery\\\"},\\\"filters\\\":[],\\\"settings\\\":{\\\"autoFitToDataBounds\\\":false,\\\"backgroundColor\\\":\\\"#ffffff\\\",\\\"disableInteractive\\\":false,\\\"disableTooltipControl\\\":false,\\\"hideToolbarOverlay\\\":false,\\\"hideLayerControl\\\":false,\\\"hideViewControl\\\":false,\\\"initialLocation\\\":\\\"LAST_SAVED_LOCATION\\\",\\\"fixedLocation\\\":{\\\"lat\\\":0,\\\"lon\\\":0,\\\"zoom\\\":2},\\\"browserLocation\\\":{\\\"zoom\\\":2},\\\"maxZoom\\\":24,\\\"minZoom\\\":0,\\\"showScaleControl\\\":false,\\\"showSpatialFilters\\\":true,\\\"spatialFiltersAlpa\\\":0.3,\\\"spatialFiltersFillColor\\\":\\\"#DA8B45\\\",\\\"spatialFiltersLineColor\\\":\\\"#DA8B45\\\"}}\",\"uiStateJSON\":\"{\\\"isLayerTOCOpen\\\":true,\\\"openTOCDetails\\\":[]}\"},\"mapCenter\":{\"lat\":-24.21784,\"lon\":4.3751,\"zoom\":1.45},\"mapBuffer\":{\"minLon\":-222.61538000000002,\"minLat\":-101.36965000000001,\"maxLon\":231.36558,\"maxLat\":65.74483000000001},\"isLayerTOCOpen\":true,\"openTOCDetails\":[],\"hiddenLayers\":[],\"enhancements\":{}}},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":30,\"w\":24,\"h\":15,\"i\":\"147049a3-5826-48e6-9514-17078f94b6bd\"},\"panelIndex\":\"147049a3-5826-48e6-9514-17078f94b6bd\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"\",\"description\":\"\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"linear\",\"orientation\":\"single\",\"minFontSize\":18,\"maxFontSize\":72,\"showLabel\":true},\"uiState\":{},\"data\":{\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"significant_terms\",\"params\":{\"field\":\"geo.srcdest\",\"size\":77},\"schema\":\"segment\"}],\"searchSource\":{\"index\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}}},\"enhancements\":{}}}]","timeRestore":false,"title":"logstash_by_value_dashboard","version":1},"coreMigrationVersion":"7.13.5","id":"08dec860-ca29-11eb-bf5e-3de94e83d4f0","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"095e13b2-d0ac-47db-a62b-0aca28931402:indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"095e13b2-d0ac-47db-a62b-0aca28931402:indexpattern-datasource-layer-f61694eb-94ed-495d-9ce8-63592f040b0b","type":"index-pattern"},{"id":"35ce3b30-ca29-11eb-bf5e-3de94e83d4f0","name":"095e13b2-d0ac-47db-a62b-0aca28931402:drilldown:DASHBOARD_TO_DASHBOARD_DRILLDOWN:da7ad2b6-1e4a-40b5-9123-d0ec2bde858d:dashboardId","type":"dashboard"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"a254a623-a9af-4372-851b-572fa95b0902:kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"35ce3b30-ca29-11eb-bf5e-3de94e83d4f0","name":"5de83d82-bbd1-4d30-be61-dd6724f32c07:drilldown:DASHBOARD_TO_DASHBOARD_DRILLDOWN:23d04651-266f-4f0a-8eef-6f190f0a84af:dashboardId","type":"dashboard"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"ac0b1e52-de90-4c93-864b-32ce338fef51:layer_1_source_index_pattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"147049a3-5826-48e6-9514-17078f94b6bd:kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"07f48f70-ca29-11eb-bf5e-3de94e83d4f0","name":"tag-07f48f70-ca29-11eb-bf5e-3de94e83d4f0","type":"tag"}],"sort":[1651613469669,1400],"type":"dashboard","updated_at":"2022-05-03T21:31:09.669Z","version":"Wzk2NiwxXQ=="} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_scriptedfieldviz","uiStateJSON":"{\"vis\":{\"defaultColors\":{\"0 - 100\":\"rgb(0,104,55)\"}}}","version":1,"visState":"{\"title\":\"logstash_scriptedfieldviz\",\"type\":\"goal\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"isDisplayWarning\":false,\"type\":\"gauge\",\"gauge\":{\"verticalSplit\":false,\"autoExtend\":false,\"percentageMode\":true,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"gaugeColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":false,\"labels\":false,\"color\":\"#333\",\"width\":2},\"type\":\"meter\",\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"range\",\"schema\":\"group\",\"params\":{\"field\":\"bytes_scripted\",\"ranges\":[{\"from\":0,\"to\":40000},{\"from\":40001,\"to\":20000000}]}}]}"},"coreMigrationVersion":"7.13.5","id":"0a274320-61cc-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,9],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzIzLDFd"} +{"attributes":{"columns":[],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[{\"meta\":{\"type\":\"phrases\",\"key\":\"geo.srcdest\",\"value\":\"IN:CN\",\"params\":[\"IN:CN\"],\"alias\":null,\"negate\":false,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"bool\":{\"should\":[{\"match_phrase\":{\"geo.srcdest\":\"IN:CN\"}}],\"minimum_should_match\":1}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["@timestamp","desc"]],"title":"search_saved","version":1},"coreMigrationVersion":"7.13.5","id":"0abce1c0-ca2a-11eb-bf5e-3de94e83d4f0","migrationVersion":{"search":"7.9.3"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"}],"sort":[1651612922671,997],"type":"search","updated_at":"2022-05-03T21:22:02.671Z","version":"WzU4MCwxXQ=="} +{"attributes":{"color":"#81a93f","description":"","name":"logstash_tag"},"coreMigrationVersion":"7.13.5","id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","references":[],"sort":[1651599812857,262],"type":"tag","updated_at":"2022-05-03T17:43:32.857Z","version":"WzI1LDFd"} +{"attributes":{"description":"","layerListJSON":"[{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"isAutoSelect\":true},\"id\":\"4c2394ca-a6a2-4f8d-9631-259eb3a9627f\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"style\":{\"type\":\"TILE\"},\"type\":\"VECTOR_TILE\"},{\"sourceDescriptor\":{\"geoField\":\"geo.coordinates\",\"filterByMapBounds\":true,\"scalingType\":\"CLUSTERS\",\"id\":\"7555324e-e793-4b7d-a9d2-cd63e6b7fe3d\",\"type\":\"ES_SEARCH\",\"applyGlobalQuery\":true,\"applyGlobalTime\":true,\"tooltipProperties\":[\"geo.srcdest\",\"machine.os\",\"type\"],\"sortField\":\"bytes_scripted\",\"sortOrder\":\"desc\",\"topHitsSplitField\":\"\",\"topHitsSize\":1,\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"id\":\"6a493d8b-a220-46bc-8906-a1a7569799e0\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}},\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"color\":\"Blues\",\"colorCategory\":\"palette_0\",\"field\":{\"name\":\"extension.raw\",\"origin\":\"source\"},\"fieldMetaOptions\":{\"isEnabled\":true,\"sigma\":3},\"type\":\"CATEGORICAL\"}},\"lineColor\":{\"type\":\"DYNAMIC\",\"options\":{\"color\":\"Blues\",\"colorCategory\":\"palette_0\",\"field\":{\"name\":\"machine.os.raw\",\"origin\":\"source\"},\"fieldMetaOptions\":{\"isEnabled\":true,\"sigma\":3},\"type\":\"CATEGORICAL\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":6}},\"iconOrientation\":{\"type\":\"STATIC\",\"options\":{\"orientation\":0}},\"labelText\":{\"type\":\"STATIC\",\"options\":{\"value\":\"\"}},\"labelColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#000000\"}},\"labelSize\":{\"type\":\"STATIC\",\"options\":{\"size\":14}},\"labelBorderColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"labelBorderSize\":{\"options\":{\"size\":\"SMALL\"}}},\"isTimeAware\":true},\"type\":\"BLENDED_VECTOR\",\"joins\":[]}]","mapStateJSON":"{\"zoom\":1.56,\"center\":{\"lon\":0,\"lat\":19.94277},\"timeFilters\":{\"from\":\"now-15y\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":0},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"settings\":{\"autoFitToDataBounds\":false,\"backgroundColor\":\"#ffffff\",\"disableInteractive\":false,\"disableTooltipControl\":false,\"hideToolbarOverlay\":false,\"hideLayerControl\":false,\"hideViewControl\":false,\"initialLocation\":\"LAST_SAVED_LOCATION\",\"fixedLocation\":{\"lat\":0,\"lon\":0,\"zoom\":2},\"browserLocation\":{\"zoom\":2},\"maxZoom\":24,\"minZoom\":0,\"showScaleControl\":false,\"showSpatialFilters\":true,\"spatialFiltersAlpa\":0.3,\"spatialFiltersFillColor\":\"#DA8B45\",\"spatialFiltersLineColor\":\"#DA8B45\"}}","title":"logstash_maps","uiStateJSON":"{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}"},"coreMigrationVersion":"7.13.5","id":"0c5974f0-be5c-11eb-9520-1b4c3ca6a781","migrationVersion":{"map":"7.12.0"},"references":[{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"layer_1_source_index_pattern","type":"index-pattern"}],"sort":[1651599812857,265],"type":"map","updated_at":"2022-05-03T17:43:32.857Z","version":"WzI2LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_datatable","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}","version":1,"visState":"{\"title\":\"logstash_datatable\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":true,\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"bucket\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"2015-07-24T08:58:14.175Z\",\"to\":\"2015-11-11T13:28:17.223Z\",\"mode\":\"absolute\"},\"useNormalizedEsInterval\":true,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"response.raw\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.5","id":"0d8a8860-623a-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,267],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzI3LDFd"} +{"attributes":{"description":null,"state":{"datasourceStates":{"indexpattern":{"layers":{"35fd070e-5bbc-4906-bf69-8548a213d7a0":{"columnOrder":["2bf7969f-0371-4df2-a398-0a191e428ce5","aab812d6-609b-444d-9990-1e67f85fd85d","e9829e8a-c484-4c9d-b489-f1eb3fb138d2","4fc9fb3b-29a5-4679-ab3c-90d5daaf0661"],"columns":{"2bf7969f-0371-4df2-a398-0a191e428ce5":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"},"4fc9fb3b-29a5-4679-ab3c-90d5daaf0661":{"dataType":"number","isBucketed":false,"label":"Moving average of Median of bytes","operationType":"moving_average","params":{"window":5},"references":["e9829e8a-c484-4c9d-b489-f1eb3fb138d2"],"scale":"ratio"},"aab812d6-609b-444d-9990-1e67f85fd85d":{"dataType":"number","isBucketed":false,"label":"Average of bytes","operationType":"average","scale":"ratio","sourceField":"bytes"},"e9829e8a-c484-4c9d-b489-f1eb3fb138d2":{"dataType":"number","isBucketed":false,"label":"Median of bytes","operationType":"median","scale":"ratio","sourceField":"bytes"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["aab812d6-609b-444d-9990-1e67f85fd85d","4fc9fb3b-29a5-4679-ab3c-90d5daaf0661"],"layerId":"35fd070e-5bbc-4906-bf69-8548a213d7a0","position":"top","seriesType":"bar_stacked","showGridlines":false,"xAccessor":"2bf7969f-0371-4df2-a398-0a191e428ce5"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_barvertical_stacked_average","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.5","id":"0dbbf8b0-be3c-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-35fd070e-5bbc-4906-bf69-8548a213d7a0","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,16],"type":"lens","updated_at":"2022-05-03T17:43:32.857Z","version":"WzI4LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_area_chart","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_area_chart\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"2010-01-28T19:25:55.242Z\",\"to\":\"2021-01-28T19:40:55.242Z\",\"mode\":\"absolute\"},\"useNormalizedEsInterval\":true,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"machine.os.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"machine OS\"}}]}"},"coreMigrationVersion":"7.13.5","id":"36b91810-6239-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,18],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzI5LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_horizontal","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_horizontal\",\"type\":\"horizontal_bar\",\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":200},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":75,\"filter\":true,\"truncate\":100},\"title\":{\"text\":\"no of documents\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"normal\",\"data\":{\"label\":\"no of documents\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":true,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{\"customLabel\":\"no of documents\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"2015-07-24T08:58:14.175Z\",\"to\":\"2015-11-11T13:28:17.223Z\",\"mode\":\"absolute\"},\"useNormalizedEsInterval\":true,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"agent.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"extension.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.5","id":"e4aef350-623d-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,20],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzMwLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_linechart","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_linechart\",\"type\":\"line\",\"params\":{\"type\":\"line\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"line\",\"mode\":\"normal\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"radiusRatio\":51,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"2015-09-18T06:38:43.311Z\",\"to\":\"2015-09-26T04:02:51.104Z\",\"mode\":\"absolute\"},\"useNormalizedEsInterval\":true,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"sum\",\"schema\":\"radius\",\"params\":{\"field\":\"bytes\",\"customLabel\":\"bubbles\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"machine.os.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.5","id":"f92e5630-623e-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,22],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzMxLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_heatmap","uiStateJSON":"{\"vis\":{\"defaultColors\":{\"0% - 25%\":\"rgb(255,255,204)\",\"25% - 50%\":\"rgb(254,217,118)\",\"50% - 75%\":\"rgb(253,141,60)\",\"75% - 100%\":\"rgb(227,27,28)\"}}}","version":1,"visState":"{\"title\":\"logstash_heatmap\",\"type\":\"heatmap\",\"params\":{\"type\":\"heatmap\",\"addTooltip\":true,\"addLegend\":true,\"enableHover\":false,\"legendPosition\":\"right\",\"times\":[],\"colorsNumber\":4,\"colorSchema\":\"Yellow to Red\",\"setColorRange\":false,\"colorsRange\":[],\"invertColors\":false,\"percentageMode\":true,\"valueAxes\":[{\"show\":false,\"id\":\"ValueAxis-1\",\"type\":\"value\",\"scale\":{\"type\":\"linear\",\"defaultYExtents\":false},\"labels\":{\"show\":false,\"rotate\":0,\"overwriteColor\":false,\"color\":\"#555\"}}],\"row\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"2015-07-24T08:58:14.175Z\",\"to\":\"2015-11-11T13:28:17.223Z\",\"mode\":\"absolute\"},\"useNormalizedEsInterval\":true,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"machine.os.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"response.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.5","id":"9853d4d0-623d-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,24],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzMyLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_goalchart","uiStateJSON":"{\"vis\":{\"defaultColors\":{\"0 - 33\":\"rgb(0,104,55)\",\"33 - 67\":\"rgb(255,255,190)\",\"67 - 100\":\"rgb(165,0,38)\"}}}","version":1,"visState":"{\"title\":\"logstash_goalchart\",\"type\":\"goal\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"isDisplayWarning\":false,\"type\":\"gauge\",\"gauge\":{\"verticalSplit\":false,\"autoExtend\":false,\"percentageMode\":true,\"gaugeType\":\"Circle\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"gaugeColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000},{\"from\":10001,\"to\":20000},{\"from\":20001,\"to\":30000}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":false,\"labels\":false,\"color\":\"#333\",\"width\":2},\"type\":\"meter\",\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60},\"minAngle\":0,\"maxAngle\":6.283185307179586}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"group\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"2015-07-24T08:58:14.175Z\",\"to\":\"2015-11-11T13:28:17.223Z\",\"mode\":\"absolute\"},\"useNormalizedEsInterval\":true,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}}]}"},"coreMigrationVersion":"7.13.5","id":"6ecb33b0-623d-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,26],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzMzLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_gauge","uiStateJSON":"{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(0,104,55)\",\"50 - 75\":\"rgb(255,255,190)\",\"75 - 100\":\"rgb(165,0,38)\"}}}","version":1,"visState":"{\"title\":\"logstash_gauge\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"extendRange\":true,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Green to Red\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":50},{\"from\":50,\"to\":75},{\"from\":75,\"to\":100}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"\",\"fontSize\":60,\"labelColor\":true},\"alignment\":\"horizontal\"}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"range\",\"schema\":\"group\",\"params\":{\"field\":\"bytes\",\"ranges\":[{\"from\":0,\"to\":10001},{\"from\":10002,\"to\":1000000}],\"json\":\"\"}}]}"},"coreMigrationVersion":"7.13.5","id":"b8e35c80-623c-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,28],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzM0LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_coordinatemaps","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_coordinatemaps\",\"type\":\"tile_map\",\"params\":{\"colorSchema\":\"Yellow to Red\",\"mapType\":\"Scaled Circle Markers\",\"isDesaturated\":false,\"addTooltip\":true,\"heatClusterSize\":1.5,\"legendPosition\":\"bottomright\",\"mapZoom\":2,\"mapCenter\":[0,0],\"wms\":{\"enabled\":false,\"options\":{\"format\":\"image/png\",\"transparent\":true},\"selectedTmsLayer\":{\"origin\":\"elastic_maps_service\",\"id\":\"road_map\",\"minZoom\":0,\"maxZoom\":18,\"attribution\":\"

© OpenStreetMap contributors|OpenMapTiles|Elastic Maps Service

\"}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"geohash_grid\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.coordinates\",\"autoPrecision\":true,\"isFilteredByCollar\":true,\"useGeocentroid\":true,\"mapZoom\":2,\"mapCenter\":[0,0],\"precision\":2,\"customLabel\":\"logstash src/dest\"}}]}"},"coreMigrationVersion":"7.13.5","id":"f1bc75d0-6239-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,30],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzM1LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"title":"logstash_inputcontrols","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_inputcontrols\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1611928563867\",\"fieldName\":\"machine.ram\",\"parent\":\"\",\"label\":\"Logstash RAM\",\"type\":\"range\",\"options\":{\"decimalPlaces\":0,\"step\":1024},\"indexPatternRefName\":\"control_0_index_pattern\"},{\"id\":\"1611928586274\",\"fieldName\":\"machine.os.raw\",\"parent\":\"\",\"label\":\"Logstash OS\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"dynamicOptions\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_1_index_pattern\"}],\"updateFiltersOnChange\":false,\"useTimeFilter\":false,\"pinFilters\":false},\"aggs\":[]}"},"coreMigrationVersion":"7.13.5","id":"d79fe3d0-6239-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"control_0_index_pattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"control_1_index_pattern","type":"index-pattern"}],"sort":[1651599812857,33],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzM2LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"title":"logstash_markdown","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_markdown\",\"type\":\"markdown\",\"params\":{\"fontSize\":12,\"openLinksInNewTab\":true,\"markdown\":\"Kibana is built with JS https://www.javascript.com/\"},\"aggs\":[]}"},"coreMigrationVersion":"7.13.5","id":"318375a0-6240-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[],"sort":[1651599812857,34],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzM3LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"title":"logstash_vegaviz","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_vegaviz\",\"type\":\"vega\",\"params\":{\"spec\":\"{\\n/*\\n\\nWelcome to Vega visualizations. Here you can design your own dataviz from scratch using a declarative language called Vega, or its simpler form Vega-Lite. In Vega, you have the full control of what data is loaded, even from multiple sources, how that data is transformed, and what visual elements are used to show it. Use help icon to view Vega examples, tutorials, and other docs. Use the wrench icon to reformat this text, or to remove comments.\\n\\nThis example graph shows the document count in all indexes in the current time range. You might need to adjust the time filter in the upper right corner.\\n*/\\n\\n $schema: https://vega.github.io/schema/vega-lite/v2.json\\n title: Event counts from all indexes\\n\\n // Define the data source\\n data: {\\n url: {\\n/*\\nAn object instead of a string for the \\\"url\\\" param is treated as an Elasticsearch query. Anything inside this object is not part of the Vega language, but only understood by Kibana and Elasticsearch server. This query counts the number of documents per time interval, assuming you have a @timestamp field in your data.\\n\\nKibana has a special handling for the fields surrounded by \\\"%\\\". They are processed before the the query is sent to Elasticsearch. This way the query becomes context aware, and can use the time range and the dashboard filters.\\n*/\\n\\n // Apply dashboard context filters when set\\n %context%: true\\n // Filter the time picker (upper right corner) with this field\\n %timefield%: @timestamp\\n\\n/*\\nSee .search() documentation for : https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/api-reference.html#api-search\\n*/\\n\\n // Which index to search\\n index: logstash-*\\n // Aggregate data by the time field into time buckets, counting the number of documents in each bucket.\\n body: {\\n aggs: {\\n time_buckets: {\\n date_histogram: {\\n // Use date histogram aggregation on @timestamp field\\n field: @timestamp\\n // The interval value will depend on the daterange picker (true), or use an integer to set an approximate bucket count\\n interval: {%autointerval%: true}\\n // Make sure we get an entire range, even if it has no data\\n extended_bounds: {\\n // Use the current time range's start and end\\n min: {%timefilter%: \\\"min\\\"}\\n max: {%timefilter%: \\\"max\\\"}\\n }\\n // Use this for linear (e.g. line, area) graphs. Without it, empty buckets will not show up\\n min_doc_count: 13\\n }\\n }\\n }\\n // Speed up the response by only including aggregation results\\n size: 0\\n }\\n }\\n/*\\nElasticsearch will return results in this format:\\n\\naggregations: {\\n time_buckets: {\\n buckets: [\\n {\\n key_as_string: 2015-11-30T22:00:00.000Z\\n key: 1448920800000\\n doc_count: 0\\n },\\n {\\n key_as_string: 2015-11-30T23:00:00.000Z\\n key: 1448924400000\\n doc_count: 0\\n }\\n ...\\n ]\\n }\\n}\\n\\nFor our graph, we only need the list of bucket values. Use the format.property to discard everything else.\\n*/\\n format: {property: \\\"aggregations.time_buckets.buckets\\\"}\\n }\\n\\n // \\\"mark\\\" is the graphics element used to show our data. Other mark values are: area, bar, circle, line, point, rect, rule, square, text, and tick. See https://vega.github.io/vega-lite/docs/mark.html\\n mark: line\\n\\n // \\\"encoding\\\" tells the \\\"mark\\\" what data to use and in what way. See https://vega.github.io/vega-lite/docs/encoding.html\\n encoding: {\\n x: {\\n // The \\\"key\\\" value is the timestamp in milliseconds. Use it for X axis.\\n field: key\\n type: temporal\\n axis: {title: false} // Customize X axis format\\n }\\n y: {\\n // The \\\"doc_count\\\" is the count per bucket. Use it for Y axis.\\n field: doc_count\\n type: quantitative\\n axis: {title: \\\"Document count\\\"}\\n }\\n }\\n}\\n\"},\"aggs\":[]}"},"coreMigrationVersion":"7.13.5","id":"e461eb20-6245-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[],"sort":[1651599812857,35],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzM4LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_regionmap","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_regionmap\",\"type\":\"region_map\",\"params\":{\"addTooltip\":true,\"colorSchema\":\"Yellow to Red\",\"emsHotLink\":\"https://maps.elastic.co/v6.7?locale=en#file/world_countries\",\"isDisplayWarning\":true,\"legendPosition\":\"bottomright\",\"mapCenter\":[0,0],\"mapZoom\":2,\"outlineWeight\":1,\"selectedJoinField\":{\"type\":\"id\",\"name\":\"iso2\",\"description\":\"ISO 3166-1 alpha-2 code\"},\"showAllShapes\":true,\"wms\":{\"enabled\":false,\"options\":{\"format\":\"image/png\",\"transparent\":true},\"selectedTmsLayer\":{\"origin\":\"elastic_maps_service\",\"id\":\"road_map\",\"minZoom\":0,\"maxZoom\":18,\"attribution\":\"

© OpenStreetMap contributors|OpenMapTiles|Elastic Maps Service

\"}},\"selectedLayer\":{\"name\":\"World Countries\",\"origin\":\"elastic_maps_service\",\"id\":\"world_countries\",\"created_at\":\"2017-04-26T17:12:15.978370\",\"attribution\":\"Made with NaturalEarth | Elastic Maps Service\",\"fields\":[{\"type\":\"id\",\"name\":\"iso2\",\"description\":\"ISO 3166-1 alpha-2 code\"},{\"type\":\"id\",\"name\":\"iso3\",\"description\":\"ISO 3166-1 alpha-3 code\"},{\"type\":\"property\",\"name\":\"name\",\"description\":\"name\"}],\"format\":{\"type\":\"geojson\"},\"layerId\":\"elastic_maps_service.World Countries\",\"isEMS\":true}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.dest\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.5","id":"25bdc750-6242-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,37],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzM5LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_verticalbarchart","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_verticalbarchart\",\"type\":\"histogram\",\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\",\"defaultYExtents\":true},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":true,\"row\":true,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"2015-09-18T06:38:43.311Z\",\"to\":\"2015-09-26T04:02:51.104Z\",\"mode\":\"absolute\"},\"useNormalizedEsInterval\":true,\"interval\":\"h\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{},\"scaleMetricValues\":true}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"response.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Response code\"}}]}"},"coreMigrationVersion":"7.13.5","id":"71dd7bc0-6248-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,39],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzQwLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_metricviz","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_metricviz\",\"type\":\"metric\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"type\":\"metric\",\"metric\":{\"percentageMode\":false,\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"metricColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"labels\":{\"show\":true},\"invertColors\":false,\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"range\",\"schema\":\"group\",\"params\":{\"field\":\"bytes_scripted\",\"ranges\":[{\"from\":0,\"to\":10000},{\"from\":10001,\"to\":300000}]}}]}"},"coreMigrationVersion":"7.13.5","id":"6aea48a0-6240-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,41],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzQxLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_piechart","uiStateJSON":"{}","version":1,"visState":"{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"machine.os.raw\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"segment\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTooltip\":true,\"isDonut\":true,\"labels\":{\"last_level\":true,\"show\":false,\"truncate\":100,\"values\":true},\"legendPosition\":\"right\",\"type\":\"pie\"},\"title\":\"logstash_piechart\",\"type\":\"pie\"}"},"coreMigrationVersion":"7.13.5","id":"32b681f0-6241-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,43],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzQyLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"logstash_tagcloud","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_tagcloud\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"log\",\"orientation\":\"single\",\"minFontSize\":18,\"maxFontSize\":72,\"showLabel\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.srcdest\",\"size\":23,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.5","id":"ccca99e0-6244-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,45],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzQzLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"title":"logstash_timelion","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_timelion\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(q='machine.os.raw:win xp' , index=logstash-*)\",\"interval\":\"auto\"},\"aggs\":[]}"},"coreMigrationVersion":"7.13.5","id":"a4d7be80-6245-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[],"sort":[1651599812857,46],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzQ0LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{}"},"title":"logstash_tsvb","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_tsvb\",\"type\":\"metrics\",\"params\":{\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"timeseries\",\"series\":[{\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"show_grid\":1,\"annotations\":[{\"fields\":\"machine.os.raw\",\"template\":\"{{machine.os.raw}}\",\"index_pattern\":\"logstash-*\",\"query_string\":{\"query\":\"machine.os.raw :\\\"win xp\\\" \",\"language\":\"lucene\"},\"id\":\"aa43ceb0-6248-11eb-9a82-ef1c6e6c0265\",\"color\":\"#F00\",\"time_field\":\"@timestamp\",\"icon\":\"fa-tag\",\"ignore_global_filters\":1,\"ignore_panel_filters\":1}],\"use_kibana_indexes\":false},\"aggs\":[]}"},"coreMigrationVersion":"7.13.5","id":"c94d8440-6248-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[],"sort":[1651599812857,47],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzQ1LDFd"} +{"attributes":{"columns":["bytes_scripted"],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"highlightAll\":true,\"version\":true,\"query\":{\"query\":\"machine.os.raw :\\\"win xp\\\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["@timestamp","desc"]],"title":"logstash_scripted_saved_search","version":1},"coreMigrationVersion":"7.13.5","id":"db6226f0-61c0-11eb-aebf-c306684b328d","migrationVersion":{"search":"7.9.3"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,49],"type":"search","updated_at":"2022-05-03T17:43:32.857Z","version":"WzQ2LDFd"} +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"1\",\"w\":24,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"2\",\"w\":24,\"x\":24,\"y\":0},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{},\"vis\":null},\"panelRefName\":\"panel_2\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"3\",\"w\":24,\"x\":0,\"y\":15},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"4\",\"w\":24,\"x\":24,\"y\":15},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"5\",\"w\":24,\"x\":0,\"y\":30},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{},\"vis\":null},\"panelRefName\":\"panel_5\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"6\",\"w\":24,\"x\":24,\"y\":30},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{},\"vis\":null},\"panelRefName\":\"panel_6\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"7\",\"w\":24,\"x\":0,\"y\":45},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{},\"vis\":null},\"panelRefName\":\"panel_7\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"8\",\"w\":24,\"x\":24,\"y\":45},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_8\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"9\",\"w\":24,\"x\":0,\"y\":60},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"10\",\"w\":24,\"x\":24,\"y\":60},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"11\",\"w\":24,\"x\":0,\"y\":75},\"panelIndex\":\"11\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_11\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"12\",\"w\":24,\"x\":24,\"y\":75},\"panelIndex\":\"12\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_12\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"13\",\"w\":24,\"x\":0,\"y\":90},\"panelIndex\":\"13\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"14\",\"w\":24,\"x\":24,\"y\":90},\"panelIndex\":\"14\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_14\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"15\",\"w\":24,\"x\":0,\"y\":105},\"panelIndex\":\"15\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_15\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"16\",\"w\":24,\"x\":24,\"y\":105},\"panelIndex\":\"16\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_16\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"17\",\"w\":24,\"x\":0,\"y\":120},\"panelIndex\":\"17\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_17\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"18\",\"w\":24,\"x\":24,\"y\":120},\"panelIndex\":\"18\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_18\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"19\",\"w\":24,\"x\":0,\"y\":135},\"panelIndex\":\"19\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_19\"},{\"version\":\"7.13.5\",\"type\":\"search\",\"gridData\":{\"h\":15,\"i\":\"20\",\"w\":24,\"x\":24,\"y\":135},\"panelIndex\":\"20\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_20\"}]","refreshInterval":{"pause":true,"value":0},"timeFrom":"2015-09-20T01:56:56.132Z","timeRestore":true,"timeTo":"2015-09-21T11:18:20.471Z","title":"logstash_dashboardwithtime","version":1},"coreMigrationVersion":"7.13.5","id":"154944b0-6249-11eb-aebf-c306684b328d","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"36b91810-6239-11eb-aebf-c306684b328d","name":"1:panel_1","type":"visualization"},{"id":"0a274320-61cc-11eb-aebf-c306684b328d","name":"2:panel_2","type":"visualization"},{"id":"e4aef350-623d-11eb-aebf-c306684b328d","name":"3:panel_3","type":"visualization"},{"id":"f92e5630-623e-11eb-aebf-c306684b328d","name":"4:panel_4","type":"visualization"},{"id":"9853d4d0-623d-11eb-aebf-c306684b328d","name":"5:panel_5","type":"visualization"},{"id":"6ecb33b0-623d-11eb-aebf-c306684b328d","name":"6:panel_6","type":"visualization"},{"id":"b8e35c80-623c-11eb-aebf-c306684b328d","name":"7:panel_7","type":"visualization"},{"id":"f1bc75d0-6239-11eb-aebf-c306684b328d","name":"8:panel_8","type":"visualization"},{"id":"0d8a8860-623a-11eb-aebf-c306684b328d","name":"9:panel_9","type":"visualization"},{"id":"d79fe3d0-6239-11eb-aebf-c306684b328d","name":"10:panel_10","type":"visualization"},{"id":"318375a0-6240-11eb-aebf-c306684b328d","name":"11:panel_11","type":"visualization"},{"id":"e461eb20-6245-11eb-aebf-c306684b328d","name":"12:panel_12","type":"visualization"},{"id":"25bdc750-6242-11eb-aebf-c306684b328d","name":"13:panel_13","type":"visualization"},{"id":"71dd7bc0-6248-11eb-aebf-c306684b328d","name":"14:panel_14","type":"visualization"},{"id":"6aea48a0-6240-11eb-aebf-c306684b328d","name":"15:panel_15","type":"visualization"},{"id":"32b681f0-6241-11eb-aebf-c306684b328d","name":"16:panel_16","type":"visualization"},{"id":"ccca99e0-6244-11eb-aebf-c306684b328d","name":"17:panel_17","type":"visualization"},{"id":"a4d7be80-6245-11eb-aebf-c306684b328d","name":"18:panel_18","type":"visualization"},{"id":"c94d8440-6248-11eb-aebf-c306684b328d","name":"19:panel_19","type":"visualization"},{"id":"db6226f0-61c0-11eb-aebf-c306684b328d","name":"20:panel_20","type":"search"}],"sort":[1651613667249,1638],"type":"dashboard","updated_at":"2022-05-03T21:34:27.249Z","version":"WzExNDMsMV0="} +{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"26e2cf99-d931-4320-9e15-9dbc148f3534":{"columnOrder":["6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e","beb72af1-239c-46d8-823b-b00d1e2ace43"],"columns":{"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e":{"dataType":"string","isBucketed":true,"label":"Top values of url.raw","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"beb72af1-239c-46d8-823b-b00d1e2ace43","type":"column"},"orderDirection":"desc","otherBucket":true,"size":20},"scale":"ordinal","sourceField":"url.raw"},"beb72af1-239c-46d8-823b-b00d1e2ace43":{"dataType":"number","isBucketed":false,"label":"Unique count of geo.srcdest","operationType":"unique_count","scale":"ratio","sourceField":"geo.srcdest"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"layers":[{"categoryDisplay":"default","groups":["6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e","6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e","6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e"],"layerId":"26e2cf99-d931-4320-9e15-9dbc148f3534","legendDisplay":"default","metric":"beb72af1-239c-46d8-823b-b00d1e2ace43","nestedLegend":false,"numberDisplay":"percent"}],"shape":"donut"}},"title":"lens_pie_chart","visualizationType":"lnsPie"},"coreMigrationVersion":"7.13.5","id":"21905950-bd9f-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-26e2cf99-d931-4320-9e15-9dbc148f3534","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,53],"type":"lens","updated_at":"2022-05-03T17:43:32.857Z","version":"WzQ4LDFd"} +{"attributes":{"description":null,"state":{"datasourceStates":{"indexpattern":{"layers":{"a3ac0e3d-63ec-49b2-882a-b34680a967ba":{"columnOrder":["352a2c02-aa6f-4a35-b776-45c3715a6c5e","8ef68cbb-e039-49d6-b15e-be81559f4b55","14fad6b1-6a7c-4ae8-ae4b-d9569e31e04a"],"columns":{"14fad6b1-6a7c-4ae8-ae4b-d9569e31e04a":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"},"352a2c02-aa6f-4a35-b776-45c3715a6c5e":{"dataType":"string","isBucketed":true,"label":"Top values of geo.srcdest","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"14fad6b1-6a7c-4ae8-ae4b-d9569e31e04a","type":"column"},"orderDirection":"desc","otherBucket":true,"size":67},"scale":"ordinal","sourceField":"geo.srcdest"},"8ef68cbb-e039-49d6-b15e-be81559f4b55":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["14fad6b1-6a7c-4ae8-ae4b-d9569e31e04a"],"layerId":"a3ac0e3d-63ec-49b2-882a-b34680a967ba","position":"top","seriesType":"bar_percentage_stacked","showGridlines":false,"splitAccessor":"352a2c02-aa6f-4a35-b776-45c3715a6c5e","xAccessor":"8ef68cbb-e039-49d6-b15e-be81559f4b55"}],"legend":{"isVisible":true,"position":"top","showSingleSeries":true},"preferredSeriesType":"bar_percentage_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_bar_verticalpercentage","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.5","id":"aa4b8da0-bd9f-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-a3ac0e3d-63ec-49b2-882a-b34680a967ba","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,57],"type":"lens","updated_at":"2022-05-03T17:43:32.857Z","version":"WzQ5LDFd"} +{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"037b7937-790b-4d2d-94a5-7f5837a6ef05":{"columnOrder":["b3d46616-75e0-419e-97ea-91148961ef94","025a0fb3-dc44-4f5c-b517-2d71d3f26f14","c476db14-0cc1-40ec-863e-d2779256a407"],"columns":{"025a0fb3-dc44-4f5c-b517-2d71d3f26f14":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"},"b3d46616-75e0-419e-97ea-91148961ef94":{"dataType":"string","isBucketed":true,"label":"Top values of geo.srcdest","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"c476db14-0cc1-40ec-863e-d2779256a407","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"geo.srcdest"},"c476db14-0cc1-40ec-863e-d2779256a407":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"lucene","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["c476db14-0cc1-40ec-863e-d2779256a407"],"layerId":"037b7937-790b-4d2d-94a5-7f5837a6ef05","position":"top","seriesType":"bar_stacked","showGridlines":false,"splitAccessor":"b3d46616-75e0-419e-97ea-91148961ef94","xAccessor":"025a0fb3-dc44-4f5c-b517-2d71d3f26f14"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_barchart_vertical","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.5","id":"2d3f1250-bd9f-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-037b7937-790b-4d2d-94a5-7f5837a6ef05","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,61],"type":"lens","updated_at":"2022-05-03T17:43:32.857Z","version":"WzUwLDFd"} +{"attributes":{"description":null,"state":{"datasourceStates":{"indexpattern":{"layers":{"212688dc-e7d7-4875-a221-09e6191bdcf7":{"columnOrder":["05410186-83c4-460a-82bf-dd7e9d998c9f","e8659feb-1db4-4706-9147-ac1fd513a1ba","c9a32fd0-a465-44fb-8adc-b957fb72cad5"],"columns":{"05410186-83c4-460a-82bf-dd7e9d998c9f":{"dataType":"string","isBucketed":true,"label":"Top values of extension.raw","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"c9a32fd0-a465-44fb-8adc-b957fb72cad5","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"extension.raw"},"c9a32fd0-a465-44fb-8adc-b957fb72cad5":{"dataType":"number","isBucketed":false,"label":"Average of bytes","operationType":"average","scale":"ratio","sourceField":"bytes"},"e8659feb-1db4-4706-9147-ac1fd513a1ba":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["c9a32fd0-a465-44fb-8adc-b957fb72cad5"],"layerId":"212688dc-e7d7-4875-a221-09e6191bdcf7","position":"top","seriesType":"bar_horizontal_stacked","showGridlines":false,"splitAccessor":"05410186-83c4-460a-82bf-dd7e9d998c9f","xAccessor":"e8659feb-1db4-4706-9147-ac1fd513a1ba"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_horizontal_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_barhorizontal_stacked","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.5","id":"edd5a560-bda4-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-212688dc-e7d7-4875-a221-09e6191bdcf7","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,65],"type":"lens","updated_at":"2022-05-03T17:43:32.857Z","version":"WzUxLDFd"} +{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"7ab04fd4-04da-4023-8899-d94620193607":{"columnOrder":["0ab2d5f8-11f0-4b25-b8bb-3127a3b8d4c7","9eb851dd-31f6-481a-84d1-9ecce53a6ad2","f6b271a7-509b-4c37-b7b6-ac5be4bcb49a"],"columns":{"0ab2d5f8-11f0-4b25-b8bb-3127a3b8d4c7":{"dataType":"string","isBucketed":true,"label":"Top values of request.raw","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"f6b271a7-509b-4c37-b7b6-ac5be4bcb49a","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"request.raw"},"9eb851dd-31f6-481a-84d1-9ecce53a6ad2":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"},"f6b271a7-509b-4c37-b7b6-ac5be4bcb49a":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["f6b271a7-509b-4c37-b7b6-ac5be4bcb49a"],"layerId":"7ab04fd4-04da-4023-8899-d94620193607","position":"top","seriesType":"bar_horizontal_percentage_stacked","showGridlines":false,"splitAccessor":"0ab2d5f8-11f0-4b25-b8bb-3127a3b8d4c7","xAccessor":"9eb851dd-31f6-481a-84d1-9ecce53a6ad2"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_horizontal_percentage_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_barhorizontalpercentage","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.5","id":"2c25a450-bda5-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-7ab04fd4-04da-4023-8899-d94620193607","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,69],"type":"lens","updated_at":"2022-05-03T17:43:32.857Z","version":"WzUyLDFd"} +{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"037b7937-790b-4d2d-94a5-7f5837a6ef05":{"columnOrder":["b3d46616-75e0-419e-97ea-91148961ef94","025a0fb3-dc44-4f5c-b517-2d71d3f26f14","c476db14-0cc1-40ec-863e-d2779256a407"],"columns":{"025a0fb3-dc44-4f5c-b517-2d71d3f26f14":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"},"b3d46616-75e0-419e-97ea-91148961ef94":{"dataType":"string","isBucketed":true,"label":"Top values of geo.srcdest","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"c476db14-0cc1-40ec-863e-d2779256a407","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"geo.srcdest"},"c476db14-0cc1-40ec-863e-d2779256a407":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"lucene","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["c476db14-0cc1-40ec-863e-d2779256a407"],"layerId":"037b7937-790b-4d2d-94a5-7f5837a6ef05","position":"top","seriesType":"bar_stacked","showGridlines":false,"splitAccessor":"b3d46616-75e0-419e-97ea-91148961ef94","xAccessor":"025a0fb3-dc44-4f5c-b517-2d71d3f26f14"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_visualization","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.5","id":"e79116e0-bd9e-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-037b7937-790b-4d2d-94a5-7f5837a6ef05","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651612765795,977],"type":"lens","updated_at":"2022-05-03T21:19:25.795Z","version":"WzU1NSwxXQ=="} +{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"72783e5f-aa7b-4b8a-b26c-a3e4d051340e":{"columnOrder":["0f619652-9ff1-453b-ae1f-7371baa82f55"],"columns":{"0f619652-9ff1-453b-ae1f-7371baa82f55":{"dataType":"number","isBucketed":false,"label":"Average of phpmemory","operationType":"average","params":{"format":{"id":"percent","params":{"decimals":10}}},"scale":"ratio","sourceField":"phpmemory"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"accessor":"0f619652-9ff1-453b-ae1f-7371baa82f55","layerId":"72783e5f-aa7b-4b8a-b26c-a3e4d051340e"}},"title":"lens_metric","visualizationType":"lnsMetric"},"coreMigrationVersion":"7.13.5","id":"974fb950-bda5-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-72783e5f-aa7b-4b8a-b26c-a3e4d051340e","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,73],"type":"lens","updated_at":"2022-05-03T17:43:32.857Z","version":"WzU0LDFd"} +{"attributes":{"description":null,"state":{"datasourceStates":{"indexpattern":{"layers":{"bb478774-f9e8-4380-bf3a-f4a89a4d79b5":{"columnOrder":["4573ae8f-8f9d-4918-b496-c08f7102c6e1","cebdc6c5-3587-4f57-879c-dd63ea99cf03"],"columns":{"4573ae8f-8f9d-4918-b496-c08f7102c6e1":{"dataType":"string","isBucketed":true,"label":"Top values of machine.os.raw","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"cebdc6c5-3587-4f57-879c-dd63ea99cf03","type":"column"},"orderDirection":"desc","otherBucket":true,"size":5},"scale":"ordinal","sourceField":"machine.os.raw"},"cebdc6c5-3587-4f57-879c-dd63ea99cf03":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"layers":[{"categoryDisplay":"default","groups":["4573ae8f-8f9d-4918-b496-c08f7102c6e1"],"layerId":"bb478774-f9e8-4380-bf3a-f4a89a4d79b5","legendDisplay":"default","metric":"cebdc6c5-3587-4f57-879c-dd63ea99cf03","nestedLegend":false,"numberDisplay":"percent"}],"shape":"pie"}},"title":"lens_piechart","visualizationType":"lnsPie"},"coreMigrationVersion":"7.13.5","id":"51b63040-bda5-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-bb478774-f9e8-4380-bf3a-f4a89a4d79b5","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,77],"type":"lens","updated_at":"2022-05-03T17:43:32.857Z","version":"WzU1LDFd"} +{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"a1b85651-db29-441f-8f08-cf1b9b6f7bf1":{"columnOrder":["2b3bdc32-0be0-49dc-993d-4630b0bd1185","b85cc0a7-0b18-4b08-b7f0-c617f80cf903","03203126-8286-444d-b5b3-4f399eaf2c26","44305317-61e8-4600-9f3c-ac4070e0c529"],"columns":{"03203126-8286-444d-b5b3-4f399eaf2c26":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"},"2b3bdc32-0be0-49dc-993d-4630b0bd1185":{"dataType":"string","isBucketed":true,"label":"Top values of extension.raw","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"44305317-61e8-4600-9f3c-ac4070e0c529","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"extension.raw"},"44305317-61e8-4600-9f3c-ac4070e0c529":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"},"b85cc0a7-0b18-4b08-b7f0-c617f80cf903":{"dataType":"string","isBucketed":true,"label":"Top values of machine.os.raw","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"44305317-61e8-4600-9f3c-ac4070e0c529","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"machine.os.raw"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"columns":[{"columnId":"2b3bdc32-0be0-49dc-993d-4630b0bd1185","isTransposed":false},{"columnId":"b85cc0a7-0b18-4b08-b7f0-c617f80cf903","isTransposed":false},{"columnId":"03203126-8286-444d-b5b3-4f399eaf2c26","isTransposed":false},{"columnId":"44305317-61e8-4600-9f3c-ac4070e0c529","isTransposed":false}],"layerId":"a1b85651-db29-441f-8f08-cf1b9b6f7bf1"}},"title":"lens_table","visualizationType":"lnsDatatable"},"coreMigrationVersion":"7.13.5","id":"b00679c0-bda5-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-a1b85651-db29-441f-8f08-cf1b9b6f7bf1","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,81],"type":"lens","updated_at":"2022-05-03T17:43:32.857Z","version":"WzU2LDFd"} +{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"4fbb51e9-1f99-4b5e-b59d-60fcb547b1d9":{"columnOrder":["08a1af05-743d-480e-9056-3405b1bdda7d","bae35990-75c2-487f-94eb-d8e03d2eda33"],"columns":{"08a1af05-743d-480e-9056-3405b1bdda7d":{"dataType":"string","isBucketed":true,"label":"Top values of geo.srcdest","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"bae35990-75c2-487f-94eb-d8e03d2eda33","type":"column"},"orderDirection":"desc","otherBucket":true,"size":25},"scale":"ordinal","sourceField":"geo.srcdest"},"bae35990-75c2-487f-94eb-d8e03d2eda33":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"layers":[{"categoryDisplay":"default","groups":["08a1af05-743d-480e-9056-3405b1bdda7d","08a1af05-743d-480e-9056-3405b1bdda7d","08a1af05-743d-480e-9056-3405b1bdda7d"],"layerId":"4fbb51e9-1f99-4b5e-b59d-60fcb547b1d9","legendDisplay":"default","metric":"bae35990-75c2-487f-94eb-d8e03d2eda33","nestedLegend":false,"numberDisplay":"percent"}],"shape":"treemap"}},"title":"lens_treemap","visualizationType":"lnsPie"},"coreMigrationVersion":"7.13.5","id":"652ade10-bd9f-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-4fbb51e9-1f99-4b5e-b59d-60fcb547b1d9","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,85],"type":"lens","updated_at":"2022-05-03T17:43:32.857Z","version":"WzU3LDFd"} +{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"e84503c1-4dbd-4ac6-9ac9-ad938654680f":{"columnOrder":["38c73fd4-6330-4162-8a7b-1a059f005da8","e8d4dad2-ac30-4741-aca0-904eb1fc8455","70433aa7-3c2c-4e6c-b8cf-4218c995cff5"],"columns":{"38c73fd4-6330-4162-8a7b-1a059f005da8":{"dataType":"string","isBucketed":true,"label":"Top values of url.raw","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"70433aa7-3c2c-4e6c-b8cf-4218c995cff5","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"url.raw"},"70433aa7-3c2c-4e6c-b8cf-4218c995cff5":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"},"e8d4dad2-ac30-4741-aca0-904eb1fc8455":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["70433aa7-3c2c-4e6c-b8cf-4218c995cff5"],"layerId":"e84503c1-4dbd-4ac6-9ac9-ad938654680f","position":"top","seriesType":"line","showGridlines":false,"splitAccessor":"38c73fd4-6330-4162-8a7b-1a059f005da8","xAccessor":"e8d4dad2-ac30-4741-aca0-904eb1fc8455"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"line","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_line_chart","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.5","id":"7f3b5fb0-be2f-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-e84503c1-4dbd-4ac6-9ac9-ad938654680f","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,89],"type":"lens","updated_at":"2022-05-03T17:43:32.857Z","version":"WzU4LDFd"} +{"attributes":{"fieldAttrs":"{\"speaker\":{\"count\":1},\"text_entry\":{\"count\":6},\"type\":{\"count\":3}}","fields":"[]","runtimeFieldMap":"{}","title":"shakespeare"},"coreMigrationVersion":"7.13.5","id":"4e937b20-619d-11eb-aebf-c306684b328d","migrationVersion":{"index-pattern":"7.11.0"},"references":[],"sort":[1651599812857,90],"type":"index-pattern","updated_at":"2022-05-03T17:43:32.857Z","version":"WzU5LDFd"} +{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"d35680ce-c285-4fae-89d6-1245671bbc78":{"columnOrder":["2bcbffbe-c24d-4e74-8a03-9a6da7db70c0","6b00fde6-bfaa-4da1-beeb-bfd85a4cb2ff","8319857d-a03b-4158-bdf1-2a788e510445"],"columns":{"2bcbffbe-c24d-4e74-8a03-9a6da7db70c0":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"},"6b00fde6-bfaa-4da1-beeb-bfd85a4cb2ff":{"dataType":"number","isBucketed":false,"label":"Average of bytes","operationType":"average","scale":"ratio","sourceField":"bytes"},"8319857d-a03b-4158-bdf1-2a788e510445":{"dataType":"number","isBucketed":false,"label":"Sum of bytes_scripted","operationType":"sum","params":{"format":{"id":"number","params":{"decimals":2}}},"scale":"ratio","sourceField":"bytes_scripted"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["6b00fde6-bfaa-4da1-beeb-bfd85a4cb2ff","8319857d-a03b-4158-bdf1-2a788e510445"],"layerId":"d35680ce-c285-4fae-89d6-1245671bbc78","position":"top","seriesType":"area","showGridlines":false,"xAccessor":"2bcbffbe-c24d-4e74-8a03-9a6da7db70c0","yConfig":[{"axisMode":"auto","forAccessor":"8319857d-a03b-4158-bdf1-2a788e510445"}]}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"area","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_area_chart","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.5","id":"bb9e5bb0-be2f-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-d35680ce-c285-4fae-89d6-1245671bbc78","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,94],"type":"lens","updated_at":"2022-05-03T17:43:32.857Z","version":"WzYwLDFd"} +{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"70bd567e-8e67-4696-a406-313b06344fa9":{"columnOrder":["96ddedfb-043b-479e-a746-600e72ab546e","d325b7da-4266-4035-9b13-5f853615149a","2fc1391b-17d1-4c49-9ddc-06ff307e3520","1cc6f19c-cbcb-4abd-b56d-1a2f9deae5f3"],"columns":{"1cc6f19c-cbcb-4abd-b56d-1a2f9deae5f3":{"dataType":"number","isBucketed":false,"label":"Average of machine.ram","operationType":"average","scale":"ratio","sourceField":"machine.ram"},"2fc1391b-17d1-4c49-9ddc-06ff307e3520":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"},"96ddedfb-043b-479e-a746-600e72ab546e":{"dataType":"string","isBucketed":true,"label":"Top values of machine.os.raw","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"2fc1391b-17d1-4c49-9ddc-06ff307e3520","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"machine.os.raw"},"d325b7da-4266-4035-9b13-5f853615149a":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["2fc1391b-17d1-4c49-9ddc-06ff307e3520","1cc6f19c-cbcb-4abd-b56d-1a2f9deae5f3"],"layerId":"70bd567e-8e67-4696-a406-313b06344fa9","position":"top","seriesType":"area_stacked","showGridlines":false,"splitAccessor":"96ddedfb-043b-479e-a746-600e72ab546e","xAccessor":"d325b7da-4266-4035-9b13-5f853615149a"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"area_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_area_stacked","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.5","id":"dd315430-be2f-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-70bd567e-8e67-4696-a406-313b06344fa9","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,98],"type":"lens","updated_at":"2022-05-03T17:43:32.857Z","version":"WzYxLDFd"} +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"2e80716f-c1b6-46f2-be2b-35db744b5031\"},\"panelIndex\":\"2e80716f-c1b6-46f2-be2b-35db744b5031\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsPie\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"26e2cf99-d931-4320-9e15-9dbc148f3534\":{\"columns\":{\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\":{\"label\":\"Top values of url.raw\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"url.raw\",\"isBucketed\":true,\"params\":{\"size\":20,\"orderBy\":{\"type\":\"column\",\"columnId\":\"beb72af1-239c-46d8-823b-b00d1e2ace43\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"beb72af1-239c-46d8-823b-b00d1e2ace43\":{\"label\":\"Unique count of geo.srcdest\",\"dataType\":\"number\",\"operationType\":\"unique_count\",\"scale\":\"ratio\",\"sourceField\":\"geo.srcdest\",\"isBucketed\":false}},\"columnOrder\":[\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"beb72af1-239c-46d8-823b-b00d1e2ace43\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"shape\":\"donut\",\"layers\":[{\"layerId\":\"26e2cf99-d931-4320-9e15-9dbc148f3534\",\"groups\":[\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\"],\"metric\":\"beb72af1-239c-46d8-823b-b00d1e2ace43\",\"numberDisplay\":\"percent\",\"categoryDisplay\":\"default\",\"legendDisplay\":\"default\",\"nestedLegend\":false}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-layer-26e2cf99-d931-4320-9e15-9dbc148f3534\"}]},\"enhancements\":{},\"type\":\"lens\"},\"panelRefName\":\"panel_2e80716f-c1b6-46f2-be2b-35db744b5031\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"da8843e0-6789-4aae-bcd0-81f270538719\"},\"panelIndex\":\"da8843e0-6789-4aae-bcd0-81f270538719\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_da8843e0-6789-4aae-bcd0-81f270538719\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":15,\"w\":24,\"h\":15,\"i\":\"adcd4418-7299-4efa-b369-5f71a7b4ebe0\"},\"panelIndex\":\"adcd4418-7299-4efa-b369-5f71a7b4ebe0\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_adcd4418-7299-4efa-b369-5f71a7b4ebe0\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"869754a7-edf0-478f-a7f1-80374f63108a\"},\"panelIndex\":\"869754a7-edf0-478f-a7f1-80374f63108a\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_869754a7-edf0-478f-a7f1-80374f63108a\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":30,\"w\":24,\"h\":15,\"i\":\"67111cf4-338e-453f-8621-e8dea64082d1\"},\"panelIndex\":\"67111cf4-338e-453f-8621-e8dea64082d1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_67111cf4-338e-453f-8621-e8dea64082d1\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":30,\"w\":24,\"h\":15,\"i\":\"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\"},\"panelIndex\":\"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":45,\"w\":24,\"h\":15,\"i\":\"88847944-ae1b-45fd-b102-3b45f9bea04b\"},\"panelIndex\":\"88847944-ae1b-45fd-b102-3b45f9bea04b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_88847944-ae1b-45fd-b102-3b45f9bea04b\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":45,\"w\":24,\"h\":15,\"i\":\"5a7924c7-eac0-4573-9199-fecec5b82e9e\"},\"panelIndex\":\"5a7924c7-eac0-4573-9199-fecec5b82e9e\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5a7924c7-eac0-4573-9199-fecec5b82e9e\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":60,\"w\":24,\"h\":15,\"i\":\"f8f49591-f071-4a96-b1ed-cd65daff5648\"},\"panelIndex\":\"f8f49591-f071-4a96-b1ed-cd65daff5648\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_f8f49591-f071-4a96-b1ed-cd65daff5648\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":60,\"w\":24,\"h\":15,\"i\":\"9f357f47-c2a0-421f-a456-9583c40837ab\"},\"panelIndex\":\"9f357f47-c2a0-421f-a456-9583c40837ab\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9f357f47-c2a0-421f-a456-9583c40837ab\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":75,\"w\":24,\"h\":15,\"i\":\"6cb383e9-1e80-44f9-80d5-7b8c585668db\"},\"panelIndex\":\"6cb383e9-1e80-44f9-80d5-7b8c585668db\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6cb383e9-1e80-44f9-80d5-7b8c585668db\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":75,\"w\":24,\"h\":15,\"i\":\"57f5f0bf-6610-4599-aad4-37484640b5e2\"},\"panelIndex\":\"57f5f0bf-6610-4599-aad4-37484640b5e2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_57f5f0bf-6610-4599-aad4-37484640b5e2\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":90,\"w\":24,\"h\":15,\"i\":\"32d3ab66-52e1-44e3-8c1f-1dccff3c5692\"},\"panelIndex\":\"32d3ab66-52e1-44e3-8c1f-1dccff3c5692\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_32d3ab66-52e1-44e3-8c1f-1dccff3c5692\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":90,\"w\":24,\"h\":15,\"i\":\"dd1718fd-74ee-4032-851b-db97e893825d\"},\"panelIndex\":\"dd1718fd-74ee-4032-851b-db97e893825d\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_dd1718fd-74ee-4032-851b-db97e893825d\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":105,\"w\":24,\"h\":15,\"i\":\"98a556ee-078b-4e03-93a8-29996133cdcb\"},\"panelIndex\":\"98a556ee-078b-4e03-93a8-29996133cdcb\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsXY\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"999a2d60-cb2a-451c-8d71-80d7e92e70fd\":{\"columns\":{\"ce9117a2-773c-474c-8fb1-18940cf58b38\":{\"label\":\"Top values of type\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"type\",\"isBucketed\":true,\"params\":{\"size\":5,\"orderBy\":{\"type\":\"column\",\"columnId\":\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\"},\"orderDirection\":\"asc\",\"otherBucket\":true,\"missingBucket\":false}},\"a3d10552-e352-40d0-a156-e86112c0501a\":{\"label\":\"Top values of _type\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"_type\",\"isBucketed\":true,\"params\":{\"size\":3,\"orderBy\":{\"type\":\"column\",\"columnId\":\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"Records\"},\"9c5db2f3-9eb0-4667-9a74-3318301de251\":{\"label\":\"Sum of bytes\",\"dataType\":\"number\",\"operationType\":\"sum\",\"sourceField\":\"bytes\",\"isBucketed\":false,\"scale\":\"ratio\"}},\"columnOrder\":[\"ce9117a2-773c-474c-8fb1-18940cf58b38\",\"a3d10552-e352-40d0-a156-e86112c0501a\",\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\",\"9c5db2f3-9eb0-4667-9a74-3318301de251\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"legend\":{\"isVisible\":true,\"position\":\"right\"},\"valueLabels\":\"hide\",\"fittingFunction\":\"None\",\"axisTitlesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"bar_stacked\",\"layers\":[{\"layerId\":\"999a2d60-cb2a-451c-8d71-80d7e92e70fd\",\"accessors\":[\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\",\"9c5db2f3-9eb0-4667-9a74-3318301de251\"],\"position\":\"top\",\"seriesType\":\"bar_stacked\",\"showGridlines\":false,\"xAccessor\":\"ce9117a2-773c-474c-8fb1-18940cf58b38\",\"splitAccessor\":\"a3d10552-e352-40d0-a156-e86112c0501a\"}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-layer-999a2d60-cb2a-451c-8d71-80d7e92e70fd\"}]},\"enhancements\":{},\"type\":\"lens\"}},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":105,\"w\":24,\"h\":15,\"i\":\"62a0f0b0-3589-4cef-807b-b1b4258b7a9b\"},\"panelIndex\":\"62a0f0b0-3589-4cef-807b-b1b4258b7a9b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_62a0f0b0-3589-4cef-807b-b1b4258b7a9b\"},{\"version\":\"7.13.1\",\"type\":\"map\",\"gridData\":{\"x\":0,\"y\":120,\"w\":24,\"h\":15,\"i\":\"dcc0defa-3376-465c-9b5b-2ba69528848c\"},\"panelIndex\":\"dcc0defa-3376-465c-9b5b-2ba69528848c\",\"embeddableConfig\":{\"mapCenter\":{\"lat\":19.94277,\"lon\":0,\"zoom\":1.56},\"mapBuffer\":{\"minLon\":-210.32666,\"minLat\":-64.8435,\"maxLon\":210.32666,\"maxLat\":95.13806},\"isLayerTOCOpen\":true,\"openTOCDetails\":[],\"hiddenLayers\":[],\"enhancements\":{}},\"panelRefName\":\"panel_dcc0defa-3376-465c-9b5b-2ba69528848c\"}]","refreshInterval":{"pause":true,"value":0},"timeFrom":"2015-09-20T01:56:56.132Z","timeRestore":true,"timeTo":"2015-09-21T11:18:20.471Z","title":"lens_maps_dashboard_logstash","version":1},"coreMigrationVersion":"7.13.5","id":"16d86080-be5c-11eb-9520-1b4c3ca6a781","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"21905950-bd9f-11eb-9520-1b4c3ca6a781","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:panel_2e80716f-c1b6-46f2-be2b-35db744b5031","type":"lens"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:indexpattern-datasource-layer-26e2cf99-d931-4320-9e15-9dbc148f3534","type":"index-pattern"},{"id":"aa4b8da0-bd9f-11eb-9520-1b4c3ca6a781","name":"da8843e0-6789-4aae-bcd0-81f270538719:panel_da8843e0-6789-4aae-bcd0-81f270538719","type":"lens"},{"id":"2d3f1250-bd9f-11eb-9520-1b4c3ca6a781","name":"adcd4418-7299-4efa-b369-5f71a7b4ebe0:panel_adcd4418-7299-4efa-b369-5f71a7b4ebe0","type":"lens"},{"id":"edd5a560-bda4-11eb-9520-1b4c3ca6a781","name":"869754a7-edf0-478f-a7f1-80374f63108a:panel_869754a7-edf0-478f-a7f1-80374f63108a","type":"lens"},{"id":"2c25a450-bda5-11eb-9520-1b4c3ca6a781","name":"67111cf4-338e-453f-8621-e8dea64082d1:panel_67111cf4-338e-453f-8621-e8dea64082d1","type":"lens"},{"id":"e79116e0-bd9e-11eb-9520-1b4c3ca6a781","name":"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d:panel_13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d","type":"lens"},{"id":"974fb950-bda5-11eb-9520-1b4c3ca6a781","name":"88847944-ae1b-45fd-b102-3b45f9bea04b:panel_88847944-ae1b-45fd-b102-3b45f9bea04b","type":"lens"},{"id":"21905950-bd9f-11eb-9520-1b4c3ca6a781","name":"5a7924c7-eac0-4573-9199-fecec5b82e9e:panel_5a7924c7-eac0-4573-9199-fecec5b82e9e","type":"lens"},{"id":"51b63040-bda5-11eb-9520-1b4c3ca6a781","name":"f8f49591-f071-4a96-b1ed-cd65daff5648:panel_f8f49591-f071-4a96-b1ed-cd65daff5648","type":"lens"},{"id":"b00679c0-bda5-11eb-9520-1b4c3ca6a781","name":"9f357f47-c2a0-421f-a456-9583c40837ab:panel_9f357f47-c2a0-421f-a456-9583c40837ab","type":"lens"},{"id":"652ade10-bd9f-11eb-9520-1b4c3ca6a781","name":"6cb383e9-1e80-44f9-80d5-7b8c585668db:panel_6cb383e9-1e80-44f9-80d5-7b8c585668db","type":"lens"},{"id":"7f3b5fb0-be2f-11eb-9520-1b4c3ca6a781","name":"57f5f0bf-6610-4599-aad4-37484640b5e2:panel_57f5f0bf-6610-4599-aad4-37484640b5e2","type":"lens"},{"id":"bb9e5bb0-be2f-11eb-9520-1b4c3ca6a781","name":"32d3ab66-52e1-44e3-8c1f-1dccff3c5692:panel_32d3ab66-52e1-44e3-8c1f-1dccff3c5692","type":"lens"},{"id":"dd315430-be2f-11eb-9520-1b4c3ca6a781","name":"dd1718fd-74ee-4032-851b-db97e893825d:panel_dd1718fd-74ee-4032-851b-db97e893825d","type":"lens"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"98a556ee-078b-4e03-93a8-29996133cdcb:indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"98a556ee-078b-4e03-93a8-29996133cdcb:indexpattern-datasource-layer-999a2d60-cb2a-451c-8d71-80d7e92e70fd","type":"index-pattern"},{"id":"0dbbf8b0-be3c-11eb-9520-1b4c3ca6a781","name":"62a0f0b0-3589-4cef-807b-b1b4258b7a9b:panel_62a0f0b0-3589-4cef-807b-b1b4258b7a9b","type":"lens"},{"id":"0c5974f0-be5c-11eb-9520-1b4c3ca6a781","name":"dcc0defa-3376-465c-9b5b-2ba69528848c:panel_dcc0defa-3376-465c-9b5b-2ba69528848c","type":"map"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,120],"type":"dashboard","updated_at":"2022-05-03T17:43:32.857Z","version":"WzYyLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"shakespeare_areachart","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"shakespeare_areachart\",\"type\":\"histogram\",\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"mode\":\"stacked\",\"type\":\"histogram\",\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"data\":{\"id\":\"2\",\"label\":\"Count\"},\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"aggs\":[{\"id\":\"2\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"play_name\",\"size\":20,\"order\":\"desc\",\"orderBy\":\"2\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"play_name\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"2\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.5","id":"185283c0-619e-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,122],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzY0LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"by_reference_logstash","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"by_reference_logstash\",\"type\":\"histogram\",\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{},\"style\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"},\"style\":{}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"radiusRatio\":0,\"addTooltip\":true,\"detailedTooltip\":true,\"palette\":{\"type\":\"palette\",\"name\":\"default\"},\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":false},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"2014-07-15T12:33:21.084Z\",\"to\":\"2019-01-28T03:18:12.440Z\"},\"useNormalizedEsInterval\":true,\"scaleMetricValues\":false,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}},\"schema\":\"segment\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"response.raw\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"group\"}]}"},"coreMigrationVersion":"7.13.5","id":"1885abb0-ca2b-11eb-bf5e-3de94e83d4f0","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651612828183,981],"type":"visualization","updated_at":"2022-05-03T21:20:28.183Z","version":"WzU1OSwxXQ=="} +{"attributes":{"color":"#f44fcf","description":"","name":"shakespeare"},"coreMigrationVersion":"7.13.5","id":"42b4cec0-be32-11eb-9520-1b4c3ca6a781","references":[],"sort":[1651599812857,123],"type":"tag","updated_at":"2022-05-03T17:43:32.857Z","version":"WzY2LDFd"} +{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"3338dd55-4007-4be5-908d-25722b6174cb":{"columnOrder":["6c83b0c2-5834-4619-888c-9e8a08e47d42","b25e7497-c188-4c25-b002-1fd5bd69e76d"],"columns":{"6c83b0c2-5834-4619-888c-9e8a08e47d42":{"dataType":"string","isBucketed":true,"label":"Top values of speaker","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"b25e7497-c188-4c25-b002-1fd5bd69e76d","type":"column"},"orderDirection":"desc","otherBucket":false,"size":90},"scale":"ordinal","sourceField":"speaker"},"b25e7497-c188-4c25-b002-1fd5bd69e76d":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"layers":[{"categoryDisplay":"default","groups":["6c83b0c2-5834-4619-888c-9e8a08e47d42","6c83b0c2-5834-4619-888c-9e8a08e47d42","6c83b0c2-5834-4619-888c-9e8a08e47d42"],"layerId":"3338dd55-4007-4be5-908d-25722b6174cb","legendDisplay":"default","metric":"b25e7497-c188-4c25-b002-1fd5bd69e76d","nestedLegend":false,"numberDisplay":"percent"}],"palette":{"name":"complimentary","type":"palette"},"shape":"treemap"}},"title":"lens_shakespeare_treemap","visualizationType":"lnsPie"},"coreMigrationVersion":"7.13.5","id":"31e9f2f0-be32-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-3338dd55-4007-4be5-908d-25722b6174cb","type":"index-pattern"},{"id":"42b4cec0-be32-11eb-9520-1b4c3ca6a781","name":"tag-ref-42b4cec0-be32-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,127],"type":"lens","updated_at":"2022-05-03T17:43:32.857Z","version":"WzY3LDFd"} +{"attributes":{"accessCount":0,"accessDate":1622059178542,"createDate":1622059178542,"url":"/app/dashboards#/view/73398a90-619e-11eb-aebf-c306684b328d?embed=true&_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:%272015-09-20T01:56:56.132Z%27,to:%272015-09-21T11:18:20.471Z%27))&_a=(description:%27%27,filters:!(),fullScreenMode:!f,options:(darkTheme:!f,hidePanelTitles:!f,useMargins:!t),panels:!((embeddableConfig:(enhancements:()),gridData:(h:15,i:%271%27,w:24,x:0,y:0),id:%27185283c0-619e-11eb-aebf-c306684b328d%27,panelIndex:%271%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%272%27,w:24,x:24,y:0),id:%2733736660-619e-11eb-aebf-c306684b328d%27,panelIndex:%272%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%273%27,w:24,x:0,y:15),id:%27622ac7f0-619e-11eb-aebf-c306684b328d%27,panelIndex:%273%27,type:visualization,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%274%27,w:24,x:24,y:15),id:%27712ebbe0-619d-11eb-aebf-c306684b328d%27,panelIndex:%274%27,type:search,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%275%27,w:24,x:0,y:30),id:ddacc820-619d-11eb-aebf-c306684b328d,panelIndex:%275%27,type:search,version:%277.13.1%27),(embeddableConfig:(enhancements:()),gridData:(h:15,i:%276%27,w:24,x:24,y:30),id:f852d570-619d-11eb-aebf-c306684b328d,panelIndex:%276%27,type:search,version:%277.13.1%27)),query:(language:kuery,query:%27%27),tags:!(),timeRestore:!f,title:shakespeare_dashboard,viewMode:view)"},"coreMigrationVersion":"7.13.5","id":"32a03249ec3a048108d4b5a427a37fc8","references":[],"sort":[1651599812857,128],"type":"url","updated_at":"2022-05-03T17:43:32.857Z","version":"WzY4LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"shakespeare_piechart","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"shakespeare_piechart\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":false,\"labels\":{\"show\":true,\"values\":true,\"last_level\":true,\"truncate\":100}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"play_name\",\"size\":15,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.5","id":"33736660-619e-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,130],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzY5LDFd"} +{"attributes":{"color":"#7b01cf","description":"","name":"By reference"},"coreMigrationVersion":"7.13.5","id":"39d2c190-ca2b-11eb-bf5e-3de94e83d4f0","references":[],"sort":[1651599812857,131],"type":"tag","updated_at":"2022-05-03T17:43:32.857Z","version":"WzcwLDFd"} +{"attributes":{"columns":[],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[{\"meta\":{\"alias\":null,\"negate\":false,\"disabled\":false,\"type\":\"phrase\",\"key\":\"ip\",\"params\":{\"query\":\"57.237.11.219\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match_phrase\":{\"ip\":\"57.237.11.219\"}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["@timestamp","desc"]],"title":"drilldown_saved_search","version":1},"coreMigrationVersion":"7.13.5","id":"4acce030-ca2a-11eb-bf5e-3de94e83d4f0","migrationVersion":{"search":"7.9.3"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"}],"sort":[1651612879859,1007],"type":"search","updated_at":"2022-05-03T21:21:19.859Z","version":"WzU2NywxXQ=="} +{"attributes":{"description":"","layerListJSON":"[{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"isAutoSelect\":true},\"id\":\"e0d51731-2bb3-4fed-92af-65f93c3e7e58\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"style\":{\"type\":\"TILE\"},\"type\":\"VECTOR_TILE\"},{\"sourceDescriptor\":{\"geoField\":\"geo.coordinates\",\"filterByMapBounds\":true,\"scalingType\":\"CLUSTERS\",\"topHitsSplitField\":\"\",\"topHitsSize\":1,\"id\":\"142e0a6b-53c9-4f66-a65d-fced755318de\",\"type\":\"ES_SEARCH\",\"applyGlobalQuery\":true,\"applyGlobalTime\":true,\"tooltipProperties\":[],\"sortField\":\"\",\"sortOrder\":\"desc\",\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"id\":\"ca96ce4a-4e73-46a5-bcc8-99a39d227030\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}},\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#CA8EAE\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#934193\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":6}},\"iconOrientation\":{\"type\":\"STATIC\",\"options\":{\"orientation\":0}},\"labelText\":{\"type\":\"STATIC\",\"options\":{\"value\":\"\"}},\"labelColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#000000\"}},\"labelSize\":{\"type\":\"STATIC\",\"options\":{\"size\":14}},\"labelBorderColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"labelBorderSize\":{\"options\":{\"size\":\"SMALL\"}}},\"isTimeAware\":true},\"type\":\"BLENDED_VECTOR\",\"joins\":[]}]","mapStateJSON":"{\"zoom\":1.38,\"center\":{\"lon\":0,\"lat\":19.94277},\"timeFilters\":{\"from\":\"2014-07-15T12:33:21.084Z\",\"to\":\"2019-01-28T03:18:12.440Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":0},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"settings\":{\"autoFitToDataBounds\":false,\"backgroundColor\":\"#ffffff\",\"disableInteractive\":false,\"disableTooltipControl\":false,\"hideToolbarOverlay\":false,\"hideLayerControl\":false,\"hideViewControl\":false,\"initialLocation\":\"LAST_SAVED_LOCATION\",\"fixedLocation\":{\"lat\":0,\"lon\":0,\"zoom\":2},\"browserLocation\":{\"zoom\":2},\"maxZoom\":24,\"minZoom\":0,\"showScaleControl\":false,\"showSpatialFilters\":true,\"spatialFiltersAlpa\":0.3,\"spatialFiltersFillColor\":\"#DA8B45\",\"spatialFiltersLineColor\":\"#DA8B45\"}}","title":"Logstash_map_by_reference","uiStateJSON":"{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}"},"coreMigrationVersion":"7.13.5","id":"a53a2db0-ca2b-11eb-bf5e-3de94e83d4f0","migrationVersion":{"map":"7.12.0"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"layer_1_source_index_pattern","type":"index-pattern"}],"sort":[1651612954243,1037],"type":"map","updated_at":"2022-05-03T21:22:34.243Z","version":"WzYwOSwxXQ=="} +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.5\",\"type\":\"search\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"7c29a321-2a9a-412b-9ed1-1d0a1f66ea63\"},\"panelIndex\":\"7c29a321-2a9a-412b-9ed1-1d0a1f66ea63\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7c29a321-2a9a-412b-9ed1-1d0a1f66ea63\"},{\"version\":\"7.13.5\",\"type\":\"search\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"f2d1feb1-d807-46b1-90ac-96d4a9edb6b1\"},\"panelIndex\":\"f2d1feb1-d807-46b1-90ac-96d4a9edb6b1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_f2d1feb1-d807-46b1-90ac-96d4a9edb6b1\"},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"a3530107-8b1c-4e94-8f99-e239fa40a09c\"},\"panelIndex\":\"a3530107-8b1c-4e94-8f99-e239fa40a09c\",\"embeddableConfig\":{\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"b14188e0-53d6-433e-874f-b1be7c97487c\",\"triggers\":[\"FILTER_TRIGGER\"],\"action\":{\"name\":\"by_reference_going_to_value\",\"config\":{\"useCurrentFilters\":true,\"useCurrentDateRange\":true},\"factoryId\":\"DASHBOARD_TO_DASHBOARD_DRILLDOWN\"}},{\"eventId\":\"60cba413-1793-4dd3-b072-9d53655d5522\",\"triggers\":[\"SELECT_RANGE_TRIGGER\"],\"action\":{\"name\":\"Goto_Discover\",\"config\":{\"url\":{\"template\":\"http://localhost:5601/app/discover#/view/b3288100-ca2c-11eb-bf5e-3de94e83d4f0?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15y,to:now))&_a=(columns:!(),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'43fcac20-ca27-11eb-bf5e-3de94e83d4f0',key:geo.dest,negate:!f,params:(query:US),type:phrase),query:(match_phrase:(geo.dest:US)))),index:'43fcac20-ca27-11eb-bf5e-3de94e83d4f0',interval:auto,query:(language:kuery,query:''),sort:!(!('@timestamp',desc)))\"},\"openInNewTab\":true,\"encodeUrl\":true},\"factoryId\":\"URL_DRILLDOWN\"}}]}}},\"panelRefName\":\"panel_a3530107-8b1c-4e94-8f99-e239fa40a09c\"},{\"version\":\"7.13.5\",\"type\":\"map\",\"gridData\":{\"x\":24,\"y\":15,\"w\":24,\"h\":15,\"i\":\"77245314-9495-4625-9f53-0946150e26d4\"},\"panelIndex\":\"77245314-9495-4625-9f53-0946150e26d4\",\"embeddableConfig\":{\"mapCenter\":{\"lat\":19.94277,\"lon\":0,\"zoom\":1.38},\"mapBuffer\":{\"minLon\":-238.27568,\"minLat\":-74.644155,\"maxLon\":238.27568,\"maxLat\":102.864625},\"isLayerTOCOpen\":true,\"openTOCDetails\":[],\"hiddenLayers\":[],\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"9b61b9d4-20a3-4bca-9697-1097c524a943\",\"triggers\":[\"FILTER_TRIGGER\"],\"action\":{\"name\":\"By_reference_to_value\",\"config\":{\"useCurrentFilters\":true,\"useCurrentDateRange\":true},\"factoryId\":\"DASHBOARD_TO_DASHBOARD_DRILLDOWN\"}}]}}},\"panelRefName\":\"panel_77245314-9495-4625-9f53-0946150e26d4\"}]","timeRestore":false,"title":"by_reference_drilldown","version":1},"coreMigrationVersion":"7.13.5","id":"3b844220-ca2b-11eb-bf5e-3de94e83d4f0","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"4acce030-ca2a-11eb-bf5e-3de94e83d4f0","name":"7c29a321-2a9a-412b-9ed1-1d0a1f66ea63:panel_7c29a321-2a9a-412b-9ed1-1d0a1f66ea63","type":"search"},{"id":"0abce1c0-ca2a-11eb-bf5e-3de94e83d4f0","name":"f2d1feb1-d807-46b1-90ac-96d4a9edb6b1:panel_f2d1feb1-d807-46b1-90ac-96d4a9edb6b1","type":"search"},{"id":"1885abb0-ca2b-11eb-bf5e-3de94e83d4f0","name":"a3530107-8b1c-4e94-8f99-e239fa40a09c:panel_a3530107-8b1c-4e94-8f99-e239fa40a09c","type":"visualization"},{"id":"08dec860-ca29-11eb-bf5e-3de94e83d4f0","name":"a3530107-8b1c-4e94-8f99-e239fa40a09c:drilldown:DASHBOARD_TO_DASHBOARD_DRILLDOWN:b14188e0-53d6-433e-874f-b1be7c97487c:dashboardId","type":"dashboard"},{"id":"a53a2db0-ca2b-11eb-bf5e-3de94e83d4f0","name":"77245314-9495-4625-9f53-0946150e26d4:panel_77245314-9495-4625-9f53-0946150e26d4","type":"map"},{"id":"35ce3b30-ca29-11eb-bf5e-3de94e83d4f0","name":"77245314-9495-4625-9f53-0946150e26d4:drilldown:DASHBOARD_TO_DASHBOARD_DRILLDOWN:9b61b9d4-20a3-4bca-9697-1097c524a943:dashboardId","type":"dashboard"},{"id":"39d2c190-ca2b-11eb-bf5e-3de94e83d4f0","name":"tag-39d2c190-ca2b-11eb-bf5e-3de94e83d4f0","type":"tag"}],"sort":[1651612956152,1046],"type":"dashboard","updated_at":"2022-05-03T21:22:36.152Z","version":"WzYxNSwxXQ=="} +{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"a7a8f2fb-066e-4023-9755-821e84560b4a":{"columnOrder":["ee46f645-0af0-4b5d-8ed3-2557c98c9c12","91859a54-9b88-4478-8c80-0779fe165fba","62a4dea1-fab9-45ff-93e0-b99cfff719d5"],"columns":{"62a4dea1-fab9-45ff-93e0-b99cfff719d5":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"},"91859a54-9b88-4478-8c80-0779fe165fba":{"dataType":"string","isBucketed":true,"label":"Top values of play_name","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"62a4dea1-fab9-45ff-93e0-b99cfff719d5","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"play_name"},"ee46f645-0af0-4b5d-8ed3-2557c98c9c12":{"dataType":"string","isBucketed":true,"label":"Top values of speaker","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"62a4dea1-fab9-45ff-93e0-b99cfff719d5","type":"column"},"orderDirection":"desc","otherBucket":true,"size":25},"scale":"ordinal","sourceField":"speaker"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"kuery","query":""},"visualization":{"layers":[{"categoryDisplay":"default","groups":["ee46f645-0af0-4b5d-8ed3-2557c98c9c12","ee46f645-0af0-4b5d-8ed3-2557c98c9c12","ee46f645-0af0-4b5d-8ed3-2557c98c9c12","ee46f645-0af0-4b5d-8ed3-2557c98c9c12","91859a54-9b88-4478-8c80-0779fe165fba"],"layerId":"a7a8f2fb-066e-4023-9755-821e84560b4a","legendDisplay":"default","metric":"62a4dea1-fab9-45ff-93e0-b99cfff719d5","nestedLegend":false,"numberDisplay":"percent"}],"palette":{"name":"kibana_palette","type":"palette"},"shape":"pie"}},"title":"lens_shakespeare_piechart","visualizationType":"lnsPie"},"coreMigrationVersion":"7.13.5","id":"b5bd5050-be31-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-a7a8f2fb-066e-4023-9755-821e84560b4a","type":"index-pattern"},{"id":"42b4cec0-be32-11eb-9520-1b4c3ca6a781","name":"tag-ref-42b4cec0-be32-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,135],"type":"lens","updated_at":"2022-05-03T17:43:32.857Z","version":"Wzc1LDFd"} +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"c4b1daae-a3af-4136-969e-8485d4ba53f9\"},\"panelIndex\":\"c4b1daae-a3af-4136-969e-8485d4ba53f9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_c4b1daae-a3af-4136-969e-8485d4ba53f9\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"f092b002-182e-49b8-bcc4-58f5233e041b\"},\"panelIndex\":\"f092b002-182e-49b8-bcc4-58f5233e041b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_f092b002-182e-49b8-bcc4-58f5233e041b\"}]","refreshInterval":{"pause":true,"value":0},"timeFrom":"2015-09-20T01:56:56.132Z","timeRestore":true,"timeTo":"2015-09-21T11:18:20.471Z","title":"lens_shakespeare_dashboard","version":1},"coreMigrationVersion":"7.13.5","id":"43fae350-be32-11eb-9520-1b4c3ca6a781","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"b5bd5050-be31-11eb-9520-1b4c3ca6a781","name":"c4b1daae-a3af-4136-969e-8485d4ba53f9:panel_c4b1daae-a3af-4136-969e-8485d4ba53f9","type":"lens"},{"id":"31e9f2f0-be32-11eb-9520-1b4c3ca6a781","name":"f092b002-182e-49b8-bcc4-58f5233e041b:panel_f092b002-182e-49b8-bcc4-58f5233e041b","type":"lens"},{"id":"42b4cec0-be32-11eb-9520-1b4c3ca6a781","name":"tag-42b4cec0-be32-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,139],"type":"dashboard","updated_at":"2022-05-03T17:43:32.857Z","version":"Wzc2LDFd"} +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"1\",\"w\":24,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"2\",\"w\":24,\"x\":24,\"y\":0},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"3\",\"w\":24,\"x\":0,\"y\":15},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"4\",\"w\":24,\"x\":24,\"y\":15},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"5\",\"w\":24,\"x\":0,\"y\":30},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"6\",\"w\":24,\"x\":24,\"y\":30},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"7\",\"w\":24,\"x\":0,\"y\":45},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"8\",\"w\":24,\"x\":24,\"y\":45},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_8\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"9\",\"w\":24,\"x\":0,\"y\":60},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"10\",\"w\":24,\"x\":24,\"y\":60},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"11\",\"w\":24,\"x\":0,\"y\":75},\"panelIndex\":\"11\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_11\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"12\",\"w\":24,\"x\":24,\"y\":75},\"panelIndex\":\"12\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_12\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"13\",\"w\":24,\"x\":0,\"y\":90},\"panelIndex\":\"13\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"14\",\"w\":24,\"x\":24,\"y\":90},\"panelIndex\":\"14\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_14\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"15\",\"w\":24,\"x\":0,\"y\":105},\"panelIndex\":\"15\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_15\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"16\",\"w\":24,\"x\":24,\"y\":105},\"panelIndex\":\"16\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_16\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"17\",\"w\":24,\"x\":0,\"y\":120},\"panelIndex\":\"17\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_17\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"18\",\"w\":24,\"x\":24,\"y\":120},\"panelIndex\":\"18\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_18\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"19\",\"w\":24,\"x\":0,\"y\":135},\"panelIndex\":\"19\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_19\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":15,\"i\":\"20\",\"w\":24,\"x\":24,\"y\":135},\"panelIndex\":\"20\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_20\"}]","timeRestore":false,"title":"logstash_dashboard_withouttime","version":1},"coreMigrationVersion":"7.13.5","id":"5d3410c0-6249-11eb-aebf-c306684b328d","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"36b91810-6239-11eb-aebf-c306684b328d","name":"1:panel_1","type":"visualization"},{"id":"0a274320-61cc-11eb-aebf-c306684b328d","name":"2:panel_2","type":"visualization"},{"id":"e4aef350-623d-11eb-aebf-c306684b328d","name":"3:panel_3","type":"visualization"},{"id":"f92e5630-623e-11eb-aebf-c306684b328d","name":"4:panel_4","type":"visualization"},{"id":"9853d4d0-623d-11eb-aebf-c306684b328d","name":"5:panel_5","type":"visualization"},{"id":"6ecb33b0-623d-11eb-aebf-c306684b328d","name":"6:panel_6","type":"visualization"},{"id":"b8e35c80-623c-11eb-aebf-c306684b328d","name":"7:panel_7","type":"visualization"},{"id":"f1bc75d0-6239-11eb-aebf-c306684b328d","name":"8:panel_8","type":"visualization"},{"id":"0d8a8860-623a-11eb-aebf-c306684b328d","name":"9:panel_9","type":"visualization"},{"id":"d79fe3d0-6239-11eb-aebf-c306684b328d","name":"10:panel_10","type":"visualization"},{"id":"318375a0-6240-11eb-aebf-c306684b328d","name":"11:panel_11","type":"visualization"},{"id":"e461eb20-6245-11eb-aebf-c306684b328d","name":"12:panel_12","type":"visualization"},{"id":"25bdc750-6242-11eb-aebf-c306684b328d","name":"13:panel_13","type":"visualization"},{"id":"71dd7bc0-6248-11eb-aebf-c306684b328d","name":"14:panel_14","type":"visualization"},{"id":"6aea48a0-6240-11eb-aebf-c306684b328d","name":"15:panel_15","type":"visualization"},{"id":"32b681f0-6241-11eb-aebf-c306684b328d","name":"16:panel_16","type":"visualization"},{"id":"ccca99e0-6244-11eb-aebf-c306684b328d","name":"17:panel_17","type":"visualization"},{"id":"a4d7be80-6245-11eb-aebf-c306684b328d","name":"18:panel_18","type":"visualization"},{"id":"c94d8440-6248-11eb-aebf-c306684b328d","name":"19:panel_19","type":"visualization"},{"id":"db6226f0-61c0-11eb-aebf-c306684b328d","name":"20:panel_20","type":"search"}],"sort":[1651599812857,160],"type":"dashboard","updated_at":"2022-05-03T17:43:32.857Z","version":"Wzc3LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"shakespeare_tag_cloud","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"shakespeare_tag_cloud\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"linear\",\"orientation\":\"multiple\",\"minFontSize\":59,\"maxFontSize\":100,\"showLabel\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"type.keyword\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"coreMigrationVersion":"7.13.5","id":"622ac7f0-619e-11eb-aebf-c306684b328d","migrationVersion":{"visualization":"7.13.1"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,162],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"Wzc4LDFd"} +{"attributes":{"numLinks":4,"numVertices":5,"title":"logstash_graph","version":1,"wsState":"\"{\\\"selectedFields\\\":[{\\\"name\\\":\\\"machine.os.raw\\\",\\\"hopSize\\\":5,\\\"lastValidHopSize\\\":5,\\\"color\\\":\\\"#B9A888\\\",\\\"selected\\\":true,\\\"iconClass\\\":\\\"fa-folder-open-o\\\"},{\\\"name\\\":\\\"response.raw\\\",\\\"hopSize\\\":5,\\\"lastValidHopSize\\\":5,\\\"color\\\":\\\"#D6BF57\\\",\\\"selected\\\":true,\\\"iconClass\\\":\\\"fa-folder-open-o\\\"}],\\\"blocklist\\\":[],\\\"vertices\\\":[{\\\"x\\\":461.96184642905024,\\\"y\\\":284.02313214227325,\\\"label\\\":\\\"osx\\\",\\\"color\\\":\\\"#B9A888\\\",\\\"field\\\":\\\"machine.os.raw\\\",\\\"term\\\":\\\"osx\\\",\\\"parent\\\":null,\\\"size\\\":15},{\\\"x\\\":383.946159835112,\\\"y\\\":375.6063135315976,\\\"label\\\":\\\"503\\\",\\\"color\\\":\\\"#D6BF57\\\",\\\"field\\\":\\\"response.raw\\\",\\\"term\\\":\\\"503\\\",\\\"parent\\\":null,\\\"size\\\":15},{\\\"x\\\":287.104700756828,\\\"y\\\":324.1245253249895,\\\"label\\\":\\\"win 7\\\",\\\"color\\\":\\\"#B9A888\\\",\\\"field\\\":\\\"machine.os.raw\\\",\\\"term\\\":\\\"win 7\\\",\\\"parent\\\":null,\\\"size\\\":15},{\\\"x\\\":487.9986107998273,\\\"y\\\":407.07546535764254,\\\"label\\\":\\\"ios\\\",\\\"color\\\":\\\"#B9A888\\\",\\\"field\\\":\\\"machine.os.raw\\\",\\\"term\\\":\\\"ios\\\",\\\"parent\\\":null,\\\"size\\\":15},{\\\"x\\\":302.35059551806023,\\\"y\\\":211.66825720913607,\\\"label\\\":\\\"200\\\",\\\"color\\\":\\\"#D6BF57\\\",\\\"field\\\":\\\"response.raw\\\",\\\"term\\\":\\\"200\\\",\\\"parent\\\":null,\\\"size\\\":15}],\\\"links\\\":[{\\\"weight\\\":0.000881324009872165,\\\"width\\\":7.983523640193488,\\\"source\\\":4,\\\"target\\\":2},{\\\"weight\\\":0.000023386835221992895,\\\"width\\\":2,\\\"source\\\":1,\\\"target\\\":0},{\\\"weight\\\":0.0011039286029480653,\\\"width\\\":2,\\\"source\\\":1,\\\"target\\\":2},{\\\"weight\\\":0.000045596928960694605,\\\"width\\\":2,\\\"source\\\":1,\\\"target\\\":3}],\\\"urlTemplates\\\":[{\\\"url\\\":\\\"/app/discover#/?_a=(columns%3A!(_source)%2Cindex%3A%2756b34100-619d-11eb-aebf-c306684b328d%27%2Cinterval%3Aauto%2Cquery%3A(language%3Akuery%2Cquery%3A{{gquery}})%2Csort%3A!(_score%2Cdesc))\\\",\\\"description\\\":\\\"Machine OS win 7\\\",\\\"isDefault\\\":false,\\\"encoderID\\\":\\\"kql\\\",\\\"iconClass\\\":\\\"fa-share-alt\\\"}],\\\"exploreControls\\\":{\\\"useSignificance\\\":true,\\\"sampleSize\\\":2000,\\\"timeoutMillis\\\":5000,\\\"maxValuesPerDoc\\\":1,\\\"minDocCount\\\":3},\\\"indexPatternRefName\\\":\\\"indexPattern_0\\\"}\""},"coreMigrationVersion":"7.13.5","id":"6afc4b40-be5c-11eb-9520-1b4c3ca6a781","migrationVersion":{"graph-workspace":"7.11.0"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexPattern_0","type":"index-pattern"}],"sort":[1651599812857,164],"type":"graph-workspace","updated_at":"2022-05-03T17:43:32.857Z","version":"Wzc5LDFd"} +{"attributes":{"buildNum":39457,"defaultIndex":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0"},"coreMigrationVersion":"7.13.5","id":"7.12.1","migrationVersion":{"config":"7.13.0"},"references":[],"sort":[1651599812857,165],"type":"config","updated_at":"2022-05-03T17:43:32.857Z","version":"WzgwLDFd"} +{"attributes":{"accessibility:disableAnimations":true,"buildNum":null,"dateFormat:tz":"UTC","defaultIndex":"56b34100-619d-11eb-aebf-c306684b328d","visualization:visualize:legacyChartsLibrary":true},"coreMigrationVersion":"7.13.5","id":"7.13.1","migrationVersion":{"config":"7.13.0"},"references":[],"sort":[1651599812857,166],"type":"config","updated_at":"2022-05-03T17:43:32.857Z","version":"WzgxLDFd"} +{"attributes":{"buildNum":40943,"defaultIndex":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0"},"coreMigrationVersion":"7.13.5","id":"7.13.2","migrationVersion":{"config":"7.13.0"},"references":[],"sort":[1651599812857,167],"type":"config","updated_at":"2022-05-03T17:43:32.857Z","version":"WzgyLDFd"} +{"attributes":{"buildNum":9007199254740991,"defaultIndex":"56b34100-619d-11eb-aebf-c306684b328d"},"coreMigrationVersion":"7.13.5","id":"7.13.5","migrationVersion":{"config":"7.13.0"},"references":[],"sort":[1651613092912,1086],"type":"config","updated_at":"2022-05-03T21:24:52.912Z","version":"WzYzOCwxXQ=="} +{"attributes":{"columns":["_source"],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"highlightAll\":true,\"version\":true,\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"negate\":false,\"type\":\"phrase\",\"key\":\"text_entry\",\"value\":\"Christendom.\",\"params\":{\"query\":\"Christendom.\",\"type\":\"phrase\"},\"disabled\":false,\"alias\":null,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"text_entry\":{\"query\":\"Christendom.\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["_score","desc"]],"title":"shakespeare_saved_search","version":1},"coreMigrationVersion":"7.13.5","id":"712ebbe0-619d-11eb-aebf-c306684b328d","migrationVersion":{"search":"7.9.3"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"}],"sort":[1651599812857,170],"type":"search","updated_at":"2022-05-03T17:43:32.857Z","version":"WzgzLDFd"} +{"attributes":{"columns":["play_name","speaker"],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"highlightAll\":true,\"version\":true,\"query\":{\"query\":\"speaker:\\\"GLOUCESTER\\\"\",\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["_score","desc"]],"title":"shakespeare_saved_lucene_search","version":1},"coreMigrationVersion":"7.13.5","id":"ddacc820-619d-11eb-aebf-c306684b328d","migrationVersion":{"search":"7.9.3"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,172],"type":"search","updated_at":"2022-05-03T17:43:32.857Z","version":"Wzg0LDFd"} +{"attributes":{"columns":["_source"],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"highlightAll\":true,\"version\":true,\"query\":{\"query\":\"text_entry :\\\"MORDAKE THE EARL OF FIFE, AND ELDEST SON\\\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["_score","desc"]],"title":"shakespeare_saved_kql_search","version":1},"coreMigrationVersion":"7.13.5","id":"f852d570-619d-11eb-aebf-c306684b328d","migrationVersion":{"search":"7.9.3"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651599812857,174],"type":"search","updated_at":"2022-05-03T17:43:32.857Z","version":"Wzg1LDFd"} +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"1\",\"w\":24,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"2\",\"w\":24,\"x\":24,\"y\":0},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"3\",\"w\":24,\"x\":0,\"y\":15},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":15,\"i\":\"4\",\"w\":24,\"x\":24,\"y\":15},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":15,\"i\":\"5\",\"w\":24,\"x\":0,\"y\":30},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":15,\"i\":\"6\",\"w\":24,\"x\":24,\"y\":30},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"}]","timeRestore":false,"title":"shakespeare_dashboard","version":1},"coreMigrationVersion":"7.13.5","id":"73398a90-619e-11eb-aebf-c306684b328d","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"185283c0-619e-11eb-aebf-c306684b328d","name":"1:panel_1","type":"visualization"},{"id":"33736660-619e-11eb-aebf-c306684b328d","name":"2:panel_2","type":"visualization"},{"id":"622ac7f0-619e-11eb-aebf-c306684b328d","name":"3:panel_3","type":"visualization"},{"id":"712ebbe0-619d-11eb-aebf-c306684b328d","name":"4:panel_4","type":"search"},{"id":"ddacc820-619d-11eb-aebf-c306684b328d","name":"5:panel_5","type":"search"},{"id":"f852d570-619d-11eb-aebf-c306684b328d","name":"6:panel_6","type":"search"}],"sort":[1651599812857,181],"type":"dashboard","updated_at":"2022-05-03T17:43:32.857Z","version":"Wzg2LDFd"} +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"geo.srcdest\",\"value\":\"IN:US\",\"params\":{\"query\":\"IN:US\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"geo.srcdest\":{\"query\":\"IN:US\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]}"},"optionsJSON":"{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"1\",\"w\":24,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"2\",\"w\":24,\"x\":24,\"y\":0},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"3\",\"w\":24,\"x\":0,\"y\":15},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"4\",\"w\":24,\"x\":24,\"y\":15},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"5\",\"w\":24,\"x\":0,\"y\":30},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"6\",\"w\":24,\"x\":24,\"y\":30},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"7\",\"w\":24,\"x\":0,\"y\":45},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"8\",\"w\":24,\"x\":24,\"y\":45},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_8\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"9\",\"w\":24,\"x\":0,\"y\":60},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"10\",\"w\":24,\"x\":24,\"y\":60},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"11\",\"w\":24,\"x\":0,\"y\":75},\"panelIndex\":\"11\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_11\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"12\",\"w\":24,\"x\":24,\"y\":75},\"panelIndex\":\"12\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_12\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"13\",\"w\":24,\"x\":0,\"y\":90},\"panelIndex\":\"13\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"14\",\"w\":24,\"x\":24,\"y\":90},\"panelIndex\":\"14\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_14\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"15\",\"w\":24,\"x\":0,\"y\":105},\"panelIndex\":\"15\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_15\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"16\",\"w\":24,\"x\":24,\"y\":105},\"panelIndex\":\"16\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_16\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"17\",\"w\":24,\"x\":0,\"y\":120},\"panelIndex\":\"17\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_17\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"18\",\"w\":24,\"x\":24,\"y\":120},\"panelIndex\":\"18\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_18\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"19\",\"w\":24,\"x\":0,\"y\":135},\"panelIndex\":\"19\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_19\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":15,\"i\":\"20\",\"w\":24,\"x\":24,\"y\":135},\"panelIndex\":\"20\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_20\"}]","timeRestore":false,"title":"logstash_dashboardwithfilters","version":1},"coreMigrationVersion":"7.13.5","id":"79794f20-6249-11eb-aebf-c306684b328d","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"36b91810-6239-11eb-aebf-c306684b328d","name":"1:panel_1","type":"visualization"},{"id":"0a274320-61cc-11eb-aebf-c306684b328d","name":"2:panel_2","type":"visualization"},{"id":"e4aef350-623d-11eb-aebf-c306684b328d","name":"3:panel_3","type":"visualization"},{"id":"f92e5630-623e-11eb-aebf-c306684b328d","name":"4:panel_4","type":"visualization"},{"id":"9853d4d0-623d-11eb-aebf-c306684b328d","name":"5:panel_5","type":"visualization"},{"id":"6ecb33b0-623d-11eb-aebf-c306684b328d","name":"6:panel_6","type":"visualization"},{"id":"b8e35c80-623c-11eb-aebf-c306684b328d","name":"7:panel_7","type":"visualization"},{"id":"f1bc75d0-6239-11eb-aebf-c306684b328d","name":"8:panel_8","type":"visualization"},{"id":"0d8a8860-623a-11eb-aebf-c306684b328d","name":"9:panel_9","type":"visualization"},{"id":"d79fe3d0-6239-11eb-aebf-c306684b328d","name":"10:panel_10","type":"visualization"},{"id":"318375a0-6240-11eb-aebf-c306684b328d","name":"11:panel_11","type":"visualization"},{"id":"e461eb20-6245-11eb-aebf-c306684b328d","name":"12:panel_12","type":"visualization"},{"id":"25bdc750-6242-11eb-aebf-c306684b328d","name":"13:panel_13","type":"visualization"},{"id":"71dd7bc0-6248-11eb-aebf-c306684b328d","name":"14:panel_14","type":"visualization"},{"id":"6aea48a0-6240-11eb-aebf-c306684b328d","name":"15:panel_15","type":"visualization"},{"id":"32b681f0-6241-11eb-aebf-c306684b328d","name":"16:panel_16","type":"visualization"},{"id":"ccca99e0-6244-11eb-aebf-c306684b328d","name":"17:panel_17","type":"visualization"},{"id":"a4d7be80-6245-11eb-aebf-c306684b328d","name":"18:panel_18","type":"visualization"},{"id":"c94d8440-6248-11eb-aebf-c306684b328d","name":"19:panel_19","type":"visualization"},{"id":"db6226f0-61c0-11eb-aebf-c306684b328d","name":"20:panel_20","type":"search"}],"sort":[1651599812857,203],"type":"dashboard","updated_at":"2022-05-03T17:43:32.857Z","version":"Wzg3LDFd"} +{"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"037b7937-790b-4d2d-94a5-7f5837a6ef05":{"columnOrder":["b3d46616-75e0-419e-97ea-91148961ef94","025a0fb3-dc44-4f5c-b517-2d71d3f26f14","c476db14-0cc1-40ec-863e-d2779256a407"],"columns":{"025a0fb3-dc44-4f5c-b517-2d71d3f26f14":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"},"b3d46616-75e0-419e-97ea-91148961ef94":{"dataType":"string","isBucketed":true,"label":"Top values of geo.srcdest","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"c476db14-0cc1-40ec-863e-d2779256a407","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"geo.srcdest"},"c476db14-0cc1-40ec-863e-d2779256a407":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"lucene","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["c476db14-0cc1-40ec-863e-d2779256a407"],"layerId":"037b7937-790b-4d2d-94a5-7f5837a6ef05","position":"top","seriesType":"bar_stacked","showGridlines":false,"splitAccessor":"b3d46616-75e0-419e-97ea-91148961ef94","xAccessor":"025a0fb3-dc44-4f5c-b517-2d71d3f26f14"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_verticalstacked","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.5","id":"8dc19b50-be32-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-037b7937-790b-4d2d-94a5-7f5837a6ef05","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,207],"type":"lens","updated_at":"2022-05-03T17:43:32.857Z","version":"Wzg4LDFd"} +{"attributes":{"columns":[],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[{\"meta\":{\"alias\":null,\"negate\":false,\"disabled\":false,\"type\":\"phrase\",\"key\":\"geo.dest\",\"params\":{\"query\":\"US\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match_phrase\":{\"geo.dest\":\"US\"}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["@timestamp","desc"]],"title":"drilldown_logstash","version":1},"coreMigrationVersion":"7.13.5","id":"b3288100-ca2c-11eb-bf5e-3de94e83d4f0","migrationVersion":{"search":"7.9.3"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"}],"sort":[1651612907652,1002],"type":"search","updated_at":"2022-05-03T21:21:47.652Z","version":"WzU3NiwxXQ=="} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{}"},"title":"logstash_timelion_panel","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_timelion_panel\",\"type\":\"timelion\",\"aggs\":[],\"params\":{\"expression\":\".es(index=logstash-*, \\\"sum:bytes\\\")\",\"interval\":\"auto\"}}"},"coreMigrationVersion":"7.13.5","id":"b3a44cd0-be5c-11eb-9520-1b4c3ca6a781","migrationVersion":{"visualization":"7.13.1"},"references":[],"sort":[1651599812857,208],"type":"visualization","updated_at":"2022-05-03T17:43:32.857Z","version":"WzkwLDFd"} +{"attributes":{"color":"#9170B8","description":"","name":"alltogether"},"coreMigrationVersion":"7.13.5","id":"be808cb0-be32-11eb-9520-1b4c3ca6a781","references":[],"sort":[1651599812857,209],"type":"tag","updated_at":"2022-05-03T17:43:32.857Z","version":"WzkxLDFd"} +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"4d9e9a01-cdb8-4aef-afcb-50db52247bb1\"},\"panelIndex\":\"4d9e9a01-cdb8-4aef-afcb-50db52247bb1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4d9e9a01-cdb8-4aef-afcb-50db52247bb1\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"d9cab9c8-667e-4d34-821b-cbb070891956\"},\"panelIndex\":\"d9cab9c8-667e-4d34-821b-cbb070891956\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_d9cab9c8-667e-4d34-821b-cbb070891956\"}]","refreshInterval":{"pause":true,"value":0},"timeFrom":"2015-09-20T01:56:56.132Z","timeRestore":true,"timeTo":"2015-09-21T11:18:20.471Z","title":"lens_combined_dashboard","version":1},"coreMigrationVersion":"7.13.5","id":"bfb3dc90-be32-11eb-9520-1b4c3ca6a781","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"8dc19b50-be32-11eb-9520-1b4c3ca6a781","name":"4d9e9a01-cdb8-4aef-afcb-50db52247bb1:panel_4d9e9a01-cdb8-4aef-afcb-50db52247bb1","type":"lens"},{"id":"b5bd5050-be31-11eb-9520-1b4c3ca6a781","name":"d9cab9c8-667e-4d34-821b-cbb070891956:panel_d9cab9c8-667e-4d34-821b-cbb070891956","type":"lens"},{"id":"be808cb0-be32-11eb-9520-1b4c3ca6a781","name":"tag-be808cb0-be32-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,213],"type":"dashboard","updated_at":"2022-05-03T17:43:32.857Z","version":"WzkyLDFd"} +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"2e80716f-c1b6-46f2-be2b-35db744b5031\"},\"panelIndex\":\"2e80716f-c1b6-46f2-be2b-35db744b5031\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsPie\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"26e2cf99-d931-4320-9e15-9dbc148f3534\":{\"columns\":{\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\":{\"label\":\"Top values of url.raw\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"url.raw\",\"isBucketed\":true,\"params\":{\"size\":20,\"orderBy\":{\"type\":\"column\",\"columnId\":\"beb72af1-239c-46d8-823b-b00d1e2ace43\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"beb72af1-239c-46d8-823b-b00d1e2ace43\":{\"label\":\"Unique count of geo.srcdest\",\"dataType\":\"number\",\"operationType\":\"unique_count\",\"scale\":\"ratio\",\"sourceField\":\"geo.srcdest\",\"isBucketed\":false}},\"columnOrder\":[\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"beb72af1-239c-46d8-823b-b00d1e2ace43\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"shape\":\"donut\",\"layers\":[{\"layerId\":\"26e2cf99-d931-4320-9e15-9dbc148f3534\",\"groups\":[\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\"],\"metric\":\"beb72af1-239c-46d8-823b-b00d1e2ace43\",\"numberDisplay\":\"percent\",\"categoryDisplay\":\"default\",\"legendDisplay\":\"default\",\"nestedLegend\":false}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-layer-26e2cf99-d931-4320-9e15-9dbc148f3534\"}]},\"enhancements\":{},\"type\":\"lens\"},\"panelRefName\":\"panel_2e80716f-c1b6-46f2-be2b-35db744b5031\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"da8843e0-6789-4aae-bcd0-81f270538719\"},\"panelIndex\":\"da8843e0-6789-4aae-bcd0-81f270538719\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_da8843e0-6789-4aae-bcd0-81f270538719\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":15,\"w\":24,\"h\":15,\"i\":\"adcd4418-7299-4efa-b369-5f71a7b4ebe0\"},\"panelIndex\":\"adcd4418-7299-4efa-b369-5f71a7b4ebe0\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_adcd4418-7299-4efa-b369-5f71a7b4ebe0\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"869754a7-edf0-478f-a7f1-80374f63108a\"},\"panelIndex\":\"869754a7-edf0-478f-a7f1-80374f63108a\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_869754a7-edf0-478f-a7f1-80374f63108a\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":30,\"w\":24,\"h\":15,\"i\":\"67111cf4-338e-453f-8621-e8dea64082d1\"},\"panelIndex\":\"67111cf4-338e-453f-8621-e8dea64082d1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_67111cf4-338e-453f-8621-e8dea64082d1\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":30,\"w\":24,\"h\":15,\"i\":\"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\"},\"panelIndex\":\"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":45,\"w\":24,\"h\":15,\"i\":\"88847944-ae1b-45fd-b102-3b45f9bea04b\"},\"panelIndex\":\"88847944-ae1b-45fd-b102-3b45f9bea04b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_88847944-ae1b-45fd-b102-3b45f9bea04b\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":45,\"w\":24,\"h\":15,\"i\":\"5a7924c7-eac0-4573-9199-fecec5b82e9e\"},\"panelIndex\":\"5a7924c7-eac0-4573-9199-fecec5b82e9e\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5a7924c7-eac0-4573-9199-fecec5b82e9e\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":60,\"w\":24,\"h\":15,\"i\":\"f8f49591-f071-4a96-b1ed-cd65daff5648\"},\"panelIndex\":\"f8f49591-f071-4a96-b1ed-cd65daff5648\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_f8f49591-f071-4a96-b1ed-cd65daff5648\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":60,\"w\":24,\"h\":15,\"i\":\"9f357f47-c2a0-421f-a456-9583c40837ab\"},\"panelIndex\":\"9f357f47-c2a0-421f-a456-9583c40837ab\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9f357f47-c2a0-421f-a456-9583c40837ab\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":75,\"w\":24,\"h\":15,\"i\":\"6cb383e9-1e80-44f9-80d5-7b8c585668db\"},\"panelIndex\":\"6cb383e9-1e80-44f9-80d5-7b8c585668db\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6cb383e9-1e80-44f9-80d5-7b8c585668db\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":75,\"w\":24,\"h\":15,\"i\":\"57f5f0bf-6610-4599-aad4-37484640b5e2\"},\"panelIndex\":\"57f5f0bf-6610-4599-aad4-37484640b5e2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_57f5f0bf-6610-4599-aad4-37484640b5e2\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":90,\"w\":24,\"h\":15,\"i\":\"32d3ab66-52e1-44e3-8c1f-1dccff3c5692\"},\"panelIndex\":\"32d3ab66-52e1-44e3-8c1f-1dccff3c5692\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_32d3ab66-52e1-44e3-8c1f-1dccff3c5692\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":90,\"w\":24,\"h\":15,\"i\":\"dd1718fd-74ee-4032-851b-db97e893825d\"},\"panelIndex\":\"dd1718fd-74ee-4032-851b-db97e893825d\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_dd1718fd-74ee-4032-851b-db97e893825d\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":105,\"w\":24,\"h\":15,\"i\":\"98a556ee-078b-4e03-93a8-29996133cdcb\"},\"panelIndex\":\"98a556ee-078b-4e03-93a8-29996133cdcb\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsXY\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"999a2d60-cb2a-451c-8d71-80d7e92e70fd\":{\"columns\":{\"ce9117a2-773c-474c-8fb1-18940cf58b38\":{\"label\":\"Top values of type\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"type\",\"isBucketed\":true,\"params\":{\"size\":5,\"orderBy\":{\"type\":\"column\",\"columnId\":\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\"},\"orderDirection\":\"asc\",\"otherBucket\":true,\"missingBucket\":false}},\"a3d10552-e352-40d0-a156-e86112c0501a\":{\"label\":\"Top values of _type\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"_type\",\"isBucketed\":true,\"params\":{\"size\":3,\"orderBy\":{\"type\":\"column\",\"columnId\":\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"Records\"},\"9c5db2f3-9eb0-4667-9a74-3318301de251\":{\"label\":\"Sum of bytes\",\"dataType\":\"number\",\"operationType\":\"sum\",\"sourceField\":\"bytes\",\"isBucketed\":false,\"scale\":\"ratio\"}},\"columnOrder\":[\"ce9117a2-773c-474c-8fb1-18940cf58b38\",\"a3d10552-e352-40d0-a156-e86112c0501a\",\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\",\"9c5db2f3-9eb0-4667-9a74-3318301de251\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"legend\":{\"isVisible\":true,\"position\":\"right\"},\"valueLabels\":\"hide\",\"fittingFunction\":\"None\",\"axisTitlesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"bar_stacked\",\"layers\":[{\"layerId\":\"999a2d60-cb2a-451c-8d71-80d7e92e70fd\",\"accessors\":[\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\",\"9c5db2f3-9eb0-4667-9a74-3318301de251\"],\"position\":\"top\",\"seriesType\":\"bar_stacked\",\"showGridlines\":false,\"xAccessor\":\"ce9117a2-773c-474c-8fb1-18940cf58b38\",\"splitAccessor\":\"a3d10552-e352-40d0-a156-e86112c0501a\"}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-layer-999a2d60-cb2a-451c-8d71-80d7e92e70fd\"}]},\"enhancements\":{},\"type\":\"lens\"}},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":105,\"w\":24,\"h\":15,\"i\":\"62a0f0b0-3589-4cef-807b-b1b4258b7a9b\"},\"panelIndex\":\"62a0f0b0-3589-4cef-807b-b1b4258b7a9b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_62a0f0b0-3589-4cef-807b-b1b4258b7a9b\"},{\"version\":\"7.13.1\",\"type\":\"map\",\"gridData\":{\"x\":0,\"y\":120,\"w\":24,\"h\":15,\"i\":\"dcc0defa-3376-465c-9b5b-2ba69528848c\"},\"panelIndex\":\"dcc0defa-3376-465c-9b5b-2ba69528848c\",\"embeddableConfig\":{\"mapCenter\":{\"lat\":19.94277,\"lon\":0,\"zoom\":1.56},\"mapBuffer\":{\"minLon\":-210.32666,\"minLat\":-64.8435,\"maxLon\":210.32666,\"maxLat\":95.13806},\"isLayerTOCOpen\":true,\"openTOCDetails\":[],\"hiddenLayers\":[],\"enhancements\":{}},\"panelRefName\":\"panel_dcc0defa-3376-465c-9b5b-2ba69528848c\"},{\"version\":\"7.13.1\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":120,\"w\":24,\"h\":15,\"i\":\"dd21a674-ae3a-40f6-9d68-4e01361ea5e2\"},\"panelIndex\":\"dd21a674-ae3a-40f6-9d68-4e01361ea5e2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_dd21a674-ae3a-40f6-9d68-4e01361ea5e2\"}]","refreshInterval":{"pause":true,"value":0},"timeFrom":"2015-09-20T01:56:56.132Z","timeRestore":true,"timeTo":"2015-09-21T11:18:20.471Z","title":"timelion_lens_maps_dashboard_logstash","version":1},"coreMigrationVersion":"7.13.5","id":"c4ab2030-be5c-11eb-9520-1b4c3ca6a781","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"21905950-bd9f-11eb-9520-1b4c3ca6a781","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:panel_2e80716f-c1b6-46f2-be2b-35db744b5031","type":"lens"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:indexpattern-datasource-layer-26e2cf99-d931-4320-9e15-9dbc148f3534","type":"index-pattern"},{"id":"aa4b8da0-bd9f-11eb-9520-1b4c3ca6a781","name":"da8843e0-6789-4aae-bcd0-81f270538719:panel_da8843e0-6789-4aae-bcd0-81f270538719","type":"lens"},{"id":"2d3f1250-bd9f-11eb-9520-1b4c3ca6a781","name":"adcd4418-7299-4efa-b369-5f71a7b4ebe0:panel_adcd4418-7299-4efa-b369-5f71a7b4ebe0","type":"lens"},{"id":"edd5a560-bda4-11eb-9520-1b4c3ca6a781","name":"869754a7-edf0-478f-a7f1-80374f63108a:panel_869754a7-edf0-478f-a7f1-80374f63108a","type":"lens"},{"id":"2c25a450-bda5-11eb-9520-1b4c3ca6a781","name":"67111cf4-338e-453f-8621-e8dea64082d1:panel_67111cf4-338e-453f-8621-e8dea64082d1","type":"lens"},{"id":"e79116e0-bd9e-11eb-9520-1b4c3ca6a781","name":"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d:panel_13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d","type":"lens"},{"id":"974fb950-bda5-11eb-9520-1b4c3ca6a781","name":"88847944-ae1b-45fd-b102-3b45f9bea04b:panel_88847944-ae1b-45fd-b102-3b45f9bea04b","type":"lens"},{"id":"21905950-bd9f-11eb-9520-1b4c3ca6a781","name":"5a7924c7-eac0-4573-9199-fecec5b82e9e:panel_5a7924c7-eac0-4573-9199-fecec5b82e9e","type":"lens"},{"id":"51b63040-bda5-11eb-9520-1b4c3ca6a781","name":"f8f49591-f071-4a96-b1ed-cd65daff5648:panel_f8f49591-f071-4a96-b1ed-cd65daff5648","type":"lens"},{"id":"b00679c0-bda5-11eb-9520-1b4c3ca6a781","name":"9f357f47-c2a0-421f-a456-9583c40837ab:panel_9f357f47-c2a0-421f-a456-9583c40837ab","type":"lens"},{"id":"652ade10-bd9f-11eb-9520-1b4c3ca6a781","name":"6cb383e9-1e80-44f9-80d5-7b8c585668db:panel_6cb383e9-1e80-44f9-80d5-7b8c585668db","type":"lens"},{"id":"7f3b5fb0-be2f-11eb-9520-1b4c3ca6a781","name":"57f5f0bf-6610-4599-aad4-37484640b5e2:panel_57f5f0bf-6610-4599-aad4-37484640b5e2","type":"lens"},{"id":"bb9e5bb0-be2f-11eb-9520-1b4c3ca6a781","name":"32d3ab66-52e1-44e3-8c1f-1dccff3c5692:panel_32d3ab66-52e1-44e3-8c1f-1dccff3c5692","type":"lens"},{"id":"dd315430-be2f-11eb-9520-1b4c3ca6a781","name":"dd1718fd-74ee-4032-851b-db97e893825d:panel_dd1718fd-74ee-4032-851b-db97e893825d","type":"lens"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"98a556ee-078b-4e03-93a8-29996133cdcb:indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"98a556ee-078b-4e03-93a8-29996133cdcb:indexpattern-datasource-layer-999a2d60-cb2a-451c-8d71-80d7e92e70fd","type":"index-pattern"},{"id":"0dbbf8b0-be3c-11eb-9520-1b4c3ca6a781","name":"62a0f0b0-3589-4cef-807b-b1b4258b7a9b:panel_62a0f0b0-3589-4cef-807b-b1b4258b7a9b","type":"lens"},{"id":"0c5974f0-be5c-11eb-9520-1b4c3ca6a781","name":"dcc0defa-3376-465c-9b5b-2ba69528848c:panel_dcc0defa-3376-465c-9b5b-2ba69528848c","type":"map"},{"id":"a4d7be80-6245-11eb-aebf-c306684b328d","name":"dd21a674-ae3a-40f6-9d68-4e01361ea5e2:panel_dd21a674-ae3a-40f6-9d68-4e01361ea5e2","type":"visualization"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,236],"type":"dashboard","updated_at":"2022-05-03T17:43:32.857Z","version":"WzkzLDFd"} +{"attributes":{"columns":[],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"speaker :\\\"DUKE VINCENTIO\\\" \",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[],"title":"drilldown_shakes","version":1},"coreMigrationVersion":"7.13.5","id":"c4b9cc00-ca2a-11eb-bf5e-3de94e83d4f0","migrationVersion":{"search":"7.9.3"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1651611630398,283],"type":"search","updated_at":"2022-05-03T21:00:30.398Z","version":"WzE1MCwxXQ=="} +{"attributes":{"@created":"2021-05-27T19:45:29.712Z","@timestamp":"2021-05-27T19:45:29.712Z","content":"{\"selectedNodes\":[{\"id\":\"element-56d2ba72-f227-4d04-9478-a1d6f0c7e601\",\"position\":{\"left\":20,\"top\":20,\"width\":500,\"height\":300,\"angle\":0,\"parent\":\"group-499b5982-25f4-4894-9540-1874a27d78e7\",\"type\":\"element\"},\"expression\":\"savedLens id=\\\"bb9e5bb0-be2f-11eb-9520-1b4c3ca6a781\\\" timerange={timerange from=\\\"now-15y\\\" to=\\\"now\\\"}\\n| render\",\"filter\":null,\"ast\":{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"savedLens\",\"arguments\":{\"id\":[\"bb9e5bb0-be2f-11eb-9520-1b4c3ca6a781\"],\"timerange\":[{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"timerange\",\"arguments\":{\"from\":[\"now-15y\"],\"to\":[\"now\"]}}]}]}},{\"type\":\"function\",\"function\":\"render\",\"arguments\":{}}]}},{\"id\":\"element-afbaa26e-10e7-47d4-bb41-b061dfdced2b\",\"position\":{\"left\":527,\"top\":20,\"width\":500,\"height\":300,\"angle\":0,\"parent\":\"group-499b5982-25f4-4894-9540-1874a27d78e7\",\"type\":\"element\"},\"expression\":\"savedVisualization id=\\\"0d8a8860-623a-11eb-aebf-c306684b328d\\\" timerange={timerange from=\\\"now-15y\\\" to=\\\"now\\\"}\\n| render\",\"filter\":null,\"ast\":{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"savedVisualization\",\"arguments\":{\"id\":[\"0d8a8860-623a-11eb-aebf-c306684b328d\"],\"timerange\":[{\"type\":\"expression\",\"chain\":[{\"type\":\"function\",\"function\":\"timerange\",\"arguments\":{\"from\":[\"now-15y\"],\"to\":[\"now\"]}}]}]}},{\"type\":\"function\",\"function\":\"render\",\"arguments\":{}}]}}]}","displayName":"element_canvas","help":"","image":"","name":"elementCanvas"},"coreMigrationVersion":"7.13.5","id":"custom-element-3bc52277-ee01-4cdc-8d2d-f2db6ade1512","references":[],"sort":[1651599812857,237],"type":"canvas-element","updated_at":"2022-05-03T17:43:32.857Z","version":"Wzk1LDFd"} +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.5\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"ced0a5ea-3ec2-4274-8431-6e76d85637f6\"},\"panelIndex\":\"ced0a5ea-3ec2-4274-8431-6e76d85637f6\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsXY\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"6a3b206a-4ac3-4d67-b2b9-c6b543a11ea3\":{\"columns\":{\"f70668f8-ae97-4b64-867f-b0c9b77914ef\":{\"label\":\"Top values of speaker\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"speaker\",\"isBucketed\":true,\"params\":{\"size\":39,\"orderBy\":{\"type\":\"column\",\"columnId\":\"fbf256d9-cae7-4244-8504-b73a5666e917\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"fbf256d9-cae7-4244-8504-b73a5666e917\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"Records\"}},\"columnOrder\":[\"f70668f8-ae97-4b64-867f-b0c9b77914ef\",\"fbf256d9-cae7-4244-8504-b73a5666e917\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"legend\":{\"isVisible\":true,\"position\":\"right\"},\"valueLabels\":\"hide\",\"fittingFunction\":\"None\",\"axisTitlesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"bar_horizontal_percentage_stacked\",\"layers\":[{\"layerId\":\"6a3b206a-4ac3-4d67-b2b9-c6b543a11ea3\",\"accessors\":[\"fbf256d9-cae7-4244-8504-b73a5666e917\"],\"position\":\"top\",\"seriesType\":\"bar_horizontal_percentage_stacked\",\"showGridlines\":false,\"splitAccessor\":\"f70668f8-ae97-4b64-867f-b0c9b77914ef\"}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"4e937b20-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"4e937b20-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-layer-6a3b206a-4ac3-4d67-b2b9-c6b543a11ea3\"}]},\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"2b9a2bad-d6aa-4d3b-a692-fd96c3fb0ac1\",\"triggers\":[\"FILTER_TRIGGER\"],\"action\":{\"name\":\"We_like_lens\",\"config\":{\"useCurrentFilters\":true,\"useCurrentDateRange\":true},\"factoryId\":\"DASHBOARD_TO_DASHBOARD_DRILLDOWN\"}}]}}}},{\"version\":\"7.13.5\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"e51c2a40-5849-47c9-9ae2-a6373d68154b\"},\"panelIndex\":\"e51c2a40-5849-47c9-9ae2-a6373d68154b\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"\",\"description\":\"\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100}},\"uiState\":{},\"data\":{\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"play_name\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":788,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"segment\"}],\"searchSource\":{\"index\":\"4e937b20-619d-11eb-aebf-c306684b328d\",\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}}},\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"3775f69f-ad03-476d-bbf8-99ab9628a55d\",\"triggers\":[\"VALUE_CLICK_TRIGGER\"],\"action\":{\"factoryId\":\"URL_DRILLDOWN\",\"name\":\"drilldown_timebased \",\"config\":{\"url\":{\"template\":\"http://localhost:5601/app/discover#/view/b3288100-ca2c-11eb-bf5e-3de94e83d4f0?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15y,to:now))&_a=(columns:!(),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'43fcac20-ca27-11eb-bf5e-3de94e83d4f0',key:geo.dest,negate:!f,params:(query:US),type:phrase),query:(match_phrase:(geo.dest:US)))),index:'43fcac20-ca27-11eb-bf5e-3de94e83d4f0',interval:auto,query:(language:kuery,query:''),sort:!(!('@timestamp',desc)))\"},\"openInNewTab\":true,\"encodeUrl\":true}}}]}}}}]","timeRestore":false,"title":"nontimebased_shakespeare_drilldown","version":1},"coreMigrationVersion":"7.13.5","id":"e9eb20f0-ca2a-11eb-bf5e-3de94e83d4f0","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"ced0a5ea-3ec2-4274-8431-6e76d85637f6:indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"ced0a5ea-3ec2-4274-8431-6e76d85637f6:indexpattern-datasource-layer-6a3b206a-4ac3-4d67-b2b9-c6b543a11ea3","type":"index-pattern"},{"id":"08dec860-ca29-11eb-bf5e-3de94e83d4f0","name":"ced0a5ea-3ec2-4274-8431-6e76d85637f6:drilldown:DASHBOARD_TO_DASHBOARD_DRILLDOWN:2b9a2bad-d6aa-4d3b-a692-fd96c3fb0ac1:dashboardId","type":"dashboard"},{"id":"4e937b20-619d-11eb-aebf-c306684b328d","name":"e51c2a40-5849-47c9-9ae2-a6373d68154b:kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"07f48f70-ca29-11eb-bf5e-3de94e83d4f0","name":"tag-07f48f70-ca29-11eb-bf5e-3de94e83d4f0","type":"tag"}],"sort":[1651612119207,862],"type":"dashboard","updated_at":"2022-05-03T21:08:39.207Z","version":"WzQ1MywxXQ=="} +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"2e80716f-c1b6-46f2-be2b-35db744b5031\"},\"panelIndex\":\"2e80716f-c1b6-46f2-be2b-35db744b5031\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsPie\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"26e2cf99-d931-4320-9e15-9dbc148f3534\":{\"columns\":{\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\":{\"label\":\"Top values of url.raw\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"url.raw\",\"isBucketed\":true,\"params\":{\"size\":20,\"orderBy\":{\"type\":\"column\",\"columnId\":\"beb72af1-239c-46d8-823b-b00d1e2ace43\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"beb72af1-239c-46d8-823b-b00d1e2ace43\":{\"label\":\"Unique count of geo.srcdest\",\"dataType\":\"number\",\"operationType\":\"unique_count\",\"scale\":\"ratio\",\"sourceField\":\"geo.srcdest\",\"isBucketed\":false}},\"columnOrder\":[\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"beb72af1-239c-46d8-823b-b00d1e2ace43\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"shape\":\"donut\",\"layers\":[{\"layerId\":\"26e2cf99-d931-4320-9e15-9dbc148f3534\",\"groups\":[\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\",\"6adde1a2-4c6f-47eb-95cc-5c6a9d863a6e\"],\"metric\":\"beb72af1-239c-46d8-823b-b00d1e2ace43\",\"numberDisplay\":\"percent\",\"categoryDisplay\":\"default\",\"legendDisplay\":\"default\",\"nestedLegend\":false}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-layer-26e2cf99-d931-4320-9e15-9dbc148f3534\"}]},\"enhancements\":{},\"type\":\"lens\"},\"panelRefName\":\"panel_2e80716f-c1b6-46f2-be2b-35db744b5031\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"da8843e0-6789-4aae-bcd0-81f270538719\"},\"panelIndex\":\"da8843e0-6789-4aae-bcd0-81f270538719\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_da8843e0-6789-4aae-bcd0-81f270538719\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":15,\"w\":24,\"h\":15,\"i\":\"adcd4418-7299-4efa-b369-5f71a7b4ebe0\"},\"panelIndex\":\"adcd4418-7299-4efa-b369-5f71a7b4ebe0\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_adcd4418-7299-4efa-b369-5f71a7b4ebe0\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"869754a7-edf0-478f-a7f1-80374f63108a\"},\"panelIndex\":\"869754a7-edf0-478f-a7f1-80374f63108a\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_869754a7-edf0-478f-a7f1-80374f63108a\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":30,\"w\":24,\"h\":15,\"i\":\"67111cf4-338e-453f-8621-e8dea64082d1\"},\"panelIndex\":\"67111cf4-338e-453f-8621-e8dea64082d1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_67111cf4-338e-453f-8621-e8dea64082d1\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":30,\"w\":24,\"h\":15,\"i\":\"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\"},\"panelIndex\":\"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":45,\"w\":24,\"h\":15,\"i\":\"88847944-ae1b-45fd-b102-3b45f9bea04b\"},\"panelIndex\":\"88847944-ae1b-45fd-b102-3b45f9bea04b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_88847944-ae1b-45fd-b102-3b45f9bea04b\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":45,\"w\":24,\"h\":15,\"i\":\"5a7924c7-eac0-4573-9199-fecec5b82e9e\"},\"panelIndex\":\"5a7924c7-eac0-4573-9199-fecec5b82e9e\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5a7924c7-eac0-4573-9199-fecec5b82e9e\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":60,\"w\":24,\"h\":15,\"i\":\"f8f49591-f071-4a96-b1ed-cd65daff5648\"},\"panelIndex\":\"f8f49591-f071-4a96-b1ed-cd65daff5648\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_f8f49591-f071-4a96-b1ed-cd65daff5648\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":60,\"w\":24,\"h\":15,\"i\":\"9f357f47-c2a0-421f-a456-9583c40837ab\"},\"panelIndex\":\"9f357f47-c2a0-421f-a456-9583c40837ab\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9f357f47-c2a0-421f-a456-9583c40837ab\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":75,\"w\":24,\"h\":15,\"i\":\"6cb383e9-1e80-44f9-80d5-7b8c585668db\"},\"panelIndex\":\"6cb383e9-1e80-44f9-80d5-7b8c585668db\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6cb383e9-1e80-44f9-80d5-7b8c585668db\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":75,\"w\":24,\"h\":15,\"i\":\"57f5f0bf-6610-4599-aad4-37484640b5e2\"},\"panelIndex\":\"57f5f0bf-6610-4599-aad4-37484640b5e2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_57f5f0bf-6610-4599-aad4-37484640b5e2\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":90,\"w\":24,\"h\":15,\"i\":\"32d3ab66-52e1-44e3-8c1f-1dccff3c5692\"},\"panelIndex\":\"32d3ab66-52e1-44e3-8c1f-1dccff3c5692\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_32d3ab66-52e1-44e3-8c1f-1dccff3c5692\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":90,\"w\":24,\"h\":15,\"i\":\"dd1718fd-74ee-4032-851b-db97e893825d\"},\"panelIndex\":\"dd1718fd-74ee-4032-851b-db97e893825d\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_dd1718fd-74ee-4032-851b-db97e893825d\"},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":105,\"w\":24,\"h\":15,\"i\":\"98a556ee-078b-4e03-93a8-29996133cdcb\"},\"panelIndex\":\"98a556ee-078b-4e03-93a8-29996133cdcb\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsXY\",\"state\":{\"datasourceStates\":{\"indexpattern\":{\"layers\":{\"999a2d60-cb2a-451c-8d71-80d7e92e70fd\":{\"columns\":{\"ce9117a2-773c-474c-8fb1-18940cf58b38\":{\"label\":\"Top values of type\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"type\",\"isBucketed\":true,\"params\":{\"size\":5,\"orderBy\":{\"type\":\"column\",\"columnId\":\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\"},\"orderDirection\":\"asc\",\"otherBucket\":true,\"missingBucket\":false}},\"a3d10552-e352-40d0-a156-e86112c0501a\":{\"label\":\"Top values of _type\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"_type\",\"isBucketed\":true,\"params\":{\"size\":3,\"orderBy\":{\"type\":\"column\",\"columnId\":\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"Records\"},\"9c5db2f3-9eb0-4667-9a74-3318301de251\":{\"label\":\"Sum of bytes\",\"dataType\":\"number\",\"operationType\":\"sum\",\"sourceField\":\"bytes\",\"isBucketed\":false,\"scale\":\"ratio\"}},\"columnOrder\":[\"ce9117a2-773c-474c-8fb1-18940cf58b38\",\"a3d10552-e352-40d0-a156-e86112c0501a\",\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\",\"9c5db2f3-9eb0-4667-9a74-3318301de251\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"legend\":{\"isVisible\":true,\"position\":\"right\"},\"valueLabels\":\"hide\",\"fittingFunction\":\"None\",\"axisTitlesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"bar_stacked\",\"layers\":[{\"layerId\":\"999a2d60-cb2a-451c-8d71-80d7e92e70fd\",\"accessors\":[\"cf07d1f1-d3fd-41f7-812c-d8587ec75959\",\"9c5db2f3-9eb0-4667-9a74-3318301de251\"],\"position\":\"top\",\"seriesType\":\"bar_stacked\",\"showGridlines\":false,\"xAccessor\":\"ce9117a2-773c-474c-8fb1-18940cf58b38\",\"splitAccessor\":\"a3d10552-e352-40d0-a156-e86112c0501a\"}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-current-indexpattern\"},{\"type\":\"index-pattern\",\"id\":\"56b34100-619d-11eb-aebf-c306684b328d\",\"name\":\"indexpattern-datasource-layer-999a2d60-cb2a-451c-8d71-80d7e92e70fd\"}]},\"enhancements\":{},\"type\":\"lens\"}},{\"version\":\"7.13.1\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":105,\"w\":24,\"h\":15,\"i\":\"62a0f0b0-3589-4cef-807b-b1b4258b7a9b\"},\"panelIndex\":\"62a0f0b0-3589-4cef-807b-b1b4258b7a9b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_62a0f0b0-3589-4cef-807b-b1b4258b7a9b\"}]","refreshInterval":{"pause":true,"value":0},"timeFrom":"2015-09-20T01:56:56.132Z","timeRestore":true,"timeTo":"2015-09-21T11:18:20.471Z","title":"lens_dashboard_logstash","version":1},"coreMigrationVersion":"7.13.5","id":"f458b9f0-bd9e-11eb-9520-1b4c3ca6a781","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"21905950-bd9f-11eb-9520-1b4c3ca6a781","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:panel_2e80716f-c1b6-46f2-be2b-35db744b5031","type":"lens"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"2e80716f-c1b6-46f2-be2b-35db744b5031:indexpattern-datasource-layer-26e2cf99-d931-4320-9e15-9dbc148f3534","type":"index-pattern"},{"id":"aa4b8da0-bd9f-11eb-9520-1b4c3ca6a781","name":"da8843e0-6789-4aae-bcd0-81f270538719:panel_da8843e0-6789-4aae-bcd0-81f270538719","type":"lens"},{"id":"2d3f1250-bd9f-11eb-9520-1b4c3ca6a781","name":"adcd4418-7299-4efa-b369-5f71a7b4ebe0:panel_adcd4418-7299-4efa-b369-5f71a7b4ebe0","type":"lens"},{"id":"edd5a560-bda4-11eb-9520-1b4c3ca6a781","name":"869754a7-edf0-478f-a7f1-80374f63108a:panel_869754a7-edf0-478f-a7f1-80374f63108a","type":"lens"},{"id":"2c25a450-bda5-11eb-9520-1b4c3ca6a781","name":"67111cf4-338e-453f-8621-e8dea64082d1:panel_67111cf4-338e-453f-8621-e8dea64082d1","type":"lens"},{"id":"e79116e0-bd9e-11eb-9520-1b4c3ca6a781","name":"13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d:panel_13f21ad2-9b2d-4aa2-a908-b62e1bdedc1d","type":"lens"},{"id":"974fb950-bda5-11eb-9520-1b4c3ca6a781","name":"88847944-ae1b-45fd-b102-3b45f9bea04b:panel_88847944-ae1b-45fd-b102-3b45f9bea04b","type":"lens"},{"id":"21905950-bd9f-11eb-9520-1b4c3ca6a781","name":"5a7924c7-eac0-4573-9199-fecec5b82e9e:panel_5a7924c7-eac0-4573-9199-fecec5b82e9e","type":"lens"},{"id":"51b63040-bda5-11eb-9520-1b4c3ca6a781","name":"f8f49591-f071-4a96-b1ed-cd65daff5648:panel_f8f49591-f071-4a96-b1ed-cd65daff5648","type":"lens"},{"id":"b00679c0-bda5-11eb-9520-1b4c3ca6a781","name":"9f357f47-c2a0-421f-a456-9583c40837ab:panel_9f357f47-c2a0-421f-a456-9583c40837ab","type":"lens"},{"id":"652ade10-bd9f-11eb-9520-1b4c3ca6a781","name":"6cb383e9-1e80-44f9-80d5-7b8c585668db:panel_6cb383e9-1e80-44f9-80d5-7b8c585668db","type":"lens"},{"id":"7f3b5fb0-be2f-11eb-9520-1b4c3ca6a781","name":"57f5f0bf-6610-4599-aad4-37484640b5e2:panel_57f5f0bf-6610-4599-aad4-37484640b5e2","type":"lens"},{"id":"bb9e5bb0-be2f-11eb-9520-1b4c3ca6a781","name":"32d3ab66-52e1-44e3-8c1f-1dccff3c5692:panel_32d3ab66-52e1-44e3-8c1f-1dccff3c5692","type":"lens"},{"id":"dd315430-be2f-11eb-9520-1b4c3ca6a781","name":"dd1718fd-74ee-4032-851b-db97e893825d:panel_dd1718fd-74ee-4032-851b-db97e893825d","type":"lens"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"98a556ee-078b-4e03-93a8-29996133cdcb:indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"98a556ee-078b-4e03-93a8-29996133cdcb:indexpattern-datasource-layer-999a2d60-cb2a-451c-8d71-80d7e92e70fd","type":"index-pattern"},{"id":"0dbbf8b0-be3c-11eb-9520-1b4c3ca6a781","name":"62a0f0b0-3589-4cef-807b-b1b4258b7a9b:panel_62a0f0b0-3589-4cef-807b-b1b4258b7a9b","type":"lens"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1651599812857,258],"type":"dashboard","updated_at":"2022-05-03T17:43:32.857Z","version":"Wzk3LDFd"} +{"attributes":{"description":"this is a logstash saved query","filters":[],"query":{"language":"kuery","query":"extension.raw :\"gif\" and machine.os.raw :\"ios\" "},"timefilter":{"from":"2015-09-20T01:56:56.132Z","refreshInterval":{"pause":true,"value":0},"to":"2015-09-21T11:18:20.471Z"},"title":"logstash_saved_query"},"coreMigrationVersion":"7.13.5","id":"logstash_saved_query","references":[],"sort":[1651599812857,259],"type":"query","updated_at":"2022-05-03T17:43:32.857Z","version":"Wzk5LDFd"} +{"attributes":{"description":"Shakespeare query","filters":[],"query":{"language":"kuery","query":"speaker : \"OTHELLO\" and play_name :\"Othello\" "},"title":"shakespeare_current_query"},"coreMigrationVersion":"7.13.5","id":"shakespeare_current_query","references":[],"sort":[1651599812857,260],"type":"query","updated_at":"2022-05-03T17:43:32.857Z","version":"WzEwMSwxXQ=="} +{"attributes":{"@created":"2021-05-27T18:53:18.432Z","@timestamp":"2021-05-27T19:46:12.539Z","assets":{},"colors":["#37988d","#c19628","#b83c6f","#3f9939","#1785b0","#ca5f35","#45bdb0","#f2bc33","#e74b8b","#4fbf48","#1ea6dc","#fd7643","#72cec3","#f5cc5d","#ec77a8","#7acf74","#4cbce4","#fd986f","#a1ded7","#f8dd91","#f2a4c5","#a6dfa2","#86d2ed","#fdba9f","#000000","#444444","#777777","#BBBBBB","#FFFFFF","rgba(255,255,255,0)"],"css":".canvasPage {\n\n}","height":720,"isWriteable":true,"name":"logstash-canvas-workpad","page":1,"pages":[{"elements":[{"expression":"savedLens id=\"bb9e5bb0-be2f-11eb-9520-1b4c3ca6a781\" timerange={timerange from=\"now-15y\" to=\"now\"}\n| render","filter":null,"id":"element-56d2ba72-f227-4d04-9478-a1d6f0c7e601","position":{"angle":0,"height":300,"left":20,"parent":null,"top":20,"width":500}},{"expression":"savedVisualization id=\"0d8a8860-623a-11eb-aebf-c306684b328d\" timerange={timerange from=\"now-15y\" to=\"now\"}\n| render","filter":null,"id":"element-afbaa26e-10e7-47d4-bb41-b061dfdced2b","position":{"angle":0,"height":300,"left":527,"parent":null,"top":20,"width":500}}],"groups":[],"id":"page-0f9ef2da-2868-4c0b-9223-fd3c9e53d6c9","style":{"background":"#FFF"},"transition":{}},{"elements":[{"expression":"image dataurl=null mode=\"contain\"\n| render","id":"element-c5534ef7-68c4-46bc-b35a-9e43a7f118c3","position":{"angle":0,"height":107,"left":20,"parent":null,"top":20,"width":132}},{"expression":"filters\n| essql query=\"SELECT machine.os.raw FROM \\\"logstash-*\\\"\"\n| pointseries x=\"machine.os.raw\" y=\"size(machine.os.raw)\" color=\"machine.os.raw\" size=\"sum(machine.os.raw)\"\n| plot defaultStyle={seriesStyle points=5 fill=1}\n| render","id":"element-5f7a3312-0e77-471c-9b8f-f98cb38075fb","position":{"angle":0,"height":192,"left":221,"parent":null,"top":56,"width":451}},{"expression":"timefilterControl compact=true column=@timestamp\n| render","filter":"timefilter from=\"now-29y\" to=now column=@timestamp","id":"element-6e00dcf4-06fe-4bd9-9315-d32d9d3fac5f","position":{"angle":0,"height":50,"left":221,"parent":null,"top":-1,"width":500}},{"expression":"filters\n| esdocs index=\"logstash-*\" fields=\"@timestamp, response.raw\"\n| pointseries x=\"size(response.raw)\" y=\"@timestamp\" color=\"response.raw\"\n| plot\n| render","id":"element-20281fac-1c3a-4ee3-9132-44379fb60b74","position":{"angle":0,"height":262,"left":51,"parent":null,"top":304,"width":590}},{"expression":"filters\n| timelion query=\".es(index=logstash-*, metric=sum:bytes)\"\n| pointseries x=\"@timestamp\" y=\"sum(value)\"\n| plot defaultStyle={seriesStyle lines=3}\n| render","id":"element-337b0548-5d6d-44cd-a324-eb50d63c7bd0","position":{"angle":0,"height":309,"left":648,"parent":null,"top":290,"width":369}},{"expression":"savedLens id=\"bb9e5bb0-be2f-11eb-9520-1b4c3ca6a781\" timerange={timerange from=\"now-15y\" to=\"now\"}\n| render","filter":null,"id":"element-353e5583-0dbb-4a6b-bac7-3b2a6b305397","position":{"angle":0,"height":181.99999999999997,"left":855,"parent":"group-d2618a19-3982-414e-93df-b2cb165b7c7e","top":15.000000000000014,"width":76.961271102284}},{"expression":"savedVisualization id=\"0d8a8860-623a-11eb-aebf-c306684b328d\" timerange={timerange from=\"now-15y\" to=\"now\"}\n| render","filter":null,"id":"element-0e5501a6-9e87-42bc-b539-1e697e62051b","position":{"angle":0,"height":181.99999999999997,"left":933.038728897716,"parent":"group-d2618a19-3982-414e-93df-b2cb165b7c7e","top":15.000000000000014,"width":76.961271102284}}],"groups":[],"id":"page-59c3cf09-1811-4324-995b-7336c1c11ab8","style":{"background":"#FFF"},"transition":{}}],"variables":[],"width":1080},"coreMigrationVersion":"7.13.5","id":"workpad-f2024ca3-e366-447a-b3af-7db4400646ef","migrationVersion":{"canvas-workpad":"7.0.0"},"references":[],"sort":[1651599812857,261],"type":"canvas-workpad","updated_at":"2022-05-03T17:43:32.857Z","version":"WzEwMiwxXQ=="} +{"exportedCount":82,"missingRefCount":0,"missingReferences":[]} \ No newline at end of file diff --git a/x-pack/test/functional/apps/saved_objects_management/import_saved_objects_between_versions.ts b/x-pack/test/functional/apps/saved_objects_management/import_saved_objects_between_versions.ts index 168a2fd8d8aec..8716b10150d79 100644 --- a/x-pack/test/functional/apps/saved_objects_management/import_saved_objects_between_versions.ts +++ b/x-pack/test/functional/apps/saved_objects_management/import_saved_objects_between_versions.ts @@ -56,7 +56,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.savedObjects.clickImportDone(); await PageObjects.savedObjects.waitTableIsLoaded(); const newObjectCount = await PageObjects.savedObjects.getExportCount(); - expect(newObjectCount - initialObjectCount).to.eql(86); + expect(newObjectCount - initialObjectCount).to.eql(82); // logstash by reference dashboard with drilldowns await PageObjects.common.navigateToApp('dashboard'); diff --git a/x-pack/test/functional/apps/saved_objects_management/index.ts b/x-pack/test/functional/apps/saved_objects_management/index.ts index 17cdae0707213..dc0dae5134f50 100644 --- a/x-pack/test/functional/apps/saved_objects_management/index.ts +++ b/x-pack/test/functional/apps/saved_objects_management/index.ts @@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function savedObjectsManagementApp({ loadTestFile }: FtrProviderContext) { describe('Saved objects management', function savedObjectsManagementAppTestSuite() { - this.tags(['ciGroup2', 'skipFirefox']); + this.tags('skipFirefox'); loadTestFile(require.resolve('./spaces_integration')); loadTestFile(require.resolve('./feature_controls/saved_objects_management_security')); diff --git a/x-pack/test/functional/apps/security/basic_license/index.ts b/x-pack/test/functional/apps/security/basic_license/index.ts index 771874f853de4..04e55abb9bac1 100644 --- a/x-pack/test/functional/apps/security/basic_license/index.ts +++ b/x-pack/test/functional/apps/security/basic_license/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security app - basic license', function () { - this.tags('ciGroup4'); - loadTestFile(require.resolve('./role_mappings')); }); } diff --git a/x-pack/test/functional/apps/security/config.ts b/x-pack/test/functional/apps/security/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/security/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/security/index.ts b/x-pack/test/functional/apps/security/index.ts index fc9caafbabb29..3260e61e67cbf 100644 --- a/x-pack/test/functional/apps/security/index.ts +++ b/x-pack/test/functional/apps/security/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security app', function () { - this.tags('ciGroup7'); - loadTestFile(require.resolve('./security')); loadTestFile(require.resolve('./doc_level_security_roles')); loadTestFile(require.resolve('./management')); diff --git a/x-pack/test/functional/apps/snapshot_restore/config.ts b/x-pack/test/functional/apps/snapshot_restore/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/snapshot_restore/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/snapshot_restore/index.ts b/x-pack/test/functional/apps/snapshot_restore/index.ts index 95fc0b80c2c91..0eefd3884cd31 100644 --- a/x-pack/test/functional/apps/snapshot_restore/index.ts +++ b/x-pack/test/functional/apps/snapshot_restore/index.ts @@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('Snapshots app', function () { - this.tags(['ciGroup4', 'skipCloud']); + this.tags('skipCloud'); loadTestFile(require.resolve('./home_page')); }); }; diff --git a/x-pack/test/functional/apps/spaces/config.ts b/x-pack/test/functional/apps/spaces/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/spaces/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/spaces/enter_space.ts b/x-pack/test/functional/apps/spaces/enter_space.ts index 1055aa43eac39..d58a5c0f19f39 100644 --- a/x-pack/test/functional/apps/spaces/enter_space.ts +++ b/x-pack/test/functional/apps/spaces/enter_space.ts @@ -14,11 +14,8 @@ export default function enterSpaceFunctonalTests({ const esArchiver = getService('esArchiver'); const PageObjects = getPageObjects(['security', 'spaceSelector']); - // FLAKY: https://github.com/elastic/kibana/issues/99879 - describe.skip('Enter Space', function () { - // FLAKY: https://github.com/elastic/kibana/issues/100570 - // These tests fail very intermittently in Firefox. Skip Firefox testing until resolved. - // this.tags('includeFirefox'); + describe('Enter Space', function () { + this.tags('includeFirefox'); before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/spaces/enter_space'); await PageObjects.security.forceLogout(); diff --git a/x-pack/test/functional/apps/spaces/index.ts b/x-pack/test/functional/apps/spaces/index.ts index 780d355e1f5c6..c951609d6a33f 100644 --- a/x-pack/test/functional/apps/spaces/index.ts +++ b/x-pack/test/functional/apps/spaces/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function spacesApp({ loadTestFile }: FtrProviderContext) { describe('Spaces app', function spacesAppTestSuite() { - this.tags('ciGroup9'); - loadTestFile(require.resolve('./copy_saved_objects')); loadTestFile(require.resolve('./feature_controls/spaces_security')); loadTestFile(require.resolve('./spaces_selection')); diff --git a/x-pack/test/functional/apps/spaces/spaces_selection.ts b/x-pack/test/functional/apps/spaces/spaces_selection.ts index dad2dee0d3c9c..5a8e852ffb11c 100644 --- a/x-pack/test/functional/apps/spaces/spaces_selection.ts +++ b/x-pack/test/functional/apps/spaces/spaces_selection.ts @@ -31,8 +31,7 @@ export default function spaceSelectorFunctionalTests({ ); this.tags('includeFirefox'); - // FLAKY: https://github.com/elastic/kibana/issues/99581 - describe.skip('Space Selector', () => { + describe('Space Selector', () => { before(async () => { await PageObjects.security.forceLogout(); }); @@ -63,9 +62,7 @@ export default function spaceSelectorFunctionalTests({ }); }); - // FLAKY: https://github.com/elastic/kibana/issues/118356 - // FLAKY: https://github.com/elastic/kibana/issues/118474 - describe.skip('Search spaces in popover', () => { + describe('Search spaces in popover', () => { const spaceId = 'default'; before(async () => { await PageObjects.security.forceLogout(); diff --git a/x-pack/test/functional/apps/status_page/config.ts b/x-pack/test/functional/apps/status_page/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/status_page/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/status_page/index.ts b/x-pack/test/functional/apps/status_page/index.ts index 69b18984f0ade..cdf6bb52ee605 100644 --- a/x-pack/test/functional/apps/status_page/index.ts +++ b/x-pack/test/functional/apps/status_page/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function statusPage({ loadTestFile }: FtrProviderContext) { describe('Status page', function statusPageTestSuite() { - this.tags('ciGroup4'); - loadTestFile(require.resolve('./status_page')); }); } diff --git a/x-pack/test/functional/apps/transform/config.ts b/x-pack/test/functional/apps/transform/config.ts new file mode 100644 index 0000000000000..17a471848867e --- /dev/null +++ b/x-pack/test/functional/apps/transform/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + junit: { + reportName: 'Chrome X-Pack UI Functional Tests - Transform', + }, + }; +} diff --git a/x-pack/test/functional/apps/transform/index.ts b/x-pack/test/functional/apps/transform/index.ts index 181325e83b36b..0c5227ae2f472 100644 --- a/x-pack/test/functional/apps/transform/index.ts +++ b/x-pack/test/functional/apps/transform/index.ts @@ -16,7 +16,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { const transform = getService('transform'); describe('transform', function () { - this.tags(['ciGroup21', 'transform']); + this.tags('transform'); before(async () => { await transform.securityCommon.createTransformRoles(); diff --git a/x-pack/test/functional/apps/upgrade_assistant/config.ts b/x-pack/test/functional/apps/upgrade_assistant/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/upgrade_assistant/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/upgrade_assistant/index.ts b/x-pack/test/functional/apps/upgrade_assistant/index.ts index d1ab46463e930..fcb9dd4ba7cca 100644 --- a/x-pack/test/functional/apps/upgrade_assistant/index.ts +++ b/x-pack/test/functional/apps/upgrade_assistant/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function upgradeCheckup({ loadTestFile }: FtrProviderContext) { describe('Upgrade Assistant', function upgradeAssistantTestSuite() { - this.tags('ciGroup4'); - loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./deprecation_pages')); loadTestFile(require.resolve('./overview_page')); diff --git a/x-pack/test/functional/apps/uptime/config.ts b/x-pack/test/functional/apps/uptime/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/uptime/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/uptime/feature_controls/uptime_security.ts b/x-pack/test/functional/apps/uptime/feature_controls/uptime_security.ts index 4d4acbe6242ba..b90337c3648b2 100644 --- a/x-pack/test/functional/apps/uptime/feature_controls/uptime_security.ts +++ b/x-pack/test/functional/apps/uptime/feature_controls/uptime_security.ts @@ -71,6 +71,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'Overview', 'Alerts', 'Uptime', + 'Synthetics', 'Stack Management', ]); }); @@ -123,7 +124,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows uptime navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Overview', 'Alerts', 'Uptime', 'Stack Management']); + expect(navLinks).to.eql(['Overview', 'Alerts', 'Uptime', 'Synthetics', 'Stack Management']); }); it('can navigate to Uptime app', async () => { diff --git a/x-pack/test/functional/apps/uptime/index.ts b/x-pack/test/functional/apps/uptime/index.ts index cc0578602951a..99359ee126502 100644 --- a/x-pack/test/functional/apps/uptime/index.ts +++ b/x-pack/test/functional/apps/uptime/index.ts @@ -42,8 +42,6 @@ export default ({ loadTestFile, getService }: FtrProviderContext) => { const uptime = getService('uptime'); describe('Uptime app', function () { - this.tags('ciGroup10'); - beforeEach('delete settings', async () => { await deleteUptimeSettingsObject(server); }); diff --git a/x-pack/test/functional/apps/visualize/config.ts b/x-pack/test/functional/apps/visualize/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/visualize/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts index 85b9a31e2d361..0bf6f6fad2a75 100644 --- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts +++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts @@ -138,10 +138,13 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('allows saving via the saved query management component popover with no saved query loaded', async () => { await queryBar.setQuery('response:200'); + await queryBar.clickQuerySubmitButton(); + await testSubjects.click('showQueryBarMenu'); await savedQueryManagementComponent.saveNewQuery('foo', 'bar', true, false); + await PageObjects.header.waitUntilLoadingHasFinished(); await savedQueryManagementComponent.savedQueryExistOrFail('foo'); await savedQueryManagementComponent.closeSavedQueryManagementComponent(); - + await testSubjects.click('showQueryBarMenu'); await savedQueryManagementComponent.deleteSavedQuery('foo'); await savedQueryManagementComponent.savedQueryMissingOrFail('foo'); }); @@ -170,13 +173,17 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('allow saving currently loaded query as a copy', async () => { await savedQueryManagementComponent.loadSavedQuery('OKJpgs'); + await queryBar.setQuery('response:404'); await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( 'ok2', 'description', true, false ); + await PageObjects.header.waitUntilLoadingHasFinished(); await savedQueryManagementComponent.savedQueryExistOrFail('ok2'); + await savedQueryManagementComponent.closeSavedQueryManagementComponent(); + await testSubjects.click('showQueryBarMenu'); await savedQueryManagementComponent.deleteSavedQuery('ok2'); }); }); diff --git a/x-pack/test/functional/apps/visualize/index.ts b/x-pack/test/functional/apps/visualize/index.ts index 105303aa1b537..c99182201eb5d 100644 --- a/x-pack/test/functional/apps/visualize/index.ts +++ b/x-pack/test/functional/apps/visualize/index.ts @@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function visualize({ loadTestFile }: FtrProviderContext) { describe('Visualize', function visualizeTestSuite() { - this.tags(['ciGroup30', 'skipFirefox']); + this.tags('skipFirefox'); loadTestFile(require.resolve('./feature_controls/visualize_security')); loadTestFile(require.resolve('./feature_controls/visualize_spaces')); diff --git a/x-pack/test/functional/apps/watcher/config.ts b/x-pack/test/functional/apps/watcher/config.ts new file mode 100644 index 0000000000000..d0d07ff200281 --- /dev/null +++ b/x-pack/test/functional/apps/watcher/config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/x-pack/test/functional/apps/watcher/index.js b/x-pack/test/functional/apps/watcher/index.js index fb39fe4aa7b29..fab662b26e80c 100644 --- a/x-pack/test/functional/apps/watcher/index.js +++ b/x-pack/test/functional/apps/watcher/index.js @@ -7,7 +7,7 @@ export default function ({ loadTestFile }) { describe('watcher app', function () { - this.tags(['ciGroup28', 'includeFirefox']); + this.tags('includeFirefox'); loadTestFile(require.resolve('./watcher_test')); }); diff --git a/x-pack/test/functional/config.base.js b/x-pack/test/functional/config.base.js new file mode 100644 index 0000000000000..a4d73d40e2d4d --- /dev/null +++ b/x-pack/test/functional/config.base.js @@ -0,0 +1,599 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { resolve } from 'path'; + +import { services } from './services'; +import { pageObjects } from './page_objects'; + +// Docker image to use for Fleet API integration tests. +// This hash comes from the latest successful build of the Snapshot Distribution of the Package Registry, for +// example: https://beats-ci.elastic.co/blue/organizations/jenkins/Ingest-manager%2Fpackage-storage/detail/snapshot/74/pipeline/257#step-302-log-1. +// It should be updated any time there is a new Docker image published for the Snapshot Distribution of the Package Registry. +export const dockerImage = + 'docker.elastic.co/package-registry/distribution:e1a3906e0c9944ecade05308022ba35eb0ebd00a'; + +// the default export of config files must be a config provider +// that returns an object with the projects config values +export default async function ({ readConfigFile }) { + const kibanaCommonConfig = await readConfigFile( + require.resolve('../../../test/common/config.js') + ); + const kibanaFunctionalConfig = await readConfigFile( + require.resolve('../../../test/functional/config.base.js') + ); + + return { + services, + pageObjects, + + servers: kibanaFunctionalConfig.get('servers'), + + esTestCluster: { + license: 'trial', + from: 'snapshot', + serverArgs: ['path.repo=/tmp/', 'xpack.security.authc.api_key.enabled=true'], + }, + + kbnTestServer: { + ...kibanaCommonConfig.get('kbnTestServer'), + serverArgs: [ + ...kibanaCommonConfig.get('kbnTestServer.serverArgs'), + '--status.allowAnonymous=true', + '--server.uuid=5b2de169-2785-441b-ae8c-186a1936b17d', + '--xpack.maps.showMapsInspectorAdapter=true', + '--xpack.maps.preserveDrawingBuffer=true', + '--xpack.security.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"', // server restarts should not invalidate active sessions + '--xpack.encryptedSavedObjects.encryptionKey="DkdXazszSCYexXqz4YktBGHCRkV6hyNK"', + '--xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled=true', + '--savedObjects.maxImportPayloadBytes=10485760', // for OSS test management/_import_objects, + '--uiSettings.overrides.observability:enableNewSyntheticsView=true', // for OSS test management/_import_objects, + ], + }, + uiSettings: { + defaults: { + 'accessibility:disableAnimations': true, + 'dateFormat:tz': 'UTC', + 'visualization:visualize:legacyPieChartsLibrary': true, + }, + }, + // the apps section defines the urls that + // `PageObjects.common.navigateTo(appKey)` will use. + // Merge urls for your plugin with the urls defined in + // Kibana's config in order to use this helper + apps: { + ...kibanaFunctionalConfig.get('apps'), + lens: { + pathname: '/app/lens', + }, + login: { + pathname: '/login', + }, + monitoring: { + pathname: '/app/monitoring', + }, + logstashPipelines: { + pathname: '/app/management/ingest/pipelines', + }, + cases: { + pathname: '/app/management/insightsAndAlerting/cases/', + }, + maps: { + pathname: '/app/maps', + }, + graph: { + pathname: '/app/graph', + }, + grokDebugger: { + pathname: '/app/dev_tools', + hash: '/grokdebugger', + }, + searchProfiler: { + pathname: '/app/dev_tools', + hash: '/searchprofiler', + }, + painlessLab: { + pathname: '/app/dev_tools', + hash: '/painless_lab', + }, + spaceSelector: { + pathname: '/', + }, + infraOps: { + pathname: '/app/metrics', + }, + infraLogs: { + pathname: '/app/logs', + }, + canvas: { + pathname: '/app/canvas', + hash: '/', + }, + uptime: { + pathname: '/app/uptime', + }, + fleet: { + pathname: '/app/fleet', + }, + ml: { + pathname: '/app/ml', + }, + roleMappings: { + pathname: '/app/management/security/role_mappings', + }, + rollupJob: { + pathname: '/app/management/data/rollup_jobs', + }, + apiKeys: { + pathname: '/app/management/security/api_keys', + }, + licenseManagement: { + pathname: '/app/management/stack/license_management', + }, + indexManagement: { + pathname: '/app/management/data/index_management', + }, + indexLifecycleManagement: { + pathname: '/app/management/data/index_lifecycle_management', + }, + ingestPipelines: { + pathname: '/app/management/ingest/ingest_pipelines', + }, + snapshotRestore: { + pathname: '/app/management/data/snapshot_restore', + }, + remoteClusters: { + pathname: '/app/management/data/remote_clusters', + }, + crossClusterReplication: { + pathname: '/app/management/data/cross_cluster_replication', + }, + apm: { + pathname: '/app/apm', + }, + watcher: { + pathname: '/app/management/insightsAndAlerting/watcher/watches', + }, + transform: { + pathname: '/app/management/data/transform', + }, + reporting: { + pathname: '/app/management/insightsAndAlerting/reporting', + }, + securitySolution: { + pathname: '/app/security', + }, + observability: { + pathname: '/app/observability', + }, + }, + + // choose where screenshots should be saved + screenshots: { + directory: resolve(__dirname, 'screenshots'), + }, + + junit: { + reportName: 'Chrome X-Pack UI Functional Tests', + }, + security: { + roles: { + test_monitoring: { + elasticsearch: { + cluster: ['monitor'], + }, + }, + test_logstash_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['logstash*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + global_canvas_all: { + kibana: [ + { + feature: { + canvas: ['all'], + visualize: ['all'], + }, + spaces: ['*'], + }, + ], + }, + + global_discover_all: { + kibana: [ + { + feature: { + discover: ['all'], + }, + spaces: ['*'], + }, + ], + }, + + global_dashboard_read: { + kibana: [ + { + feature: { + dashboard: ['read'], + }, + spaces: ['*'], + }, + ], + }, + + global_discover_read: { + kibana: [ + { + feature: { + discover: ['read'], + }, + spaces: ['*'], + }, + ], + }, + global_visualize_read: { + kibana: [ + { + feature: { + visualize: ['read'], + }, + spaces: ['*'], + }, + ], + }, + global_visualize_all: { + kibana: [ + { + feature: { + visualize: ['all'], + }, + spaces: ['*'], + }, + ], + }, + global_dashboard_all: { + kibana: [ + { + feature: { + dashboard: ['all'], + }, + spaces: ['*'], + }, + ], + }, + global_maps_all: { + kibana: [ + { + feature: { + maps: ['all'], + }, + spaces: ['*'], + }, + ], + }, + + global_maps_read: { + kibana: [ + { + feature: { + maps: ['read'], + }, + spaces: ['*'], + }, + ], + }, + + geoshape_data_reader: { + elasticsearch: { + indices: [ + { + names: ['geo_shapes*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + }, + }, + antimeridian_points_reader: { + elasticsearch: { + indices: [ + { + names: ['antimeridian_points*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + }, + }, + antimeridian_shapes_reader: { + elasticsearch: { + indices: [ + { + names: ['antimeridian_shapes*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + }, + }, + + meta_for_geoshape_data_reader: { + elasticsearch: { + indices: [ + { + names: ['meta_for_geo_shapes*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + }, + }, + + geoconnections_data_reader: { + elasticsearch: { + indices: [ + { + names: ['connections*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + }, + }, + + test_logs_data_reader: { + elasticsearch: { + indices: [ + { + names: ['test_data_stream'], + privileges: ['read', 'view_index_metadata'], + }, + ], + }, + }, + + geoall_data_writer: { + elasticsearch: { + indices: [ + { + names: ['*'], + privileges: ['create', 'read', 'view_index_metadata', 'monitor', 'create_index'], + }, + ], + }, + }, + + global_index_pattern_management_all: { + kibana: [ + { + feature: { + indexPatterns: ['all'], + }, + spaces: ['*'], + }, + ], + }, + + global_devtools_read: { + elasticsearch: { + indices: [ + { + names: ['*'], + privileges: ['read', 'all'], + }, + ], + }, + kibana: [ + { + feature: { + dev_tools: ['read'], + }, + spaces: ['*'], + }, + ], + }, + + global_upgrade_assistant_role: { + elasticsearch: { + cluster: ['manage'], + }, + kibana: [ + { + feature: { + discover: ['read'], + }, + spaces: ['*'], + }, + ], + }, + + // using this role even for remote clusters + global_ccr_role: { + elasticsearch: { + cluster: ['manage', 'manage_ccr'], + }, + kibana: [ + { + base: ['all'], + spaces: ['*'], + }, + ], + }, + manage_rollups_role: { + elasticsearch: { + cluster: ['manage', 'manage_rollup'], + indices: [ + { + names: ['*'], + privileges: ['read', 'delete', 'create_index', 'view_index_metadata'], + }, + ], + }, + kibana: [ + { + feature: { + discover: ['read'], + }, + spaces: ['*'], + }, + ], + }, + + test_rollup_reader: { + elasticsearch: { + indices: [ + { + names: ['rollup-*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + }, + }, + + // Kibana feature privilege isn't specific to advancedSetting. It can be anything. https://github.com/elastic/kibana/issues/35965 + test_api_keys: { + elasticsearch: { + cluster: ['manage_security', 'manage_api_key'], + }, + kibana: [ + { + feature: { + advancedSettings: ['read'], + }, + spaces: ['default'], + }, + ], + }, + + manage_security: { + elasticsearch: { + cluster: ['manage_security'], + }, + }, + + ccr_user: { + elasticsearch: { + cluster: ['manage', 'manage_ccr'], + }, + }, + + manage_ilm: { + elasticsearch: { + cluster: ['manage_ilm'], + }, + kibana: [ + { + feature: { + advancedSettings: ['read'], + }, + spaces: ['default'], + }, + ], + }, + + index_management_user: { + elasticsearch: { + cluster: ['monitor', 'manage_index_templates'], + indices: [ + { + names: ['*'], + privileges: ['all'], + }, + ], + }, + kibana: [ + { + feature: { + advancedSettings: ['read'], + }, + spaces: ['*'], + }, + ], + }, + // https://www.elastic.co/guide/en/elasticsearch/reference/master/snapshots-register-repository.html#snapshot-repo-prereqs + snapshot_restore_user: { + elasticsearch: { + cluster: [ + 'monitor', + 'manage_slm', + 'cluster:admin/snapshot', + 'cluster:admin/repository', + ], + }, + kibana: [ + { + feature: { + advancedSettings: ['read'], + }, + spaces: ['*'], + }, + ], + }, + + ingest_pipelines_user: { + elasticsearch: { + cluster: ['manage_pipeline', 'cluster:monitor/nodes/info'], + }, + kibana: [ + { + feature: { + advancedSettings: ['read'], + }, + spaces: ['*'], + }, + ], + }, + + license_management_user: { + elasticsearch: { + cluster: ['manage'], + }, + }, + + logstash_read_user: { + elasticsearch: { + cluster: ['manage_logstash_pipelines'], + }, + }, + + remote_clusters_user: { + elasticsearch: { + cluster: ['manage'], + }, + }, + + global_alerts_logs_all_else_read: { + kibana: [ + { + feature: { + apm: ['read'], + logs: ['all'], + infrastructure: ['read'], + }, + spaces: ['*'], + }, + ], + elasticsearch: { + indices: [ + { + names: ['*'], + privileges: ['all'], + }, + ], + }, + }, + }, + defaultRoles: ['superuser'], + }, + }; +} diff --git a/x-pack/test/functional/config.ccs.ts b/x-pack/test/functional/config.ccs.ts index e6e0da5293190..87bdb17e736bd 100644 --- a/x-pack/test/functional/config.ccs.ts +++ b/x-pack/test/functional/config.ccs.ts @@ -10,12 +10,12 @@ import { RemoteEsArchiverProvider } from './services/remote_es/remote_es_archive import { RemoteEsProvider } from './services/remote_es/remote_es'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const functionalConfig = await readConfigFile(require.resolve('./config')); + const functionalConfig = await readConfigFile(require.resolve('./config.base.js')); return { ...functionalConfig.getAll(), - testFiles: [require.resolve('./apps/lens')], + testFiles: [require.resolve('./apps/lens/group1')], junit: { reportName: 'X-Pack CCS Tests', diff --git a/x-pack/test/functional/config.coverage.js b/x-pack/test/functional/config.coverage.js deleted file mode 100644 index b403fe933c4a3..0000000000000 --- a/x-pack/test/functional/config.coverage.js +++ /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. - */ - -export default async function ({ readConfigFile }) { - const chromeConfig = await readConfigFile(require.resolve('./config')); - - return { - ...chromeConfig.getAll(), - - suiteTags: { - exclude: ['skipCoverage'], - }, - - junit: { - reportName: 'Code Coverage for Functional Tests', - }, - }; -} diff --git a/x-pack/test/functional/config.edge.js b/x-pack/test/functional/config.edge.js index b51b09d0f4878..58f35e844e283 100644 --- a/x-pack/test/functional/config.edge.js +++ b/x-pack/test/functional/config.edge.js @@ -6,10 +6,10 @@ */ export default async function ({ readConfigFile }) { - const chromeConfig = await readConfigFile(require.resolve('./config')); + const firefoxConfig = await readConfigFile(require.resolve('./config.firefox.js')); return { - ...chromeConfig.getAll(), + ...firefoxConfig.getAll(), browser: { type: 'msedge', diff --git a/x-pack/test/functional/config.firefox.js b/x-pack/test/functional/config.firefox.js index 148f3c454a859..91249df789de8 100644 --- a/x-pack/test/functional/config.firefox.js +++ b/x-pack/test/functional/config.firefox.js @@ -6,16 +6,26 @@ */ export default async function ({ readConfigFile }) { - const chromeConfig = await readConfigFile(require.resolve('./config')); + const chromeConfig = await readConfigFile(require.resolve('./config.base.js')); return { ...chromeConfig.getAll(), + testFiles: [ + require.resolve('./apps/canvas'), + require.resolve('./apps/infra'), + require.resolve('./apps/security'), + require.resolve('./apps/spaces'), + require.resolve('./apps/status_page'), + require.resolve('./apps/watcher'), + ], + browser: { type: 'firefox', }, suiteTags: { + include: ['includeFirefox'], exclude: ['skipFirefox'], }, diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js deleted file mode 100644 index 3d1f7d5e3dbd5..0000000000000 --- a/x-pack/test/functional/config.js +++ /dev/null @@ -1,639 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { resolve } from 'path'; - -import { services } from './services'; -import { pageObjects } from './page_objects'; - -// Docker image to use for Fleet API integration tests. -// This hash comes from the latest successful build of the Snapshot Distribution of the Package Registry, for -// example: https://beats-ci.elastic.co/blue/organizations/jenkins/Ingest-manager%2Fpackage-storage/detail/snapshot/74/pipeline/257#step-302-log-1. -// It should be updated any time there is a new Docker image published for the Snapshot Distribution of the Package Registry. -export const dockerImage = - 'docker.elastic.co/package-registry/distribution:e1a3906e0c9944ecade05308022ba35eb0ebd00a'; - -// the default export of config files must be a config provider -// that returns an object with the projects config values -export default async function ({ readConfigFile }) { - const kibanaCommonConfig = await readConfigFile( - require.resolve('../../../test/common/config.js') - ); - const kibanaFunctionalConfig = await readConfigFile( - require.resolve('../../../test/functional/config.js') - ); - - return { - // list paths to the files that contain your plugins tests - testFiles: [ - resolve(__dirname, './apps/advanced_settings'), - resolve(__dirname, './apps/canvas'), - resolve(__dirname, './apps/graph'), - resolve(__dirname, './apps/monitoring'), - resolve(__dirname, './apps/watcher'), - resolve(__dirname, './apps/dashboard'), - resolve(__dirname, './apps/discover'), - resolve(__dirname, './apps/security'), - resolve(__dirname, './apps/spaces'), - resolve(__dirname, './apps/logstash'), - resolve(__dirname, './apps/grok_debugger'), - resolve(__dirname, './apps/infra'), - resolve(__dirname, './apps/ml'), - resolve(__dirname, './apps/rollup_job'), - resolve(__dirname, './apps/maps'), - resolve(__dirname, './apps/status_page'), - resolve(__dirname, './apps/upgrade_assistant'), - resolve(__dirname, './apps/visualize'), - resolve(__dirname, './apps/uptime'), - resolve(__dirname, './apps/saved_objects_management'), - resolve(__dirname, './apps/dev_tools'), - resolve(__dirname, './apps/apm'), - resolve(__dirname, './apps/api_keys'), - resolve(__dirname, './apps/data_views'), - resolve(__dirname, './apps/index_management'), - resolve(__dirname, './apps/index_lifecycle_management'), - resolve(__dirname, './apps/ingest_pipelines'), - resolve(__dirname, './apps/snapshot_restore'), - resolve(__dirname, './apps/cross_cluster_replication'), - resolve(__dirname, './apps/remote_clusters'), - resolve(__dirname, './apps/transform'), - resolve(__dirname, './apps/reporting_management'), - resolve(__dirname, './apps/management'), - resolve(__dirname, './apps/lens'), // smokescreen tests cause flakiness in other tests - - // This license_management file must be last because it is destructive. - resolve(__dirname, './apps/license_management'), - ], - - services, - pageObjects, - - servers: kibanaFunctionalConfig.get('servers'), - - esTestCluster: { - license: 'trial', - from: 'snapshot', - serverArgs: ['path.repo=/tmp/', 'xpack.security.authc.api_key.enabled=true'], - }, - - kbnTestServer: { - ...kibanaCommonConfig.get('kbnTestServer'), - serverArgs: [ - ...kibanaCommonConfig.get('kbnTestServer.serverArgs'), - '--status.allowAnonymous=true', - '--server.uuid=5b2de169-2785-441b-ae8c-186a1936b17d', - '--xpack.maps.showMapsInspectorAdapter=true', - '--xpack.maps.preserveDrawingBuffer=true', - '--xpack.security.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"', // server restarts should not invalidate active sessions - '--xpack.encryptedSavedObjects.encryptionKey="DkdXazszSCYexXqz4YktBGHCRkV6hyNK"', - '--xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled=true', - '--savedObjects.maxImportPayloadBytes=10485760', // for OSS test management/_import_objects, - ], - }, - uiSettings: { - defaults: { - 'accessibility:disableAnimations': true, - 'dateFormat:tz': 'UTC', - 'visualization:visualize:legacyPieChartsLibrary': true, - }, - }, - // the apps section defines the urls that - // `PageObjects.common.navigateTo(appKey)` will use. - // Merge urls for your plugin with the urls defined in - // Kibana's config in order to use this helper - apps: { - ...kibanaFunctionalConfig.get('apps'), - lens: { - pathname: '/app/lens', - }, - login: { - pathname: '/login', - }, - monitoring: { - pathname: '/app/monitoring', - }, - logstashPipelines: { - pathname: '/app/management/ingest/pipelines', - }, - cases: { - pathname: '/app/management/insightsAndAlerting/cases/', - }, - maps: { - pathname: '/app/maps', - }, - graph: { - pathname: '/app/graph', - }, - grokDebugger: { - pathname: '/app/dev_tools', - hash: '/grokdebugger', - }, - searchProfiler: { - pathname: '/app/dev_tools', - hash: '/searchprofiler', - }, - painlessLab: { - pathname: '/app/dev_tools', - hash: '/painless_lab', - }, - spaceSelector: { - pathname: '/', - }, - infraOps: { - pathname: '/app/metrics', - }, - infraLogs: { - pathname: '/app/logs', - }, - canvas: { - pathname: '/app/canvas', - hash: '/', - }, - uptime: { - pathname: '/app/uptime', - }, - fleet: { - pathname: '/app/fleet', - }, - ml: { - pathname: '/app/ml', - }, - roleMappings: { - pathname: '/app/management/security/role_mappings', - }, - rollupJob: { - pathname: '/app/management/data/rollup_jobs', - }, - apiKeys: { - pathname: '/app/management/security/api_keys', - }, - licenseManagement: { - pathname: '/app/management/stack/license_management', - }, - indexManagement: { - pathname: '/app/management/data/index_management', - }, - indexLifecycleManagement: { - pathname: '/app/management/data/index_lifecycle_management', - }, - ingestPipelines: { - pathname: '/app/management/ingest/ingest_pipelines', - }, - snapshotRestore: { - pathname: '/app/management/data/snapshot_restore', - }, - remoteClusters: { - pathname: '/app/management/data/remote_clusters', - }, - crossClusterReplication: { - pathname: '/app/management/data/cross_cluster_replication', - }, - apm: { - pathname: '/app/apm', - }, - watcher: { - pathname: '/app/management/insightsAndAlerting/watcher/watches', - }, - transform: { - pathname: '/app/management/data/transform', - }, - reporting: { - pathname: '/app/management/insightsAndAlerting/reporting', - }, - securitySolution: { - pathname: '/app/security', - }, - observability: { - pathname: '/app/observability', - }, - }, - - // choose where screenshots should be saved - screenshots: { - directory: resolve(__dirname, 'screenshots'), - }, - - junit: { - reportName: 'Chrome X-Pack UI Functional Tests', - }, - security: { - roles: { - test_monitoring: { - elasticsearch: { - cluster: ['monitor'], - }, - }, - test_logstash_reader: { - elasticsearch: { - cluster: [], - indices: [ - { - names: ['logstash*'], - privileges: ['read', 'view_index_metadata'], - field_security: { grant: ['*'], except: [] }, - }, - ], - run_as: [], - }, - kibana: [], - }, - - global_canvas_all: { - kibana: [ - { - feature: { - canvas: ['all'], - visualize: ['all'], - }, - spaces: ['*'], - }, - ], - }, - - global_discover_all: { - kibana: [ - { - feature: { - discover: ['all'], - }, - spaces: ['*'], - }, - ], - }, - - global_dashboard_read: { - kibana: [ - { - feature: { - dashboard: ['read'], - }, - spaces: ['*'], - }, - ], - }, - - global_discover_read: { - kibana: [ - { - feature: { - discover: ['read'], - }, - spaces: ['*'], - }, - ], - }, - global_visualize_read: { - kibana: [ - { - feature: { - visualize: ['read'], - }, - spaces: ['*'], - }, - ], - }, - global_visualize_all: { - kibana: [ - { - feature: { - visualize: ['all'], - }, - spaces: ['*'], - }, - ], - }, - global_dashboard_all: { - kibana: [ - { - feature: { - dashboard: ['all'], - }, - spaces: ['*'], - }, - ], - }, - global_maps_all: { - kibana: [ - { - feature: { - maps: ['all'], - }, - spaces: ['*'], - }, - ], - }, - - global_maps_read: { - kibana: [ - { - feature: { - maps: ['read'], - }, - spaces: ['*'], - }, - ], - }, - - geoshape_data_reader: { - elasticsearch: { - indices: [ - { - names: ['geo_shapes*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - }, - }, - antimeridian_points_reader: { - elasticsearch: { - indices: [ - { - names: ['antimeridian_points*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - }, - }, - antimeridian_shapes_reader: { - elasticsearch: { - indices: [ - { - names: ['antimeridian_shapes*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - }, - }, - - meta_for_geoshape_data_reader: { - elasticsearch: { - indices: [ - { - names: ['meta_for_geo_shapes*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - }, - }, - - geoconnections_data_reader: { - elasticsearch: { - indices: [ - { - names: ['connections*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - }, - }, - - test_logs_data_reader: { - elasticsearch: { - indices: [ - { - names: ['test_data_stream'], - privileges: ['read', 'view_index_metadata'], - }, - ], - }, - }, - - geoall_data_writer: { - elasticsearch: { - indices: [ - { - names: ['*'], - privileges: ['create', 'read', 'view_index_metadata', 'monitor', 'create_index'], - }, - ], - }, - }, - - global_index_pattern_management_all: { - kibana: [ - { - feature: { - indexPatterns: ['all'], - }, - spaces: ['*'], - }, - ], - }, - - global_devtools_read: { - elasticsearch: { - indices: [ - { - names: ['*'], - privileges: ['read', 'all'], - }, - ], - }, - kibana: [ - { - feature: { - dev_tools: ['read'], - }, - spaces: ['*'], - }, - ], - }, - - global_upgrade_assistant_role: { - elasticsearch: { - cluster: ['manage'], - }, - kibana: [ - { - feature: { - discover: ['read'], - }, - spaces: ['*'], - }, - ], - }, - - // using this role even for remote clusters - global_ccr_role: { - elasticsearch: { - cluster: ['manage', 'manage_ccr'], - }, - kibana: [ - { - base: ['all'], - spaces: ['*'], - }, - ], - }, - manage_rollups_role: { - elasticsearch: { - cluster: ['manage', 'manage_rollup'], - indices: [ - { - names: ['*'], - privileges: ['read', 'delete', 'create_index', 'view_index_metadata'], - }, - ], - }, - kibana: [ - { - feature: { - discover: ['read'], - }, - spaces: ['*'], - }, - ], - }, - - test_rollup_reader: { - elasticsearch: { - indices: [ - { - names: ['rollup-*'], - privileges: ['read', 'view_index_metadata'], - }, - ], - }, - }, - - // Kibana feature privilege isn't specific to advancedSetting. It can be anything. https://github.com/elastic/kibana/issues/35965 - test_api_keys: { - elasticsearch: { - cluster: ['manage_security', 'manage_api_key'], - }, - kibana: [ - { - feature: { - advancedSettings: ['read'], - }, - spaces: ['default'], - }, - ], - }, - - manage_security: { - elasticsearch: { - cluster: ['manage_security'], - }, - }, - - ccr_user: { - elasticsearch: { - cluster: ['manage', 'manage_ccr'], - }, - }, - - manage_ilm: { - elasticsearch: { - cluster: ['manage_ilm'], - }, - kibana: [ - { - feature: { - advancedSettings: ['read'], - }, - spaces: ['default'], - }, - ], - }, - - index_management_user: { - elasticsearch: { - cluster: ['monitor', 'manage_index_templates'], - indices: [ - { - names: ['*'], - privileges: ['all'], - }, - ], - }, - kibana: [ - { - feature: { - advancedSettings: ['read'], - }, - spaces: ['*'], - }, - ], - }, - // https://www.elastic.co/guide/en/elasticsearch/reference/master/snapshots-register-repository.html#snapshot-repo-prereqs - snapshot_restore_user: { - elasticsearch: { - cluster: [ - 'monitor', - 'manage_slm', - 'cluster:admin/snapshot', - 'cluster:admin/repository', - ], - }, - kibana: [ - { - feature: { - advancedSettings: ['read'], - }, - spaces: ['*'], - }, - ], - }, - - ingest_pipelines_user: { - elasticsearch: { - cluster: ['manage_pipeline', 'cluster:monitor/nodes/info'], - }, - kibana: [ - { - feature: { - advancedSettings: ['read'], - }, - spaces: ['*'], - }, - ], - }, - - license_management_user: { - elasticsearch: { - cluster: ['manage'], - }, - }, - - logstash_read_user: { - elasticsearch: { - cluster: ['manage_logstash_pipelines'], - }, - }, - - remote_clusters_user: { - elasticsearch: { - cluster: ['manage'], - }, - }, - - global_alerts_logs_all_else_read: { - kibana: [ - { - feature: { - apm: ['read'], - logs: ['all'], - infrastructure: ['read'], - }, - spaces: ['*'], - }, - ], - elasticsearch: { - indices: [ - { - names: ['*'], - privileges: ['all'], - }, - ], - }, - }, - }, - defaultRoles: ['superuser'], - }, - }; -} diff --git a/x-pack/test/functional/config_security_basic.ts b/x-pack/test/functional/config_security_basic.ts index dc4bfc437347e..d56b91d45a63e 100644 --- a/x-pack/test/functional/config_security_basic.ts +++ b/x-pack/test/functional/config_security_basic.ts @@ -20,7 +20,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { require.resolve('../../../test/common/config.js') ); const kibanaFunctionalConfig = await readConfigFile( - require.resolve('../../../test/functional/config.js') + require.resolve('../../../test/functional/config.base.js') ); return { diff --git a/x-pack/test/functional/es_archives/actions/data.json b/x-pack/test/functional/es_archives/actions/data.json index 79e7920872ab0..75206672358db 100644 --- a/x-pack/test/functional/es_archives/actions/data.json +++ b/x-pack/test/functional/es_archives/actions/data.json @@ -205,4 +205,96 @@ "updated_at": "2021-08-31T12:43:37.117Z" } } +} + +{ + "type": "doc", + "value": { + "id": "action:7d04bc30-c4c0-11ec-ae29-917aa31a5b75", + "index": ".kibana_1", + "source": { + "action": { + "actionTypeId" : ".servicenow-sir", + "name" : "test servicenow SecOps", + "isMissingSecrets" : false, + "config" : { + "apiUrl": "https://devtestsecops.service-now.com", + "usesTableApi": false + }, + "secrets" : "kPp4tl4ueQ2ZNWSfATR3dFrbxd+NNBo4MY8izS6GJf358Lmeg/YaYjb2rIymrbPktR6HnPBRaVyXWlRTvBGstRicJc0LJHZbx3wNJlTRIj4UFlVqZLGQWQ/GcSqFLSZ1JQbKwgAvyfLtF6BhjAhGYEovK3/OLUNzGc3gvUOOHBiPWjiAY8A=" + }, + "migrationVersion": { + "action": "8.0.0" + }, + "coreMigrationVersion" : "8.2.0", + "references": [ + ], + "namespaces": [ + "default" + ], + "type": "action", + "updated_at": "2022-04-25T17:52:35.201Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "action:8a9331b0-c4c0-11ec-ae29-917aa31a5b75", + "index": ".kibana_1", + "source": { + "action": { + "actionTypeId" : ".servicenow-itom", + "name" : "test servicenow ITOM", + "isMissingSecrets" : false, + "config" : { + "apiUrl": "https://devtestsecops.service-now.com" + }, + "secrets" : "yYThM4vbrSTIg5IjKWE+eMDrxzL7UO0JQIyh6FvEMgqoNREUxRrIavSo25v+DXQIX1DyfsvjjKg97pNPlZhvS3siCwDZZafSFrwkCKDl+S4KHORgIMX+slilcQeuEnzwit7bFxcY7Y/AcNF8Ks6jO0Gs1UR58ibSPUALXoK2VOlJnHSgtvE=" + }, + "migrationVersion": { + "action": "8.0.0" + }, + "coreMigrationVersion" : "8.2.0", + "references": [ + ], + "namespaces": [ + "default" + ], + "type": "action", + "updated_at": "2022-04-25T17:52:35.201Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "action:6d3a1250-c4c0-11ec-ae29-917aa31a5b75", + "index": ".kibana_1", + "source": { + "action": { + "actionTypeId" : ".servicenow", + "name" : "test servicenow ITSM", + "isMissingSecrets" : false, + "config" : { + "usesTableApi": false, + "apiUrl": "https://devtestsecops.service-now.com" + }, + "secrets" : "zfXUDtG0CyJkJUKnQ8rSqo75hb6ZhbRUWkV1NiFEjApM87b72Rcqz3Fv+sbm8eBDOO1Fdd9CVyK+Bfly4ZwVCgL2lR0qIbPzz34q36r267dnGVsaERyJIVv2WPy+EGdiRZKgfpy4XFbMNT1R3gyIsUkd4TT+McqGfVTont2XTFIpMW2A9y8=" + }, + "migrationVersion": { + "action": "8.0.0" + }, + "coreMigrationVersion" : "8.2.0", + "references": [ + ], + "namespaces": [ + "default" + ], + "type": "action", + "updated_at": "2022-04-25T17:52:35.201Z" + } + } } \ No newline at end of file diff --git a/x-pack/test/functional/es_archives/alerts/data.json b/x-pack/test/functional/es_archives/alerts/data.json index 1c096d9df9930..3ce8cddcb284d 100644 --- a/x-pack/test/functional/es_archives/alerts/data.json +++ b/x-pack/test/functional/es_archives/alerts/data.json @@ -891,6 +891,57 @@ } } +{ + "type": "doc", + "value": { + "id": "alert:776cb5c0-ad1e-11ec-ab9e-5f5932f4fad8", + "index": ".kibana_1", + "source": { + "alert": { + "name": "123", + "alertTypeId": ".es-query", + "consumer": "alerts", + "params": { + "esQuery": "{\n \"query\":{\n \"match_all\" : {}\n }\n}", + "size": 100, + "timeWindowSize": 5, + "timeWindowUnit": "m", + "threshold": [ + 1000 + ], + "thresholdComparator": ">", + "index": [ + "kibana_sample_data_ecommerce" + ], + "timeField": "order_date" + }, + "schedule": { + "interval": "1m" + }, + "enabled": true, + "actions": [ + ], + "throttle": null, + "apiKeyOwner": null, + "createdBy" : "elastic", + "updatedBy" : "elastic", + "createdAt": "2022-03-26T16:04:50.698Z", + "muteAll": false, + "mutedInstanceIds": [], + "scheduledTaskId": "776cb5c0-ad1e-11ec-ab9e-5f5932f4fad8", + "tags": [] + }, + "type": "alert", + "updated_at": "2022-03-26T16:05:55.957Z", + "migrationVersion": { + "alert": "8.0.1" + }, + "references": [ + ] + } + } +} + { "type":"doc", "value":{ @@ -989,4 +1040,4 @@ ] } } -} \ No newline at end of file +} diff --git a/x-pack/test/functional/es_archives/rule_registry/alerts/data.json b/x-pack/test/functional/es_archives/rule_registry/alerts/data.json index 284c6e9519cc1..9c8d233e02074 100644 --- a/x-pack/test/functional/es_archives/rule_registry/alerts/data.json +++ b/x-pack/test/functional/es_archives/rule_registry/alerts/data.json @@ -57,7 +57,7 @@ "source": { "event.kind" : "signal", "@timestamp": "2020-12-16T15:16:18.570Z", - "kibana.alert.rule.rule_type_id": "siem.signals", + "kibana.alert.rule.rule_type_id": "siem.queryRule", "message": "hello world security", "kibana.alert.rule.consumer": "siem", "kibana.alert.workflow_status": "open", @@ -74,7 +74,7 @@ "source": { "event.kind" : "signal", "@timestamp": "2020-12-16T15:16:18.570Z", - "kibana.alert.rule.rule_type_id": "siem.customRule", + "kibana.alert.rule.rule_type_id": "siem.queryRule", "message": "hello world security", "kibana.alert.rule.consumer": "siem", "kibana.alert.workflow_status": "open", @@ -90,7 +90,7 @@ "id": "space1securityalert", "source": { "@timestamp": "2020-12-16T15:16:18.570Z", - "kibana.alert.rule.rule_type_id": "siem.signals", + "kibana.alert.rule.rule_type_id": "siem.queryRule", "message": "hello world security", "kibana.alert.rule.consumer": "siem", "kibana.alert.workflow_status": "open", @@ -106,7 +106,7 @@ "id": "space2securityalert", "source": { "@timestamp": "2020-12-16T15:16:18.570Z", - "kibana.alert.rule.rule_type_id": "siem.signals", + "kibana.alert.rule.rule_type_id": "siem.queryRule", "message": "hello world security", "kibana.alert.rule.consumer": "siem", "kibana.alert.workflow_status": "open", diff --git a/x-pack/test/functional/es_archives/security_solution/legacy_actions/data.json b/x-pack/test/functional/es_archives/security_solution/legacy_actions/data.json new file mode 100644 index 0000000000000..25aa371e02144 --- /dev/null +++ b/x-pack/test/functional/es_archives/security_solution/legacy_actions/data.json @@ -0,0 +1,1064 @@ +{ + "type": "doc", + "value": { + "index" : ".kibana", + "id" : "action:c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "source" : { + "action" : { + "actionTypeId" : ".email", + "name" : "test", + "isMissingSecrets" : false, + "config" : { + "hasAuth" : true, + "from" : "a@a.com", + "host" : "gmail", + "port" : 56, + "service" : "other", + "secure" : null + }, + "secrets" : "jCv0fZczCM+rlWXIkI6Qo/ZqasgxPhplokCaNeXJrJlJYjKeCgbZgiLAEue4bpq4GUDohfye8o1BlJCQi+3HJPb2Qp6lhbcvXn+C6wUzxj9ZitwY8ZTDo34e22TX7qHFs/1mVfO4stjZ6X86ufRZttjTNu4qjchfi3Jao7nWbw==" + }, + "type" : "action", + "references" : [ ], + "namespaces" : [ + "default" + ], + "migrationVersion" : { + "action" : "8.0.0" + }, + "coreMigrationVersion" : "8.1.2", + "updated_at" : "2022-03-30T22:07:27.777Z" + } + } +} + +{ + "type": "doc", + "value": { + "index" : ".kibana", + "id" : "action:207fa0e0-c04e-11ec-8a52-4fb92379525a", + "source" : { + "action" : { + "actionTypeId" : ".slack", + "name" : "Slack connector", + "isMissingSecrets" : false, + "config" : { }, + "secrets" : "WTpvAWxAnR1yHdFqQrmGGeOmqGzeIDQnjGexz7F3JdFVEH0CJkPbiyEk9SiiBr1OPZ3s0LWqphjNREjyxSTCTuYTKaEIQ1+mJFQzjIulekMZklQdon7kNh4hOVHW832IX1Lpx9f5cgAPQst2bqJCehz+3BeZOh17Hgrvb/0zzKRCR4cvlhYbOUNUCNR/d6YoEniwUK8pO9xcel9hle7XY6MNfBGiMX8T41O87lvgp9xDjch3LpwqzDjUIFs=" + }, + "type" : "action", + "references" : [ ], + "namespaces" : [ + "default" + ], + "migrationVersion" : { + "action" : "8.0.0" + }, + "coreMigrationVersion" : "8.1.2", + "updated_at" : "2022-03-30T22:07:27.777Z" + } + } +} + +{ + "type": "doc", + "value": { + "index" : ".kibana", + "id" : "alert:9095ee90-b075-11ec-bb3f-1f063f8e06cf", + "source" : { + "alert" : { + "name" : "test w/ no actions", + "tags" : [ + "__internal_rule_id:2297be91-894c-4831-830f-b424a0ec84f0", + "__internal_immutable:false", + "auto_disabled_8.0" + ], + "alertTypeId" : "siem.queryRule", + "consumer" : "siem", + "params" : { + "author" : [ ], + "description" : "a", + "ruleId" : "2297be91-894c-4831-830f-b424a0ec84f0", + "falsePositives" : [ ], + "from" : "now-360s", + "immutable" : false, + "license" : "", + "outputIndex" : "", + "meta" : { + "from" : "1m", + "kibana_siem_app_url" : "https://actions.kb.us-central1.gcp.cloud.es.io:9243/app/security" + }, + "maxSignals" : 100, + "riskScore" : 21, + "riskScoreMapping" : [ ], + "severity" : "low", + "severityMapping" : [ ], + "threat" : [ ], + "to" : "now", + "references" : [ ], + "version" : 1, + "exceptionsList" : [ ], + "type" : "query", + "language" : "kuery", + "index" : [ + "apm-*-transaction*", + "traces-apm*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "logs-*", + "packetbeat-*", + "winlogbeat-*" + ], + "query" : "*:*", + "filters" : [ ] + }, + "schedule" : { + "interval" : "5m" + }, + "enabled" : true, + "actions" : [ ], + "throttle" : null, + "notifyWhen" : "onActiveAlert", + "apiKeyOwner" : null, + "apiKey" : null, + "createdBy" : "1527796724", + "updatedBy" : "1527796724", + "createdAt" : "2022-03-30T22:05:53.511Z", + "updatedAt" : "2022-03-30T22:05:53.511Z", + "muteAll" : false, + "mutedInstanceIds" : [ ], + "executionStatus" : { + "status" : "ok", + "lastExecutionDate" : "2022-03-31T19:53:37.507Z", + "error" : null, + "lastDuration" : 2377 + }, + "meta" : { + "versionApiKeyLastmodified" : "7.15.2" + }, + "scheduledTaskId" : null, + "legacyId" : "9095ee90-b075-11ec-bb3f-1f063f8e06cf" + }, + "type" : "alert", + "references" : [ ], + "namespaces" : [ + "default" + ], + "migrationVersion" : { + "alert" : "8.0.1" + }, + "coreMigrationVersion" : "8.1.2", + "updated_at" : "2022-03-31T19:53:39.885Z" + } + } +} + +{ + "type": "doc", + "value": { + "index" : ".kibana", + "id" : "alert:dc6595f0-b075-11ec-bb3f-1f063f8e06cf", + "source" : { + "alert" : { + "name" : "test w/ action - every rule run", + "tags" : [ + "__internal_rule_id:72a0d429-363b-4f70-905e-c6019a224d40", + "__internal_immutable:false", + "auto_disabled_8.0" + ], + "alertTypeId" : "siem.queryRule", + "consumer" : "siem", + "params" : { + "author" : [ ], + "description" : "a", + "ruleId" : "72a0d429-363b-4f70-905e-c6019a224d40", + "falsePositives" : [ ], + "from" : "now-360s", + "immutable" : false, + "license" : "", + "outputIndex" : "", + "meta" : { + "from" : "1m", + "kibana_siem_app_url" : "https://actions.kb.us-central1.gcp.cloud.es.io:9243/app/security" + }, + "maxSignals" : 100, + "riskScore" : 21, + "riskScoreMapping" : [ ], + "severity" : "low", + "severityMapping" : [ ], + "threat" : [ ], + "to" : "now", + "references" : [ ], + "version" : 1, + "exceptionsList" : [ ], + "type" : "query", + "language" : "kuery", + "index" : [ + "apm-*-transaction*", + "traces-apm*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "logs-*", + "packetbeat-*", + "winlogbeat-*" + ], + "query" : "*:* ", + "filters" : [ ] + }, + "schedule" : { + "interval" : "5m" + }, + "enabled" : true, + "actions" : [ + { + "group" : "default", + "params" : { + "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", + "to" : [ + "test@test.com" + ], + "subject" : "Test Actions" + }, + "actionTypeId" : ".email", + "actionRef" : "action_0" + } + ], + "throttle" : null, + "notifyWhen" : "onActiveAlert", + "apiKeyOwner" : null, + "apiKey" : null, + "createdBy" : "1527796724", + "updatedBy" : "1527796724", + "createdAt" : "2022-03-30T22:08:00.172Z", + "updatedAt" : "2022-03-30T22:08:00.172Z", + "muteAll" : false, + "mutedInstanceIds" : [ ], + "executionStatus" : { + "status" : "ok", + "lastExecutionDate" : "2022-03-31T19:55:43.502Z", + "error" : null, + "lastDuration" : 2700 + }, + "meta" : { + "versionApiKeyLastmodified" : "7.15.2" + }, + "scheduledTaskId" : null, + "legacyId" : "dc6595f0-b075-11ec-bb3f-1f063f8e06cf" + }, + "type" : "alert", + "references" : [ + { + "id" : "c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "name" : "action_0", + "type" : "action" + } + ], + "namespaces" : [ + "default" + ], + "migrationVersion" : { + "alert" : "8.0.1" + }, + "coreMigrationVersion" : "8.1.2", + "updated_at" : "2022-03-31T19:55:46.202Z" + } + } +} + +{ + "type": "doc", + "value": { + "index" : ".kibana", + "id" : "alert:064e3160-b076-11ec-bb3f-1f063f8e06cf", + "source" : { + "alert" : { + "name" : "test w/ action - every hour", + "tags" : [ + "__internal_rule_id:4c056b05-75ac-4209-be32-82100f771eb4", + "__internal_immutable:false", + "auto_disabled_8.0" + ], + "alertTypeId" : "siem.queryRule", + "consumer" : "siem", + "params" : { + "author" : [ ], + "description" : "a", + "ruleId" : "4c056b05-75ac-4209-be32-82100f771eb4", + "falsePositives" : [ ], + "from" : "now-360s", + "immutable" : false, + "license" : "", + "outputIndex" : "", + "meta" : { + "from" : "1m", + "kibana_siem_app_url" : "https://actions.kb.us-central1.gcp.cloud.es.io:9243/app/security" + }, + "maxSignals" : 100, + "riskScore" : 21, + "riskScoreMapping" : [ ], + "severity" : "low", + "severityMapping" : [ ], + "threat" : [ ], + "to" : "now", + "references" : [ ], + "version" : 1, + "exceptionsList" : [ ], + "type" : "query", + "language" : "kuery", + "index" : [ + "apm-*-transaction*", + "traces-apm*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "logs-*", + "packetbeat-*", + "winlogbeat-*" + ], + "query" : "*:*", + "filters" : [ ] + }, + "schedule" : { + "interval" : "5m" + }, + "enabled" : true, + "actions" : [ ], + "throttle" : null, + "notifyWhen" : "onActiveAlert", + "apiKeyOwner" : null, + "apiKey" : null, + "createdBy" : "1527796724", + "updatedBy" : "1527796724", + "createdAt" : "2022-03-30T22:09:10.261Z", + "updatedAt" : "2022-03-30T22:09:10.261Z", + "muteAll" : false, + "mutedInstanceIds" : [ ], + "executionStatus" : { + "status" : "ok", + "lastExecutionDate" : "2022-03-31T19:56:43.499Z", + "error" : null, + "lastDuration" : 2815 + }, + "meta" : { + "versionApiKeyLastmodified" : "7.15.2" + }, + "scheduledTaskId" : null, + "legacyId" : "064e3160-b076-11ec-bb3f-1f063f8e06cf" + }, + "type" : "alert", + "references" : [ ], + "namespaces" : [ + "default" + ], + "migrationVersion" : { + "alert" : "8.0.1" + }, + "coreMigrationVersion" : "8.1.2", + "updated_at" : "2022-03-31T19:56:46.314Z" + } + } +} + +{ + "type": "doc", + "value": { + "index" : ".kibana", + "id" : "alert:27639570-b076-11ec-bb3f-1f063f8e06cf", + "source" : { + "alert" : { + "name" : "test w/ action - daily", + "tags" : [ + "__internal_rule_id:8e2c8550-f13f-4e21-be0c-92148d71a5f1", + "__internal_immutable:false", + "auto_disabled_8.0" + ], + "alertTypeId" : "siem.queryRule", + "consumer" : "siem", + "params" : { + "author" : [ ], + "description" : "a", + "ruleId" : "8e2c8550-f13f-4e21-be0c-92148d71a5f1", + "falsePositives" : [ ], + "from" : "now-360s", + "immutable" : false, + "license" : "", + "outputIndex" : "", + "meta" : { + "from" : "1m", + "kibana_siem_app_url" : "https://actions.kb.us-central1.gcp.cloud.es.io:9243/app/security" + }, + "maxSignals" : 100, + "riskScore" : 21, + "riskScoreMapping" : [ ], + "severity" : "low", + "severityMapping" : [ ], + "threat" : [ ], + "to" : "now", + "references" : [ ], + "version" : 1, + "exceptionsList" : [ ], + "type" : "query", + "language" : "kuery", + "index" : [ + "apm-*-transaction*", + "traces-apm*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "logs-*", + "packetbeat-*", + "winlogbeat-*" + ], + "query" : "*:*", + "filters" : [ ] + }, + "schedule" : { + "interval" : "5m" + }, + "enabled" : true, + "actions" : [ ], + "throttle" : null, + "notifyWhen" : "onActiveAlert", + "apiKeyOwner" : null, + "apiKey" : null, + "createdBy" : "1527796724", + "updatedBy" : "1527796724", + "createdAt" : "2022-03-30T22:10:06.358Z", + "updatedAt" : "2022-03-30T22:10:06.358Z", + "muteAll" : false, + "mutedInstanceIds" : [ ], + "executionStatus" : { + "status" : "ok", + "lastExecutionDate" : "2022-03-31T19:58:13.480Z", + "error" : null, + "lastDuration" : 3023 + }, + "meta" : { + "versionApiKeyLastmodified" : "7.15.2" + }, + "scheduledTaskId" : null, + "legacyId" : "27639570-b076-11ec-bb3f-1f063f8e06cf" + }, + "type" : "alert", + "references" : [ ], + "namespaces" : [ + "default" + ], + "migrationVersion" : { + "alert" : "8.0.1" + }, + "coreMigrationVersion" : "8.1.2", + "updated_at" : "2022-03-31T19:58:16.503Z" + } + } +} + +{ + "type": "doc", + "value": { + "index" : ".kibana", + "id" : "alert:61ec7a40-b076-11ec-bb3f-1f063f8e06cf", + "source" : { + "alert" : { + "name" : "test w/ actions - weekly", + "tags" : [ + "__internal_rule_id:05fbdd2a-e802-420b-bdc3-95ae0acca454", + "__internal_immutable:false", + "auto_disabled_8.0" + ], + "alertTypeId" : "siem.queryRule", + "consumer" : "siem", + "params" : { + "author" : [ ], + "description" : "a", + "ruleId" : "05fbdd2a-e802-420b-bdc3-95ae0acca454", + "falsePositives" : [ ], + "from" : "now-360s", + "immutable" : false, + "license" : "", + "outputIndex" : "", + "meta" : { + "from" : "1m", + "kibana_siem_app_url" : "https://actions.kb.us-central1.gcp.cloud.es.io:9243/app/security" + }, + "maxSignals" : 100, + "riskScore" : 21, + "riskScoreMapping" : [ ], + "severity" : "low", + "severityMapping" : [ ], + "threat" : [ ], + "to" : "now", + "references" : [ ], + "version" : 1, + "exceptionsList" : [ ], + "type" : "query", + "language" : "kuery", + "index" : [ + "apm-*-transaction*", + "traces-apm*", + "auditbeat-*", + "endgame-*", + "filebeat-*", + "logs-*", + "packetbeat-*", + "winlogbeat-*" + ], + "query" : "*:*", + "filters" : [ ] + }, + "schedule" : { + "interval" : "5m" + }, + "enabled" : true, + "actions" : [ ], + "throttle" : null, + "notifyWhen" : "onActiveAlert", + "apiKeyOwner" : null, + "apiKey" : null, + "createdBy" : "1527796724", + "updatedBy" : "1527796724", + "createdAt" : "2022-03-30T22:11:44.516Z", + "updatedAt" : "2022-03-30T22:11:44.516Z", + "muteAll" : false, + "mutedInstanceIds" : [ ], + "executionStatus" : { + "status" : "ok", + "lastExecutionDate" : "2022-03-31T19:54:22.442Z", + "error" : null, + "lastDuration" : 2612 + }, + "meta" : { + "versionApiKeyLastmodified" : "7.15.2" + }, + "scheduledTaskId" : null, + "legacyId" : "61ec7a40-b076-11ec-bb3f-1f063f8e06cf" + }, + "type" : "alert", + "references" : [ ], + "namespaces" : [ + "default" + ], + "migrationVersion" : { + "alert" : "8.0.1" + }, + "coreMigrationVersion" : "8.1.2", + "updated_at" : "2022-03-31T19:54:25.054Z" + } + } +} + +{ + "type": "doc", + "value": { + "index" : ".kibana", + "id" : "alert:64492ef0-b076-11ec-bb3f-1f063f8e06cf", + "source" : { + "alert" : { + "name" : "test w/ actions - weekly", + "tags" : [ + "__internal_rule_alert_id:61ec7a40-b076-11ec-bb3f-1f063f8e06cf" + ], + "alertTypeId" : "siem.notifications", + "consumer" : "siem", + "params" : { + "ruleAlertId" : "61ec7a40-b076-11ec-bb3f-1f063f8e06cf" + }, + "schedule" : { + "interval" : "7d" + }, + "enabled" : true, + "actions" : [ + { + "group" : "default", + "params" : { + "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", + "to" : [ + "test@test.com" + ], + "subject" : "Test Actions" + }, + "actionTypeId" : ".email", + "actionRef" : "action_0" + } + ], + "throttle" : null, + "notifyWhen" : "onActiveAlert", + "apiKeyOwner" : null, + "apiKey" : null, + "createdBy" : "1527796724", + "updatedBy" : "1527796724", + "createdAt" : "2022-03-30T22:11:48.661Z", + "updatedAt" : "2022-03-30T22:11:48.661Z", + "muteAll" : false, + "mutedInstanceIds" : [ ], + "executionStatus" : { + "status" : "ok", + "lastExecutionDate" : "2022-03-30T22:11:51.640Z", + "error" : null + }, + "meta" : { + "versionApiKeyLastmodified" : "7.15.2" + }, + "scheduledTaskId" : null, + "legacyId" : "64492ef0-b076-11ec-bb3f-1f063f8e06cf" + }, + "type" : "alert", + "references" : [ + { + "id" : "c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "name" : "action_0", + "type" : "action" + }, + { + "id" : "61ec7a40-b076-11ec-bb3f-1f063f8e06cf", + "name" : "param:alert_0", + "type" : "alert" + } + ], + "namespaces" : [ + "default" + ], + "migrationVersion" : { + "alert" : "8.0.1" + }, + "coreMigrationVersion" : "8.1.2", + "updated_at" : "2022-03-30T22:11:51.707Z" + } + } +} + +{ + "type": "doc", + "value": { + "index" : ".kibana", + "id" : "alert:d42e8210-b076-11ec-bb3f-1f063f8e06cf", + "source" : { + "alert" : { + "name" : "test w/ action - every hour", + "tags" : [ + "__internal_rule_alert_id:064e3160-b076-11ec-bb3f-1f063f8e06cf" + ], + "alertTypeId" : "siem.notifications", + "consumer" : "siem", + "params" : { + "ruleAlertId" : "064e3160-b076-11ec-bb3f-1f063f8e06cf" + }, + "schedule" : { + "interval" : "1h" + }, + "enabled" : true, + "actions" : [ + { + "group" : "default", + "params" : { + "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", + "to" : [ + "test@test.com" + ], + "subject" : "Rule email" + }, + "actionTypeId" : ".email", + "actionRef" : "action_0" + }, + { + "group" : "default", + "params" : { + "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts" + }, + "actionTypeId" : ".slack", + "actionRef" : "action_1" + } + ], + "throttle" : null, + "notifyWhen" : "onActiveAlert", + "apiKeyOwner" : null, + "apiKey" : null, + "createdBy" : "1527796724", + "updatedBy" : "1527796724", + "createdAt" : "2022-03-30T22:14:56.318Z", + "updatedAt" : "2022-03-30T22:15:12.135Z", + "muteAll" : false, + "mutedInstanceIds" : [ ], + "executionStatus" : { + "status" : "pending", + "lastExecutionDate" : "2022-03-30T22:14:56.318Z", + "error" : null + }, + "meta" : { + "versionApiKeyLastmodified" : "7.15.2" + }, + "legacyId" : "d42e8210-b076-11ec-bb3f-1f063f8e06cf" + }, + "type" : "alert", + "references" : [ + { + "id" : "c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "name" : "action_0", + "type" : "action" + }, + { + "id" : "207fa0e0-c04e-11ec-8a52-4fb92379525a", + "name" : "action_1", + "type" : "action" + }, + { + "id" : "064e3160-b076-11ec-bb3f-1f063f8e06cf", + "name" : "param:alert_0", + "type" : "alert" + } + ], + "namespaces" : [ + "default" + ], + "migrationVersion" : { + "alert" : "8.0.1" + }, + "coreMigrationVersion" : "8.1.2", + "updated_at" : "2022-03-30T22:15:12.135Z" + } + } +} + +{ + "type": "doc", + "value": { + "index" : ".kibana", + "id" : "alert:29ba2fa0-b076-11ec-bb3f-1f063f8e06cf", + "source" : { + "alert" : { + "name" : "test w/ action - daily", + "tags" : [ + "__internal_rule_alert_id:27639570-b076-11ec-bb3f-1f063f8e06cf" + ], + "alertTypeId" : "siem.notifications", + "consumer" : "siem", + "params" : { + "ruleAlertId" : "27639570-b076-11ec-bb3f-1f063f8e06cf" + }, + "schedule" : { + "interval" : "1d" + }, + "enabled" : true, + "actions" : [ + { + "group" : "default", + "params" : { + "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", + "to" : [ + "test@test.com" + ], + "subject" : "Test Actions" + }, + "actionTypeId" : ".email", + "actionRef" : "action_0" + } + ], + "throttle" : null, + "notifyWhen" : "onActiveAlert", + "apiKeyOwner" : null, + "apiKey" : null, + "createdBy" : "1527796724", + "updatedBy" : "1527796724", + "createdAt" : "2022-03-30T22:10:10.435Z", + "updatedAt" : "2022-03-30T22:10:10.435Z", + "muteAll" : false, + "mutedInstanceIds" : [ ], + "executionStatus" : { + "status" : "ok", + "lastExecutionDate" : "2022-04-01T22:10:15.478Z", + "error" : null, + "lastDuration" : 984 + }, + "meta" : { + "versionApiKeyLastmodified" : "7.15.2" + }, + "scheduledTaskId" : null, + "legacyId" : "29ba2fa0-b076-11ec-bb3f-1f063f8e06cf", + "monitoring" : { + "execution" : { + "history" : [ + { + "duration" : 111, + "success" : true, + "timestamp" : 1648764614215 + }, + { + "duration" : 984, + "success" : true, + "timestamp" : 1648851016462 + } + ], + "calculated_metrics" : { + "p99" : 984, + "success_ratio" : 1, + "p50" : 547.5, + "p95" : 984 + } + } + } + }, + "type" : "alert", + "references" : [ + { + "id" : "c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "name" : "action_0", + "type" : "action" + }, + { + "id" : "27639570-b076-11ec-bb3f-1f063f8e06cf", + "name" : "param:alert_0", + "type" : "alert" + } + ], + "namespaces" : [ + "default" + ], + "migrationVersion" : { + "alert" : "8.0.1" + }, + "coreMigrationVersion" : "8.1.2", + "updated_at" : "2022-04-01T22:10:16.467Z" + } + } +} + +{ + "type": "doc", + "value": { + "index" : ".kibana", + "id" : "siem-detection-engine-rule-actions:926668d0-b075-11ec-bb3f-1f063f8e06cf", + "source" : { + "siem-detection-engine-rule-actions" : { + "actions" : [ ], + "ruleThrottle" : "no_actions", + "alertThrottle" : null + }, + "type" : "siem-detection-engine-rule-actions", + "references" : [ + { + "id" : "9095ee90-b075-11ec-bb3f-1f063f8e06cf", + "type" : "alert", + "name" : "alert_0" + } + ], + "namespaces" : [ + "default" + ], + "migrationVersion" : { + "siem-detection-engine-rule-actions" : "8.0.0" + }, + "coreMigrationVersion" : "8.1.2", + "updated_at" : "2022-03-30T22:05:55.563Z" + } + } +} + +{ + "type": "doc", + "value": { + "index" : ".kibana", + "id" : "siem-detection-engine-rule-actions:dde13970-b075-11ec-bb3f-1f063f8e06cf", + "source" : { + "siem-detection-engine-rule-actions" : { + "actions" : [ + { + "actionRef" : "action_0", + "group" : "default", + "params" : { + "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", + "to" : [ + "test@test.com" + ], + "subject" : "Test Actions" + }, + "action_type_id" : ".email" + } + ], + "ruleThrottle" : "rule", + "alertThrottle" : null + }, + "type" : "siem-detection-engine-rule-actions", + "references" : [ + { + "id" : "dc6595f0-b075-11ec-bb3f-1f063f8e06cf", + "type" : "alert", + "name" : "alert_0" + }, + { + "id" : "c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "type" : "action", + "name" : "action_0" + } + ], + "namespaces" : [ + "default" + ], + "migrationVersion" : { + "siem-detection-engine-rule-actions" : "8.0.0" + }, + "coreMigrationVersion" : "8.1.2", + "updated_at" : "2022-03-30T22:08:02.207Z" + } + } +} + +{ + "type": "doc", + "value": { + "index" : ".kibana", + "id" : "siem-detection-engine-rule-actions:07aa8d10-b076-11ec-bb3f-1f063f8e06cf", + "source" : { + "siem-detection-engine-rule-actions" : { + "actions" : [ + { + "actionRef" : "action_0", + "group" : "default", + "params" : { + "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", + "to" : [ + "test@test.com" + ], + "subject" : "Rule email" + }, + "action_type_id" : ".email" + }, + { + "actionRef" : "action_1", + "group" : "default", + "params" : { + "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts" + }, + "action_type_id" : ".slack" + } + ], + "ruleThrottle" : "1h", + "alertThrottle" : "1h" + }, + "type" : "siem-detection-engine-rule-actions", + "references" : [ + { + "id" : "064e3160-b076-11ec-bb3f-1f063f8e06cf", + "type" : "alert", + "name" : "alert_0" + }, + { + "id" : "c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "type" : "action", + "name" : "action_0" + }, + { + "id" : "207fa0e0-c04e-11ec-8a52-4fb92379525a", + "type" : "action", + "name" : "action_1" + } + ], + "namespaces" : [ + "default" + ], + "migrationVersion" : { + "siem-detection-engine-rule-actions" : "8.0.0" + }, + "coreMigrationVersion" : "8.1.2", + "updated_at" : "2022-03-30T22:09:12.300Z" + } + } +} + +{ + "type": "doc", + "value": { + "index" : ".kibana", + "id" : "siem-detection-engine-rule-actions:291ae260-b076-11ec-bb3f-1f063f8e06cf", + "source" : { + "siem-detection-engine-rule-actions" : { + "actions" : [ + { + "actionRef" : "action_0", + "group" : "default", + "params" : { + "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", + "to" : [ + "test@test.com" + ], + "subject" : "Test Actions" + }, + "action_type_id" : ".email" + } + ], + "ruleThrottle" : "1d", + "alertThrottle" : "1d" + }, + "type" : "siem-detection-engine-rule-actions", + "references" : [ + { + "id" : "27639570-b076-11ec-bb3f-1f063f8e06cf", + "type" : "alert", + "name" : "alert_0" + }, + { + "id" : "c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "type" : "action", + "name" : "action_0" + } + ], + "namespaces" : [ + "default" + ], + "migrationVersion" : { + "siem-detection-engine-rule-actions" : "8.0.0" + }, + "coreMigrationVersion" : "8.1.2", + "updated_at" : "2022-03-30T22:10:08.399Z" + } + } +} + +{ + "type": "doc", + "value": { + "index" : ".kibana", + "id" : "siem-detection-engine-rule-actions:63aa2fd0-b076-11ec-bb3f-1f063f8e06cf", + "source" : { + "siem-detection-engine-rule-actions" : { + "actions" : [ + { + "actionRef" : "action_0", + "group" : "default", + "params" : { + "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", + "to" : [ + "test@test.com" + ], + "subject" : "Test Actions" + }, + "action_type_id" : ".email" + } + ], + "ruleThrottle" : "7d", + "alertThrottle" : "7d" + }, + "type" : "siem-detection-engine-rule-actions", + "references" : [ + { + "id" : "61ec7a40-b076-11ec-bb3f-1f063f8e06cf", + "type" : "alert", + "name" : "alert_0" + }, + { + "id" : "c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "type" : "action", + "name" : "action_0" + } + ], + "namespaces" : [ + "default" + ], + "migrationVersion" : { + "siem-detection-engine-rule-actions" : "8.0.0" + }, + "coreMigrationVersion" : "8.1.2", + "updated_at" : "2022-03-30T22:11:46.643Z" + } + } +} diff --git a/x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_duration.json b/x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_duration.json new file mode 100644 index 0000000000000..ba8bab9227fbb --- /dev/null +++ b/x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_duration.json @@ -0,0 +1,129 @@ +{ + "attributes": { + "closed_at": "2022-03-25T10:18:00.000Z", + "closed_by": { + "email": "", + "full_name": "", + "username": "elastic" + }, + "connector": { + "fields": null, + "name": "none", + "type": ".none" + }, + "created_at": "2022-03-25T10:16:00.000Z", + "created_by": { + "email": "", + "full_name": "", + "username": "elastic" + }, + "description": "test 2", + "external_service": null, + "owner": "securitySolutionFixture", + "settings": { + "syncAlerts": false + }, + "status": "closed", + "tags": [], + "title": "stack", + "updated_at": "2022-03-29T10:33:09.754Z", + "updated_by": { + "email": "", + "full_name": "", + "username": "elastic" + } + }, + "coreMigrationVersion": "8.2.0", + "id": "4537b380-a512-11ec-b92f-859b9e89e434", + "migrationVersion": { + "cases": "8.1.0" + }, + "references": [], + "type": "cases", + "updated_at": "2022-03-29T10:33:09.754Z", + "version": "WzE2OTYyNCwxNF0=" +} + +{ + "attributes": { + "closed_at": null, + "closed_by": null, + "connector": { + "fields": null, + "name": "none", + "type": ".none" + }, + "created_at": "2022-03-20T10:16:56.252Z", + "created_by": { + "email": "", + "full_name": "", + "username": "elastic" + }, + "description": "test 2", + "external_service": null, + "owner": "observabilityFixture", + "settings": { + "syncAlerts": false + }, + "status": "open", + "tags": [], + "title": "stack", + "updated_at": "2022-03-29T10:33:09.754Z", + "updated_by": { + "email": "", + "full_name": "", + "username": "elastic" + } + }, + "coreMigrationVersion": "8.2.0", + "id": "7537b580-a512-11ec-b94f-85979e89e434", + "migrationVersion": { + "cases": "8.1.0" + }, + "references": [], + "type": "cases", + "updated_at": "2022-03-29T10:33:09.754Z", + "version": "WzE2OTYyNCwxNF0=" +} + +{ + "attributes": { + "closed_at": null, + "closed_by": null, + "connector": { + "fields": null, + "name": "none", + "type": ".none" + }, + "created_at": "2022-03-20T10:16:56.252Z", + "created_by": { + "email": "", + "full_name": "", + "username": "elastic" + }, + "description": "test 2", + "external_service": null, + "owner": "observabilityFixture", + "settings": { + "syncAlerts": false + }, + "status": "in-progress", + "tags": [], + "title": "stack", + "updated_at": "2022-03-29T10:33:09.754Z", + "updated_by": { + "email": "", + "full_name": "", + "username": "elastic" + } + }, + "coreMigrationVersion": "8.2.0", + "id": "1537b580-a512-11ec-b94f-85979e89e434", + "migrationVersion": { + "cases": "8.1.0" + }, + "references": [], + "type": "cases", + "updated_at": "2022-03-29T10:33:09.754Z", + "version": "WzE2OTYyNCwxNF0=" +} diff --git a/x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_various_dates.json b/x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_various_dates.json index ffdfef08735fd..cd87b6c44de1b 100644 --- a/x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_various_dates.json +++ b/x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_various_dates.json @@ -11,7 +11,7 @@ "created_by": { "email": "", "full_name": "", - "username": "cnasikas" + "username": "elastic" }, "description": "test", "external_service": null, @@ -26,7 +26,7 @@ "updated_by": { "email": "", "full_name": "", - "username": "cnasikas" + "username": "elastic" } }, "coreMigrationVersion": "8.2.0", @@ -53,7 +53,7 @@ "created_by": { "email": "", "full_name": "", - "username": "cnasikas" + "username": "elastic" }, "description": "test 2", "external_service": null, @@ -68,7 +68,7 @@ "updated_by": { "email": "", "full_name": "", - "username": "cnasikas" + "username": "elastic" } }, "coreMigrationVersion": "8.2.0", @@ -95,7 +95,7 @@ "created_by": { "email": "", "full_name": "", - "username": "cnasikas" + "username": "elastic" }, "description": "test 2", "external_service": null, @@ -110,7 +110,7 @@ "updated_by": { "email": "", "full_name": "", - "username": "cnasikas" + "username": "elastic" } }, "coreMigrationVersion": "8.2.0", diff --git a/x-pack/test/functional/fixtures/kbn_archiver/cases/8.3.0/all_cases_metrics.json b/x-pack/test/functional/fixtures/kbn_archiver/cases/8.3.0/all_cases_metrics.json new file mode 100644 index 0000000000000..f677d7624692c --- /dev/null +++ b/x-pack/test/functional/fixtures/kbn_archiver/cases/8.3.0/all_cases_metrics.json @@ -0,0 +1,182 @@ +{ + "attributes": { + "closed_at": "2022-04-29T13:24:44.448Z", + "closed_by": { + "email": null, + "full_name": null, + "username": "elastic" + }, + "connector": { + "fields": [], + "name": "none", + "type": ".none" + }, + "created_at": "2022-04-28T13:24:24.011Z", + "created_by": { + "email": null, + "full_name": null, + "username": "elastic" + }, + "description": "test mttr", + "duration": 20, + "external_service": null, + "owner": "securitySolutionFixture", + "settings": { + "syncAlerts": true + }, + "status": "closed", + "tags": [], + "title": "test mttr", + "updated_at": "2022-04-29T13:24:44.448Z", + "updated_by": { + "email": null, + "full_name": null, + "username": "elastic" + } + }, + "coreMigrationVersion": "8.3.0", + "id": "af948570-c7bf-11ec-9771-d5eef9232089", + "migrationVersion": { + "cases": "8.3.0" + }, + "references": [], + "type": "cases", + "updated_at": "2022-04-29T13:24:44.449Z", + "version": "WzE0NjgsMV0=" +} + +{ + "attributes": { + "closed_at": "2022-04-30T13:32:00.448Z", + "closed_by": { + "email": null, + "full_name": null, + "username": "elastic" + }, + "connector": { + "fields": [], + "name": "none", + "type": ".none" + }, + "created_at": "2022-04-30T13:24:00.011Z", + "created_by": { + "email": null, + "full_name": null, + "username": "elastic" + }, + "description": "test mttr", + "duration": 480, + "external_service": null, + "owner": "securitySolutionFixture", + "settings": { + "syncAlerts": true + }, + "status": "closed", + "tags": [], + "title": "test mttr", + "updated_at": "2022-04-29T13:24:44.448Z", + "updated_by": { + "email": null, + "full_name": null, + "username": "elastic" + } + }, + "coreMigrationVersion": "8.3.0", + "id": "bf948570-c7bf-11ec-9771-d5eef9232089", + "migrationVersion": { + "cases": "8.3.0" + }, + "references": [], + "type": "cases", + "updated_at": "2022-04-29T13:24:44.449Z", + "version": "WzE0NjgsMV0=" +} + +{ + "attributes": { + "closed_at": "2022-04-29T13:32:00.448Z", + "closed_by": { + "email": null, + "full_name": null, + "username": "elastic" + }, + "connector": { + "fields": [], + "name": "none", + "type": ".none" + }, + "created_at": "2022-04-29T13:24:00.011Z", + "created_by": { + "email": null, + "full_name": null, + "username": "elastic" + }, + "description": "test mttr", + "duration": 160, + "external_service": null, + "owner": "observabilityFixture", + "settings": { + "syncAlerts": true + }, + "status": "closed", + "tags": [], + "title": "test mttr", + "updated_at": "2022-04-29T13:24:44.448Z", + "updated_by": { + "email": null, + "full_name": null, + "username": "elastic" + } + }, + "coreMigrationVersion": "8.3.0", + "id": "cf948570-c7bf-11ec-9771-d5eef9232089", + "migrationVersion": { + "cases": "8.3.0" + }, + "references": [], + "type": "cases", + "updated_at": "2022-04-29T13:24:44.449Z", + "version": "WzE0NjgsMV0=" +} + +{ + "attributes": { + "closed_at": null, + "closed_by": null, + "connector": { + "fields": null, + "name": "none", + "type": ".none" + }, + "created_at": "2022-03-20T10:16:56.252Z", + "created_by": { + "email": "", + "full_name": "", + "username": "elastic" + }, + "description": "test 2", + "external_service": null, + "owner": "securitySolutionFixture", + "settings": { + "syncAlerts": false + }, + "status": "open", + "tags": [], + "title": "stack", + "updated_at": "2022-03-29T10:33:09.754Z", + "updated_by": { + "email": "", + "full_name": "", + "username": "elastic" + } + }, + "coreMigrationVersion": "8.3.0", + "id": "df948570-c7bf-11ec-9771-d5eef9232089", + "migrationVersion": { + "cases": "8.3.0" + }, + "references": [], + "type": "cases", + "updated_at": "2022-04-29T13:24:44.449Z", + "version": "WzE0NjgsMV0=" +} diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 244e69c43f8e3..7432c5e066a3d 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -28,6 +28,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont 'visualize', 'dashboard', 'timeToVisualize', + 'unifiedSearch', ]); return logWrapper('lensPage', log, { @@ -56,6 +57,8 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont fromTime = fromTime || PageObjects.timePicker.defaultStartTime; toTime = toTime || PageObjects.timePicker.defaultEndTime; await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + // give some time for the update button tooltip to close + await PageObjects.common.sleep(500); }, /** @@ -96,6 +99,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont return retry.try(async () => { await testSubjects.click(`visListingTitleLink-${title}`); await this.isLensPageOrFail(); + await PageObjects.unifiedSearch.closeTourPopoverByLocalStorage(); }); }, @@ -566,10 +570,11 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont // pressing Enter at this point may lead to auto-complete the queryInput with random stuff from the // dropdown which was not intended originally. // To close the Filter popover we need to move to the label input and then press Enter: - // solution is to press Tab 2 twice (first Tab will close the dropdown) instead of Enter to avoid + // solution is to press Tab 3 tims (first Tab will close the dropdown) instead of Enter to avoid // race condition with the dropdown await PageObjects.common.pressTabKey(); await PageObjects.common.pressTabKey(); + await PageObjects.common.pressTabKey(); // Now it is safe to press Enter as we're in the label input await PageObjects.common.pressEnterKey(); await PageObjects.common.sleep(1000); // give time for debounced components to rerender @@ -655,7 +660,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont }, async editDimensionLabel(label: string) { - await testSubjects.setValue('indexPattern-label-edit', label, { clearWithKeyboard: true }); + await testSubjects.setValue('column-label-edit', label, { clearWithKeyboard: true }); }, async editDimensionFormat(format: string) { const formatInput = await testSubjects.find('indexPattern-dimension-format'); @@ -837,7 +842,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont * Changes the index pattern in the data panel */ async switchDataPanelIndexPattern(name: string) { - await testSubjects.click('indexPattern-switch-link'); + await testSubjects.click('lns-dataView-switch-link'); await find.clickByCssSelector(`[title="${name}"]`); await PageObjects.header.waitUntilLoadingHasFinished(); }, @@ -855,7 +860,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont * Returns the current index pattern of the data panel */ async getDataPanelIndexPattern() { - return await (await testSubjects.find('indexPattern-switch-link')).getAttribute('title'); + return await (await testSubjects.find('lns-dataView-switch-link')).getAttribute('title'); }, /** @@ -906,7 +911,8 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont ); }, - async getCurrentChartDebugState() { + async getCurrentChartDebugState(visType: string) { + await this.waitForVisualization(visType); return await elasticChart.getChartDebugData('lnsWorkspace'); }, @@ -1127,6 +1133,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont await PageObjects.dashboard.switchToEditMode(); } await dashboardAddPanel.clickCreateNewLink(); + await PageObjects.unifiedSearch.closeTourPopoverByLocalStorage(); await this.goToTimeRange(); await this.configureDimension({ dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', @@ -1199,7 +1206,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont }, async clickAddField() { - await testSubjects.click('lnsIndexPatternActions'); + await testSubjects.click('lns-dataView-switch-link'); await testSubjects.existOrFail('indexPattern-add-field'); await testSubjects.click('indexPattern-add-field'); }, @@ -1370,9 +1377,9 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont }, async closeSettingsMenu() { - if (!(await this.settingsMenuOpen())) return; - - await testSubjects.click('lnsApp_settingsButton'); + if (await this.settingsMenuOpen()) { + await testSubjects.click('lnsApp_settingsButton'); + } }, async enableAutoApply() { diff --git a/x-pack/test/functional/page_objects/space_selector_page.ts b/x-pack/test/functional/page_objects/space_selector_page.ts index f60da33763240..9e395ce65e36e 100644 --- a/x-pack/test/functional/page_objects/space_selector_page.ts +++ b/x-pack/test/functional/page_objects/space_selector_page.ts @@ -48,7 +48,10 @@ export class SpaceSelectorPageObject extends FtrService { async openSpacesNav() { this.log.debug('openSpacesNav()'); - return await this.testSubjects.click('spacesNavSelector'); + return await this.retry.try(async () => { + await this.testSubjects.click('spacesNavSelector'); + await this.find.byCssSelector('#headerSpacesMenuContent'); + }); } async clickManageSpaces() { diff --git a/x-pack/test/functional/services/cases/common.ts b/x-pack/test/functional/services/cases/common.ts index 7722c32f79837..5ba0c4dbbaa57 100644 --- a/x-pack/test/functional/services/cases/common.ts +++ b/x-pack/test/functional/services/cases/common.ts @@ -7,12 +7,14 @@ import expect from '@kbn/expect'; import { CaseStatuses } from '@kbn/cases-plugin/common'; +import { CaseSeverity } from '@kbn/cases-plugin/common/api'; import { FtrProviderContext } from '../../ftr_provider_context'; export function CasesCommonServiceProvider({ getService, getPageObject }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const find = getService('find'); const header = getPageObject('header'); + const common = getPageObject('common'); return { /** @@ -58,5 +60,13 @@ export function CasesCommonServiceProvider({ getService, getPageObject }: FtrPro await label.click(); await this.assertRadioGroupValue(testSubject, value); }, + + async selectSeverity(severity: CaseSeverity) { + await common.clickAndValidate( + 'case-severity-selection', + `case-severity-selection-${severity}` + ); + await testSubjects.click(`case-severity-selection-${severity}`); + }, }; } diff --git a/x-pack/test/functional/services/cases/create.ts b/x-pack/test/functional/services/cases/create.ts index 5ed22ad51ad9f..536badeee56a6 100644 --- a/x-pack/test/functional/services/cases/create.ts +++ b/x-pack/test/functional/services/cases/create.ts @@ -5,10 +5,12 @@ * 2.0. */ +import { CaseSeverity } from '@kbn/cases-plugin/common/api'; import uuid from 'uuid'; import { FtrProviderContext } from '../../ftr_provider_context'; export function CasesCreateViewServiceProvider({ getService, getPageObject }: FtrProviderContext) { + const common = getPageObject('common'); const testSubjects = getService('testSubjects'); const find = getService('find'); const comboBox = getService('comboBox'); @@ -39,10 +41,12 @@ export function CasesCreateViewServiceProvider({ getService, getPageObject }: Ft title = 'test-' + uuid.v4(), description = 'desc' + uuid.v4(), tag = 'tagme', + severity = CaseSeverity.LOW, }: { title: string; description: string; tag: string; + severity: CaseSeverity; }) { // case name await testSubjects.setValue('input', title); @@ -54,6 +58,11 @@ export function CasesCreateViewServiceProvider({ getService, getPageObject }: Ft const descriptionArea = await find.byCssSelector('textarea.euiMarkdownEditorTextArea'); await descriptionArea.focus(); await descriptionArea.type(description); + await common.clickAndValidate( + 'case-severity-selection', + `case-severity-selection-${severity}` + ); + await testSubjects.click(`case-severity-selection-${severity}`); // save await testSubjects.click('create-case-submit'); diff --git a/x-pack/test/functional/services/cases/list.ts b/x-pack/test/functional/services/cases/list.ts index 651f52434e55f..f4d7103db0a61 100644 --- a/x-pack/test/functional/services/cases/list.ts +++ b/x-pack/test/functional/services/cases/list.ts @@ -7,6 +7,7 @@ import expect from '@kbn/expect'; import { CaseStatuses } from '@kbn/cases-plugin/common'; +import { CaseSeverityWithAll } from '@kbn/cases-plugin/common/ui'; import { WebElementWrapper } from '../../../../../test/functional/services/lib/web_element_wrapper'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -126,6 +127,11 @@ export function CasesTableServiceProvider({ getService, getPageObject }: FtrProv await testSubjects.click(`case-status-filter-${status}`); }, + async filterBySeverity(severity: CaseSeverityWithAll) { + await common.clickAndValidate('case-severity-filter', `case-severity-filter-${severity}`); + await testSubjects.click(`case-severity-filter-${severity}`); + }, + async filterByReporter(reporter: string) { await common.clickAndValidate( 'options-filter-popover-button-Reporter', diff --git a/x-pack/test/functional/services/ml/common_data_grid.ts b/x-pack/test/functional/services/ml/common_data_grid.ts index c48cf92107dab..444b01cce902f 100644 --- a/x-pack/test/functional/services/ml/common_data_grid.ts +++ b/x-pack/test/functional/services/ml/common_data_grid.ts @@ -8,8 +8,8 @@ import expect from '@kbn/expect'; import { chunk } from 'lodash'; import type { ProvidedType } from '@kbn/test'; +import { asyncForEachWithLimit } from '@kbn/std'; import type { FtrProviderContext } from '../../ftr_provider_context'; -import { asyncForEach } from '../../apps/ml/settings/common'; export interface SetValueOptions { clearWithKeyboard?: boolean; @@ -154,7 +154,7 @@ export function MachineLearningCommonDataGridProvider({ getService }: FtrProvide await find.byClassName('euiDataGrid__controlScroll') ).findAllByCssSelector('[role="switch"]'); - await asyncForEach(visibilityToggles, async (toggle) => { + await asyncForEachWithLimit(visibilityToggles, 1, async (toggle) => { const checked = (await toggle.getAttribute('aria-checked')) === 'true'; expect(checked).to.eql( expectedState, diff --git a/x-pack/test/functional/services/ml/dashboard_embeddables.ts b/x-pack/test/functional/services/ml/dashboard_embeddables.ts index 1eb4e25a4edd5..74268f74d19a2 100644 --- a/x-pack/test/functional/services/ml/dashboard_embeddables.ts +++ b/x-pack/test/functional/services/ml/dashboard_embeddables.ts @@ -117,7 +117,9 @@ export function MachineLearningDashboardEmbeddablesProvider( async selectDiscoverIndexPattern(indexPattern: string) { await retry.tryForTime(2 * 1000, async () => { await PageObjects.discover.selectIndexPattern(indexPattern); - const indexPatternTitle = await testSubjects.getVisibleText('indexPattern-switch-link'); + const indexPatternTitle = await testSubjects.getVisibleText( + 'discover-dataView-switch-link' + ); expect(indexPatternTitle).to.be(indexPattern); }); }, diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts index 88ef0fdf08c8d..77f1e34e67157 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts @@ -49,6 +49,7 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( const jobTypeAttribute = `mlAnalyticsCreation-${jobType}-option`; await testSubjects.click(jobTypeAttribute); await this.assertJobTypeSelection(jobTypeAttribute); + await headerPage.waitUntilLoadingHasFinished(); }, async assertAdvancedEditorSwitchExists() { @@ -127,29 +128,41 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( await testSubjects.existOrFail('mlAnalyticsCreationDataGridHistogramButton'); }, - async enableSourceDataPreviewHistogramCharts(expectedDefaultButtonState: boolean) { - await this.assertSourceDataPreviewHistogramChartButtonCheckState(expectedDefaultButtonState); - if (expectedDefaultButtonState === false) { + async enableSourceDataPreviewHistogramCharts(shouldBeEnabled: boolean) { + const isEnabled = await this.getSourceDataPreviewHistogramChartButtonCheckState(); + if (isEnabled !== shouldBeEnabled) { await testSubjects.click('mlAnalyticsCreationDataGridHistogramButton'); - await this.assertSourceDataPreviewHistogramChartButtonCheckState(true); + await this.assertSourceDataPreviewHistogramChartEnabled(shouldBeEnabled); } }, - async assertSourceDataPreviewHistogramChartButtonCheckState(expectedCheckState: boolean) { - const actualCheckState = + async assertSourceDataPreviewHistogramChartEnabled(shouldBeEnabled: boolean) { + const isEnabled = await this.getSourceDataPreviewHistogramChartButtonCheckState(); + expect(isEnabled).to.eql( + shouldBeEnabled, + `Source data preview histogram charts should be '${ + shouldBeEnabled ? 'enabled' : 'disabled' + }' (got '${isEnabled ? 'enabled' : 'disabled'}')` + ); + }, + + async getSourceDataPreviewHistogramChartButtonCheckState(): Promise { + return ( (await testSubjects.getAttribute( 'mlAnalyticsCreationDataGridHistogramButton', 'aria-pressed' - )) === 'true'; - expect(actualCheckState).to.eql( - expectedCheckState, - `Chart histogram button check state should be '${expectedCheckState}' (got '${actualCheckState}')` + )) === 'true' ); }, + async scrollSourceDataPreviewIntoView() { + await testSubjects.scrollIntoView('mlAnalyticsCreationDataGrid loaded'); + }, + async assertSourceDataPreviewHistogramCharts( expectedHistogramCharts: Array<{ chartAvailable: boolean; id: string; legend: string }> ) { + await this.scrollSourceDataPreviewIntoView(); // For each chart, get the content of each header cell and assert // the legend text and column id and if the chart should be present or not. await retry.tryForTime(10000, async () => { @@ -178,6 +191,17 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( }); }, + async enableAndAssertSourceDataPreviewHistogramCharts( + expectedHistogramCharts: Array<{ chartAvailable: boolean; id: string; legend: string }> + ) { + await retry.tryForTime(20 * 1000, async () => { + // turn histogram charts off and on before checking + await this.enableSourceDataPreviewHistogramCharts(false); + await this.enableSourceDataPreviewHistogramCharts(true); + await this.assertSourceDataPreviewHistogramCharts(expectedHistogramCharts); + }); + }, + async assertIncludeFieldsSelectionExists() { await testSubjects.existOrFail('mlAnalyticsCreateJobWizardIncludesTable', { timeout: 8000 }); diff --git a/x-pack/test/functional/services/observability/alerts/common.ts b/x-pack/test/functional/services/observability/alerts/common.ts index fc81cce20426d..26312b81b7b3b 100644 --- a/x-pack/test/functional/services/observability/alerts/common.ts +++ b/x-pack/test/functional/services/observability/alerts/common.ts @@ -131,12 +131,14 @@ export function ObservabilityAlertsCommonProvider({ }; // Flyout - const getOpenFlyoutButton = async () => { - return await testSubjects.find('openFlyoutButton'); + const getViewAlertDetailsFlyoutButton = async () => { + await openActionsMenuForRow(0); + + return await testSubjects.find('viewAlertDetails'); }; const openAlertsFlyout = async () => { - await (await getOpenFlyoutButton()).click(); + await (await getViewAlertDetailsFlyoutButton()).click(); await retry.waitFor( 'flyout open', async () => await testSubjects.exists(ALERTS_FLYOUT_SELECTOR, { timeout: 2500 }) diff --git a/x-pack/test/functional/services/transform/discover.ts b/x-pack/test/functional/services/transform/discover.ts index a98f7e5ae9890..d96ab079043a0 100644 --- a/x-pack/test/functional/services/transform/discover.ts +++ b/x-pack/test/functional/services/transform/discover.ts @@ -28,7 +28,7 @@ export function TransformDiscoverProvider({ getService }: FtrProviderContext) { async assertNoResults(expectedDestinationIndex: string) { // Discover should use the destination index pattern const actualIndexPatternSwitchLinkText = await ( - await testSubjects.find('indexPattern-switch-link') + await testSubjects.find('discover-dataView-switch-link') ).getVisibleText(); expect(actualIndexPatternSwitchLinkText).to.eql( expectedDestinationIndex, diff --git a/x-pack/test/functional/services/transform/wizard.ts b/x-pack/test/functional/services/transform/wizard.ts index 3c75db1c4c366..bc28120400895 100644 --- a/x-pack/test/functional/services/transform/wizard.ts +++ b/x-pack/test/functional/services/transform/wizard.ts @@ -25,7 +25,7 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi const comboBox = getService('comboBox'); const retry = getService('retry'); const ml = getService('ml'); - const PageObjects = getPageObjects(['discover', 'timePicker']); + const PageObjects = getPageObjects(['discover', 'timePicker', 'unifiedSearch']); return { async clickNextButton() { @@ -911,6 +911,7 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi await testSubjects.click('transformWizardCardDiscover'); await PageObjects.discover.isDiscoverAppOnScreen(); }); + await PageObjects.unifiedSearch.closeTourPopoverByLocalStorage(); }, async setDiscoverTimeRange(fromTime: string, toTime: string) { diff --git a/x-pack/test/functional_basic/apps/ml/data_visualizer/index_data_visualizer_actions_panel.ts b/x-pack/test/functional_basic/apps/ml/data_visualizer/index_data_visualizer_actions_panel.ts index a2a135b8cef0c..2fcdf957f8909 100644 --- a/x-pack/test/functional_basic/apps/ml/data_visualizer/index_data_visualizer_actions_panel.ts +++ b/x-pack/test/functional_basic/apps/ml/data_visualizer/index_data_visualizer_actions_panel.ts @@ -12,7 +12,7 @@ export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); describe('index based actions panel on basic license', function () { - this.tags(['mlqa']); + this.tags(['ml']); const indexPatternName = 'ft_farequote'; const savedSearch = 'ft_farequote_kuery'; diff --git a/x-pack/test/functional_basic/apps/ml/index.ts b/x-pack/test/functional_basic/apps/ml/index.ts index af2fdc8c45f29..dbdab2cc0a4b2 100644 --- a/x-pack/test/functional_basic/apps/ml/index.ts +++ b/x-pack/test/functional_basic/apps/ml/index.ts @@ -12,7 +12,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { const ml = getService('ml'); describe('machine learning basic license', function () { - this.tags(['ciGroup14', 'skipFirefox', 'mlqa']); + this.tags(['skipFirefox', 'ml']); before(async () => { await ml.securityCommon.createMlRoles(); diff --git a/x-pack/test/functional_basic/apps/ml/permissions/full_ml_access.ts b/x-pack/test/functional_basic/apps/ml/permissions/full_ml_access.ts index 6db82801a5bdd..b90b97ca87a57 100644 --- a/x-pack/test/functional_basic/apps/ml/permissions/full_ml_access.ts +++ b/x-pack/test/functional_basic/apps/ml/permissions/full_ml_access.ts @@ -5,8 +5,6 @@ * 2.0. */ -import path from 'path'; - import { FtrProviderContext } from '../../../ftr_provider_context'; import { USER } from '../../../../functional/services/ml/security_common'; @@ -20,26 +18,14 @@ export default function ({ getService }: FtrProviderContext) { { user: USER.ML_POWERUSER_SPACES, discoverAvailable: false }, ]; - // FLAKY: https://github.com/elastic/kibana/issues/124413 - // FLAKY: https://github.com/elastic/kibana/issues/122838 - describe.skip('for user with full ML access', function () { + describe('for user with full ML access', function () { for (const testUser of testUsers) { describe(`(${testUser.user})`, function () { const ecIndexPattern = 'ft_module_sample_ecommerce'; const ecExpectedTotalCount = '287'; - const uploadFilePath = path.join( - __dirname, - '..', - '..', - '..', - '..', - 'functional', - 'apps', - 'ml', - 'data_visualizer', - 'files_to_import', - 'artificial_server_log' + const uploadFilePath = require.resolve( + '../../../../functional/apps/ml/data_visualizer/files_to_import/artificial_server_log' ); const expectedUploadFileTitle = 'artificial_server_log'; diff --git a/x-pack/test/functional_basic/apps/ml/permissions/read_ml_access.ts b/x-pack/test/functional_basic/apps/ml/permissions/read_ml_access.ts index c1b13d6dc1f11..fc293defceb86 100644 --- a/x-pack/test/functional_basic/apps/ml/permissions/read_ml_access.ts +++ b/x-pack/test/functional_basic/apps/ml/permissions/read_ml_access.ts @@ -5,8 +5,6 @@ * 2.0. */ -import path from 'path'; - import { FtrProviderContext } from '../../../ftr_provider_context'; import { USER } from '../../../../functional/services/ml/security_common'; @@ -26,18 +24,8 @@ export default function ({ getService }: FtrProviderContext) { const ecIndexPattern = 'ft_module_sample_ecommerce'; const ecExpectedTotalCount = '287'; - const uploadFilePath = path.join( - __dirname, - '..', - '..', - '..', - '..', - 'functional', - 'apps', - 'ml', - 'data_visualizer', - 'files_to_import', - 'artificial_server_log' + const uploadFilePath = require.resolve( + '../../../../functional/apps/ml/data_visualizer/files_to_import/artificial_server_log' ); const expectedUploadFileTitle = 'artificial_server_log'; diff --git a/x-pack/test/functional_basic/config.ts b/x-pack/test/functional_basic/config.ts index e1dac88436e4c..f35ece0ce5d16 100644 --- a/x-pack/test/functional_basic/config.ts +++ b/x-pack/test/functional_basic/config.ts @@ -8,7 +8,9 @@ import { FtrConfigProviderContext } from '@kbn/test'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xpackFunctionalConfig = await readConfigFile(require.resolve('../functional/config.js')); + const xpackFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); return { // default to the xpack functional config diff --git a/x-pack/test/functional_cors/config.ts b/x-pack/test/functional_cors/config.ts index 738285b4ff40f..364be1383ae94 100644 --- a/x-pack/test/functional_cors/config.ts +++ b/x-pack/test/functional_cors/config.ts @@ -16,7 +16,9 @@ const pluginPort = process.env.TEST_CORS_SERVER_PORT : 5699; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const kibanaFunctionalConfig = await readConfigFile(require.resolve('../functional/config.js')); + const kibanaFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); const corsTestPlugin = Path.resolve(__dirname, './plugins/kibana_cors_test'); diff --git a/x-pack/test/functional_cors/tests/index.ts b/x-pack/test/functional_cors/tests/index.ts index 424dac86c4f1a..3ca455eccd339 100644 --- a/x-pack/test/functional_cors/tests/index.ts +++ b/x-pack/test/functional_cors/tests/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('Kibana cors', function () { - this.tags('ciGroup12'); loadTestFile(require.resolve('./cors')); }); } diff --git a/x-pack/test/functional_embedded/config.firefox.ts b/x-pack/test/functional_embedded/config.firefox.ts deleted file mode 100644 index 49359d37673de..0000000000000 --- a/x-pack/test/functional_embedded/config.firefox.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrConfigProviderContext } from '@kbn/test'; - -export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const chromeConfig = await readConfigFile(require.resolve('./config')); - - return { - ...chromeConfig.getAll(), - - browser: { - type: 'firefox', - acceptInsecureCerts: true, - }, - - suiteTags: { - exclude: ['skipFirefox'], - }, - - junit: { - reportName: 'Firefox Kibana Embedded in iframe with X-Pack Security', - }, - }; -} diff --git a/x-pack/test/functional_embedded/config.ts b/x-pack/test/functional_embedded/config.ts index 868d53ee17ee9..cdbbffea0eab9 100644 --- a/x-pack/test/functional_embedded/config.ts +++ b/x-pack/test/functional_embedded/config.ts @@ -12,7 +12,9 @@ import { FtrConfigProviderContext } from '@kbn/test'; import { pageObjects } from '../functional/page_objects'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const kibanaFunctionalConfig = await readConfigFile(require.resolve('../functional/config.js')); + const kibanaFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); const iframeEmbeddedPlugin = resolve(__dirname, './plugins/iframe_embedded'); diff --git a/x-pack/test/functional_embedded/tests/index.ts b/x-pack/test/functional_embedded/tests/index.ts index aa210aff19728..1c3f01febd6d4 100644 --- a/x-pack/test/functional_embedded/tests/index.ts +++ b/x-pack/test/functional_embedded/tests/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('Kibana embedded', function () { - this.tags('ciGroup2'); loadTestFile(require.resolve('./iframe_embedded')); }); } diff --git a/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/index.ts b/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/index.ts index 653f0842ef1bb..dda2e20745394 100644 --- a/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/index.ts +++ b/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('Enterprise Search', function () { - this.tags('ciGroup10'); - loadTestFile(require.resolve('./app_search/setup_guide')); loadTestFile(require.resolve('./workplace_search/setup_guide')); }); diff --git a/x-pack/test/functional_enterprise_search/base_config.ts b/x-pack/test/functional_enterprise_search/base_config.ts index 2c21ccf5c5c39..8f72e1ebd6503 100644 --- a/x-pack/test/functional_enterprise_search/base_config.ts +++ b/x-pack/test/functional_enterprise_search/base_config.ts @@ -10,7 +10,9 @@ import { pageObjects } from './page_objects'; import { services } from './services'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xPackFunctionalConfig = await readConfigFile(require.resolve('../functional/config')); + const xPackFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); return { // default to the xpack functional config diff --git a/x-pack/test/functional_execution_context/config.ts b/x-pack/test/functional_execution_context/config.ts index 6169df6adcef3..caf88769b6a06 100644 --- a/x-pack/test/functional_execution_context/config.ts +++ b/x-pack/test/functional_execution_context/config.ts @@ -12,7 +12,7 @@ import { logFilePath } from './test_utils'; const alertTestPlugin = Path.resolve(__dirname, './fixtures/plugins/alerts'); export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); const servers = { ...functionalConfig.get('servers'), diff --git a/x-pack/test/functional_execution_context/tests/index.ts b/x-pack/test/functional_execution_context/tests/index.ts index c092be9bd8bdb..2c34783a9bae3 100644 --- a/x-pack/test/functional_execution_context/tests/index.ts +++ b/x-pack/test/functional_execution_context/tests/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('Execution context', function () { - this.tags('ciGroup1'); loadTestFile(require.resolve('./browser')); loadTestFile(require.resolve('./server')); loadTestFile(require.resolve('./log_correlation')); diff --git a/x-pack/test/functional_synthetics/apps/uptime/index.ts b/x-pack/test/functional_synthetics/apps/uptime/index.ts index 925731c769bfc..64a9da5c30ea3 100644 --- a/x-pack/test/functional_synthetics/apps/uptime/index.ts +++ b/x-pack/test/functional_synthetics/apps/uptime/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile, getService }: FtrProviderContext) => { describe('Uptime app', function () { - this.tags('ciGroup8'); describe('with generated data', () => { loadTestFile(require.resolve('./synthetics_integration')); }); diff --git a/x-pack/test/functional_synthetics/config.js b/x-pack/test/functional_synthetics/config.js index 20488e3c52b2c..f5db09a5d60d9 100644 --- a/x-pack/test/functional_synthetics/config.js +++ b/x-pack/test/functional_synthetics/config.js @@ -28,7 +28,7 @@ export default async function ({ readConfigFile }) { require.resolve('../../../test/common/config.js') ); const kibanaFunctionalConfig = await readConfigFile( - require.resolve('../../../test/functional/config.js') + require.resolve('../../../test/functional/config.base.js') ); // mount the config file for the package registry as well as diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/create_case_form.ts b/x-pack/test/functional_with_es_ssl/apps/cases/create_case_form.ts index c5aed361aba3e..c4a7fad8224ea 100644 --- a/x-pack/test/functional_with_es_ssl/apps/cases/create_case_form.ts +++ b/x-pack/test/functional_with_es_ssl/apps/cases/create_case_form.ts @@ -7,6 +7,7 @@ import expect from '@kbn/expect'; import uuid from 'uuid'; +import { CaseSeverity } from '@kbn/cases-plugin/common/api'; import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ getService }: FtrProviderContext) => { @@ -30,6 +31,7 @@ export default ({ getService }: FtrProviderContext) => { title: caseTitle, description: 'test description', tag: 'tagme', + severity: CaseSeverity.HIGH, }); // validate title diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/index.ts b/x-pack/test/functional_with_es_ssl/apps/cases/index.ts index 53d2c2d9767f1..04cceba32c8a7 100644 --- a/x-pack/test/functional_with_es_ssl/apps/cases/index.ts +++ b/x-pack/test/functional_with_es_ssl/apps/cases/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('Cases', function () { - this.tags('ciGroup27'); loadTestFile(require.resolve('./create_case_form')); loadTestFile(require.resolve('./view_case')); loadTestFile(require.resolve('./list_view')); diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/list_view.ts b/x-pack/test/functional_with_es_ssl/apps/cases/list_view.ts index c64f1514b7c45..b05763cfcf079 100644 --- a/x-pack/test/functional_with_es_ssl/apps/cases/list_view.ts +++ b/x-pack/test/functional_with_es_ssl/apps/cases/list_view.ts @@ -7,6 +7,8 @@ import expect from '@kbn/expect'; import { CaseStatuses } from '@kbn/cases-plugin/common'; +import { CaseSeverity } from '@kbn/cases-plugin/common/api'; +import { SeverityAll } from '@kbn/cases-plugin/common/ui'; import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ getPageObject, getService }: FtrProviderContext) => { @@ -143,6 +145,51 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); }); + describe('severity filtering', () => { + before(async () => { + await cases.api.createCase({ severity: CaseSeverity.LOW }); + await cases.api.createCase({ severity: CaseSeverity.LOW }); + await cases.api.createCase({ severity: CaseSeverity.HIGH }); + await cases.api.createCase({ severity: CaseSeverity.HIGH }); + await cases.api.createCase({ severity: CaseSeverity.CRITICAL }); + await header.waitUntilLoadingHasFinished(); + await cases.casesTable.waitForCasesToBeListed(); + }); + beforeEach(async () => { + /** + * There is no easy way to clear the filtering. + * Refreshing the page seems to be easier. + */ + await cases.navigation.navigateToApp(); + }); + + after(async () => { + await cases.api.deleteAllCases(); + await cases.casesTable.waitForCasesToBeDeleted(); + }); + + it('filters cases by severity', async () => { + // by default filter by all + await cases.casesTable.validateCasesTableHasNthRows(5); + + // low + await cases.casesTable.filterBySeverity(CaseSeverity.LOW); + await cases.casesTable.validateCasesTableHasNthRows(2); + + // high + await cases.casesTable.filterBySeverity(CaseSeverity.HIGH); + await cases.casesTable.validateCasesTableHasNthRows(2); + + // critical + await cases.casesTable.filterBySeverity(CaseSeverity.CRITICAL); + await cases.casesTable.validateCasesTableHasNthRows(1); + + // back to all + await cases.casesTable.filterBySeverity(SeverityAll); + await cases.casesTable.validateCasesTableHasNthRows(5); + }); + }); + describe('pagination', () => { before(async () => { await cases.api.createNthRandomCases(8); diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/view_case.ts b/x-pack/test/functional_with_es_ssl/apps/cases/view_case.ts index a175e10fb7d18..9aaf523de6638 100644 --- a/x-pack/test/functional_with_es_ssl/apps/cases/view_case.ts +++ b/x-pack/test/functional_with_es_ssl/apps/cases/view_case.ts @@ -8,6 +8,7 @@ import expect from '@kbn/expect'; import uuid from 'uuid'; import { CaseStatuses } from '@kbn/cases-plugin/common'; +import { CaseSeverity } from '@kbn/cases-plugin/common/api'; import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ getPageObject, getService }: FtrProviderContext) => { @@ -184,9 +185,35 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { await common.clickAndValidate('property-actions-ellipses', 'property-actions-trash'); await common.clickAndValidate('property-actions-trash', 'confirmModalConfirmButton'); await testSubjects.click('confirmModalConfirmButton'); - await testSubjects.existOrFail('cases-all-title', { timeout: 2000 }); + await header.waitUntilLoadingHasFinished(); await cases.casesTable.validateCasesTableHasNthRows(0); }); }); + + describe('Severity field', () => { + before(async () => { + await cases.navigation.navigateToApp(); + await cases.api.createNthRandomCases(1); + await cases.casesTable.waitForCasesToBeListed(); + await cases.casesTable.goToFirstListedCase(); + await header.waitUntilLoadingHasFinished(); + }); + + after(async () => { + await cases.api.deleteAllCases(); + }); + + it('shows the severity field on the sidebar', async () => { + await testSubjects.existOrFail('case-severity-selection'); + }); + it('changes the severity level from the selector', async () => { + await cases.common.selectSeverity(CaseSeverity.MEDIUM); + await header.waitUntilLoadingHasFinished(); + await testSubjects.existOrFail('case-severity-selection-' + CaseSeverity.MEDIUM); + + // validate user action + await find.byCssSelector('[data-test-subj*="severity-update-action"]'); + }); + }); }); }; diff --git a/x-pack/test/functional_with_es_ssl/apps/discover/index.ts b/x-pack/test/functional_with_es_ssl/apps/discover/index.ts index 708da2f02da74..f876140dbc92a 100644 --- a/x-pack/test/functional_with_es_ssl/apps/discover/index.ts +++ b/x-pack/test/functional_with_es_ssl/apps/discover/index.ts @@ -8,7 +8,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile, getService }: FtrProviderContext) => { describe('Discover alerting', function () { - this.tags('ciGroup6'); loadTestFile(require.resolve('./search_source_alert')); }); }; diff --git a/x-pack/test/functional_with_es_ssl/apps/ml/alert_flyout.ts b/x-pack/test/functional_with_es_ssl/apps/ml/alert_flyout.ts index 9a486d4983d9c..4676004d9eb89 100644 --- a/x-pack/test/functional_with_es_ssl/apps/ml/alert_flyout.ts +++ b/x-pack/test/functional_with_es_ssl/apps/ml/alert_flyout.ts @@ -68,8 +68,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { let testJobId = ''; describe('anomaly detection alert', function () { - this.tags('ciGroup13'); - before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); await ml.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); diff --git a/x-pack/test/functional_with_es_ssl/apps/ml/index.ts b/x-pack/test/functional_with_es_ssl/apps/ml/index.ts index fac9e46dcb65b..942416c73b357 100644 --- a/x-pack/test/functional_with_es_ssl/apps/ml/index.ts +++ b/x-pack/test/functional_with_es_ssl/apps/ml/index.ts @@ -12,7 +12,7 @@ export default ({ loadTestFile, getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); describe('ML app', function () { - this.tags(['mlqa', 'skipFirefox']); + this.tags(['ml', 'skipFirefox']); before(async () => { await ml.securityCommon.createMlRoles(); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts index 581edecc3d8bc..a036c25e3d657 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts @@ -14,6 +14,7 @@ import { createFailingAlert, disableAlert, muteAlert, + snoozeAlert, } from '../../lib/alert_api_actions'; import { ObjectRemover } from '../../lib/object_remover'; import { generateUniqueKey } from '../../lib/get_test_data'; @@ -31,6 +32,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { } describe('rules list', function () { + const assertRulesLength = async (length: number) => { + return await retry.try(async () => { + const rules = await pageObjects.triggersActionsUI.getAlertsList(); + expect(rules.length).to.equal(length); + }); + }; + before(async () => { await pageObjects.common.navigateToApp('triggersActions'); await testSubjects.click('rulesTab'); @@ -462,8 +470,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const refreshResults = await pageObjects.triggersActionsUI.getAlertsListWithStatus(); expect(refreshResults.map((item: any) => item.status).sort()).to.eql(['Error', 'Ok']); }); - await testSubjects.click('ruleStatusFilterButton'); - await testSubjects.click('ruleStatuserrorFilerOption'); // select Error status filter + await testSubjects.click('ruleExecutionStatusFilterButton'); + await testSubjects.click('ruleExecutionStatuserrorFilterOption'); // select Error status filter await retry.try(async () => { const filterErrorOnlyResults = await pageObjects.triggersActionsUI.getAlertsListWithStatus(); @@ -600,5 +608,125 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.missingOrFail('centerJustifiedSpinner'); }); + + it('should filter alerts by the rule status', async () => { + // Enabled alert + await createAlert({ + supertest, + objectRemover, + }); + const disabledAlert = await createAlert({ + supertest, + objectRemover, + }); + const snoozedAlert = await createAlert({ + supertest, + objectRemover, + }); + + await disableAlert({ + supertest, + alertId: disabledAlert.id, + }); + await snoozeAlert({ + supertest, + alertId: snoozedAlert.id, + }); + + await refreshAlertsList(); + await assertRulesLength(3); + + // Select enabled + await testSubjects.click('ruleStatusFilterButton'); + await testSubjects.click('ruleStatusFilterOption-enabled'); + await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); + await assertRulesLength(1); + + // Select disabled + await testSubjects.click('ruleStatusFilterOption-enabled'); + await testSubjects.click('ruleStatusFilterOption-disabled'); + await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); + await assertRulesLength(1); + + // Select snoozed + await testSubjects.click('ruleStatusFilterOption-disabled'); + await testSubjects.click('ruleStatusFilterOption-snoozed'); + await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); + await assertRulesLength(1); + + // Select disabled and snoozed + await testSubjects.click('ruleStatusFilterOption-disabled'); + await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); + await assertRulesLength(2); + + // Select all 3 + await testSubjects.click('ruleStatusFilterOption-enabled'); + await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); + await assertRulesLength(3); + }); + + it('should filter alerts by the tag', async () => { + await createAlert({ + supertest, + objectRemover, + overwrites: { + tags: ['a'], + }, + }); + await createAlert({ + supertest, + objectRemover, + overwrites: { + tags: ['b'], + }, + }); + await createAlert({ + supertest, + objectRemover, + overwrites: { + tags: ['a', 'b'], + }, + }); + await createAlert({ + supertest, + objectRemover, + overwrites: { + tags: ['b', 'c'], + }, + }); + await createAlert({ + supertest, + objectRemover, + overwrites: { + tags: ['c'], + }, + }); + + await refreshAlertsList(); + await testSubjects.click('ruleTagFilter'); + + // Select a -> selected: a + await testSubjects.click('ruleTagFilterOption-a'); + await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); + await assertRulesLength(2); + + // Unselect a -> selected: none + await testSubjects.click('ruleTagFilterOption-a'); + await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); + await assertRulesLength(5); + + // Select a, b -> selected: a, b + await testSubjects.click('ruleTagFilterOption-a'); + await testSubjects.click('ruleTagFilterOption-b'); + await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); + await assertRulesLength(4); + + // Unselect a, b, select c -> selected: c + await testSubjects.click('ruleTagFilterOption-a'); + await testSubjects.click('ruleTagFilterOption-b'); + await testSubjects.click('ruleTagFilterOption-c'); + await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); + await assertRulesLength(2); + }); }); }; diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_table.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_table.ts index f0797ffd54d91..56026093c88dd 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_table.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_table.ts @@ -30,27 +30,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await waitTableIsLoaded(); - const euiDataGridRows = await find.allByCssSelector('.euiDataGridRow'); - const rows = []; - for (const euiDataGridRow of euiDataGridRows) { - const $ = await euiDataGridRow.parseDomContent(); - rows.push({ - status: $.findTestSubjects('dataGridRowCell') - .find('[data-gridcell-column-id="event.action"] .euiDataGridRowCell__truncate') - .text(), - lastUpdated: $.findTestSubjects('dataGridRowCell') - .find('[data-gridcell-column-id="@timestamp"] .euiDataGridRowCell__truncate') - .text(), - duration: $.findTestSubjects('dataGridRowCell') - .find( - '[data-gridcell-column-id="kibana.alert.duration.us"] .euiDataGridRowCell__truncate' - ) - .text(), - reason: $.findTestSubjects('dataGridRowCell') - .find('[data-gridcell-column-id="kibana.alert.reason"] .euiDataGridRowCell__truncate') - .text(), - }); - } + const rows = await getRows(); expect(rows.length).to.be(10); expect(rows[0].status).to.be('active'); expect(rows[0].lastUpdated).to.be('2021-10-19T15:20:38.749Z'); @@ -76,27 +56,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await waitTableIsLoaded(); - const euiDataGridRows = await find.allByCssSelector('.euiDataGridRow'); - const rows = []; - for (const euiDataGridRow of euiDataGridRows) { - const $ = await euiDataGridRow.parseDomContent(); - rows.push({ - status: $.findTestSubjects('dataGridRowCell') - .find('[data-gridcell-column-id="event.action"] .euiDataGridRowCell__truncate') - .text(), - lastUpdated: $.findTestSubjects('dataGridRowCell') - .find('[data-gridcell-column-id="@timestamp"] .euiDataGridRowCell__truncate') - .text(), - duration: $.findTestSubjects('dataGridRowCell') - .find( - '[data-gridcell-column-id="kibana.alert.duration.us"] .euiDataGridRowCell__truncate' - ) - .text(), - reason: $.findTestSubjects('dataGridRowCell') - .find('[data-gridcell-column-id="kibana.alert.reason"] .euiDataGridRowCell__truncate') - .text(), - }); - } + const rows = await getRows(); expect(rows.length).to.be(10); expect(rows[0].status).to.be('open'); expect(rows[0].lastUpdated).to.be('2021-10-19T15:20:26.974Z'); @@ -117,6 +77,81 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await waitTableIsLoaded(); + const rows = await getRows(); + expect(rows.length).to.be(10); + expect(rows[0].status).to.be('active'); + expect(rows[0].lastUpdated).to.be('2021-10-19T15:20:26.974Z'); + expect(rows[0].duration).to.be('63291000'); + expect(rows[0].reason).to.be( + 'CPU usage is greater than a threshold of 40 (current value is 40.8%) for gke-edge-oblt-default-pool-350b44de-3v4p' + ); + }); + + // This keeps failing in CI because the next button is not clickable + // Revisit this once we change the UI around based on feedback + /* + fail: Actions and Triggers app Alerts table should open a flyout and paginate through the flyout + │ Error: retry.try timeout: ElementClickInterceptedError: element click intercepted: Element ... is not clickable at point (1564, 795). Other element would receive the click:
...
+ */ + // it('should open a flyout and paginate through the flyout', async () => { + // await PageObjects.common.navigateToUrlWithBrowserHistory('triggersActions', '/alerts'); + // await waitTableIsLoaded(); + // await testSubjects.click('expandColumnCellOpenFlyoutButton-0'); + // await waitFlyoutOpen(); + // await waitFlyoutIsLoaded(); + + // expect(await testSubjects.getVisibleText('alertsFlyoutName')).to.be( + // 'APM Failed Transaction Rate (one)' + // ); + // expect(await testSubjects.getVisibleText('alertsFlyoutReason')).to.be( + // 'Failed transactions rate is greater than 5.0% (current value is 31%) for elastic-co-frontend' + // ); + + // await testSubjects.click('pagination-button-next'); + + // expect(await testSubjects.getVisibleText('alertsFlyoutName')).to.be( + // 'APM Failed Transaction Rate (one)' + // ); + // expect(await testSubjects.getVisibleText('alertsFlyoutReason')).to.be( + // 'Failed transactions rate is greater than 5.0% (current value is 35%) for opbeans-python' + // ); + + // await testSubjects.click('pagination-button-previous'); + // await testSubjects.click('pagination-button-previous'); + + // await waitTableIsLoaded(); + + // const rows = await getRows(); + // expect(rows[0].status).to.be('close'); + // expect(rows[0].lastUpdated).to.be('2021-10-19T14:55:14.503Z'); + // expect(rows[0].duration).to.be('252002000'); + // expect(rows[0].reason).to.be( + // 'CPU usage is greater than a threshold of 40 (current value is 56.7%) for gke-edge-oblt-default-pool-350b44de-c3dd' + // ); + // }); + + async function waitTableIsLoaded() { + return await retry.try(async () => { + const exists = await testSubjects.exists('internalAlertsPageLoading'); + if (exists) throw new Error('Still loading...'); + }); + } + + // async function waitFlyoutOpen() { + // return await retry.try(async () => { + // const exists = await testSubjects.exists('alertsFlyout'); + // if (!exists) throw new Error('Still loading...'); + // }); + // } + + // async function waitFlyoutIsLoaded() { + // return await retry.try(async () => { + // const exists = await testSubjects.exists('alertsFlyoutLoading'); + // if (exists) throw new Error('Still loading...'); + // }); + // } + + async function getRows() { const euiDataGridRows = await find.allByCssSelector('.euiDataGridRow'); const rows = []; for (const euiDataGridRow of euiDataGridRows) { @@ -138,20 +173,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { .text(), }); } - expect(rows.length).to.be(10); - expect(rows[0].status).to.be('active'); - expect(rows[0].lastUpdated).to.be('2021-10-19T15:20:26.974Z'); - expect(rows[0].duration).to.be('63291000'); - expect(rows[0].reason).to.be( - 'CPU usage is greater than a threshold of 40 (current value is 40.8%) for gke-edge-oblt-default-pool-350b44de-3v4p' - ); - }); - - async function waitTableIsLoaded() { - return await retry.try(async () => { - const exists = await testSubjects.exists('internalAlertsPageLoading'); - if (exists) throw new Error('Still loading...'); - }); + return rows; } }); }; diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts index 1127a7423a3aa..56dfa17ef6268 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts @@ -168,6 +168,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const ruleType = await pageObjects.ruleDetailsUI.getRuleType(); expect(ruleType).to.be(`Always Firing`); + const owner = await pageObjects.ruleDetailsUI.getAPIKeyOwner(); + expect(owner).to.be('elastic'); + const { connectorType } = await pageObjects.ruleDetailsUI.getActionsLabels(); expect(connectorType).to.be(`Slack`); }); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/index.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/index.ts index 179cd0c7ab8e9..3b2803e17e184 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/index.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile, getService }: FtrProviderContext) => { describe('Actions and Triggers app', function () { - this.tags('ciGroup10'); loadTestFile(require.resolve('./home_page')); loadTestFile(require.resolve('./alerts_list')); loadTestFile(require.resolve('./alert_create_flyout')); @@ -17,5 +16,8 @@ export default ({ loadTestFile, getService }: FtrProviderContext) => { loadTestFile(require.resolve('./connectors')); loadTestFile(require.resolve('./alerts_table')); loadTestFile(require.resolve('./rule_status_dropdown')); + loadTestFile(require.resolve('./rule_tag_filter')); + loadTestFile(require.resolve('./rule_status_filter')); + loadTestFile(require.resolve('./rule_tag_badge')); }); }; diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rule_status_filter.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rule_status_filter.ts new file mode 100644 index 0000000000000..0afdc932b0289 --- /dev/null +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rule_status_filter.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']); + const esArchiver = getService('esArchiver'); + + describe('Rule status filter', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); + await PageObjects.common.navigateToUrlWithBrowserHistory( + 'triggersActions', + '/__components_sandbox' + ); + }); + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); + }); + + it('should load from the shareable lazy loader', async () => { + await testSubjects.find('ruleStatusFilter'); + const exists = await testSubjects.exists('ruleStatusFilter'); + expect(exists).to.be(true); + }); + + it('should allow rule statuses to be filtered', async () => { + const ruleStatusFilter = await testSubjects.find('ruleStatusFilter'); + let badge = await ruleStatusFilter.findByCssSelector('.euiFilterButton__notification'); + expect(await badge.getVisibleText()).to.be('0'); + + await testSubjects.click('ruleStatusFilter'); + await testSubjects.click('ruleStatusFilterOption-enabled'); + + badge = await ruleStatusFilter.findByCssSelector('.euiFilterButton__notification'); + expect(await badge.getVisibleText()).to.be('1'); + + await testSubjects.click('ruleStatusFilterOption-disabled'); + + badge = await ruleStatusFilter.findByCssSelector('.euiFilterButton__notification'); + expect(await badge.getVisibleText()).to.be('2'); + + await testSubjects.click('ruleStatusFilterOption-enabled'); + expect(await badge.getVisibleText()).to.be('1'); + }); + }); +}; diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rule_tag_badge.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rule_tag_badge.ts new file mode 100644 index 0000000000000..0743896fbcbdb --- /dev/null +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rule_tag_badge.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 { FtrProviderContext } from '../../ftr_provider_context'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']); + const esArchiver = getService('esArchiver'); + + describe('Rule tag badge', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); + await PageObjects.common.navigateToUrlWithBrowserHistory( + 'triggersActions', + '/__components_sandbox' + ); + }); + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); + }); + + it('should load from the shareable lazy loader', async () => { + await testSubjects.find('ruleTagBadge'); + const exists = await testSubjects.exists('ruleTagBadge'); + expect(exists).to.be(true); + }); + + it('should open and display tags', async () => { + await testSubjects.click('ruleTagBadge'); + expect(await testSubjects.exists('ruleTagBadgeItem-tag1')).to.be(true); + expect(await testSubjects.exists('ruleTagBadgeItem-tag2')).to.be(true); + expect(await testSubjects.exists('ruleTagBadgeItem-tag3')).to.be(true); + expect(await testSubjects.exists('ruleTagBadgeItem-tag4')).to.be(true); + }); + }); +}; diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rule_tag_filter.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rule_tag_filter.ts new file mode 100644 index 0000000000000..77d57e2819db5 --- /dev/null +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rule_tag_filter.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const testSubjects = getService('testSubjects'); + const find = getService('find'); + const PageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']); + const esArchiver = getService('esArchiver'); + + describe('Rule tag filter', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); + await PageObjects.common.navigateToUrlWithBrowserHistory( + 'triggersActions', + '/__components_sandbox' + ); + }); + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); + }); + + it('shoud load from shareable lazy loader', async () => { + await testSubjects.find('ruleTagFilter'); + const exists = await testSubjects.exists('ruleTagFilter'); + expect(exists).to.be(true); + }); + + it('should allow tag filters to be selected', async () => { + let badge = await find.byCssSelector('.euiFilterButton__notification'); + expect(await badge.getVisibleText()).to.be('0'); + + await testSubjects.click('ruleTagFilter'); + await testSubjects.click('ruleTagFilterOption-tag1'); + + badge = await find.byCssSelector('.euiFilterButton__notification'); + expect(await badge.getVisibleText()).to.be('1'); + + await testSubjects.click('ruleTagFilterOption-tag2'); + + badge = await find.byCssSelector('.euiFilterButton__notification'); + expect(await badge.getVisibleText()).to.be('2'); + + await testSubjects.click('ruleTagFilterOption-tag1'); + expect(await badge.getVisibleText()).to.be('1'); + }); + }); +}; diff --git a/x-pack/test/functional_with_es_ssl/apps/uptime/alert_flyout.ts b/x-pack/test/functional_with_es_ssl/apps/uptime/alert_flyout.ts index 8cddb9b08a01b..81e05b65348ee 100644 --- a/x-pack/test/functional_with_es_ssl/apps/uptime/alert_flyout.ts +++ b/x-pack/test/functional_with_es_ssl/apps/uptime/alert_flyout.ts @@ -16,7 +16,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const supertest = getService('supertest'); const retry = getService('retry'); - describe('overview page alert flyout controls', function () { + // FLAKY: https://github.com/elastic/kibana/issues/101984 + describe.skip('overview page alert flyout controls', function () { const DEFAULT_DATE_START = 'Sep 10, 2019 @ 12:40:08.078'; const DEFAULT_DATE_END = 'Sep 11, 2019 @ 19:40:08.078'; let alerts: any; diff --git a/x-pack/test/functional_with_es_ssl/apps/uptime/index.ts b/x-pack/test/functional_with_es_ssl/apps/uptime/index.ts index d2078267bde85..2c39ef045972f 100644 --- a/x-pack/test/functional_with_es_ssl/apps/uptime/index.ts +++ b/x-pack/test/functional_with_es_ssl/apps/uptime/index.ts @@ -14,8 +14,6 @@ export default ({ getService, loadTestFile }: FtrProviderContext) => { const kibanaServer = getService('kibanaServer'); describe('Uptime app', function () { - this.tags('ciGroup6'); - describe('with real-world data', () => { before(async () => { await esArchiver.load(ARCHIVE); diff --git a/x-pack/test/functional_with_es_ssl/apps/uptime/simple_down_alert.ts b/x-pack/test/functional_with_es_ssl/apps/uptime/simple_down_alert.ts index 425ce5a55524d..963acca117881 100644 --- a/x-pack/test/functional_with_es_ssl/apps/uptime/simple_down_alert.ts +++ b/x-pack/test/functional_with_es_ssl/apps/uptime/simple_down_alert.ts @@ -107,7 +107,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { group: 'xpack.uptime.alerts.actionGroups.monitorStatus', params: { message: - 'Monitor {{state.monitorName}} with url {{{state.monitorUrl}}} from {{state.observerLocation}} {{{state.statusMessage}}} The latest error message is {{{state.latestErrorMessage}}}', + 'Monitor {{context.monitorName}} with url {{{context.monitorUrl}}} from {{context.observerLocation}} {{{context.statusMessage}}} The latest error message is {{{context.latestErrorMessage}}}', }, id: 'my-slack1', }, diff --git a/x-pack/test/functional_with_es_ssl/config.ts b/x-pack/test/functional_with_es_ssl/config.ts index 5243b97898578..62984ace526fb 100644 --- a/x-pack/test/functional_with_es_ssl/config.ts +++ b/x-pack/test/functional_with_es_ssl/config.ts @@ -31,7 +31,9 @@ const enabledActionTypes = [ ]; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xpackFunctionalConfig = await readConfigFile(require.resolve('../functional/config.js')); + const xpackFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); const servers = { ...xpackFunctionalConfig.get('servers'), @@ -74,6 +76,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { `--xpack.trigger_actions_ui.enableExperimental=${JSON.stringify([ 'internalAlertsTable', 'internalShareableComponentsSandbox', + 'ruleTagFilter', + 'ruleStatusFilter', ])}`, `--xpack.alerting.rules.minimumScheduleInterval.value="2s"`, `--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`, diff --git a/x-pack/test/functional_with_es_ssl/lib/alert_api_actions.ts b/x-pack/test/functional_with_es_ssl/lib/alert_api_actions.ts index 40e567c299826..ab15d4b2ec3f4 100644 --- a/x-pack/test/functional_with_es_ssl/lib/alert_api_actions.ts +++ b/x-pack/test/functional_with_es_ssl/lib/alert_api_actions.ts @@ -8,6 +8,8 @@ import type { ObjectRemover } from './object_remover'; import { getTestAlertData, getTestActionData } from './get_test_data'; +const FUTURE_SNOOZE_TIME = '9999-12-31T06:00:00.000Z'; + export async function createAlertManualCleanup({ supertest, overwrites = {}, @@ -85,3 +87,14 @@ export async function disableAlert({ supertest, alertId }: { supertest: any; ale .set('kbn-xsrf', 'foo'); return alert; } + +export async function snoozeAlert({ supertest, alertId }: { supertest: any; alertId: string }) { + const { body: alert } = await supertest + .post(`/internal/alerting/rule/${alertId}/_snooze`) + .set('kbn-xsrf', 'foo') + .set('content-type', 'application/json') + .send({ + snooze_end_time: FUTURE_SNOOZE_TIME, + }); + return alert; +} diff --git a/x-pack/test/functional_with_es_ssl/page_objects/rule_details.ts b/x-pack/test/functional_with_es_ssl/page_objects/rule_details.ts index 01d7c24be2f41..cff396276eefd 100644 --- a/x-pack/test/functional_with_es_ssl/page_objects/rule_details.ts +++ b/x-pack/test/functional_with_es_ssl/page_objects/rule_details.ts @@ -21,6 +21,9 @@ export function RuleDetailsPageProvider({ getService }: FtrProviderContext) { async getRuleType() { return await testSubjects.getVisibleText('ruleTypeLabel'); }, + async getAPIKeyOwner() { + return await testSubjects.getVisibleText('apiKeyOwnerLabel'); + }, async getActionsLabels() { return { connectorType: await testSubjects.getVisibleText('actionTypeLabel'), diff --git a/x-pack/test/licensing_plugin/config.ts b/x-pack/test/licensing_plugin/config.ts index 155d761020b29..c4b197c10a824 100644 --- a/x-pack/test/licensing_plugin/config.ts +++ b/x-pack/test/licensing_plugin/config.ts @@ -11,7 +11,9 @@ import { services, pageObjects } from './services'; const license = 'basic'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const functionalTestsConfig = await readConfigFile(require.resolve('../functional/config.js')); + const functionalTestsConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); const servers = { ...functionalTestsConfig.get('servers'), diff --git a/x-pack/test/licensing_plugin/public/index.ts b/x-pack/test/licensing_plugin/public/index.ts index 194db6266b510..904b9eaecd757 100644 --- a/x-pack/test/licensing_plugin/public/index.ts +++ b/x-pack/test/licensing_plugin/public/index.ts @@ -10,7 +10,6 @@ import { FtrProviderContext } from '../services'; // eslint-disable-next-line import/no-default-export export default function ({ loadTestFile }: FtrProviderContext) { describe('Licensing plugin public client', function () { - this.tags('ciGroup5'); loadTestFile(require.resolve('./feature_usage')); // MUST BE LAST! CHANGES LICENSE TYPE! loadTestFile(require.resolve('./updates')); diff --git a/x-pack/test/licensing_plugin/server/index.ts b/x-pack/test/licensing_plugin/server/index.ts index 619d29a4a2fd2..28426eba962b8 100644 --- a/x-pack/test/licensing_plugin/server/index.ts +++ b/x-pack/test/licensing_plugin/server/index.ts @@ -10,7 +10,6 @@ import { FtrProviderContext } from '../services'; // eslint-disable-next-line import/no-default-export export default function ({ loadTestFile }: FtrProviderContext) { describe('Licensing plugin server client', function () { - this.tags('ciGroup13'); loadTestFile(require.resolve('./info')); loadTestFile(require.resolve('./header')); diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts index c28447ef0ac18..95cce7e7fb85f 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts @@ -10,8 +10,6 @@ import { FtrProviderContext } from '../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default ({ loadTestFile }: FtrProviderContext): void => { describe('lists api security and spaces enabled', function () { - this.tags('ciGroup1'); - loadTestFile(require.resolve('./create_lists')); loadTestFile(require.resolve('./create_list_items')); loadTestFile(require.resolve('./read_lists')); diff --git a/x-pack/test/load/config.ts b/x-pack/test/load/config.ts index 2d20806f3e9e8..dcaa2031c9c02 100644 --- a/x-pack/test/load/config.ts +++ b/x-pack/test/load/config.ts @@ -21,7 +21,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { require.resolve('../../../test/common/config.js') ); const xpackFunctionalTestsConfig = await readConfigFile( - require.resolve('../functional/config.js') + require.resolve('../functional/config.base.js') ); return { diff --git a/x-pack/test/observability_api_integration/basic/tests/index.ts b/x-pack/test/observability_api_integration/basic/tests/index.ts index c62cf4be0d7c7..a1ea41e2b7c36 100644 --- a/x-pack/test/observability_api_integration/basic/tests/index.ts +++ b/x-pack/test/observability_api_integration/basic/tests/index.ts @@ -10,7 +10,6 @@ import { FtrProviderContext } from '../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function observabilityApiIntegrationTests({ loadTestFile }: FtrProviderContext) { describe('Observability specs (basic)', function () { - this.tags('ciGroup1'); loadTestFile(require.resolve('./annotations')); }); } diff --git a/x-pack/test/observability_api_integration/trial/tests/index.ts b/x-pack/test/observability_api_integration/trial/tests/index.ts index 037cf48275de2..e426efd90188c 100644 --- a/x-pack/test/observability_api_integration/trial/tests/index.ts +++ b/x-pack/test/observability_api_integration/trial/tests/index.ts @@ -10,7 +10,6 @@ import { FtrProviderContext } from '../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderContext) { describe('Observability specs (trial)', function () { - this.tags('ciGroup1'); loadTestFile(require.resolve('./annotations')); }); } diff --git a/x-pack/test/observability_functional/apps/observability/index.ts b/x-pack/test/observability_functional/apps/observability/index.ts index 9ec3791aef35f..d60f93f1285ad 100644 --- a/x-pack/test/observability_functional/apps/observability/index.ts +++ b/x-pack/test/observability_functional/apps/observability/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('ObservabilityApp', function () { - this.tags('ciGroup22'); - loadTestFile(require.resolve('./alerts')); loadTestFile(require.resolve('./alerts/add_to_case')); loadTestFile(require.resolve('./alerts/alert_disclaimer')); diff --git a/x-pack/test/observability_functional/with_rac_write.config.ts b/x-pack/test/observability_functional/with_rac_write.config.ts index 71a1de1df6a77..bc5b39358fedb 100644 --- a/x-pack/test/observability_functional/with_rac_write.config.ts +++ b/x-pack/test/observability_functional/with_rac_write.config.ts @@ -27,7 +27,9 @@ const enabledActionTypes = [ ]; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xpackFunctionalConfig = await readConfigFile(require.resolve('../functional/config.js')); + const xpackFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); const servers = { ...xpackFunctionalConfig.get('servers'), diff --git a/x-pack/test/osquery_cypress/config.ts b/x-pack/test/osquery_cypress/config.ts index 2bd39acfa1359..37f7b3f63b36c 100644 --- a/x-pack/test/osquery_cypress/config.ts +++ b/x-pack/test/osquery_cypress/config.ts @@ -14,7 +14,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { require.resolve('../../../test/common/config.js') ); const xpackFunctionalTestsConfig = await readConfigFile( - require.resolve('../functional/config.js') + require.resolve('../functional/config.base.js') ); return { diff --git a/x-pack/test/performance/config.playwright.ts b/x-pack/test/performance/config.playwright.ts index 9077c58a30e15..44a53d7be80a1 100644 --- a/x-pack/test/performance/config.playwright.ts +++ b/x-pack/test/performance/config.playwright.ts @@ -15,7 +15,7 @@ const APM_SERVER_URL = 'https://kibana-ops-e2e-perf.apm.us-central1.gcp.cloud.es const APM_PUBLIC_TOKEN = 'CTs9y3cvcfq13bQqsB'; export default async function ({ readConfigFile, log }: FtrConfigProviderContext) { - const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); const testFiles = [require.resolve('./tests/playwright')]; @@ -63,6 +63,7 @@ export default async function ({ readConfigFile, log }: FtrConfigProviderContext performancePhase: process.env.TEST_PERFORMANCE_PHASE, journeyName: process.env.JOURNEY_NAME, testJobId, + testBuildId, }) .filter(([, v]) => !!v) .reduce((acc, [k, v]) => (acc ? `${acc},${k}=${v}` : `${k}=${v}`), ''), diff --git a/x-pack/test/plugin_api_integration/test_suites/event_log/index.ts b/x-pack/test/plugin_api_integration/test_suites/event_log/index.ts index c29367fb852ab..0901c96f522cc 100644 --- a/x-pack/test/plugin_api_integration/test_suites/event_log/index.ts +++ b/x-pack/test/plugin_api_integration/test_suites/event_log/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('event_log', function taskManagerSuite() { - this.tags('ciGroup6'); loadTestFile(require.resolve('./public_api_integration')); loadTestFile(require.resolve('./service_api_integration')); }); diff --git a/x-pack/test/plugin_api_integration/test_suites/licensed_feature_usage/index.ts b/x-pack/test/plugin_api_integration/test_suites/licensed_feature_usage/index.ts index 7f2a4c12a26eb..6ee46b58c4bcd 100644 --- a/x-pack/test/plugin_api_integration/test_suites/licensed_feature_usage/index.ts +++ b/x-pack/test/plugin_api_integration/test_suites/licensed_feature_usage/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('Licensed feature usage APIs', function () { - this.tags('ciGroup13'); loadTestFile(require.resolve('./feature_usage')); }); } diff --git a/x-pack/test/plugin_api_integration/test_suites/platform/index.ts b/x-pack/test/plugin_api_integration/test_suites/platform/index.ts index 46c468e9b6d78..907ebfe6bdf79 100644 --- a/x-pack/test/plugin_api_integration/test_suites/platform/index.ts +++ b/x-pack/test/plugin_api_integration/test_suites/platform/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('platform', function taskManagerSuite() { - this.tags('ciGroup13'); loadTestFile(require.resolve('./elasticsearch_client')); }); } diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/index.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/index.ts index fe494ac33d461..2712069008598 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/index.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('task_manager', function taskManagerSuite() { - this.tags('ciGroup12'); loadTestFile(require.resolve('./health_route')); loadTestFile(require.resolve('./task_management')); loadTestFile(require.resolve('./task_management_scheduled_at')); diff --git a/x-pack/test/plugin_api_perf/README.md b/x-pack/test/plugin_api_perf/README.md index f47a2aeb7878a..2ae7c7d201328 100644 --- a/x-pack/test/plugin_api_perf/README.md +++ b/x-pack/test/plugin_api_perf/README.md @@ -71,7 +71,7 @@ Ideally we can clean this up and make it easier and less hacky in the future, bu 1. You can run the FTS in the main clone of your fork by running `node scripts/functional_tests_server.js --config=test/plugin_api_perf/config.js` in the `x-pack` folder. 1. Once you've began running the default FTS, you want your second FTS to run such that it is referencing the Elasticsearch instance started by that first FTS. You achieve this by exporting a `TEST_ES_URL` Environment variable that points at it. By default, you should be able to run this: `export TEST_ES_URL=http://elastic:changeme@localhost:9220`. Do this in a terminal window opened in your **second** clone of Kibana (in my case, the `./elastic/_kibana` folder). 1. One issue I encountered with FTS is that I can't tell it _not to start its own ES instance at all_. To achieve this, in `packages/kbn-test/src/functional_tests/tasks.js` you need to comment out the line that starts up its own ES (`const es = await runElasticsearch({ config, options: opts });` [line 85] and `await es.cleanup();` shortly after) -1. Next you want each instance of Kibana to run with its own UUID as that is used to identify each Kibana's owned tasks. In the file `x-pack/test/functional/config.js` simple change the uuid on the line `--server.uuid=` into any random UUID. +1. Next you want each instance of Kibana to run with its own UUID as that is used to identify each Kibana's owned tasks. In the file `x-pack/test/functional/config.base.js` simple change the uuid on the line `--server.uuid=` into any random UUID. 1. Now that you've made these changes you can kick off your second Kibana FTS by running ths following in the second clone's `x-pack` folder: `TEST_KIBANA_PORT=5621 node scripts/functional_tests_server.js --config=test/plugin_api_perf/config.js`. This runs Kibana on a different port than the first FTS (`5621` instead of `5620`). 1. With two FTS Kibana running and both pointing at the same Elasticsearch. Now, you can run the actual perf test by running `node scripts/functional_test_runner.js --config=test/plugin_api_perf/config.js` in a third terminal diff --git a/x-pack/test/plugin_api_perf/test_suites/task_manager/index.ts b/x-pack/test/plugin_api_perf/test_suites/task_manager/index.ts index 703c97e4f2c63..9055a2d9e023c 100644 --- a/x-pack/test/plugin_api_perf/test_suites/task_manager/index.ts +++ b/x-pack/test/plugin_api_perf/test_suites/task_manager/index.ts @@ -15,7 +15,6 @@ export default function ({ loadTestFile }: { loadTestFile: (file: string) => voi * worth keeping around for future use, rather than being rewritten time and time again. */ describe.skip('task_manager_perf', function taskManagerSuite() { - this.tags('ciGroup12'); loadTestFile(require.resolve('./task_manager_perf_integration')); }); } diff --git a/x-pack/test/plugin_functional/config.ts b/x-pack/test/plugin_functional/config.ts index 8f3c5be04a8bc..a21b8f406e506 100644 --- a/x-pack/test/plugin_functional/config.ts +++ b/x-pack/test/plugin_functional/config.ts @@ -17,7 +17,9 @@ import { pageObjects } from './page_objects'; // that returns an object with the projects config values export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xpackFunctionalConfig = await readConfigFile(require.resolve('../functional/config.js')); + const xpackFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); // Find all folders in ./plugins since we treat all them as plugin folder const allFiles = fs.readdirSync(resolve(__dirname, 'plugins')); diff --git a/x-pack/test/plugin_functional/plugins/timelines_test/kibana.json b/x-pack/test/plugin_functional/plugins/timelines_test/kibana.json index f96e8b2bbcc23..1960c49839566 100644 --- a/x-pack/test/plugin_functional/plugins/timelines_test/kibana.json +++ b/x-pack/test/plugin_functional/plugins/timelines_test/kibana.json @@ -4,7 +4,7 @@ "version": "1.0.0", "kibanaVersion": "kibana", "configPath": ["xpack", "timelinesTest"], - "requiredPlugins": ["timelines", "data", "dataEnhanced"], + "requiredPlugins": ["timelines", "data"], "requiredBundles": ["kibanaReact"], "server": false, "ui": true diff --git a/x-pack/test/plugin_functional/test_suites/global_search/index.ts b/x-pack/test/plugin_functional/test_suites/global_search/index.ts index a87d9c5e4d503..651bb2b903924 100644 --- a/x-pack/test/plugin_functional/test_suites/global_search/index.ts +++ b/x-pack/test/plugin_functional/test_suites/global_search/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('GlobalSearch API', function () { - this.tags('ciGroup7'); loadTestFile(require.resolve('./global_search_providers')); loadTestFile(require.resolve('./global_search_bar')); }); diff --git a/x-pack/test/plugin_functional/test_suites/resolver/index.ts b/x-pack/test/plugin_functional/test_suites/resolver/index.ts index 688ba536b1232..3c43683b6bf7a 100644 --- a/x-pack/test/plugin_functional/test_suites/resolver/index.ts +++ b/x-pack/test/plugin_functional/test_suites/resolver/index.ts @@ -29,8 +29,6 @@ export default function ({ // FLAKY: https://github.com/elastic/kibana/issues/87425 describe('Resolver test app', function () { - this.tags('ciGroup7'); - // Note: these tests are intended to run on the same page in serial. before(async function () { await pageObjects.common.navigateToApp('resolverTest'); diff --git a/x-pack/test/plugin_functional/test_suites/timelines/index.ts b/x-pack/test/plugin_functional/test_suites/timelines/index.ts index 2ca8d81132ab3..955966eab12c0 100644 --- a/x-pack/test/plugin_functional/test_suites/timelines/index.ts +++ b/x-pack/test/plugin_functional/test_suites/timelines/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { describe('Timelines plugin API', function () { - this.tags('ciGroup7'); const pageObjects = getPageObjects(['common']); const testSubjects = getService('testSubjects'); diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/index.ts b/x-pack/test/reporting_api_integration/reporting_and_security/index.ts index 4cff15dc9f444..808c813145b84 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/index.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/index.ts @@ -10,8 +10,6 @@ import { FtrProviderContext } from '../ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function ({ getService, loadTestFile }: FtrProviderContext) { describe('Reporting APIs', function () { - this.tags('ciGroup2'); - before(async () => { const reportingAPI = getService('reportingAPI'); await reportingAPI.logTaskManagerHealth(); diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/usage/_post_urls.ts b/x-pack/test/reporting_api_integration/reporting_and_security/usage/_post_urls.ts index 510e94cf95f0d..9e6919c7a00e6 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/usage/_post_urls.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/usage/_post_urls.ts @@ -19,7 +19,7 @@ export const PDF_PRESERVE_PIE_VISUALIZATION_6_3 = `/api/reporting/generate/print )}`; export const PDF_PRINT_PIE_VISUALIZATION_FILTER_AND_SAVED_SEARCH_6_3 = `/api/reporting/generate/printablePdf?jobParams=${encodeURIComponent( - `(browserTimezone:America/New_York,layout:(id:print),objectType:visualization,relativeUrls:!('/app/kibana#/visualize/edit/befdb6b0-3e59-11e8-9fc3-39e49624228e?_g=(refreshInterval:(display:Off,pause:!!f,value:0),time:(from:!'Mon+Apr+09+2018+17:56:08+GMT-0400!',mode:absolute,to:!'Wed+Apr+11+2018+17:56:08+GMT-0400!'))&_a=(filters:!!((!'$state!':(store:appState),meta:(alias:!!n,disabled:!!f,index:a0f483a0-3dc9-11e8-8660-4d65aa086b3c,key:animal.keyword,negate:!!f,params:(query:dog,type:phrase),type:phrase,value:dog),query:(match:(animal.keyword:(query:dog,type:phrase))))),linked:!!t,query:(language:lucene,query:!'!'),uiState:(),vis:(aggs:!!((enabled:!!t,id:!'1!',params:(),schema:metric,type:count),(enabled:!!t,id:!'2!',params:(field:name.keyword,missingBucket:!!f,missingBucketLabel:Missing,order:desc,orderBy:!'1!',otherBucket:!!f,otherBucketLabel:Other,size:5),schema:segment,type:terms)),params:(addLegend:!!t,addTooltip:!!t,isDonut:!!t,labels:(last_level:!!t,show:!!f,truncate:100,values:!!t),legendPosition:right,type:pie),title:!'Filter+Test:+animals:+linked+to+search+with+filter!',type:pie))'),title:'Filter Test: animals: linked to search with filter')` + `(browserTimezone:America/New_York,layout:(dimensions:(height:588,width:1038),id:preserve_layout),objectType:visualization,relativeUrls:!('/app/kibana#/visualize/edit/befdb6b0-3e59-11e8-9fc3-39e49624228e?_g=(refreshInterval:(display:Off,pause:!!f,value:0),time:(from:!'Mon+Apr+09+2018+17:56:08+GMT-0400!',mode:absolute,to:!'Wed+Apr+11+2018+17:56:08+GMT-0400!'))&_a=(filters:!!((!'$state!':(store:appState),meta:(alias:!!n,disabled:!!f,index:a0f483a0-3dc9-11e8-8660-4d65aa086b3c,key:animal.keyword,negate:!!f,params:(query:dog,type:phrase),type:phrase,value:dog),query:(match:(animal.keyword:(query:dog,type:phrase))))),linked:!!t,query:(language:lucene,query:!'!'),uiState:(),vis:(aggs:!!((enabled:!!t,id:!'1!',params:(),schema:metric,type:count),(enabled:!!t,id:!'2!',params:(field:name.keyword,missingBucket:!!f,missingBucketLabel:Missing,order:desc,orderBy:!'1!',otherBucket:!!f,otherBucketLabel:Other,size:5),schema:segment,type:terms)),params:(addLegend:!!t,addTooltip:!!t,isDonut:!!t,labels:(last_level:!!t,show:!!f,truncate:100,values:!!t),legendPosition:right,type:pie),title:!'Filter+Test:+animals:+linked+to+search+with+filter!',type:pie))'),title:'Filter Test: animals: linked to search with filter')` )}`; export const JOB_PARAMS_CSV_DEFAULT_SPACE = `/api/reporting/generate/csv_searchsource?jobParams=${encodeURIComponent( diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/usage/new_jobs.ts b/x-pack/test/reporting_api_integration/reporting_and_security/usage/new_jobs.ts index 086f3373e2c71..e702be05f9bd8 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/usage/new_jobs.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/usage/new_jobs.ts @@ -74,15 +74,15 @@ export default function ({ getService }: FtrProviderContext) { const usage = await usageAPI.getUsageStats(); reportingAPI.expectRecentPdfAppStats(usage, 'visualization', 1); reportingAPI.expectRecentPdfAppStats(usage, 'dashboard', 1); - reportingAPI.expectRecentPdfLayoutStats(usage, 'preserve_layout', 0); - reportingAPI.expectRecentPdfLayoutStats(usage, 'print', 2); + reportingAPI.expectRecentPdfLayoutStats(usage, 'preserve_layout', 1); + reportingAPI.expectRecentPdfLayoutStats(usage, 'print', 1); reportingAPI.expectRecentJobTypeTotalStats(usage, 'csv_searchsource', 0); reportingAPI.expectRecentJobTypeTotalStats(usage, 'printable_pdf', 2); reportingAPI.expectAllTimePdfAppStats(usage, 'visualization', 1); reportingAPI.expectAllTimePdfAppStats(usage, 'dashboard', 1); - reportingAPI.expectAllTimePdfLayoutStats(usage, 'preserve_layout', 0); - reportingAPI.expectAllTimePdfLayoutStats(usage, 'print', 2); + reportingAPI.expectAllTimePdfLayoutStats(usage, 'preserve_layout', 1); + reportingAPI.expectAllTimePdfLayoutStats(usage, 'print', 1); reportingAPI.expectAllTimeJobTypeTotalStats(usage, 'csv_searchsource', 0); reportingAPI.expectAllTimeJobTypeTotalStats(usage, 'printable_pdf', 2); }); diff --git a/x-pack/test/reporting_api_integration/reporting_without_security/index.ts b/x-pack/test/reporting_api_integration/reporting_without_security/index.ts index 72cfc36947517..19f96aa5d2869 100644 --- a/x-pack/test/reporting_api_integration/reporting_without_security/index.ts +++ b/x-pack/test/reporting_api_integration/reporting_without_security/index.ts @@ -14,7 +14,7 @@ export default function ({ loadTestFile, getService }: FtrProviderContext) { const reportingAPI = getService('reportingAPI'); await reportingAPI.logTaskManagerHealth(); }); - this.tags('ciGroup13'); + loadTestFile(require.resolve('./job_apis_csv')); }); } diff --git a/x-pack/test/reporting_api_integration/services/usage.ts b/x-pack/test/reporting_api_integration/services/usage.ts index fd16f3859fa11..80204875cd6d6 100644 --- a/x-pack/test/reporting_api_integration/services/usage.ts +++ b/x-pack/test/reporting_api_integration/services/usage.ts @@ -101,31 +101,47 @@ export function createUsageServices({ getService }: FtrProviderContext) { }, expectRecentPdfAppStats(stats: UsageStats, app: string, count: number) { - expect( - stats.reporting.last_7_days.printable_pdf.app![app as keyof AvailableTotal['app']] - ).to.be(count); + const actual = + stats.reporting.last_7_days.printable_pdf.app![app as keyof AvailableTotal['app']]; + log.info(`expecting recent ${app} stats to have ${count} printable pdfs (actual: ${actual})`); + expect(actual).to.be(count); }, expectAllTimePdfAppStats(stats: UsageStats, app: string, count: number) { - expect(stats.reporting.printable_pdf.app![app as keyof AvailableTotal['app']]).to.be(count); + const actual = stats.reporting.printable_pdf.app![app as keyof AvailableTotal['app']]; + log.info( + `expecting all time pdf ${app} stats to have ${count} printable pdfs (actual: ${actual})` + ); + expect(actual).to.be(count); }, expectRecentPdfLayoutStats(stats: UsageStats, layout: string, count: number) { - expect(stats.reporting.last_7_days.printable_pdf.layout![layout as keyof LayoutCounts]).to.be( - count - ); + const actual = + stats.reporting.last_7_days.printable_pdf.layout![layout as keyof LayoutCounts]; + log.info(`expecting recent stats to report ${count} ${layout} layouts (actual: ${actual})`); + expect(actual).to.be(count); }, expectAllTimePdfLayoutStats(stats: UsageStats, layout: string, count: number) { - expect(stats.reporting.printable_pdf.layout![layout as keyof LayoutCounts]).to.be(count); + const actual = stats.reporting.printable_pdf.layout![layout as keyof LayoutCounts]; + log.info(`expecting all time stats to report ${count} ${layout} layouts (actual: ${actual})`); + expect(actual).to.be(count); }, expectRecentJobTypeTotalStats(stats: UsageStats, jobType: string, count: number) { - expect(stats.reporting.last_7_days[jobType as keyof JobTypes].total).to.be(count); + const actual = stats.reporting.last_7_days[jobType as keyof JobTypes].total; + log.info( + `expecting recent stats to report ${count} ${jobType} job types (actual: ${actual})` + ); + expect(actual).to.be(count); }, expectAllTimeJobTypeTotalStats(stats: UsageStats, jobType: string, count: number) { - expect(stats.reporting[jobType as keyof JobTypes].total).to.be(count); + const actual = stats.reporting[jobType as keyof JobTypes].total; + log.info( + `expecting all time stats to report ${count} ${jobType} job types (actual: ${actual})` + ); + expect(actual).to.be(count); }, getCompletedReportCount(stats: UsageStats) { diff --git a/x-pack/test/reporting_functional/reporting_and_deprecated_security/index.ts b/x-pack/test/reporting_functional/reporting_and_deprecated_security/index.ts index 4725cb1eae82e..722b6545115cd 100644 --- a/x-pack/test/reporting_functional/reporting_and_deprecated_security/index.ts +++ b/x-pack/test/reporting_functional/reporting_and_deprecated_security/index.ts @@ -43,8 +43,6 @@ export default function (context: FtrProviderContext) { }; describe('Reporting Functional Tests with Deprecated Security configuration enabled', function () { - this.tags('ciGroup20'); - before(async () => { const reportingAPI = context.getService('reportingAPI'); await reportingAPI.logTaskManagerHealth(); diff --git a/x-pack/test/reporting_functional/reporting_and_security.config.ts b/x-pack/test/reporting_functional/reporting_and_security.config.ts index 3037aeacde033..7d8c3ed696696 100644 --- a/x-pack/test/reporting_functional/reporting_and_security.config.ts +++ b/x-pack/test/reporting_functional/reporting_and_security.config.ts @@ -11,7 +11,7 @@ import { ReportingAPIProvider } from '../reporting_api_integration/services'; import { ReportingFunctionalProvider } from './services'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const functionalConfig = await readConfigFile(require.resolve('../functional/config')); // Reporting API tests need a fully working UI + const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); // Reporting API tests need a fully working UI const apiConfig = await readConfigFile(require.resolve('../api_integration/config')); return { diff --git a/x-pack/test/reporting_functional/reporting_and_security/index.ts b/x-pack/test/reporting_functional/reporting_and_security/index.ts index 4b06eb426389e..ec7afea96f194 100644 --- a/x-pack/test/reporting_functional/reporting_and_security/index.ts +++ b/x-pack/test/reporting_functional/reporting_and_security/index.ts @@ -10,8 +10,6 @@ import { FtrProviderContext } from '../ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function ({ getService, loadTestFile }: FtrProviderContext) { describe('Reporting Functional Tests with Security enabled', function () { - this.tags('ciGroup20'); - before(async () => { const reportingFunctional = getService('reportingFunctional'); await reportingFunctional.logTaskManagerHealth(); diff --git a/x-pack/test/reporting_functional/reporting_without_security/index.ts b/x-pack/test/reporting_functional/reporting_without_security/index.ts index fecc0e97daac0..cc07e97a9c3a7 100644 --- a/x-pack/test/reporting_functional/reporting_without_security/index.ts +++ b/x-pack/test/reporting_functional/reporting_without_security/index.ts @@ -10,8 +10,6 @@ import { FtrProviderContext } from '../ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function ({ loadTestFile, getService }: FtrProviderContext) { describe('Reporting Functional Tests with Security disabled', function () { - this.tags('ciGroup2'); - before(async () => { const reportingAPI = getService('reportingAPI'); await reportingAPI.logTaskManagerHealth(); diff --git a/x-pack/test/rule_registry/security_and_spaces/tests/basic/find_alerts.ts b/x-pack/test/rule_registry/security_and_spaces/tests/basic/find_alerts.ts index ef06fc80c485c..35461b47f6def 100644 --- a/x-pack/test/rule_registry/security_and_spaces/tests/basic/find_alerts.ts +++ b/x-pack/test/rule_registry/security_and_spaces/tests/basic/find_alerts.ts @@ -50,7 +50,6 @@ export default ({ getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const TEST_URL = '/internal/rac/alerts'; - const ALERTS_INDEX_URL = `${TEST_URL}/index`; const SPACE1 = 'space1'; const SPACE2 = 'space2'; const APM_ALERT_ID = 'NoxgpHkBqbdrfX07MqXV'; @@ -58,38 +57,7 @@ export default ({ getService }: FtrProviderContext) => { const SECURITY_SOLUTION_ALERT_ID = '020202'; const SECURITY_SOLUTION_ALERT_INDEX = '.alerts-security.alerts'; - const getAPMIndexName = async (user: User) => { - const { body: indexNames }: { body: { index_name: string[] | undefined } } = - await supertestWithoutAuth - .get(`${getSpaceUrlPrefix(SPACE1)}${ALERTS_INDEX_URL}`) - .auth(user.username, user.password) - .set('kbn-xsrf', 'true') - .expect(200); - const observabilityIndex = indexNames?.index_name?.find( - (indexName) => indexName === APM_ALERT_INDEX - ); - expect(observabilityIndex).to.eql(APM_ALERT_INDEX); // assert this here so we can use constants in the dynamically-defined test cases below - }; - - const getSecuritySolutionIndexName = async (user: User) => { - const { body: indexNames }: { body: { index_name: string[] | undefined } } = - await supertestWithoutAuth - .get(`${getSpaceUrlPrefix(SPACE1)}${ALERTS_INDEX_URL}`) - .auth(user.username, user.password) - .set('kbn-xsrf', 'true') - .expect(200); - const securitySolution = indexNames?.index_name?.find((indexName) => - indexName.startsWith(SECURITY_SOLUTION_ALERT_INDEX) - ); - expect(securitySolution).to.eql(`${SECURITY_SOLUTION_ALERT_INDEX}-${SPACE1}`); // assert this here so we can use constants in the dynamically-defined test cases below - }; - describe('Alert - Find - RBAC - spaces', () => { - before(async () => { - await getSecuritySolutionIndexName(superUser); - await getAPMIndexName(superUser); - }); - before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/rule_registry/alerts'); }); @@ -98,6 +66,32 @@ export default ({ getService }: FtrProviderContext) => { await esArchiver.unload('x-pack/test/functional/es_archives/rule_registry/alerts'); }); + it(`${superUser.username} should reject at route level when aggs contains script alerts which match query in ${SPACE1}/${SECURITY_SOLUTION_ALERT_INDEX}`, async () => { + const found = await supertestWithoutAuth + .post(`${getSpaceUrlPrefix(SPACE1)}${TEST_URL}/find`) + .auth(superUser.username, superUser.password) + .set('kbn-xsrf', 'true') + .send({ + query: { match: { [ALERT_WORKFLOW_STATUS]: 'open' } }, + aggs: { + alertsByGroupingCount: { + terms: { + field: 'kibana.alert.rule.name', + order: { + _count: 'desc', + }, + script: { + source: 'SCRIPT', + }, + size: 10000, + }, + }, + }, + index: SECURITY_SOLUTION_ALERT_INDEX, + }); + expect(found.statusCode).to.eql(400); + }); + it(`${superUser.username} should reject at route level when nested aggs contains script alerts which match query in ${SPACE1}/${SECURITY_SOLUTION_ALERT_INDEX}`, async () => { const found = await supertestWithoutAuth .post(`${getSpaceUrlPrefix(SPACE1)}${TEST_URL}/find`) @@ -164,6 +158,26 @@ export default ({ getService }: FtrProviderContext) => { expect(found.body.hits.total.value).to.be.above(0); }); + it(`${superUser.username} should allow cardinality aggs in ${SPACE1}/${SECURITY_SOLUTION_ALERT_INDEX}`, async () => { + const found = await supertestWithoutAuth + .post(`${getSpaceUrlPrefix(SPACE1)}${TEST_URL}/find`) + .auth(superUser.username, superUser.password) + .set('kbn-xsrf', 'true') + .send({ + size: 1, + aggs: { + nbr_consumer: { + cardinality: { + field: 'kibana.alert.rule.consumer', + }, + }, + }, + index: '.alerts*', + }); + expect(found.statusCode).to.eql(200); + expect(found.body.aggregations.nbr_consumer.value).to.be.equal(2); + }); + function addTests({ space, authorizedUsers, unauthorizedUsers, alertId, index }: TestCase) { authorizedUsers.forEach(({ username, password }) => { it(`${username} should finds alerts which match query in ${space}/${index}`, async () => { diff --git a/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_alerts_index.ts b/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_alerts_index.ts index 1ba48d5d1aa27..3b30f1b64dc2b 100644 --- a/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_alerts_index.ts +++ b/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_alerts_index.ts @@ -20,17 +20,16 @@ export default ({ getService }: FtrProviderContext) => { const TEST_URL = '/internal/rac/alerts'; const ALERTS_INDEX_URL = `${TEST_URL}/index`; const SPACE1 = 'space1'; - const APM_ALERT_INDEX = '.alerts-observability-apm'; + const APM_ALERT_INDEX = '.alerts-observability.apm.alerts'; const SECURITY_SOLUTION_ALERT_INDEX = '.alerts-security.alerts'; - const getAPMIndexName = async (user: User, space: string, expected: number = 200) => { - const { body: indexNames }: { body: { index_name: string[] | undefined } } = - await supertestWithoutAuth - .get(`${getSpaceUrlPrefix(space)}${ALERTS_INDEX_URL}?features=apm`) - .auth(user.username, user.password) - .set('kbn-xsrf', 'true') - .expect(expected); - return indexNames; + const getAPMIndexName = async (user: User, space: string, expectedStatusCode: number = 200) => { + const resp = await supertestWithoutAuth + .get(`${getSpaceUrlPrefix(space)}${ALERTS_INDEX_URL}?features=apm`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'true') + .expect(expectedStatusCode); + return resp.body.index_name as string[]; }; const getSecuritySolutionIndexName = async ( @@ -38,13 +37,13 @@ export default ({ getService }: FtrProviderContext) => { space: string, expectedStatusCode: number = 200 ) => { - const { body: indexNames }: { body: { index_name: string[] | undefined } } = - await supertestWithoutAuth - .get(`${getSpaceUrlPrefix(space)}${ALERTS_INDEX_URL}?features=siem`) - .auth(user.username, user.password) - .set('kbn-xsrf', 'true') - .expect(expectedStatusCode); - return indexNames; + const resp = await supertestWithoutAuth + .get(`${getSpaceUrlPrefix(space)}${ALERTS_INDEX_URL}?features=siem`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'true') + .expect(expectedStatusCode); + + return resp.body.index_name as string[]; }; describe('Alert - Get Index - RBAC - spaces', () => { @@ -54,31 +53,22 @@ export default ({ getService }: FtrProviderContext) => { describe('Users:', () => { it(`${obsOnlySpacesAll.username} should be able to access the APM alert in ${SPACE1}`, async () => { const indexNames = await getAPMIndexName(obsOnlySpacesAll, SPACE1); - const observabilityIndex = indexNames?.index_name?.find( - (indexName) => indexName === APM_ALERT_INDEX - ); - expect(observabilityIndex).to.eql(APM_ALERT_INDEX); // assert this here so we can use constants in the dynamically-defined test cases below + expect(indexNames.includes(`${APM_ALERT_INDEX}-${SPACE1}`)).to.eql(true); // assert this here so we can use constants in the dynamically-defined test cases below }); it(`${superUser.username} should be able to access the APM alert in ${SPACE1}`, async () => { const indexNames = await getAPMIndexName(superUser, SPACE1); - const observabilityIndex = indexNames?.index_name?.find( - (indexName) => indexName === APM_ALERT_INDEX - ); - expect(observabilityIndex).to.eql(APM_ALERT_INDEX); // assert this here so we can use constants in the dynamically-defined test cases below + expect(indexNames.includes(`${APM_ALERT_INDEX}-${SPACE1}`)).to.eql(true); // assert this here so we can use constants in the dynamically-defined test cases below }); it(`${secOnlyRead.username} should NOT be able to access the APM alert in ${SPACE1}`, async () => { const indexNames = await getAPMIndexName(secOnlyRead, SPACE1); - expect(indexNames?.index_name?.length).to.eql(0); + expect(indexNames?.length).to.eql(0); }); it(`${secOnlyRead.username} should be able to access the security solution alert in ${SPACE1}`, async () => { const indexNames = await getSecuritySolutionIndexName(secOnlyRead, SPACE1); - const securitySolution = indexNames?.index_name?.find((indexName) => - indexName.startsWith(SECURITY_SOLUTION_ALERT_INDEX) - ); - expect(securitySolution).to.eql(`${SECURITY_SOLUTION_ALERT_INDEX}-${SPACE1}`); // assert this here so we can use constants in the dynamically-defined test cases below + expect(indexNames.includes(`${SECURITY_SOLUTION_ALERT_INDEX}-${SPACE1}`)).to.eql(true); // assert this here so we can use constants in the dynamically-defined test cases below }); }); }); diff --git a/x-pack/test/rule_registry/security_and_spaces/tests/basic/index.ts b/x-pack/test/rule_registry/security_and_spaces/tests/basic/index.ts index 229f31375200a..d010cbfce9150 100644 --- a/x-pack/test/rule_registry/security_and_spaces/tests/basic/index.ts +++ b/x-pack/test/rule_registry/security_and_spaces/tests/basic/index.ts @@ -11,9 +11,6 @@ import { createSpacesAndUsers, deleteSpacesAndUsers } from '../../../common/lib/ // eslint-disable-next-line import/no-default-export export default ({ loadTestFile, getService }: FtrProviderContext): void => { describe('rules security and spaces enabled: basic', function () { - // Fastest ciGroup for the moment. - this.tags('ciGroup5'); - before(async () => { await createSpacesAndUsers(getService); }); @@ -27,8 +24,9 @@ export default ({ loadTestFile, getService }: FtrProviderContext): void => { // loadTestFile(require.resolve('./get_alert_by_id')); // loadTestFile(require.resolve('./update_alert')); // loadTestFile(require.resolve('./bulk_update_alerts')); - // loadTestFile(require.resolve('./find_alerts')); - // loadTestFile(require.resolve('./get_alerts_index')); + + loadTestFile(require.resolve('./get_alerts_index')); + loadTestFile(require.resolve('./find_alerts')); loadTestFile(require.resolve('./search_strategy')); }); }; diff --git a/x-pack/test/rule_registry/security_and_spaces/tests/basic/search_strategy.ts b/x-pack/test/rule_registry/security_and_spaces/tests/basic/search_strategy.ts index 27dfd7a9c9a90..dfda18b5a0c05 100644 --- a/x-pack/test/rule_registry/security_and_spaces/tests/basic/search_strategy.ts +++ b/x-pack/test/rule_registry/security_and_spaces/tests/basic/search_strategy.ts @@ -19,7 +19,7 @@ import { waitForSignalsToBePresent, waitForRuleSuccessOrStatus, } from '../../../../detection_engine_api_integration/utils'; -import { ID } from '../../../../detection_engine_api_integration/security_and_spaces/tests/generating_signals'; +import { ID } from '../../../../detection_engine_api_integration/security_and_spaces/group1/generating_signals'; import { obsOnlySpacesAllEsRead, obsOnlySpacesAll, @@ -43,7 +43,7 @@ export default ({ getService }: FtrProviderContext) => { const SPACE1 = 'space1'; // Failing: See https://github.com/elastic/kibana/issues/129219 - describe.skip('ruleRegistryAlertsSearchStrategy', () => { + describe('ruleRegistryAlertsSearchStrategy', () => { let kibanaVersion: string; before(async () => { kibanaVersion = await kbnClient.version.get(); diff --git a/x-pack/test/rule_registry/security_and_spaces/tests/trial/index.ts b/x-pack/test/rule_registry/security_and_spaces/tests/trial/index.ts index 3e13d64b936a4..53a788f6c7829 100644 --- a/x-pack/test/rule_registry/security_and_spaces/tests/trial/index.ts +++ b/x-pack/test/rule_registry/security_and_spaces/tests/trial/index.ts @@ -39,9 +39,6 @@ import { export default ({ loadTestFile, getService }: FtrProviderContext): void => { // FAILING: https://github.com/elastic/kibana/issues/110153 describe.skip('rules security and spaces enabled: trial', function () { - // Fastest ciGroup for the moment. - this.tags('ciGroup5'); - before(async () => { await createSpaces(getService); await createUsersAndRoles( diff --git a/x-pack/test/rule_registry/spaces_only/tests/basic/index.ts b/x-pack/test/rule_registry/spaces_only/tests/basic/index.ts index aeb2b085ad379..f47b4b6254ff2 100644 --- a/x-pack/test/rule_registry/spaces_only/tests/basic/index.ts +++ b/x-pack/test/rule_registry/spaces_only/tests/basic/index.ts @@ -11,9 +11,6 @@ import { createSpaces, deleteSpaces } from '../../../common/lib/authentication'; // eslint-disable-next-line import/no-default-export export default ({ loadTestFile, getService }: FtrProviderContext): void => { describe('rule registry spaces only: trial', function () { - // Fastest ciGroup for the moment. - this.tags('ciGroup5'); - before(async () => { await createSpaces(getService); }); diff --git a/x-pack/test/rule_registry/spaces_only/tests/trial/get_alert_by_id.ts b/x-pack/test/rule_registry/spaces_only/tests/trial/get_alert_by_id.ts index a9969fd3bfd52..d248858f19f6d 100644 --- a/x-pack/test/rule_registry/spaces_only/tests/trial/get_alert_by_id.ts +++ b/x-pack/test/rule_registry/spaces_only/tests/trial/get_alert_by_id.ts @@ -31,10 +31,9 @@ export default ({ getService }: FtrProviderContext) => { .get(`${getSpaceUrlPrefix(SPACE1)}${ALERTS_INDEX_URL}`) .set('kbn-xsrf', 'true') .expect(200); - const observabilityIndex = indexNames?.index_name?.find( - (indexName) => indexName === APM_ALERT_INDEX - ); - expect(observabilityIndex).to.eql(APM_ALERT_INDEX); // assert this here so we can use constants in the dynamically-defined test cases below + expect( + indexNames?.index_name?.filter((indexName) => indexName.startsWith(APM_ALERT_INDEX)).length + ).to.eql(1); // assert this here so we can use constants in the dynamically-defined test cases below }; const getSecuritySolutionIndexName = async (user: User) => { @@ -43,10 +42,11 @@ export default ({ getService }: FtrProviderContext) => { .get(`${getSpaceUrlPrefix(SPACE1)}${ALERTS_INDEX_URL}`) .set('kbn-xsrf', 'true') .expect(200); - const securitySolution = indexNames?.index_name?.find((indexName) => - indexName.startsWith(SECURITY_SOLUTION_ALERT_INDEX) - ); - expect(securitySolution).to.eql(`${SECURITY_SOLUTION_ALERT_INDEX}-${SPACE1}`); // assert this here so we can use constants in the dynamically-defined test cases below + expect( + indexNames?.index_name?.filter((indexName) => + indexName.startsWith(`${SECURITY_SOLUTION_ALERT_INDEX}-${SPACE1}`) + ).length + ).to.eql(1); // assert this here so we can use constants in the dynamically-defined test cases below }; describe('Alerts - GET - RBAC', () => { diff --git a/x-pack/test/rule_registry/spaces_only/tests/trial/index.ts b/x-pack/test/rule_registry/spaces_only/tests/trial/index.ts index 19e35019eb50a..d519dd16dab45 100644 --- a/x-pack/test/rule_registry/spaces_only/tests/trial/index.ts +++ b/x-pack/test/rule_registry/spaces_only/tests/trial/index.ts @@ -11,9 +11,6 @@ import { createSpaces, deleteSpaces } from '../../../common/lib/authentication'; // eslint-disable-next-line import/no-default-export export default ({ loadTestFile, getService }: FtrProviderContext): void => { describe('rule registry spaces only: trial', function () { - // Fastest ciGroup for the moment. - this.tags('ciGroup5'); - before(async () => { await createSpaces(getService); }); diff --git a/x-pack/test/rule_registry/spaces_only/tests/trial/update_alert.ts b/x-pack/test/rule_registry/spaces_only/tests/trial/update_alert.ts index 5b5bc64a5a9bd..31c3cfdecb9ee 100644 --- a/x-pack/test/rule_registry/spaces_only/tests/trial/update_alert.ts +++ b/x-pack/test/rule_registry/spaces_only/tests/trial/update_alert.ts @@ -31,10 +31,9 @@ export default ({ getService }: FtrProviderContext) => { .get(`${getSpaceUrlPrefix(SPACE1)}${ALERTS_INDEX_URL}`) .set('kbn-xsrf', 'true') .expect(200); - const observabilityIndex = indexNames?.index_name?.find( - (indexName) => indexName === APM_ALERT_INDEX - ); - expect(observabilityIndex).to.eql(APM_ALERT_INDEX); // assert this here so we can use constants in the dynamically-defined test cases below + expect( + indexNames?.index_name?.filter((indexName) => indexName.startsWith(APM_ALERT_INDEX)).length + ).to.eql(1); // assert this here so we can use constants in the dynamically-defined test cases below }; const getSecuritySolutionIndexName = async (user: User) => { @@ -43,10 +42,11 @@ export default ({ getService }: FtrProviderContext) => { .get(`${getSpaceUrlPrefix(SPACE1)}${ALERTS_INDEX_URL}`) .set('kbn-xsrf', 'true') .expect(200); - const securitySolution = indexNames?.index_name?.find((indexName) => - indexName.startsWith(SECURITY_SOLUTION_ALERT_INDEX) - ); - expect(securitySolution).to.eql(`${SECURITY_SOLUTION_ALERT_INDEX}-${SPACE1}`); // assert this here so we can use constants in the dynamically-defined test cases below + expect( + indexNames?.index_name?.filter((indexName) => + indexName.startsWith(`${SECURITY_SOLUTION_ALERT_INDEX}-${SPACE1}`) + ).length + ).to.eql(1); // assert this here so we can use constants in the dynamically-defined test cases below }; describe('Alert - Update - RBAC - spaces', () => { diff --git a/x-pack/test/saved_object_api_integration/common/config.ts b/x-pack/test/saved_object_api_integration/common/config.ts index 8ca74c7fcea49..32d2d73d5cebc 100644 --- a/x-pack/test/saved_object_api_integration/common/config.ts +++ b/x-pack/test/saved_object_api_integration/common/config.ts @@ -24,7 +24,9 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) const config = { kibana: { api: await readConfigFile(path.resolve(REPO_ROOT, 'test/api_integration/config.js')), - functional: await readConfigFile(require.resolve('../../../../test/functional/config.js')), + functional: await readConfigFile( + require.resolve('../../../../test/functional/config.base.js') + ), }, xpack: { api: await readConfigFile(require.resolve('../../api_integration/config.ts')), diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/index.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/index.ts index 740b9d91927bf..4eb0b90480314 100644 --- a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/index.ts +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/index.ts @@ -13,8 +13,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { const supertest = getService('supertest'); describe('saved objects security and spaces enabled', function () { - this.tags('ciGroup20'); - before(async () => { await createUsersAndRoles(es, supertest); }); diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/index.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/index.ts index c6bdbde07fc02..1be7ed754a971 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/index.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../common/ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('saved objects spaces only enabled', function () { - this.tags('ciGroup5'); - loadTestFile(require.resolve('./bulk_create')); loadTestFile(require.resolve('./bulk_get')); loadTestFile(require.resolve('./bulk_resolve')); diff --git a/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/index.ts b/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/index.ts index 54740e73aba65..f28b3cd615887 100644 --- a/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/index.ts +++ b/x-pack/test/saved_object_tagging/api_integration/security_and_spaces/apis/index.ts @@ -11,8 +11,6 @@ import { createUsersAndRoles } from '../../../common/lib'; // eslint-disable-next-line import/no-default-export export default function ({ getService, loadTestFile }: FtrProviderContext) { describe('saved objects tagging API - security and spaces integration', function () { - this.tags('ciGroup10'); - before(async () => { await createUsersAndRoles(getService); }); diff --git a/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/index.ts b/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/index.ts index 3d5b0b9c3b989..f291d2537ed02 100644 --- a/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/index.ts +++ b/x-pack/test/saved_object_tagging/api_integration/tagging_api/apis/index.ts @@ -10,8 +10,6 @@ import { FtrProviderContext } from '../services'; // eslint-disable-next-line import/no-default-export export default function ({ loadTestFile }: FtrProviderContext) { describe('saved objects tagging API', function () { - this.tags('ciGroup12'); - loadTestFile(require.resolve('./delete')); loadTestFile(require.resolve('./create')); loadTestFile(require.resolve('./update')); diff --git a/x-pack/test/saved_object_tagging/functional/config.ts b/x-pack/test/saved_object_tagging/functional/config.ts index 6ad1f05e2be5b..1c40864f2fa02 100644 --- a/x-pack/test/saved_object_tagging/functional/config.ts +++ b/x-pack/test/saved_object_tagging/functional/config.ts @@ -11,7 +11,7 @@ import { services, pageObjects } from './ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default async function ({ readConfigFile }: FtrConfigProviderContext) { const kibanaFunctionalConfig = await readConfigFile( - require.resolve('../../functional/config.js') + require.resolve('../../functional/config.base.js') ); return { diff --git a/x-pack/test/saved_object_tagging/functional/tests/index.ts b/x-pack/test/saved_object_tagging/functional/tests/index.ts index fbf0954382dd1..2d79d0a7a45ec 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/index.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/index.ts @@ -11,8 +11,6 @@ import { createUsersAndRoles } from '../../common/lib'; // eslint-disable-next-line import/no-default-export export default function ({ loadTestFile, getService }: FtrProviderContext) { describe('saved objects tagging - functional tests', function () { - this.tags('ciGroup14'); - before(async () => { await createUsersAndRoles(getService); }); diff --git a/x-pack/test/saved_objects_field_count/config.ts b/x-pack/test/saved_objects_field_count/config.ts index 7967b6c4f3b9c..ab5154adb8d59 100644 --- a/x-pack/test/saved_objects_field_count/config.ts +++ b/x-pack/test/saved_objects_field_count/config.ts @@ -6,7 +6,6 @@ */ import { FtrConfigProviderContext } from '@kbn/test'; -import { testRunner } from './runner'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { const kibanaCommonTestsConfig = await readConfigFile( @@ -16,7 +15,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { return { ...kibanaCommonTestsConfig.getAll(), - testRunner, + testFiles: [require.resolve('./test')], esTestCluster: { license: 'trial', @@ -28,5 +27,9 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { ...kibanaCommonTestsConfig.get('kbnTestServer'), serverArgs: [...kibanaCommonTestsConfig.get('kbnTestServer.serverArgs')], }, + + junit: { + reportName: 'Saved Object Field Count', + }, }; } diff --git a/x-pack/test/saved_objects_field_count/runner.ts b/x-pack/test/saved_objects_field_count/runner.ts deleted file mode 100644 index b88f2129ba64d..0000000000000 --- a/x-pack/test/saved_objects_field_count/runner.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { CiStatsReporter } from '@kbn/ci-stats-reporter'; -import { FtrProviderContext } from '../functional/ftr_provider_context'; - -const IGNORED_FIELDS = [ - // The following fields are returned by the _field_caps API but aren't counted - // towards the index field limit. - '_seq_no', - '_id', - '_version', - '_field_names', - '_ignored', - '_feature', - '_index', - '_routing', - '_source', - '_type', - '_nested_path', - '_timestamp', - // migrationVersion is dynamic so will be anywhere between 1..type count - // depending on which objects are present in the index when querying the - // field caps API. See https://github.com/elastic/kibana/issues/70815 - 'migrationVersion', -]; - -export async function testRunner({ getService }: FtrProviderContext) { - const log = getService('log'); - const es = getService('es'); - - const reporter = CiStatsReporter.fromEnv(log); - - log.debug('Saved Objects field count metrics starting'); - - const { fields } = await es.fieldCaps({ - index: '.kibana', - fields: '*', - }); - - const fieldCountPerTypeMap: Map = Object.keys(fields) - .map((f) => f.split('.')[0]) - .filter((f) => !IGNORED_FIELDS.includes(f)) - .reduce((accumulator, f) => { - accumulator.set(f, accumulator.get(f) + 1 || 1); - return accumulator; - }, new Map()); - - const metrics = Array.from(fieldCountPerTypeMap.entries()) - .sort((a, b) => a[0].localeCompare(b[0])) - .map(([fieldType, count]) => ({ - group: 'Saved Objects .kibana field count', - id: fieldType, - value: count, - })); - - log.debug( - 'Saved Objects field count metrics:\n', - metrics.map(({ id, value }) => `${id}:${value}`).join('\n') - ); - await reporter.metrics(metrics); - log.debug('Saved Objects field count metrics done'); -} diff --git a/x-pack/test/saved_objects_field_count/test.ts b/x-pack/test/saved_objects_field_count/test.ts new file mode 100644 index 0000000000000..e931b1aa5ef26 --- /dev/null +++ b/x-pack/test/saved_objects_field_count/test.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CiStatsReporter } from '@kbn/ci-stats-reporter'; +import { FtrProviderContext } from '../functional/ftr_provider_context'; + +const IGNORED_FIELDS = [ + // The following fields are returned by the _field_caps API but aren't counted + // towards the index field limit. + '_seq_no', + '_id', + '_version', + '_field_names', + '_ignored', + '_feature', + '_index', + '_routing', + '_source', + '_type', + '_nested_path', + '_timestamp', + // migrationVersion is dynamic so will be anywhere between 1..type count + // depending on which objects are present in the index when querying the + // field caps API. See https://github.com/elastic/kibana/issues/70815 + 'migrationVersion', +]; + +// eslint-disable-next-line import/no-default-export +export default function ({ getService }: FtrProviderContext) { + const log = getService('log'); + const es = getService('es'); + + describe('Saved Objects Field Count', () => { + it('capture', async () => { + const reporter = CiStatsReporter.fromEnv(log); + + log.debug('Saved Objects field count metrics starting'); + + const { fields } = await es.fieldCaps({ + index: '.kibana', + fields: '*', + }); + + const fieldCountPerTypeMap: Map = Object.keys(fields) + .map((f) => f.split('.')[0]) + .filter((f) => !IGNORED_FIELDS.includes(f)) + .reduce((accumulator, f) => { + accumulator.set(f, accumulator.get(f) + 1 || 1); + return accumulator; + }, new Map()); + + const metrics = Array.from(fieldCountPerTypeMap.entries()) + .sort((a, b) => a[0].localeCompare(b[0])) + .map(([fieldType, count]) => ({ + group: 'Saved Objects .kibana field count', + id: fieldType, + value: count, + })); + + log.debug( + 'Saved Objects field count metrics:\n', + metrics.map(({ id, value }) => `${id}:${value}`).join('\n') + ); + + await reporter.metrics(metrics); + log.debug('Saved Objects field count metrics done'); + }); + }); +} diff --git a/x-pack/test/screenshot_creation/apps/ml_docs/index.ts b/x-pack/test/screenshot_creation/apps/ml_docs/index.ts index 9a12153682618..82460db174add 100644 --- a/x-pack/test/screenshot_creation/apps/ml_docs/index.ts +++ b/x-pack/test/screenshot_creation/apps/ml_docs/index.ts @@ -16,7 +16,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { const ml = getService('ml'); describe('machine learning docs', function () { - this.tags(['mlqa']); + this.tags(['ml']); before(async () => { await ml.testResources.installAllKibanaSampleData(); diff --git a/x-pack/test/screenshot_creation/config.ts b/x-pack/test/screenshot_creation/config.ts index 659034e9fbe8b..18dda361ac2cd 100644 --- a/x-pack/test/screenshot_creation/config.ts +++ b/x-pack/test/screenshot_creation/config.ts @@ -9,7 +9,9 @@ import { FtrConfigProviderContext } from '@kbn/test'; import { services } from './services'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xpackFunctionalConfig = await readConfigFile(require.resolve('../functional/config.js')); + const xpackFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); return { // default to the xpack functional config diff --git a/x-pack/test/search_sessions_integration/config.ts b/x-pack/test/search_sessions_integration/config.ts index 9dc542038a48a..2d570a607c746 100644 --- a/x-pack/test/search_sessions_integration/config.ts +++ b/x-pack/test/search_sessions_integration/config.ts @@ -10,7 +10,9 @@ import { FtrConfigProviderContext } from '@kbn/test'; import { services } from '../functional/services'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xpackFunctionalConfig = await readConfigFile(require.resolve('../functional/config')); + const xpackFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); return { // default to the xpack functional config diff --git a/x-pack/test/search_sessions_integration/tests/apps/dashboard/async_search/index.ts b/x-pack/test/search_sessions_integration/tests/apps/dashboard/async_search/index.ts index b87f43f73ed2e..9465de1de5922 100644 --- a/x-pack/test/search_sessions_integration/tests/apps/dashboard/async_search/index.ts +++ b/x-pack/test/search_sessions_integration/tests/apps/dashboard/async_search/index.ts @@ -14,8 +14,6 @@ export default function ({ loadTestFile, getService, getPageObjects }: FtrProvid const searchSessions = getService('searchSessions'); describe('Dashboard', function () { - this.tags('ciGroup5'); - before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); await esArchiver.load('x-pack/test/functional/es_archives/dashboard/async_search'); diff --git a/x-pack/test/search_sessions_integration/tests/apps/dashboard/session_sharing/index.ts b/x-pack/test/search_sessions_integration/tests/apps/dashboard/session_sharing/index.ts index 6a13dc268b705..1ff11eb988456 100644 --- a/x-pack/test/search_sessions_integration/tests/apps/dashboard/session_sharing/index.ts +++ b/x-pack/test/search_sessions_integration/tests/apps/dashboard/session_sharing/index.ts @@ -13,8 +13,6 @@ export default function ({ loadTestFile, getService, getPageObjects }: FtrProvid const PageObjects = getPageObjects(['common']); describe('Search session sharing', function () { - this.tags('ciGroup5'); - before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' }); diff --git a/x-pack/test/search_sessions_integration/tests/apps/discover/index.ts b/x-pack/test/search_sessions_integration/tests/apps/discover/index.ts index 1f8d219674428..2af94730ff918 100644 --- a/x-pack/test/search_sessions_integration/tests/apps/discover/index.ts +++ b/x-pack/test/search_sessions_integration/tests/apps/discover/index.ts @@ -14,8 +14,6 @@ export default function ({ loadTestFile, getService, getPageObjects }: FtrProvid const searchSessions = getService('searchSessions'); describe('Discover', function () { - this.tags('ciGroup5'); - before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' }); diff --git a/x-pack/test/search_sessions_integration/tests/apps/lens/index.ts b/x-pack/test/search_sessions_integration/tests/apps/lens/index.ts index 5e45db7bd7233..978c5bdec0df5 100644 --- a/x-pack/test/search_sessions_integration/tests/apps/lens/index.ts +++ b/x-pack/test/search_sessions_integration/tests/apps/lens/index.ts @@ -12,8 +12,6 @@ export default function ({ loadTestFile, getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); describe('lens search sessions', function () { - this.tags('ciGroup5'); - before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' }); diff --git a/x-pack/test/search_sessions_integration/tests/apps/management/search_sessions/index.ts b/x-pack/test/search_sessions_integration/tests/apps/management/search_sessions/index.ts index b72d8db06a1d8..60e4ea1b3bfbb 100644 --- a/x-pack/test/search_sessions_integration/tests/apps/management/search_sessions/index.ts +++ b/x-pack/test/search_sessions_integration/tests/apps/management/search_sessions/index.ts @@ -12,8 +12,6 @@ export default function ({ loadTestFile, getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); describe('search sessions management', function () { - this.tags('ciGroup5'); - before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); await esArchiver.load('x-pack/test/functional/es_archives/dashboard/async_search'); diff --git a/x-pack/test/security_api_integration/tests/anonymous/index.ts b/x-pack/test/security_api_integration/tests/anonymous/index.ts index 08f075950a57f..0f976589483a8 100644 --- a/x-pack/test/security_api_integration/tests/anonymous/index.ts +++ b/x-pack/test/security_api_integration/tests/anonymous/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security APIs - Anonymous access', function () { - this.tags('ciGroup6'); loadTestFile(require.resolve('./login')); loadTestFile(require.resolve('./capabilities')); }); diff --git a/x-pack/test/security_api_integration/tests/audit/index.ts b/x-pack/test/security_api_integration/tests/audit/index.ts index 14628bbc51e89..96b2ceb5ae3a7 100644 --- a/x-pack/test/security_api_integration/tests/audit/index.ts +++ b/x-pack/test/security_api_integration/tests/audit/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security APIs - Audit Log', function () { - this.tags('ciGroup6'); loadTestFile(require.resolve('./audit_log')); }); } diff --git a/x-pack/test/security_api_integration/tests/http_bearer/index.ts b/x-pack/test/security_api_integration/tests/http_bearer/index.ts index 4dbad2660ebaa..c796c0be9befb 100644 --- a/x-pack/test/security_api_integration/tests/http_bearer/index.ts +++ b/x-pack/test/security_api_integration/tests/http_bearer/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security APIs - HTTP Bearer', function () { - this.tags('ciGroup6'); loadTestFile(require.resolve('./header')); }); } diff --git a/x-pack/test/security_api_integration/tests/http_no_auth_providers/index.ts b/x-pack/test/security_api_integration/tests/http_no_auth_providers/index.ts index 652bcc419e243..23096b2449c9f 100644 --- a/x-pack/test/security_api_integration/tests/http_no_auth_providers/index.ts +++ b/x-pack/test/security_api_integration/tests/http_no_auth_providers/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security APIs - HTTP no authentication providers are enabled', function () { - this.tags('ciGroup6'); loadTestFile(require.resolve('./authentication')); }); } diff --git a/x-pack/test/security_api_integration/tests/kerberos/index.ts b/x-pack/test/security_api_integration/tests/kerberos/index.ts index cec92939a5194..828ce7220458f 100644 --- a/x-pack/test/security_api_integration/tests/kerberos/index.ts +++ b/x-pack/test/security_api_integration/tests/kerberos/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security APIs - Kerberos', function () { - this.tags('ciGroup31'); - loadTestFile(require.resolve('./kerberos_login')); }); } diff --git a/x-pack/test/security_api_integration/tests/login_selector/index.ts b/x-pack/test/security_api_integration/tests/login_selector/index.ts index a38a0acc68cca..e3698340d3967 100644 --- a/x-pack/test/security_api_integration/tests/login_selector/index.ts +++ b/x-pack/test/security_api_integration/tests/login_selector/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security APIs - Login Selector', function () { - this.tags('ciGroup6'); loadTestFile(require.resolve('./basic_functionality')); }); } diff --git a/x-pack/test/security_api_integration/tests/oidc/authorization_code_flow/index.ts b/x-pack/test/security_api_integration/tests/oidc/authorization_code_flow/index.ts index 858b8e2fbb750..2c8edc1569bd2 100644 --- a/x-pack/test/security_api_integration/tests/oidc/authorization_code_flow/index.ts +++ b/x-pack/test/security_api_integration/tests/oidc/authorization_code_flow/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security APIs - OIDC (Authorization Code Flow)', function () { - this.tags('ciGroup6'); loadTestFile(require.resolve('./oidc_auth')); }); } diff --git a/x-pack/test/security_api_integration/tests/oidc/implicit_flow/index.ts b/x-pack/test/security_api_integration/tests/oidc/implicit_flow/index.ts index c42014661b915..7479ba8e7bd81 100644 --- a/x-pack/test/security_api_integration/tests/oidc/implicit_flow/index.ts +++ b/x-pack/test/security_api_integration/tests/oidc/implicit_flow/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security APIs - OIDC (Implicit Flow)', function () { - this.tags('ciGroup6'); loadTestFile(require.resolve('./oidc_auth')); }); } diff --git a/x-pack/test/security_api_integration/tests/pki/index.ts b/x-pack/test/security_api_integration/tests/pki/index.ts index c3b733d0b31f8..9926f16619898 100644 --- a/x-pack/test/security_api_integration/tests/pki/index.ts +++ b/x-pack/test/security_api_integration/tests/pki/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security APIs - PKI', function () { - this.tags('ciGroup22'); - loadTestFile(require.resolve('./pki_auth')); }); } diff --git a/x-pack/test/security_api_integration/tests/saml/index.ts b/x-pack/test/security_api_integration/tests/saml/index.ts index e7ffdbd410de3..3597f1a6104ec 100644 --- a/x-pack/test/security_api_integration/tests/saml/index.ts +++ b/x-pack/test/security_api_integration/tests/saml/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security APIs - SAML', function () { - this.tags('ciGroup27'); - loadTestFile(require.resolve('./saml_login')); }); } diff --git a/x-pack/test/security_api_integration/tests/session_idle/index.ts b/x-pack/test/security_api_integration/tests/session_idle/index.ts index 76457ee7ad0c7..6966b9f2ed2c7 100644 --- a/x-pack/test/security_api_integration/tests/session_idle/index.ts +++ b/x-pack/test/security_api_integration/tests/session_idle/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security APIs - Session Idle', function () { - this.tags('ciGroup18'); - loadTestFile(require.resolve('./cleanup')); loadTestFile(require.resolve('./extension')); }); diff --git a/x-pack/test/security_api_integration/tests/session_invalidate/index.ts b/x-pack/test/security_api_integration/tests/session_invalidate/index.ts index 6408e4cfbd43d..dcfb3d7fc5259 100644 --- a/x-pack/test/security_api_integration/tests/session_invalidate/index.ts +++ b/x-pack/test/security_api_integration/tests/session_invalidate/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security APIs - Session Invalidate', function () { - this.tags('ciGroup6'); - loadTestFile(require.resolve('./invalidate')); }); } diff --git a/x-pack/test/security_api_integration/tests/session_lifespan/index.ts b/x-pack/test/security_api_integration/tests/session_lifespan/index.ts index 15522da907958..e297805b4ab3c 100644 --- a/x-pack/test/security_api_integration/tests/session_lifespan/index.ts +++ b/x-pack/test/security_api_integration/tests/session_lifespan/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security APIs - Session Lifespan', function () { - this.tags('ciGroup6'); - loadTestFile(require.resolve('./cleanup')); }); } diff --git a/x-pack/test/security_api_integration/tests/token/index.ts b/x-pack/test/security_api_integration/tests/token/index.ts index 88c82125ee1d9..54717dc1c8617 100644 --- a/x-pack/test/security_api_integration/tests/token/index.ts +++ b/x-pack/test/security_api_integration/tests/token/index.ts @@ -9,7 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security APIs - Token', function () { - this.tags('ciGroup6'); loadTestFile(require.resolve('./login')); loadTestFile(require.resolve('./logout')); loadTestFile(require.resolve('./header')); diff --git a/x-pack/test/security_functional/login_selector.config.ts b/x-pack/test/security_functional/login_selector.config.ts index aa145e2ec6216..d2035a9b228e8 100644 --- a/x-pack/test/security_functional/login_selector.config.ts +++ b/x-pack/test/security_functional/login_selector.config.ts @@ -17,7 +17,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { require.resolve('../../../test/common/config.js') ); const kibanaFunctionalConfig = await readConfigFile( - require.resolve('../../../test/functional/config.js') + require.resolve('../../../test/functional/config.base.js') ); const kibanaPort = kibanaFunctionalConfig.get('servers.kibana.port'); diff --git a/x-pack/test/security_functional/oidc.config.ts b/x-pack/test/security_functional/oidc.config.ts index 9c00960671e03..6476bbb501b77 100644 --- a/x-pack/test/security_functional/oidc.config.ts +++ b/x-pack/test/security_functional/oidc.config.ts @@ -17,7 +17,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { require.resolve('../../../test/common/config.js') ); const kibanaFunctionalConfig = await readConfigFile( - require.resolve('../../../test/functional/config.js') + require.resolve('../../../test/functional/config.base.js') ); const kibanaPort = kibanaFunctionalConfig.get('servers.kibana.port'); diff --git a/x-pack/test/security_functional/saml.config.ts b/x-pack/test/security_functional/saml.config.ts index 264197c961123..60a934a712bbf 100644 --- a/x-pack/test/security_functional/saml.config.ts +++ b/x-pack/test/security_functional/saml.config.ts @@ -17,7 +17,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { require.resolve('../../../test/common/config.js') ); const kibanaFunctionalConfig = await readConfigFile( - require.resolve('../../../test/functional/config.js') + require.resolve('../../../test/functional/config.base.js') ); const kibanaPort = kibanaFunctionalConfig.get('servers.kibana.port'); diff --git a/x-pack/test/security_functional/tests/login_selector/index.ts b/x-pack/test/security_functional/tests/login_selector/index.ts index 1a34fc5eac6d9..bf3cc557f0bd7 100644 --- a/x-pack/test/security_functional/tests/login_selector/index.ts +++ b/x-pack/test/security_functional/tests/login_selector/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security app - login selector', function () { - this.tags('ciGroup13'); - loadTestFile(require.resolve('./basic_functionality')); loadTestFile(require.resolve('./auth_provider_hint')); }); diff --git a/x-pack/test/security_functional/tests/oidc/index.ts b/x-pack/test/security_functional/tests/oidc/index.ts index cd328384febd3..37490a0193089 100644 --- a/x-pack/test/security_functional/tests/oidc/index.ts +++ b/x-pack/test/security_functional/tests/oidc/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security app - OIDC interactions', function () { - this.tags('ciGroup13'); - loadTestFile(require.resolve('./url_capture')); }); } diff --git a/x-pack/test/security_functional/tests/saml/index.ts b/x-pack/test/security_functional/tests/saml/index.ts index 66a497db8af40..ebf97ebf8edfb 100644 --- a/x-pack/test/security_functional/tests/saml/index.ts +++ b/x-pack/test/security_functional/tests/saml/index.ts @@ -9,8 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('security app - SAML interactions', function () { - this.tags('ciGroup13'); - loadTestFile(require.resolve('./url_capture')); }); } diff --git a/x-pack/test/security_solution_cypress/config.firefox.ts b/x-pack/test/security_solution_cypress/config.firefox.ts index 2a2ce410850ff..c29f47708a170 100644 --- a/x-pack/test/security_solution_cypress/config.firefox.ts +++ b/x-pack/test/security_solution_cypress/config.firefox.ts @@ -16,7 +16,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { require.resolve('../../../test/common/config.js') ); const xpackFunctionalTestsConfig = await readConfigFile( - require.resolve('../functional/config.js') + require.resolve('../functional/config.base.js') ); return { diff --git a/x-pack/test/security_solution_cypress/config.ts b/x-pack/test/security_solution_cypress/config.ts index 7f196880e8fca..4b5b2c361c1b9 100644 --- a/x-pack/test/security_solution_cypress/config.ts +++ b/x-pack/test/security_solution_cypress/config.ts @@ -14,7 +14,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { require.resolve('../../../test/common/config.js') ); const xpackFunctionalTestsConfig = await readConfigFile( - require.resolve('../functional/config.js') + require.resolve('../functional/config.base.js') ); return { diff --git a/x-pack/test/security_solution_cypress/response_ops_cli_config.ts b/x-pack/test/security_solution_cypress/response_ops_cli_config.ts new file mode 100644 index 0000000000000..d70837bff55e7 --- /dev/null +++ b/x-pack/test/security_solution_cypress/response_ops_cli_config.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrConfigProviderContext } from '@kbn/test'; + +import { SecuritySolutionCypressCliResponseOpsTestRunner } from './runner'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const securitySolutionCypressConfig = await readConfigFile(require.resolve('./config.ts')); + return { + ...securitySolutionCypressConfig.getAll(), + + testRunner: SecuritySolutionCypressCliResponseOpsTestRunner, + }; +} diff --git a/x-pack/test/security_solution_cypress/runner.ts b/x-pack/test/security_solution_cypress/runner.ts index 21404b8b2e47f..6ae5812c56658 100644 --- a/x-pack/test/security_solution_cypress/runner.ts +++ b/x-pack/test/security_solution_cypress/runner.ts @@ -38,6 +38,33 @@ export async function SecuritySolutionCypressCliTestRunner({ getService }: FtrPr }); } +export async function SecuritySolutionCypressCliResponseOpsTestRunner({ + getService, +}: FtrProviderContext) { + const log = getService('log'); + const config = getService('config'); + const esArchiver = getService('esArchiver'); + + await esArchiver.load('x-pack/test/security_solution_cypress/es_archives/auditbeat'); + + await withProcRunner(log, async (procs) => { + await procs.run('cypress', { + cmd: 'yarn', + args: ['cypress:run:respops'], + cwd: resolve(__dirname, '../../plugins/security_solution'), + env: { + FORCE_COLOR: '1', + CYPRESS_BASE_URL: Url.format(config.get('servers.kibana')), + CYPRESS_ELASTICSEARCH_URL: Url.format(config.get('servers.elasticsearch')), + CYPRESS_ELASTICSEARCH_USERNAME: config.get('servers.elasticsearch.username'), + CYPRESS_ELASTICSEARCH_PASSWORD: config.get('servers.elasticsearch.password'), + ...process.env, + }, + wait: true, + }); + }); +} + export async function SecuritySolutionCypressCliCasesTestRunner({ getService, }: FtrProviderContext) { diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts index 368783b0efd1c..0168a15f21b6e 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts @@ -34,29 +34,29 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { 'Last active', 'Actions', ], - ['Host-9fafsc3tqe', 'x', 'x', 'Warning', 'Windows', '10.231.117.28', '7.17.12', 'x', ''], [ 'Host-ku5jy6j0pw', 'x', 'x', - 'Warning', + 'Unsupported', 'Windows', - '10.246.87.11, 10.145.117.106,10.109.242.136', + '10.12.215.130, 10.130.188.228,10.19.102.141', '7.0.13', 'x', '', ], [ - 'Host-o07wj6uaa5', + 'Host-ntr4rkj24m', 'x', 'x', - 'Failure', + 'Success', 'Windows', - '10.82.134.220, 10.47.25.170', - '7.11.13', + '10.36.46.252, 10.222.152.110', + '7.4.13', 'x', '', ], + ['Host-q9qenwrl9k', 'x', 'x', 'Warning', 'Windows', '10.206.226.90', '7.11.10', 'x', ''], ]; const formattedTableData = async () => { @@ -82,7 +82,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await deleteAllDocsFromMetadataUnitedIndex(getService); await pageObjects.endpoint.navigateToEndpointList(); }); - it('finds no data in list and prompts onboarding to add policy', async () => { await testSubjects.exists('emptyPolicyTable'); }); @@ -99,7 +98,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { after(async () => { await deleteAllDocsFromMetadataCurrentIndex(getService); await deleteAllDocsFromMetadataUnitedIndex(getService); - await endpointTestResources.unloadEndpointData(indexedData); + if (indexedData) { + await endpointTestResources.unloadEndpointData(indexedData); + } }); it('finds page title', async () => { @@ -209,9 +210,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { 'Host-ku5jy6j0pw', 'x', 'x', - 'Warning', + 'Unsupported', 'Windows', - '10.246.87.11, 10.145.117.106,10.109.242.136', + '10.12.215.130, 10.130.188.228,10.19.102.141', '7.0.13', 'x', '', diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts index e57895f4f32b7..f74bd3b91cfce 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts @@ -16,7 +16,6 @@ export default function (providerContext: FtrProviderContext) { const { loadTestFile, getService } = providerContext; describe('endpoint', function () { - this.tags('ciGroup7'); const ingestManager = getService('ingestManager'); const log = getService('log'); const endpointTestResources = getService('endpointTestResources'); @@ -36,6 +35,7 @@ export default function (providerContext: FtrProviderContext) { await endpointTestResources.installOrUpgradeEndpointFleetPackage(); }); loadTestFile(require.resolve('./endpoint_list')); + loadTestFile(require.resolve('./policy_list')); loadTestFile(require.resolve('./policy_details')); loadTestFile(require.resolve('./endpoint_telemetry')); loadTestFile(require.resolve('./trusted_apps_list')); diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts index 9bf6a19b4a589..c95afc743c839 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts @@ -331,6 +331,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it('should show event filters card and link should go back to policy', async () => { await testSubjects.existOrFail('eventFilters-fleet-integration-card'); + const eventFiltersCard = await testSubjects.find('eventFilters-fleet-integration-card'); + await pageObjects.ingestManagerCreatePackagePolicy.scrollToCenterOfWindow(eventFiltersCard); await (await testSubjects.find('eventFilters-link-to-exceptions')).click(); await testSubjects.existOrFail('policyDetailsPage'); await (await testSubjects.find('policyDetailsBackLink')).click(); diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts new file mode 100644 index 0000000000000..840c36a558ba0 --- /dev/null +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts @@ -0,0 +1,112 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { IndexedHostsAndAlertsResponse } from '@kbn/security-solution-plugin/common/endpoint/index_data'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { PolicyTestResourceInfo } from '../../services/endpoint_policy'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const browser = getService('browser'); + const pageObjects = getPageObjects([ + 'common', + 'endpoint', + 'policy', + 'endpointPageUtils', + 'ingestManagerCreatePackagePolicy', + 'trustedApps', + ]); + const testSubjects = getService('testSubjects'); + const policyTestResources = getService('policyTestResources'); + const endpointTestResources = getService('endpointTestResources'); + + describe('When on the Endpoint Policy List Page', () => { + before(async () => { + const endpointPackage = await policyTestResources.getEndpointPackage(); + await endpointTestResources.setMetadataTransformFrequency('1s', endpointPackage.version); + await browser.refresh(); + }); + + describe('with no policies', () => { + it('shows the empty page', async () => { + await pageObjects.policy.navigateToPolicyList(); + await testSubjects.existOrFail('emptyPolicyTable'); + }); + it('navigates to Fleet and ensures the integration page is loaded correctly', async () => { + const fleetButton = await testSubjects.find('onboardingStartButton'); + await fleetButton.click(); + await testSubjects.existOrFail('createPackagePolicy_pageTitle'); + expect(await testSubjects.getVisibleText('createPackagePolicy_pageTitle')).to.equal( + 'Add Endpoint Security integration' + ); + }); + it('navigates back to the policy list page', async () => { + const cancelButton = await testSubjects.find('createPackagePolicy_cancelBackLink'); + cancelButton.click(); + await pageObjects.policy.ensureIsOnListPage(); + }); + }); + describe('with policies', () => { + let indexedData: IndexedHostsAndAlertsResponse; + let policyInfo: PolicyTestResourceInfo; + before(async () => { + indexedData = await endpointTestResources.loadEndpointData(); + policyInfo = await policyTestResources.createPolicy(); + await browser.refresh(); + }); + after(async () => { + if (indexedData) { + await endpointTestResources.unloadEndpointData(indexedData); + } + if (policyInfo) { + await policyInfo.cleanup(); + } + }); + it('shows the policy list table', async () => { + await pageObjects.policy.navigateToPolicyList(); + await testSubjects.existOrFail('policyListTable'); + }); + it('navigates to the policy details page when the policy name is clicked and returns back to the policy list page using the header back button', async () => { + const policyName = (await testSubjects.findAll('policyNameCellLink'))[0]; + await policyName.click(); + await pageObjects.policy.ensureIsOnDetailsPage(); + const backButton = await testSubjects.find('policyDetailsBackLink'); + await backButton.click(); + await pageObjects.policy.ensureIsOnListPage(); + }); + // FLAKY: https://github.com/elastic/kibana/issues/131602 + describe.skip('when the endpoint count link is clicked', () => { + it('navigates to the endpoint list page filtered by policy', async () => { + const endpointCount = (await testSubjects.findAll('policyEndpointCountLink'))[0]; + await endpointCount.click(); + await pageObjects.endpoint.ensureIsOnEndpointListPage(); + }); + it('admin searchbar contains the selected policy id', async () => { + const expectedPolicyId = indexedData.integrationPolicies[0].id; + await pageObjects.endpoint.ensureIsOnEndpointListPage(); + expect(await testSubjects.getVisibleText('adminSearchBar')).to.equal( + `united.endpoint.Endpoint.policy.applied.id : "${expectedPolicyId}"` + ); + }); + it('endpoint table shows the endpoints associated with selected policy', async () => { + const expectedPolicyName = indexedData.integrationPolicies[0].name; + await pageObjects.endpoint.ensureIsOnEndpointListPage(); + const policyName = (await testSubjects.findAll('policyNameCellLink'))[0]; + expect(await policyName.getVisibleText()).to.be.equal( + expectedPolicyName.substring(0, expectedPolicyName.indexOf('-')) + ); + }); + it('returns back to the policy list page when the header back button is clicked', async () => { + await pageObjects.endpoint.ensureIsOnEndpointListPage(); + const backButton = await testSubjects.find('endpointListBackLink'); + await backButton.click(); + await pageObjects.policy.ensureIsOnListPage(); + }); + }); + }); + }); +} diff --git a/x-pack/test/security_solution_endpoint/config.ts b/x-pack/test/security_solution_endpoint/config.ts index b00df7732ea4f..b5b52b7bc5cd5 100644 --- a/x-pack/test/security_solution_endpoint/config.ts +++ b/x-pack/test/security_solution_endpoint/config.ts @@ -15,7 +15,9 @@ import { } from '../security_solution_endpoint_api_int/registry'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xpackFunctionalConfig = await readConfigFile(require.resolve('../functional/config.js')); + const xpackFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); return { ...xpackFunctionalConfig.getAll(), diff --git a/x-pack/test/security_solution_endpoint/page_objects/endpoint_page.ts b/x-pack/test/security_solution_endpoint/page_objects/endpoint_page.ts index d4e2479d24bc7..21d7b25cabb34 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/endpoint_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/endpoint_page.ts @@ -25,6 +25,10 @@ export function EndpointPageProvider({ getService, getPageObjects }: FtrProvider await pageObjects.header.waitUntilLoadingHasFinished(); }, + async ensureIsOnEndpointListPage() { + await testSubjects.existOrFail('endpointPage'); + }, + async waitForTableToHaveData(dataTestSubj: string, timeout = 2000) { await retry.waitForWithTimeout('table to have data', timeout, async () => { const tableData = await pageObjects.endpointPageUtils.tableData(dataTestSubj); diff --git a/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts b/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts index b5eccd0ef1147..da4fb936d6655 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts @@ -14,6 +14,16 @@ export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrPr const retryService = getService('retry'); return { + /** + * Navigates to the Endpoint Policy List page + */ + async navigateToPolicyList() { + await pageObjects.common.navigateToUrlWithBrowserHistory( + 'securitySolutionManagement', + `/policy` + ); + await pageObjects.header.waitUntilLoadingHasFinished(); + }, /** * Navigates to the Endpoint Policy Details page * @@ -27,6 +37,12 @@ export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrPr await pageObjects.header.waitUntilLoadingHasFinished(); }, + /** + * Ensures the current page is the policy list page + */ + async ensureIsOnListPage() { + await testSubjects.existOrFail('policyListPage'); + }, /** * Finds and returns the Policy Details Page Save button */ @@ -127,7 +143,7 @@ export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrPr /** * Used when looking a the Ingest create/edit package policy pages. Finds the endpoint - * custom configuaration component + * custom configuration component * @param onEditPage */ async findPackagePolicyEndpointCustomConfiguration(onEditPage: boolean = false) { diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/index.ts b/x-pack/test/security_solution_endpoint_api_int/apis/index.ts index 3c98b703aed55..7be4ce2243303 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/index.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/index.ts @@ -13,8 +13,6 @@ export default function endpointAPIIntegrationTests(providerContext: FtrProvider const { loadTestFile, getService } = providerContext; describe('Endpoint plugin', function () { - this.tags('ciGroup9'); - const ingestManager = getService('ingestManager'); const log = getService('log'); diff --git a/x-pack/test/spaces_api_integration/common/config.ts b/x-pack/test/spaces_api_integration/common/config.ts index 5d135cd05605c..15a63fec6d309 100644 --- a/x-pack/test/spaces_api_integration/common/config.ts +++ b/x-pack/test/spaces_api_integration/common/config.ts @@ -21,7 +21,9 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) const config = { kibana: { api: await readConfigFile(path.resolve(REPO_ROOT, 'test/api_integration/config.js')), - functional: await readConfigFile(require.resolve('../../../../test/functional/config.js')), + functional: await readConfigFile( + require.resolve('../../../../test/functional/config.base.js') + ), }, xpack: { api: await readConfigFile(require.resolve('../../api_integration/config.ts')), diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts index a86fef0d758fc..75381f35dacd3 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts @@ -14,8 +14,6 @@ export default function ({ loadTestFile, getService }: FtrProviderContext) { const supertest = getService('supertest'); describe('spaces api with security', function () { - this.tags('ciGroup8'); - before(async () => { await createUsersAndRoles(es, supertest); }); diff --git a/x-pack/test/spaces_api_integration/spaces_only/apis/index.ts b/x-pack/test/spaces_api_integration/spaces_only/apis/index.ts index f64336b2b4908..6a8148efaa1d6 100644 --- a/x-pack/test/spaces_api_integration/spaces_only/apis/index.ts +++ b/x-pack/test/spaces_api_integration/spaces_only/apis/index.ts @@ -10,8 +10,6 @@ import { FtrProviderContext } from '../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function spacesOnlyTestSuite({ loadTestFile }: FtrProviderContext) { describe('spaces api without security', function () { - this.tags('ciGroup5'); - loadTestFile(require.resolve('./copy_to_space')); loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); loadTestFile(require.resolve('./create')); diff --git a/x-pack/test/stack_functional_integration/apps/telemetry/index.js b/x-pack/test/stack_functional_integration/apps/telemetry/index.js index 80cffcfaf70a7..3d1dffb3a442f 100644 --- a/x-pack/test/stack_functional_integration/apps/telemetry/index.js +++ b/x-pack/test/stack_functional_integration/apps/telemetry/index.js @@ -7,7 +7,6 @@ export default function ({ loadTestFile }) { describe('telemetry feature', function () { - this.tags('ciGroup1'); loadTestFile(require.resolve('./_telemetry')); }); } diff --git a/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js b/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js index 9b768ab61baec..1658bcbf6cd35 100644 --- a/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js +++ b/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js @@ -25,7 +25,9 @@ const testsFolder = '../apps'; const prepend = (testFile) => require.resolve(`${testsFolder}/${testFile}`); export default async ({ readConfigFile }) => { - const xpackFunctionalConfig = await readConfigFile(require.resolve('../../functional/config')); + const xpackFunctionalConfig = await readConfigFile( + require.resolve('../../functional/config.base.js') + ); const externalConf = consumeState(resolve(__dirname, stateFilePath)); process.env.stack_functional_integration = true; logAll(log); diff --git a/x-pack/test/timeline/security_and_spaces/tests/basic/index.ts b/x-pack/test/timeline/security_and_spaces/tests/basic/index.ts index 4672a8e2e7f65..248c5ece3641d 100644 --- a/x-pack/test/timeline/security_and_spaces/tests/basic/index.ts +++ b/x-pack/test/timeline/security_and_spaces/tests/basic/index.ts @@ -14,9 +14,6 @@ import { // eslint-disable-next-line import/no-default-export export default ({ loadTestFile, getService }: FtrProviderContext): void => { describe('timeline security and spaces enabled: basic', function () { - // Fastest ciGroup for the moment. - this.tags('ciGroup5'); - before(async () => { await createSpacesAndUsers(getService); }); diff --git a/x-pack/test/timeline/security_and_spaces/tests/trial/index.ts b/x-pack/test/timeline/security_and_spaces/tests/trial/index.ts index 736fb6619c82d..2103097891a31 100644 --- a/x-pack/test/timeline/security_and_spaces/tests/trial/index.ts +++ b/x-pack/test/timeline/security_and_spaces/tests/trial/index.ts @@ -38,9 +38,6 @@ import { // eslint-disable-next-line import/no-default-export export default ({ loadTestFile, getService }: FtrProviderContext): void => { describe('timeline security and spaces enabled: trial', function () { - // Fastest ciGroup for the moment. - this.tags('ciGroup5'); - before(async () => { await createSpaces(getService); await createUsersAndRoles( diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index d6bdf8cfaa6fd..8c6a1cb88c0ba 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -58,7 +58,6 @@ { "path": "../plugins/global_search/tsconfig.json" }, { "path": "../plugins/global_search_providers/tsconfig.json" }, { "path": "../plugins/features/tsconfig.json" }, - { "path": "../plugins/data_enhanced/tsconfig.json" }, { "path": "../plugins/drilldowns/url_drilldown/tsconfig.json" }, { "path": "../plugins/embeddable_enhanced/tsconfig.json" }, { "path": "../plugins/encrypted_saved_objects/tsconfig.json" }, diff --git a/x-pack/test/ui_capabilities/common/config.ts b/x-pack/test/ui_capabilities/common/config.ts index f676a5eeccee1..32e7538ecbbe7 100644 --- a/x-pack/test/ui_capabilities/common/config.ts +++ b/x-pack/test/ui_capabilities/common/config.ts @@ -20,7 +20,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) return async ({ readConfigFile }: FtrConfigProviderContext) => { const xPackFunctionalTestsConfig = await readConfigFile( - require.resolve('../../functional/config.js') + require.resolve('../../functional/config.base.js') ); return { diff --git a/x-pack/test/ui_capabilities/security_and_spaces/tests/index.ts b/x-pack/test/ui_capabilities/security_and_spaces/tests/index.ts index a257f8fcabb8e..77619157c615f 100644 --- a/x-pack/test/ui_capabilities/security_and_spaces/tests/index.ts +++ b/x-pack/test/ui_capabilities/security_and_spaces/tests/index.ts @@ -16,8 +16,6 @@ export default function uiCapabilitiesTests({ loadTestFile, getService }: FtrPro const featuresService: FeaturesService = getService('features'); describe('ui capabilities', function () { - this.tags('ciGroup9'); - before(async () => { const features = await featuresService.get(); for (const space of Spaces) { diff --git a/x-pack/test/ui_capabilities/spaces_only/tests/index.ts b/x-pack/test/ui_capabilities/spaces_only/tests/index.ts index 73a068cf9ec6b..02ed1533b56ad 100644 --- a/x-pack/test/ui_capabilities/spaces_only/tests/index.ts +++ b/x-pack/test/ui_capabilities/spaces_only/tests/index.ts @@ -14,8 +14,6 @@ export default function uiCapabilitesTests({ loadTestFile, getService }: FtrProv const featuresService: FeaturesService = getService('features'); describe('ui capabilities', function () { - this.tags('ciGroup9'); - before(async () => { // we're using a basic license, so if we want to disable all features, we have to ignore the valid licenses const features = await featuresService.get({ ignoreValidLicenses: true }); diff --git a/x-pack/test/upgrade/apps/discover/discover_smoke_tests.ts b/x-pack/test/upgrade/apps/discover/discover_smoke_tests.ts index 150458919d41d..1d2df7a703161 100644 --- a/x-pack/test/upgrade/apps/discover/discover_smoke_tests.ts +++ b/x-pack/test/upgrade/apps/discover/discover_smoke_tests.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getPageObjects, getService }: FtrProviderContext) { +export default function ({ getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'header', 'home', 'discover', 'timePicker']); describe('upgrade discover smoke tests', function describeIndexTests() { @@ -18,9 +18,9 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { ]; const discoverTests = [ - { name: 'kibana_sample_data_flights', timefield: true, hits: '' }, - { name: 'kibana_sample_data_logs', timefield: true, hits: '' }, - { name: 'kibana_sample_data_ecommerce', timefield: true, hits: '' }, + { name: 'flights', timefield: true, hits: '' }, + { name: 'logs', timefield: true, hits: '' }, + { name: 'ecommerce', timefield: true, hits: '' }, ]; spaces.forEach(({ space, basePath }) => { @@ -31,7 +31,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { basePath, }); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.discover.selectIndexPattern(name); + await PageObjects.discover.selectIndexPattern(`kibana_sample_data_${name}`); await PageObjects.discover.waitUntilSearchingHasFinished(); if (timefield) { await PageObjects.timePicker.setCommonlyUsedTime('Last_24 hours'); @@ -52,6 +52,35 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); }); + + discoverTests.forEach(({ name, timefield, hits }) => { + describe('space: ' + space + ', name: ' + name, () => { + before(async () => { + await PageObjects.common.navigateToActualUrl('home', '/tutorial_directory/sampleData', { + basePath, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.home.launchSampleDiscover(name); + await PageObjects.header.waitUntilLoadingHasFinished(); + if (timefield) { + await PageObjects.timePicker.setCommonlyUsedTime('Last_24 hours'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + } + }); + it('shows hit count greater than zero', async () => { + const hitCount = await PageObjects.discover.getHitCount(); + if (hits === '') { + expect(hitCount).to.be.greaterThan(0); + } else { + expect(hitCount).to.be.equal(hits); + } + }); + it('shows table rows not empty', async () => { + const tableRows = await PageObjects.discover.getDocTableRows(); + expect(tableRows.length).to.be.greaterThan(0); + }); + }); + }); }); }); } diff --git a/x-pack/test/upgrade/config.ts b/x-pack/test/upgrade/config.ts index 78d61d5239556..181abe8ca408f 100644 --- a/x-pack/test/upgrade/config.ts +++ b/x-pack/test/upgrade/config.ts @@ -12,7 +12,7 @@ import { MapsHelper } from './services/maps_upgrade_services'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { const apiConfig = await readConfigFile(require.resolve('../api_integration/config')); - const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); return { ...functionalConfig.getAll(), diff --git a/x-pack/test/upgrade_assistant_integration/config.js b/x-pack/test/upgrade_assistant_integration/config.js index 8152b5790fc40..e1248c717b216 100644 --- a/x-pack/test/upgrade_assistant_integration/config.js +++ b/x-pack/test/upgrade_assistant_integration/config.js @@ -11,7 +11,7 @@ export default async function ({ readConfigFile }) { require.resolve('../../../test/api_integration/config.js') ); const xPackFunctionalTestsConfig = await readConfigFile( - require.resolve('../functional/config.js') + require.resolve('../functional/config.base.js') ); const kibanaCommonConfig = await readConfigFile( require.resolve('../../../test/common/config.js') diff --git a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.js b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.js index 1a7090a3cbdfb..eb09d24b79b6a 100644 --- a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.js +++ b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.js @@ -7,8 +7,6 @@ export default function ({ loadTestFile }) { describe('upgrade assistant', function () { - this.tags('ciGroup7'); - loadTestFile(require.resolve('./reindexing')); }); } diff --git a/x-pack/test/usage_collection/config.ts b/x-pack/test/usage_collection/config.ts index beb934219422a..248f00ff81233 100644 --- a/x-pack/test/usage_collection/config.ts +++ b/x-pack/test/usage_collection/config.ts @@ -15,7 +15,9 @@ import { pageObjects } from './page_objects'; // that returns an object with the projects config values export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xpackFunctionalConfig = await readConfigFile(require.resolve('../functional/config.js')); + const xpackFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); // Find all folders in ./plugins since we treat all them as plugin folder const allFiles = fs.readdirSync(resolve(__dirname, 'plugins')); diff --git a/x-pack/test/usage_collection/test_suites/application_usage/index.ts b/x-pack/test/usage_collection/test_suites/application_usage/index.ts index 4b41aada9ad29..754ae98997c16 100644 --- a/x-pack/test/usage_collection/test_suites/application_usage/index.ts +++ b/x-pack/test/usage_collection/test_suites/application_usage/index.ts @@ -11,7 +11,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('Application Usage', function () { - this.tags('ciGroup1'); const { common } = getPageObjects(['common']); const browser = getService('browser'); diff --git a/x-pack/test/usage_collection/test_suites/stack_management_usage/index.ts b/x-pack/test/usage_collection/test_suites/stack_management_usage/index.ts index dac552220f7c1..a595c2662d451 100644 --- a/x-pack/test/usage_collection/test_suites/stack_management_usage/index.ts +++ b/x-pack/test/usage_collection/test_suites/stack_management_usage/index.ts @@ -12,7 +12,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { // FLAKY: https://github.com/elastic/kibana/issues/119038 describe.skip('Stack Management', function () { - this.tags('ciGroup1'); const { common } = getPageObjects(['common']); const browser = getService('browser'); diff --git a/x-pack/test/visual_regression/config.ts b/x-pack/test/visual_regression/config.ts index c211918ef8e52..c7f0d8203833e 100644 --- a/x-pack/test/visual_regression/config.ts +++ b/x-pack/test/visual_regression/config.ts @@ -10,7 +10,7 @@ import { FtrConfigProviderContext } from '@kbn/test'; import { services } from './services'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); return { ...functionalConfig.getAll(), diff --git a/x-pack/test/visual_regression/tests/canvas/index.js b/x-pack/test/visual_regression/tests/canvas/index.js index 099c96e6eaf01..20a262fef10fe 100644 --- a/x-pack/test/visual_regression/tests/canvas/index.js +++ b/x-pack/test/visual_regression/tests/canvas/index.js @@ -25,7 +25,6 @@ export default function ({ loadTestFile, getService }) { await esArchiver.unload('x-pack/test/functional/es_archives/canvas/default'); }); - this.tags('ciGroup10'); loadTestFile(require.resolve('./fullscreen')); }); } diff --git a/x-pack/test/visual_regression/tests/infra/index.js b/x-pack/test/visual_regression/tests/infra/index.js index b624c6ec848f2..13669c50953f9 100644 --- a/x-pack/test/visual_regression/tests/infra/index.js +++ b/x-pack/test/visual_regression/tests/infra/index.js @@ -13,7 +13,6 @@ export default function ({ loadTestFile, getService }) { await browser.setWindowSize(1600, 1000); }); - this.tags('ciGroup10'); loadTestFile(require.resolve('./waffle_map')); loadTestFile(require.resolve('./saved_views')); }); diff --git a/x-pack/test/visual_regression/tests/maps/index.js b/x-pack/test/visual_regression/tests/maps/index.js index 3459896baacd6..9d53d70ad2abc 100644 --- a/x-pack/test/visual_regression/tests/maps/index.js +++ b/x-pack/test/visual_regression/tests/maps/index.js @@ -56,7 +56,6 @@ export default function ({ loadTestFile, getService }) { ); }); - this.tags('ciGroup10'); loadTestFile(require.resolve('./vector_styling')); }); } diff --git a/yarn.lock b/yarn.lock index 4d1428bc991a4..43d8d6ce5a120 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1527,10 +1527,10 @@ resolved "https://registry.yarnpkg.com/@elastic/eslint-plugin-eui/-/eslint-plugin-eui-0.0.2.tgz#56b9ef03984a05cc213772ae3713ea8ef47b0314" integrity sha512-IoxURM5zraoQ7C8f+mJb9HYSENiZGgRVcG4tLQxE61yHNNRDXtGDWTZh8N1KIHcsqN1CEPETjuzBXkJYF/fDiQ== -"@elastic/eui@54.0.0": - version "54.0.0" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-54.0.0.tgz#373a50f0ebbecfbe39b86f1d87f3ba1c6f942a81" - integrity sha512-0qm4SoGYup0Cyb+k50heIdJ9pTIYQwA2knNWZ+UE92SzPUzAwSmhrwkiqzTqGCgB6/Kd2Ck4gzFjI+ct1njkmg== +"@elastic/eui@55.0.1": + version "55.0.1" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-55.0.1.tgz#d10978aa01b3e44f54dbfa148bc31af58d64b251" + integrity sha512-k6ES2Sp8bZlYGRFYIoBS2EQ3C9BmEgC4p/A4ViHWNKpb0qriS37A28/QyIR2cPXwiesRq6rt7L+LQSLJlXmGzA== dependencies: "@types/chroma-js" "^2.0.0" "@types/lodash" "^4.14.160" @@ -1564,7 +1564,7 @@ remark-emoji "^2.1.0" remark-parse "^8.0.3" remark-rehype "^8.0.0" - tabbable "^3.0.0" + tabbable "^5.2.1" text-diff "^1.0.1" unified "^9.2.0" unist-util-visit "^2.0.3" @@ -2932,6 +2932,18 @@ version "0.0.0" uid "" +"@kbn/analytics-shippers-elastic-v3-browser@link:bazel-bin/packages/analytics/shippers/elastic_v3/browser": + version "0.0.0" + uid "" + +"@kbn/analytics-shippers-elastic-v3-common@link:bazel-bin/packages/analytics/shippers/elastic_v3/common": + version "0.0.0" + uid "" + +"@kbn/analytics-shippers-elastic-v3-server@link:bazel-bin/packages/analytics/shippers/elastic_v3/server": + version "0.0.0" + uid "" + "@kbn/analytics-shippers-fullstory@link:bazel-bin/packages/analytics/shippers/fullstory": version "0.0.0" uid "" @@ -2968,10 +2980,6 @@ version "0.0.0" uid "" -"@kbn/ci-stats-client@link:bazel-bin/packages/kbn-ci-stats-client": - version "0.0.0" - uid "" - "@kbn/ci-stats-core@link:bazel-bin/packages/kbn-ci-stats-core": version "0.0.0" uid "" @@ -3104,6 +3112,10 @@ version "0.0.0" uid "" +"@kbn/performance-testing-dataset-extractor@link:bazel-bin/packages/kbn-performance-testing-dataset-extractor": + version "0.0.0" + uid "" + "@kbn/plugin-discovery@link:bazel-bin/packages/kbn-plugin-discovery": version "0.0.0" uid "" @@ -3192,6 +3204,10 @@ version "0.0.0" uid "" +"@kbn/shared-ux-avatar-solution@link:bazel-bin/packages/shared-ux/avatar/solution": + version "0.0.0" + uid "" + "@kbn/shared-ux-button-exit-full-screen@link:bazel-bin/packages/shared-ux/button/exit_full_screen": version "0.0.0" uid "" @@ -3200,6 +3216,14 @@ version "0.0.0" uid "" +"@kbn/shared-ux-link-redirect-app@link:bazel-bin/packages/shared-ux/link/redirect_app": + version "0.0.0" + uid "" + +"@kbn/shared-ux-page-analytics-no-data@link:bazel-bin/packages/shared-ux/page/analytics_no_data": + version "0.0.0" + uid "" + "@kbn/shared-ux-services@link:bazel-bin/packages/kbn-shared-ux-services": version "0.0.0" uid "" @@ -6036,6 +6060,18 @@ version "0.0.0" uid "" +"@types/kbn__analytics-shippers-elastic-v3-browser@link:bazel-bin/packages/analytics/shippers/elastic_v3/browser/npm_module_types": + version "0.0.0" + uid "" + +"@types/kbn__analytics-shippers-elastic-v3-common@link:bazel-bin/packages/analytics/shippers/elastic_v3/common/npm_module_types": + version "0.0.0" + uid "" + +"@types/kbn__analytics-shippers-elastic-v3-server@link:bazel-bin/packages/analytics/shippers/elastic_v3/server/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__analytics-shippers-fullstory@link:bazel-bin/packages/analytics/shippers/fullstory/npm_module_types": version "0.0.0" uid "" @@ -6064,10 +6100,6 @@ version "0.0.0" uid "" -"@types/kbn__ci-stats-client@link:bazel-bin/packages/kbn-ci-stats-client/npm_module_types": - version "0.0.0" - uid "" - "@types/kbn__ci-stats-core@link:bazel-bin/packages/kbn-ci-stats-core/npm_module_types": version "0.0.0" uid "" @@ -6184,6 +6216,10 @@ version "0.0.0" uid "" +"@types/kbn__performance-testing-dataset-extractor@link:bazel-bin/packages/kbn-performance-testing-dataset-extractor/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__plugin-discovery@link:bazel-bin/packages/kbn-plugin-discovery/npm_module_types": version "0.0.0" uid "" @@ -6268,6 +6304,10 @@ version "0.0.0" uid "" +"@types/kbn__shared-ux-avatar-solution@link:bazel-bin/packages/shared-ux/avatar/solution/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__shared-ux-button-exit-full-screen@link:bazel-bin/packages/shared-ux/button/exit_full_screen/npm_module_types": version "0.0.0" uid "" @@ -6276,6 +6316,14 @@ version "0.0.0" uid "" +"@types/kbn__shared-ux-link-redirect-app@link:bazel-bin/packages/shared-ux/link/redirect_app/npm_module_types": + version "0.0.0" + uid "" + +"@types/kbn__shared-ux-page-analytics-no-data@link:bazel-bin/packages/shared-ux/page/analytics_no_data/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__shared-ux-services@link:bazel-bin/packages/kbn-shared-ux-services/npm_module_types": version "0.0.0" uid "" @@ -6636,11 +6684,6 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/parse-link-header@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-link-header/-/parse-link-header-1.0.0.tgz#69f059e40a0fa93dc2e095d4142395ae6adc5d7a" - integrity sha512-fCA3btjE7QFeRLfcD0Sjg+6/CnmC66HpMBoRfRzd2raTaWMJV21CCZ0LO8MOqf8onl5n0EPfjq4zDhbyX8SVwA== - "@types/parse5@*", "@types/parse5@^5.0.0": version "5.0.3" resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" @@ -6963,10 +7006,10 @@ resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-2.4.28.tgz#9ce8fa048c1e8c85cb71d7fe4d704e000226036f" integrity sha512-SMA+fUwULwK7sd/ZJicUztiPs8F1yCPwF3O23Z9uQ32ME5Ha0NmDK9+QTsYE4O2tHXChzXomSWWeIhCnoN1LqA== -"@types/selenium-webdriver@^4.0.18": - version "4.0.18" - resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-4.0.18.tgz#98f6e1ccd2d92f6fddaccfc7c148d2e158da0f92" - integrity sha512-gkrUo3QldGr8V9im/DjgKkX4UVd1rtflfEBuPG9hPSA1keu7A0rF8h/MQjpTMm2EPVhBCd2K8tn5nlC9Vsd5Xw== +"@types/selenium-webdriver@^4.0.19": + version "4.0.19" + resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-4.0.19.tgz#25699713552a63ee70215effdfd2e5d6dda19f8e" + integrity sha512-Irrh+iKc6Cxj6DwTupi4zgWhSBm1nK+JElOklIUiBVE6rcLYDtT1mwm9oFkHie485BQXNmZRoayjwxhowdInnA== "@types/semver@^7": version "7.3.4" @@ -7072,13 +7115,6 @@ resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.6.tgz#a9ca4b70a18b270ccb2bc0aaafefd1d486b7ea74" integrity sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA== -"@types/tar-fs@^1.16.1": - version "1.16.1" - resolved "https://registry.yarnpkg.com/@types/tar-fs/-/tar-fs-1.16.1.tgz#6e3fba276c173e365ae91e55f7b797a0e64298e5" - integrity sha512-uQQIaa8ukcKf/1yy2kzfP1PF+7jEZghFDKpDvgtsYo/mbqM1g4Qza1Y5oAw6kJMa7eLA/HkmxUsDqb2sWKVF9g== - dependencies: - "@types/node" "*" - "@types/tar@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@types/tar/-/tar-4.0.5.tgz#5f953f183e36a15c6ce3f336568f6051b7b183f3" @@ -8563,7 +8599,7 @@ async@^1.4.2: resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= -async@^2.1.4, async@^2.6.2: +async@^2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== @@ -9165,7 +9201,7 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.9: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== -body-parser@1.19.0, body-parser@^1.18.1, body-parser@^1.18.3: +body-parser@1.19.0, body-parser@^1.18.3: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== @@ -9289,6 +9325,20 @@ brfs@^2.0.0, brfs@^2.0.2: static-module "^3.0.2" through2 "^2.0.0" +broadcast-channel@4.10.0: + version "4.10.0" + resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-4.10.0.tgz#d19fb902df227df40b1b580351713d30c302d198" + integrity sha512-hOUh312XyHk6JTVyX9cyXaH1UYs+2gHVtnW16oQAu9FL7ALcXGXc/YoJWqlkV8vUn14URQPMmRi4A9q4UrwVEQ== + dependencies: + "@babel/runtime" "^7.16.0" + detect-node "^2.1.0" + microseconds "0.2.0" + nano-time "1.0.0" + oblivious-set "1.0.0" + p-queue "6.6.2" + rimraf "3.0.2" + unload "2.3.1" + broadcast-channel@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.4.1.tgz#65b63068d0a5216026a19905c9b2d5e9adf0928a" @@ -9302,19 +9352,6 @@ broadcast-channel@^3.4.1: rimraf "3.0.2" unload "2.2.0" -broadcast-channel@^4.11.0: - version "4.11.0" - resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-4.11.0.tgz#b9ebc7ce1326120088e61d2197477496908a1a9e" - integrity sha512-4FS1Zk+ttekfXHq5I2R7KhN9AsnZUFVV5SczrTtnZPuf5w+jw+fqM1PJHuHzwEXJezJeCbJxoZMDcFqsIN2c1Q== - dependencies: - "@babel/runtime" "^7.16.0" - detect-node "^2.1.0" - microtime "3.0.0" - oblivious-set "1.0.0" - p-queue "6.6.2" - rimraf "3.0.2" - unload "2.3.1" - brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" @@ -9891,9 +9928,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001097, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001286: - version "1.0.30001309" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001309.tgz#e0ee78b9bec0704f67304b00ff3c5c0c768a9f62" - integrity sha512-Pl8vfigmBXXq+/yUz1jUwULeq9xhMJznzdc/xwl4WclDAuebcTHVefpz8lE/bMI+UN7TOkSSe7B7RnZd6+dzjA== + version "1.0.30001335" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001335.tgz" + integrity sha512-ddP1Tgm7z2iIxu6QTtbZUv6HJxSaV/PZeSrWFZtbY4JZ69tOeNhBCl3HyRQgeNZKE5AOn1kpV7fhljigy0Ty3w== canvg@^3.0.9: version "3.0.9" @@ -10808,16 +10845,6 @@ connect-history-api-fallback@^1.6.0: resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== -connect@^3.4.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" - integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== - dependencies: - debug "2.6.9" - finalhandler "1.1.2" - parseurl "~1.3.3" - utils-merge "1.0.1" - console-browserify@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" @@ -10981,10 +11008,10 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.9: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== -core-js@^3.0.4, core-js@^3.21.1, core-js@^3.6.5, core-js@^3.8.2, core-js@^3.8.3: - version "3.21.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.1.tgz#f2e0ddc1fc43da6f904706e8e955bc19d06a0d94" - integrity sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig== +core-js@^3.0.4, core-js@^3.22.4, core-js@^3.6.5, core-js@^3.8.2, core-js@^3.8.3: + version "3.22.4" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.22.4.tgz#f4b3f108d45736935aa028444a69397e40d8c531" + integrity sha512-1uLykR+iOfYja+6Jn/57743gc9n73EWiOnSJJ4ba3B4fOEYDBv25MagmEZBxTp5cWq4b/KPx/l77zgsp28ju4w== core-util-is@1.0.2, core-util-is@^1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -11713,9 +11740,9 @@ d3-color@1, d3-color@^1.0.3: integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q== "d3-color@1 - 3", d3-color@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.0.1.tgz#03316e595955d1fcd39d9f3610ad41bb90194d0a" - integrity sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw== + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" + integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== d3-contour@^1.1.0: version "1.3.2" @@ -12016,11 +12043,6 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -dashify@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/dashify/-/dashify-0.1.0.tgz#107daf9cca5e326e30a8b39ffa5048b6684922ea" - integrity sha1-EH2vnMpeMm4wqLOf+lBItmhJIuo= - data-uri-to-buffer@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-0.0.3.tgz#18ae979a6a0ca994b0625853916d2662bbae0b1a" @@ -13016,10 +13038,10 @@ elastic-apm-http-client@11.0.1: semver "^6.3.0" stream-chopper "^3.0.1" -elastic-apm-node@^3.31.0: - version "3.31.0" - resolved "https://registry.yarnpkg.com/elastic-apm-node/-/elastic-apm-node-3.31.0.tgz#6e0bf622d922c95ff0127a263babcdeaeea71457" - integrity sha512-0OulazfhkXYbOaGkHncqjwOfxtcvzsDyzUKr6Y1k95HwKrjf1Vi+xPutZv4p/WfDdO+JadphI0U2Uu5ncGB2iA== +elastic-apm-node@^3.33.0: + version "3.33.0" + resolved "https://registry.yarnpkg.com/elastic-apm-node/-/elastic-apm-node-3.33.0.tgz#ad2850b005355299c3a9fdc631875162480ceb15" + integrity sha512-ZJKcRbYdEU87MWyB9CczGTLEERA595OWd0nqpxrDBkogogpxwpzCOialKZA+bekpNA0Oa4Sv18zRCdyqpV25Pw== dependencies: "@elastic/ecs-pino-format" "^1.2.0" after-all-results "^2.0.0" @@ -13051,7 +13073,6 @@ elastic-apm-node@^3.31.0: shallow-clone-shim "^2.0.0" source-map "^0.8.0-beta.0" sql-summary "^1.0.1" - traceparent "^1.0.0" traverse "^0.6.6" unicode-byte-truncate "^1.0.0" @@ -14410,7 +14431,7 @@ fbjs@^0.8.1, fbjs@^0.8.9: setimmediate "^1.0.5" ua-parser-js "^0.7.18" -fd-slicer@1.1.0, fd-slicer@~1.1.0: +fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= @@ -14562,7 +14583,7 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -finalhandler@1.1.2, finalhandler@~1.1.2: +finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== @@ -15360,7 +15381,7 @@ glob-to-regexp@^0.4.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob-watcher@5.0.3, glob-watcher@^5.0.3: +glob-watcher@^5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-5.0.3.tgz#88a8abf1c4d131eb93928994bc4a593c2e5dd626" integrity sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg== @@ -16140,11 +16161,6 @@ highlight.js@^10.1.1, highlight.js@^10.4.1, highlight.js@~10.4.0: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.4.1.tgz#d48fbcf4a9971c4361b3f95f302747afe19dbad0" integrity sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg== -history-extra@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/history-extra/-/history-extra-5.0.1.tgz#95a2e59dda526c4241d0ae1b124a77a5e4675ce8" - integrity sha512-6XV1L1lHgporVWgppa/Kq+Fnz4lhBew7iMxYCTfzVmoEywsAKJnTjdw1zOd+EGLHGYp0/V8jSVMEgqx4QbHLTw== - history@^4.9.0: version "4.9.0" resolved "https://registry.yarnpkg.com/history/-/history-4.9.0.tgz#84587c2068039ead8af769e9d6a6860a14fa1bca" @@ -16389,7 +16405,7 @@ http-deceiver@^1.2.7: resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= -http-errors@1.7.2, http-errors@~1.7.0, http-errors@~1.7.2: +http-errors@1.7.2, http-errors@~1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== @@ -16560,11 +16576,6 @@ icss-utils@^4.0.0, icss-utils@^4.1.1: dependencies: postcss "^7.0.14" -idx@^2.5.6: - version "2.5.6" - resolved "https://registry.yarnpkg.com/idx/-/idx-2.5.6.tgz#1f824595070100ae9ad585c86db08dc74f83a59d" - integrity sha512-WFXLF7JgPytbMgelpRY46nHz5tyDcedJ76pLV+RJWdb8h33bxFq4bdZau38DhNSzk5eVniBf1K3jwfK+Lb5nYA== - ieee754@^1.1.12, ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -16761,13 +16772,6 @@ inline-style-prefixer@^4.0.0: bowser "^1.7.3" css-in-js-utils "^2.0.0" -inline-style@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/inline-style/-/inline-style-2.0.0.tgz#2fa9cf624596a8109355b925094e138bbd5ea29b" - integrity sha1-L6nPYkWWqBCTVbklCU4Ti71eops= - dependencies: - dashify "^0.1.0" - inquirer@^7.0.0, inquirer@^7.3.3: version "7.3.3" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" @@ -19215,7 +19219,7 @@ loader-utils@2.0.0, loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" -loader-utils@^1.0.0, loader-utils@^1.0.2, loader-utils@^1.0.4, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: +loader-utils@^1.0.0, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== @@ -20137,14 +20141,6 @@ microseconds@0.2.0: resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39" integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA== -microtime@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/microtime/-/microtime-3.0.0.tgz#d140914bde88aa89b4f9fd2a18620b435af0f39b" - integrity sha512-SirJr7ZL4ow2iWcb54bekS4aWyBQNVcEDBiwAz9D/sTgY59A+uE8UJU15cp5wyZmPBwg/3zf8lyCJ5NUe1nVlQ== - dependencies: - node-addon-api "^1.2.0" - node-gyp-build "^3.8.0" - miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -20255,7 +20251,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@0.0.8, minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6, minimist@~1.2.0: +minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6, minimist@~1.2.0: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== @@ -20357,13 +20353,6 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= - dependencies: - minimist "0.0.8" - mkdirp@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.5.tgz#de3e5f8961c88c787ee1368df849ac4413eca8d7" @@ -20470,16 +20459,6 @@ mock-fs@^5.1.2: resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-5.1.2.tgz#6fa486e06d00f8793a8d2228de980eff93ce6db7" integrity sha512-YkjQkdLulFrz0vD4BfNQdQRVmgycXTV7ykuHMlyv+C8WCHazpkiQRDthwa02kSyo8wKnY9wRptHfQLgmf0eR+A== -mock-http-server@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/mock-http-server/-/mock-http-server-1.3.0.tgz#d2c2ffe65f77d3a4da8302c91d3bf687e5b51519" - integrity sha512-WC1fQ4kfOiiRZZ6IEOispJcfvz66m7VVbVFmnWsv1pOwL3psqYyLQGjFXg//zjPeZ//y/rxa8e2eh1Bb58cN7g== - dependencies: - body-parser "^1.18.1" - connect "^3.4.0" - multiparty "^4.1.2" - underscore "^1.8.3" - module-deps@^6.2.3: version "6.2.3" resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-6.2.3.tgz#15490bc02af4b56cf62299c7c17cba32d71a96ee" @@ -20631,16 +20610,6 @@ multimatch@^4.0.0: arrify "^2.0.1" minimatch "^3.0.4" -multiparty@^4.1.2: - version "4.2.1" - resolved "https://registry.yarnpkg.com/multiparty/-/multiparty-4.2.1.tgz#d9b6c46d8b8deab1ee70c734b0af771dd46e0b13" - integrity sha512-AvESCnNoQlZiOfP9R4mxN8M9csy2L16EIbWIkt3l4FuGti9kXBS8QVzlfyg4HEnarJhrzZilgNFlZtqmoiAIIA== - dependencies: - fd-slicer "1.1.0" - http-errors "~1.7.0" - safe-buffer "5.1.2" - uid-safe "2.1.5" - murmurhash-js@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/murmurhash-js/-/murmurhash-js-1.0.0.tgz#b06278e21fc6c37fa5313732b0412bcb6ae15f51" @@ -20875,11 +20844,6 @@ node-abi@^3.3.0: dependencies: semver "^7.3.5" -node-addon-api@^1.2.0: - version "1.7.2" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d" - integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg== - node-addon-api@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" @@ -20921,11 +20885,6 @@ node-forge@^1.2.1, node-forge@^1.3.1: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== -node-gyp-build@^3.8.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.9.0.tgz#53a350187dd4d5276750da21605d1cb681d09e25" - integrity sha512-zLcTg6P4AbcHPq465ZMFNXx7XpKKJh+7kkN699NiQWisR2uWYOWNWqRHAmbnmKiL4e9aLSlmy5U7rEMUXV59+A== - node-gyp-build@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" @@ -22049,13 +22008,6 @@ parse-json@^5.0.0: json-parse-better-errors "^1.0.1" lines-and-columns "^1.1.6" -parse-link-header@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parse-link-header/-/parse-link-header-1.0.1.tgz#bedfe0d2118aeb84be75e7b025419ec8a61140a7" - integrity sha1-vt/g0hGK64S+deewJUGeyKYRQKc= - dependencies: - xtend "~4.0.1" - parse-ms@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" @@ -22231,7 +22183,7 @@ pathval@^1.1.0: resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== -pbf@3.2.1, pbf@^3.0.5, pbf@^3.2.1: +pbf@3.2.1, pbf@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/pbf/-/pbf-3.2.1.tgz#b4c1b9e72af966cd82c6531691115cc0409ffe2a" integrity sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ== @@ -22250,6 +22202,13 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" +pdfjs-dist@^2.13.216: + version "2.13.216" + resolved "https://registry.yarnpkg.com/pdfjs-dist/-/pdfjs-dist-2.13.216.tgz#251a11c9c8c6db19baacd833a4e6986c517d1ab3" + integrity sha512-qn/9a/3IHIKZarTK6ajeeFXBkG15Lg1Fx99PxU09PAU2i874X8mTcHJYyDJxu7WDfNhV6hM7bRQBZU384anoqQ== + dependencies: + web-streams-polyfill "^3.2.0" + pdfmake@^0.2.4: version "0.2.4" resolved "https://registry.yarnpkg.com/pdfmake/-/pdfmake-0.2.4.tgz#7d58d64b59f8e9b9ed0b2494b17a9d94c575825b" @@ -22378,13 +22337,6 @@ pixelmatch@^4.0.2: dependencies: pngjs "^3.0.0" -pixelmatch@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-5.1.0.tgz#b640f0e5a03a09f235a4b818ef3b9b98d9d0b911" - integrity sha512-HqtgvuWN12tBzKJf7jYsc38Ha28Q2NYpmBL9WostEGgDHJqbTLkjydZXL1ZHM02ZnB+Dkwlxo87HBY38kMiD6A== - dependencies: - pngjs "^3.4.0" - pkg-dir@4.2.0, pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -23602,16 +23554,6 @@ randexp@0.4.6: discontinuous-range "1.0.0" ret "~0.1.10" -random-bytes@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" - integrity sha1-T2ih3Arli9P7lYSMMDJNt11kNgs= - -random-poly-fill@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/random-poly-fill/-/random-poly-fill-1.0.1.tgz#13634dc0255a31ecf85d4a182d92c40f9bbcf5ed" - integrity sha512-bMOL0hLfrNs52+EHtIPIXxn2PxYwXb0qjnKruTjXiM/sKfYqj506aB2plFwWW1HN+ri724bAVVGparh4AtlJKw== - random-word-slugs@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/random-word-slugs/-/random-word-slugs-0.0.5.tgz#6ccd6c7ea320be9fbc19507f8c3a7d4a970ff61f" @@ -25622,16 +25564,6 @@ sass-loader@^10.2.0: schema-utils "^3.0.0" semver "^7.3.2" -sass-resources-loader@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/sass-resources-loader/-/sass-resources-loader-2.0.1.tgz#c8427f3760bf7992f24f27d3889a1c797e971d3a" - integrity sha512-UsjQWm01xglINC1kPidYwKOBBzOElVupm9RwtOkRlY0hPA4GKi2KFsn4BZypRD1kudaXgUnGnfbiVOE7c+ybAg== - dependencies: - async "^2.1.4" - chalk "^1.1.3" - glob "^7.1.1" - loader-utils "^1.0.4" - save-pixels@^2.3.2: version "2.3.4" resolved "https://registry.yarnpkg.com/save-pixels/-/save-pixels-2.3.4.tgz#49d349c06b8d7c0127dbf0da24b44aca5afb59fe" @@ -27482,15 +27414,10 @@ syntax-error@^1.1.1: dependencies: acorn-node "^1.2.0" -tabbable@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-1.1.3.tgz#0e4ee376f3631e42d7977a074dbd2b3827843081" - integrity sha512-nOWwx35/JuDI4ONuF0ZTo6lYvI0fY0tZCH1ErzY2EXfu4az50ZyiUX8X073FLiZtmWUVlkRnuXsehjJgCw9tYg== - -tabbable@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-3.1.2.tgz#f2d16cccd01f400e38635c7181adfe0ad965a4a2" - integrity sha512-wjB6puVXTYO0BSFtCmWQubA/KIn7Xvajw0x0l6eJUudMG/EAiJvIUnyNX6xO4NpGrJ16lbD0eUseB9WxW0vlpQ== +tabbable@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-5.2.1.tgz#e3fda7367ddbb172dcda9f871c0fdb36d1c4cd9c" + integrity sha512-40pEZ2mhjaZzK0BnI+QGNjJO8UYx9pP5v7BGe17SORTO0OEuuaAwQTkAp8whcZvqon44wKFOikD+Al11K3JICQ== table@^6.0.3, table@^6.0.9: version "6.7.1" @@ -27571,16 +27498,6 @@ tar-fs@^2.0.0, tar-fs@^2.1.1: pump "^3.0.0" tar-stream "^2.1.4" -tar-fs@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.0.tgz#d1cdd121ab465ee0eb9ccde2d35049d3f3daf0d5" - integrity sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg== - dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" - pump "^3.0.0" - tar-stream "^2.0.0" - tar-stream@^2.0.0: version "2.1.3" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.3.tgz#1e2022559221b7866161660f118255e20fa79e41" @@ -28117,13 +28034,6 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= -traceparent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/traceparent/-/traceparent-1.0.0.tgz#9b14445cdfe5c19f023f1c04d249c3d8e003a5ce" - integrity sha512-b/hAbgx57pANQ6cg2eBguY3oxD6FGVLI1CC2qoi01RmHR7AYpQHPXTig9FkzbWohEsVuHENZHP09aXuw3/LM+w== - dependencies: - random-poly-fill "^1.0.1" - traverse@^0.6.6, traverse@~0.6.6: version "0.6.6" resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" @@ -28458,13 +28368,6 @@ uglify-to-browserify@~1.0.0: resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" integrity sha1-bgkk1r2mta/jSeOabWMoUKD4grc= -uid-safe@2.1.5: - version "2.1.5" - resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a" - integrity sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA== - dependencies: - random-bytes "~1.0.0" - umd@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.3.tgz#aa9fe653c42b9097678489c01000acb69f0b26cf" @@ -28517,7 +28420,7 @@ undefsafe@^2.0.5: resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== -underscore@^1.13.1, underscore@^1.8.3: +underscore@^1.13.1: version "1.13.1" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1" integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g== @@ -29737,15 +29640,6 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019" integrity sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw== -vt-pbf@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/vt-pbf/-/vt-pbf-3.1.1.tgz#b0f627e39a10ce91d943b898ed2363d21899fb82" - integrity sha512-pHjWdrIoxurpmTcbfBWXaPwSmtPAHS105253P1qyEfSTV2HJddqjM+kIHquaT/L6lVJIk9ltTGc0IxR/G47hYA== - dependencies: - "@mapbox/point-geometry" "0.1.0" - "@mapbox/vector-tile" "^1.3.1" - pbf "^3.0.5" - vt-pbf@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/vt-pbf/-/vt-pbf-3.1.3.tgz#68fd150756465e2edae1cc5c048e063916dcfaac" @@ -29870,6 +29764,11 @@ web-streams-polyfill@^3.0.0: resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.0.1.tgz#1f836eea307e8f4af15758ee473c7af755eb879e" integrity sha512-M+EmTdszMWINywOZaqpZ6VIEDUmNpRaTOuizF0ZKPjSDC8paMRe/jBBwFv0Yeyn5WYnM5pMqMQa82vpaE+IJRw== +web-streams-polyfill@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz#a6b74026b38e4885869fb5c589e90b95ccfc7965" + integrity sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"